📄 http-cgi.c
字号:
* cgi file (including the path). This MUST be freed afterwards. */char *http_pre_exec (svz_socket_t *sock, /* socket structure */ svz_envblock_t *envp, /* environment block to be filled */ char *file, /* plain executable name */ char *request, /* original http request */ int type) /* POST or GET ? */{ char *cgidir; char *cgifile; char *p; http_config_t *cfg = sock->cfg; /* change into the CGI directory temporarily */ if (chdir (cfg->cgidir) == -1) { svz_log (LOG_ERROR, "cgi: chdir: %s\n", SYS_ERROR);#if ENABLE_DEBUG svz_log (LOG_DEBUG, "cgi: cannot change dir: %s\n", cfg->cgidir);#endif return NULL; } /* get the current directory */ cgidir = svz_getcwd (); /* put the directory and file together */ cgifile = svz_malloc (strlen (cgidir) + strlen (file) + 1); sprintf (cgifile, "%s%s", cgidir, file); svz_free (cgidir); /* create the environment block for the CGI script */ http_create_cgi_envp (sock, envp, file, type); /* put the QUERY_STRING into the env variables if necessary */ if (type == GET_METHOD) { p = request; while (*p != '?' && *p != 0) p++; svz_envblock_add (envp, "QUERY_STRING=%s", *p ? p + 1 : ""); } return cgifile;}/* * Write an initial HTTP response header to the socket SOCK * right after the the actual CGI script has been invoked. */inthttp_cgi_accepted (svz_socket_t *sock){ http_socket_t *http = sock->data; http->response = 202; return svz_sock_printf (sock, HTTP_OK "Date: %s\r\n" "Server: %s/%s\r\n" "Connection: close\r\n", http_asc_date (time (NULL)), svz_library, svz_version);}/* * This routine generates some standard cgi associations. */#define DEFAULT_CGIAPP "default"voidhttp_gen_cgi_apps (http_config_t *cfg){ char *p; /* create the cgi association hash table if necessary */ if (cfg->cgiapps == NULL) cfg->cgiapps = svz_hash_create (4); /* the associations need to be in the hash to be executed at all */ if ((p = svz_hash_put (cfg->cgiapps, "exe", svz_strdup (DEFAULT_CGIAPP))) != NULL) svz_free (p); if ((p = svz_hash_put (cfg->cgiapps, "com", svz_strdup (DEFAULT_CGIAPP))) != NULL) svz_free (p); if ((p = svz_hash_put (cfg->cgiapps, "bat", svz_strdup (DEFAULT_CGIAPP))) != NULL) svz_free (p);}/* * Invoke a cgi script. In Unices we fork() us and in Win32 we * CreateProcess(). */inthttp_cgi_exec (svz_socket_t *sock, /* the socket structure */ HANDLE in, /* here the cgi reads from or NULL if GET */ HANDLE out, /* here the cgi writes to */ char *file, /* cgi script file */ char *request, /* original request (needed for GET) */ int type) /* request type (POST or GET) */{ HANDLE pid; /* the pid from fork() or the process handle in Win32 */ char *cgifile; /* path including the name of the cgi script */ http_socket_t *http; svz_envblock_t *envp;#ifdef __MINGW32__ http_config_t *cfg = sock->cfg; STARTUPINFO StartupInfo; /* store here the inherited handles */ PROCESS_INFORMATION ProcessInfo; /* where we get the process handle from */ char *savedir; /* save the original directory */ char *suffix, *p; char *cgiapp;#else char *argv[2]; struct stat buf; int retries;#endif /* Assign local CGI disconnection routine. */ sock->disconnected_socket = http_cgi_disconnect;#ifdef __MINGW32__ /* * Clean the StartupInfo, use the stdio handles, and store the * pipe handles there if necessary (depends on type). */ memset (&StartupInfo, 0, sizeof (StartupInfo)); StartupInfo.cb = sizeof (StartupInfo); StartupInfo.dwFlags = STARTF_USESTDHANDLES; StartupInfo.hStdOutput = out; /* StartupInfo.hStdError = out; */ if (type == POST_METHOD) StartupInfo.hStdInput = in; /* reserve buffer space for the environment block */ envp = svz_envblock_create (); /* save the current directory */ savedir = svz_getcwd (); if ((cgifile = http_pre_exec (sock, envp, file, request, type)) == NULL) { svz_sock_printf (sock, HTTP_INTERNAL_ERROR "\r\n"); http_error_response (sock, 500); sock->userflags |= HTTP_FLAG_DONE; chdir (savedir); svz_envblock_destroy (envp); svz_free (savedir); return -1; } /* find a cgi interpreter if possible */ p = cgifile + strlen (cgifile) - 1; while (p != cgifile && *p != '.') p--; suffix = p + 1; if ((p = svz_hash_get (cfg->cgiapps, svz_tolower (suffix))) != NULL) { if (strcmp (p, DEFAULT_CGIAPP)) { cgiapp = svz_malloc (strlen (cgifile) + strlen (p) + 2); sprintf (cgiapp, "%s %s", p, cgifile); svz_free (cgifile); cgifile = cgiapp; } } /* not a valid file extension */ else { /* find an appropriate system association */ cgiapp = svz_malloc (MAX_PATH); if (FindExecutable (cgifile, NULL, cgiapp) <= (HINSTANCE) 32) svz_log (LOG_ERROR, "FindExecutable: %s\n", SYS_ERROR);#if ENABLE_DEBUG /* if this is enabled you could learn about the system */ else svz_log (LOG_DEBUG, "FindExecutable: %s\n", cgiapp);#endif svz_free (cgiapp); /* print some error message */ svz_sock_printf (sock, HTTP_ACCESS_DENIED "\r\n"); http_error_response (sock, 403); sock->userflags |= HTTP_FLAG_DONE; chdir (savedir); svz_free (cgifile); svz_envblock_destroy (envp); svz_free (savedir); return -1; } /* send http header response */ if (http_cgi_accepted (sock) == -1) { sock->userflags |= HTTP_FLAG_DONE; chdir (savedir); svz_free (cgifile); svz_envblock_destroy (envp); svz_free (savedir); return -1; } /* create the process here */ if (!CreateProcess (NULL, /* ApplicationName */ cgifile, /* CommandLine */ NULL, /* ProcessAttributes */ NULL, /* ThreadAttributes */ TRUE, /* InheritHandles */ DETACHED_PROCESS, /* CreationFlags */ svz_envblock_get (envp), /* Environment */ NULL, /* CurrentDirectory */ &StartupInfo, &ProcessInfo)) { svz_log (LOG_ERROR, "cgi: CreateProcess: %s\n", SYS_ERROR);#if ENABLE_DEBUG svz_log (LOG_DEBUG, "cgi: cannot execute: %s\n", cgifile);#endif svz_sock_printf (sock, "\r\n"); sock->userflags |= HTTP_FLAG_DONE; chdir (savedir); svz_free (cgifile); svz_envblock_destroy (envp); svz_free (savedir); return -1; } /* reenter the actual directory and free reserved space */ chdir (savedir); svz_free (cgifile); svz_envblock_destroy (envp); svz_free (savedir); pid = ProcessInfo.hProcess;#ifdef ENABLE_DEBUG svz_log (LOG_DEBUG, "http: cgi %s got pid 0x%08X\n", file + 1, ProcessInfo.dwProcessId);#endif#else /* not __MINGW32__ */ retries = 3; retry: /* fork us here */ if ((pid = fork ()) == 0) { /* ------ child process here ------ */ /* create environment block */ envp = svz_envblock_create (); if ((cgifile = http_pre_exec (sock, envp, file, request, type)) == NULL) { exit (0); } /* make the output blocking */ if (fcntl (out, F_SETFL, ~O_NONBLOCK) == -1) { svz_log (LOG_ERROR, "cgi: fcntl: %s\n", SYS_ERROR); exit (0); } /* duplicate the receiving pipe descriptor to stdout */ if (dup2 (out, 1) != 1) { svz_log (LOG_ERROR, "cgi: dup2: %s\n", SYS_ERROR); exit (0); }#ifndef ENABLE_DEBUG /* duplicate stderr to the cgi output */ if (dup2 (out, 2) != 2) { svz_log (LOG_ERROR, "cgi: dup2: %s\n", SYS_ERROR); exit (0); }#endif /* !ENABLE_DEBUG */ /* handle post method */ if (type == POST_METHOD) { /* make the input blocking */ if (fcntl (in, F_SETFL, ~O_NONBLOCK) == -1) { svz_log (LOG_ERROR, "cgi: fcntl: %s\n", SYS_ERROR); exit (0); } /* duplicate the sending pipe descriptor to stdin */ if (dup2 (in, 0) != 0) { svz_log (LOG_ERROR, "cgi: dup2: %s\n", SYS_ERROR); exit (0); } /* close the old file descriptors */ if (close (in) < 0) svz_log (LOG_ERROR, "cgi: close: %s\n", SYS_ERROR); } /* close remaining stdin in get method */ else { close (0); } /* close the old file descriptors */ if (close (out) < 0) svz_log (LOG_ERROR, "cgi: close: %s\n", SYS_ERROR); /* get the cgi scripts permissions */ if (stat (cgifile, &buf) == -1) { svz_log (LOG_ERROR, "cgi: stat: %s\n", SYS_ERROR); exit (0); } /* set the appropriate user permissions */ if (setgid (buf.st_gid) == -1) { svz_log (LOG_ERROR, "cgi: setgid: %s\n", SYS_ERROR); exit (0); } if (setuid (buf.st_uid) == -1) { svz_log (LOG_ERROR, "cgi: setuid: %s\n", SYS_ERROR); exit (0); } /* create the argv[] and envp[] pointers */ argv[0] = cgifile; argv[1] = NULL; /* * Execute the CGI script itself here. This will overwrite the * current process. */ if (execve (cgifile, argv, svz_envblock_get (envp)) == -1) { svz_log (LOG_ERROR, "cgi: execve: %s\n", SYS_ERROR); exit (0); } } else if (pid == -1) { if (errno == EAGAIN && --retries) { /* sleep (1); */ goto retry; } /* ------ error forking new process ------ */ svz_log (LOG_ERROR, "cgi: fork: %s\n", SYS_ERROR); svz_sock_printf (sock, HTTP_BAD_REQUEST "\r\n"); http_error_response (sock, 400); sock->userflags |= HTTP_FLAG_DONE; return -1; } /* ------ still current (parent) process here ------ */#ifdef ENABLE_DEBUG svz_log (LOG_DEBUG, "http: cgi %s got pid %d\n", file + 1, pid);#endif /* send http header response */ if (http_cgi_accepted (sock) == -1) { sock->userflags |= HTTP_FLAG_DONE; return -1; }#endif /* not __MINGW32__ */ /* save the process id */ http = sock->data; http->pid = pid; /* close the inherited http data handles */ if (closehandle (out) == -1) { svz_log (LOG_ERROR, "cgi: close: %s\n", SYS_ERROR); } if (type == POST_METHOD) { /* close the reading end of the pipe for the post data */ if (closehandle (in) == -1) { svz_log (LOG_ERROR, "cgi: close: %s\n", SYS_ERROR); } } return 0;}/* * The http GET cgi request response. */inthttp_cgi_get_response (svz_socket_t *sock, char *request, int flags){ HANDLE cgi2s[2]; char *file; /* check if this is a cgi request at all */ if ((file = http_check_cgi (sock, request)) == HTTP_NO_CGI) return -2; if (file == NULL) { svz_sock_printf (sock, HTTP_INTERNAL_ERROR "\r\n"); http_error_response (sock, 500); sock->userflags |= HTTP_FLAG_DONE; return -1; } /* create a pipe for the cgi script process */ if (svz_pipe_create_pair (cgi2s) == -1) { svz_sock_printf (sock, HTTP_INTERNAL_ERROR "\r\n"); http_error_response (sock, 500); sock->userflags |= HTTP_FLAG_DONE; svz_free (file); return -1; } /* execute the cgi script in FILE */ sock->userflags |= HTTP_FLAG_CGI; sock->flags |= SOCK_FLAG_RECV_PIPE; sock->read_socket = http_cgi_read; sock->pipe_desc[READ] = cgi2s[READ]; svz_fd_cloexec ((int) cgi2s[READ]); if (http_cgi_exec (sock, INVALID_HANDLE, cgi2s[WRITE], file, request, GET_METHOD)) { /* some error occurred here */ sock->read_socket = svz_tcp_read_socket; svz_free (file); return -1; } svz_free (file); return 0;}/* * The http POST request response. */inthttp_post_response (svz_socket_t *sock, char *request, int flags){ char *file; char *length; HANDLE s2cgi[2]; HANDLE cgi2s[2]; http_socket_t *http; /* get http socket structure */ http = sock->data; /* is this a valid POST request ? */ file = http_check_cgi (sock, request); if (file == NULL || file == HTTP_NO_CGI) { svz_sock_printf (sock, HTTP_INTERNAL_ERROR "\r\n"); http_error_response (sock, 500); sock->userflags |= HTTP_FLAG_DONE; return -1; } /* create a pair of pipes for the cgi script process */ if (svz_pipe_create_pair (cgi2s) == -1) { svz_sock_printf (sock, HTTP_INTERNAL_ERROR "\r\n"); http_error_response (sock, 500); sock->userflags |= HTTP_FLAG_DONE; svz_free (file); return -1; } if (svz_pipe_create_pair (s2cgi) == -1) { svz_sock_printf (sock, HTTP_INTERNAL_ERROR "\r\n"); http_error_response (sock, 500); sock->userflags |= HTTP_FLAG_DONE; svz_free (file); return -1; } /* get the content length from the header information */ if ((length = http_find_property (http, "Content-length")) == NULL) { svz_sock_printf (sock, HTTP_BAD_REQUEST "\r\n"); http_error_response (sock, 411); sock->userflags |= HTTP_FLAG_DONE; svz_free (file); return -1; } http->contentlength = svz_atoi (length); /* prepare everything for the cgi pipe handling */ sock->pipe_desc[WRITE] = s2cgi[WRITE]; sock->pipe_desc[READ] = cgi2s[READ]; svz_fd_cloexec ((int) s2cgi[WRITE]); svz_fd_cloexec ((int) cgi2s[READ]); /* execute the cgi script in FILE */ if (http_cgi_exec (sock, s2cgi[READ], cgi2s[WRITE], file, request, POST_METHOD)) { /* some error occurred here */ sock->read_socket = svz_tcp_read_socket; sock->write_socket = http_default_write; svz_free (file); return -1; } sock->write_socket = http_cgi_write; sock->flags |= SOCK_FLAG_SEND_PIPE; sock->userflags |= HTTP_FLAG_POST; svz_free (file); return 0;}#else /* ENABLE_HTTP_PROTO */int http_cgi_dummy; /* Shut up compiler warnings. */#endif /* not ENABLE_HTTP_PROTO */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -