From a68882e05f2036b7e5f89da1b131266c45b3b84e Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sat, 15 Feb 2025 16:58:33 +0100 Subject: [PATCH 01/13] Revert "removed sapi/isapi" This reverts commit 4fedc42c2b4765d473863aaf49efe0342b0a2b5f. --- sapi/isapi/CREDITS | 2 + sapi/isapi/config.m4 | 24 + sapi/isapi/config.w32 | 13 + sapi/isapi/php.sym | 5 + sapi/isapi/php7isapi.c | 971 +++++++++++++++++++++++++++ sapi/isapi/php7isapi.def | 5 + sapi/isapi/stresstest/getopt.c | 175 +++++ sapi/isapi/stresstest/getopt.h | 12 + sapi/isapi/stresstest/notes.txt | 56 ++ sapi/isapi/stresstest/stresstest.cpp | 936 ++++++++++++++++++++++++++ 10 files changed, 2199 insertions(+) create mode 100644 sapi/isapi/CREDITS create mode 100644 sapi/isapi/config.m4 create mode 100644 sapi/isapi/config.w32 create mode 100644 sapi/isapi/php.sym create mode 100644 sapi/isapi/php7isapi.c create mode 100644 sapi/isapi/php7isapi.def create mode 100644 sapi/isapi/stresstest/getopt.c create mode 100644 sapi/isapi/stresstest/getopt.h create mode 100644 sapi/isapi/stresstest/notes.txt create mode 100644 sapi/isapi/stresstest/stresstest.cpp diff --git a/sapi/isapi/CREDITS b/sapi/isapi/CREDITS new file mode 100644 index 0000000000000..11c6fdc73c599 --- /dev/null +++ b/sapi/isapi/CREDITS @@ -0,0 +1,2 @@ +ISAPI +Andi Gutmans, Zeev Suraski diff --git a/sapi/isapi/config.m4 b/sapi/isapi/config.m4 new file mode 100644 index 0000000000000..4073173eafc61 --- /dev/null +++ b/sapi/isapi/config.m4 @@ -0,0 +1,24 @@ +dnl +dnl $Id$ +dnl + +PHP_ARG_WITH(isapi, for Zeus ISAPI support, +[ --with-isapi[=DIR] Build PHP as an ISAPI module for use with Zeus], no, no) + +if test "$PHP_ISAPI" != "no"; then + if test "$PHP_ISAPI" = "yes"; then + ZEUSPATH=/usr/local/zeus # the default + else + ZEUSPATH=$PHP_ISAPI + fi + test -f "$ZEUSPATH/web/include/httpext.h" || AC_MSG_ERROR(Unable to find httpext.h in $ZEUSPATH/web/include) + PHP_BUILD_THREAD_SAFE + AC_DEFINE(WITH_ZEUS, 1, [ ]) + PHP_ADD_INCLUDE($ZEUSPATH/web/include) + PHP_SELECT_SAPI(isapi, shared, php7isapi.c) + INSTALL_IT="\$(SHELL) \$(srcdir)/install-sh -m 0755 $SAPI_SHARED \$(INSTALL_ROOT)$ZEUSPATH/web/bin/" +fi + +dnl ## Local Variables: +dnl ## tab-width: 4 +dnl ## End: diff --git a/sapi/isapi/config.w32 b/sapi/isapi/config.w32 new file mode 100644 index 0000000000000..5a359f11b2070 --- /dev/null +++ b/sapi/isapi/config.w32 @@ -0,0 +1,13 @@ +// vim:ft=javascript +// $Id$ + +ARG_ENABLE('isapi', 'Build ISAPI version of PHP', 'no'); + +if (PHP_ISAPI == "yes") { + if (PHP_ZTS == "no") { + WARNING("ISAPI module requires an --enable-zts build of PHP"); + } else { + SAPI('isapi', 'php7isapi.c', 'php' + PHP_VERSION + 'isapi.dll', '/D PHP7ISAPI_EXPORTS'); + ADD_FLAG('LDFLAGS_ISAPI', '/DEF:sapi\\isapi\\php7isapi.def'); + } +} diff --git a/sapi/isapi/php.sym b/sapi/isapi/php.sym new file mode 100644 index 0000000000000..34b50b851cad0 --- /dev/null +++ b/sapi/isapi/php.sym @@ -0,0 +1,5 @@ +GetFilterVersion +HttpFilterProc +GetExtensionVersion +HttpExtensionProc +ZSLMain diff --git a/sapi/isapi/php7isapi.c b/sapi/isapi/php7isapi.c new file mode 100644 index 0000000000000..627c20c18cc62 --- /dev/null +++ b/sapi/isapi/php7isapi.c @@ -0,0 +1,971 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2015 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Zeev Suraski | + | Ben Mansell (Zeus Support) | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#include "php.h" +#include +#include +#include +#include "php_main.h" +#include "SAPI.h" +#include "php_globals.h" +#include "ext/standard/info.h" +#include "php_variables.h" +#include "php_ini.h" + +#ifdef PHP_WIN32 +# include +#else +# define __try +# define __except(val) +# define __declspec(foo) +#endif + + +#ifdef WITH_ZEUS +# include "httpext.h" +# include +# define GetLastError() errno +#endif + +#ifdef PHP_WIN32 +#define PHP_ENABLE_SEH +#endif + +/* +uncomment the following lines to turn off +exception trapping when running under a debugger + +#ifdef _DEBUG +#undef PHP_ENABLE_SEH +#endif +*/ + +#define MAX_STATUS_LENGTH sizeof("xxxx LONGEST POSSIBLE STATUS DESCRIPTION") +#define ISAPI_SERVER_VAR_BUF_SIZE 1024 +#define ISAPI_POST_DATA_BUF 1024 + +static zend_bool bFilterLoaded=0; +static zend_bool bTerminateThreadsOnError=0; + +static char *isapi_special_server_variable_names[] = { + "ALL_HTTP", + "HTTPS", +#ifndef WITH_ZEUS + "SCRIPT_NAME", +#endif + NULL +}; + +#define NUM_SPECIAL_VARS (sizeof(isapi_special_server_variable_names)/sizeof(char *)) +#define SPECIAL_VAR_ALL_HTTP 0 +#define SPECIAL_VAR_HTTPS 1 +#define SPECIAL_VAR_PHP_SELF 2 + +static char *isapi_server_variable_names[] = { + "AUTH_PASSWORD", + "AUTH_TYPE", + "AUTH_USER", + "CONTENT_LENGTH", + "CONTENT_TYPE", + "PATH_TRANSLATED", + "QUERY_STRING", + "REMOTE_ADDR", + "REMOTE_HOST", + "REMOTE_USER", + "REQUEST_METHOD", + "SERVER_NAME", + "SERVER_PORT", + "SERVER_PROTOCOL", + "SERVER_SOFTWARE", +#ifndef WITH_ZEUS + "APPL_MD_PATH", + "APPL_PHYSICAL_PATH", + "INSTANCE_ID", + "INSTANCE_META_PATH", + "LOGON_USER", + "REQUEST_URI", + "URL", +#else + "DOCUMENT_ROOT", +#endif + NULL +}; + + +static char *isapi_secure_server_variable_names[] = { + "CERT_COOKIE", + "CERT_FLAGS", + "CERT_ISSUER", + "CERT_KEYSIZE", + "CERT_SECRETKEYSIZE", + "CERT_SERIALNUMBER", + "CERT_SERVER_ISSUER", + "CERT_SERVER_SUBJECT", + "CERT_SUBJECT", + "HTTPS_KEYSIZE", + "HTTPS_SECRETKEYSIZE", + "HTTPS_SERVER_ISSUER", + "HTTPS_SERVER_SUBJECT", + "SERVER_PORT_SECURE", +#ifdef WITH_ZEUS + "SSL_CLIENT_CN", + "SSL_CLIENT_EMAIL", + "SSL_CLIENT_OU", + "SSL_CLIENT_O", + "SSL_CLIENT_L", + "SSL_CLIENT_ST", + "SSL_CLIENT_C", + "SSL_CLIENT_I_CN", + "SSL_CLIENT_I_EMAIL", + "SSL_CLIENT_I_OU", + "SSL_CLIENT_I_O", + "SSL_CLIENT_I_L", + "SSL_CLIENT_I_ST", + "SSL_CLIENT_I_C", +#endif + NULL +}; + + +static void php_info_isapi(ZEND_MODULE_INFO_FUNC_ARGS) +{ + char **p; + char variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; + DWORD variable_len; + char **all_variables[] = { + isapi_server_variable_names, + isapi_special_server_variable_names, + isapi_secure_server_variable_names, + NULL + }; + char ***server_variable_names; + LPEXTENSION_CONTROL_BLOCK lpECB; + + lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context); + + php_info_print_table_start(); + php_info_print_table_header(2, "Server Variable", "Value"); + server_variable_names = all_variables; + while (*server_variable_names) { + p = *server_variable_names; + while (*p) { + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len) + && variable_buf[0]) { + php_info_print_table_row(2, *p, variable_buf); + } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + char *tmp_variable_buf; + + tmp_variable_buf = (char *) emalloc(variable_len); + if (lpECB->GetServerVariable(lpECB->ConnID, *p, tmp_variable_buf, &variable_len) + && variable_buf[0]) { + php_info_print_table_row(2, *p, tmp_variable_buf); + } + efree(tmp_variable_buf); + } + p++; + } + server_variable_names++; + } + php_info_print_table_end(); +} + + +static zend_module_entry php_isapi_module = { + STANDARD_MODULE_HEADER, + "ISAPI", + NULL, + NULL, + NULL, + NULL, + NULL, + php_info_isapi, + NULL, + STANDARD_MODULE_PROPERTIES +}; + + +static int sapi_isapi_ub_write(const char *str, uint str_length) +{ + DWORD num_bytes = str_length; + LPEXTENSION_CONTROL_BLOCK ecb; + + ecb = (LPEXTENSION_CONTROL_BLOCK) SG(server_context); + if (ecb->WriteClient(ecb->ConnID, (char *) str, &num_bytes, HSE_IO_SYNC) == FALSE) { + php_handle_aborted_connection(); + } + return num_bytes; +} + + +static int sapi_isapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers) +{ + return SAPI_HEADER_ADD; +} + + + +static void accumulate_header_length(sapi_header_struct *sapi_header, uint *total_length) +{ + *total_length += sapi_header->header_len+2; +} + + +static void concat_header(sapi_header_struct *sapi_header, char **combined_headers_ptr) +{ + memcpy(*combined_headers_ptr, sapi_header->header, sapi_header->header_len); + *combined_headers_ptr += sapi_header->header_len; + **combined_headers_ptr = '\r'; + (*combined_headers_ptr)++; + **combined_headers_ptr = '\n'; + (*combined_headers_ptr)++; +} + + +static int sapi_isapi_send_headers(sapi_headers_struct *sapi_headers) +{ + uint total_length = 2; /* account for the trailing \r\n */ + char *combined_headers, *combined_headers_ptr; + LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context); + HSE_SEND_HEADER_EX_INFO header_info; + sapi_header_struct default_content_type; + char *status_buf = NULL; + + /* Obtain headers length */ + if (SG(sapi_headers).send_default_content_type) { + sapi_get_default_content_type_header(&default_content_type); + accumulate_header_length(&default_content_type, (void *) &total_length); + } + zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) accumulate_header_length, (void *) &total_length); + + /* Generate headers */ + combined_headers = (char *) emalloc(total_length+1); + combined_headers_ptr = combined_headers; + if (SG(sapi_headers).send_default_content_type) { + concat_header(&default_content_type, (void *) &combined_headers_ptr); + sapi_free_header(&default_content_type); /* we no longer need it */ + } + zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) concat_header, (void *) &combined_headers_ptr); + *combined_headers_ptr++ = '\r'; + *combined_headers_ptr++ = '\n'; + *combined_headers_ptr = 0; + + switch (SG(sapi_headers).http_response_code) { + case 200: + header_info.pszStatus = "200 OK"; + break; + case 302: + header_info.pszStatus = "302 Moved Temporarily"; + break; + case 401: + header_info.pszStatus = "401 Authorization Required"; + break; + default: { + const char *sline = SG(sapi_headers).http_status_line; + int sline_len; + + /* httpd requires that r->status_line is set to the first digit of + * the status-code: */ + if (sline && ((sline_len = strlen(sline)) > 12) && strncmp(sline, "HTTP/1.", 7) == 0 && sline[8] == ' ') { + if ((sline_len - 9) > MAX_STATUS_LENGTH) { + status_buf = estrndup(sline + 9, MAX_STATUS_LENGTH); + } else { + status_buf = estrndup(sline + 9, sline_len - 9); + } + } else { + status_buf = emalloc(MAX_STATUS_LENGTH + 1); + snprintf(status_buf, MAX_STATUS_LENGTH, "%d Undescribed", SG(sapi_headers).http_response_code); + } + header_info.pszStatus = status_buf; + break; + } + } + header_info.cchStatus = strlen(header_info.pszStatus); + header_info.pszHeader = combined_headers; + header_info.cchHeader = total_length; + header_info.fKeepConn = FALSE; + lpECB->dwHttpStatusCode = SG(sapi_headers).http_response_code; + + lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &header_info, NULL, NULL); + + efree(combined_headers); + if (status_buf) { + efree(status_buf); + } + return SAPI_HEADER_SENT_SUCCESSFULLY; +} + + +static int php_isapi_startup(sapi_module_struct *sapi_module) +{ + if (php_module_startup(sapi_module, &php_isapi_module, 1)==FAILURE) { + return FAILURE; + } else { + bTerminateThreadsOnError = (zend_bool) INI_INT("isapi.terminate_threads_on_error"); + return SUCCESS; + } +} + + +static int sapi_isapi_read_post(char *buffer, uint count_bytes) +{ + LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context); + DWORD read_from_buf=0; + DWORD read_from_input=0; + DWORD total_read=0; + + if ((DWORD) SG(read_post_bytes) < lpECB->cbAvailable) { + read_from_buf = MIN(lpECB->cbAvailable-SG(read_post_bytes), count_bytes); + memcpy(buffer, lpECB->lpbData+SG(read_post_bytes), read_from_buf); + total_read += read_from_buf; + } + if (read_from_bufcbTotalBytes) { + DWORD cbRead=0, cbSize; + + read_from_input = MIN(count_bytes-read_from_buf, lpECB->cbTotalBytes-SG(read_post_bytes)-read_from_buf); + while (cbRead < read_from_input) { + cbSize = read_from_input - cbRead; + if (!lpECB->ReadClient(lpECB->ConnID, buffer+read_from_buf+cbRead, &cbSize) || cbSize==0) { + break; + } + cbRead += cbSize; + } + total_read += cbRead; + } + return total_read; +} + + +static char *sapi_isapi_read_cookies(void) +{ + LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context); + char variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; + DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + + if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_COOKIE", variable_buf, &variable_len)) { + return estrndup(variable_buf, variable_len); + } else if (GetLastError()==ERROR_INSUFFICIENT_BUFFER) { + char *tmp_variable_buf = (char *) emalloc(variable_len+1); + + if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_COOKIE", tmp_variable_buf, &variable_len)) { + tmp_variable_buf[variable_len] = 0; + return tmp_variable_buf; + } else { + efree(tmp_variable_buf); + } + } + return STR_EMPTY_ALLOC(); +} + + +#ifdef WITH_ZEUS + +static void sapi_isapi_register_zeus_ssl_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array) +{ + char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; + DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + char static_cons_buf[ISAPI_SERVER_VAR_BUF_SIZE]; + /* + * We need to construct the /C=.../ST=... + * DN's for SSL_CLIENT_DN and SSL_CLIENT_I_DN + */ + strcpy( static_cons_buf, "/C=" ); + if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) { + strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE); + } + strlcat( static_cons_buf, "/ST=", ISAPI_SERVER_VAR_BUF_SIZE); + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) { + strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE ); + } + php_register_variable( "SSL_CLIENT_DN", static_cons_buf, track_vars_array ); + + strcpy( static_cons_buf, "/C=" ); + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) { + strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE ); + } + strlcat( static_cons_buf, "/ST=", ISAPI_SERVER_VAR_BUF_SIZE); + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) { + strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE ); + } + php_register_variable( "SSL_CLIENT_I_DN", static_cons_buf, track_vars_array ); +} + +static void sapi_isapi_register_zeus_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array) +{ + char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; + DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE; + DWORD pathinfo_len = 0; + char *strtok_buf = NULL; + + /* Get SCRIPT_NAME, we use this to work out which bit of the URL + * belongs in PHP's version of PATH_INFO + */ + lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len); + + /* Adjust Zeus' version of PATH_INFO, set PHP_SELF, + * and generate REQUEST_URI + */ + if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) { + + /* PHP_SELF is just PATH_INFO */ + php_register_variable( "PHP_SELF", static_variable_buf, track_vars_array ); + + /* Chop off filename to get just the 'real' PATH_INFO' */ + pathinfo_len = variable_len - scriptname_len; + php_register_variable( "PATH_INFO", static_variable_buf + scriptname_len - 1, track_vars_array ); + /* append query string to give url... extra byte for '?' */ + if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) { + /* append query string only if it is present... */ + if ( strlen(lpECB->lpszQueryString) ) { + static_variable_buf[ variable_len - 1 ] = '?'; + strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString ); + } + php_register_variable( "URL", static_variable_buf, track_vars_array ); + php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array ); + } + } + + /* Get and adjust PATH_TRANSLATED to what PHP wants */ + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) { + static_variable_buf[ variable_len - pathinfo_len - 1 ] = '\0'; + php_register_variable( "PATH_TRANSLATED", static_variable_buf, track_vars_array ); + } + + /* Bring in the AUTHENTICATION stuff as needed */ + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_USER", static_variable_buf, &variable_len) && static_variable_buf[0] ) { + php_register_variable( "PHP_AUTH_USER", static_variable_buf, track_vars_array ); + } + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_PASSWORD", static_variable_buf, &variable_len) && static_variable_buf[0] ) { + php_register_variable( "PHP_AUTH_PW", static_variable_buf, track_vars_array ); + } + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_TYPE", static_variable_buf, &variable_len) && static_variable_buf[0] ) { + php_register_variable( "AUTH_TYPE", static_variable_buf, track_vars_array ); + } + + /* And now, for the SSL variables (if applicable) */ + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if ( lpECB->GetServerVariable(lpECB->ConnID, "CERT_COOKIE", static_variable_buf, &variable_len) && static_variable_buf[0] ) { + sapi_isapi_register_zeus_ssl_variables( lpECB, track_vars_array ); + } + /* Copy some of the variables we need to meet Apache specs */ + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if ( lpECB->GetServerVariable(lpECB->ConnID, "SERVER_SOFTWARE", static_variable_buf, &variable_len) && static_variable_buf[0] ) { + php_register_variable( "SERVER_SIGNATURE", static_variable_buf, track_vars_array ); + } +} +#else + +static void sapi_isapi_register_iis_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array) +{ + char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; + char path_info_buf[ISAPI_SERVER_VAR_BUF_SIZE]; + DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE; + DWORD pathinfo_len = 0; + HSE_URL_MAPEX_INFO humi; + + /* Get SCRIPT_NAME, we use this to work out which bit of the URL + * belongs in PHP's version of PATH_INFO. SCRIPT_NAME also becomes PHP_SELF. + */ + lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len); + php_register_variable("SCRIPT_FILENAME", SG(request_info).path_translated, track_vars_array); + + /* Adjust IIS' version of PATH_INFO, set PHP_SELF, + * and generate REQUEST_URI + * Get and adjust PATH_TRANSLATED to what PHP wants + */ + if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) { + + /* Chop off filename to get just the 'real' PATH_INFO' */ + php_register_variable( "ORIG_PATH_INFO", static_variable_buf, track_vars_array ); + pathinfo_len = variable_len - scriptname_len; + strncpy(path_info_buf, static_variable_buf + scriptname_len - 1, sizeof(path_info_buf)-1); + php_register_variable( "PATH_INFO", path_info_buf, track_vars_array ); + /* append query string to give url... extra byte for '?' */ + if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) { + /* append query string only if it is present... */ + if ( strlen(lpECB->lpszQueryString) ) { + static_variable_buf[ variable_len - 1 ] = '?'; + strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString ); + } + php_register_variable( "URL", static_variable_buf, track_vars_array ); + php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array ); + } + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) { + php_register_variable( "ORIG_PATH_TRANSLATED", static_variable_buf, track_vars_array ); + } + if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, path_info_buf, &pathinfo_len, (LPDWORD) &humi)) { + /* Remove trailing \ */ + if (humi.lpszPath[variable_len-2] == '\\') { + humi.lpszPath[variable_len-2] = 0; + } + php_register_variable("PATH_TRANSLATED", humi.lpszPath, track_vars_array); + } + } + + static_variable_buf[0] = '/'; + static_variable_buf[1] = 0; + variable_len = 2; + if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) { + /* Remove trailing \ */ + if (humi.lpszPath[variable_len-2] == '\\') { + humi.lpszPath[variable_len-2] = 0; + } + php_register_variable("DOCUMENT_ROOT", humi.lpszPath, track_vars_array); + } + + if (!SG(request_info).auth_user || !SG(request_info).auth_password || + !SG(request_info).auth_user[0] || !SG(request_info).auth_password[0]) { + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_AUTHORIZATION", static_variable_buf, &variable_len) + && static_variable_buf[0]) { + php_handle_auth_data(static_variable_buf); + } + } + + if (SG(request_info).auth_user) { + php_register_variable("PHP_AUTH_USER", SG(request_info).auth_user, track_vars_array ); + } + if (SG(request_info).auth_password) { + php_register_variable("PHP_AUTH_PW", SG(request_info).auth_password, track_vars_array ); + } +} +#endif + +static void sapi_isapi_register_server_variables2(char **server_variables, LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array, char **recorded_values) +{ + char **p=server_variables; + DWORD variable_len; + char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; + char *variable_buf; + + while (*p) { + variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + if (lpECB->GetServerVariable(lpECB->ConnID, *p, static_variable_buf, &variable_len) + && static_variable_buf[0]) { + php_register_variable(*p, static_variable_buf, track_vars_array); + if (recorded_values) { + recorded_values[p-server_variables] = estrndup(static_variable_buf, variable_len); + } + } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + variable_buf = (char *) emalloc(variable_len+1); + if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len) + && variable_buf[0]) { + php_register_variable(*p, variable_buf, track_vars_array); + } + if (recorded_values) { + recorded_values[p-server_variables] = variable_buf; + } else { + efree(variable_buf); + } + } else { /* for compatibility with Apache SAPIs */ + php_register_variable(*p, "", track_vars_array); + } + p++; + } +} + + +static void sapi_isapi_register_server_variables(zval *track_vars_array) +{ + DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + char *variable; + char *strtok_buf = NULL; + char *isapi_special_server_variables[NUM_SPECIAL_VARS]; + LPEXTENSION_CONTROL_BLOCK lpECB; + + lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context); + + /* Register the special ISAPI variables */ + memset(isapi_special_server_variables, 0, sizeof(isapi_special_server_variables)); + sapi_isapi_register_server_variables2(isapi_special_server_variable_names, lpECB, track_vars_array, isapi_special_server_variables); + if (SG(request_info).cookie_data) { + php_register_variable("HTTP_COOKIE", SG(request_info).cookie_data, track_vars_array); + } + + /* Register the standard ISAPI variables */ + sapi_isapi_register_server_variables2(isapi_server_variable_names, lpECB, track_vars_array, NULL); + + if (isapi_special_server_variables[SPECIAL_VAR_HTTPS] + && (atoi(isapi_special_server_variables[SPECIAL_VAR_HTTPS]) + || !strcasecmp(isapi_special_server_variables[SPECIAL_VAR_HTTPS], "on")) + ) { + /* Register SSL ISAPI variables */ + sapi_isapi_register_server_variables2(isapi_secure_server_variable_names, lpECB, track_vars_array, NULL); + } + + if (isapi_special_server_variables[SPECIAL_VAR_HTTPS]) { + efree(isapi_special_server_variables[SPECIAL_VAR_HTTPS]); + } + + +#ifdef WITH_ZEUS + sapi_isapi_register_zeus_variables(lpECB, track_vars_array); +#else + sapi_isapi_register_iis_variables(lpECB, track_vars_array); +#endif + + /* PHP_SELF support */ + if (isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]) { + php_register_variable("PHP_SELF", isapi_special_server_variables[SPECIAL_VAR_PHP_SELF], track_vars_array); + efree(isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]); + } + + if (isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP]) { + /* Register the internal bits of ALL_HTTP */ + variable = php_strtok_r(isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP], "\r\n", &strtok_buf); + while (variable) { + char *colon = strchr(variable, ':'); + + if (colon) { + char *value = colon+1; + + while (*value==' ') { + value++; + } + *colon = 0; + php_register_variable(variable, value, track_vars_array); + *colon = ':'; + } + variable = php_strtok_r(NULL, "\r\n", &strtok_buf); + } + efree(isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP]); + } +} + + +static sapi_module_struct isapi_sapi_module = { + "isapi", /* name */ + "ISAPI", /* pretty name */ + + php_isapi_startup, /* startup */ + php_module_shutdown_wrapper, /* shutdown */ + + NULL, /* activate */ + NULL, /* deactivate */ + + sapi_isapi_ub_write, /* unbuffered write */ + NULL, /* flush */ + NULL, /* get uid */ + NULL, /* getenv */ + + php_error, /* error handler */ + + sapi_isapi_header_handler, /* header handler */ + sapi_isapi_send_headers, /* send headers handler */ + NULL, /* send header handler */ + + sapi_isapi_read_post, /* read POST data */ + sapi_isapi_read_cookies, /* read Cookies */ + + sapi_isapi_register_server_variables, /* register server variables */ + NULL, /* Log message */ + NULL, /* Get request time */ + NULL, /* Child terminate */ + + STANDARD_SAPI_MODULE_PROPERTIES +}; + + +BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pFilterVersion) +{ + bFilterLoaded = 1; + pFilterVersion->dwFilterVersion = HTTP_FILTER_REVISION; + strcpy(pFilterVersion->lpszFilterDesc, isapi_sapi_module.pretty_name); + pFilterVersion->dwFlags= (SF_NOTIFY_AUTHENTICATION | SF_NOTIFY_PREPROC_HEADERS); + return TRUE; +} + + +DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification) +{ + + switch (notificationType) { + case SF_NOTIFY_PREPROC_HEADERS: + SG(request_info).auth_user = NULL; + SG(request_info).auth_password = NULL; + SG(request_info).auth_digest = NULL; + break; + case SF_NOTIFY_AUTHENTICATION: { + char *auth_user = ((HTTP_FILTER_AUTHENT *) pvNotification)->pszUser; + char *auth_password = ((HTTP_FILTER_AUTHENT *) pvNotification)->pszPassword; + + if (auth_user && auth_user[0]) { + SG(request_info).auth_user = estrdup(auth_user); + } + if (auth_password && auth_password[0]) { + SG(request_info).auth_password = estrdup(auth_password); + } + return SF_STATUS_REQ_HANDLED_NOTIFICATION; + } + break; + } + return SF_STATUS_REQ_NEXT_NOTIFICATION; +} + + +static void init_request_info(LPEXTENSION_CONTROL_BLOCK lpECB) +{ + DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE; + char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; +#ifndef WITH_ZEUS + HSE_URL_MAPEX_INFO humi; +#endif + + SG(request_info).request_method = lpECB->lpszMethod; + SG(request_info).query_string = lpECB->lpszQueryString; + SG(request_info).request_uri = lpECB->lpszPathInfo; + SG(request_info).content_type = lpECB->lpszContentType; + SG(request_info).content_length = lpECB->cbTotalBytes; + SG(sapi_headers).http_response_code = 200; /* I think dwHttpStatusCode is invalid at this stage -RL */ + if (!bFilterLoaded) { /* we don't have valid ISAPI Filter information */ + SG(request_info).auth_user = SG(request_info).auth_password = SG(request_info).auth_digest = NULL; + } + +#ifdef WITH_ZEUS + /* PATH_TRANSLATED can contain extra PATH_INFO stuff after the + * file being loaded, so we must use SCRIPT_FILENAME instead + */ + if(lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_FILENAME", static_variable_buf, &variable_len)) { + SG(request_info).path_translated = estrdup(static_variable_buf); + } else +#else + /* happily, IIS gives us SCRIPT_NAME which is correct (without PATH_INFO stuff) + so we can just map that to the physical path and we have our filename */ + + lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &variable_len); + if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) { + SG(request_info).path_translated = estrdup(humi.lpszPath); + } else +#endif + /* if mapping fails, default to what the server tells us */ + SG(request_info).path_translated = estrdup(lpECB->lpszPathTranslated); + + /* some server configurations allow '..' to slip through in the + translated path. We'll just refuse to handle such a path. */ + if (strstr(SG(request_info).path_translated,"..")) { + SG(sapi_headers).http_response_code = 404; + efree(SG(request_info).path_translated); + SG(request_info).path_translated = NULL; + } +} + + +static void php_isapi_report_exception(char *message, int message_len) +{ + if (!SG(headers_sent)) { + HSE_SEND_HEADER_EX_INFO header_info; + LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context); + + header_info.pszStatus = "500 Internal Server Error"; + header_info.cchStatus = strlen(header_info.pszStatus); + header_info.pszHeader = "Content-Type: text/html\r\n\r\n"; + header_info.cchHeader = strlen(header_info.pszHeader); + + lpECB->dwHttpStatusCode = 500; + lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &header_info, NULL, NULL); + SG(headers_sent)=1; + } + sapi_isapi_ub_write(message, message_len); +} + + +BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer) +{ + pVer->dwExtensionVersion = HSE_VERSION; +#ifdef WITH_ZEUS + strncpy( pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN); +#else + lstrcpyn(pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN); +#endif + return TRUE; +} + + +static void my_endthread() +{ +#ifdef PHP_WIN32 + if (bTerminateThreadsOnError) { + _endthread(); + } +#endif +} + +#ifdef PHP_WIN32 +/* ep is accessible only in the context of the __except expression, + * so we have to call this function to obtain it. + */ +BOOL exceptionhandler(LPEXCEPTION_POINTERS *e, LPEXCEPTION_POINTERS ep) +{ + *e=ep; + return TRUE; +} +#endif + +DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) +{ + zend_file_handle file_handle; + zend_bool stack_overflown=0; + int retval = FAILURE; +#ifdef PHP_ENABLE_SEH + LPEXCEPTION_POINTERS e; +#endif + + zend_first_try { +#ifdef PHP_ENABLE_SEH + __try { +#endif + init_request_info(lpECB); + SG(server_context) = lpECB; + + php_request_startup(); + + file_handle.filename = SG(request_info).path_translated; + file_handle.free_filename = 0; + file_handle.type = ZEND_HANDLE_FILENAME; + file_handle.opened_path = NULL; + + /* open the script here so we can 404 if it fails */ + if (file_handle.filename) + retval = php_fopen_primary_script(&file_handle); + + if (!file_handle.filename || retval == FAILURE) { + SG(sapi_headers).http_response_code = 404; + PUTS("No input file specified.\n"); + } else { + php_execute_script(&file_handle); + } + + if (SG(request_info).cookie_data) { + efree(SG(request_info).cookie_data); + } + if (SG(request_info).path_translated) + efree(SG(request_info).path_translated); +#ifdef PHP_ENABLE_SEH + } __except(exceptionhandler(&e, GetExceptionInformation())) { + char buf[1024]; + if (_exception_code()==EXCEPTION_STACK_OVERFLOW) { + LPBYTE lpPage; + static SYSTEM_INFO si; + static MEMORY_BASIC_INFORMATION mi; + static DWORD dwOldProtect; + + GetSystemInfo(&si); + + /* Get page ESP is pointing to */ + _asm mov lpPage, esp; + + /* Get stack allocation base */ + VirtualQuery(lpPage, &mi, sizeof(mi)); + + /* Go to the page below the current page */ + lpPage = (LPBYTE) (mi.BaseAddress) - si.dwPageSize; + + /* Free pages below current page */ + if (!VirtualFree(mi.AllocationBase, (LPBYTE)lpPage - (LPBYTE) mi.AllocationBase, MEM_DECOMMIT)) { + _endthread(); + } + + /* Restore the guard page */ + if (!VirtualProtect(lpPage, si.dwPageSize, PAGE_GUARD | PAGE_READWRITE, &dwOldProtect)) { + _endthread(); + } + + CG(unclean_shutdown)=1; + _snprintf(buf, sizeof(buf)-1,"PHP has encountered a Stack overflow"); + php_isapi_report_exception(buf, strlen(buf)); + } else if (_exception_code()==EXCEPTION_ACCESS_VIOLATION) { + _snprintf(buf, sizeof(buf)-1,"PHP has encountered an Access Violation at %p", e->ExceptionRecord->ExceptionAddress); + php_isapi_report_exception(buf, strlen(buf)); + my_endthread(); + } else { + _snprintf(buf, sizeof(buf)-1,"PHP has encountered an Unhandled Exception Code %d at %p", e->ExceptionRecord->ExceptionCode , e->ExceptionRecord->ExceptionAddress); + php_isapi_report_exception(buf, strlen(buf)); + my_endthread(); + } + } +#endif +#ifdef PHP_ENABLE_SEH + __try { + php_request_shutdown(NULL); + } __except(EXCEPTION_EXECUTE_HANDLER) { + my_endthread(); + } +#else + php_request_shutdown(NULL); +#endif + } zend_catch { + zend_try { + php_request_shutdown(NULL); + } zend_end_try(); + return HSE_STATUS_ERROR; + } zend_end_try(); + + return HSE_STATUS_SUCCESS; +} + + + +__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) { + case DLL_PROCESS_ATTACH: +#ifdef WITH_ZEUS + tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "TSRM.log"); +#else + tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "C:\\TSRM.log"); +#endif + sapi_startup(&isapi_sapi_module); + if (isapi_sapi_module.startup) { + isapi_sapi_module.startup(&sapi_module); + } + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + ts_free_thread(); + break; + case DLL_PROCESS_DETACH: + if (isapi_sapi_module.shutdown) { + isapi_sapi_module.shutdown(&sapi_module); + } + sapi_shutdown(); + tsrm_shutdown(); + break; + } + return TRUE; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/sapi/isapi/php7isapi.def b/sapi/isapi/php7isapi.def new file mode 100644 index 0000000000000..596023ef55489 --- /dev/null +++ b/sapi/isapi/php7isapi.def @@ -0,0 +1,5 @@ +EXPORTS +HttpFilterProc +GetFilterVersion +HttpExtensionProc +GetExtensionVersion diff --git a/sapi/isapi/stresstest/getopt.c b/sapi/isapi/stresstest/getopt.c new file mode 100644 index 0000000000000..21056bf883f6a --- /dev/null +++ b/sapi/isapi/stresstest/getopt.c @@ -0,0 +1,175 @@ +/* Borrowed from Apache NT Port */ + +#include +#include +#include +#include +#include "getopt.h" +#define OPTERRCOLON (1) +#define OPTERRNF (2) +#define OPTERRARG (3) + + +char *ap_optarg; +int ap_optind = 1; +static int ap_opterr = 1; +static int ap_optopt; + +static int +ap_optiserr(int argc, char * const *argv, int oint, const char *optstr, + int optchr, int err) +{ + if (ap_opterr) + { + fprintf(stderr, "Error in argument %d, char %d: ", oint, optchr+1); + switch(err) + { + case OPTERRCOLON: + fprintf(stderr, ": in flags\n"); + break; + case OPTERRNF: + fprintf(stderr, "option not found %c\n", argv[oint][optchr]); + break; + case OPTERRARG: + fprintf(stderr, "no argument for option %c\n", argv[oint][optchr]); + break; + default: + fprintf(stderr, "unknown\n"); + break; + } + } + ap_optopt = argv[oint][optchr]; + return('?'); +} + +int ap_getopt(int argc, char* const *argv, const char *optstr) +{ + static int optchr = 0; + static int dash = 0; /* have already seen the - */ + + char *cp; + + if (ap_optind >= argc) + return(EOF); + if (!dash && (argv[ap_optind][0] != '-')) + return(EOF); + if (!dash && (argv[ap_optind][0] == '-') && !argv[ap_optind][1]) + { + /* + * use to specify stdin. Need to let pgm process this and + * the following args + */ + return(EOF); + } + if ((argv[ap_optind][0] == '-') && (argv[ap_optind][1] == '-')) + { + /* -- indicates end of args */ + ap_optind++; + return(EOF); + } + if (!dash) + { + assert((argv[ap_optind][0] == '-') && argv[ap_optind][1]); + dash = 1; + optchr = 1; + } + + /* Check if the guy tries to do a -: kind of flag */ + assert(dash); + if (argv[ap_optind][optchr] == ':') + { + dash = 0; + ap_optind++; + return(ap_optiserr(argc, argv, ap_optind-1, optstr, optchr, OPTERRCOLON)); + } + if (!(cp = strchr(optstr, argv[ap_optind][optchr]))) + { + int errind = ap_optind; + int errchr = optchr; + + if (!argv[ap_optind][optchr+1]) + { + dash = 0; + ap_optind++; + } + else + optchr++; + return(ap_optiserr(argc, argv, errind, optstr, errchr, OPTERRNF)); + } + if (cp[1] == ':') + { + /* Check for cases where the value of the argument + is in the form - or in the form - */ + dash = 0; + if(!argv[ap_optind][2]) { + ap_optind++; + if (ap_optind == argc) + return(ap_optiserr(argc, argv, ap_optind-1, optstr, optchr, OPTERRARG)); + ap_optarg = argv[ap_optind++]; + } + else + { + ap_optarg = &argv[ap_optind][2]; + ap_optind++; + } + return(*cp); + } + else + { + if (!argv[ap_optind][optchr+1]) + { + dash = 0; + ap_optind++; + } + else + optchr++; + return(*cp); + } + assert(0); + return(0); +} + +#ifdef TESTGETOPT +int + main (int argc, char **argv) + { + int c; + extern char *ap_optarg; + extern int ap_optind; + int aflg = 0; + int bflg = 0; + int errflg = 0; + char *ofile = NULL; + + while ((c = ap_getopt(argc, argv, "abo:")) != EOF) + switch (c) { + case 'a': + if (bflg) + errflg++; + else + aflg++; + break; + case 'b': + if (aflg) + errflg++; + else + bflg++; + break; + case 'o': + ofile = ap_optarg; + (void)printf("ofile = %s\n", ofile); + break; + case '?': + errflg++; + } + if (errflg) { + (void)fprintf(stderr, + "usage: cmd [-a|-b] [-o ] files...\n"); + exit (2); + } + for ( ; ap_optind < argc; ap_optind++) + (void)printf("%s\n", argv[ap_optind]); + return 0; + } + +#endif /* TESTGETOPT */ diff --git a/sapi/isapi/stresstest/getopt.h b/sapi/isapi/stresstest/getopt.h new file mode 100644 index 0000000000000..6d4139bfe0dca --- /dev/null +++ b/sapi/isapi/stresstest/getopt.h @@ -0,0 +1,12 @@ +/* Borrowed from Apache NT Port */ +#ifdef __cplusplus +extern "C" { +#endif +extern char *ap_optarg; +extern int ap_optind; + +int ap_getopt(int argc, char* const *argv, const char *optstr); + +#ifdef __cplusplus +} +#endif diff --git a/sapi/isapi/stresstest/notes.txt b/sapi/isapi/stresstest/notes.txt new file mode 100644 index 0000000000000..e6d10dd486a08 --- /dev/null +++ b/sapi/isapi/stresstest/notes.txt @@ -0,0 +1,56 @@ +This stress test program is for debugging threading issues with the ISAPI +module. + +2 ways to use it: + +1: test any php script file on multiple threads +2: run the php test scripts bundled with the source code + + + +GLOBAL SETTINGS +=============== + +If you need to set special environement variables, in addition to your +regular environment, create a file that contains them, one setting per line: + +MY_ENV_VAR=XXXXXXXX + +This can be used to simulate ISAPI environment variables if need be. + +By default, stress test uses 10 threads. To change this, change the define +NUM_THREADS in stresstest.cpp. + + + +1: Test any php script file on multiple threads +=============================================== + +Create a file that contains a list of php script files, one per line. If +you need to provide input, place the GET data, or Query String, after the +filename. File contents would look like: + +e:\inetpub\pages\index.php +e:\inetpub\pages\info.php +e:\inetpub\pages\test.php a=1&b=2 + +Run: stresstest L files.txt + + + +2: Run the php test scripts bundled with the source code +======================================================== + +supply the path to the parent of the "tests" directory (expect a couple +long pauses for a couple of the larger tests) + +Run: stresstest T c:\php7-source + + + +TODO: + +* Make more options configurable: number of threads, iterations, etc. +* Improve stdout output to make it more useful +* Implement support for SKIPIF +* Improve speed of CompareFile function (too slow on big files). diff --git a/sapi/isapi/stresstest/stresstest.cpp b/sapi/isapi/stresstest/stresstest.cpp new file mode 100644 index 0000000000000..ddb06473c2d71 --- /dev/null +++ b/sapi/isapi/stresstest/stresstest.cpp @@ -0,0 +1,936 @@ +/* + * ======================================================================= * + * File: stress .c * + * stress tester for isapi dll's * + * based on cgiwrap * + * ======================================================================= * + * +*/ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include "getopt.h" + +// These are things that go out in the Response Header +// +#define HTTP_VER "HTTP/1.0" +#define SERVER_VERSION "Http-Srv-Beta2/1.0" + +// +// Simple wrappers for the heap APIS +// +#define xmalloc(s) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (s)) +#define xfree(s) HeapFree(GetProcessHeap(), 0, (s)) + +// +// The mandatory exports from the ISAPI DLL +// +DWORD numThreads = 1; +DWORD iterations = 1; + +HANDLE StartNow; +// quick and dirty environment +typedef CMapStringToString TEnvironment; +TEnvironment IsapiEnvironment; + +typedef struct _TResults { + LONG ok; + LONG bad; +} TResults; + +CStringArray IsapiFileList; // list of filenames +CStringArray TestNames; // --TEST-- +CStringArray IsapiGetData; // --GET-- +CStringArray IsapiPostData; // --POST-- +CStringArray IsapiMatchData; // --EXPECT-- +CArray Results; + +typedef struct _TIsapiContext { + HANDLE in; + HANDLE out; + DWORD tid; + TEnvironment env; + HANDLE waitEvent; +} TIsapiContext; + +// +// Prototypes of the functions this sample implements +// +extern "C" { +HINSTANCE hDll; +typedef BOOL (WINAPI *VersionProc)(HSE_VERSION_INFO *) ; +typedef DWORD (WINAPI *HttpExtProc)(EXTENSION_CONTROL_BLOCK *); +typedef BOOL (WINAPI *TerminateProc) (DWORD); +BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *, TIsapiContext *) ; +BOOL WINAPI GetServerVariable(HCONN, LPSTR, LPVOID, LPDWORD ); +BOOL WINAPI ReadClient(HCONN, LPVOID, LPDWORD); +BOOL WINAPI WriteClient(HCONN, LPVOID, LPDWORD, DWORD); +BOOL WINAPI ServerSupportFunction(HCONN, DWORD, LPVOID, LPDWORD, LPDWORD); +VersionProc IsapiGetExtensionVersion; +HttpExtProc IsapiHttpExtensionProc; +TerminateProc TerminateExtensionProc; +HSE_VERSION_INFO version_info; +} + +char * MakeDateStr(VOID); +char * GetEnv(char *); + + + + +DWORD CALLBACK IsapiThread(void *); +int stress_main(const char *filename, + const char *arg, + const char *postfile, + const char *matchdata); + + + +BOOL bUseTestFiles = FALSE; +char temppath[MAX_PATH]; + +void stripcrlf(char *line) +{ + DWORD l = strlen(line)-1; + if (line[l]==10 || line[l]==13) line[l]=0; + l = strlen(line)-1; + if (line[l]==10 || line[l]==13) line[l]=0; +} + +#define COMPARE_BUF_SIZE 1024 + +BOOL CompareFiles(const char*f1, const char*f2) +{ + FILE *fp1, *fp2; + bool retval; + char buf1[COMPARE_BUF_SIZE], buf2[COMPARE_BUF_SIZE]; + int length1, length2; + + if ((fp1=fopen(f1, "r"))==NULL) { + return FALSE; + } + + if ((fp2=fopen(f2, "r"))==NULL) { + fclose(fp1); + return FALSE; + } + + retval = TRUE; // success oriented + while (true) { + length1 = fread(buf1, 1, sizeof(buf1), fp1); + length2 = fread(buf2, 1, sizeof(buf2), fp2); + + // check for end of file + if (feof(fp1)) { + if (!feof(fp2)) { + retval = FALSE; + } + break; + } else if (feof(fp2)) { + if (!feof(fp1)) { + retval = FALSE; + } + break; + } + + // compare data + if (length1!=length2 + || memcmp(buf1, buf2, length1)!=0) { + retval = FALSE; + break; + } + } + fclose(fp1); + fclose(fp2); + + return retval; +} + + +BOOL CompareStringWithFile(const char *filename, const char *str, unsigned int str_length) +{ + FILE *fp; + bool retval; + char buf[COMPARE_BUF_SIZE]; + unsigned int offset=0, readbytes; + fprintf(stderr, "test %s\n",filename); + if ((fp=fopen(filename, "rb"))==NULL) { + fprintf(stderr, "Error opening %s\n",filename); + return FALSE; + } + + retval = TRUE; // success oriented + while (true) { + readbytes = fread(buf, 1, sizeof(buf), fp); + + // check for end of file + + if (offset+readbytes > str_length + || memcmp(buf, str+offset, readbytes)!=NULL) { + fprintf(stderr, "File missmatch %s\n",filename); + retval = FALSE; + break; + } + if (feof(fp)) { + if (!retval) fprintf(stderr, "File zero length %s\n",filename); + break; + } + } + fclose(fp); + + return retval; +} + + +BOOL ReadGlobalEnvironment(const char *environment) +{ + if (environment) { + FILE *fp = fopen(environment, "r"); + DWORD i=0; + if (fp) { + char line[2048]; + while (fgets(line, sizeof(line)-1, fp)) { + // file.php arg1 arg2 etc. + char *p = strchr(line, '='); + if (p) { + *p=0; + IsapiEnvironment[line]=p+1; + } + } + fclose(fp); + return IsapiEnvironment.GetCount() > 0; + } + } + return FALSE; +} + +BOOL ReadFileList(const char *filelist) +{ + FILE *fp = fopen(filelist, "r"); + if (!fp) { + printf("Unable to open %s\r\n", filelist); + } + char line[2048]; + int i=0; + while (fgets(line, sizeof(line)-1, fp)) { + // file.php arg1 arg2 etc. + stripcrlf(line); + if (strlen(line)>3) { + char *p = strchr(line, ' '); + if (p) { + *p = 0; + // get file + + IsapiFileList.Add(line); + IsapiGetData.Add(p+1); + } else { + // just a filename is all + IsapiFileList.Add(line); + IsapiGetData.Add(""); + } + } + + // future use + IsapiPostData.Add(""); + IsapiMatchData.Add(""); + TestNames.Add(""); + + i++; + } + Results.SetSize(TestNames.GetSize()); + + fclose(fp); + return IsapiFileList.GetSize() > 0; +} + +void DoThreads() { + + if (IsapiFileList.GetSize() == 0) { + printf("No Files to test\n"); + return; + } + + printf("Starting Threads...\n"); + // loop creating threads + DWORD tid; + HANDLE *threads = new HANDLE[numThreads]; + DWORD i; + for (i=0; i< numThreads; i++) { + threads[i]=CreateThread(NULL, 0, IsapiThread, NULL, CREATE_SUSPENDED, &tid); + } + for (i=0; i< numThreads; i++) { + if (threads[i]) ResumeThread(threads[i]); + } + // wait for threads to finish + WaitForMultipleObjects(numThreads, threads, TRUE, INFINITE); + for (i=0; i< numThreads; i++) { + CloseHandle(threads[i]); + } + delete [] threads; +} + +void DoFileList(const char *filelist, const char *environment) +{ + // read config files + + if (!ReadFileList(filelist)) { + printf("No Files to test!\r\n"); + return; + } + + ReadGlobalEnvironment(environment); + + DoThreads(); +} + + +/** + * ParseTestFile + * parse a single phpt file and add it to the arrays + */ +BOOL ParseTestFile(const char *path, const char *fn) +{ + // parse the test file + char filename[MAX_PATH]; + _snprintf(filename, sizeof(filename)-1, "%s\\%s", path, fn); + char line[1024]; + memset(line, 0, sizeof(line)); + CString cTest, cSkipIf, cPost, cGet, cFile, cExpect; + printf("Reading %s\r\n", filename); + + enum state {none, test, skipif, post, get, file, expect} parsestate = none; + + FILE *fp = fopen(filename, "rb"); + char *tn = _tempnam(temppath,"pht."); + char *en = _tempnam(temppath,"exp."); + FILE *ft = fopen(tn, "wb+"); + FILE *fe = fopen(en, "wb+"); + if (fp && ft && fe) { + while (fgets(line, sizeof(line)-1, fp)) { + if (line[0]=='-') { + if (_strnicmp(line, "--TEST--", 8)==0) { + parsestate = test; + continue; + } else if (_strnicmp(line, "--SKIPIF--", 10)==0) { + parsestate = skipif; + continue; + } else if (_strnicmp(line, "--POST--", 8)==0) { + parsestate = post; + continue; + } else if (_strnicmp(line, "--GET--", 7)==0) { + parsestate = get; + continue; + } else if (_strnicmp(line, "--FILE--", 8)==0) { + parsestate = file; + continue; + } else if (_strnicmp(line, "--EXPECT--", 10)==0) { + parsestate = expect; + continue; + } + } + switch (parsestate) { + case test: + stripcrlf(line); + cTest = line; + break; + case skipif: + cSkipIf += line; + break; + case post: + cPost += line; + break; + case get: + cGet += line; + break; + case file: + fputs(line, ft); + break; + case expect: + fputs(line, fe); + break; + } + } + + fclose(fp); + fclose(ft); + fclose(fe); + + if (!cTest.IsEmpty()) { + IsapiFileList.Add(tn); + TestNames.Add(cTest); + IsapiGetData.Add(cGet); + IsapiPostData.Add(cPost); + IsapiMatchData.Add(en); + free(tn); + free(en); + return TRUE; + } + } + free(tn); + free(en); + return FALSE; +} + + +/** + * GetTestFiles + * Recurse through the path and subdirectories, parse each phpt file + */ +BOOL GetTestFiles(const char *path) +{ + // find all files .phpt under testpath\tests + char FindPath[MAX_PATH]; + WIN32_FIND_DATA fd; + memset(&fd, 0, sizeof(WIN32_FIND_DATA)); + + _snprintf(FindPath, sizeof(FindPath)-1, "%s\\*.*", path); + HANDLE fh = FindFirstFile(FindPath, &fd); + if (fh != INVALID_HANDLE_VALUE) { + do { + if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + !strchr(fd.cFileName, '.')) { + // subdirectory, recurse into it + char NewFindPath[MAX_PATH]; + _snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s", path, fd.cFileName); + GetTestFiles(NewFindPath); + } else if (strstr(fd.cFileName, ".phpt")) { + // got test file, parse it now + if (ParseTestFile(path, fd.cFileName)) { + printf("Test File Added: %s\\%s\r\n", path, fd.cFileName); + } + } + memset(&fd, 0, sizeof(WIN32_FIND_DATA)); + } while (FindNextFile(fh, &fd) != 0); + FindClose(fh); + } + return IsapiFileList.GetSize() > 0; +} + +void DeleteTempFiles(const char *mask) +{ + char FindPath[MAX_PATH]; + WIN32_FIND_DATA fd; + memset(&fd, 0, sizeof(WIN32_FIND_DATA)); + + _snprintf(FindPath, sizeof(FindPath)-1, "%s\\%s", temppath, mask); + HANDLE fh = FindFirstFile(FindPath, &fd); + if (fh != INVALID_HANDLE_VALUE) { + do { + char NewFindPath[MAX_PATH]; + _snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s", temppath, fd.cFileName); + DeleteFile(NewFindPath); + memset(&fd, 0, sizeof(WIN32_FIND_DATA)); + } while (FindNextFile(fh, &fd) != 0); + FindClose(fh); + } +} + +void DoTestFiles(const char *filelist, const char *environment) +{ + if (!GetTestFiles(filelist)) { + printf("No Files to test!\r\n"); + return; + } + + Results.SetSize(IsapiFileList.GetSize()); + + ReadGlobalEnvironment(environment); + + DoThreads(); + + printf("\r\nRESULTS:\r\n"); + // show results: + DWORD r = Results.GetSize(); + for (DWORD i=0; i< r; i++) { + TResults result = Results.GetAt(i); + printf("%s\r\nOK: %d FAILED: %d\r\n", TestNames.GetAt(i), result.ok, result.bad); + } + + // delete temp files + printf("Deleting Temp Files\r\n"); + DeleteTempFiles("exp.*"); + DeleteTempFiles("pht.*"); + printf("Done\r\n"); +} + +#define OPTSTRING "m:f:d:h:t:i:" +static void _usage(char *argv0) +{ + char *prog; + + prog = strrchr(argv0, '/'); + if (prog) { + prog++; + } else { + prog = "stresstest"; + } + + printf("Usage: %s -m -d|-l [-t ] [-i ]\n" + " -m path to isapi dll\n" + " -d php directory (to run php test files).\n" + " -f file containing list of files to run\n" + " -t number of threads to use (default=1)\n" + " -i number of iterations per thread (default=1)\n" + " -h This help\n", prog); +} +int main(int argc, char* argv[]) +{ + LPVOID lpMsgBuf; + char *filelist=NULL, *environment=NULL, *module=NULL; + int c = NULL; + while ((c=ap_getopt(argc, argv, OPTSTRING))!=-1) { + switch (c) { + case 'd': + bUseTestFiles = TRUE; + filelist = strdup(ap_optarg); + break; + case 'f': + bUseTestFiles = FALSE; + filelist = strdup(ap_optarg); + break; + case 'e': + environment = strdup(ap_optarg); + break; + case 't': + numThreads = atoi(ap_optarg); + break; + case 'i': + iterations = atoi(ap_optarg); + break; + case 'm': + module = strdup(ap_optarg); + break; + case 'h': + _usage(argv[0]); + exit(0); + break; + } + } + if (!module || !filelist) { + _usage(argv[0]); + exit(0); + } + + GetTempPath(sizeof(temppath), temppath); + hDll = LoadLibrary(module); // Load our DLL + + if (!hDll) { + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + fprintf(stderr,"Error: Dll 'php7isapi.dll' not found -%d\n%s\n", GetLastError(), lpMsgBuf); + free (module); + free(filelist); + LocalFree( lpMsgBuf ); + return -1; + } + + // + // Find the exported functions + + IsapiGetExtensionVersion = (VersionProc)GetProcAddress(hDll,"GetExtensionVersion"); + if (!IsapiGetExtensionVersion) { + fprintf(stderr,"Can't Get Extension Version %d\n", GetLastError()); + free (module); + free(filelist); + return -1; + } + IsapiHttpExtensionProc = (HttpExtProc)GetProcAddress(hDll,"HttpExtensionProc"); + if (!IsapiHttpExtensionProc) { + fprintf(stderr,"Can't Get Extension proc %d\n", GetLastError()); + free (module); + free(filelist); + return -1; + } + TerminateExtensionProc = (TerminateProc) GetProcAddress(hDll, + "TerminateExtension"); + + // This should really check if the version information matches what we + // expect. + // + if (!IsapiGetExtensionVersion(&version_info) ) { + fprintf(stderr,"Fatal: GetExtensionVersion failed\n"); + free (module); + free(filelist); + return -1; + } + + if (bUseTestFiles) { + char TestPath[MAX_PATH]; + if (filelist != NULL) + _snprintf(TestPath, sizeof(TestPath)-1, "%s\\tests", filelist); + else strcpy(TestPath, "tests"); + DoTestFiles(TestPath, environment); + } else { + DoFileList(filelist, environment); + } + + // cleanup + if (TerminateExtensionProc) TerminateExtensionProc(0); + + // We should really free memory (e.g., from GetEnv), but we'll be dead + // soon enough + + FreeLibrary(hDll); + free (module); + free(filelist); + return 0; +} + + +DWORD CALLBACK IsapiThread(void *p) +{ + DWORD filecount = IsapiFileList.GetSize(); + + for (DWORD j=0; jenv.Lookup(lpszVariableName, value)) { + rc = value.GetLength(); + strncpy((char *)lpBuffer, value, *lpdwSize-1); + } else + rc = GetEnvironmentVariable(lpszVariableName, (char *)lpBuffer, *lpdwSize) ; + + if (!rc) { // return of 0 indicates the variable was not found + SetLastError(ERROR_NO_DATA); + return FALSE; + } + + if (rc > *lpdwSize) { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + *lpdwSize =rc + 1 ; // GetEnvironmentVariable does not count the NULL + + return TRUE; + +} +// +// Again, we don't have an HCONN, so we simply wrap ReadClient() to +// ReadFile on stdin. The semantics of the two functions are the same +// +BOOL WINAPI ReadClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize) { + TIsapiContext *c = (TIsapiContext *)hConn; + if (!c) return FALSE; + + if (c->in != INVALID_HANDLE_VALUE) + return ReadFile(c->in, lpBuffer, (*lpdwSize), lpdwSize, NULL); + + return FALSE; +} +// +// ditto for WriteClient() +// +BOOL WINAPI WriteClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize, + DWORD dwReserved) { + TIsapiContext *c = (TIsapiContext *)hConn; + if (!c) return FALSE; + + if (c->out != INVALID_HANDLE_VALUE) + return WriteFile(c->out, lpBuffer, *lpdwSize, lpdwSize, NULL); + return FALSE; +} +// +// This is a special callback function used by the DLL for certain extra +// functionality. Look at the API help for details. +// +BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwHSERequest, + LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType){ + + TIsapiContext *c = (TIsapiContext *)hConn; + char *lpszRespBuf; + char * temp = NULL; + DWORD dwBytes; + BOOL bRet = TRUE; + + switch(dwHSERequest) { + case (HSE_REQ_SEND_RESPONSE_HEADER) : + lpszRespBuf = (char *)xmalloc(*lpdwSize);//+ 80);//accommodate our header + if (!lpszRespBuf) + return FALSE; + wsprintf(lpszRespBuf,"%s", + //HTTP_VER, + + /* Default response is 200 Ok */ + + //lpvBuffer?lpvBuffer:"200 Ok", + + /* Create a string for the time. */ + //temp=MakeDateStr(), + + //SERVER_VERSION, + + /* If this exists, it is a pointer to a data buffer to + be sent. */ + lpdwDataType?(char *)lpdwDataType:NULL); + + if (temp) xfree(temp); + + dwBytes = strlen(lpszRespBuf); + bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0); + xfree(lpszRespBuf); + + break; + // + // A real server would do cleanup here + case (HSE_REQ_DONE_WITH_SESSION): + SetEvent(c->waitEvent); + //ExitThread(0); + break; + + // + // This sends a redirect (temporary) to the client. + // The header construction is similar to RESPONSE_HEADER above. + // + case (HSE_REQ_SEND_URL_REDIRECT_RESP): + lpszRespBuf = (char *)xmalloc(*lpdwSize +80) ; + if (!lpszRespBuf) + return FALSE; + wsprintf(lpszRespBuf,"%s %s %s\r\n", + HTTP_VER, + "302 Moved Temporarily", + (lpdwSize > 0)?lpvBuffer:0); + xfree(temp); + dwBytes = strlen(lpszRespBuf); + bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0); + xfree(lpszRespBuf); + break; + default: + return FALSE; + break; + } + return bRet; + +} +// +// Makes a string of the date and time from GetSystemTime(). +// This is in UTC, as required by the HTTP spec.` +// +char * MakeDateStr(void){ + SYSTEMTIME systime; + char *szDate= (char *)xmalloc(64); + + char * DaysofWeek[] = {"Sun","Mon","Tue","Wed","Thurs","Fri","Sat"}; + char * Months[] = {"NULL","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug", + "Sep","Oct","Nov","Dec"}; + + GetSystemTime(&systime); + + wsprintf(szDate,"%s, %d %s %d %d:%d.%d", DaysofWeek[systime.wDayOfWeek], + systime.wDay, + Months[systime.wMonth], + systime.wYear, + systime.wHour, systime.wMinute, + systime.wSecond ); + + return szDate; +} +// +// Fill the ECB up +// +BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *ECB, TIsapiContext *context) { + + char * temp; + ECB->cbSize = sizeof(EXTENSION_CONTROL_BLOCK); + ECB->dwVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR); + ECB->ConnID = (void *)context; + // + // Pointers to the functions the DLL will call. + // + ECB->GetServerVariable = GetServerVariable; + ECB->ReadClient = ReadClient; + ECB->WriteClient = WriteClient; + ECB->ServerSupportFunction = ServerSupportFunction; + + // + // Fill in the standard CGI environment variables + // + ECB->lpszMethod = GetEnv("REQUEST_METHOD"); + if (!ECB->lpszMethod) ECB->lpszMethod = "GET"; + + ECB->lpszQueryString = GetEnv("QUERY_STRING"); + ECB->lpszPathInfo = GetEnv("PATH_INFO"); + ECB->lpszPathTranslated = GetEnv("PATH_TRANSLATED"); + ECB->cbTotalBytes=( (temp=GetEnv("CONTENT_LENGTH")) ? (atoi(temp)): 0); + ECB->cbAvailable = 0; + ECB->lpbData = (unsigned char *)""; + ECB->lpszContentType = GetEnv("CONTENT_TYPE"); + return TRUE; + +} + +// +// Works like _getenv(), but uses win32 functions instead. +// +char *GetEnv(LPSTR lpszEnvVar) +{ + + char *var, dummy; + DWORD dwLen; + + if (!lpszEnvVar) + return ""; + + dwLen =GetEnvironmentVariable(lpszEnvVar, &dummy, 1); + + if (dwLen == 0) + return ""; + + var = (char *)xmalloc(dwLen); + if (!var) + return ""; + (void)GetEnvironmentVariable(lpszEnvVar, var, dwLen); + + return var; +} From 4edbf37800651d6e2772a09d641b43f67b445866 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sat, 15 Feb 2025 18:43:20 +0100 Subject: [PATCH 02/13] Basic working under PHP 8.5 Most notably we need to adapt to the phpng TLS model, but also need to make some minor changes. We also disable SEH support for now, since inline asm is no longer supported by MSVC. --- sapi/isapi/config.w32 | 2 +- sapi/isapi/php7isapi.c | 47 +++++++++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/sapi/isapi/config.w32 b/sapi/isapi/config.w32 index 5a359f11b2070..af3559ea6edc3 100644 --- a/sapi/isapi/config.w32 +++ b/sapi/isapi/config.w32 @@ -7,7 +7,7 @@ if (PHP_ISAPI == "yes") { if (PHP_ZTS == "no") { WARNING("ISAPI module requires an --enable-zts build of PHP"); } else { - SAPI('isapi', 'php7isapi.c', 'php' + PHP_VERSION + 'isapi.dll', '/D PHP7ISAPI_EXPORTS'); + SAPI('isapi', 'php7isapi.c', 'php' + PHP_VERSION + 'isapi.dll', '/D PHP7ISAPI_EXPORTS /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); ADD_FLAG('LDFLAGS_ISAPI', '/DEF:sapi\\isapi\\php7isapi.def'); } } diff --git a/sapi/isapi/php7isapi.c b/sapi/isapi/php7isapi.c index 627c20c18cc62..6a72d21137c25 100644 --- a/sapi/isapi/php7isapi.c +++ b/sapi/isapi/php7isapi.c @@ -45,7 +45,7 @@ #endif #ifdef PHP_WIN32 -#define PHP_ENABLE_SEH +// #define PHP_ENABLE_SEH #endif /* @@ -143,6 +143,9 @@ static char *isapi_secure_server_variable_names[] = { NULL }; +#if defined(PHP_WIN32) && defined(ZTS) +ZEND_TSRMLS_CACHE_DEFINE() +#endif static void php_info_isapi(ZEND_MODULE_INFO_FUNC_ARGS) { @@ -202,7 +205,7 @@ static zend_module_entry php_isapi_module = { }; -static int sapi_isapi_ub_write(const char *str, uint str_length) +static size_t sapi_isapi_ub_write(const char *str, size_t str_length) { DWORD num_bytes = str_length; LPEXTENSION_CONTROL_BLOCK ecb; @@ -222,7 +225,7 @@ static int sapi_isapi_header_handler(sapi_header_struct *sapi_header, sapi_heade -static void accumulate_header_length(sapi_header_struct *sapi_header, uint *total_length) +static void accumulate_header_length(sapi_header_struct *sapi_header, unsigned int *total_length) { *total_length += sapi_header->header_len+2; } @@ -241,7 +244,7 @@ static void concat_header(sapi_header_struct *sapi_header, char **combined_heade static int sapi_isapi_send_headers(sapi_headers_struct *sapi_headers) { - uint total_length = 2; /* account for the trailing \r\n */ + unsigned int total_length = 2; /* account for the trailing \r\n */ char *combined_headers, *combined_headers_ptr; LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context); HSE_SEND_HEADER_EX_INFO header_info; @@ -315,7 +318,7 @@ static int sapi_isapi_send_headers(sapi_headers_struct *sapi_headers) static int php_isapi_startup(sapi_module_struct *sapi_module) { - if (php_module_startup(sapi_module, &php_isapi_module, 1)==FAILURE) { + if (php_module_startup(sapi_module, &php_isapi_module)==FAILURE) { return FAILURE; } else { bTerminateThreadsOnError = (zend_bool) INI_INT("isapi.terminate_threads_on_error"); @@ -324,7 +327,7 @@ static int php_isapi_startup(sapi_module_struct *sapi_module) } -static int sapi_isapi_read_post(char *buffer, uint count_bytes) +static size_t sapi_isapi_read_post(char *buffer, size_t count_bytes) { LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context); DWORD read_from_buf=0; @@ -372,7 +375,7 @@ static char *sapi_isapi_read_cookies(void) efree(tmp_variable_buf); } } - return STR_EMPTY_ALLOC(); + return NULL; } @@ -836,6 +839,13 @@ DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) int retval = FAILURE; #ifdef PHP_ENABLE_SEH LPEXCEPTION_POINTERS e; +#endif +#ifdef ZTS + /* initial resource fetch */ + (void)ts_resource(0); +# ifdef PHP_WIN32 + ZEND_TSRMLS_CACHE_UPDATE(); +# endif #endif zend_first_try { @@ -844,13 +854,11 @@ DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) #endif init_request_info(lpECB); SG(server_context) = lpECB; - php_request_startup(); - file_handle.filename = SG(request_info).path_translated; - file_handle.free_filename = 0; - file_handle.type = ZEND_HANDLE_FILENAME; - file_handle.opened_path = NULL; + // FIXME: we're leaking the file_handle.filename + zend_stream_init_filename(&file_handle, (char *) SG(request_info).path_translated); + file_handle.primary_script = 1; /* open the script here so we can 404 if it fails */ if (file_handle.filename) @@ -862,12 +870,14 @@ DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) } else { php_execute_script(&file_handle); } + zend_destroy_file_handle(&file_handle); if (SG(request_info).cookie_data) { efree(SG(request_info).cookie_data); } - if (SG(request_info).path_translated) + if (SG(request_info).path_translated) { efree(SG(request_info).path_translated); + } #ifdef PHP_ENABLE_SEH } __except(exceptionhandler(&e, GetExceptionInformation())) { char buf[1024]; @@ -880,7 +890,7 @@ DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) GetSystemInfo(&si); /* Get page ESP is pointing to */ - _asm mov lpPage, esp; + // _asm mov lpPage, esp; /* Get stack allocation base */ VirtualQuery(lpPage, &mi, sizeof(mi)); @@ -937,11 +947,10 @@ __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, L { switch (fdwReason) { case DLL_PROCESS_ATTACH: -#ifdef WITH_ZEUS - tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "TSRM.log"); -#else - tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "C:\\TSRM.log"); -#endif + php_tsrm_startup_ex(128); +# ifdef PHP_WIN32 + ZEND_TSRMLS_CACHE_UPDATE(); +# endif sapi_startup(&isapi_sapi_module); if (isapi_sapi_module.startup) { isapi_sapi_module.startup(&sapi_module); From 09aef172600f7e9fb1a4284058609b9c5ad728eb Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 16 Feb 2025 00:43:11 +0100 Subject: [PATCH 03/13] Drop Zeus support Zeus Web Server is no longer supported for more than ten years[1], so it does not make sense to keep the Zeus specific code, and its POSIX config.m4. [1] --- sapi/isapi/config.m4 | 24 ------- sapi/isapi/php7isapi.c | 156 ----------------------------------------- 2 files changed, 180 deletions(-) delete mode 100644 sapi/isapi/config.m4 diff --git a/sapi/isapi/config.m4 b/sapi/isapi/config.m4 deleted file mode 100644 index 4073173eafc61..0000000000000 --- a/sapi/isapi/config.m4 +++ /dev/null @@ -1,24 +0,0 @@ -dnl -dnl $Id$ -dnl - -PHP_ARG_WITH(isapi, for Zeus ISAPI support, -[ --with-isapi[=DIR] Build PHP as an ISAPI module for use with Zeus], no, no) - -if test "$PHP_ISAPI" != "no"; then - if test "$PHP_ISAPI" = "yes"; then - ZEUSPATH=/usr/local/zeus # the default - else - ZEUSPATH=$PHP_ISAPI - fi - test -f "$ZEUSPATH/web/include/httpext.h" || AC_MSG_ERROR(Unable to find httpext.h in $ZEUSPATH/web/include) - PHP_BUILD_THREAD_SAFE - AC_DEFINE(WITH_ZEUS, 1, [ ]) - PHP_ADD_INCLUDE($ZEUSPATH/web/include) - PHP_SELECT_SAPI(isapi, shared, php7isapi.c) - INSTALL_IT="\$(SHELL) \$(srcdir)/install-sh -m 0755 $SAPI_SHARED \$(INSTALL_ROOT)$ZEUSPATH/web/bin/" -fi - -dnl ## Local Variables: -dnl ## tab-width: 4 -dnl ## End: diff --git a/sapi/isapi/php7isapi.c b/sapi/isapi/php7isapi.c index 6a72d21137c25..ece1f88ba41d8 100644 --- a/sapi/isapi/php7isapi.c +++ b/sapi/isapi/php7isapi.c @@ -13,7 +13,6 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Zeev Suraski | - | Ben Mansell (Zeus Support) | +----------------------------------------------------------------------+ */ /* $Id$ */ @@ -38,12 +37,6 @@ #endif -#ifdef WITH_ZEUS -# include "httpext.h" -# include -# define GetLastError() errno -#endif - #ifdef PHP_WIN32 // #define PHP_ENABLE_SEH #endif @@ -67,9 +60,7 @@ static zend_bool bTerminateThreadsOnError=0; static char *isapi_special_server_variable_names[] = { "ALL_HTTP", "HTTPS", -#ifndef WITH_ZEUS "SCRIPT_NAME", -#endif NULL }; @@ -94,7 +85,6 @@ static char *isapi_server_variable_names[] = { "SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SOFTWARE", -#ifndef WITH_ZEUS "APPL_MD_PATH", "APPL_PHYSICAL_PATH", "INSTANCE_ID", @@ -102,9 +92,6 @@ static char *isapi_server_variable_names[] = { "LOGON_USER", "REQUEST_URI", "URL", -#else - "DOCUMENT_ROOT", -#endif NULL }; @@ -124,22 +111,6 @@ static char *isapi_secure_server_variable_names[] = { "HTTPS_SERVER_ISSUER", "HTTPS_SERVER_SUBJECT", "SERVER_PORT_SECURE", -#ifdef WITH_ZEUS - "SSL_CLIENT_CN", - "SSL_CLIENT_EMAIL", - "SSL_CLIENT_OU", - "SSL_CLIENT_O", - "SSL_CLIENT_L", - "SSL_CLIENT_ST", - "SSL_CLIENT_C", - "SSL_CLIENT_I_CN", - "SSL_CLIENT_I_EMAIL", - "SSL_CLIENT_I_OU", - "SSL_CLIENT_I_O", - "SSL_CLIENT_I_L", - "SSL_CLIENT_I_ST", - "SSL_CLIENT_I_C", -#endif NULL }; @@ -378,112 +349,6 @@ static char *sapi_isapi_read_cookies(void) return NULL; } - -#ifdef WITH_ZEUS - -static void sapi_isapi_register_zeus_ssl_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array) -{ - char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; - DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE; - char static_cons_buf[ISAPI_SERVER_VAR_BUF_SIZE]; - /* - * We need to construct the /C=.../ST=... - * DN's for SSL_CLIENT_DN and SSL_CLIENT_I_DN - */ - strcpy( static_cons_buf, "/C=" ); - if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) { - strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE); - } - strlcat( static_cons_buf, "/ST=", ISAPI_SERVER_VAR_BUF_SIZE); - variable_len = ISAPI_SERVER_VAR_BUF_SIZE; - if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) { - strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE ); - } - php_register_variable( "SSL_CLIENT_DN", static_cons_buf, track_vars_array ); - - strcpy( static_cons_buf, "/C=" ); - variable_len = ISAPI_SERVER_VAR_BUF_SIZE; - if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) { - strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE ); - } - strlcat( static_cons_buf, "/ST=", ISAPI_SERVER_VAR_BUF_SIZE); - variable_len = ISAPI_SERVER_VAR_BUF_SIZE; - if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) { - strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE ); - } - php_register_variable( "SSL_CLIENT_I_DN", static_cons_buf, track_vars_array ); -} - -static void sapi_isapi_register_zeus_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array) -{ - char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; - DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE; - DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE; - DWORD pathinfo_len = 0; - char *strtok_buf = NULL; - - /* Get SCRIPT_NAME, we use this to work out which bit of the URL - * belongs in PHP's version of PATH_INFO - */ - lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len); - - /* Adjust Zeus' version of PATH_INFO, set PHP_SELF, - * and generate REQUEST_URI - */ - if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) { - - /* PHP_SELF is just PATH_INFO */ - php_register_variable( "PHP_SELF", static_variable_buf, track_vars_array ); - - /* Chop off filename to get just the 'real' PATH_INFO' */ - pathinfo_len = variable_len - scriptname_len; - php_register_variable( "PATH_INFO", static_variable_buf + scriptname_len - 1, track_vars_array ); - /* append query string to give url... extra byte for '?' */ - if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) { - /* append query string only if it is present... */ - if ( strlen(lpECB->lpszQueryString) ) { - static_variable_buf[ variable_len - 1 ] = '?'; - strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString ); - } - php_register_variable( "URL", static_variable_buf, track_vars_array ); - php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array ); - } - } - - /* Get and adjust PATH_TRANSLATED to what PHP wants */ - variable_len = ISAPI_SERVER_VAR_BUF_SIZE; - if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) { - static_variable_buf[ variable_len - pathinfo_len - 1 ] = '\0'; - php_register_variable( "PATH_TRANSLATED", static_variable_buf, track_vars_array ); - } - - /* Bring in the AUTHENTICATION stuff as needed */ - variable_len = ISAPI_SERVER_VAR_BUF_SIZE; - if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_USER", static_variable_buf, &variable_len) && static_variable_buf[0] ) { - php_register_variable( "PHP_AUTH_USER", static_variable_buf, track_vars_array ); - } - variable_len = ISAPI_SERVER_VAR_BUF_SIZE; - if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_PASSWORD", static_variable_buf, &variable_len) && static_variable_buf[0] ) { - php_register_variable( "PHP_AUTH_PW", static_variable_buf, track_vars_array ); - } - variable_len = ISAPI_SERVER_VAR_BUF_SIZE; - if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_TYPE", static_variable_buf, &variable_len) && static_variable_buf[0] ) { - php_register_variable( "AUTH_TYPE", static_variable_buf, track_vars_array ); - } - - /* And now, for the SSL variables (if applicable) */ - variable_len = ISAPI_SERVER_VAR_BUF_SIZE; - if ( lpECB->GetServerVariable(lpECB->ConnID, "CERT_COOKIE", static_variable_buf, &variable_len) && static_variable_buf[0] ) { - sapi_isapi_register_zeus_ssl_variables( lpECB, track_vars_array ); - } - /* Copy some of the variables we need to meet Apache specs */ - variable_len = ISAPI_SERVER_VAR_BUF_SIZE; - if ( lpECB->GetServerVariable(lpECB->ConnID, "SERVER_SOFTWARE", static_variable_buf, &variable_len) && static_variable_buf[0] ) { - php_register_variable( "SERVER_SIGNATURE", static_variable_buf, track_vars_array ); - } -} -#else - static void sapi_isapi_register_iis_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array) { char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; @@ -560,7 +425,6 @@ static void sapi_isapi_register_iis_variables(LPEXTENSION_CONTROL_BLOCK lpECB, z php_register_variable("PHP_AUTH_PW", SG(request_info).auth_password, track_vars_array ); } } -#endif static void sapi_isapi_register_server_variables2(char **server_variables, LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array, char **recorded_values) { @@ -628,12 +492,7 @@ static void sapi_isapi_register_server_variables(zval *track_vars_array) efree(isapi_special_server_variables[SPECIAL_VAR_HTTPS]); } - -#ifdef WITH_ZEUS - sapi_isapi_register_zeus_variables(lpECB, track_vars_array); -#else sapi_isapi_register_iis_variables(lpECB, track_vars_array); -#endif /* PHP_SELF support */ if (isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]) { @@ -738,9 +597,7 @@ static void init_request_info(LPEXTENSION_CONTROL_BLOCK lpECB) { DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE; char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE]; -#ifndef WITH_ZEUS HSE_URL_MAPEX_INFO humi; -#endif SG(request_info).request_method = lpECB->lpszMethod; SG(request_info).query_string = lpECB->lpszQueryString; @@ -752,14 +609,6 @@ static void init_request_info(LPEXTENSION_CONTROL_BLOCK lpECB) SG(request_info).auth_user = SG(request_info).auth_password = SG(request_info).auth_digest = NULL; } -#ifdef WITH_ZEUS - /* PATH_TRANSLATED can contain extra PATH_INFO stuff after the - * file being loaded, so we must use SCRIPT_FILENAME instead - */ - if(lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_FILENAME", static_variable_buf, &variable_len)) { - SG(request_info).path_translated = estrdup(static_variable_buf); - } else -#else /* happily, IIS gives us SCRIPT_NAME which is correct (without PATH_INFO stuff) so we can just map that to the physical path and we have our filename */ @@ -767,7 +616,6 @@ static void init_request_info(LPEXTENSION_CONTROL_BLOCK lpECB) if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) { SG(request_info).path_translated = estrdup(humi.lpszPath); } else -#endif /* if mapping fails, default to what the server tells us */ SG(request_info).path_translated = estrdup(lpECB->lpszPathTranslated); @@ -803,11 +651,7 @@ static void php_isapi_report_exception(char *message, int message_len) BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer) { pVer->dwExtensionVersion = HSE_VERSION; -#ifdef WITH_ZEUS - strncpy( pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN); -#else lstrcpyn(pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN); -#endif return TRUE; } From 3d26c93cfb11b103f156a7456223d6dcc62ddefc Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 16 Feb 2025 01:00:30 +0100 Subject: [PATCH 04/13] Drop yearly range from copyright notice This has been done across the code base years ago[1], so we follow suit. While we're at it, we also remove the obsolete information about PHP 7 compatibility. [1] --- sapi/isapi/php7isapi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sapi/isapi/php7isapi.c b/sapi/isapi/php7isapi.c index ece1f88ba41d8..2c2e8eaa4ac30 100644 --- a/sapi/isapi/php7isapi.c +++ b/sapi/isapi/php7isapi.c @@ -1,8 +1,6 @@ /* +----------------------------------------------------------------------+ - | PHP Version 7 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2015 The PHP Group | + | Copyright (c) The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | From ea53748fe307c2a982417054e6b4d8871e431b5d Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 16 Feb 2025 01:01:29 +0100 Subject: [PATCH 05/13] Drop obsolete SVN Id placeholders --- sapi/isapi/config.w32 | 1 - sapi/isapi/php7isapi.c | 1 - 2 files changed, 2 deletions(-) diff --git a/sapi/isapi/config.w32 b/sapi/isapi/config.w32 index af3559ea6edc3..5281c2488fc92 100644 --- a/sapi/isapi/config.w32 +++ b/sapi/isapi/config.w32 @@ -1,5 +1,4 @@ // vim:ft=javascript -// $Id$ ARG_ENABLE('isapi', 'Build ISAPI version of PHP', 'no'); diff --git a/sapi/isapi/php7isapi.c b/sapi/isapi/php7isapi.c index 2c2e8eaa4ac30..20b2c0c450640 100644 --- a/sapi/isapi/php7isapi.c +++ b/sapi/isapi/php7isapi.c @@ -13,7 +13,6 @@ | Authors: Zeev Suraski | +----------------------------------------------------------------------+ */ -/* $Id$ */ #include "php.h" #include From b73358dcd36c75eb58a6ffba78000079a399c6e6 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 16 Feb 2025 10:28:10 +0100 Subject: [PATCH 06/13] Drop PHP major version from source files While it may make some sense to have the PHP major version in the names of the binaries[1], there is no reason to have it in the names of the source files. [1] --- sapi/isapi/config.w32 | 4 ++-- sapi/isapi/{php7isapi.c => php_isapi.c} | 0 sapi/isapi/{php7isapi.def => php_isapi.def} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename sapi/isapi/{php7isapi.c => php_isapi.c} (100%) rename sapi/isapi/{php7isapi.def => php_isapi.def} (100%) diff --git a/sapi/isapi/config.w32 b/sapi/isapi/config.w32 index 5281c2488fc92..0e1c4244c362f 100644 --- a/sapi/isapi/config.w32 +++ b/sapi/isapi/config.w32 @@ -6,7 +6,7 @@ if (PHP_ISAPI == "yes") { if (PHP_ZTS == "no") { WARNING("ISAPI module requires an --enable-zts build of PHP"); } else { - SAPI('isapi', 'php7isapi.c', 'php' + PHP_VERSION + 'isapi.dll', '/D PHP7ISAPI_EXPORTS /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); - ADD_FLAG('LDFLAGS_ISAPI', '/DEF:sapi\\isapi\\php7isapi.def'); + SAPI('isapi', 'php_isapi.c', 'php' + PHP_VERSION + 'isapi.dll', '/D PHP7ISAPI_EXPORTS /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); + ADD_FLAG('LDFLAGS_ISAPI', '/DEF:sapi\\isapi\\php_isapi.def'); } } diff --git a/sapi/isapi/php7isapi.c b/sapi/isapi/php_isapi.c similarity index 100% rename from sapi/isapi/php7isapi.c rename to sapi/isapi/php_isapi.c diff --git a/sapi/isapi/php7isapi.def b/sapi/isapi/php_isapi.def similarity index 100% rename from sapi/isapi/php7isapi.def rename to sapi/isapi/php_isapi.def From f426f168dbb8949c6a4cc8597907d2df015d4e9a Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 16 Feb 2025 10:49:06 +0100 Subject: [PATCH 07/13] Drop unused PHP7ISAPI_EXPORTS These definitions are usually used to distinguish between `dllexport` and `dllimport`, but there is no need for `dllimport`, and we don't use `dllexport` to specify the visible functions, but rather rely on a .def file, since the Microsoft ISAPI headers predeclare the functions, so the `dllexport` name mangling would cause link errors. --- sapi/isapi/config.w32 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sapi/isapi/config.w32 b/sapi/isapi/config.w32 index 0e1c4244c362f..5e69b1fa07b5c 100644 --- a/sapi/isapi/config.w32 +++ b/sapi/isapi/config.w32 @@ -6,7 +6,7 @@ if (PHP_ISAPI == "yes") { if (PHP_ZTS == "no") { WARNING("ISAPI module requires an --enable-zts build of PHP"); } else { - SAPI('isapi', 'php_isapi.c', 'php' + PHP_VERSION + 'isapi.dll', '/D PHP7ISAPI_EXPORTS /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); + SAPI('isapi', 'php_isapi.c', 'php' + PHP_VERSION + 'isapi.dll', '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); ADD_FLAG('LDFLAGS_ISAPI', '/DEF:sapi\\isapi\\php_isapi.def'); } } From acc6b32e822726277da0ba4aa63b91d072fe83cd Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 16 Feb 2025 16:37:15 +0100 Subject: [PATCH 08/13] Replace legacy stack restore with _resetstkoflw() That code won't build on x64, because inline assembly is not supported on that platform, but calling `_resetstkoflw()` is simpler anyway. After we have fixed this, we also enable SEH again. This needs to be thoroughly tested, though. --- sapi/isapi/php_isapi.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/sapi/isapi/php_isapi.c b/sapi/isapi/php_isapi.c index 20b2c0c450640..f6b0c75afd902 100644 --- a/sapi/isapi/php_isapi.c +++ b/sapi/isapi/php_isapi.c @@ -35,7 +35,7 @@ #ifdef PHP_WIN32 -// #define PHP_ENABLE_SEH +#define PHP_ENABLE_SEH #endif /* @@ -723,29 +723,7 @@ DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) } __except(exceptionhandler(&e, GetExceptionInformation())) { char buf[1024]; if (_exception_code()==EXCEPTION_STACK_OVERFLOW) { - LPBYTE lpPage; - static SYSTEM_INFO si; - static MEMORY_BASIC_INFORMATION mi; - static DWORD dwOldProtect; - - GetSystemInfo(&si); - - /* Get page ESP is pointing to */ - // _asm mov lpPage, esp; - - /* Get stack allocation base */ - VirtualQuery(lpPage, &mi, sizeof(mi)); - - /* Go to the page below the current page */ - lpPage = (LPBYTE) (mi.BaseAddress) - si.dwPageSize; - - /* Free pages below current page */ - if (!VirtualFree(mi.AllocationBase, (LPBYTE)lpPage - (LPBYTE) mi.AllocationBase, MEM_DECOMMIT)) { - _endthread(); - } - - /* Restore the guard page */ - if (!VirtualProtect(lpPage, si.dwPageSize, PAGE_GUARD | PAGE_READWRITE, &dwOldProtect)) { + if (!_resetstkoflw()) { _endthread(); } From a6ee0bc875de5d81d192873dab86597b85528025 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 16 Feb 2025 16:40:22 +0100 Subject: [PATCH 09/13] PHP_WIN32 is always defined: simplify --- sapi/isapi/php_isapi.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/sapi/isapi/php_isapi.c b/sapi/isapi/php_isapi.c index f6b0c75afd902..857c2e6e4dfa3 100644 --- a/sapi/isapi/php_isapi.c +++ b/sapi/isapi/php_isapi.c @@ -24,19 +24,9 @@ #include "ext/standard/info.h" #include "php_variables.h" #include "php_ini.h" +#include -#ifdef PHP_WIN32 -# include -#else -# define __try -# define __except(val) -# define __declspec(foo) -#endif - - -#ifdef PHP_WIN32 #define PHP_ENABLE_SEH -#endif /* uncomment the following lines to turn off @@ -111,7 +101,7 @@ static char *isapi_secure_server_variable_names[] = { NULL }; -#if defined(PHP_WIN32) && defined(ZTS) +#ifdef ZTS ZEND_TSRMLS_CACHE_DEFINE() #endif @@ -655,14 +645,11 @@ BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer) static void my_endthread() { -#ifdef PHP_WIN32 if (bTerminateThreadsOnError) { _endthread(); } -#endif } -#ifdef PHP_WIN32 /* ep is accessible only in the context of the __except expression, * so we have to call this function to obtain it. */ @@ -671,7 +658,6 @@ BOOL exceptionhandler(LPEXCEPTION_POINTERS *e, LPEXCEPTION_POINTERS ep) *e=ep; return TRUE; } -#endif DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) { @@ -684,9 +670,7 @@ DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) #ifdef ZTS /* initial resource fetch */ (void)ts_resource(0); -# ifdef PHP_WIN32 ZEND_TSRMLS_CACHE_UPDATE(); -# endif #endif zend_first_try { @@ -767,9 +751,7 @@ __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, L switch (fdwReason) { case DLL_PROCESS_ATTACH: php_tsrm_startup_ex(128); -# ifdef PHP_WIN32 ZEND_TSRMLS_CACHE_UPDATE(); -# endif sapi_startup(&isapi_sapi_module); if (isapi_sapi_module.startup) { isapi_sapi_module.startup(&sapi_module); From f601cbc42563daeb73f3604dd57ef221ee1e6798 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 16 Feb 2025 16:45:33 +0100 Subject: [PATCH 10/13] Drop VIM specific editor configuration comments We have .editorconfig for this now. --- sapi/isapi/config.w32 | 2 -- sapi/isapi/php_isapi.c | 7 ------- 2 files changed, 9 deletions(-) diff --git a/sapi/isapi/config.w32 b/sapi/isapi/config.w32 index 5e69b1fa07b5c..ea365c4fe4100 100644 --- a/sapi/isapi/config.w32 +++ b/sapi/isapi/config.w32 @@ -1,5 +1,3 @@ -// vim:ft=javascript - ARG_ENABLE('isapi', 'Build ISAPI version of PHP', 'no'); if (PHP_ISAPI == "yes") { diff --git a/sapi/isapi/php_isapi.c b/sapi/isapi/php_isapi.c index 857c2e6e4dfa3..abc948e90592d 100644 --- a/sapi/isapi/php_isapi.c +++ b/sapi/isapi/php_isapi.c @@ -772,10 +772,3 @@ __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, L } return TRUE; } - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - */ From b4fce3aa0d09ecba45e4f24f271680a1786eaf13 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 16 Feb 2025 16:50:16 +0100 Subject: [PATCH 11/13] Drop unused macro definition --- sapi/isapi/php_isapi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sapi/isapi/php_isapi.c b/sapi/isapi/php_isapi.c index abc948e90592d..7c341958b544e 100644 --- a/sapi/isapi/php_isapi.c +++ b/sapi/isapi/php_isapi.c @@ -39,7 +39,6 @@ exception trapping when running under a debugger #define MAX_STATUS_LENGTH sizeof("xxxx LONGEST POSSIBLE STATUS DESCRIPTION") #define ISAPI_SERVER_VAR_BUF_SIZE 1024 -#define ISAPI_POST_DATA_BUF 1024 static zend_bool bFilterLoaded=0; static zend_bool bTerminateThreadsOnError=0; From ac8ef77c9827823104f99e00e66f6dd9c7a2c4ee Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 17 Feb 2025 13:56:49 +0100 Subject: [PATCH 12/13] Update credits_sapi.h --- ext/standard/credits_sapi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/standard/credits_sapi.h b/ext/standard/credits_sapi.h index 357461d5ac4a8..5c9d763a61d2f 100644 --- a/ext/standard/credits_sapi.h +++ b/ext/standard/credits_sapi.h @@ -15,5 +15,6 @@ CREDIT_LINE("CGI / FastCGI", "Rasmus Lerdorf, Stig Bakken, Shane Caraveo, Dmitry CREDIT_LINE("CLI", "Edin Kadribasic, Marcus Boerger, Johannes Schlueter, Moriyoshi Koizumi, Xinchen Hui"); CREDIT_LINE("Embed", "Edin Kadribasic"); CREDIT_LINE("FastCGI Process Manager", "Andrei Nigmatulin, dreamcat4, Antony Dovgal, Jerome Loyet"); +CREDIT_LINE("ISAPI", "Andi Gutmans, Zeev Suraski"); CREDIT_LINE("litespeed", "George Wang"); CREDIT_LINE("phpdbg", "Felipe Pena, Joe Watkins, Bob Weinand"); From db2025844de969aed14a01c1b8443b6969a939c9 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Fri, 21 Feb 2025 12:05:53 +0100 Subject: [PATCH 13/13] Support further HTTP status codes We take the list from lsapi, and basically apply the same linear search. --- sapi/isapi/php_isapi.c | 102 ++++++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/sapi/isapi/php_isapi.c b/sapi/isapi/php_isapi.c index 7c341958b544e..35411260a77f8 100644 --- a/sapi/isapi/php_isapi.c +++ b/sapi/isapi/php_isapi.c @@ -100,6 +100,57 @@ static char *isapi_secure_server_variable_names[] = { NULL }; +typedef struct http_status { + int code; + const char* msg; + } http_status; + + static const http_status http_status_codes[] = { + {100, "Continue"}, + {101, "Switching Protocols"}, + {200, "OK"}, + {201, "Created"}, + {202, "Accepted"}, + {203, "Non-Authoritative Information"}, + {204, "No Content"}, + {205, "Reset Content"}, + {206, "Partial Content"}, + {300, "Multiple Choices"}, + {301, "Moved Permanently"}, + {302, "Moved Temporarily"}, + {303, "See Other"}, + {304, "Not Modified"}, + {305, "Use Proxy"}, + {400, "Bad Request"}, + {401, "Unauthorized"}, + {402, "Payment Required"}, + {403, "Forbidden"}, + {404, "Not Found"}, + {405, "Method Not Allowed"}, + {406, "Not Acceptable"}, + {407, "Proxy Authentication Required"}, + {408, "Request Time-out"}, + {409, "Conflict"}, + {410, "Gone"}, + {411, "Length Required"}, + {412, "Precondition Failed"}, + {413, "Request Entity Too Large"}, + {414, "Request-URI Too Large"}, + {415, "Unsupported Media Type"}, + {428, "Precondition Required"}, + {429, "Too Many Requests"}, + {431, "Request Header Fields Too Large"}, + {451, "Unavailable For Legal Reasons"}, + {500, "Internal Server Error"}, + {501, "Not Implemented"}, + {502, "Bad Gateway"}, + {503, "Service Unavailable"}, + {504, "Gateway Time-out"}, + {505, "HTTP Version not supported"}, + {511, "Network Authentication Required"}, + {0, NULL} +}; + #ifdef ZTS ZEND_TSRMLS_CACHE_DEFINE() #endif @@ -227,36 +278,31 @@ static int sapi_isapi_send_headers(sapi_headers_struct *sapi_headers) *combined_headers_ptr++ = '\n'; *combined_headers_ptr = 0; - switch (SG(sapi_headers).http_response_code) { - case 200: - header_info.pszStatus = "200 OK"; - break; - case 302: - header_info.pszStatus = "302 Moved Temporarily"; - break; - case 401: - header_info.pszStatus = "401 Authorization Required"; - break; - default: { - const char *sline = SG(sapi_headers).http_status_line; - int sline_len; - - /* httpd requires that r->status_line is set to the first digit of - * the status-code: */ - if (sline && ((sline_len = strlen(sline)) > 12) && strncmp(sline, "HTTP/1.", 7) == 0 && sline[8] == ' ') { - if ((sline_len - 9) > MAX_STATUS_LENGTH) { - status_buf = estrndup(sline + 9, MAX_STATUS_LENGTH); - } else { - status_buf = estrndup(sline + 9, sline_len - 9); - } - } else { - status_buf = emalloc(MAX_STATUS_LENGTH + 1); - snprintf(status_buf, MAX_STATUS_LENGTH, "%d Undescribed", SG(sapi_headers).http_response_code); - } - header_info.pszStatus = status_buf; - break; + const char *sline = SG(sapi_headers).http_status_line; + int sline_len; + + /* httpd requires that r->status_line is set to the first digit of + * the status-code: */ + if (sline && ((sline_len = strlen(sline)) > 12) && strncmp(sline, "HTTP/1.", 7) == 0 && sline[8] == ' ') { + if ((sline_len - 9) > MAX_STATUS_LENGTH) { + status_buf = estrndup(sline + 9, MAX_STATUS_LENGTH); + } else { + status_buf = estrndup(sline + 9, sline_len - 9); + } + } else { + http_status *status = (http_status*) http_status_codes; + + while (status->code != 0 && status->code != SG(sapi_headers).http_response_code) { + status++; + } + status_buf = emalloc(MAX_STATUS_LENGTH + 1); + if (status->code) { + snprintf(status_buf, MAX_STATUS_LENGTH, "%d %s", status->code, status->msg); + } else { + snprintf(status_buf, MAX_STATUS_LENGTH, "%d Undefined", SG(sapi_headers).http_response_code); } } + header_info.pszStatus = status_buf; header_info.cchStatus = strlen(header_info.pszStatus); header_info.pszHeader = combined_headers; header_info.cchHeader = total_length;