--- parser3/src/targets/cgi/parser3.C 2019/12/27 14:37:17 1.283 +++ parser3/src/targets/cgi/parser3.C 2020/10/03 22:58:07 1.293 @@ -5,7 +5,7 @@ Author: Alexandr Petrosian (http://paf.design.ru) */ -volatile const char * IDENT_PARSER3_C="$Id: parser3.C,v 1.283 2019/12/27 14:37:17 moko Exp $"; +volatile const char * IDENT_PARSER3_C="$Id: parser3.C,v 1.293 2020/10/03 22:58:07 moko Exp $"; #include "pa_config_includes.h" @@ -25,7 +25,7 @@ volatile const char * IDENT_PARSER3_C="$ // defines // comment remove me after debugging -//#define PA_DEBUG_CGI_ENTRY_EXIT "parser3-debug.log" +//#define PA_DEBUG_CGI_ENTRY_EXIT "parser3-debug.log" #if defined(_MSC_VER) && !defined(_DEBUG) # define PA_SUPPRESS_SYSTEM_EXCEPTION @@ -40,15 +40,13 @@ volatile const char * IDENT_PARSER3_C="$ /// IIS refuses to read bigger chunks const size_t READ_POST_CHUNK_SIZE=0x400*0x400; // 1M -static const char* argv0; -static const char* config_filespec_cstr=0; -static bool fail_on_config_read_problem=true; +static const char* config_filespec_cstr=0; // -f option +static bool mail_received=false; // -m option? [asked to parse incoming message to $mail:received] static int args_skip=1; static char** argv_all = NULL; static bool cgi; ///< we were started as CGI? -static bool mail_received=false; ///< we were started with -m option? [asked to parse incoming message to $mail:received] // for signal handlers Request *request=0; @@ -113,8 +111,8 @@ static void log(const char* fmt, va_list if(request_info) fprintf(f, " [uri=%s, method=%s, cl=%lu]", - request_info->uri? request_info->uri: "", - request_info->method? request_info->method: "", + request_info->uri ? request_info->uri : "", + request_info->method ? request_info->method : "", request_info->content_length); else fputs(" [no request info]", f); @@ -145,9 +143,16 @@ void SAPI::log(SAPI_Info&, const char* f va_end(args); } -static void die_or_abort(const char* fmt, va_list args, bool write_core) { - // inform user +void SAPI::die(const char* fmt, ...) { + va_list args; + + // logging first, first vsnprintf + va_start(args,fmt); + ::log(fmt, args); + va_end(args); + // inform user, second vsnprintf + va_start(args, fmt); char body[MAX_STRING]; int content_length=vsnprintf(body, MAX_STRING, fmt, args); @@ -167,41 +172,7 @@ static void die_or_abort(const char* fmt // body SAPI::send_body(SAPI_info, body, content_length); - // exit & try to produce core dump[unix] or invoke debugger[Win32 Debug version] - if(write_core) { -#ifdef WIN32 - // IIS with abort failes to show STDOUT, it just barks "abnormal program termination" - exit(1); -#else - abort(); -#endif - } else - exit(1); -} - -void SAPI::die(const char* fmt, ...) { - va_list args; - - // logging first, can't log inside die_or_abort due to vsnprintf (bug #106) - va_start(args,fmt); - ::log(fmt, args); - va_end(args); - - va_start(args, fmt); - die_or_abort(fmt, args, false /*write core?*/); -// va_end(args); -} - -void SAPI::abort(const char* fmt, ...) { - va_list args; - - // logging first, can't log inside die_or_abort due to vsnprintf (bug #106) - va_start(args,fmt); - ::log(fmt, args); - va_end(args); - - va_start(args, fmt); - die_or_abort(fmt, args, true /*write core?*/); + exit(1); // va_end(args); } @@ -272,15 +243,9 @@ static void full_file_spec(const char* f static void log_signal(const char* signal_name) { if(request_info) - SAPI::log(SAPI_info, "%s received while %s. uri=%s, method=%s, cl=%u", - signal_name, - request ? "executing code" : "reading data", - request_info->uri, - request_info->method, - request_info->content_length); + SAPI::log(SAPI_info, "%s received while %s.", signal_name, request ? "executing code" : "reading data"); else - SAPI::log(SAPI_info, "%s received before or after processing request", - signal_name); + SAPI::log(SAPI_info, "%s received before or after processing request", signal_name); } #ifdef SIGUSR1 @@ -306,8 +271,7 @@ static void SIGPIPE_handler(int /*sig*/) #endif #ifdef WIN32 -const char* maybe_reconstruct_IIS_status_in_qs(const char* original) -{ +const char* maybe_reconstruct_IIS_status_in_qs(const char* original) { // 404;http://servername/page[?param=value...] // ';' should be urlencoded by HTTP standard, so we shouldn't get it from browser // and can consider that as an indication that this is IIS way to report errors @@ -355,6 +319,28 @@ public: } }; +static bool locate_config(){ + if(!config_filespec_cstr) { + config_filespec_cstr=getenv(PARSER_CONFIG_ENV_NAME); + if(!config_filespec_cstr) + config_filespec_cstr=getenv(REDIRECT_PREFIX PARSER_CONFIG_ENV_NAME); + if(!config_filespec_cstr){ + // beside by binary + char beside_binary_path[MAX_STRING]; + strncpy(beside_binary_path, argv_all[0], MAX_STRING-1); beside_binary_path[MAX_STRING-1]=0; // filespec of my binary + if(!(rsplit(beside_binary_path, '/') || rsplit(beside_binary_path, '\\'))) { // strip filename + // no path, just filename + // @todo full path, not ./! + beside_binary_path[0]='.'; beside_binary_path[1]=0; + } + char config_filespec_buf[MAX_STRING]; + snprintf(config_filespec_buf, MAX_STRING, "%s/%s", beside_binary_path, AUTO_FILE_NAME); + config_filespec_cstr=pa_strdup(config_filespec_buf); + return entry_exists(config_filespec_cstr); + } + } + return true; +} /** main workhorse @@ -373,7 +359,7 @@ static void real_parser_handler(const ch SAPI::die("Parser/%s", PARSER_VERSION); // Request info - Request_info request_info; memset(&request_info, 0, sizeof(request_info)); + Request_info request_info; memset(&request_info, 0, sizeof(request_info)); char document_root_buf[MAX_STRING]; request_info.path_translated = filespec_to_process; @@ -381,73 +367,61 @@ static void real_parser_handler(const ch request_info.query_string = MAYBE_RECONSTRUCT_IIS_STATUS_IN_QS(getenv("QUERY_STRING")); if(cgi) { - // few absolute obligatory + // obligatory const char* path_info=getenv("PATH_INFO"); if(!path_info) SAPI::die("CGI: illegal call (missing PATH_INFO)"); - const char* script_name=getenv("SCRIPT_NAME"); - if(!script_name) - SAPI::die("CGI: illegal call (missing SCRIPT_NAME)"); - request_info.document_root = getenv("DOCUMENT_ROOT"); if(!request_info.document_root) { - // IIS - size_t len=min(sizeof(document_root_buf)-1, strlen(filespec_to_process)-strlen(path_info)); - memcpy(document_root_buf, filespec_to_process, len); document_root_buf[len]=0; - request_info.document_root=document_root_buf; + // IIS or fcgiwrap minimalistic setup + ssize_t prefix_len = strlen(filespec_to_process) - strlen(path_info); + if(prefix_len < 0 || strcmp(filespec_to_process + prefix_len, path_info) != 0) + SAPI::die("CGI: illegal call (invalid PATH_INFO in reinventing DOCUMENT_ROOT)"); + + char* document_root = new(PointerFreeGC) char[prefix_len + 1/*0*/]; + memcpy(document_root, filespec_to_process, prefix_len); document_root[prefix_len] = 0; + request_info.document_root = document_root; } - request_info.uri=request_info.strip_absolute_uri(getenv("REQUEST_URI")); + request_info.uri = request_info.strip_absolute_uri(getenv("REQUEST_URI")); if(request_info.uri) { // apache & others stuck to standards + // another obligatory + const char* script_name = getenv("SCRIPT_NAME"); + if(!script_name) + SAPI::die("CGI: illegal call (missing SCRIPT_NAME)"); /* http://parser3/env.html?123 =OK $request:uri=/env.html?123 REQUEST_URI='/env.html?123' SCRIPT_NAME='/cgi-bin/parser3' PATH_INFO='/env.html' - + http://parser3/cgi-bin/parser3/env.html?123 =ERROR $request:uri=/cgi-bin/parser3/env.html?123 REQUEST_URI='/cgi-bin/parser3/env.html?123' SCRIPT_NAME='/cgi-bin/parser3' PATH_INFO='/env.html' */ - size_t script_name_len=strlen(script_name); - size_t uri_len=strlen(request_info.uri); + size_t script_name_len = strlen(script_name); + size_t uri_len = strlen(request_info.uri); if(strncmp(request_info.uri, script_name, script_name_len)==0 && script_name_len != uri_len) // under IIS they are the same SAPI::die("CGI: illegal call (1)"); - } else { // seen on IIS5 + } else { // fcgiwrap minimalistic setup - if(request_info.query_string) { - char* reconstructed_uri=new(PointerFreeGC) char[strlen(path_info) + 1/*'?'*/+ strlen(request_info.query_string) + 1/*0*/]; + if(request_info.query_string && *request_info.query_string) { + char* reconstructed_uri = new(PointerFreeGC) char[strlen(path_info) + 1/*'?'*/+ strlen(request_info.query_string) + 1/*0*/]; strcpy(reconstructed_uri, path_info); strcat(reconstructed_uri, "?"); strcat(reconstructed_uri, request_info.query_string); - request_info.uri=reconstructed_uri; + request_info.uri = reconstructed_uri; } else - request_info.uri=path_info; - - /* - http://nestle/env.html?123 =OK - $request:uri=/env.html?123 - REQUEST_URI='' - SCRIPT_NAME='/env.html' - PATH_INFO='/env.html' - - http://nestle/cgi-bin/parser3.exe/env.html =ERROR - $request:uri=/env.html - REQUEST_URI='' - SCRIPT_NAME='/cgi-bin/parser3.exe' - PATH_INFO='/env.html' - */ - if(strcmp(script_name, path_info)!=0) - SAPI::die("CGI: illegal call (2)"); + request_info.uri = path_info; } } else{ full_file_spec("", document_root_buf, sizeof(document_root_buf)); - request_info.document_root=document_root_buf; - request_info.uri=""; + request_info.document_root = document_root_buf; + request_info.uri = ""; } request_info.content_type = getenv("CONTENT_TYPE"); @@ -477,36 +451,12 @@ static void real_parser_handler(const ch // prepare to process request Request request(SAPI_info, request_info, cgi ? String::Language(String::L_HTML|String::L_OPTIMIZE_BIT) : String::L_AS_IS); - { // get ::request ptr for signal handlers RequestController rc(&request); - - char config_filespec_buf[MAX_STRING]; - if(!config_filespec_cstr) { - const char* config_by_env=getenv(PARSER_CONFIG_ENV_NAME); - if(!config_by_env) - config_by_env=getenv(REDIRECT_PREFIX PARSER_CONFIG_ENV_NAME); - if(config_by_env) - config_filespec_cstr=config_by_env; - else { - // beside by binary - char beside_binary_path[MAX_STRING]; - strncpy(beside_binary_path, argv0, MAX_STRING-1); beside_binary_path[MAX_STRING-1]=0; // filespec of my binary - if(!(rsplit(beside_binary_path, '/') || rsplit(beside_binary_path, '\\'))) { // strip filename - // no path, just filename - // @todo full path, not ./! - beside_binary_path[0]='.'; beside_binary_path[1]=0; - } - snprintf(config_filespec_buf, MAX_STRING, "%s/%s", beside_binary_path, AUTO_FILE_NAME); - config_filespec_cstr=config_filespec_buf; - fail_on_config_read_problem=entry_exists(config_filespec_cstr); - } - } - + bool fail_on_config_read_problem=locate_config(); // process the request request.core(config_filespec_cstr, fail_on_config_read_problem, header_only); - // ::request cleared in RequestController desctructor to prevent signal handlers from accessing invalid memory } @@ -515,7 +465,7 @@ static void real_parser_handler(const ch } #ifdef PA_SUPPRESS_SYSTEM_EXCEPTION -static const Exception call_real_parser_handler__do_PEH_return_it(const char* filespec_to_process, const char* request_method, bool header_only){ +static const Exception call_real_parser_handler__do_PEH_return_it(const char* filespec_to_process, const char* request_method, bool header_only) { try { real_parser_handler(filespec_to_process, request_method, header_only); } catch(const Exception& e) { @@ -525,17 +475,13 @@ static const Exception call_real_parser_ return Exception(); } -static void call_real_parser_handler__supress_system_exception(const char* filespec_to_process, const char* request_method, bool header_only){ +static void call_real_parser_handler__supress_system_exception(const char* filespec_to_process, const char* request_method, bool header_only) { Exception parser_exception; LPEXCEPTION_POINTERS system_exception=0; __try { parser_exception=call_real_parser_handler__do_PEH_return_it(filespec_to_process, request_method, header_only); - } __except ( - (system_exception=GetExceptionInformation()), - EXCEPTION_EXECUTE_HANDLER) - { - + } __except ( (system_exception=GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER) { if(system_exception) if(_EXCEPTION_RECORD *er=system_exception->ExceptionRecord) throw Exception("system", 0, "0x%08X at 0x%08X", er->ExceptionCode, er->ExceptionAddress); @@ -587,8 +533,6 @@ int main(int argc, char *argv[]) { #endif argv_all=argv; - argv0=argv[0]; - umask(2); // were we started as CGI? @@ -668,34 +612,8 @@ int main(int argc, char *argv[]) { try { // global try REAL_PARSER_HANDLER(filespec_to_process, request_method, header_only); - } catch(const Exception& e) { // global problem - // don't allocate anything on pool here: - // possible pool' exception not catch-ed now - // and there could be out-of-memory exception - char buf[MAX_STRING]; - snprintf(buf, MAX_STRING, "Unhandled exception %s", e.comment()); - // log it - SAPI::log(SAPI_info, "%s", buf); - - // - int content_length=strlen(buf); - - // prepare header - // capitalized headers are used for preventing malloc during capitalization - SAPI::add_header_attribute(SAPI_info, HTTP_CONTENT_TYPE_CAPITALIZED, "text/plain"); - // don't use 'format' function because it calls malloc - char content_length_cstr[MAX_NUMBER]; - snprintf(content_length_cstr, MAX_NUMBER, "%u", content_length); - SAPI::add_header_attribute(SAPI_info, HTTP_CONTENT_LENGTH_CAPITALIZED, content_length_cstr); - - // send header - SAPI::send_header(SAPI_info); - - // send body - if(!header_only) - SAPI::send_body(SAPI_info, buf, content_length); - - // unsuccessful finish + } catch(const Exception& e) { // exception in unhandled exception + SAPI::die("Unhandled exception %s", e.comment()); } #ifdef PA_DEBUG_CGI_ENTRY_EXIT