xref: /PHP-5.5/sapi/isapi/php5isapi.c (revision 73c1be26)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2015 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Zeev Suraski <zeev@zend.com>                                |
16    |          Ben Mansell <ben@zeus.com> (Zeus Support)                   |
17    +----------------------------------------------------------------------+
18  */
19 /* $Id$ */
20 
21 #include "php.h"
22 #include <httpext.h>
23 #include <httpfilt.h>
24 #include <httpext.h>
25 #include "php_main.h"
26 #include "SAPI.h"
27 #include "php_globals.h"
28 #include "ext/standard/info.h"
29 #include "php_variables.h"
30 #include "php_ini.h"
31 
32 #ifdef PHP_WIN32
33 # include <process.h>
34 #else
35 # define __try
36 # define __except(val)
37 # define __declspec(foo)
38 #endif
39 
40 
41 #ifdef WITH_ZEUS
42 # include "httpext.h"
43 # include <errno.h>
44 # define GetLastError() errno
45 #endif
46 
47 #ifdef PHP_WIN32
48 #define PHP_ENABLE_SEH
49 #endif
50 
51 /*
52 uncomment the following lines to turn off
53 exception trapping when running under a debugger
54 
55 #ifdef _DEBUG
56 #undef PHP_ENABLE_SEH
57 #endif
58 */
59 
60 #define MAX_STATUS_LENGTH sizeof("xxxx LONGEST POSSIBLE STATUS DESCRIPTION")
61 #define ISAPI_SERVER_VAR_BUF_SIZE 1024
62 #define ISAPI_POST_DATA_BUF 1024
63 
64 static zend_bool bFilterLoaded=0;
65 static zend_bool bTerminateThreadsOnError=0;
66 
67 static char *isapi_special_server_variable_names[] = {
68 	"ALL_HTTP",
69 	"HTTPS",
70 #ifndef WITH_ZEUS
71 	"SCRIPT_NAME",
72 #endif
73 	NULL
74 };
75 
76 #define NUM_SPECIAL_VARS		(sizeof(isapi_special_server_variable_names)/sizeof(char *))
77 #define SPECIAL_VAR_ALL_HTTP	0
78 #define SPECIAL_VAR_HTTPS		1
79 #define SPECIAL_VAR_PHP_SELF	2
80 
81 static char *isapi_server_variable_names[] = {
82 	"AUTH_PASSWORD",
83 	"AUTH_TYPE",
84 	"AUTH_USER",
85 	"CONTENT_LENGTH",
86 	"CONTENT_TYPE",
87 	"PATH_TRANSLATED",
88 	"QUERY_STRING",
89 	"REMOTE_ADDR",
90 	"REMOTE_HOST",
91 	"REMOTE_USER",
92 	"REQUEST_METHOD",
93 	"SERVER_NAME",
94 	"SERVER_PORT",
95 	"SERVER_PROTOCOL",
96 	"SERVER_SOFTWARE",
97 #ifndef WITH_ZEUS
98 	"APPL_MD_PATH",
99 	"APPL_PHYSICAL_PATH",
100 	"INSTANCE_ID",
101 	"INSTANCE_META_PATH",
102 	"LOGON_USER",
103 	"REQUEST_URI",
104 	"URL",
105 #else
106 	"DOCUMENT_ROOT",
107 #endif
108 	NULL
109 };
110 
111 
112 static char *isapi_secure_server_variable_names[] = {
113 	"CERT_COOKIE",
114 	"CERT_FLAGS",
115 	"CERT_ISSUER",
116 	"CERT_KEYSIZE",
117 	"CERT_SECRETKEYSIZE",
118 	"CERT_SERIALNUMBER",
119 	"CERT_SERVER_ISSUER",
120 	"CERT_SERVER_SUBJECT",
121 	"CERT_SUBJECT",
122 	"HTTPS_KEYSIZE",
123 	"HTTPS_SECRETKEYSIZE",
124 	"HTTPS_SERVER_ISSUER",
125 	"HTTPS_SERVER_SUBJECT",
126 	"SERVER_PORT_SECURE",
127 #ifdef WITH_ZEUS
128 	"SSL_CLIENT_CN",
129 	"SSL_CLIENT_EMAIL",
130 	"SSL_CLIENT_OU",
131 	"SSL_CLIENT_O",
132 	"SSL_CLIENT_L",
133 	"SSL_CLIENT_ST",
134 	"SSL_CLIENT_C",
135 	"SSL_CLIENT_I_CN",
136 	"SSL_CLIENT_I_EMAIL",
137 	"SSL_CLIENT_I_OU",
138 	"SSL_CLIENT_I_O",
139 	"SSL_CLIENT_I_L",
140 	"SSL_CLIENT_I_ST",
141 	"SSL_CLIENT_I_C",
142 #endif
143 	NULL
144 };
145 
146 
php_info_isapi(ZEND_MODULE_INFO_FUNC_ARGS)147 static void php_info_isapi(ZEND_MODULE_INFO_FUNC_ARGS)
148 {
149 	char **p;
150 	char variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
151 	DWORD variable_len;
152 	char **all_variables[] = {
153 		isapi_server_variable_names,
154 		isapi_special_server_variable_names,
155 		isapi_secure_server_variable_names,
156 		NULL
157 	};
158 	char ***server_variable_names;
159 	LPEXTENSION_CONTROL_BLOCK lpECB;
160 
161 	lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
162 
163 	php_info_print_table_start();
164 	php_info_print_table_header(2, "Server Variable", "Value");
165 	server_variable_names = all_variables;
166 	while (*server_variable_names) {
167 		p = *server_variable_names;
168 		while (*p) {
169 			variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
170 			if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len)
171 				&& variable_buf[0]) {
172 				php_info_print_table_row(2, *p, variable_buf);
173 			} else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
174 				char *tmp_variable_buf;
175 
176 				tmp_variable_buf = (char *) emalloc(variable_len);
177 				if (lpECB->GetServerVariable(lpECB->ConnID, *p, tmp_variable_buf, &variable_len)
178 					&& variable_buf[0]) {
179 					php_info_print_table_row(2, *p, tmp_variable_buf);
180 				}
181 				efree(tmp_variable_buf);
182 			}
183 			p++;
184 		}
185 		server_variable_names++;
186 	}
187 	php_info_print_table_end();
188 }
189 
190 
191 static zend_module_entry php_isapi_module = {
192     STANDARD_MODULE_HEADER,
193 	"ISAPI",
194 	NULL,
195 	NULL,
196 	NULL,
197 	NULL,
198 	NULL,
199 	php_info_isapi,
200     NULL,
201 	STANDARD_MODULE_PROPERTIES
202 };
203 
204 
sapi_isapi_ub_write(const char * str,uint str_length TSRMLS_DC)205 static int sapi_isapi_ub_write(const char *str, uint str_length TSRMLS_DC)
206 {
207 	DWORD num_bytes = str_length;
208 	LPEXTENSION_CONTROL_BLOCK ecb;
209 
210 	ecb = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
211 	if (ecb->WriteClient(ecb->ConnID, (char *) str, &num_bytes, HSE_IO_SYNC) == FALSE) {
212 		php_handle_aborted_connection();
213 	}
214 	return num_bytes;
215 }
216 
217 
sapi_isapi_header_handler(sapi_header_struct * sapi_header,sapi_header_op_enum op,sapi_headers_struct * sapi_headers TSRMLS_DC)218 static int sapi_isapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
219 {
220 	return SAPI_HEADER_ADD;
221 }
222 
223 
224 
accumulate_header_length(sapi_header_struct * sapi_header,uint * total_length TSRMLS_DC)225 static void accumulate_header_length(sapi_header_struct *sapi_header, uint *total_length TSRMLS_DC)
226 {
227 	*total_length += sapi_header->header_len+2;
228 }
229 
230 
concat_header(sapi_header_struct * sapi_header,char ** combined_headers_ptr TSRMLS_DC)231 static void concat_header(sapi_header_struct *sapi_header, char **combined_headers_ptr TSRMLS_DC)
232 {
233 	memcpy(*combined_headers_ptr, sapi_header->header, sapi_header->header_len);
234 	*combined_headers_ptr += sapi_header->header_len;
235 	**combined_headers_ptr = '\r';
236 	(*combined_headers_ptr)++;
237 	**combined_headers_ptr = '\n';
238 	(*combined_headers_ptr)++;
239 }
240 
241 
sapi_isapi_send_headers(sapi_headers_struct * sapi_headers TSRMLS_DC)242 static int sapi_isapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
243 {
244 	uint total_length = 2;		/* account for the trailing \r\n */
245 	char *combined_headers, *combined_headers_ptr;
246 	LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
247 	HSE_SEND_HEADER_EX_INFO header_info;
248 	sapi_header_struct default_content_type;
249 	char *status_buf = NULL;
250 
251 	/* Obtain headers length */
252 	if (SG(sapi_headers).send_default_content_type) {
253 		sapi_get_default_content_type_header(&default_content_type TSRMLS_CC);
254 		accumulate_header_length(&default_content_type, (void *) &total_length TSRMLS_CC);
255 	}
256 	zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) accumulate_header_length, (void *) &total_length TSRMLS_CC);
257 
258 	/* Generate headers */
259 	combined_headers = (char *) emalloc(total_length+1);
260 	combined_headers_ptr = combined_headers;
261 	if (SG(sapi_headers).send_default_content_type) {
262 		concat_header(&default_content_type, (void *) &combined_headers_ptr TSRMLS_CC);
263 		sapi_free_header(&default_content_type); /* we no longer need it */
264 	}
265 	zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) concat_header, (void *) &combined_headers_ptr TSRMLS_CC);
266 	*combined_headers_ptr++ = '\r';
267 	*combined_headers_ptr++ = '\n';
268 	*combined_headers_ptr = 0;
269 
270 	switch (SG(sapi_headers).http_response_code) {
271 		case 200:
272 			header_info.pszStatus = "200 OK";
273 			break;
274 		case 302:
275 			header_info.pszStatus = "302 Moved Temporarily";
276 			break;
277 		case 401:
278 			header_info.pszStatus = "401 Authorization Required";
279 			break;
280 		default: {
281 			const char *sline = SG(sapi_headers).http_status_line;
282 			int sline_len;
283 
284 			/* httpd requires that r->status_line is set to the first digit of
285 			 * the status-code: */
286 			if (sline && ((sline_len = strlen(sline)) > 12) && strncmp(sline, "HTTP/1.", 7) == 0 && sline[8] == ' ') {
287 				if ((sline_len - 9) > MAX_STATUS_LENGTH) {
288 					status_buf = estrndup(sline + 9, MAX_STATUS_LENGTH);
289 				} else {
290 					status_buf = estrndup(sline + 9, sline_len - 9);
291 				}
292 			} else {
293 				status_buf = emalloc(MAX_STATUS_LENGTH + 1);
294 				snprintf(status_buf, MAX_STATUS_LENGTH, "%d Undescribed", SG(sapi_headers).http_response_code);
295 			}
296 			header_info.pszStatus = status_buf;
297 			break;
298 		}
299 	}
300 	header_info.cchStatus = strlen(header_info.pszStatus);
301 	header_info.pszHeader = combined_headers;
302 	header_info.cchHeader = total_length;
303 	header_info.fKeepConn = FALSE;
304 	lpECB->dwHttpStatusCode = SG(sapi_headers).http_response_code;
305 
306 	lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &header_info, NULL, NULL);
307 
308 	efree(combined_headers);
309 	if (status_buf) {
310 		efree(status_buf);
311 	}
312 	return SAPI_HEADER_SENT_SUCCESSFULLY;
313 }
314 
315 
php_isapi_startup(sapi_module_struct * sapi_module)316 static int php_isapi_startup(sapi_module_struct *sapi_module)
317 {
318 	if (php_module_startup(sapi_module, &php_isapi_module, 1)==FAILURE) {
319 		return FAILURE;
320 	} else {
321 		bTerminateThreadsOnError = (zend_bool) INI_INT("isapi.terminate_threads_on_error");
322 		return SUCCESS;
323 	}
324 }
325 
326 
sapi_isapi_read_post(char * buffer,uint count_bytes TSRMLS_DC)327 static int sapi_isapi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
328 {
329 	LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
330 	DWORD read_from_buf=0;
331 	DWORD read_from_input=0;
332 	DWORD total_read=0;
333 
334 	if ((DWORD) SG(read_post_bytes) < lpECB->cbAvailable) {
335 		read_from_buf = MIN(lpECB->cbAvailable-SG(read_post_bytes), count_bytes);
336 		memcpy(buffer, lpECB->lpbData+SG(read_post_bytes), read_from_buf);
337 		total_read += read_from_buf;
338 	}
339 	if (read_from_buf<count_bytes
340 		&& (SG(read_post_bytes)+read_from_buf) < lpECB->cbTotalBytes) {
341 		DWORD cbRead=0, cbSize;
342 
343 		read_from_input = MIN(count_bytes-read_from_buf, lpECB->cbTotalBytes-SG(read_post_bytes)-read_from_buf);
344 		while (cbRead < read_from_input) {
345 			cbSize = read_from_input - cbRead;
346 			if (!lpECB->ReadClient(lpECB->ConnID, buffer+read_from_buf+cbRead, &cbSize) || cbSize==0) {
347 				break;
348 			}
349 			cbRead += cbSize;
350 		}
351 		total_read += cbRead;
352 	}
353 	return total_read;
354 }
355 
356 
sapi_isapi_read_cookies(TSRMLS_D)357 static char *sapi_isapi_read_cookies(TSRMLS_D)
358 {
359 	LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
360 	char variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
361 	DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
362 
363 	if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_COOKIE", variable_buf, &variable_len)) {
364 		return estrndup(variable_buf, variable_len);
365 	} else if (GetLastError()==ERROR_INSUFFICIENT_BUFFER) {
366 		char *tmp_variable_buf = (char *) emalloc(variable_len+1);
367 
368 		if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_COOKIE", tmp_variable_buf, &variable_len)) {
369 			tmp_variable_buf[variable_len] = 0;
370 			return tmp_variable_buf;
371 		} else {
372 			efree(tmp_variable_buf);
373 		}
374 	}
375 	return STR_EMPTY_ALLOC();
376 }
377 
378 
379 #ifdef WITH_ZEUS
380 
sapi_isapi_register_zeus_ssl_variables(LPEXTENSION_CONTROL_BLOCK lpECB,zval * track_vars_array TSRMLS_DC)381 static void sapi_isapi_register_zeus_ssl_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
382 {
383 	char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
384 	DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
385 	char static_cons_buf[ISAPI_SERVER_VAR_BUF_SIZE];
386 	/*
387 	 * We need to construct the /C=.../ST=...
388 	 * DN's for SSL_CLIENT_DN and SSL_CLIENT_I_DN
389 	 */
390 	strcpy( static_cons_buf, "/C=" );
391 	if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
392 		strlcat( static_cons_buf, static_variable_buf,  ISAPI_SERVER_VAR_BUF_SIZE);
393 	}
394 	strlcat( static_cons_buf, "/ST=",  ISAPI_SERVER_VAR_BUF_SIZE);
395 	variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
396 	if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
397 		strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
398 	}
399 	php_register_variable( "SSL_CLIENT_DN", static_cons_buf, track_vars_array TSRMLS_CC );
400 
401 	strcpy( static_cons_buf, "/C=" );
402 	variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
403 	if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
404 		strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
405 	}
406 	strlcat( static_cons_buf, "/ST=", ISAPI_SERVER_VAR_BUF_SIZE);
407 	variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
408 	if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
409 		strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
410 	}
411 	php_register_variable( "SSL_CLIENT_I_DN", static_cons_buf, track_vars_array TSRMLS_CC );
412 }
413 
sapi_isapi_register_zeus_variables(LPEXTENSION_CONTROL_BLOCK lpECB,zval * track_vars_array TSRMLS_DC)414 static void sapi_isapi_register_zeus_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
415 {
416 	char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
417 	DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
418 	DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE;
419 	DWORD pathinfo_len = 0;
420 	char *strtok_buf = NULL;
421 
422 	/* Get SCRIPT_NAME, we use this to work out which bit of the URL
423 	 * belongs in PHP's version of PATH_INFO
424 	 */
425 	lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len);
426 
427 	/* Adjust Zeus' version of PATH_INFO, set PHP_SELF,
428 	 * and generate REQUEST_URI
429 	 */
430 	if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
431 
432 		/* PHP_SELF is just PATH_INFO */
433 		php_register_variable( "PHP_SELF", static_variable_buf, track_vars_array TSRMLS_CC );
434 
435 		/* Chop off filename to get just the 'real' PATH_INFO' */
436 		pathinfo_len = variable_len - scriptname_len;
437 		php_register_variable( "PATH_INFO", static_variable_buf + scriptname_len - 1, track_vars_array TSRMLS_CC );
438 		/* append query string to give url... extra byte for '?' */
439 		if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) {
440 			/* append query string only if it is present... */
441 			if ( strlen(lpECB->lpszQueryString) ) {
442 				static_variable_buf[ variable_len - 1 ] = '?';
443 				strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString );
444 			}
445 			php_register_variable( "URL", static_variable_buf, track_vars_array TSRMLS_CC );
446 			php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array TSRMLS_CC );
447 		}
448 	}
449 
450 	/* Get and adjust PATH_TRANSLATED to what PHP wants */
451 	variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
452 	if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
453 		static_variable_buf[ variable_len - pathinfo_len - 1 ] = '\0';
454 		php_register_variable( "PATH_TRANSLATED", static_variable_buf, track_vars_array TSRMLS_CC );
455 	}
456 
457 	/* Bring in the AUTHENTICATION stuff as needed */
458 	variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
459 	if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_USER", static_variable_buf, &variable_len) && static_variable_buf[0] )  {
460 		php_register_variable( "PHP_AUTH_USER", static_variable_buf, track_vars_array TSRMLS_CC );
461 	}
462 	variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
463 	if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_PASSWORD", static_variable_buf, &variable_len) && static_variable_buf[0] )  {
464 		php_register_variable( "PHP_AUTH_PW", static_variable_buf, track_vars_array TSRMLS_CC );
465 	}
466 	variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
467 	if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_TYPE", static_variable_buf, &variable_len) && static_variable_buf[0] )  {
468 		php_register_variable( "AUTH_TYPE", static_variable_buf, track_vars_array TSRMLS_CC );
469 	}
470 
471 	/* And now, for the SSL variables (if applicable) */
472 	variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
473 	if ( lpECB->GetServerVariable(lpECB->ConnID, "CERT_COOKIE", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
474 		sapi_isapi_register_zeus_ssl_variables( lpECB, track_vars_array TSRMLS_CC );
475 	}
476 	/* Copy some of the variables we need to meet Apache specs */
477 	variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
478 	if ( lpECB->GetServerVariable(lpECB->ConnID, "SERVER_SOFTWARE", static_variable_buf, &variable_len) && static_variable_buf[0] )  {
479 		php_register_variable( "SERVER_SIGNATURE", static_variable_buf, track_vars_array TSRMLS_CC );
480 	}
481 }
482 #else
483 
sapi_isapi_register_iis_variables(LPEXTENSION_CONTROL_BLOCK lpECB,zval * track_vars_array TSRMLS_DC)484 static void sapi_isapi_register_iis_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
485 {
486 	char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
487 	char path_info_buf[ISAPI_SERVER_VAR_BUF_SIZE];
488 	DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
489 	DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE;
490 	DWORD pathinfo_len = 0;
491 	HSE_URL_MAPEX_INFO humi;
492 
493 	/* Get SCRIPT_NAME, we use this to work out which bit of the URL
494 	 * belongs in PHP's version of PATH_INFO.  SCRIPT_NAME also becomes PHP_SELF.
495 	 */
496 	lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len);
497 	php_register_variable("SCRIPT_FILENAME", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
498 
499 	/* Adjust IIS' version of PATH_INFO, set PHP_SELF,
500 	 * and generate REQUEST_URI
501 	 * Get and adjust PATH_TRANSLATED to what PHP wants
502 	 */
503 	if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
504 
505 		/* Chop off filename to get just the 'real' PATH_INFO' */
506 		php_register_variable( "ORIG_PATH_INFO", static_variable_buf, track_vars_array TSRMLS_CC );
507 		pathinfo_len = variable_len - scriptname_len;
508 		strncpy(path_info_buf, static_variable_buf + scriptname_len - 1, sizeof(path_info_buf)-1);
509 		php_register_variable( "PATH_INFO", path_info_buf, track_vars_array TSRMLS_CC );
510 		/* append query string to give url... extra byte for '?' */
511 		if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) {
512 			/* append query string only if it is present... */
513 			if ( strlen(lpECB->lpszQueryString) ) {
514 				static_variable_buf[ variable_len - 1 ] = '?';
515 				strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString );
516 			}
517 			php_register_variable( "URL", static_variable_buf, track_vars_array TSRMLS_CC );
518 			php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array TSRMLS_CC );
519 		}
520 		variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
521 		if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
522 			php_register_variable( "ORIG_PATH_TRANSLATED", static_variable_buf, track_vars_array TSRMLS_CC );
523 		}
524 		if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, path_info_buf, &pathinfo_len, (LPDWORD) &humi)) {
525 			/* Remove trailing \  */
526 			if (humi.lpszPath[variable_len-2] == '\\') {
527 				humi.lpszPath[variable_len-2] = 0;
528 			}
529 			php_register_variable("PATH_TRANSLATED", humi.lpszPath, track_vars_array TSRMLS_CC);
530 		}
531 	}
532 
533 	static_variable_buf[0] = '/';
534 	static_variable_buf[1] = 0;
535 	variable_len = 2;
536 	if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) {
537 		/* Remove trailing \  */
538 		if (humi.lpszPath[variable_len-2] == '\\') {
539 			humi.lpszPath[variable_len-2] = 0;
540 		}
541 		php_register_variable("DOCUMENT_ROOT", humi.lpszPath, track_vars_array TSRMLS_CC);
542 	}
543 
544 	if (!SG(request_info).auth_user || !SG(request_info).auth_password ||
545 		!SG(request_info).auth_user[0] || !SG(request_info).auth_password[0]) {
546 		variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
547 		if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_AUTHORIZATION", static_variable_buf, &variable_len)
548 			&& static_variable_buf[0]) {
549 			php_handle_auth_data(static_variable_buf TSRMLS_CC);
550 		}
551 	}
552 
553 	if (SG(request_info).auth_user)  {
554 		php_register_variable("PHP_AUTH_USER", SG(request_info).auth_user, track_vars_array TSRMLS_CC );
555 	}
556 	if (SG(request_info).auth_password) {
557 		php_register_variable("PHP_AUTH_PW", SG(request_info).auth_password, track_vars_array TSRMLS_CC );
558 	}
559 }
560 #endif
561 
sapi_isapi_register_server_variables2(char ** server_variables,LPEXTENSION_CONTROL_BLOCK lpECB,zval * track_vars_array,char ** recorded_values TSRMLS_DC)562 static void sapi_isapi_register_server_variables2(char **server_variables, LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array, char **recorded_values TSRMLS_DC)
563 {
564 	char **p=server_variables;
565 	DWORD variable_len;
566 	char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
567 	char *variable_buf;
568 
569 	while (*p) {
570 		variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
571 		if (lpECB->GetServerVariable(lpECB->ConnID, *p, static_variable_buf, &variable_len)
572 			&& static_variable_buf[0]) {
573 			php_register_variable(*p, static_variable_buf, track_vars_array TSRMLS_CC);
574 			if (recorded_values) {
575 				recorded_values[p-server_variables] = estrndup(static_variable_buf, variable_len);
576 			}
577 		} else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
578 			variable_buf = (char *) emalloc(variable_len+1);
579 			if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len)
580 				&& variable_buf[0]) {
581 				php_register_variable(*p, variable_buf, track_vars_array TSRMLS_CC);
582 			}
583 			if (recorded_values) {
584 				recorded_values[p-server_variables] = variable_buf;
585 			} else {
586 				efree(variable_buf);
587 			}
588 		} else { /* for compatibility with Apache SAPIs */
589 			php_register_variable(*p, "", track_vars_array TSRMLS_CC);
590 		}
591 		p++;
592 	}
593 }
594 
595 
sapi_isapi_register_server_variables(zval * track_vars_array TSRMLS_DC)596 static void sapi_isapi_register_server_variables(zval *track_vars_array TSRMLS_DC)
597 {
598 	DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
599 	char *variable;
600 	char *strtok_buf = NULL;
601 	char *isapi_special_server_variables[NUM_SPECIAL_VARS];
602 	LPEXTENSION_CONTROL_BLOCK lpECB;
603 
604 	lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
605 
606 	/* Register the special ISAPI variables */
607 	memset(isapi_special_server_variables, 0, sizeof(isapi_special_server_variables));
608 	sapi_isapi_register_server_variables2(isapi_special_server_variable_names, lpECB, track_vars_array, isapi_special_server_variables TSRMLS_CC);
609 	if (SG(request_info).cookie_data) {
610 		php_register_variable("HTTP_COOKIE", SG(request_info).cookie_data, track_vars_array TSRMLS_CC);
611 	}
612 
613 	/* Register the standard ISAPI variables */
614 	sapi_isapi_register_server_variables2(isapi_server_variable_names, lpECB, track_vars_array, NULL TSRMLS_CC);
615 
616 	if (isapi_special_server_variables[SPECIAL_VAR_HTTPS]
617 		&& (atoi(isapi_special_server_variables[SPECIAL_VAR_HTTPS])
618 		|| !strcasecmp(isapi_special_server_variables[SPECIAL_VAR_HTTPS], "on"))
619 	) {
620 		/* Register SSL ISAPI variables */
621 		sapi_isapi_register_server_variables2(isapi_secure_server_variable_names, lpECB, track_vars_array, NULL TSRMLS_CC);
622 	}
623 
624 	if (isapi_special_server_variables[SPECIAL_VAR_HTTPS]) {
625 		efree(isapi_special_server_variables[SPECIAL_VAR_HTTPS]);
626 	}
627 
628 
629 #ifdef WITH_ZEUS
630 	sapi_isapi_register_zeus_variables(lpECB, track_vars_array TSRMLS_CC);
631 #else
632 	sapi_isapi_register_iis_variables(lpECB, track_vars_array TSRMLS_CC);
633 #endif
634 
635 	/* PHP_SELF support */
636 	if (isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]) {
637 		php_register_variable("PHP_SELF", isapi_special_server_variables[SPECIAL_VAR_PHP_SELF], track_vars_array TSRMLS_CC);
638 		efree(isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]);
639 	}
640 
641 	if (isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP]) {
642 		/* Register the internal bits of ALL_HTTP */
643 		variable = php_strtok_r(isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP], "\r\n", &strtok_buf);
644 		while (variable) {
645 			char *colon = strchr(variable, ':');
646 
647 			if (colon) {
648 				char *value = colon+1;
649 
650 				while (*value==' ') {
651 					value++;
652 				}
653 				*colon = 0;
654 				php_register_variable(variable, value, track_vars_array TSRMLS_CC);
655 				*colon = ':';
656 			}
657 			variable = php_strtok_r(NULL, "\r\n", &strtok_buf);
658 		}
659 		efree(isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP]);
660 	}
661 }
662 
663 
664 static sapi_module_struct isapi_sapi_module = {
665 	"isapi",						/* name */
666 	"ISAPI",						/* pretty name */
667 
668 	php_isapi_startup,				/* startup */
669 	php_module_shutdown_wrapper,	/* shutdown */
670 
671 	NULL,							/* activate */
672 	NULL,							/* deactivate */
673 
674 	sapi_isapi_ub_write,			/* unbuffered write */
675 	NULL,							/* flush */
676 	NULL,							/* get uid */
677 	NULL,							/* getenv */
678 
679 	php_error,						/* error handler */
680 
681 	sapi_isapi_header_handler,		/* header handler */
682 	sapi_isapi_send_headers,		/* send headers handler */
683 	NULL,							/* send header handler */
684 
685 	sapi_isapi_read_post,			/* read POST data */
686 	sapi_isapi_read_cookies,		/* read Cookies */
687 
688 	sapi_isapi_register_server_variables,	/* register server variables */
689 	NULL,							/* Log message */
690 	NULL,							/* Get request time */
691 	NULL,							/* Child terminate */
692 
693 	STANDARD_SAPI_MODULE_PROPERTIES
694 };
695 
696 
GetFilterVersion(PHTTP_FILTER_VERSION pFilterVersion)697 BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pFilterVersion)
698 {
699 	bFilterLoaded = 1;
700 	pFilterVersion->dwFilterVersion = HTTP_FILTER_REVISION;
701 	strcpy(pFilterVersion->lpszFilterDesc, isapi_sapi_module.pretty_name);
702 	pFilterVersion->dwFlags= (SF_NOTIFY_AUTHENTICATION | SF_NOTIFY_PREPROC_HEADERS);
703 	return TRUE;
704 }
705 
706 
HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,DWORD notificationType,LPVOID pvNotification)707 DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification)
708 {
709 	TSRMLS_FETCH();
710 
711 	switch (notificationType) {
712 		case SF_NOTIFY_PREPROC_HEADERS:
713 			SG(request_info).auth_user = NULL;
714 			SG(request_info).auth_password = NULL;
715 			SG(request_info).auth_digest = NULL;
716 			break;
717 		case SF_NOTIFY_AUTHENTICATION: {
718 				char *auth_user = ((HTTP_FILTER_AUTHENT *) pvNotification)->pszUser;
719 				char *auth_password = ((HTTP_FILTER_AUTHENT *) pvNotification)->pszPassword;
720 
721 				if (auth_user && auth_user[0]) {
722 					SG(request_info).auth_user = estrdup(auth_user);
723 				}
724 				if (auth_password && auth_password[0]) {
725 					SG(request_info).auth_password = estrdup(auth_password);
726 				}
727 				return SF_STATUS_REQ_HANDLED_NOTIFICATION;
728 			}
729 			break;
730 	}
731 	return SF_STATUS_REQ_NEXT_NOTIFICATION;
732 }
733 
734 
init_request_info(LPEXTENSION_CONTROL_BLOCK lpECB TSRMLS_DC)735 static void init_request_info(LPEXTENSION_CONTROL_BLOCK lpECB TSRMLS_DC)
736 {
737 	DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
738 	char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
739 #ifndef WITH_ZEUS
740 	HSE_URL_MAPEX_INFO humi;
741 #endif
742 
743 	SG(request_info).request_method = lpECB->lpszMethod;
744 	SG(request_info).query_string = lpECB->lpszQueryString;
745 	SG(request_info).request_uri = lpECB->lpszPathInfo;
746 	SG(request_info).content_type = lpECB->lpszContentType;
747 	SG(request_info).content_length = lpECB->cbTotalBytes;
748 	SG(sapi_headers).http_response_code = 200;  /* I think dwHttpStatusCode is invalid at this stage -RL */
749 	if (!bFilterLoaded) { /* we don't have valid ISAPI Filter information */
750 		SG(request_info).auth_user = SG(request_info).auth_password = SG(request_info).auth_digest = NULL;
751 	}
752 
753 #ifdef WITH_ZEUS
754 	/* PATH_TRANSLATED can contain extra PATH_INFO stuff after the
755 	 * file being loaded, so we must use SCRIPT_FILENAME instead
756 	 */
757 	if(lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_FILENAME", static_variable_buf, &variable_len)) {
758 		SG(request_info).path_translated = estrdup(static_variable_buf);
759 	} else
760 #else
761 	/* happily, IIS gives us SCRIPT_NAME which is correct (without PATH_INFO stuff)
762 	   so we can just map that to the physical path and we have our filename */
763 
764 	lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &variable_len);
765 	if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) {
766 		SG(request_info).path_translated = estrdup(humi.lpszPath);
767 	} else
768 #endif
769 		/* if mapping fails, default to what the server tells us */
770 		SG(request_info).path_translated = estrdup(lpECB->lpszPathTranslated);
771 
772 	/* some server configurations allow '..' to slip through in the
773 	   translated path.   We'll just refuse to handle such a path. */
774 	if (strstr(SG(request_info).path_translated,"..")) {
775 		SG(sapi_headers).http_response_code = 404;
776 		efree(SG(request_info).path_translated);
777 		SG(request_info).path_translated = NULL;
778 	}
779 }
780 
781 
php_isapi_report_exception(char * message,int message_len TSRMLS_DC)782 static void php_isapi_report_exception(char *message, int message_len TSRMLS_DC)
783 {
784 	if (!SG(headers_sent)) {
785 		HSE_SEND_HEADER_EX_INFO header_info;
786 		LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
787 
788 		header_info.pszStatus = "500 Internal Server Error";
789 		header_info.cchStatus = strlen(header_info.pszStatus);
790 		header_info.pszHeader = "Content-Type: text/html\r\n\r\n";
791 		header_info.cchHeader = strlen(header_info.pszHeader);
792 
793 		lpECB->dwHttpStatusCode = 500;
794 		lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &header_info, NULL, NULL);
795 		SG(headers_sent)=1;
796 	}
797 	sapi_isapi_ub_write(message, message_len TSRMLS_CC);
798 }
799 
800 
GetExtensionVersion(HSE_VERSION_INFO * pVer)801 BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
802 {
803 	pVer->dwExtensionVersion = HSE_VERSION;
804 #ifdef WITH_ZEUS
805 	strncpy( pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN);
806 #else
807 	lstrcpyn(pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN);
808 #endif
809 	return TRUE;
810 }
811 
812 
my_endthread()813 static void my_endthread()
814 {
815 #ifdef PHP_WIN32
816 	if (bTerminateThreadsOnError) {
817 		_endthread();
818 	}
819 #endif
820 }
821 
822 #ifdef PHP_WIN32
823 /* ep is accessible only in the context of the __except expression,
824  * so we have to call this function to obtain it.
825  */
exceptionhandler(LPEXCEPTION_POINTERS * e,LPEXCEPTION_POINTERS ep)826 BOOL exceptionhandler(LPEXCEPTION_POINTERS *e, LPEXCEPTION_POINTERS ep)
827 {
828 	*e=ep;
829 	return TRUE;
830 }
831 #endif
832 
HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)833 DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
834 {
835 	zend_file_handle file_handle;
836 	zend_bool stack_overflown=0;
837 	int retval = FAILURE;
838 #ifdef PHP_ENABLE_SEH
839 	LPEXCEPTION_POINTERS e;
840 #endif
841 	TSRMLS_FETCH();
842 
843 	zend_first_try {
844 #ifdef PHP_ENABLE_SEH
845 		__try {
846 #endif
847 			init_request_info(lpECB TSRMLS_CC);
848 			SG(server_context) = lpECB;
849 
850 			php_request_startup(TSRMLS_C);
851 
852 			file_handle.filename = SG(request_info).path_translated;
853 			file_handle.free_filename = 0;
854 			file_handle.type = ZEND_HANDLE_FILENAME;
855 			file_handle.opened_path = NULL;
856 
857 			/* open the script here so we can 404 if it fails */
858 			if (file_handle.filename)
859 				retval = php_fopen_primary_script(&file_handle TSRMLS_CC);
860 
861 			if (!file_handle.filename || retval == FAILURE) {
862 				SG(sapi_headers).http_response_code = 404;
863 				PUTS("No input file specified.\n");
864 			} else {
865 				php_execute_script(&file_handle TSRMLS_CC);
866 			}
867 
868 			if (SG(request_info).cookie_data) {
869 				efree(SG(request_info).cookie_data);
870 			}
871 			if (SG(request_info).path_translated)
872 				efree(SG(request_info).path_translated);
873 #ifdef PHP_ENABLE_SEH
874 		} __except(exceptionhandler(&e, GetExceptionInformation())) {
875 			char buf[1024];
876 			if (_exception_code()==EXCEPTION_STACK_OVERFLOW) {
877 				LPBYTE lpPage;
878 				static SYSTEM_INFO si;
879 				static MEMORY_BASIC_INFORMATION mi;
880 				static DWORD dwOldProtect;
881 
882 				GetSystemInfo(&si);
883 
884 				/* Get page ESP is pointing to */
885 				_asm mov lpPage, esp;
886 
887 				/* Get stack allocation base */
888 				VirtualQuery(lpPage, &mi, sizeof(mi));
889 
890 				/* Go to the page below the current page */
891 				lpPage = (LPBYTE) (mi.BaseAddress) - si.dwPageSize;
892 
893 				/* Free pages below current page */
894 				if (!VirtualFree(mi.AllocationBase, (LPBYTE)lpPage - (LPBYTE) mi.AllocationBase, MEM_DECOMMIT)) {
895 					_endthread();
896 				}
897 
898 				/* Restore the guard page */
899 				if (!VirtualProtect(lpPage, si.dwPageSize, PAGE_GUARD | PAGE_READWRITE, &dwOldProtect)) {
900 					_endthread();
901 				}
902 
903 				CG(unclean_shutdown)=1;
904 				_snprintf(buf, sizeof(buf)-1,"PHP has encountered a Stack overflow");
905 				php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
906 			} else if (_exception_code()==EXCEPTION_ACCESS_VIOLATION) {
907 				_snprintf(buf, sizeof(buf)-1,"PHP has encountered an Access Violation at %p", e->ExceptionRecord->ExceptionAddress);
908 				php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
909 				my_endthread();
910 			} else {
911 				_snprintf(buf, sizeof(buf)-1,"PHP has encountered an Unhandled Exception Code %d at %p", e->ExceptionRecord->ExceptionCode , e->ExceptionRecord->ExceptionAddress);
912 				php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
913 				my_endthread();
914 			}
915 		}
916 #endif
917 #ifdef PHP_ENABLE_SEH
918 		__try {
919 			php_request_shutdown(NULL);
920 		} __except(EXCEPTION_EXECUTE_HANDLER) {
921 			my_endthread();
922 		}
923 #else
924 		php_request_shutdown(NULL);
925 #endif
926 	} zend_catch {
927 		zend_try {
928 			php_request_shutdown(NULL);
929 		} zend_end_try();
930 		return HSE_STATUS_ERROR;
931 	} zend_end_try();
932 
933 	return HSE_STATUS_SUCCESS;
934 }
935 
936 
937 
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)938 __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
939 {
940 	switch (fdwReason) {
941 		case DLL_PROCESS_ATTACH:
942 #ifdef WITH_ZEUS
943 			tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "TSRM.log");
944 #else
945 			tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "C:\\TSRM.log");
946 #endif
947 			sapi_startup(&isapi_sapi_module);
948 			if (isapi_sapi_module.startup) {
949 				isapi_sapi_module.startup(&sapi_module);
950 			}
951 			break;
952 		case DLL_THREAD_ATTACH:
953 			break;
954 		case DLL_THREAD_DETACH:
955 			ts_free_thread();
956 			break;
957 		case DLL_PROCESS_DETACH:
958 			if (isapi_sapi_module.shutdown) {
959 				isapi_sapi_module.shutdown(&sapi_module);
960 			}
961 			sapi_shutdown();
962 			tsrm_shutdown();
963 			break;
964 	}
965 	return TRUE;
966 }
967 
968 /*
969  * Local variables:
970  * tab-width: 4
971  * c-basic-offset: 4
972  * End:
973  */
974