18abd06a7SGlenn Strauss #include "first.h"
28abd06a7SGlenn Strauss
304d76e7aSGlenn Strauss #include "base.h"
422e8b456SStefan Bühler #include "stat_cache.h"
5c56b2108SGlenn Strauss #include "http_kv.h"
67b615d5dSGlenn Strauss #include "fdlog.h"
722e8b456SStefan Bühler #include "log.h"
81f23ba9aSGlenn Strauss #include "response.h"
9c95f832fSGlenn Strauss #include "http_cgi.h"
1022e8b456SStefan Bühler #include "http_chunk.h"
113dd3cde9SGlenn Strauss #include "http_header.h"
1222e8b456SStefan Bühler
1322e8b456SStefan Bühler #include "plugin.h"
1422e8b456SStefan Bühler
15bcdc6a3bSJan Kneschke #include <sys/types.h>
16a53f662aSGlenn Strauss #include "sys-socket.h"
1729f50bdeSGlenn Strauss #ifdef HAVE_SYS_WAIT_H
18bcdc6a3bSJan Kneschke #include <sys/wait.h>
1929f50bdeSGlenn Strauss #endif
20bcdc6a3bSJan Kneschke
21bcdc6a3bSJan Kneschke #include <unistd.h>
22bcdc6a3bSJan Kneschke #include <errno.h>
23bcdc6a3bSJan Kneschke #include <stdlib.h>
24bcdc6a3bSJan Kneschke #include <string.h>
25bcdc6a3bSJan Kneschke #include <fdevent.h>
26bcdc6a3bSJan Kneschke
276e193e40SJan Kneschke #include <fcntl.h>
28c5865d4fSGlenn Strauss #include <signal.h>
29bcdc6a3bSJan Kneschke
30bcdc6a3bSJan Kneschke typedef struct {
317b3a4f13SGlenn Strauss uintptr_t *offsets;
32407b4d14SGlenn Strauss size_t osize;
33407b4d14SGlenn Strauss size_t oused;
347b3a4f13SGlenn Strauss buffer *b;
357b3a4f13SGlenn Strauss buffer *boffsets;
362d914758SGlenn Strauss buffer *ld_preload;
372d914758SGlenn Strauss buffer *ld_library_path;
382d914758SGlenn Strauss #ifdef __CYGWIN__
392d914758SGlenn Strauss buffer *systemroot;
402d914758SGlenn Strauss #endif
41407b4d14SGlenn Strauss } env_accum;
42bcdc6a3bSJan Kneschke
43bcdc6a3bSJan Kneschke typedef struct {
448c36615fSGlenn Strauss unix_time64_t read_timeout;
458c36615fSGlenn Strauss unix_time64_t write_timeout;
4633ffec35SGlenn Strauss int signal_fin;
478c36615fSGlenn Strauss } cgi_limits;
488c36615fSGlenn Strauss
498c36615fSGlenn Strauss typedef struct {
50131450daSGlenn Strauss const array *cgi;
518c36615fSGlenn Strauss const cgi_limits *limits;
523c117059SStefan Bühler unsigned short execute_x_only;
5357ab20acSGlenn Strauss unsigned short local_redir;
541f23ba9aSGlenn Strauss unsigned short xsendfile_allow;
55574fb562SGlenn Strauss unsigned short upgrade;
56131450daSGlenn Strauss const array *xsendfile_docroot;
57bcdc6a3bSJan Kneschke } plugin_config;
58bcdc6a3bSJan Kneschke
597e000de0SGlenn Strauss struct cgi_pid_t;
607e000de0SGlenn Strauss
61bcdc6a3bSJan Kneschke typedef struct {
62bcdc6a3bSJan Kneschke PLUGIN_DATA;
63131450daSGlenn Strauss plugin_config defaults;
64bcdc6a3bSJan Kneschke plugin_config conf;
6570195d06SGlenn Strauss int tempfile_accum;
667e000de0SGlenn Strauss struct cgi_pid_t *cgi_pid;
67407b4d14SGlenn Strauss env_accum env;
68bcdc6a3bSJan Kneschke } plugin_data;
69bcdc6a3bSJan Kneschke
70bcdc6a3bSJan Kneschke typedef struct {
717e000de0SGlenn Strauss struct cgi_pid_t *cgi_pid;
72bcdc6a3bSJan Kneschke int fd;
737c0f8a77SGlenn Strauss int fdtocgi;
74dd23fcb2SGlenn Strauss int rd_revents;
75dd23fcb2SGlenn Strauss int wr_revents;
769113011dSGlenn Strauss fdnode *fdn;
779113011dSGlenn Strauss fdnode *fdntocgi;
78bcdc6a3bSJan Kneschke
797c7f8c46SGlenn Strauss request_st *r;
80dd23fcb2SGlenn Strauss connection *con; /* dumb pointer */
817c7f8c46SGlenn Strauss struct fdevents *ev; /* dumb pointer */
82bcdc6a3bSJan Kneschke plugin_data *plugin_data; /* dumb pointer */
83bcdc6a3bSJan Kneschke
84bcdc6a3bSJan Kneschke buffer *response;
858c36615fSGlenn Strauss unix_time64_t read_ts;
868c36615fSGlenn Strauss unix_time64_t write_ts;
874d7f5737SGlenn Strauss buffer *cgi_handler; /* dumb pointer */
880a635fc8SGlenn Strauss http_response_opts opts;
895bf5e1adSGlenn Strauss plugin_config conf;
90ce9e0dfcSGlenn Strauss off_t orig_reqbody_length;
91bcdc6a3bSJan Kneschke } handler_ctx;
92bcdc6a3bSJan Kneschke
937e000de0SGlenn Strauss typedef struct cgi_pid_t {
947e000de0SGlenn Strauss pid_t pid;
9533ffec35SGlenn Strauss int signal_sent;
967e000de0SGlenn Strauss handler_ctx *hctx;
977e000de0SGlenn Strauss struct cgi_pid_t *next;
987e000de0SGlenn Strauss struct cgi_pid_t *prev;
997e000de0SGlenn Strauss } cgi_pid_t;
1007e000de0SGlenn Strauss
cgi_handler_ctx_init(void)1010c6a5645SCyril Brulebois static handler_ctx * cgi_handler_ctx_init(void) {
1025e14db43SGlenn Strauss handler_ctx *hctx = ck_calloc(1, sizeof(*hctx));
1033d8d56d8SGlenn Strauss hctx->response = chunk_buffer_acquire();
104bbbbfb3dSGlenn Strauss hctx->fd = -1;
1057c0f8a77SGlenn Strauss hctx->fdtocgi = -1;
106bcdc6a3bSJan Kneschke return hctx;
107bcdc6a3bSJan Kneschke }
108bcdc6a3bSJan Kneschke
cgi_handler_ctx_free(handler_ctx * hctx)109bcdc6a3bSJan Kneschke static void cgi_handler_ctx_free(handler_ctx *hctx) {
1103d8d56d8SGlenn Strauss chunk_buffer_release(hctx->response);
111bcdc6a3bSJan Kneschke free(hctx);
112bcdc6a3bSJan Kneschke }
113bcdc6a3bSJan Kneschke
INIT_FUNC(mod_cgi_init)114bcdc6a3bSJan Kneschke INIT_FUNC(mod_cgi_init) {
1155e14db43SGlenn Strauss plugin_data * const p = ck_calloc(1, sizeof(*p));
1162d914758SGlenn Strauss const char *s;
117bcdc6a3bSJan Kneschke
1182d914758SGlenn Strauss /* for valgrind */
1192d914758SGlenn Strauss s = getenv("LD_PRELOAD");
120bade1c03SGlenn Strauss if (s) buffer_copy_string((p->env.ld_preload = buffer_init()), s);
1212d914758SGlenn Strauss s = getenv("LD_LIBRARY_PATH");
122bade1c03SGlenn Strauss if (s) buffer_copy_string((p->env.ld_library_path = buffer_init()), s);
1232d914758SGlenn Strauss #ifdef __CYGWIN__
1242d914758SGlenn Strauss /* CYGWIN needs SYSTEMROOT */
1252d914758SGlenn Strauss s = getenv("SYSTEMROOT");
126bade1c03SGlenn Strauss if (s) buffer_copy_string((p->env.systemroot = buffer_init()), s);
1272d914758SGlenn Strauss #endif
1282d914758SGlenn Strauss
129bcdc6a3bSJan Kneschke return p;
130bcdc6a3bSJan Kneschke }
131bcdc6a3bSJan Kneschke
132bcdc6a3bSJan Kneschke
FREE_FUNC(mod_cgi_free)133bcdc6a3bSJan Kneschke FREE_FUNC(mod_cgi_free) {
134bcdc6a3bSJan Kneschke plugin_data *p = p_d;
1352d914758SGlenn Strauss buffer_free(p->env.ld_preload);
1362d914758SGlenn Strauss buffer_free(p->env.ld_library_path);
1372d914758SGlenn Strauss #ifdef __CYGWIN__
1382d914758SGlenn Strauss buffer_free(p->env.systemroot);
1392d914758SGlenn Strauss #endif
1408c36615fSGlenn Strauss
1417e000de0SGlenn Strauss for (cgi_pid_t *cgi_pid = p->cgi_pid, *next; cgi_pid; cgi_pid = next) {
1427e000de0SGlenn Strauss next = cgi_pid->next;
1437e000de0SGlenn Strauss free(cgi_pid);
1447e000de0SGlenn Strauss }
1457e000de0SGlenn Strauss
1468c36615fSGlenn Strauss if (NULL == p->cvlist) return;
1478c36615fSGlenn Strauss /* (init i to 0 if global context; to 1 to skip empty global context) */
1488c36615fSGlenn Strauss for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
1498c36615fSGlenn Strauss config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
1508c36615fSGlenn Strauss for (; -1 != cpv->k_id; ++cpv) {
1518c36615fSGlenn Strauss if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue;
1528c36615fSGlenn Strauss switch (cpv->k_id) {
1538c36615fSGlenn Strauss case 6: /* cgi.limits */
1548c36615fSGlenn Strauss free(cpv->v.v);
1558c36615fSGlenn Strauss break;
1568c36615fSGlenn Strauss default:
1578c36615fSGlenn Strauss break;
1588c36615fSGlenn Strauss }
1598c36615fSGlenn Strauss }
1608c36615fSGlenn Strauss }
161bcdc6a3bSJan Kneschke }
162bcdc6a3bSJan Kneschke
mod_cgi_merge_config_cpv(plugin_config * const pconf,const config_plugin_value_t * const cpv)163131450daSGlenn Strauss static void mod_cgi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
164131450daSGlenn Strauss switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
165131450daSGlenn Strauss case 0: /* cgi.assign */
166131450daSGlenn Strauss pconf->cgi = cpv->v.a;
167131450daSGlenn Strauss break;
168131450daSGlenn Strauss case 1: /* cgi.execute-x-only */
169131450daSGlenn Strauss pconf->execute_x_only = (unsigned short)cpv->v.u;
170131450daSGlenn Strauss break;
171131450daSGlenn Strauss case 2: /* cgi.x-sendfile */
172131450daSGlenn Strauss pconf->xsendfile_allow = (unsigned short)cpv->v.u;
173131450daSGlenn Strauss break;
174131450daSGlenn Strauss case 3: /* cgi.x-sendfile-docroot */
175131450daSGlenn Strauss pconf->xsendfile_docroot = cpv->v.a;
176131450daSGlenn Strauss break;
177131450daSGlenn Strauss case 4: /* cgi.local-redir */
178131450daSGlenn Strauss pconf->local_redir = (unsigned short)cpv->v.u;
179131450daSGlenn Strauss break;
180131450daSGlenn Strauss case 5: /* cgi.upgrade */
181131450daSGlenn Strauss pconf->upgrade = (unsigned short)cpv->v.u;
182131450daSGlenn Strauss break;
1838c36615fSGlenn Strauss case 6: /* cgi.limits */
1848c36615fSGlenn Strauss if (cpv->vtype != T_CONFIG_LOCAL) break;
1858c36615fSGlenn Strauss pconf->limits = cpv->v.v;
1868c36615fSGlenn Strauss break;
187131450daSGlenn Strauss default:/* should not happen */
188131450daSGlenn Strauss return;
189131450daSGlenn Strauss }
190131450daSGlenn Strauss }
191bcdc6a3bSJan Kneschke
mod_cgi_merge_config(plugin_config * const pconf,const config_plugin_value_t * cpv)192131450daSGlenn Strauss static void mod_cgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
193131450daSGlenn Strauss do {
194131450daSGlenn Strauss mod_cgi_merge_config_cpv(pconf, cpv);
195131450daSGlenn Strauss } while ((++cpv)->k_id != -1);
196131450daSGlenn Strauss }
197131450daSGlenn Strauss
mod_cgi_patch_config(request_st * const r,plugin_data * const p)1987c7f8c46SGlenn Strauss static void mod_cgi_patch_config(request_st * const r, plugin_data * const p) {
199cc2134c8SGlenn Strauss p->conf = p->defaults; /* copy small struct instead of memcpy() */
200cc2134c8SGlenn Strauss /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/
201131450daSGlenn Strauss for (int i = 1, used = p->nconfig; i < used; ++i) {
2027c7f8c46SGlenn Strauss if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
203131450daSGlenn Strauss mod_cgi_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
204131450daSGlenn Strauss }
205131450daSGlenn Strauss }
206131450daSGlenn Strauss
20733ffec35SGlenn Strauss __attribute_cold__
20833ffec35SGlenn Strauss __attribute_pure__
mod_cgi_str_to_signal(const char * s,int default_sig)20933ffec35SGlenn Strauss static int mod_cgi_str_to_signal (const char *s, int default_sig) {
21033ffec35SGlenn Strauss static const struct { const char *name; int sig; } sigs[] = {
21133ffec35SGlenn Strauss { "HUP", SIGHUP }
21233ffec35SGlenn Strauss ,{ "INT", SIGINT }
21333ffec35SGlenn Strauss ,{ "QUIT", SIGQUIT }
21433ffec35SGlenn Strauss ,{ "ILL", SIGILL }
21533ffec35SGlenn Strauss ,{ "TRAP", SIGTRAP }
21633ffec35SGlenn Strauss ,{ "ABRT", SIGABRT }
21733ffec35SGlenn Strauss #ifdef SIGBUS
21833ffec35SGlenn Strauss ,{ "BUS", SIGBUS }
21933ffec35SGlenn Strauss #endif
22033ffec35SGlenn Strauss ,{ "FPE", SIGFPE }
22133ffec35SGlenn Strauss ,{ "KILL", SIGKILL }
22233ffec35SGlenn Strauss #ifdef SIGUSR1
22333ffec35SGlenn Strauss ,{ "USR1", SIGUSR1 }
22433ffec35SGlenn Strauss #endif
22533ffec35SGlenn Strauss ,{ "SEGV", SIGSEGV }
22633ffec35SGlenn Strauss #ifdef SIGUSR2
22733ffec35SGlenn Strauss ,{ "USR2", SIGUSR2 }
22833ffec35SGlenn Strauss #endif
22933ffec35SGlenn Strauss ,{ "PIPE", SIGPIPE }
23033ffec35SGlenn Strauss ,{ "ALRM", SIGALRM }
23133ffec35SGlenn Strauss ,{ "TERM", SIGTERM }
23233ffec35SGlenn Strauss #ifdef SIGCHLD
23333ffec35SGlenn Strauss ,{ "CHLD", SIGCHLD }
23433ffec35SGlenn Strauss #endif
23533ffec35SGlenn Strauss #ifdef SIGCONT
23633ffec35SGlenn Strauss ,{ "CONT", SIGCONT }
23733ffec35SGlenn Strauss #endif
23833ffec35SGlenn Strauss #ifdef SIGURG
23933ffec35SGlenn Strauss ,{ "URG", SIGURG }
24033ffec35SGlenn Strauss #endif
24133ffec35SGlenn Strauss #ifdef SIGXCPU
24233ffec35SGlenn Strauss ,{ "XCPU", SIGXCPU }
24333ffec35SGlenn Strauss #endif
24433ffec35SGlenn Strauss #ifdef SIGXFSZ
24533ffec35SGlenn Strauss ,{ "XFSZ", SIGXFSZ }
24633ffec35SGlenn Strauss #endif
24733ffec35SGlenn Strauss #ifdef SIGWINCH
24833ffec35SGlenn Strauss ,{ "WINCH",SIGWINCH}
24933ffec35SGlenn Strauss #endif
25033ffec35SGlenn Strauss #ifdef SIGPOLL
25133ffec35SGlenn Strauss ,{ "POLL", SIGPOLL }
25233ffec35SGlenn Strauss #endif
25333ffec35SGlenn Strauss #ifdef SIGIO
25433ffec35SGlenn Strauss ,{ "IO", SIGIO }
25533ffec35SGlenn Strauss #endif
25633ffec35SGlenn Strauss };
25733ffec35SGlenn Strauss
25833ffec35SGlenn Strauss if (s[0] == 'S' && s[1] == 'I' && s[2] == 'G') s += 3; /*("SIG" prefix)*/
25933ffec35SGlenn Strauss for (uint32_t i = 0; i < sizeof(sigs)/sizeof(*sigs); ++i) {
26033ffec35SGlenn Strauss if (0 == strcmp(s, sigs[i].name)) return sigs[i].sig;
26133ffec35SGlenn Strauss }
26233ffec35SGlenn Strauss return default_sig;
26333ffec35SGlenn Strauss }
26433ffec35SGlenn Strauss
mod_cgi_parse_limits(const array * const a,log_error_st * const errh)2658c36615fSGlenn Strauss static cgi_limits * mod_cgi_parse_limits(const array * const a, log_error_st * const errh) {
2665e14db43SGlenn Strauss cgi_limits * const limits = ck_calloc(1, sizeof(cgi_limits));
2678c36615fSGlenn Strauss for (uint32_t i = 0; i < a->used; ++i) {
2688c36615fSGlenn Strauss const data_unset * const du = a->data[i];
2698c36615fSGlenn Strauss int32_t v = config_plugin_value_to_int32(du, -1);
2708c36615fSGlenn Strauss if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("read-timeout"))) {
2718c36615fSGlenn Strauss limits->read_timeout = (unix_time64_t)v;
2728c36615fSGlenn Strauss continue;
2738c36615fSGlenn Strauss }
2748c36615fSGlenn Strauss if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("write-timeout"))) {
2758c36615fSGlenn Strauss limits->write_timeout = (unix_time64_t)v;
2768c36615fSGlenn Strauss continue;
2778c36615fSGlenn Strauss }
27833ffec35SGlenn Strauss if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("tcp-fin-propagate"))) {
27933ffec35SGlenn Strauss if (-1 == v) {
28033ffec35SGlenn Strauss v = SIGTERM;
28133ffec35SGlenn Strauss if (du->type == TYPE_STRING) {
28233ffec35SGlenn Strauss buffer * const vstr = &((data_string *)du)->value;
28333ffec35SGlenn Strauss buffer_to_upper(vstr);
28433ffec35SGlenn Strauss v = mod_cgi_str_to_signal(vstr->ptr, SIGTERM);
28533ffec35SGlenn Strauss }
28633ffec35SGlenn Strauss }
28733ffec35SGlenn Strauss limits->signal_fin = v;
28833ffec35SGlenn Strauss continue;
28933ffec35SGlenn Strauss }
2908c36615fSGlenn Strauss log_error(errh, __FILE__, __LINE__,
2918c36615fSGlenn Strauss "unrecognized cgi.limits param: %s", du->key.ptr);
2928c36615fSGlenn Strauss }
2938c36615fSGlenn Strauss return limits;
2948c36615fSGlenn Strauss }
2958c36615fSGlenn Strauss
SETDEFAULTS_FUNC(mod_cgi_set_defaults)296131450daSGlenn Strauss SETDEFAULTS_FUNC(mod_cgi_set_defaults) {
297131450daSGlenn Strauss static const config_plugin_keys_t cpk[] = {
298131450daSGlenn Strauss { CONST_STR_LEN("cgi.assign"),
29903b4c993SGlenn Strauss T_CONFIG_ARRAY_KVSTRING,
300131450daSGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
301131450daSGlenn Strauss ,{ CONST_STR_LEN("cgi.execute-x-only"),
302131450daSGlenn Strauss T_CONFIG_BOOL,
303131450daSGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
304131450daSGlenn Strauss ,{ CONST_STR_LEN("cgi.x-sendfile"),
305131450daSGlenn Strauss T_CONFIG_BOOL,
306131450daSGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
307131450daSGlenn Strauss ,{ CONST_STR_LEN("cgi.x-sendfile-docroot"),
30803b4c993SGlenn Strauss T_CONFIG_ARRAY_VLIST,
309131450daSGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
310131450daSGlenn Strauss ,{ CONST_STR_LEN("cgi.local-redir"),
311131450daSGlenn Strauss T_CONFIG_BOOL,
312131450daSGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
313131450daSGlenn Strauss ,{ CONST_STR_LEN("cgi.upgrade"),
314131450daSGlenn Strauss T_CONFIG_BOOL,
315131450daSGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
3168c36615fSGlenn Strauss ,{ CONST_STR_LEN("cgi.limits"),
3178c36615fSGlenn Strauss T_CONFIG_ARRAY_KVANY,
3188c36615fSGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
319131450daSGlenn Strauss ,{ NULL, 0,
320131450daSGlenn Strauss T_CONFIG_UNSET,
321131450daSGlenn Strauss T_CONFIG_SCOPE_UNSET }
322bcdc6a3bSJan Kneschke };
323bcdc6a3bSJan Kneschke
324131450daSGlenn Strauss plugin_data * const p = p_d;
325131450daSGlenn Strauss if (!config_plugin_values_init(srv, p, cpk, "mod_cgi"))
326131450daSGlenn Strauss return HANDLER_ERROR;
327bcdc6a3bSJan Kneschke
328131450daSGlenn Strauss /* process and validate config directives
329131450daSGlenn Strauss * (init i to 0 if global context; to 1 to skip empty global context) */
330131450daSGlenn Strauss for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
3318c36615fSGlenn Strauss config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
332131450daSGlenn Strauss for (; -1 != cpv->k_id; ++cpv) {
333131450daSGlenn Strauss switch (cpv->k_id) {
334131450daSGlenn Strauss case 0: /* cgi.assign */
335131450daSGlenn Strauss case 1: /* cgi.execute-x-only */
336131450daSGlenn Strauss case 2: /* cgi.x-sendfile */
337131450daSGlenn Strauss break;
338131450daSGlenn Strauss case 3: /* cgi.x-sendfile-docroot */
339131450daSGlenn Strauss for (uint32_t j = 0; j < cpv->v.a->used; ++j) {
340131450daSGlenn Strauss data_string *ds = (data_string *)cpv->v.a->data[j];
341601c572cSGlenn Strauss if (ds->value.ptr[0] != '/') {
342131450daSGlenn Strauss log_error(srv->errh, __FILE__, __LINE__,
343131450daSGlenn Strauss "%s paths must begin with '/'; invalid: \"%s\"",
344131450daSGlenn Strauss cpk[cpv->k_id].k, ds->value.ptr);
3451f23ba9aSGlenn Strauss return HANDLER_ERROR;
3461f23ba9aSGlenn Strauss }
347980554bcSGlenn Strauss buffer_path_simplify(&ds->value);
348601c572cSGlenn Strauss buffer_append_slash(&ds->value);
3491f23ba9aSGlenn Strauss }
350131450daSGlenn Strauss break;
351131450daSGlenn Strauss case 4: /* cgi.local-redir */
352131450daSGlenn Strauss case 5: /* cgi.upgrade */
353131450daSGlenn Strauss break;
3548c36615fSGlenn Strauss case 6: /* cgi.limits */
3558c36615fSGlenn Strauss cpv->v.v = mod_cgi_parse_limits(cpv->v.a, srv->errh);
3568c36615fSGlenn Strauss if (NULL == cpv->v.v) return HANDLER_ERROR;
3578c36615fSGlenn Strauss cpv->vtype = T_CONFIG_LOCAL;
3588c36615fSGlenn Strauss break;
359131450daSGlenn Strauss default:/* should not happen */
360131450daSGlenn Strauss break;
3611f23ba9aSGlenn Strauss }
362bcdc6a3bSJan Kneschke }
363131450daSGlenn Strauss }
364131450daSGlenn Strauss
365131450daSGlenn Strauss /* initialize p->defaults from global config context */
366131450daSGlenn Strauss if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
367131450daSGlenn Strauss const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
368131450daSGlenn Strauss if (-1 != cpv->k_id)
369131450daSGlenn Strauss mod_cgi_merge_config(&p->defaults, cpv);
370131450daSGlenn Strauss }
371bcdc6a3bSJan Kneschke
3724d99d9b7SGlenn Strauss p->tempfile_accum = config_feature_bool(srv, "cgi.tempfile-accum", 1);
37370195d06SGlenn Strauss
374bcdc6a3bSJan Kneschke return HANDLER_GO_ON;
375bcdc6a3bSJan Kneschke }
376bcdc6a3bSJan Kneschke
377bcdc6a3bSJan Kneschke
cgi_pid_add(plugin_data * p,pid_t pid,handler_ctx * hctx)3787e000de0SGlenn Strauss static cgi_pid_t * cgi_pid_add(plugin_data *p, pid_t pid, handler_ctx *hctx) {
3795e14db43SGlenn Strauss cgi_pid_t *cgi_pid = ck_malloc(sizeof(cgi_pid_t));
3807e000de0SGlenn Strauss cgi_pid->pid = pid;
38133ffec35SGlenn Strauss cgi_pid->signal_sent = 0;
3827e000de0SGlenn Strauss cgi_pid->hctx = hctx;
3837e000de0SGlenn Strauss cgi_pid->prev = NULL;
3847e000de0SGlenn Strauss cgi_pid->next = p->cgi_pid;
3857e000de0SGlenn Strauss p->cgi_pid = cgi_pid;
3867e000de0SGlenn Strauss return cgi_pid;
387bcdc6a3bSJan Kneschke }
388bcdc6a3bSJan Kneschke
cgi_pid_kill(cgi_pid_t * cgi_pid,int sig)38933ffec35SGlenn Strauss static void cgi_pid_kill(cgi_pid_t *cgi_pid, int sig) {
39033ffec35SGlenn Strauss cgi_pid->signal_sent = sig; /*(save last signal sent)*/
39133ffec35SGlenn Strauss kill(cgi_pid->pid, sig);
392bcdc6a3bSJan Kneschke }
393bcdc6a3bSJan Kneschke
cgi_pid_del(plugin_data * p,cgi_pid_t * cgi_pid)3947e000de0SGlenn Strauss static void cgi_pid_del(plugin_data *p, cgi_pid_t *cgi_pid) {
3957e000de0SGlenn Strauss if (cgi_pid->prev)
3967e000de0SGlenn Strauss cgi_pid->prev->next = cgi_pid->next;
3977e000de0SGlenn Strauss else
3987e000de0SGlenn Strauss p->cgi_pid = cgi_pid->next;
399bcdc6a3bSJan Kneschke
4007e000de0SGlenn Strauss if (cgi_pid->next)
4017e000de0SGlenn Strauss cgi_pid->next->prev = cgi_pid->prev;
402bcdc6a3bSJan Kneschke
4037e000de0SGlenn Strauss free(cgi_pid);
404bcdc6a3bSJan Kneschke }
405bcdc6a3bSJan Kneschke
406bcdc6a3bSJan Kneschke
cgi_connection_close_fdtocgi(handler_ctx * hctx)4077c7f8c46SGlenn Strauss static void cgi_connection_close_fdtocgi(handler_ctx *hctx) {
4087c0f8a77SGlenn Strauss /*(closes only hctx->fdtocgi)*/
409ec50657eSGlenn Strauss if (-1 == hctx->fdtocgi) return;
4107c7f8c46SGlenn Strauss struct fdevents * const ev = hctx->ev;
411b5775b99SGlenn Strauss fdevent_fdnode_event_del(ev, hctx->fdntocgi);
412*f0786a75SGlenn Strauss /*fdevent_unregister(ev, hctx->fdntocgi);*//*(handled below)*/
413*f0786a75SGlenn Strauss fdevent_sched_close(ev, hctx->fdntocgi);
4149113011dSGlenn Strauss hctx->fdntocgi = NULL;
4157c0f8a77SGlenn Strauss hctx->fdtocgi = -1;
4167c0f8a77SGlenn Strauss }
4177c0f8a77SGlenn Strauss
cgi_connection_close(handler_ctx * hctx)4187c7f8c46SGlenn Strauss static void cgi_connection_close(handler_ctx *hctx) {
419bcdc6a3bSJan Kneschke /* the connection to the browser went away, but we still have a connection
420bcdc6a3bSJan Kneschke * to the CGI script
421bcdc6a3bSJan Kneschke *
422bcdc6a3bSJan Kneschke * close cgi-connection
423bcdc6a3bSJan Kneschke */
424bcdc6a3bSJan Kneschke
425efa87f47SJan Kneschke if (hctx->fd != -1) {
4267c7f8c46SGlenn Strauss struct fdevents * const ev = hctx->ev;
427bcdc6a3bSJan Kneschke /* close connection to the cgi-script */
428b5775b99SGlenn Strauss fdevent_fdnode_event_del(ev, hctx->fdn);
429*f0786a75SGlenn Strauss /*fdevent_unregister(ev, hctx->fdn);*//*(handled below)*/
430*f0786a75SGlenn Strauss fdevent_sched_close(ev, hctx->fdn);
4319113011dSGlenn Strauss hctx->fdn = NULL;
4327c0f8a77SGlenn Strauss }
433bcdc6a3bSJan Kneschke
4347c0f8a77SGlenn Strauss if (hctx->fdtocgi != -1) {
4357c7f8c46SGlenn Strauss cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/
436efa87f47SJan Kneschke }
437bcdc6a3bSJan Kneschke
4387c7f8c46SGlenn Strauss plugin_data * const p = hctx->plugin_data;
4397c7f8c46SGlenn Strauss request_st * const r = hctx->r;
4407c7f8c46SGlenn Strauss r->plugin_ctx[p->id] = NULL;
441bcdc6a3bSJan Kneschke
44233ffec35SGlenn Strauss if (hctx->cgi_pid) {
44333ffec35SGlenn Strauss cgi_pid_kill(hctx->cgi_pid, SIGTERM);
44433ffec35SGlenn Strauss hctx->cgi_pid->hctx = NULL;
44533ffec35SGlenn Strauss }
446bcdc6a3bSJan Kneschke cgi_handler_ctx_free(hctx);
447bcdc6a3bSJan Kneschke
4487c7f8c46SGlenn Strauss /* finish response (if not already r->resp_body_started, r->resp_body_finished) */
4497c7f8c46SGlenn Strauss if (r->handler_module == p->self) {
4507c7f8c46SGlenn Strauss http_response_backend_done(r);
45194647804SGlenn Strauss }
452bcdc6a3bSJan Kneschke }
453bcdc6a3bSJan Kneschke
cgi_connection_close_callback(request_st * const r,void * p_d)4547c7f8c46SGlenn Strauss static handler_t cgi_connection_close_callback(request_st * const r, void *p_d) {
45570195d06SGlenn Strauss handler_ctx *hctx = r->plugin_ctx[((plugin_data *)p_d)->id];
45670195d06SGlenn Strauss if (hctx) {
45770195d06SGlenn Strauss chunkqueue_set_tempdirs(&r->reqbody_queue, /* reset sz */
45870195d06SGlenn Strauss r->reqbody_queue.tempdirs, 0);
45970195d06SGlenn Strauss cgi_connection_close(hctx);
46070195d06SGlenn Strauss }
461bbbbfb3dSGlenn Strauss return HANDLER_GO_ON;
462bcdc6a3bSJan Kneschke }
463bcdc6a3bSJan Kneschke
464bcdc6a3bSJan Kneschke
46550bdb55dSGlenn Strauss static int cgi_write_request(handler_ctx *hctx, int fd);
4667c0f8a77SGlenn Strauss
4677c0f8a77SGlenn Strauss
cgi_handle_fdevent_send(void * ctx,int revents)46805cc88ddSGlenn Strauss static handler_t cgi_handle_fdevent_send (void *ctx, int revents) {
4697c0f8a77SGlenn Strauss handler_ctx *hctx = ctx;
470dd23fcb2SGlenn Strauss hctx->wr_revents |= revents;
471dd23fcb2SGlenn Strauss joblist_append(hctx->con);
472dd23fcb2SGlenn Strauss return HANDLER_FINISHED;
473dd23fcb2SGlenn Strauss }
4747c0f8a77SGlenn Strauss
4757c0f8a77SGlenn Strauss
cgi_process_wr_revents(handler_ctx * const hctx,request_st * const r,int revents)476dd23fcb2SGlenn Strauss static handler_t cgi_process_wr_revents (handler_ctx * const hctx, request_st * const r, int revents) {
4777c0f8a77SGlenn Strauss if (revents & FDEVENT_OUT) {
47850bdb55dSGlenn Strauss if (0 != cgi_write_request(hctx, hctx->fdtocgi)) {
4797c7f8c46SGlenn Strauss cgi_connection_close(hctx);
4807c0f8a77SGlenn Strauss return HANDLER_ERROR;
4817c0f8a77SGlenn Strauss }
4827c0f8a77SGlenn Strauss /* more request body to be sent to CGI */
4837c0f8a77SGlenn Strauss }
4847c0f8a77SGlenn Strauss
4857c0f8a77SGlenn Strauss if (revents & FDEVENT_HUP) {
4867c0f8a77SGlenn Strauss /* skip sending remaining data to CGI */
4877c7f8c46SGlenn Strauss if (r->reqbody_length) {
48881029b8bSGlenn Strauss chunkqueue *cq = &r->reqbody_queue;
4897c0f8a77SGlenn Strauss chunkqueue_mark_written(cq, chunkqueue_length(cq));
4907c7f8c46SGlenn Strauss if (cq->bytes_in != (off_t)r->reqbody_length) {
4917c7f8c46SGlenn Strauss r->keep_alive = 0;
492f69f209eSGlenn Strauss }
493f69f209eSGlenn Strauss }
4947c0f8a77SGlenn Strauss
4957c7f8c46SGlenn Strauss cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/
4967c0f8a77SGlenn Strauss } else if (revents & FDEVENT_ERR) {
4977c0f8a77SGlenn Strauss /* kill all connections to the cgi process */
4987c0f8a77SGlenn Strauss #if 1
4997c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, "cgi-FDEVENT_ERR");
5007c0f8a77SGlenn Strauss #endif
5017c7f8c46SGlenn Strauss cgi_connection_close(hctx);
5027c0f8a77SGlenn Strauss return HANDLER_ERROR;
5037c0f8a77SGlenn Strauss }
5047c0f8a77SGlenn Strauss
505dd23fcb2SGlenn Strauss return HANDLER_GO_ON;
5067c0f8a77SGlenn Strauss }
5077c0f8a77SGlenn Strauss
5087c0f8a77SGlenn Strauss
cgi_response_headers(request_st * const r,struct http_response_opts_t * opts)5097c7f8c46SGlenn Strauss static handler_t cgi_response_headers(request_st * const r, struct http_response_opts_t *opts) {
510574fb562SGlenn Strauss /* response headers just completed */
51145b970e6SGlenn Strauss handler_ctx *hctx = (handler_ctx *)opts->pdata;
512574fb562SGlenn Strauss
5139c8981a7SGlenn Strauss if (light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) {
5147c7f8c46SGlenn Strauss if (hctx->conf.upgrade && r->http_status == 101) {
515574fb562SGlenn Strauss /* 101 Switching Protocols; transition to transparent proxy */
5165d1aa5d0SGlenn Strauss if (r->h2_connect_ext) {
5175d1aa5d0SGlenn Strauss r->http_status = 200; /* OK (response status for CONNECT) */
5185d1aa5d0SGlenn Strauss http_header_response_unset(r, HTTP_HEADER_UPGRADE,
5195d1aa5d0SGlenn Strauss CONST_STR_LEN("Upgrade"));
5205d1aa5d0SGlenn Strauss http_header_response_unset(r, HTTP_HEADER_OTHER,
5215d1aa5d0SGlenn Strauss CONST_STR_LEN("Sec-WebSocket-Accept"));
5225d1aa5d0SGlenn Strauss }
5237c7f8c46SGlenn Strauss http_response_upgrade_read_body_unknown(r);
524574fb562SGlenn Strauss }
525574fb562SGlenn Strauss else {
5269c8981a7SGlenn Strauss light_bclr(r->resp_htags, HTTP_HEADER_UPGRADE);
527574fb562SGlenn Strauss #if 0
528574fb562SGlenn Strauss /* preserve prior questionable behavior; likely broken behavior
529574fb562SGlenn Strauss * anyway if backend thinks connection is being upgraded but client
530574fb562SGlenn Strauss * does not receive Connection: upgrade */
5317c7f8c46SGlenn Strauss http_header_response_unset(r, HTTP_HEADER_UPGRADE,
532f13db690SGlenn Strauss CONST_STR_LEN("Upgrade"));
533574fb562SGlenn Strauss #endif
534574fb562SGlenn Strauss }
535574fb562SGlenn Strauss }
5365d1aa5d0SGlenn Strauss else if (__builtin_expect( (r->h2_connect_ext != 0), 0)
5375d1aa5d0SGlenn Strauss && r->http_status < 300) {
5385d1aa5d0SGlenn Strauss /*(not handling other 1xx intermediate responses here; not expected)*/
5395d1aa5d0SGlenn Strauss http_response_body_clear(r, 0);
5405d1aa5d0SGlenn Strauss r->handler_module = NULL;
5415d1aa5d0SGlenn Strauss r->http_status = 405; /* Method Not Allowed */
5425d1aa5d0SGlenn Strauss return HANDLER_FINISHED;
5435d1aa5d0SGlenn Strauss }
544574fb562SGlenn Strauss
5459c8981a7SGlenn Strauss if (hctx->conf.upgrade
546ce9e0dfcSGlenn Strauss && !light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) {
54781029b8bSGlenn Strauss chunkqueue *cq = &r->reqbody_queue;
548574fb562SGlenn Strauss hctx->conf.upgrade = 0;
549ce9e0dfcSGlenn Strauss r->reqbody_length = hctx->orig_reqbody_length;
5507c7f8c46SGlenn Strauss if (cq->bytes_out == (off_t)r->reqbody_length) {
5517c7f8c46SGlenn Strauss cgi_connection_close_fdtocgi(hctx); /*(closes hctx->fdtocgi)*/
552574fb562SGlenn Strauss }
553574fb562SGlenn Strauss }
554574fb562SGlenn Strauss
55545b970e6SGlenn Strauss return HANDLER_GO_ON;
556574fb562SGlenn Strauss }
557574fb562SGlenn Strauss
558574fb562SGlenn Strauss
559dd23fcb2SGlenn Strauss __attribute_cold__
560dd23fcb2SGlenn Strauss __attribute_noinline__
cgi_local_redir(request_st * const r,handler_ctx * const hctx)561dd23fcb2SGlenn Strauss static handler_t cgi_local_redir(request_st * const r, handler_ctx * const hctx) {
562dd23fcb2SGlenn Strauss buffer_clear(hctx->response);
563dd23fcb2SGlenn Strauss chunk_buffer_yield(hctx->response);
564dd23fcb2SGlenn Strauss http_response_reset(r); /*(includes r->http_status = 0)*/
565cc65a21eSGlenn Strauss r->con->srv->plugins_request_reset(r);
566dd23fcb2SGlenn Strauss /*cgi_connection_close(hctx);*//*(already cleaned up and hctx is now invalid)*/
567dd23fcb2SGlenn Strauss return HANDLER_COMEBACK;
568dd23fcb2SGlenn Strauss }
569dd23fcb2SGlenn Strauss
570dd23fcb2SGlenn Strauss
cgi_recv_response(request_st * const r,handler_ctx * const hctx)5717c7f8c46SGlenn Strauss static int cgi_recv_response(request_st * const r, handler_ctx * const hctx) {
5728c36615fSGlenn Strauss const off_t bytes_in = r->write_queue.bytes_in;
5737c7f8c46SGlenn Strauss switch (http_response_read(r, &hctx->opts,
5749113011dSGlenn Strauss hctx->response, hctx->fdn)) {
5750a635fc8SGlenn Strauss default:
5768c36615fSGlenn Strauss if (r->write_queue.bytes_in > bytes_in)
5778c36615fSGlenn Strauss hctx->read_ts = log_monotonic_secs;
5780a635fc8SGlenn Strauss return HANDLER_GO_ON;
5790a635fc8SGlenn Strauss case HANDLER_ERROR:
5807c7f8c46SGlenn Strauss http_response_backend_error(r);
58176faed91SGlenn Strauss __attribute_fallthrough__
5820a635fc8SGlenn Strauss case HANDLER_FINISHED:
5837c7f8c46SGlenn Strauss cgi_connection_close(hctx);
584efa87f47SJan Kneschke return HANDLER_FINISHED;
5850a635fc8SGlenn Strauss case HANDLER_COMEBACK:
586dd23fcb2SGlenn Strauss return cgi_local_redir(r, hctx); /* HANDLER_COMEBACK */
587bcdc6a3bSJan Kneschke }
588ddfae019SGlenn Strauss }
589ddfae019SGlenn Strauss
590ddfae019SGlenn Strauss
cgi_handle_fdevent(void * ctx,int revents)59105cc88ddSGlenn Strauss static handler_t cgi_handle_fdevent(void *ctx, int revents) {
592ddfae019SGlenn Strauss handler_ctx *hctx = ctx;
593dd23fcb2SGlenn Strauss hctx->rd_revents |= revents;
594dd23fcb2SGlenn Strauss joblist_append(hctx->con);
595dd23fcb2SGlenn Strauss return HANDLER_FINISHED;
596dd23fcb2SGlenn Strauss }
597ddfae019SGlenn Strauss
598ddfae019SGlenn Strauss
cgi_process_rd_revents(handler_ctx * const hctx,request_st * const r,int revents)599dd23fcb2SGlenn Strauss static handler_t cgi_process_rd_revents(handler_ctx * const hctx, request_st * const r, int revents) {
600ddfae019SGlenn Strauss if (revents & FDEVENT_IN) {
6017c7f8c46SGlenn Strauss handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/
602ddfae019SGlenn Strauss if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
603bcdc6a3bSJan Kneschke }
604bcdc6a3bSJan Kneschke
605bcdc6a3bSJan Kneschke /* perhaps this issue is already handled */
606d5d02583SGlenn Strauss if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) {
6077c7f8c46SGlenn Strauss if (r->resp_body_started) {
608923688d2SGlenn Strauss /* drain any remaining data from kernel pipe buffers
6097c7f8c46SGlenn Strauss * even if (r->conf.stream_response_body
610923688d2SGlenn Strauss * & FDEVENT_STREAM_RESPONSE_BUFMIN)
611923688d2SGlenn Strauss * since event loop will spin on fd FDEVENT_HUP event
612923688d2SGlenn Strauss * until unregistered. */
613923688d2SGlenn Strauss handler_t rc;
6147c7f8c46SGlenn Strauss const unsigned short flags = r->conf.stream_response_body;
6157c7f8c46SGlenn Strauss r->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN;
6167c7f8c46SGlenn Strauss r->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP;
617923688d2SGlenn Strauss do {
6187c7f8c46SGlenn Strauss rc = cgi_recv_response(r,hctx); /*(might invalidate hctx)*/
619923688d2SGlenn Strauss } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/
6207c7f8c46SGlenn Strauss r->conf.stream_response_body = flags;
6218861c2bbSGlenn Strauss return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */
622af3df29aSGlenn Strauss } else if (!buffer_is_blank(hctx->response)) {
623923688d2SGlenn Strauss /* unfinished header package which is a body in reality */
6247c7f8c46SGlenn Strauss r->resp_body_started = 1;
6257c7f8c46SGlenn Strauss if (0 != http_chunk_append_buffer(r, hctx->response)) {
6267c7f8c46SGlenn Strauss cgi_connection_close(hctx);
6275a91fd4bSGlenn Strauss return HANDLER_ERROR;
6285a91fd4bSGlenn Strauss }
6297c7f8c46SGlenn Strauss if (0 == r->http_status) r->http_status = 200; /* OK */
630923688d2SGlenn Strauss }
6317c7f8c46SGlenn Strauss cgi_connection_close(hctx);
632467fb2bfSGlenn Strauss return HANDLER_FINISHED;
633bcdc6a3bSJan Kneschke } else if (revents & FDEVENT_ERR) {
634bcdc6a3bSJan Kneschke /* kill all connections to the cgi process */
6357c7f8c46SGlenn Strauss cgi_connection_close(hctx);
636bcdc6a3bSJan Kneschke return HANDLER_ERROR;
637bcdc6a3bSJan Kneschke }
638bcdc6a3bSJan Kneschke
639dd23fcb2SGlenn Strauss return HANDLER_GO_ON;
640bcdc6a3bSJan Kneschke }
641bcdc6a3bSJan Kneschke
642bcdc6a3bSJan Kneschke
6437b3a4f13SGlenn Strauss __attribute_cold__
6447b3a4f13SGlenn Strauss __attribute_noinline__
cgi_env_offset_resize(env_accum * env)6457b3a4f13SGlenn Strauss static void cgi_env_offset_resize(env_accum *env) {
6467b3a4f13SGlenn Strauss chunk_buffer_prepare_append(env->boffsets, env->boffsets->size*2);
6477b3a4f13SGlenn Strauss env->offsets = (uintptr_t *)(void *)env->boffsets->ptr;
6487b3a4f13SGlenn Strauss env->osize = env->boffsets->size/sizeof(*env->offsets);
6497b3a4f13SGlenn Strauss }
6507b3a4f13SGlenn Strauss
cgi_env_add(void * venv,const char * key,size_t key_len,const char * val,size_t val_len)6517fa5bfc9SGlenn Strauss static int cgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) {
652407b4d14SGlenn Strauss env_accum *env = venv;
653bcdc6a3bSJan Kneschke
654c0e2667bSGlenn Strauss if (!key || (!val && val_len)) return -1;
655bcdc6a3bSJan Kneschke
6567b3a4f13SGlenn Strauss if (__builtin_expect( (env->osize == env->oused), 0))
6577b3a4f13SGlenn Strauss cgi_env_offset_resize(env);
6587b3a4f13SGlenn Strauss env->offsets[env->oused++] = env->b->used-1;
659407b4d14SGlenn Strauss
6607b3a4f13SGlenn Strauss char * const dst = buffer_extend(env->b, key_len + val_len + 2);
661bcdc6a3bSJan Kneschke memcpy(dst, key, key_len);
662bcdc6a3bSJan Kneschke dst[key_len] = '=';
6635ccebbf0SGlenn Strauss if (val_len) memcpy(dst + key_len + 1, val, val_len);
664ecfdc609SStefan Bühler dst[key_len + 1 + val_len] = '\0';
665bcdc6a3bSJan Kneschke
666bcdc6a3bSJan Kneschke return 0;
667bcdc6a3bSJan Kneschke }
668bcdc6a3bSJan Kneschke
cgi_write_request(handler_ctx * hctx,int fd)66950bdb55dSGlenn Strauss static int cgi_write_request(handler_ctx *hctx, int fd) {
6707c7f8c46SGlenn Strauss request_st * const r = hctx->r;
67181029b8bSGlenn Strauss chunkqueue *cq = &r->reqbody_queue;
6727c0f8a77SGlenn Strauss chunk *c;
6737c0f8a77SGlenn Strauss
6742639e5aeSGlenn Strauss chunkqueue_remove_finished_chunks(cq); /* unnecessary? */
6752639e5aeSGlenn Strauss
6767c0f8a77SGlenn Strauss /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms.
6777c0f8a77SGlenn Strauss */
6787c0f8a77SGlenn Strauss
6797c0f8a77SGlenn Strauss for (c = cq->first; c; c = cq->first) {
6802639e5aeSGlenn Strauss ssize_t wr = chunkqueue_write_chunk_to_pipe(fd, cq, r->conf.errh);
6812639e5aeSGlenn Strauss if (wr > 0) {
6828c36615fSGlenn Strauss hctx->write_ts = log_monotonic_secs;
6832639e5aeSGlenn Strauss chunkqueue_mark_written(cq, wr);
6842639e5aeSGlenn Strauss /* continue if wrote whole chunk or wrote 16k block
6852639e5aeSGlenn Strauss * (see chunkqueue_write_chunk_file_intermed()) */
6862639e5aeSGlenn Strauss if (c != cq->first || wr == 16384)
6872639e5aeSGlenn Strauss continue;
6882639e5aeSGlenn Strauss /*(else partial write)*/
6892639e5aeSGlenn Strauss }
6902639e5aeSGlenn Strauss else if (wr < 0) {
6917c0f8a77SGlenn Strauss switch(errno) {
6927c0f8a77SGlenn Strauss case EAGAIN:
6937c0f8a77SGlenn Strauss case EINTR:
6942639e5aeSGlenn Strauss /* ignore and try again later */
6957c0f8a77SGlenn Strauss break;
6967c0f8a77SGlenn Strauss case EPIPE:
6977c0f8a77SGlenn Strauss case ECONNRESET:
6987c0f8a77SGlenn Strauss /* connection closed */
6997c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__,
700010c2894SGlenn Strauss "failed to send post data to cgi, connection closed by CGI");
7017c0f8a77SGlenn Strauss /* skip all remaining data */
7027c0f8a77SGlenn Strauss chunkqueue_mark_written(cq, chunkqueue_length(cq));
7037c0f8a77SGlenn Strauss break;
7047c0f8a77SGlenn Strauss default:
7052639e5aeSGlenn Strauss /* fatal error */
7062639e5aeSGlenn Strauss log_perror(r->conf.errh, __FILE__, __LINE__, "write() failed");
7072639e5aeSGlenn Strauss return -1;
7087c0f8a77SGlenn Strauss }
7097c0f8a77SGlenn Strauss }
7102639e5aeSGlenn Strauss /*if (0 == wr) break;*/ /*(might block)*/
7112639e5aeSGlenn Strauss break;
7122639e5aeSGlenn Strauss }
7137c0f8a77SGlenn Strauss
7147c7f8c46SGlenn Strauss if (cq->bytes_out == (off_t)r->reqbody_length && !hctx->conf.upgrade) {
7157c0f8a77SGlenn Strauss /* sent all request body input */
7167c0f8a77SGlenn Strauss /* close connection to the cgi-script */
717f69f209eSGlenn Strauss if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/
7187c7f8c46SGlenn Strauss --r->con->srv->cur_fds;
7197c0f8a77SGlenn Strauss if (close(fd)) {
7207c7f8c46SGlenn Strauss log_perror(r->conf.errh, __FILE__, __LINE__, "cgi stdin close %d failed", fd);
7217c0f8a77SGlenn Strauss }
7227c0f8a77SGlenn Strauss } else {
7237c7f8c46SGlenn Strauss cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/
7247c0f8a77SGlenn Strauss }
7257c0f8a77SGlenn Strauss } else {
72697e314fcSGlenn Strauss off_t cqlen = chunkqueue_length(cq);
7277c7f8c46SGlenn Strauss if (cq->bytes_in != r->reqbody_length && cqlen < 65536 - 16384) {
7287c7f8c46SGlenn Strauss /*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
7297c7f8c46SGlenn Strauss if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) {
7307c7f8c46SGlenn Strauss r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
7317c7f8c46SGlenn Strauss r->con->is_readable = 1; /* trigger optimistic read from client */
732f69f209eSGlenn Strauss }
733f69f209eSGlenn Strauss }
7347c7f8c46SGlenn Strauss struct fdevents * const ev = hctx->ev;
7357c0f8a77SGlenn Strauss if (-1 == hctx->fdtocgi) { /*(not registered yet)*/
7367c0f8a77SGlenn Strauss hctx->fdtocgi = fd;
737b5775b99SGlenn Strauss hctx->fdntocgi = fdevent_register(ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx);
738f69f209eSGlenn Strauss }
739f69f209eSGlenn Strauss if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/
7409113011dSGlenn Strauss if ((fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)) {
741b5775b99SGlenn Strauss fdevent_fdnode_event_set(ev, hctx->fdntocgi, 0);
742f69f209eSGlenn Strauss }
743f69f209eSGlenn Strauss } else {
744f69f209eSGlenn Strauss /* more request body remains to be sent to CGI so register for fdevents */
7458c36615fSGlenn Strauss hctx->write_ts = log_monotonic_secs;
746b5775b99SGlenn Strauss fdevent_fdnode_event_set(ev, hctx->fdntocgi, FDEVENT_OUT);
7477c0f8a77SGlenn Strauss }
7487c0f8a77SGlenn Strauss }
7497c0f8a77SGlenn Strauss
7506d6dbadbSStefan Bühler return 0;
7516d6dbadbSStefan Bühler }
7526d6dbadbSStefan Bühler
cgi_create_env(request_st * const r,plugin_data * const p,handler_ctx * const hctx,buffer * const cgi_handler)7537c7f8c46SGlenn Strauss static int cgi_create_env(request_st * const r, plugin_data * const p, handler_ctx * const hctx, buffer * const cgi_handler) {
754a9970fecSGlenn Strauss char *args[3];
755bcdc6a3bSJan Kneschke int to_cgi_fds[2];
756bcdc6a3bSJan Kneschke int from_cgi_fds[2];
7577fa5bfc9SGlenn Strauss UNUSED(p);
758bcdc6a3bSJan Kneschke
759af3df29aSGlenn Strauss if (!buffer_is_blank(cgi_handler)) {
760fe021118SGlenn Strauss if (NULL == stat_cache_path_stat(cgi_handler)) {
7617c7f8c46SGlenn Strauss log_perror(r->conf.errh, __FILE__, __LINE__,
762010c2894SGlenn Strauss "stat for cgi-handler %s", cgi_handler->ptr);
763bcdc6a3bSJan Kneschke return -1;
764bcdc6a3bSJan Kneschke }
76591066a8bSJan Kneschke }
766bcdc6a3bSJan Kneschke
76770195d06SGlenn Strauss to_cgi_fds[0] = -1;
76870195d06SGlenn Strauss if (0 == r->reqbody_length) {
76970195d06SGlenn Strauss /* future: might keep fd open in p->devnull for reuse
77070195d06SGlenn Strauss * and dup() here, or do not close() (later in this func) */
77170195d06SGlenn Strauss to_cgi_fds[0] = fdevent_open_devnull();
77270195d06SGlenn Strauss if (-1 == to_cgi_fds[0]) {
77370195d06SGlenn Strauss log_perror(r->conf.errh, __FILE__, __LINE__, "open /dev/null");
77470195d06SGlenn Strauss return -1;
77570195d06SGlenn Strauss }
77670195d06SGlenn Strauss }
77770195d06SGlenn Strauss else if (!(r->conf.stream_request_body /*(if not streaming request body)*/
778317b4ba1SGlenn Strauss & (FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN))
779317b4ba1SGlenn Strauss && !hctx->conf.upgrade) {
78070195d06SGlenn Strauss chunkqueue * const cq = &r->reqbody_queue;
78170195d06SGlenn Strauss chunk * const c = cq->first;
78270195d06SGlenn Strauss if (c && c == cq->last && c->type == FILE_CHUNK && c->file.is_temp) {
78370195d06SGlenn Strauss /* request body in single tempfile if not streaming req body */
78470195d06SGlenn Strauss if (-1 == c->file.fd
78570195d06SGlenn Strauss && 0 != chunkqueue_open_file_chunk(cq, r->conf.errh))
78670195d06SGlenn Strauss return -1;
787a3e9faa4SGlenn Strauss #ifdef __COVERITY__
788a3e9faa4SGlenn Strauss force_assert(-1 != c->file.fd);
789a3e9faa4SGlenn Strauss #endif
79070195d06SGlenn Strauss if (-1 == lseek(c->file.fd, 0, SEEK_SET)) {
79170195d06SGlenn Strauss log_perror(r->conf.errh, __FILE__, __LINE__,
79270195d06SGlenn Strauss "lseek %s", c->mem->ptr);
79370195d06SGlenn Strauss return -1;
79470195d06SGlenn Strauss }
79570195d06SGlenn Strauss to_cgi_fds[0] = c->file.fd;
79670195d06SGlenn Strauss to_cgi_fds[1] = -1;
79770195d06SGlenn Strauss }
79870195d06SGlenn Strauss }
79970195d06SGlenn Strauss
800cc2fcd3eSGlenn Strauss unsigned int bufsz_hint = 16384;
801cc2fcd3eSGlenn Strauss #ifdef _WIN32
802ce9e0dfcSGlenn Strauss if (r->reqbody_length <= 1048576 && r->reqbody_length > 0)
803cc2fcd3eSGlenn Strauss bufsz_hint = (unsigned int)r->reqbody_length;
804cc2fcd3eSGlenn Strauss #endif
805cc2fcd3eSGlenn Strauss if (-1 == to_cgi_fds[0] && fdevent_pipe_cloexec(to_cgi_fds, bufsz_hint)) {
8067c7f8c46SGlenn Strauss log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed");
807bcdc6a3bSJan Kneschke return -1;
808bcdc6a3bSJan Kneschke }
809cc2fcd3eSGlenn Strauss if (fdevent_pipe_cloexec(from_cgi_fds, bufsz_hint)) {
81070195d06SGlenn Strauss if (0 == r->reqbody_length) {
81170195d06SGlenn Strauss close(to_cgi_fds[0]);
81270195d06SGlenn Strauss }
81370195d06SGlenn Strauss else if (-1 != to_cgi_fds[1]) {
81485b1f6afSStefan Bühler close(to_cgi_fds[0]);
81585b1f6afSStefan Bühler close(to_cgi_fds[1]);
81670195d06SGlenn Strauss }
8177c7f8c46SGlenn Strauss log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed");
818bcdc6a3bSJan Kneschke return -1;
819bcdc6a3bSJan Kneschke }
820bcdc6a3bSJan Kneschke
8217b3a4f13SGlenn Strauss env_accum * const env = &p->env;
8227b3a4f13SGlenn Strauss env->b = chunk_buffer_acquire();
8237b3a4f13SGlenn Strauss env->boffsets = chunk_buffer_acquire();
8247b3a4f13SGlenn Strauss buffer_truncate(env->b, 0);
8257b3a4f13SGlenn Strauss char **envp;
826a9970fecSGlenn Strauss {
827407b4d14SGlenn Strauss size_t i = 0;
8287fa5bfc9SGlenn Strauss http_cgi_opts opts = { 0, 0, NULL, NULL };
8297b3a4f13SGlenn Strauss env->offsets = (uintptr_t *)(void *)env->boffsets->ptr;
8307b3a4f13SGlenn Strauss env->osize = env->boffsets->size/sizeof(*env->offsets);
831407b4d14SGlenn Strauss env->oused = 0;
832bcdc6a3bSJan Kneschke
833bcdc6a3bSJan Kneschke /* create environment */
834bcdc6a3bSJan Kneschke
8355d1aa5d0SGlenn Strauss if (r->h2_connect_ext) {
8365d1aa5d0SGlenn Strauss /*(SERVER_PROTOCOL=HTTP/1.1 instead of HTTP/2.0)*/
8375d1aa5d0SGlenn Strauss r->http_version = HTTP_VERSION_1_1;
8385d1aa5d0SGlenn Strauss r->http_method = HTTP_METHOD_GET;
839ce9e0dfcSGlenn Strauss }
840ce9e0dfcSGlenn Strauss if (hctx->conf.upgrade) {
841ce9e0dfcSGlenn Strauss r->reqbody_length = hctx->orig_reqbody_length;
842ce9e0dfcSGlenn Strauss if (r->reqbody_length < 0)
8435d1aa5d0SGlenn Strauss r->reqbody_length = 0;
8445d1aa5d0SGlenn Strauss }
8455d1aa5d0SGlenn Strauss
8467c7f8c46SGlenn Strauss http_cgi_headers(r, &opts, cgi_env_add, env);
847bcdc6a3bSJan Kneschke
848ce9e0dfcSGlenn Strauss if (hctx->conf.upgrade)
849ce9e0dfcSGlenn Strauss r->reqbody_length = -1;
8505d1aa5d0SGlenn Strauss if (r->h2_connect_ext) {
8515d1aa5d0SGlenn Strauss r->http_version = HTTP_VERSION_2;
8525d1aa5d0SGlenn Strauss r->http_method = HTTP_METHOD_CONNECT;
8535d1aa5d0SGlenn Strauss /* https://datatracker.ietf.org/doc/html/rfc6455#section-4.1
8545d1aa5d0SGlenn Strauss * 7. The request MUST include a header field with the name
8555d1aa5d0SGlenn Strauss * |Sec-WebSocket-Key|. The value of this header field MUST be a
8565d1aa5d0SGlenn Strauss * nonce consisting of a randomly selected 16-byte value that has
8575d1aa5d0SGlenn Strauss * been base64-encoded (see Section 4 of [RFC4648]). The nonce
8585d1aa5d0SGlenn Strauss * MUST be selected randomly for each connection.
8595d1aa5d0SGlenn Strauss * Note: Sec-WebSocket-Key is not used in RFC8441;
8605d1aa5d0SGlenn Strauss * include Sec-WebSocket-Key for HTTP/1.1 compatibility;
8615d1aa5d0SGlenn Strauss * !!not random!! base64-encoded "0000000000000000" */
8625d1aa5d0SGlenn Strauss if (!http_header_request_get(r, HTTP_HEADER_OTHER,
8635d1aa5d0SGlenn Strauss CONST_STR_LEN("Sec-WebSocket-Key")))
8645d1aa5d0SGlenn Strauss cgi_env_add(env, CONST_STR_LEN("HTTP_SEC_WEBSOCKET_KEY"),
865cda9b716SShulyaka CONST_STR_LEN("MDAwMDAwMDAwMDAwMDAwMA=="));
8665d1aa5d0SGlenn Strauss /*(Upgrade and Connection should not exist for HTTP/2 request)*/
8675d1aa5d0SGlenn Strauss cgi_env_add(env, CONST_STR_LEN("HTTP_UPGRADE"), CONST_STR_LEN("websocket"));
8685d1aa5d0SGlenn Strauss cgi_env_add(env, CONST_STR_LEN("HTTP_CONNECTION"), CONST_STR_LEN("upgrade"));
8695d1aa5d0SGlenn Strauss }
8705d1aa5d0SGlenn Strauss
871bcdc6a3bSJan Kneschke /* for valgrind */
8722d914758SGlenn Strauss if (p->env.ld_preload) {
873af3df29aSGlenn Strauss cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), BUF_PTR_LEN(p->env.ld_preload));
874433f1883SJan Kneschke }
8752d914758SGlenn Strauss if (p->env.ld_library_path) {
876af3df29aSGlenn Strauss cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), BUF_PTR_LEN(p->env.ld_library_path));
877433f1883SJan Kneschke }
878bcdc6a3bSJan Kneschke #ifdef __CYGWIN__
879bcdc6a3bSJan Kneschke /* CYGWIN needs SYSTEMROOT */
8802d914758SGlenn Strauss if (p->env.systemroot) {
881af3df29aSGlenn Strauss cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), BUF_PTR_LEN(p->env.systemroot));
882433f1883SJan Kneschke }
883bcdc6a3bSJan Kneschke #endif
884bcdc6a3bSJan Kneschke
8857b3a4f13SGlenn Strauss /* adjust (uintptr_t) offsets to (char *) ptr
8867b3a4f13SGlenn Strauss * (stored as offsets while accumulating in buffer,
8877b3a4f13SGlenn Strauss * in case buffer is reallocated during env creation) */
8887b3a4f13SGlenn Strauss if (__builtin_expect( (env->osize == env->oused), 0))
8897b3a4f13SGlenn Strauss cgi_env_offset_resize(env);
8907b3a4f13SGlenn Strauss envp = (char **)env->offsets;
8917b3a4f13SGlenn Strauss envp[env->oused] = NULL;
8927b3a4f13SGlenn Strauss const uintptr_t baseptr = (uintptr_t)env->b->ptr;
8937b3a4f13SGlenn Strauss for (i = 0; i < env->oused; ++i)
8947b3a4f13SGlenn Strauss envp[i] += baseptr;
895bcdc6a3bSJan Kneschke
896bcdc6a3bSJan Kneschke /* set up args */
897bcdc6a3bSJan Kneschke i = 0;
898bcdc6a3bSJan Kneschke
899af3df29aSGlenn Strauss if (!buffer_is_blank(cgi_handler)) {
900bcdc6a3bSJan Kneschke args[i++] = cgi_handler->ptr;
901bcdc6a3bSJan Kneschke }
9027c7f8c46SGlenn Strauss args[i++] = r->physical.path.ptr;
9034df22f2aSStefan Bühler args[i ] = NULL;
904bcdc6a3bSJan Kneschke }
905bcdc6a3bSJan Kneschke
9069a2404ceSGlenn Strauss int dfd = fdevent_open_dirname(r->physical.path.ptr,r->conf.follow_symlink);
907a9970fecSGlenn Strauss if (-1 == dfd) {
9087c7f8c46SGlenn Strauss log_perror(r->conf.errh, __FILE__, __LINE__, "open dirname %s failed", r->physical.path.ptr);
909bcdc6a3bSJan Kneschke }
910bcdc6a3bSJan Kneschke
9117b615d5dSGlenn Strauss int serrh_fd = r->conf.serrh ? r->conf.serrh->fd : -1;
9127e000de0SGlenn Strauss pid_t pid = (dfd >= 0)
9137b3a4f13SGlenn Strauss ? fdevent_fork_execve(args[0], args, envp,
9147b3a4f13SGlenn Strauss to_cgi_fds[0], from_cgi_fds[1], serrh_fd, dfd)
9157b3a4f13SGlenn Strauss : -1;
9167b3a4f13SGlenn Strauss
9177b3a4f13SGlenn Strauss chunk_buffer_release(env->boffsets);
9187b3a4f13SGlenn Strauss chunk_buffer_release(env->b);
9197b3a4f13SGlenn Strauss env->boffsets = NULL;
9207b3a4f13SGlenn Strauss env->b = NULL;
921a9970fecSGlenn Strauss
9227e000de0SGlenn Strauss if (-1 == pid) {
923a9970fecSGlenn Strauss /* log error with errno prior to calling close() (might change errno) */
9247c7f8c46SGlenn Strauss log_perror(r->conf.errh, __FILE__, __LINE__, "fork failed");
925a9970fecSGlenn Strauss if (-1 != dfd) close(dfd);
92685b1f6afSStefan Bühler close(from_cgi_fds[0]);
92785b1f6afSStefan Bühler close(from_cgi_fds[1]);
92870195d06SGlenn Strauss if (0 == r->reqbody_length) {
92970195d06SGlenn Strauss close(to_cgi_fds[0]);
93070195d06SGlenn Strauss }
93170195d06SGlenn Strauss else if (-1 != to_cgi_fds[1]) {
93285b1f6afSStefan Bühler close(to_cgi_fds[0]);
93385b1f6afSStefan Bühler close(to_cgi_fds[1]);
93470195d06SGlenn Strauss }
935c065f36bSStefan Bühler return -1;
936a9970fecSGlenn Strauss } else {
937a9970fecSGlenn Strauss if (-1 != dfd) close(dfd);
9381982fda2SJan Kneschke close(from_cgi_fds[1]);
9391982fda2SJan Kneschke
940bcdc6a3bSJan Kneschke hctx->fd = from_cgi_fds[0];
9417e000de0SGlenn Strauss hctx->cgi_pid = cgi_pid_add(p, pid, hctx);
9429030cfaeSGlenn Strauss
9437c7f8c46SGlenn Strauss if (0 == r->reqbody_length) {
94470195d06SGlenn Strauss close(to_cgi_fds[0]);
94570195d06SGlenn Strauss }
94670195d06SGlenn Strauss else if (-1 == to_cgi_fds[1]) {
94770195d06SGlenn Strauss chunkqueue * const cq = &r->reqbody_queue;
94870195d06SGlenn Strauss chunkqueue_mark_written(cq, chunkqueue_length(cq));
9497c0f8a77SGlenn Strauss }
950fc19558fSGlenn Strauss else if (0 == fdevent_fcntl_set_nb(to_cgi_fds[1])
951fc19558fSGlenn Strauss && 0 == cgi_write_request(hctx, to_cgi_fds[1])) {
95270195d06SGlenn Strauss close(to_cgi_fds[0]);
953fc19558fSGlenn Strauss ++r->con->srv->cur_fds;
954fc19558fSGlenn Strauss }
955fc19558fSGlenn Strauss else {
95670195d06SGlenn Strauss close(to_cgi_fds[0]);
9577c0f8a77SGlenn Strauss close(to_cgi_fds[1]);
958fc19558fSGlenn Strauss /*(hctx->fd not yet registered with fdevent, so manually
959fc19558fSGlenn Strauss * cleanup here; see fdevent_register() further below)*/
960fc19558fSGlenn Strauss close(hctx->fd);
961fc19558fSGlenn Strauss hctx->fd = -1;
9627c7f8c46SGlenn Strauss cgi_connection_close(hctx);
9637c0f8a77SGlenn Strauss return -1;
9647c0f8a77SGlenn Strauss }
96540f16d52SGlenn Strauss
9667c7f8c46SGlenn Strauss ++r->con->srv->cur_fds;
9677c0f8a77SGlenn Strauss
9687c7f8c46SGlenn Strauss struct fdevents * const ev = hctx->ev;
969b5775b99SGlenn Strauss hctx->fdn = fdevent_register(ev, hctx->fd, cgi_handle_fdevent, hctx);
97048004c6aSGlenn Strauss if (-1 == fdevent_fcntl_set_nb(hctx->fd)) {
9717c7f8c46SGlenn Strauss log_perror(r->conf.errh, __FILE__, __LINE__, "fcntl failed");
9727c7f8c46SGlenn Strauss cgi_connection_close(hctx);
973bcdc6a3bSJan Kneschke return -1;
974bcdc6a3bSJan Kneschke }
9758c36615fSGlenn Strauss hctx->read_ts = log_monotonic_secs;
976b5775b99SGlenn Strauss fdevent_fdnode_event_set(ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP);
977bcdc6a3bSJan Kneschke
978bcdc6a3bSJan Kneschke return 0;
979a9970fecSGlenn Strauss }
980bcdc6a3bSJan Kneschke }
981bcdc6a3bSJan Kneschke
URIHANDLER_FUNC(cgi_is_handled)982bcdc6a3bSJan Kneschke URIHANDLER_FUNC(cgi_is_handled) {
983bcdc6a3bSJan Kneschke plugin_data *p = p_d;
984fe021118SGlenn Strauss const stat_cache_st *st;
985d61f3381SGlenn Strauss data_string *ds;
986bcdc6a3bSJan Kneschke
9877c7f8c46SGlenn Strauss if (NULL != r->handler_module) return HANDLER_GO_ON;
9889a5e1652SGlenn Strauss /* r->physical.path is non-empty for handle_subrequest_start */
989af3df29aSGlenn Strauss /*if (buffer_is_blank(&r->physical.path)) return HANDLER_GO_ON;*/
990bcdc6a3bSJan Kneschke
9917c7f8c46SGlenn Strauss mod_cgi_patch_config(r, p);
992131450daSGlenn Strauss if (NULL == p->conf.cgi) return HANDLER_GO_ON;
993bcdc6a3bSJan Kneschke
9947c7f8c46SGlenn Strauss ds = (data_string *)array_match_key_suffix(p->conf.cgi, &r->physical.path);
995d61f3381SGlenn Strauss if (NULL == ds) return HANDLER_GO_ON;
996d61f3381SGlenn Strauss
9979a5e1652SGlenn Strauss /* r->tmp_sce is set in http_response_physical_path_check() and is valid
9989a5e1652SGlenn Strauss * in handle_subrequest_start callback -- handle_subrequest_start callbacks
9999a5e1652SGlenn Strauss * should not change r->physical.path (or should invalidate r->tmp_sce) */
10009a5e1652SGlenn Strauss st = r->tmp_sce && buffer_is_equal(&r->tmp_sce->name, &r->physical.path)
10019a5e1652SGlenn Strauss ? &r->tmp_sce->st
10029a5e1652SGlenn Strauss : stat_cache_path_stat(&r->physical.path);
1003aa00359eSGlenn Strauss if (NULL == st) return HANDLER_GO_ON;
1004b9f245f2SGlenn Strauss
1005fe021118SGlenn Strauss /* (aside: CGI might be executable even if it is not readable) */
1006b9f245f2SGlenn Strauss if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON;
1007b9f245f2SGlenn Strauss if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
1008ff5c18beSStefan Bühler
10095d1aa5d0SGlenn Strauss if (__builtin_expect( (r->h2_connect_ext != 0), 0) && !p->conf.upgrade) {
10105d1aa5d0SGlenn Strauss r->http_status = 405; /* Method Not Allowed */
10115d1aa5d0SGlenn Strauss return HANDLER_FINISHED;
10125d1aa5d0SGlenn Strauss }
10135d1aa5d0SGlenn Strauss
101470195d06SGlenn Strauss if (r->reqbody_length
101570195d06SGlenn Strauss && p->tempfile_accum
101670195d06SGlenn Strauss && !(r->conf.stream_request_body /*(if not streaming request body)*/
101770195d06SGlenn Strauss & (FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN))) {
101870195d06SGlenn Strauss /* store request body in single tempfile if not streaming request body*/
101973df7b64SGlenn Strauss r->reqbody_queue.upload_temp_file_size =
102073df7b64SGlenn Strauss (off_t)((1uLL << (sizeof(off_t)*8-1))-1);
102170195d06SGlenn Strauss }
102270195d06SGlenn Strauss
1023d61f3381SGlenn Strauss {
1024bbbbfb3dSGlenn Strauss handler_ctx *hctx = cgi_handler_ctx_init();
10257c7f8c46SGlenn Strauss hctx->ev = r->con->srv->ev;
10267c7f8c46SGlenn Strauss hctx->r = r;
1027dd23fcb2SGlenn Strauss hctx->con = r->con;
1028bbbbfb3dSGlenn Strauss hctx->plugin_data = p;
1029601c572cSGlenn Strauss hctx->cgi_handler = &ds->value;
10305bf5e1adSGlenn Strauss memcpy(&hctx->conf, &p->conf, sizeof(plugin_config));
10315d1aa5d0SGlenn Strauss if (__builtin_expect( (r->h2_connect_ext != 0), 0)) {
10325d1aa5d0SGlenn Strauss }
10335d1aa5d0SGlenn Strauss else if (!light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE))
1034e8a6ed6eSGlenn Strauss hctx->conf.upgrade = 0;
1035e8a6ed6eSGlenn Strauss else if (!hctx->conf.upgrade || r->http_version != HTTP_VERSION_1_1) {
1036e8a6ed6eSGlenn Strauss hctx->conf.upgrade = 0;
1037e8a6ed6eSGlenn Strauss http_header_request_unset(r, HTTP_HEADER_UPGRADE,
1038e8a6ed6eSGlenn Strauss CONST_STR_LEN("Upgrade"));
1039e8a6ed6eSGlenn Strauss }
1040ce9e0dfcSGlenn Strauss if (hctx->conf.upgrade) {
1041ce9e0dfcSGlenn Strauss hctx->orig_reqbody_length = r->reqbody_length;
1042ce9e0dfcSGlenn Strauss r->reqbody_length = -1;
1043ce9e0dfcSGlenn Strauss }
1044f19f7162SGlenn Strauss hctx->opts.max_per_read =
1045f19f7162SGlenn Strauss !(r->conf.stream_response_body /*(if not streaming response body)*/
1046f19f7162SGlenn Strauss & (FDEVENT_STREAM_RESPONSE|FDEVENT_STREAM_RESPONSE_BUFMIN))
1047f19f7162SGlenn Strauss ? 262144
1048f19f7162SGlenn Strauss : (r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
1049f19f7162SGlenn Strauss ? 16384 /* FDEVENT_STREAM_RESPONSE_BUFMIN */
1050f19f7162SGlenn Strauss : 65536; /* FDEVENT_STREAM_RESPONSE */
10514796313eSGlenn Strauss hctx->opts.fdfmt = S_IFIFO;
10520a635fc8SGlenn Strauss hctx->opts.backend = BACKEND_CGI;
10530a635fc8SGlenn Strauss hctx->opts.authorizer = 0;
10540a635fc8SGlenn Strauss hctx->opts.local_redir = hctx->conf.local_redir;
10550a635fc8SGlenn Strauss hctx->opts.xsendfile_allow = hctx->conf.xsendfile_allow;
10560a635fc8SGlenn Strauss hctx->opts.xsendfile_docroot = hctx->conf.xsendfile_docroot;
105745b970e6SGlenn Strauss hctx->opts.pdata = hctx;
105845b970e6SGlenn Strauss hctx->opts.headers = cgi_response_headers;
10597c7f8c46SGlenn Strauss r->plugin_ctx[p->id] = hctx;
10607c7f8c46SGlenn Strauss r->handler_module = p->self;
10612bc0d288SJan Kneschke }
1062bcdc6a3bSJan Kneschke
1063bcdc6a3bSJan Kneschke return HANDLER_GO_ON;
1064bcdc6a3bSJan Kneschke }
1065bcdc6a3bSJan Kneschke
106673e0bb27SStefan Bühler /*
106773e0bb27SStefan Bühler * - HANDLER_GO_ON : not our job
1068e5e66f79SGlenn Strauss * - HANDLER_FINISHED: got response
1069e5e66f79SGlenn Strauss * - HANDLER_WAIT_FOR_EVENT: waiting for response
107073e0bb27SStefan Bühler */
SUBREQUEST_FUNC(mod_cgi_handle_subrequest)1071bcdc6a3bSJan Kneschke SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
107250bdb55dSGlenn Strauss plugin_data * const p = p_d;
10737c7f8c46SGlenn Strauss handler_ctx * const hctx = r->plugin_ctx[p->id];
1074bcdc6a3bSJan Kneschke if (NULL == hctx) return HANDLER_GO_ON;
1075bcdc6a3bSJan Kneschke
107633ffec35SGlenn Strauss if (__builtin_expect(
107733ffec35SGlenn Strauss (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN), 0)
107833ffec35SGlenn Strauss && hctx->conf.limits && hctx->conf.limits->signal_fin) {
107933ffec35SGlenn Strauss /* XXX: consider setting r->http_status = 499 if (0 == r->http_status)
108033ffec35SGlenn Strauss * (499 is nginx custom status to indicate client closed connection) */
108133ffec35SGlenn Strauss if (-1 == hctx->fd) return HANDLER_ERROR; /*(CGI not yet spawned)*/
108233ffec35SGlenn Strauss if (hctx->cgi_pid) /* send signal to notify CGI about TCP FIN */
108333ffec35SGlenn Strauss cgi_pid_kill(hctx->cgi_pid, hctx->conf.limits->signal_fin);
108433ffec35SGlenn Strauss }
108533ffec35SGlenn Strauss
1086dd23fcb2SGlenn Strauss const int rd_revents = hctx->rd_revents;
1087dd23fcb2SGlenn Strauss const int wr_revents = hctx->wr_revents;
1088dd23fcb2SGlenn Strauss if (rd_revents) {
1089dd23fcb2SGlenn Strauss hctx->rd_revents = 0;
1090dd23fcb2SGlenn Strauss handler_t rc = cgi_process_rd_revents(hctx, r, rd_revents);
1091dd23fcb2SGlenn Strauss if (rc != HANDLER_GO_ON) return rc; /*(might invalidate hctx)*/
1092dd23fcb2SGlenn Strauss }
1093dd23fcb2SGlenn Strauss if (wr_revents) {
1094dd23fcb2SGlenn Strauss hctx->wr_revents = 0;
1095dd23fcb2SGlenn Strauss handler_t rc = cgi_process_wr_revents(hctx, r, wr_revents);
1096dd23fcb2SGlenn Strauss if (rc != HANDLER_GO_ON) return rc; /*(might invalidate hctx)*/
1097dd23fcb2SGlenn Strauss }
10983f4f9344SGlenn Strauss
10997c7f8c46SGlenn Strauss if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
11007c7f8c46SGlenn Strauss && r->resp_body_started) {
110181029b8bSGlenn Strauss if (chunkqueue_length(&r->write_queue) > 65536 - 4096) {
11027c7f8c46SGlenn Strauss fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_IN);
11039113011dSGlenn Strauss } else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) {
1104e4bb5622SGlenn Strauss /* optimistic read from backend */
11057c7f8c46SGlenn Strauss handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/
110618a7b2beSGlenn Strauss if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
11078c36615fSGlenn Strauss hctx->read_ts = log_monotonic_secs;
11087c7f8c46SGlenn Strauss fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN);
110918a7b2beSGlenn Strauss }
111018a7b2beSGlenn Strauss }
111118a7b2beSGlenn Strauss
111281029b8bSGlenn Strauss chunkqueue * const cq = &r->reqbody_queue;
111350bdb55dSGlenn Strauss
11147c7f8c46SGlenn Strauss if (cq->bytes_in != (off_t)r->reqbody_length) {
1115f69f209eSGlenn Strauss /*(64k - 4k to attempt to avoid temporary files
1116f69f209eSGlenn Strauss * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
111797e314fcSGlenn Strauss if (chunkqueue_length(cq) > 65536 - 4096
11187c7f8c46SGlenn Strauss && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){
11197c7f8c46SGlenn Strauss r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
1120f69f209eSGlenn Strauss } else {
11218940fec8SGlenn Strauss handler_t rc = r->con->reqbody_read(r);
1122fa5e9b53SGlenn Strauss if (rc != HANDLER_GO_ON
1123317b4ba1SGlenn Strauss && !(hctx->conf.upgrade && -1 == hctx->fd
1124fa5e9b53SGlenn Strauss && rc == HANDLER_WAIT_FOR_EVENT))
1125fa5e9b53SGlenn Strauss return rc;
1126f69f209eSGlenn Strauss }
1127f69f209eSGlenn Strauss }
11284d7f5737SGlenn Strauss
1129fa5e9b53SGlenn Strauss if (-1 == hctx->fd) {
11304d7f5737SGlenn Strauss /* CGI environment requires that Content-Length be set.
11314d7f5737SGlenn Strauss * Send 411 Length Required if Content-Length missing.
11324d7f5737SGlenn Strauss * (occurs here if client sends Transfer-Encoding: chunked
11334d7f5737SGlenn Strauss * and module is flagged to stream request body to backend) */
1134317b4ba1SGlenn Strauss if (-1 == r->reqbody_length && !hctx->conf.upgrade) {
1135bcddbe18SGlenn Strauss return (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)
11362f2eec18SGlenn Strauss ? http_response_reqbody_read_error(r, 411)
1137bcddbe18SGlenn Strauss : HANDLER_WAIT_FOR_EVENT;
11384d7f5737SGlenn Strauss }
11397c7f8c46SGlenn Strauss if (cgi_create_env(r, p, hctx, hctx->cgi_handler)) {
11407c7f8c46SGlenn Strauss r->http_status = 500;
11417c7f8c46SGlenn Strauss r->handler_module = NULL;
1142bbbbfb3dSGlenn Strauss
1143bbbbfb3dSGlenn Strauss return HANDLER_FINISHED;
1144bbbbfb3dSGlenn Strauss }
11451474be78SGlenn Strauss } else if (!chunkqueue_is_empty(cq)) {
1146fa5e9b53SGlenn Strauss if (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)
1147fa5e9b53SGlenn Strauss return HANDLER_WAIT_FOR_EVENT;
114850bdb55dSGlenn Strauss if (0 != cgi_write_request(hctx, hctx->fdtocgi)) {
11497c7f8c46SGlenn Strauss cgi_connection_close(hctx);
1150f69f209eSGlenn Strauss return HANDLER_ERROR;
1151f69f209eSGlenn Strauss }
1152f69f209eSGlenn Strauss }
115373e0bb27SStefan Bühler
1154f2cbd0a3SGlenn Strauss /* if not done, wait for CGI to close stdout, so we read EOF on pipe */
11554ef4baa5SGlenn Strauss return HANDLER_WAIT_FOR_EVENT;
1156bcdc6a3bSJan Kneschke }
1157bcdc6a3bSJan Kneschke
1158bcdc6a3bSJan Kneschke
11598c36615fSGlenn Strauss __attribute_cold__
11608c36615fSGlenn Strauss __attribute_noinline__
cgi_trigger_hctx_timeout(handler_ctx * const hctx,const char * const msg)11618c36615fSGlenn Strauss static void cgi_trigger_hctx_timeout(handler_ctx * const hctx, const char * const msg) {
11628c36615fSGlenn Strauss request_st * const r = hctx->r;
11638c36615fSGlenn Strauss joblist_append(r->con);
11648c36615fSGlenn Strauss
11658c36615fSGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__,
11668c36615fSGlenn Strauss "%s timeout on CGI: %s (pid: %lld)",
11677e000de0SGlenn Strauss msg, r->physical.path.ptr, (long long)hctx->cgi_pid->pid);
11688c36615fSGlenn Strauss
11698c36615fSGlenn Strauss if (*msg == 'w') { /* "write" */
11708c36615fSGlenn Strauss /* theoretically, response might be waiting on hctx->fdn pipe
11718c36615fSGlenn Strauss * if it arrived since we last checked for event, and if CGI
11728c36615fSGlenn Strauss * timeout out while reading (or did not read) request body */
11738c36615fSGlenn Strauss handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/
11748c36615fSGlenn Strauss if (rc != HANDLER_GO_ON) return; /*(unless HANDLER_GO_ON)*/
11758c36615fSGlenn Strauss }
11768c36615fSGlenn Strauss
11778c36615fSGlenn Strauss if (0 == r->http_status) r->http_status = 504; /* Gateway Timeout */
11788c36615fSGlenn Strauss cgi_connection_close(hctx);
11798c36615fSGlenn Strauss }
11808c36615fSGlenn Strauss
11818c36615fSGlenn Strauss
cgi_trigger_cb(server * srv,void * p_d)11828c36615fSGlenn Strauss static handler_t cgi_trigger_cb(server *srv, void *p_d) {
11838c36615fSGlenn Strauss UNUSED(srv);
11848c36615fSGlenn Strauss const unix_time64_t mono = log_monotonic_secs;
11857e000de0SGlenn Strauss plugin_data * const p = p_d;
11867e000de0SGlenn Strauss for (cgi_pid_t *cgi_pid = p->cgi_pid; cgi_pid; cgi_pid = cgi_pid->next) {
11878c36615fSGlenn Strauss /*(hctx stays in cgi_pid list until process pid is reaped,
11888c36615fSGlenn Strauss * so p->cgi_pid[] is not modified during this loop)*/
11897e000de0SGlenn Strauss handler_ctx * const hctx = cgi_pid->hctx;
11908c36615fSGlenn Strauss if (!hctx) continue; /*(already called cgi_pid_kill())*/
11918c36615fSGlenn Strauss const cgi_limits * const limits = hctx->conf.limits;
11928c36615fSGlenn Strauss if (NULL == limits) continue;
11938c36615fSGlenn Strauss if (limits->read_timeout && hctx->fdn
11948c36615fSGlenn Strauss && (fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)
11958c36615fSGlenn Strauss && mono - hctx->read_ts > limits->read_timeout) {
11968c36615fSGlenn Strauss cgi_trigger_hctx_timeout(hctx, "read");
11978c36615fSGlenn Strauss continue;
11988c36615fSGlenn Strauss }
11998c36615fSGlenn Strauss if (limits->write_timeout && hctx->fdntocgi
12008c36615fSGlenn Strauss && (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)
12018c36615fSGlenn Strauss && mono - hctx->write_ts > limits->write_timeout) {
12028c36615fSGlenn Strauss cgi_trigger_hctx_timeout(hctx, "write");
12038c36615fSGlenn Strauss continue;
12048c36615fSGlenn Strauss }
12058c36615fSGlenn Strauss }
12068c36615fSGlenn Strauss return HANDLER_GO_ON;
12078c36615fSGlenn Strauss }
12088c36615fSGlenn Strauss
12098c36615fSGlenn Strauss
cgi_waitpid_cb(server * srv,void * p_d,pid_t pid,int status)12109030cfaeSGlenn Strauss static handler_t cgi_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) {
12117e000de0SGlenn Strauss /*(XXX: if supporting a large number of CGI, might use a different algorithm
12127e000de0SGlenn Strauss * instead of linked list, e.g. splaytree indexed with pid)*/
12139030cfaeSGlenn Strauss plugin_data *p = (plugin_data *)p_d;
12147e000de0SGlenn Strauss for (cgi_pid_t *cgi_pid = p->cgi_pid; cgi_pid; cgi_pid = cgi_pid->next) {
12157e000de0SGlenn Strauss if (pid != cgi_pid->pid) continue;
12169030cfaeSGlenn Strauss
12177e000de0SGlenn Strauss handler_ctx * const hctx = cgi_pid->hctx;
12187e000de0SGlenn Strauss if (hctx) hctx->cgi_pid = NULL;
12199030cfaeSGlenn Strauss
12209030cfaeSGlenn Strauss if (WIFEXITED(status)) {
12219030cfaeSGlenn Strauss /* (skip logging (non-zero) CGI exit; might be very noisy) */
12229030cfaeSGlenn Strauss }
12239030cfaeSGlenn Strauss else if (WIFSIGNALED(status)) {
12249030cfaeSGlenn Strauss /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/
122533ffec35SGlenn Strauss if (WTERMSIG(status) != cgi_pid->signal_sent) {
12267c7f8c46SGlenn Strauss log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh;
1227010c2894SGlenn Strauss log_error(errh, __FILE__, __LINE__,
1228010c2894SGlenn Strauss "CGI pid %d died with signal %d", pid, WTERMSIG(status));
12299030cfaeSGlenn Strauss }
12309030cfaeSGlenn Strauss }
12319030cfaeSGlenn Strauss else {
12327c7f8c46SGlenn Strauss log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh;
1233010c2894SGlenn Strauss log_error(errh, __FILE__, __LINE__,
1234010c2894SGlenn Strauss "CGI pid %d ended unexpectedly", pid);
12359030cfaeSGlenn Strauss }
12369030cfaeSGlenn Strauss
123733ffec35SGlenn Strauss cgi_pid_del(p, cgi_pid);
12389030cfaeSGlenn Strauss return HANDLER_FINISHED;
12399030cfaeSGlenn Strauss }
12409030cfaeSGlenn Strauss
12419030cfaeSGlenn Strauss return HANDLER_GO_ON;
12429030cfaeSGlenn Strauss }
12439030cfaeSGlenn Strauss
12449030cfaeSGlenn Strauss
1245b82d7b8aSGlenn Strauss __attribute_cold__
124663f785a2SStefan Bühler int mod_cgi_plugin_init(plugin *p);
mod_cgi_plugin_init(plugin * p)1247bcdc6a3bSJan Kneschke int mod_cgi_plugin_init(plugin *p) {
1248bcdc6a3bSJan Kneschke p->version = LIGHTTPD_VERSION_ID;
1249e2de4e58SGlenn Strauss p->name = "cgi";
1250bcdc6a3bSJan Kneschke
125133c8cf41SGlenn Strauss p->handle_request_reset = cgi_connection_close_callback;
1252bcdc6a3bSJan Kneschke p->handle_subrequest_start = cgi_is_handled;
1253bcdc6a3bSJan Kneschke p->handle_subrequest = mod_cgi_handle_subrequest;
12548c36615fSGlenn Strauss p->handle_trigger = cgi_trigger_cb;
12559030cfaeSGlenn Strauss p->handle_waitpid = cgi_waitpid_cb;
1256bcdc6a3bSJan Kneschke p->init = mod_cgi_init;
1257bcdc6a3bSJan Kneschke p->cleanup = mod_cgi_free;
1258131450daSGlenn Strauss p->set_defaults = mod_cgi_set_defaults;
1259bcdc6a3bSJan Kneschke
1260bcdc6a3bSJan Kneschke return 0;
1261bcdc6a3bSJan Kneschke }
1262