1*76404edcSAsim Jamshed #include "server.h"
2*76404edcSAsim Jamshed #include "stat_cache.h"
3*76404edcSAsim Jamshed #include "keyvalue.h"
4*76404edcSAsim Jamshed #include "log.h"
5*76404edcSAsim Jamshed #include "connections.h"
6*76404edcSAsim Jamshed #include "joblist.h"
7*76404edcSAsim Jamshed #include "http_chunk.h"
8*76404edcSAsim Jamshed
9*76404edcSAsim Jamshed #include "plugin.h"
10*76404edcSAsim Jamshed
11*76404edcSAsim Jamshed #include <sys/types.h>
12*76404edcSAsim Jamshed
13*76404edcSAsim Jamshed #ifdef __WIN32
14*76404edcSAsim Jamshed # include <winsock2.h>
15*76404edcSAsim Jamshed #else
16*76404edcSAsim Jamshed # include <sys/socket.h>
17*76404edcSAsim Jamshed # include <sys/wait.h>
18*76404edcSAsim Jamshed # include <sys/mman.h>
19*76404edcSAsim Jamshed # include <netinet/in.h>
20*76404edcSAsim Jamshed # include <arpa/inet.h>
21*76404edcSAsim Jamshed #endif
22*76404edcSAsim Jamshed
23*76404edcSAsim Jamshed #include <unistd.h>
24*76404edcSAsim Jamshed #include <errno.h>
25*76404edcSAsim Jamshed #include <stdlib.h>
26*76404edcSAsim Jamshed #include <string.h>
27*76404edcSAsim Jamshed #include <fdevent.h>
28*76404edcSAsim Jamshed #include <signal.h>
29*76404edcSAsim Jamshed #include <ctype.h>
30*76404edcSAsim Jamshed #include <assert.h>
31*76404edcSAsim Jamshed
32*76404edcSAsim Jamshed #include <stdio.h>
33*76404edcSAsim Jamshed #include <fcntl.h>
34*76404edcSAsim Jamshed
35*76404edcSAsim Jamshed #ifdef HAVE_SYS_FILIO_H
36*76404edcSAsim Jamshed # include <sys/filio.h>
37*76404edcSAsim Jamshed #endif
38*76404edcSAsim Jamshed
39*76404edcSAsim Jamshed #include "version.h"
40*76404edcSAsim Jamshed
41*76404edcSAsim Jamshed enum {EOL_UNSET, EOL_N, EOL_RN};
42*76404edcSAsim Jamshed
43*76404edcSAsim Jamshed typedef struct {
44*76404edcSAsim Jamshed char **ptr;
45*76404edcSAsim Jamshed
46*76404edcSAsim Jamshed size_t size;
47*76404edcSAsim Jamshed size_t used;
48*76404edcSAsim Jamshed } char_array;
49*76404edcSAsim Jamshed
50*76404edcSAsim Jamshed typedef struct {
51*76404edcSAsim Jamshed pid_t *ptr;
52*76404edcSAsim Jamshed size_t used;
53*76404edcSAsim Jamshed size_t size;
54*76404edcSAsim Jamshed } buffer_pid_t;
55*76404edcSAsim Jamshed
56*76404edcSAsim Jamshed typedef struct {
57*76404edcSAsim Jamshed array *cgi;
58*76404edcSAsim Jamshed unsigned short execute_x_only;
59*76404edcSAsim Jamshed } plugin_config;
60*76404edcSAsim Jamshed
61*76404edcSAsim Jamshed typedef struct {
62*76404edcSAsim Jamshed PLUGIN_DATA;
63*76404edcSAsim Jamshed buffer_pid_t cgi_pid;
64*76404edcSAsim Jamshed
65*76404edcSAsim Jamshed buffer *tmp_buf;
66*76404edcSAsim Jamshed buffer *parse_response;
67*76404edcSAsim Jamshed
68*76404edcSAsim Jamshed plugin_config **config_storage;
69*76404edcSAsim Jamshed
70*76404edcSAsim Jamshed plugin_config conf;
71*76404edcSAsim Jamshed } plugin_data;
72*76404edcSAsim Jamshed
73*76404edcSAsim Jamshed typedef struct {
74*76404edcSAsim Jamshed pid_t pid;
75*76404edcSAsim Jamshed int fd;
76*76404edcSAsim Jamshed int fde_ndx; /* index into the fd-event buffer */
77*76404edcSAsim Jamshed
78*76404edcSAsim Jamshed connection *remote_conn; /* dumb pointer */
79*76404edcSAsim Jamshed plugin_data *plugin_data; /* dumb pointer */
80*76404edcSAsim Jamshed
81*76404edcSAsim Jamshed buffer *response;
82*76404edcSAsim Jamshed buffer *response_header;
83*76404edcSAsim Jamshed } handler_ctx;
84*76404edcSAsim Jamshed
cgi_handler_ctx_init(void)85*76404edcSAsim Jamshed static handler_ctx * cgi_handler_ctx_init(void) {
86*76404edcSAsim Jamshed handler_ctx *hctx = calloc(1, sizeof(*hctx));
87*76404edcSAsim Jamshed
88*76404edcSAsim Jamshed assert(hctx);
89*76404edcSAsim Jamshed
90*76404edcSAsim Jamshed hctx->response = buffer_init();
91*76404edcSAsim Jamshed hctx->response_header = buffer_init();
92*76404edcSAsim Jamshed
93*76404edcSAsim Jamshed return hctx;
94*76404edcSAsim Jamshed }
95*76404edcSAsim Jamshed
cgi_handler_ctx_free(handler_ctx * hctx)96*76404edcSAsim Jamshed static void cgi_handler_ctx_free(handler_ctx *hctx) {
97*76404edcSAsim Jamshed buffer_free(hctx->response);
98*76404edcSAsim Jamshed buffer_free(hctx->response_header);
99*76404edcSAsim Jamshed
100*76404edcSAsim Jamshed free(hctx);
101*76404edcSAsim Jamshed }
102*76404edcSAsim Jamshed
103*76404edcSAsim Jamshed enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_ERROR};
104*76404edcSAsim Jamshed
INIT_FUNC(mod_cgi_init)105*76404edcSAsim Jamshed INIT_FUNC(mod_cgi_init) {
106*76404edcSAsim Jamshed plugin_data *p;
107*76404edcSAsim Jamshed
108*76404edcSAsim Jamshed p = calloc(1, sizeof(*p));
109*76404edcSAsim Jamshed
110*76404edcSAsim Jamshed assert(p);
111*76404edcSAsim Jamshed
112*76404edcSAsim Jamshed p->tmp_buf = buffer_init();
113*76404edcSAsim Jamshed p->parse_response = buffer_init();
114*76404edcSAsim Jamshed
115*76404edcSAsim Jamshed return p;
116*76404edcSAsim Jamshed }
117*76404edcSAsim Jamshed
118*76404edcSAsim Jamshed
FREE_FUNC(mod_cgi_free)119*76404edcSAsim Jamshed FREE_FUNC(mod_cgi_free) {
120*76404edcSAsim Jamshed plugin_data *p = p_d;
121*76404edcSAsim Jamshed buffer_pid_t *r = &(p->cgi_pid);
122*76404edcSAsim Jamshed
123*76404edcSAsim Jamshed UNUSED(srv);
124*76404edcSAsim Jamshed
125*76404edcSAsim Jamshed if (p->config_storage) {
126*76404edcSAsim Jamshed size_t i;
127*76404edcSAsim Jamshed for (i = 0; i < srv->config_context->used; i++) {
128*76404edcSAsim Jamshed plugin_config *s = p->config_storage[i];
129*76404edcSAsim Jamshed
130*76404edcSAsim Jamshed array_free(s->cgi);
131*76404edcSAsim Jamshed
132*76404edcSAsim Jamshed free(s);
133*76404edcSAsim Jamshed }
134*76404edcSAsim Jamshed free(p->config_storage);
135*76404edcSAsim Jamshed }
136*76404edcSAsim Jamshed
137*76404edcSAsim Jamshed
138*76404edcSAsim Jamshed if (r->ptr) free(r->ptr);
139*76404edcSAsim Jamshed
140*76404edcSAsim Jamshed buffer_free(p->tmp_buf);
141*76404edcSAsim Jamshed buffer_free(p->parse_response);
142*76404edcSAsim Jamshed
143*76404edcSAsim Jamshed free(p);
144*76404edcSAsim Jamshed
145*76404edcSAsim Jamshed return HANDLER_GO_ON;
146*76404edcSAsim Jamshed }
147*76404edcSAsim Jamshed
SETDEFAULTS_FUNC(mod_fastcgi_set_defaults)148*76404edcSAsim Jamshed SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
149*76404edcSAsim Jamshed plugin_data *p = p_d;
150*76404edcSAsim Jamshed size_t i = 0;
151*76404edcSAsim Jamshed
152*76404edcSAsim Jamshed config_values_t cv[] = {
153*76404edcSAsim Jamshed { "cgi.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
154*76404edcSAsim Jamshed { "cgi.execute-x-only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
155*76404edcSAsim Jamshed { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET}
156*76404edcSAsim Jamshed };
157*76404edcSAsim Jamshed
158*76404edcSAsim Jamshed if (!p) return HANDLER_ERROR;
159*76404edcSAsim Jamshed
160*76404edcSAsim Jamshed p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
161*76404edcSAsim Jamshed
162*76404edcSAsim Jamshed for (i = 0; i < srv->config_context->used; i++) {
163*76404edcSAsim Jamshed plugin_config *s;
164*76404edcSAsim Jamshed
165*76404edcSAsim Jamshed s = calloc(1, sizeof(plugin_config));
166*76404edcSAsim Jamshed assert(s);
167*76404edcSAsim Jamshed
168*76404edcSAsim Jamshed s->cgi = array_init();
169*76404edcSAsim Jamshed s->execute_x_only = 0;
170*76404edcSAsim Jamshed
171*76404edcSAsim Jamshed cv[0].destination = s->cgi;
172*76404edcSAsim Jamshed cv[1].destination = &(s->execute_x_only);
173*76404edcSAsim Jamshed
174*76404edcSAsim Jamshed p->config_storage[i] = s;
175*76404edcSAsim Jamshed
176*76404edcSAsim Jamshed if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
177*76404edcSAsim Jamshed return HANDLER_ERROR;
178*76404edcSAsim Jamshed }
179*76404edcSAsim Jamshed }
180*76404edcSAsim Jamshed
181*76404edcSAsim Jamshed return HANDLER_GO_ON;
182*76404edcSAsim Jamshed }
183*76404edcSAsim Jamshed
184*76404edcSAsim Jamshed
cgi_pid_add(server * srv,plugin_data * p,pid_t pid)185*76404edcSAsim Jamshed static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) {
186*76404edcSAsim Jamshed int m = -1;
187*76404edcSAsim Jamshed size_t i;
188*76404edcSAsim Jamshed buffer_pid_t *r = &(p->cgi_pid);
189*76404edcSAsim Jamshed
190*76404edcSAsim Jamshed UNUSED(srv);
191*76404edcSAsim Jamshed
192*76404edcSAsim Jamshed for (i = 0; i < r->used; i++) {
193*76404edcSAsim Jamshed if (r->ptr[i] > m) m = r->ptr[i];
194*76404edcSAsim Jamshed }
195*76404edcSAsim Jamshed
196*76404edcSAsim Jamshed if (r->size == 0) {
197*76404edcSAsim Jamshed r->size = 16;
198*76404edcSAsim Jamshed r->ptr = malloc(sizeof(*r->ptr) * r->size);
199*76404edcSAsim Jamshed } else if (r->used == r->size) {
200*76404edcSAsim Jamshed r->size += 16;
201*76404edcSAsim Jamshed r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size);
202*76404edcSAsim Jamshed }
203*76404edcSAsim Jamshed
204*76404edcSAsim Jamshed r->ptr[r->used++] = pid;
205*76404edcSAsim Jamshed
206*76404edcSAsim Jamshed return m;
207*76404edcSAsim Jamshed }
208*76404edcSAsim Jamshed
cgi_pid_del(server * srv,plugin_data * p,pid_t pid)209*76404edcSAsim Jamshed static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) {
210*76404edcSAsim Jamshed size_t i;
211*76404edcSAsim Jamshed buffer_pid_t *r = &(p->cgi_pid);
212*76404edcSAsim Jamshed
213*76404edcSAsim Jamshed UNUSED(srv);
214*76404edcSAsim Jamshed
215*76404edcSAsim Jamshed for (i = 0; i < r->used; i++) {
216*76404edcSAsim Jamshed if (r->ptr[i] == pid) break;
217*76404edcSAsim Jamshed }
218*76404edcSAsim Jamshed
219*76404edcSAsim Jamshed if (i != r->used) {
220*76404edcSAsim Jamshed /* found */
221*76404edcSAsim Jamshed
222*76404edcSAsim Jamshed if (i != r->used - 1) {
223*76404edcSAsim Jamshed r->ptr[i] = r->ptr[r->used - 1];
224*76404edcSAsim Jamshed }
225*76404edcSAsim Jamshed r->used--;
226*76404edcSAsim Jamshed }
227*76404edcSAsim Jamshed
228*76404edcSAsim Jamshed return 0;
229*76404edcSAsim Jamshed }
230*76404edcSAsim Jamshed
cgi_response_parse(server * srv,connection * con,plugin_data * p,buffer * in)231*76404edcSAsim Jamshed static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
232*76404edcSAsim Jamshed char *ns;
233*76404edcSAsim Jamshed const char *s;
234*76404edcSAsim Jamshed int line = 0;
235*76404edcSAsim Jamshed
236*76404edcSAsim Jamshed UNUSED(srv);
237*76404edcSAsim Jamshed
238*76404edcSAsim Jamshed buffer_copy_string_buffer(p->parse_response, in);
239*76404edcSAsim Jamshed
240*76404edcSAsim Jamshed for (s = p->parse_response->ptr;
241*76404edcSAsim Jamshed NULL != (ns = strchr(s, '\n'));
242*76404edcSAsim Jamshed s = ns + 1, line++) {
243*76404edcSAsim Jamshed const char *key, *value;
244*76404edcSAsim Jamshed int key_len;
245*76404edcSAsim Jamshed data_string *ds;
246*76404edcSAsim Jamshed
247*76404edcSAsim Jamshed /* strip the \n */
248*76404edcSAsim Jamshed ns[0] = '\0';
249*76404edcSAsim Jamshed
250*76404edcSAsim Jamshed if (ns > s && ns[-1] == '\r') ns[-1] = '\0';
251*76404edcSAsim Jamshed
252*76404edcSAsim Jamshed if (line == 0 &&
253*76404edcSAsim Jamshed 0 == strncmp(s, "HTTP/1.", 7)) {
254*76404edcSAsim Jamshed /* non-parsed header ... we parse them anyway */
255*76404edcSAsim Jamshed
256*76404edcSAsim Jamshed if ((s[7] == '1' ||
257*76404edcSAsim Jamshed s[7] == '0') &&
258*76404edcSAsim Jamshed s[8] == ' ') {
259*76404edcSAsim Jamshed int status;
260*76404edcSAsim Jamshed /* after the space should be a status code for us */
261*76404edcSAsim Jamshed
262*76404edcSAsim Jamshed status = strtol(s+9, NULL, 10);
263*76404edcSAsim Jamshed
264*76404edcSAsim Jamshed if (status >= 100 &&
265*76404edcSAsim Jamshed status < 1000) {
266*76404edcSAsim Jamshed /* we expected 3 digits and didn't got them */
267*76404edcSAsim Jamshed con->parsed_response |= HTTP_STATUS;
268*76404edcSAsim Jamshed con->http_status = status;
269*76404edcSAsim Jamshed }
270*76404edcSAsim Jamshed }
271*76404edcSAsim Jamshed } else {
272*76404edcSAsim Jamshed /* parse the headers */
273*76404edcSAsim Jamshed key = s;
274*76404edcSAsim Jamshed if (NULL == (value = strchr(s, ':'))) {
275*76404edcSAsim Jamshed /* we expect: "<key>: <value>\r\n" */
276*76404edcSAsim Jamshed continue;
277*76404edcSAsim Jamshed }
278*76404edcSAsim Jamshed
279*76404edcSAsim Jamshed key_len = value - key;
280*76404edcSAsim Jamshed value += 1;
281*76404edcSAsim Jamshed
282*76404edcSAsim Jamshed /* skip LWS */
283*76404edcSAsim Jamshed while (*value == ' ' || *value == '\t') value++;
284*76404edcSAsim Jamshed
285*76404edcSAsim Jamshed if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
286*76404edcSAsim Jamshed ds = data_response_init();
287*76404edcSAsim Jamshed }
288*76404edcSAsim Jamshed buffer_copy_string_len(ds->key, key, key_len);
289*76404edcSAsim Jamshed buffer_copy_string(ds->value, value);
290*76404edcSAsim Jamshed
291*76404edcSAsim Jamshed array_insert_unique(con->response.headers, (data_unset *)ds);
292*76404edcSAsim Jamshed
293*76404edcSAsim Jamshed switch(key_len) {
294*76404edcSAsim Jamshed case 4:
295*76404edcSAsim Jamshed if (0 == strncasecmp(key, "Date", key_len)) {
296*76404edcSAsim Jamshed con->parsed_response |= HTTP_DATE;
297*76404edcSAsim Jamshed }
298*76404edcSAsim Jamshed break;
299*76404edcSAsim Jamshed case 6:
300*76404edcSAsim Jamshed if (0 == strncasecmp(key, "Status", key_len)) {
301*76404edcSAsim Jamshed con->http_status = strtol(value, NULL, 10);
302*76404edcSAsim Jamshed con->parsed_response |= HTTP_STATUS;
303*76404edcSAsim Jamshed }
304*76404edcSAsim Jamshed break;
305*76404edcSAsim Jamshed case 8:
306*76404edcSAsim Jamshed if (0 == strncasecmp(key, "Location", key_len)) {
307*76404edcSAsim Jamshed con->parsed_response |= HTTP_LOCATION;
308*76404edcSAsim Jamshed }
309*76404edcSAsim Jamshed break;
310*76404edcSAsim Jamshed case 10:
311*76404edcSAsim Jamshed if (0 == strncasecmp(key, "Connection", key_len)) {
312*76404edcSAsim Jamshed con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
313*76404edcSAsim Jamshed con->parsed_response |= HTTP_CONNECTION;
314*76404edcSAsim Jamshed }
315*76404edcSAsim Jamshed break;
316*76404edcSAsim Jamshed case 14:
317*76404edcSAsim Jamshed if (0 == strncasecmp(key, "Content-Length", key_len)) {
318*76404edcSAsim Jamshed con->response.content_length = strtol(value, NULL, 10);
319*76404edcSAsim Jamshed con->parsed_response |= HTTP_CONTENT_LENGTH;
320*76404edcSAsim Jamshed }
321*76404edcSAsim Jamshed break;
322*76404edcSAsim Jamshed default:
323*76404edcSAsim Jamshed break;
324*76404edcSAsim Jamshed }
325*76404edcSAsim Jamshed }
326*76404edcSAsim Jamshed }
327*76404edcSAsim Jamshed
328*76404edcSAsim Jamshed /* CGI/1.1 rev 03 - 7.2.1.2 */
329*76404edcSAsim Jamshed if ((con->parsed_response & HTTP_LOCATION) &&
330*76404edcSAsim Jamshed !(con->parsed_response & HTTP_STATUS)) {
331*76404edcSAsim Jamshed con->http_status = 302;
332*76404edcSAsim Jamshed }
333*76404edcSAsim Jamshed
334*76404edcSAsim Jamshed return 0;
335*76404edcSAsim Jamshed }
336*76404edcSAsim Jamshed
337*76404edcSAsim Jamshed
cgi_demux_response(server * srv,handler_ctx * hctx)338*76404edcSAsim Jamshed static int cgi_demux_response(server *srv, handler_ctx *hctx) {
339*76404edcSAsim Jamshed plugin_data *p = hctx->plugin_data;
340*76404edcSAsim Jamshed connection *con = hctx->remote_conn;
341*76404edcSAsim Jamshed
342*76404edcSAsim Jamshed while(1) {
343*76404edcSAsim Jamshed int n;
344*76404edcSAsim Jamshed int toread;
345*76404edcSAsim Jamshed
346*76404edcSAsim Jamshed #if defined(__WIN32)
347*76404edcSAsim Jamshed buffer_prepare_copy(hctx->response, 4 * 1024);
348*76404edcSAsim Jamshed #else
349*76404edcSAsim Jamshed if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) {
350*76404edcSAsim Jamshed buffer_prepare_copy(hctx->response, 4 * 1024);
351*76404edcSAsim Jamshed } else {
352*76404edcSAsim Jamshed if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
353*76404edcSAsim Jamshed buffer_prepare_copy(hctx->response, toread + 1);
354*76404edcSAsim Jamshed }
355*76404edcSAsim Jamshed #endif
356*76404edcSAsim Jamshed
357*76404edcSAsim Jamshed if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
358*76404edcSAsim Jamshed if (errno == EAGAIN || errno == EINTR) {
359*76404edcSAsim Jamshed /* would block, wait for signal */
360*76404edcSAsim Jamshed return FDEVENT_HANDLED_NOT_FINISHED;
361*76404edcSAsim Jamshed }
362*76404edcSAsim Jamshed /* error */
363*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
364*76404edcSAsim Jamshed return FDEVENT_HANDLED_ERROR;
365*76404edcSAsim Jamshed }
366*76404edcSAsim Jamshed
367*76404edcSAsim Jamshed if (n == 0) {
368*76404edcSAsim Jamshed /* read finished */
369*76404edcSAsim Jamshed
370*76404edcSAsim Jamshed con->file_finished = 1;
371*76404edcSAsim Jamshed
372*76404edcSAsim Jamshed /* send final chunk */
373*76404edcSAsim Jamshed http_chunk_append_mem(srv, con, NULL, 0);
374*76404edcSAsim Jamshed joblist_append(srv, con);
375*76404edcSAsim Jamshed
376*76404edcSAsim Jamshed return FDEVENT_HANDLED_FINISHED;
377*76404edcSAsim Jamshed }
378*76404edcSAsim Jamshed
379*76404edcSAsim Jamshed hctx->response->ptr[n] = '\0';
380*76404edcSAsim Jamshed hctx->response->used = n+1;
381*76404edcSAsim Jamshed
382*76404edcSAsim Jamshed /* split header from body */
383*76404edcSAsim Jamshed
384*76404edcSAsim Jamshed if (con->file_started == 0) {
385*76404edcSAsim Jamshed int is_header = 0;
386*76404edcSAsim Jamshed int is_header_end = 0;
387*76404edcSAsim Jamshed size_t last_eol = 0;
388*76404edcSAsim Jamshed size_t i;
389*76404edcSAsim Jamshed
390*76404edcSAsim Jamshed buffer_append_string_buffer(hctx->response_header, hctx->response);
391*76404edcSAsim Jamshed
392*76404edcSAsim Jamshed /**
393*76404edcSAsim Jamshed * we have to handle a few cases:
394*76404edcSAsim Jamshed *
395*76404edcSAsim Jamshed * nph:
396*76404edcSAsim Jamshed *
397*76404edcSAsim Jamshed * HTTP/1.0 200 Ok\n
398*76404edcSAsim Jamshed * Header: Value\n
399*76404edcSAsim Jamshed * \n
400*76404edcSAsim Jamshed *
401*76404edcSAsim Jamshed * CGI:
402*76404edcSAsim Jamshed * Header: Value\n
403*76404edcSAsim Jamshed * Status: 200\n
404*76404edcSAsim Jamshed * \n
405*76404edcSAsim Jamshed *
406*76404edcSAsim Jamshed * and different mixes of \n and \r\n combinations
407*76404edcSAsim Jamshed *
408*76404edcSAsim Jamshed * Some users also forget about CGI and just send a response and hope
409*76404edcSAsim Jamshed * we handle it. No headers, no header-content seperator
410*76404edcSAsim Jamshed *
411*76404edcSAsim Jamshed */
412*76404edcSAsim Jamshed
413*76404edcSAsim Jamshed /* nph (non-parsed headers) */
414*76404edcSAsim Jamshed if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;
415*76404edcSAsim Jamshed
416*76404edcSAsim Jamshed for (i = 0; !is_header_end && i < hctx->response_header->used - 1; i++) {
417*76404edcSAsim Jamshed char c = hctx->response_header->ptr[i];
418*76404edcSAsim Jamshed
419*76404edcSAsim Jamshed switch (c) {
420*76404edcSAsim Jamshed case ':':
421*76404edcSAsim Jamshed /* we found a colon
422*76404edcSAsim Jamshed *
423*76404edcSAsim Jamshed * looks like we have a normal header
424*76404edcSAsim Jamshed */
425*76404edcSAsim Jamshed is_header = 1;
426*76404edcSAsim Jamshed break;
427*76404edcSAsim Jamshed case '\n':
428*76404edcSAsim Jamshed /* EOL */
429*76404edcSAsim Jamshed if (is_header == 0) {
430*76404edcSAsim Jamshed /* we got a EOL but we don't seem to got a HTTP header */
431*76404edcSAsim Jamshed
432*76404edcSAsim Jamshed is_header_end = 1;
433*76404edcSAsim Jamshed
434*76404edcSAsim Jamshed break;
435*76404edcSAsim Jamshed }
436*76404edcSAsim Jamshed
437*76404edcSAsim Jamshed /**
438*76404edcSAsim Jamshed * check if we saw a \n(\r)?\n sequence
439*76404edcSAsim Jamshed */
440*76404edcSAsim Jamshed if (last_eol > 0 &&
441*76404edcSAsim Jamshed ((i - last_eol == 1) ||
442*76404edcSAsim Jamshed (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
443*76404edcSAsim Jamshed is_header_end = 1;
444*76404edcSAsim Jamshed break;
445*76404edcSAsim Jamshed }
446*76404edcSAsim Jamshed
447*76404edcSAsim Jamshed last_eol = i;
448*76404edcSAsim Jamshed
449*76404edcSAsim Jamshed break;
450*76404edcSAsim Jamshed }
451*76404edcSAsim Jamshed }
452*76404edcSAsim Jamshed
453*76404edcSAsim Jamshed if (is_header_end) {
454*76404edcSAsim Jamshed if (!is_header) {
455*76404edcSAsim Jamshed /* no header, but a body */
456*76404edcSAsim Jamshed
457*76404edcSAsim Jamshed if (con->request.http_version == HTTP_VERSION_1_1) {
458*76404edcSAsim Jamshed con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
459*76404edcSAsim Jamshed }
460*76404edcSAsim Jamshed
461*76404edcSAsim Jamshed http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used);
462*76404edcSAsim Jamshed joblist_append(srv, con);
463*76404edcSAsim Jamshed } else {
464*76404edcSAsim Jamshed const char *bstart;
465*76404edcSAsim Jamshed size_t blen;
466*76404edcSAsim Jamshed
467*76404edcSAsim Jamshed /**
468*76404edcSAsim Jamshed * i still points to the char after the terminating EOL EOL
469*76404edcSAsim Jamshed *
470*76404edcSAsim Jamshed * put it on the last \n again
471*76404edcSAsim Jamshed */
472*76404edcSAsim Jamshed i--;
473*76404edcSAsim Jamshed
474*76404edcSAsim Jamshed /* the body starts after the EOL */
475*76404edcSAsim Jamshed bstart = hctx->response_header->ptr + (i + 1);
476*76404edcSAsim Jamshed blen = (hctx->response_header->used - 1) - (i + 1);
477*76404edcSAsim Jamshed
478*76404edcSAsim Jamshed /* string the last \r?\n */
479*76404edcSAsim Jamshed if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
480*76404edcSAsim Jamshed i--;
481*76404edcSAsim Jamshed }
482*76404edcSAsim Jamshed
483*76404edcSAsim Jamshed hctx->response_header->ptr[i] = '\0';
484*76404edcSAsim Jamshed hctx->response_header->used = i + 1; /* the string + \0 */
485*76404edcSAsim Jamshed
486*76404edcSAsim Jamshed /* parse the response header */
487*76404edcSAsim Jamshed cgi_response_parse(srv, con, p, hctx->response_header);
488*76404edcSAsim Jamshed
489*76404edcSAsim Jamshed /* enable chunked-transfer-encoding */
490*76404edcSAsim Jamshed if (con->request.http_version == HTTP_VERSION_1_1 &&
491*76404edcSAsim Jamshed !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
492*76404edcSAsim Jamshed con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
493*76404edcSAsim Jamshed }
494*76404edcSAsim Jamshed
495*76404edcSAsim Jamshed if (blen > 0) {
496*76404edcSAsim Jamshed http_chunk_append_mem(srv, con, bstart, blen + 1);
497*76404edcSAsim Jamshed joblist_append(srv, con);
498*76404edcSAsim Jamshed }
499*76404edcSAsim Jamshed }
500*76404edcSAsim Jamshed
501*76404edcSAsim Jamshed con->file_started = 1;
502*76404edcSAsim Jamshed }
503*76404edcSAsim Jamshed } else {
504*76404edcSAsim Jamshed http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used);
505*76404edcSAsim Jamshed joblist_append(srv, con);
506*76404edcSAsim Jamshed }
507*76404edcSAsim Jamshed
508*76404edcSAsim Jamshed #if 0
509*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
510*76404edcSAsim Jamshed #endif
511*76404edcSAsim Jamshed }
512*76404edcSAsim Jamshed
513*76404edcSAsim Jamshed return FDEVENT_HANDLED_NOT_FINISHED;
514*76404edcSAsim Jamshed }
515*76404edcSAsim Jamshed
cgi_connection_close(server * srv,handler_ctx * hctx)516*76404edcSAsim Jamshed static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) {
517*76404edcSAsim Jamshed int status;
518*76404edcSAsim Jamshed pid_t pid;
519*76404edcSAsim Jamshed plugin_data *p;
520*76404edcSAsim Jamshed connection *con;
521*76404edcSAsim Jamshed
522*76404edcSAsim Jamshed if (NULL == hctx) return HANDLER_GO_ON;
523*76404edcSAsim Jamshed
524*76404edcSAsim Jamshed p = hctx->plugin_data;
525*76404edcSAsim Jamshed con = hctx->remote_conn;
526*76404edcSAsim Jamshed
527*76404edcSAsim Jamshed if (con->mode != p->id) return HANDLER_GO_ON;
528*76404edcSAsim Jamshed
529*76404edcSAsim Jamshed #ifndef __WIN32
530*76404edcSAsim Jamshed
531*76404edcSAsim Jamshed /* the connection to the browser went away, but we still have a connection
532*76404edcSAsim Jamshed * to the CGI script
533*76404edcSAsim Jamshed *
534*76404edcSAsim Jamshed * close cgi-connection
535*76404edcSAsim Jamshed */
536*76404edcSAsim Jamshed
537*76404edcSAsim Jamshed if (hctx->fd != -1) {
538*76404edcSAsim Jamshed /* close connection to the cgi-script */
539*76404edcSAsim Jamshed fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
540*76404edcSAsim Jamshed fdevent_unregister(srv->ev, hctx->fd);
541*76404edcSAsim Jamshed
542*76404edcSAsim Jamshed if (close(hctx->fd)) {
543*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
544*76404edcSAsim Jamshed }
545*76404edcSAsim Jamshed
546*76404edcSAsim Jamshed hctx->fd = -1;
547*76404edcSAsim Jamshed hctx->fde_ndx = -1;
548*76404edcSAsim Jamshed }
549*76404edcSAsim Jamshed
550*76404edcSAsim Jamshed pid = hctx->pid;
551*76404edcSAsim Jamshed
552*76404edcSAsim Jamshed con->plugin_ctx[p->id] = NULL;
553*76404edcSAsim Jamshed
554*76404edcSAsim Jamshed /* is this a good idea ? */
555*76404edcSAsim Jamshed cgi_handler_ctx_free(hctx);
556*76404edcSAsim Jamshed
557*76404edcSAsim Jamshed /* if waitpid hasn't been called by response.c yet, do it here */
558*76404edcSAsim Jamshed if (pid) {
559*76404edcSAsim Jamshed /* check if the CGI-script is already gone */
560*76404edcSAsim Jamshed switch(waitpid(pid, &status, WNOHANG)) {
561*76404edcSAsim Jamshed case 0:
562*76404edcSAsim Jamshed /* not finished yet */
563*76404edcSAsim Jamshed #if 0
564*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid);
565*76404edcSAsim Jamshed #endif
566*76404edcSAsim Jamshed break;
567*76404edcSAsim Jamshed case -1:
568*76404edcSAsim Jamshed /* */
569*76404edcSAsim Jamshed if (errno == EINTR) break;
570*76404edcSAsim Jamshed
571*76404edcSAsim Jamshed /*
572*76404edcSAsim Jamshed * errno == ECHILD happens if _subrequest catches the process-status before
573*76404edcSAsim Jamshed * we have read the response of the cgi process
574*76404edcSAsim Jamshed *
575*76404edcSAsim Jamshed * -> catch status
576*76404edcSAsim Jamshed * -> WAIT_FOR_EVENT
577*76404edcSAsim Jamshed * -> read response
578*76404edcSAsim Jamshed * -> we get here with waitpid == ECHILD
579*76404edcSAsim Jamshed *
580*76404edcSAsim Jamshed */
581*76404edcSAsim Jamshed if (errno == ECHILD) return HANDLER_GO_ON;
582*76404edcSAsim Jamshed
583*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
584*76404edcSAsim Jamshed return HANDLER_ERROR;
585*76404edcSAsim Jamshed default:
586*76404edcSAsim Jamshed /* Send an error if we haven't sent any data yet */
587*76404edcSAsim Jamshed if (0 == con->file_started) {
588*76404edcSAsim Jamshed connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
589*76404edcSAsim Jamshed con->http_status = 500;
590*76404edcSAsim Jamshed con->mode = DIRECT;
591*76404edcSAsim Jamshed } else {
592*76404edcSAsim Jamshed con->file_finished = 1;
593*76404edcSAsim Jamshed }
594*76404edcSAsim Jamshed
595*76404edcSAsim Jamshed if (WIFEXITED(status)) {
596*76404edcSAsim Jamshed #if 0
597*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid);
598*76404edcSAsim Jamshed #endif
599*76404edcSAsim Jamshed return HANDLER_GO_ON;
600*76404edcSAsim Jamshed } else {
601*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid);
602*76404edcSAsim Jamshed return HANDLER_GO_ON;
603*76404edcSAsim Jamshed }
604*76404edcSAsim Jamshed }
605*76404edcSAsim Jamshed
606*76404edcSAsim Jamshed
607*76404edcSAsim Jamshed kill(pid, SIGTERM);
608*76404edcSAsim Jamshed
609*76404edcSAsim Jamshed /* cgi-script is still alive, queue the PID for removal */
610*76404edcSAsim Jamshed cgi_pid_add(srv, p, pid);
611*76404edcSAsim Jamshed }
612*76404edcSAsim Jamshed #endif
613*76404edcSAsim Jamshed return HANDLER_GO_ON;
614*76404edcSAsim Jamshed }
615*76404edcSAsim Jamshed
cgi_connection_close_callback(server * srv,connection * con,void * p_d)616*76404edcSAsim Jamshed static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) {
617*76404edcSAsim Jamshed plugin_data *p = p_d;
618*76404edcSAsim Jamshed
619*76404edcSAsim Jamshed return cgi_connection_close(srv, con->plugin_ctx[p->id]);
620*76404edcSAsim Jamshed }
621*76404edcSAsim Jamshed
622*76404edcSAsim Jamshed
cgi_handle_fdevent(server * srv,void * ctx,int revents)623*76404edcSAsim Jamshed static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) {
624*76404edcSAsim Jamshed handler_ctx *hctx = ctx;
625*76404edcSAsim Jamshed connection *con = hctx->remote_conn;
626*76404edcSAsim Jamshed
627*76404edcSAsim Jamshed joblist_append(srv, con);
628*76404edcSAsim Jamshed
629*76404edcSAsim Jamshed if (hctx->fd == -1) {
630*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "invalid cgi-fd");
631*76404edcSAsim Jamshed
632*76404edcSAsim Jamshed return HANDLER_ERROR;
633*76404edcSAsim Jamshed }
634*76404edcSAsim Jamshed
635*76404edcSAsim Jamshed if (revents & FDEVENT_IN) {
636*76404edcSAsim Jamshed switch (cgi_demux_response(srv, hctx)) {
637*76404edcSAsim Jamshed case FDEVENT_HANDLED_NOT_FINISHED:
638*76404edcSAsim Jamshed break;
639*76404edcSAsim Jamshed case FDEVENT_HANDLED_FINISHED:
640*76404edcSAsim Jamshed /* we are done */
641*76404edcSAsim Jamshed
642*76404edcSAsim Jamshed #if 0
643*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "finished");
644*76404edcSAsim Jamshed #endif
645*76404edcSAsim Jamshed cgi_connection_close(srv, hctx);
646*76404edcSAsim Jamshed
647*76404edcSAsim Jamshed /* if we get a IN|HUP and have read everything don't exec the close twice */
648*76404edcSAsim Jamshed return HANDLER_FINISHED;
649*76404edcSAsim Jamshed case FDEVENT_HANDLED_ERROR:
650*76404edcSAsim Jamshed /* Send an error if we haven't sent any data yet */
651*76404edcSAsim Jamshed if (0 == con->file_started) {
652*76404edcSAsim Jamshed connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
653*76404edcSAsim Jamshed con->http_status = 500;
654*76404edcSAsim Jamshed con->mode = DIRECT;
655*76404edcSAsim Jamshed } else {
656*76404edcSAsim Jamshed con->file_finished = 1;
657*76404edcSAsim Jamshed }
658*76404edcSAsim Jamshed
659*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: ");
660*76404edcSAsim Jamshed break;
661*76404edcSAsim Jamshed }
662*76404edcSAsim Jamshed }
663*76404edcSAsim Jamshed
664*76404edcSAsim Jamshed if (revents & FDEVENT_OUT) {
665*76404edcSAsim Jamshed /* nothing to do */
666*76404edcSAsim Jamshed }
667*76404edcSAsim Jamshed
668*76404edcSAsim Jamshed /* perhaps this issue is already handled */
669*76404edcSAsim Jamshed if (revents & FDEVENT_HUP) {
670*76404edcSAsim Jamshed /* check if we still have a unfinished header package which is a body in reality */
671*76404edcSAsim Jamshed if (con->file_started == 0 &&
672*76404edcSAsim Jamshed hctx->response_header->used) {
673*76404edcSAsim Jamshed con->file_started = 1;
674*76404edcSAsim Jamshed http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used);
675*76404edcSAsim Jamshed joblist_append(srv, con);
676*76404edcSAsim Jamshed }
677*76404edcSAsim Jamshed
678*76404edcSAsim Jamshed if (con->file_finished == 0) {
679*76404edcSAsim Jamshed http_chunk_append_mem(srv, con, NULL, 0);
680*76404edcSAsim Jamshed joblist_append(srv, con);
681*76404edcSAsim Jamshed }
682*76404edcSAsim Jamshed
683*76404edcSAsim Jamshed con->file_finished = 1;
684*76404edcSAsim Jamshed
685*76404edcSAsim Jamshed if (chunkqueue_is_empty(con->write_queue)) {
686*76404edcSAsim Jamshed /* there is nothing left to write */
687*76404edcSAsim Jamshed connection_set_state(srv, con, CON_STATE_RESPONSE_END);
688*76404edcSAsim Jamshed } else {
689*76404edcSAsim Jamshed /* used the write-handler to finish the request on demand */
690*76404edcSAsim Jamshed
691*76404edcSAsim Jamshed }
692*76404edcSAsim Jamshed
693*76404edcSAsim Jamshed # if 0
694*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sddd", "got HUP from cgi", con->fd, hctx->fd, revents);
695*76404edcSAsim Jamshed # endif
696*76404edcSAsim Jamshed
697*76404edcSAsim Jamshed /* rtsigs didn't liked the close */
698*76404edcSAsim Jamshed cgi_connection_close(srv, hctx);
699*76404edcSAsim Jamshed } else if (revents & FDEVENT_ERR) {
700*76404edcSAsim Jamshed con->file_finished = 1;
701*76404edcSAsim Jamshed
702*76404edcSAsim Jamshed /* kill all connections to the cgi process */
703*76404edcSAsim Jamshed cgi_connection_close(srv, hctx);
704*76404edcSAsim Jamshed #if 1
705*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR");
706*76404edcSAsim Jamshed #endif
707*76404edcSAsim Jamshed return HANDLER_ERROR;
708*76404edcSAsim Jamshed }
709*76404edcSAsim Jamshed
710*76404edcSAsim Jamshed return HANDLER_FINISHED;
711*76404edcSAsim Jamshed }
712*76404edcSAsim Jamshed
713*76404edcSAsim Jamshed
cgi_env_add(char_array * env,const char * key,size_t key_len,const char * val,size_t val_len)714*76404edcSAsim Jamshed static int cgi_env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
715*76404edcSAsim Jamshed char *dst;
716*76404edcSAsim Jamshed
717*76404edcSAsim Jamshed if (!key || !val) return -1;
718*76404edcSAsim Jamshed
719*76404edcSAsim Jamshed dst = malloc(key_len + val_len + 2);
720*76404edcSAsim Jamshed memcpy(dst, key, key_len);
721*76404edcSAsim Jamshed dst[key_len] = '=';
722*76404edcSAsim Jamshed memcpy(dst + key_len + 1, val, val_len);
723*76404edcSAsim Jamshed dst[key_len + 1 + val_len] = '\0';
724*76404edcSAsim Jamshed
725*76404edcSAsim Jamshed if (env->size == 0) {
726*76404edcSAsim Jamshed env->size = 16;
727*76404edcSAsim Jamshed env->ptr = malloc(env->size * sizeof(*env->ptr));
728*76404edcSAsim Jamshed } else if (env->size == env->used) {
729*76404edcSAsim Jamshed env->size += 16;
730*76404edcSAsim Jamshed env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
731*76404edcSAsim Jamshed }
732*76404edcSAsim Jamshed
733*76404edcSAsim Jamshed env->ptr[env->used++] = dst;
734*76404edcSAsim Jamshed
735*76404edcSAsim Jamshed return 0;
736*76404edcSAsim Jamshed }
737*76404edcSAsim Jamshed
cgi_create_env(server * srv,connection * con,plugin_data * p,buffer * cgi_handler)738*76404edcSAsim Jamshed static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) {
739*76404edcSAsim Jamshed pid_t pid;
740*76404edcSAsim Jamshed
741*76404edcSAsim Jamshed #ifdef HAVE_IPV6
742*76404edcSAsim Jamshed char b2[INET6_ADDRSTRLEN + 1];
743*76404edcSAsim Jamshed #endif
744*76404edcSAsim Jamshed
745*76404edcSAsim Jamshed int to_cgi_fds[2];
746*76404edcSAsim Jamshed int from_cgi_fds[2];
747*76404edcSAsim Jamshed struct stat st;
748*76404edcSAsim Jamshed
749*76404edcSAsim Jamshed #ifndef __WIN32
750*76404edcSAsim Jamshed
751*76404edcSAsim Jamshed if (cgi_handler->used > 1) {
752*76404edcSAsim Jamshed /* stat the exec file */
753*76404edcSAsim Jamshed if (-1 == (stat(cgi_handler->ptr, &st))) {
754*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sbss",
755*76404edcSAsim Jamshed "stat for cgi-handler", cgi_handler,
756*76404edcSAsim Jamshed "failed:", strerror(errno));
757*76404edcSAsim Jamshed return -1;
758*76404edcSAsim Jamshed }
759*76404edcSAsim Jamshed }
760*76404edcSAsim Jamshed
761*76404edcSAsim Jamshed if (pipe(to_cgi_fds)) {
762*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
763*76404edcSAsim Jamshed return -1;
764*76404edcSAsim Jamshed }
765*76404edcSAsim Jamshed
766*76404edcSAsim Jamshed if (pipe(from_cgi_fds)) {
767*76404edcSAsim Jamshed close(to_cgi_fds[0]);
768*76404edcSAsim Jamshed close(to_cgi_fds[1]);
769*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
770*76404edcSAsim Jamshed return -1;
771*76404edcSAsim Jamshed }
772*76404edcSAsim Jamshed
773*76404edcSAsim Jamshed /* fork, execve */
774*76404edcSAsim Jamshed switch (pid = fork()) {
775*76404edcSAsim Jamshed case 0: {
776*76404edcSAsim Jamshed /* child */
777*76404edcSAsim Jamshed char **args;
778*76404edcSAsim Jamshed int argc;
779*76404edcSAsim Jamshed int i = 0;
780*76404edcSAsim Jamshed char buf[32];
781*76404edcSAsim Jamshed size_t n;
782*76404edcSAsim Jamshed char_array env;
783*76404edcSAsim Jamshed char *c;
784*76404edcSAsim Jamshed const char *s;
785*76404edcSAsim Jamshed server_socket *srv_sock = con->srv_socket;
786*76404edcSAsim Jamshed
787*76404edcSAsim Jamshed /* move stdout to from_cgi_fd[1] */
788*76404edcSAsim Jamshed close(STDOUT_FILENO);
789*76404edcSAsim Jamshed dup2(from_cgi_fds[1], STDOUT_FILENO);
790*76404edcSAsim Jamshed close(from_cgi_fds[1]);
791*76404edcSAsim Jamshed /* not needed */
792*76404edcSAsim Jamshed close(from_cgi_fds[0]);
793*76404edcSAsim Jamshed
794*76404edcSAsim Jamshed /* move the stdin to to_cgi_fd[0] */
795*76404edcSAsim Jamshed close(STDIN_FILENO);
796*76404edcSAsim Jamshed dup2(to_cgi_fds[0], STDIN_FILENO);
797*76404edcSAsim Jamshed close(to_cgi_fds[0]);
798*76404edcSAsim Jamshed /* not needed */
799*76404edcSAsim Jamshed close(to_cgi_fds[1]);
800*76404edcSAsim Jamshed
801*76404edcSAsim Jamshed /* create environment */
802*76404edcSAsim Jamshed env.ptr = NULL;
803*76404edcSAsim Jamshed env.size = 0;
804*76404edcSAsim Jamshed env.used = 0;
805*76404edcSAsim Jamshed
806*76404edcSAsim Jamshed if (buffer_is_empty(con->conf.server_tag)) {
807*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC));
808*76404edcSAsim Jamshed } else {
809*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag));
810*76404edcSAsim Jamshed }
811*76404edcSAsim Jamshed
812*76404edcSAsim Jamshed if (!buffer_is_empty(con->server_name)) {
813*76404edcSAsim Jamshed size_t len = con->server_name->used - 1;
814*76404edcSAsim Jamshed
815*76404edcSAsim Jamshed if (con->server_name->ptr[0] == '[') {
816*76404edcSAsim Jamshed const char *colon = strstr(con->server_name->ptr, "]:");
817*76404edcSAsim Jamshed if (colon) len = (colon + 1) - con->server_name->ptr;
818*76404edcSAsim Jamshed } else {
819*76404edcSAsim Jamshed const char *colon = strchr(con->server_name->ptr, ':');
820*76404edcSAsim Jamshed if (colon) len = colon - con->server_name->ptr;
821*76404edcSAsim Jamshed }
822*76404edcSAsim Jamshed
823*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len);
824*76404edcSAsim Jamshed } else {
825*76404edcSAsim Jamshed #ifdef HAVE_IPV6
826*76404edcSAsim Jamshed s = inet_ntop(srv_sock->addr.plain.sa_family,
827*76404edcSAsim Jamshed srv_sock->addr.plain.sa_family == AF_INET6 ?
828*76404edcSAsim Jamshed (const void *) &(srv_sock->addr.ipv6.sin6_addr) :
829*76404edcSAsim Jamshed (const void *) &(srv_sock->addr.ipv4.sin_addr),
830*76404edcSAsim Jamshed b2, sizeof(b2)-1);
831*76404edcSAsim Jamshed #else
832*76404edcSAsim Jamshed s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
833*76404edcSAsim Jamshed #endif
834*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s));
835*76404edcSAsim Jamshed }
836*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));
837*76404edcSAsim Jamshed
838*76404edcSAsim Jamshed s = get_http_version_name(con->request.http_version);
839*76404edcSAsim Jamshed
840*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s));
841*76404edcSAsim Jamshed
842*76404edcSAsim Jamshed LI_ltostr(buf,
843*76404edcSAsim Jamshed #ifdef HAVE_IPV6
844*76404edcSAsim Jamshed ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
845*76404edcSAsim Jamshed #else
846*76404edcSAsim Jamshed ntohs(srv_sock->addr.ipv4.sin_port)
847*76404edcSAsim Jamshed #endif
848*76404edcSAsim Jamshed );
849*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf));
850*76404edcSAsim Jamshed
851*76404edcSAsim Jamshed switch (srv_sock->addr.plain.sa_family) {
852*76404edcSAsim Jamshed #ifdef HAVE_IPV6
853*76404edcSAsim Jamshed case AF_INET6:
854*76404edcSAsim Jamshed s = inet_ntop(srv_sock->addr.plain.sa_family,
855*76404edcSAsim Jamshed (const void *) &(srv_sock->addr.ipv6.sin6_addr),
856*76404edcSAsim Jamshed b2, sizeof(b2)-1);
857*76404edcSAsim Jamshed break;
858*76404edcSAsim Jamshed case AF_INET:
859*76404edcSAsim Jamshed s = inet_ntop(srv_sock->addr.plain.sa_family,
860*76404edcSAsim Jamshed (const void *) &(srv_sock->addr.ipv4.sin_addr),
861*76404edcSAsim Jamshed b2, sizeof(b2)-1);
862*76404edcSAsim Jamshed break;
863*76404edcSAsim Jamshed #else
864*76404edcSAsim Jamshed case AF_INET:
865*76404edcSAsim Jamshed s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
866*76404edcSAsim Jamshed break;
867*76404edcSAsim Jamshed #endif
868*76404edcSAsim Jamshed default:
869*76404edcSAsim Jamshed s = "";
870*76404edcSAsim Jamshed break;
871*76404edcSAsim Jamshed }
872*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s));
873*76404edcSAsim Jamshed
874*76404edcSAsim Jamshed s = get_http_method_name(con->request.http_method);
875*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s));
876*76404edcSAsim Jamshed
877*76404edcSAsim Jamshed if (!buffer_is_empty(con->request.pathinfo)) {
878*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
879*76404edcSAsim Jamshed }
880*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200"));
881*76404edcSAsim Jamshed if (!buffer_is_empty(con->uri.query)) {
882*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query));
883*76404edcSAsim Jamshed }
884*76404edcSAsim Jamshed if (!buffer_is_empty(con->request.orig_uri)) {
885*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
886*76404edcSAsim Jamshed }
887*76404edcSAsim Jamshed
888*76404edcSAsim Jamshed
889*76404edcSAsim Jamshed switch (con->dst_addr.plain.sa_family) {
890*76404edcSAsim Jamshed #ifdef HAVE_IPV6
891*76404edcSAsim Jamshed case AF_INET6:
892*76404edcSAsim Jamshed s = inet_ntop(con->dst_addr.plain.sa_family,
893*76404edcSAsim Jamshed (const void *) &(con->dst_addr.ipv6.sin6_addr),
894*76404edcSAsim Jamshed b2, sizeof(b2)-1);
895*76404edcSAsim Jamshed break;
896*76404edcSAsim Jamshed case AF_INET:
897*76404edcSAsim Jamshed s = inet_ntop(con->dst_addr.plain.sa_family,
898*76404edcSAsim Jamshed (const void *) &(con->dst_addr.ipv4.sin_addr),
899*76404edcSAsim Jamshed b2, sizeof(b2)-1);
900*76404edcSAsim Jamshed break;
901*76404edcSAsim Jamshed #else
902*76404edcSAsim Jamshed case AF_INET:
903*76404edcSAsim Jamshed s = inet_ntoa(con->dst_addr.ipv4.sin_addr);
904*76404edcSAsim Jamshed break;
905*76404edcSAsim Jamshed #endif
906*76404edcSAsim Jamshed default:
907*76404edcSAsim Jamshed s = "";
908*76404edcSAsim Jamshed break;
909*76404edcSAsim Jamshed }
910*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s));
911*76404edcSAsim Jamshed
912*76404edcSAsim Jamshed LI_ltostr(buf,
913*76404edcSAsim Jamshed #ifdef HAVE_IPV6
914*76404edcSAsim Jamshed ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port)
915*76404edcSAsim Jamshed #else
916*76404edcSAsim Jamshed ntohs(con->dst_addr.ipv4.sin_port)
917*76404edcSAsim Jamshed #endif
918*76404edcSAsim Jamshed );
919*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf));
920*76404edcSAsim Jamshed
921*76404edcSAsim Jamshed if (!buffer_is_empty(con->authed_user)) {
922*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("REMOTE_USER"),
923*76404edcSAsim Jamshed CONST_BUF_LEN(con->authed_user));
924*76404edcSAsim Jamshed }
925*76404edcSAsim Jamshed
926*76404edcSAsim Jamshed #ifdef USE_OPENSSL
927*76404edcSAsim Jamshed if (srv_sock->is_ssl) {
928*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
929*76404edcSAsim Jamshed }
930*76404edcSAsim Jamshed #endif
931*76404edcSAsim Jamshed
932*76404edcSAsim Jamshed /* request.content_length < SSIZE_MAX, see request.c */
933*76404edcSAsim Jamshed LI_ltostr(buf, con->request.content_length);
934*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf));
935*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
936*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
937*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
938*76404edcSAsim Jamshed
939*76404edcSAsim Jamshed /* for valgrind */
940*76404edcSAsim Jamshed if (NULL != (s = getenv("LD_PRELOAD"))) {
941*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s));
942*76404edcSAsim Jamshed }
943*76404edcSAsim Jamshed
944*76404edcSAsim Jamshed if (NULL != (s = getenv("LD_LIBRARY_PATH"))) {
945*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s));
946*76404edcSAsim Jamshed }
947*76404edcSAsim Jamshed #ifdef __CYGWIN__
948*76404edcSAsim Jamshed /* CYGWIN needs SYSTEMROOT */
949*76404edcSAsim Jamshed if (NULL != (s = getenv("SYSTEMROOT"))) {
950*76404edcSAsim Jamshed cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s));
951*76404edcSAsim Jamshed }
952*76404edcSAsim Jamshed #endif
953*76404edcSAsim Jamshed
954*76404edcSAsim Jamshed for (n = 0; n < con->request.headers->used; n++) {
955*76404edcSAsim Jamshed data_string *ds;
956*76404edcSAsim Jamshed
957*76404edcSAsim Jamshed ds = (data_string *)con->request.headers->data[n];
958*76404edcSAsim Jamshed
959*76404edcSAsim Jamshed if (ds->value->used && ds->key->used) {
960*76404edcSAsim Jamshed size_t j;
961*76404edcSAsim Jamshed
962*76404edcSAsim Jamshed buffer_reset(p->tmp_buf);
963*76404edcSAsim Jamshed
964*76404edcSAsim Jamshed if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) {
965*76404edcSAsim Jamshed buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_"));
966*76404edcSAsim Jamshed p->tmp_buf->used--; /* strip \0 after HTTP_ */
967*76404edcSAsim Jamshed }
968*76404edcSAsim Jamshed
969*76404edcSAsim Jamshed buffer_prepare_append(p->tmp_buf, ds->key->used + 2);
970*76404edcSAsim Jamshed
971*76404edcSAsim Jamshed for (j = 0; j < ds->key->used - 1; j++) {
972*76404edcSAsim Jamshed char cr = '_';
973*76404edcSAsim Jamshed if (light_isalpha(ds->key->ptr[j])) {
974*76404edcSAsim Jamshed /* upper-case */
975*76404edcSAsim Jamshed cr = ds->key->ptr[j] & ~32;
976*76404edcSAsim Jamshed } else if (light_isdigit(ds->key->ptr[j])) {
977*76404edcSAsim Jamshed /* copy */
978*76404edcSAsim Jamshed cr = ds->key->ptr[j];
979*76404edcSAsim Jamshed }
980*76404edcSAsim Jamshed p->tmp_buf->ptr[p->tmp_buf->used++] = cr;
981*76404edcSAsim Jamshed }
982*76404edcSAsim Jamshed p->tmp_buf->ptr[p->tmp_buf->used++] = '\0';
983*76404edcSAsim Jamshed
984*76404edcSAsim Jamshed cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
985*76404edcSAsim Jamshed }
986*76404edcSAsim Jamshed }
987*76404edcSAsim Jamshed
988*76404edcSAsim Jamshed for (n = 0; n < con->environment->used; n++) {
989*76404edcSAsim Jamshed data_string *ds;
990*76404edcSAsim Jamshed
991*76404edcSAsim Jamshed ds = (data_string *)con->environment->data[n];
992*76404edcSAsim Jamshed
993*76404edcSAsim Jamshed if (ds->value->used && ds->key->used) {
994*76404edcSAsim Jamshed size_t j;
995*76404edcSAsim Jamshed
996*76404edcSAsim Jamshed buffer_reset(p->tmp_buf);
997*76404edcSAsim Jamshed
998*76404edcSAsim Jamshed buffer_prepare_append(p->tmp_buf, ds->key->used + 2);
999*76404edcSAsim Jamshed
1000*76404edcSAsim Jamshed for (j = 0; j < ds->key->used - 1; j++) {
1001*76404edcSAsim Jamshed char cr = '_';
1002*76404edcSAsim Jamshed if (light_isalpha(ds->key->ptr[j])) {
1003*76404edcSAsim Jamshed /* upper-case */
1004*76404edcSAsim Jamshed cr = ds->key->ptr[j] & ~32;
1005*76404edcSAsim Jamshed } else if (light_isdigit(ds->key->ptr[j])) {
1006*76404edcSAsim Jamshed /* copy */
1007*76404edcSAsim Jamshed cr = ds->key->ptr[j];
1008*76404edcSAsim Jamshed }
1009*76404edcSAsim Jamshed p->tmp_buf->ptr[p->tmp_buf->used++] = cr;
1010*76404edcSAsim Jamshed }
1011*76404edcSAsim Jamshed p->tmp_buf->ptr[p->tmp_buf->used++] = '\0';
1012*76404edcSAsim Jamshed
1013*76404edcSAsim Jamshed cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
1014*76404edcSAsim Jamshed }
1015*76404edcSAsim Jamshed }
1016*76404edcSAsim Jamshed
1017*76404edcSAsim Jamshed if (env.size == env.used) {
1018*76404edcSAsim Jamshed env.size += 16;
1019*76404edcSAsim Jamshed env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr));
1020*76404edcSAsim Jamshed }
1021*76404edcSAsim Jamshed
1022*76404edcSAsim Jamshed env.ptr[env.used] = NULL;
1023*76404edcSAsim Jamshed
1024*76404edcSAsim Jamshed /* set up args */
1025*76404edcSAsim Jamshed argc = 3;
1026*76404edcSAsim Jamshed args = malloc(sizeof(*args) * argc);
1027*76404edcSAsim Jamshed i = 0;
1028*76404edcSAsim Jamshed
1029*76404edcSAsim Jamshed if (cgi_handler->used > 1) {
1030*76404edcSAsim Jamshed args[i++] = cgi_handler->ptr;
1031*76404edcSAsim Jamshed }
1032*76404edcSAsim Jamshed args[i++] = con->physical.path->ptr;
1033*76404edcSAsim Jamshed args[i ] = NULL;
1034*76404edcSAsim Jamshed
1035*76404edcSAsim Jamshed /* search for the last / */
1036*76404edcSAsim Jamshed if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) {
1037*76404edcSAsim Jamshed *c = '\0';
1038*76404edcSAsim Jamshed
1039*76404edcSAsim Jamshed /* change to the physical directory */
1040*76404edcSAsim Jamshed if (-1 == chdir(con->physical.path->ptr)) {
1041*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path);
1042*76404edcSAsim Jamshed }
1043*76404edcSAsim Jamshed *c = '/';
1044*76404edcSAsim Jamshed }
1045*76404edcSAsim Jamshed
1046*76404edcSAsim Jamshed /* we don't need the client socket */
1047*76404edcSAsim Jamshed for (i = 3; i < 256; i++) {
1048*76404edcSAsim Jamshed if (i != srv->errorlog_fd) close(i);
1049*76404edcSAsim Jamshed }
1050*76404edcSAsim Jamshed
1051*76404edcSAsim Jamshed /* exec the cgi */
1052*76404edcSAsim Jamshed execve(args[0], args, env.ptr);
1053*76404edcSAsim Jamshed
1054*76404edcSAsim Jamshed /* log_error_write(srv, __FILE__, __LINE__, "sss", "CGI failed:", strerror(errno), args[0]); */
1055*76404edcSAsim Jamshed
1056*76404edcSAsim Jamshed /* */
1057*76404edcSAsim Jamshed SEGFAULT();
1058*76404edcSAsim Jamshed break;
1059*76404edcSAsim Jamshed }
1060*76404edcSAsim Jamshed case -1:
1061*76404edcSAsim Jamshed /* error */
1062*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
1063*76404edcSAsim Jamshed close(from_cgi_fds[0]);
1064*76404edcSAsim Jamshed close(from_cgi_fds[1]);
1065*76404edcSAsim Jamshed close(to_cgi_fds[0]);
1066*76404edcSAsim Jamshed close(to_cgi_fds[1]);
1067*76404edcSAsim Jamshed return -1;
1068*76404edcSAsim Jamshed break;
1069*76404edcSAsim Jamshed default: {
1070*76404edcSAsim Jamshed handler_ctx *hctx;
1071*76404edcSAsim Jamshed /* father */
1072*76404edcSAsim Jamshed
1073*76404edcSAsim Jamshed close(from_cgi_fds[1]);
1074*76404edcSAsim Jamshed close(to_cgi_fds[0]);
1075*76404edcSAsim Jamshed
1076*76404edcSAsim Jamshed if (con->request.content_length) {
1077*76404edcSAsim Jamshed chunkqueue *cq = con->request_content_queue;
1078*76404edcSAsim Jamshed chunk *c;
1079*76404edcSAsim Jamshed
1080*76404edcSAsim Jamshed assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
1081*76404edcSAsim Jamshed
1082*76404edcSAsim Jamshed /* there is content to send */
1083*76404edcSAsim Jamshed for (c = cq->first; c; c = cq->first) {
1084*76404edcSAsim Jamshed int r = 0;
1085*76404edcSAsim Jamshed
1086*76404edcSAsim Jamshed /* copy all chunks */
1087*76404edcSAsim Jamshed switch(c->type) {
1088*76404edcSAsim Jamshed case FILE_CHUNK:
1089*76404edcSAsim Jamshed
1090*76404edcSAsim Jamshed if (c->file.mmap.start == MAP_FAILED) {
1091*76404edcSAsim Jamshed if (-1 == c->file.fd && /* open the file if not already open */
1092*76404edcSAsim Jamshed -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
1093*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
1094*76404edcSAsim Jamshed
1095*76404edcSAsim Jamshed close(from_cgi_fds[0]);
1096*76404edcSAsim Jamshed close(to_cgi_fds[1]);
1097*76404edcSAsim Jamshed return -1;
1098*76404edcSAsim Jamshed }
1099*76404edcSAsim Jamshed
1100*76404edcSAsim Jamshed c->file.mmap.length = c->file.length;
1101*76404edcSAsim Jamshed
1102*76404edcSAsim Jamshed if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
1103*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ",
1104*76404edcSAsim Jamshed strerror(errno), c->file.name, c->file.fd);
1105*76404edcSAsim Jamshed
1106*76404edcSAsim Jamshed close(from_cgi_fds[0]);
1107*76404edcSAsim Jamshed close(to_cgi_fds[1]);
1108*76404edcSAsim Jamshed return -1;
1109*76404edcSAsim Jamshed }
1110*76404edcSAsim Jamshed
1111*76404edcSAsim Jamshed close(c->file.fd);
1112*76404edcSAsim Jamshed c->file.fd = -1;
1113*76404edcSAsim Jamshed
1114*76404edcSAsim Jamshed /* chunk_reset() or chunk_free() will cleanup for us */
1115*76404edcSAsim Jamshed }
1116*76404edcSAsim Jamshed
1117*76404edcSAsim Jamshed if ((r = write(to_cgi_fds[1], c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) {
1118*76404edcSAsim Jamshed switch(errno) {
1119*76404edcSAsim Jamshed case ENOSPC:
1120*76404edcSAsim Jamshed con->http_status = 507;
1121*76404edcSAsim Jamshed break;
1122*76404edcSAsim Jamshed case EINTR:
1123*76404edcSAsim Jamshed continue;
1124*76404edcSAsim Jamshed default:
1125*76404edcSAsim Jamshed con->http_status = 403;
1126*76404edcSAsim Jamshed break;
1127*76404edcSAsim Jamshed }
1128*76404edcSAsim Jamshed }
1129*76404edcSAsim Jamshed break;
1130*76404edcSAsim Jamshed case MEM_CHUNK:
1131*76404edcSAsim Jamshed if ((r = write(to_cgi_fds[1], c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) {
1132*76404edcSAsim Jamshed switch(errno) {
1133*76404edcSAsim Jamshed case ENOSPC:
1134*76404edcSAsim Jamshed con->http_status = 507;
1135*76404edcSAsim Jamshed break;
1136*76404edcSAsim Jamshed case EINTR:
1137*76404edcSAsim Jamshed continue;
1138*76404edcSAsim Jamshed default:
1139*76404edcSAsim Jamshed con->http_status = 403;
1140*76404edcSAsim Jamshed break;
1141*76404edcSAsim Jamshed }
1142*76404edcSAsim Jamshed }
1143*76404edcSAsim Jamshed break;
1144*76404edcSAsim Jamshed case UNUSED_CHUNK:
1145*76404edcSAsim Jamshed break;
1146*76404edcSAsim Jamshed }
1147*76404edcSAsim Jamshed
1148*76404edcSAsim Jamshed if (r > 0) {
1149*76404edcSAsim Jamshed c->offset += r;
1150*76404edcSAsim Jamshed cq->bytes_out += r;
1151*76404edcSAsim Jamshed } else {
1152*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ss", "write() failed due to: ", strerror(errno));
1153*76404edcSAsim Jamshed con->http_status = 500;
1154*76404edcSAsim Jamshed break;
1155*76404edcSAsim Jamshed }
1156*76404edcSAsim Jamshed chunkqueue_remove_finished_chunks(cq);
1157*76404edcSAsim Jamshed }
1158*76404edcSAsim Jamshed }
1159*76404edcSAsim Jamshed
1160*76404edcSAsim Jamshed close(to_cgi_fds[1]);
1161*76404edcSAsim Jamshed
1162*76404edcSAsim Jamshed /* register PID and wait for them asyncronously */
1163*76404edcSAsim Jamshed con->mode = p->id;
1164*76404edcSAsim Jamshed buffer_reset(con->physical.path);
1165*76404edcSAsim Jamshed
1166*76404edcSAsim Jamshed hctx = cgi_handler_ctx_init();
1167*76404edcSAsim Jamshed
1168*76404edcSAsim Jamshed hctx->remote_conn = con;
1169*76404edcSAsim Jamshed hctx->plugin_data = p;
1170*76404edcSAsim Jamshed hctx->pid = pid;
1171*76404edcSAsim Jamshed hctx->fd = from_cgi_fds[0];
1172*76404edcSAsim Jamshed hctx->fde_ndx = -1;
1173*76404edcSAsim Jamshed
1174*76404edcSAsim Jamshed con->plugin_ctx[p->id] = hctx;
1175*76404edcSAsim Jamshed
1176*76404edcSAsim Jamshed fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
1177*76404edcSAsim Jamshed fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1178*76404edcSAsim Jamshed
1179*76404edcSAsim Jamshed if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
1180*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
1181*76404edcSAsim Jamshed
1182*76404edcSAsim Jamshed fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1183*76404edcSAsim Jamshed fdevent_unregister(srv->ev, hctx->fd);
1184*76404edcSAsim Jamshed
1185*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sd", "cgi close:", hctx->fd);
1186*76404edcSAsim Jamshed
1187*76404edcSAsim Jamshed close(hctx->fd);
1188*76404edcSAsim Jamshed
1189*76404edcSAsim Jamshed cgi_handler_ctx_free(hctx);
1190*76404edcSAsim Jamshed
1191*76404edcSAsim Jamshed con->plugin_ctx[p->id] = NULL;
1192*76404edcSAsim Jamshed
1193*76404edcSAsim Jamshed return -1;
1194*76404edcSAsim Jamshed }
1195*76404edcSAsim Jamshed
1196*76404edcSAsim Jamshed break;
1197*76404edcSAsim Jamshed }
1198*76404edcSAsim Jamshed }
1199*76404edcSAsim Jamshed
1200*76404edcSAsim Jamshed return 0;
1201*76404edcSAsim Jamshed #else
1202*76404edcSAsim Jamshed return -1;
1203*76404edcSAsim Jamshed #endif
1204*76404edcSAsim Jamshed }
1205*76404edcSAsim Jamshed
1206*76404edcSAsim Jamshed #define PATCH(x) \
1207*76404edcSAsim Jamshed p->conf.x = s->x;
mod_cgi_patch_connection(server * srv,connection * con,plugin_data * p)1208*76404edcSAsim Jamshed static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) {
1209*76404edcSAsim Jamshed size_t i, j;
1210*76404edcSAsim Jamshed plugin_config *s = p->config_storage[0];
1211*76404edcSAsim Jamshed
1212*76404edcSAsim Jamshed PATCH(cgi);
1213*76404edcSAsim Jamshed PATCH(execute_x_only);
1214*76404edcSAsim Jamshed
1215*76404edcSAsim Jamshed /* skip the first, the global context */
1216*76404edcSAsim Jamshed for (i = 1; i < srv->config_context->used; i++) {
1217*76404edcSAsim Jamshed data_config *dc = (data_config *)srv->config_context->data[i];
1218*76404edcSAsim Jamshed s = p->config_storage[i];
1219*76404edcSAsim Jamshed
1220*76404edcSAsim Jamshed /* condition didn't match */
1221*76404edcSAsim Jamshed if (!config_check_cond(srv, con, dc)) continue;
1222*76404edcSAsim Jamshed
1223*76404edcSAsim Jamshed /* merge config */
1224*76404edcSAsim Jamshed for (j = 0; j < dc->value->used; j++) {
1225*76404edcSAsim Jamshed data_unset *du = dc->value->data[j];
1226*76404edcSAsim Jamshed
1227*76404edcSAsim Jamshed if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.assign"))) {
1228*76404edcSAsim Jamshed PATCH(cgi);
1229*76404edcSAsim Jamshed } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) {
1230*76404edcSAsim Jamshed PATCH(execute_x_only);
1231*76404edcSAsim Jamshed }
1232*76404edcSAsim Jamshed }
1233*76404edcSAsim Jamshed }
1234*76404edcSAsim Jamshed
1235*76404edcSAsim Jamshed return 0;
1236*76404edcSAsim Jamshed }
1237*76404edcSAsim Jamshed #undef PATCH
1238*76404edcSAsim Jamshed
URIHANDLER_FUNC(cgi_is_handled)1239*76404edcSAsim Jamshed URIHANDLER_FUNC(cgi_is_handled) {
1240*76404edcSAsim Jamshed size_t k, s_len;
1241*76404edcSAsim Jamshed plugin_data *p = p_d;
1242*76404edcSAsim Jamshed buffer *fn = con->physical.path;
1243*76404edcSAsim Jamshed stat_cache_entry *sce = NULL;
1244*76404edcSAsim Jamshed
1245*76404edcSAsim Jamshed if (con->mode != DIRECT) return HANDLER_GO_ON;
1246*76404edcSAsim Jamshed
1247*76404edcSAsim Jamshed if (fn->used == 0) return HANDLER_GO_ON;
1248*76404edcSAsim Jamshed
1249*76404edcSAsim Jamshed mod_cgi_patch_connection(srv, con, p);
1250*76404edcSAsim Jamshed
1251*76404edcSAsim Jamshed if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) return HANDLER_GO_ON;
1252*76404edcSAsim Jamshed if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON;
1253*76404edcSAsim Jamshed if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
1254*76404edcSAsim Jamshed
1255*76404edcSAsim Jamshed s_len = fn->used - 1;
1256*76404edcSAsim Jamshed
1257*76404edcSAsim Jamshed for (k = 0; k < p->conf.cgi->used; k++) {
1258*76404edcSAsim Jamshed data_string *ds = (data_string *)p->conf.cgi->data[k];
1259*76404edcSAsim Jamshed size_t ct_len = ds->key->used - 1;
1260*76404edcSAsim Jamshed
1261*76404edcSAsim Jamshed if (ds->key->used == 0) continue;
1262*76404edcSAsim Jamshed if (s_len < ct_len) continue;
1263*76404edcSAsim Jamshed
1264*76404edcSAsim Jamshed if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
1265*76404edcSAsim Jamshed if (cgi_create_env(srv, con, p, ds->value)) {
1266*76404edcSAsim Jamshed con->mode = DIRECT;
1267*76404edcSAsim Jamshed con->http_status = 500;
1268*76404edcSAsim Jamshed
1269*76404edcSAsim Jamshed buffer_reset(con->physical.path);
1270*76404edcSAsim Jamshed return HANDLER_FINISHED;
1271*76404edcSAsim Jamshed }
1272*76404edcSAsim Jamshed /* one handler is enough for the request */
1273*76404edcSAsim Jamshed break;
1274*76404edcSAsim Jamshed }
1275*76404edcSAsim Jamshed }
1276*76404edcSAsim Jamshed
1277*76404edcSAsim Jamshed return HANDLER_GO_ON;
1278*76404edcSAsim Jamshed }
1279*76404edcSAsim Jamshed
TRIGGER_FUNC(cgi_trigger)1280*76404edcSAsim Jamshed TRIGGER_FUNC(cgi_trigger) {
1281*76404edcSAsim Jamshed plugin_data *p = p_d;
1282*76404edcSAsim Jamshed size_t ndx;
1283*76404edcSAsim Jamshed /* the trigger handle only cares about lonely PID which we have to wait for */
1284*76404edcSAsim Jamshed #ifndef __WIN32
1285*76404edcSAsim Jamshed
1286*76404edcSAsim Jamshed for (ndx = 0; ndx < p->cgi_pid.used; ndx++) {
1287*76404edcSAsim Jamshed int status;
1288*76404edcSAsim Jamshed
1289*76404edcSAsim Jamshed switch(waitpid(p->cgi_pid.ptr[ndx], &status, WNOHANG)) {
1290*76404edcSAsim Jamshed case 0:
1291*76404edcSAsim Jamshed /* not finished yet */
1292*76404edcSAsim Jamshed #if 0
1293*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", p->cgi_pid.ptr[ndx]);
1294*76404edcSAsim Jamshed #endif
1295*76404edcSAsim Jamshed break;
1296*76404edcSAsim Jamshed case -1:
1297*76404edcSAsim Jamshed if (errno == ECHILD) {
1298*76404edcSAsim Jamshed /* someone else called waitpid... remove the pid to stop looping the error each time */
1299*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "cgi child vanished, probably someone else called waitpid");
1300*76404edcSAsim Jamshed
1301*76404edcSAsim Jamshed cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1302*76404edcSAsim Jamshed ndx--;
1303*76404edcSAsim Jamshed continue;
1304*76404edcSAsim Jamshed }
1305*76404edcSAsim Jamshed
1306*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
1307*76404edcSAsim Jamshed
1308*76404edcSAsim Jamshed return HANDLER_ERROR;
1309*76404edcSAsim Jamshed default:
1310*76404edcSAsim Jamshed
1311*76404edcSAsim Jamshed if (WIFEXITED(status)) {
1312*76404edcSAsim Jamshed #if 0
1313*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]);
1314*76404edcSAsim Jamshed #endif
1315*76404edcSAsim Jamshed } else if (WIFSIGNALED(status)) {
1316*76404edcSAsim Jamshed /* FIXME: what if we killed the CGI script with a kill(..., SIGTERM) ?
1317*76404edcSAsim Jamshed */
1318*76404edcSAsim Jamshed if (WTERMSIG(status) != SIGTERM) {
1319*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sd", "cleaning up CGI: process died with signal", WTERMSIG(status));
1320*76404edcSAsim Jamshed }
1321*76404edcSAsim Jamshed } else {
1322*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "cleaning up CGI: ended unexpectedly");
1323*76404edcSAsim Jamshed }
1324*76404edcSAsim Jamshed
1325*76404edcSAsim Jamshed cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1326*76404edcSAsim Jamshed /* del modified the buffer structure
1327*76404edcSAsim Jamshed * and copies the last entry to the current one
1328*76404edcSAsim Jamshed * -> recheck the current index
1329*76404edcSAsim Jamshed */
1330*76404edcSAsim Jamshed ndx--;
1331*76404edcSAsim Jamshed }
1332*76404edcSAsim Jamshed }
1333*76404edcSAsim Jamshed #endif
1334*76404edcSAsim Jamshed return HANDLER_GO_ON;
1335*76404edcSAsim Jamshed }
1336*76404edcSAsim Jamshed
1337*76404edcSAsim Jamshed /*
1338*76404edcSAsim Jamshed * - HANDLER_GO_ON : not our job
1339*76404edcSAsim Jamshed * - HANDLER_FINISHED: got response header
1340*76404edcSAsim Jamshed * - HANDLER_WAIT_FOR_EVENT: waiting for response header
1341*76404edcSAsim Jamshed */
SUBREQUEST_FUNC(mod_cgi_handle_subrequest)1342*76404edcSAsim Jamshed SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
1343*76404edcSAsim Jamshed int status;
1344*76404edcSAsim Jamshed plugin_data *p = p_d;
1345*76404edcSAsim Jamshed handler_ctx *hctx = con->plugin_ctx[p->id];
1346*76404edcSAsim Jamshed
1347*76404edcSAsim Jamshed if (con->mode != p->id) return HANDLER_GO_ON;
1348*76404edcSAsim Jamshed if (NULL == hctx) return HANDLER_GO_ON;
1349*76404edcSAsim Jamshed
1350*76404edcSAsim Jamshed #if 0
1351*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid);
1352*76404edcSAsim Jamshed #endif
1353*76404edcSAsim Jamshed
1354*76404edcSAsim Jamshed if (hctx->pid == 0) {
1355*76404edcSAsim Jamshed /* cgi already dead */
1356*76404edcSAsim Jamshed if (!con->file_started) return HANDLER_WAIT_FOR_EVENT;
1357*76404edcSAsim Jamshed return HANDLER_FINISHED;
1358*76404edcSAsim Jamshed }
1359*76404edcSAsim Jamshed
1360*76404edcSAsim Jamshed #ifndef __WIN32
1361*76404edcSAsim Jamshed switch(waitpid(hctx->pid, &status, WNOHANG)) {
1362*76404edcSAsim Jamshed case 0:
1363*76404edcSAsim Jamshed /* we only have for events here if we don't have the header yet,
1364*76404edcSAsim Jamshed * otherwise the event-handler will send us the incoming data */
1365*76404edcSAsim Jamshed if (con->file_started) return HANDLER_FINISHED;
1366*76404edcSAsim Jamshed
1367*76404edcSAsim Jamshed return HANDLER_WAIT_FOR_EVENT;
1368*76404edcSAsim Jamshed case -1:
1369*76404edcSAsim Jamshed if (errno == EINTR) return HANDLER_WAIT_FOR_EVENT;
1370*76404edcSAsim Jamshed
1371*76404edcSAsim Jamshed if (errno == ECHILD && con->file_started == 0) {
1372*76404edcSAsim Jamshed /*
1373*76404edcSAsim Jamshed * second round but still not response
1374*76404edcSAsim Jamshed */
1375*76404edcSAsim Jamshed return HANDLER_WAIT_FOR_EVENT;
1376*76404edcSAsim Jamshed }
1377*76404edcSAsim Jamshed
1378*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
1379*76404edcSAsim Jamshed con->mode = DIRECT;
1380*76404edcSAsim Jamshed con->http_status = 500;
1381*76404edcSAsim Jamshed
1382*76404edcSAsim Jamshed hctx->pid = 0;
1383*76404edcSAsim Jamshed
1384*76404edcSAsim Jamshed fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1385*76404edcSAsim Jamshed fdevent_unregister(srv->ev, hctx->fd);
1386*76404edcSAsim Jamshed
1387*76404edcSAsim Jamshed if (close(hctx->fd)) {
1388*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
1389*76404edcSAsim Jamshed }
1390*76404edcSAsim Jamshed
1391*76404edcSAsim Jamshed cgi_handler_ctx_free(hctx);
1392*76404edcSAsim Jamshed
1393*76404edcSAsim Jamshed con->plugin_ctx[p->id] = NULL;
1394*76404edcSAsim Jamshed
1395*76404edcSAsim Jamshed return HANDLER_FINISHED;
1396*76404edcSAsim Jamshed default:
1397*76404edcSAsim Jamshed /* cgi process exited
1398*76404edcSAsim Jamshed */
1399*76404edcSAsim Jamshed
1400*76404edcSAsim Jamshed hctx->pid = 0;
1401*76404edcSAsim Jamshed
1402*76404edcSAsim Jamshed /* we already have response headers? just continue */
1403*76404edcSAsim Jamshed if (con->file_started) return HANDLER_FINISHED;
1404*76404edcSAsim Jamshed
1405*76404edcSAsim Jamshed if (WIFEXITED(status)) {
1406*76404edcSAsim Jamshed /* clean exit - just continue */
1407*76404edcSAsim Jamshed return HANDLER_WAIT_FOR_EVENT;
1408*76404edcSAsim Jamshed }
1409*76404edcSAsim Jamshed
1410*76404edcSAsim Jamshed /* cgi proc died, and we didn't get any data yet - send error message and close cgi con */
1411*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "cgi died ?");
1412*76404edcSAsim Jamshed
1413*76404edcSAsim Jamshed con->http_status = 500;
1414*76404edcSAsim Jamshed con->mode = DIRECT;
1415*76404edcSAsim Jamshed
1416*76404edcSAsim Jamshed fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1417*76404edcSAsim Jamshed fdevent_unregister(srv->ev, hctx->fd);
1418*76404edcSAsim Jamshed
1419*76404edcSAsim Jamshed if (close(hctx->fd)) {
1420*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
1421*76404edcSAsim Jamshed }
1422*76404edcSAsim Jamshed
1423*76404edcSAsim Jamshed cgi_handler_ctx_free(hctx);
1424*76404edcSAsim Jamshed
1425*76404edcSAsim Jamshed con->plugin_ctx[p->id] = NULL;
1426*76404edcSAsim Jamshed return HANDLER_FINISHED;
1427*76404edcSAsim Jamshed }
1428*76404edcSAsim Jamshed #else
1429*76404edcSAsim Jamshed return HANDLER_ERROR;
1430*76404edcSAsim Jamshed #endif
1431*76404edcSAsim Jamshed }
1432*76404edcSAsim Jamshed
1433*76404edcSAsim Jamshed
1434*76404edcSAsim Jamshed int mod_cgi_plugin_init(plugin *p);
mod_cgi_plugin_init(plugin * p)1435*76404edcSAsim Jamshed int mod_cgi_plugin_init(plugin *p) {
1436*76404edcSAsim Jamshed p->version = LIGHTTPD_VERSION_ID;
1437*76404edcSAsim Jamshed p->name = buffer_init_string("cgi");
1438*76404edcSAsim Jamshed
1439*76404edcSAsim Jamshed p->connection_reset = cgi_connection_close_callback;
1440*76404edcSAsim Jamshed p->handle_subrequest_start = cgi_is_handled;
1441*76404edcSAsim Jamshed p->handle_subrequest = mod_cgi_handle_subrequest;
1442*76404edcSAsim Jamshed #if 0
1443*76404edcSAsim Jamshed p->handle_fdevent = cgi_handle_fdevent;
1444*76404edcSAsim Jamshed #endif
1445*76404edcSAsim Jamshed p->handle_trigger = cgi_trigger;
1446*76404edcSAsim Jamshed p->init = mod_cgi_init;
1447*76404edcSAsim Jamshed p->cleanup = mod_cgi_free;
1448*76404edcSAsim Jamshed p->set_defaults = mod_fastcgi_set_defaults;
1449*76404edcSAsim Jamshed
1450*76404edcSAsim Jamshed p->data = NULL;
1451*76404edcSAsim Jamshed
1452*76404edcSAsim Jamshed return 0;
1453*76404edcSAsim Jamshed }
1454