Imported Upstream version 3.2.0
[platform/upstream/libwebsockets.git] / lib / roles / cgi / cgi-server.c
1 /*
2  * libwebsockets - CGI management
3  *
4  * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation:
9  *  version 2.1 of the License.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA  02110-1301  USA
20  */
21
22 #define  _GNU_SOURCE
23
24 #include "core/private.h"
25
26 #if defined(WIN32) || defined(_WIN32)
27 #else
28 #include <sys/wait.h>
29 #endif
30
31 static const char *hex = "0123456789ABCDEF";
32
33 static int
34 urlencode(const char *in, int inlen, char *out, int outlen)
35 {
36         char *start = out, *end = out + outlen;
37
38         while (inlen-- && out < end - 4) {
39                 if ((*in >= 'A' && *in <= 'Z') ||
40                     (*in >= 'a' && *in <= 'z') ||
41                     (*in >= '0' && *in <= '9') ||
42                     *in == '-' ||
43                     *in == '_' ||
44                     *in == '.' ||
45                     *in == '~') {
46                         *out++ = *in++;
47                         continue;
48                 }
49                 if (*in == ' ') {
50                         *out++ = '+';
51                         in++;
52                         continue;
53                 }
54                 *out++ = '%';
55                 *out++ = hex[(*in) >> 4];
56                 *out++ = hex[(*in++) & 15];
57         }
58         *out = '\0';
59
60         if (out >= end - 4)
61                 return -1;
62
63         return out - start;
64 }
65
66 static struct lws *
67 lws_create_basic_wsi(struct lws_context *context, int tsi)
68 {
69         struct lws *new_wsi;
70
71         if (!context->vhost_list)
72                 return NULL;
73
74         if ((unsigned int)context->pt[tsi].fds_count ==
75             context->fd_limit_per_thread - 1) {
76                 lwsl_err("no space for new conn\n");
77                 return NULL;
78         }
79
80         new_wsi = lws_zalloc(sizeof(struct lws), "new wsi");
81         if (new_wsi == NULL) {
82                 lwsl_err("Out of memory for new connection\n");
83                 return NULL;
84         }
85
86         new_wsi->tsi = tsi;
87         new_wsi->context = context;
88         new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
89         new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
90
91         /* initialize the instance struct */
92
93         lws_role_transition(new_wsi, 0, LRS_ESTABLISHED, &role_ops_cgi);
94
95         new_wsi->hdr_parsing_completed = 0;
96         new_wsi->position_in_fds_table = LWS_NO_FDS_POS;
97
98         /*
99          * these can only be set once the protocol is known
100          * we set an unestablished connection's protocol pointer
101          * to the start of the defauly vhost supported list, so it can look
102          * for matching ones during the handshake
103          */
104         new_wsi->protocol = context->vhost_list->protocols;
105         new_wsi->user_space = NULL;
106         new_wsi->desc.sockfd = LWS_SOCK_INVALID;
107         context->count_wsi_allocated++;
108
109         return new_wsi;
110 }
111
112 LWS_VISIBLE LWS_EXTERN int
113 lws_cgi(struct lws *wsi, const char * const *exec_array,
114         int script_uri_path_len, int timeout_secs,
115         const struct lws_protocol_vhost_options *mp_cgienv)
116 {
117         struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
118         char *env_array[30], cgi_path[500], e[1024], *p = e,
119              *end = p + sizeof(e) - 1, tok[256], *t, *sum, *sumend;
120         struct lws_cgi *cgi;
121         int n, m = 0, i, uritok = -1, c;
122
123         /*
124          * give the master wsi a cgi struct
125          */
126
127         wsi->http.cgi = lws_zalloc(sizeof(*wsi->http.cgi), "new cgi");
128         if (!wsi->http.cgi) {
129                 lwsl_err("%s: OOM\n", __func__);
130                 return -1;
131         }
132
133         wsi->http.cgi->response_code = HTTP_STATUS_OK;
134
135         cgi = wsi->http.cgi;
136         cgi->wsi = wsi; /* set cgi's owning wsi */
137         sum = cgi->summary;
138         sumend = sum + strlen(cgi->summary) - 1;
139
140         for (n = 0; n < 3; n++) {
141                 cgi->pipe_fds[n][0] = -1;
142                 cgi->pipe_fds[n][1] = -1;
143         }
144
145         /* create pipes for [stdin|stdout] and [stderr] */
146
147         for (n = 0; n < 3; n++)
148                 if (pipe(cgi->pipe_fds[n]) == -1)
149                         goto bail1;
150
151         /* create cgi wsis for each stdin/out/err fd */
152
153         for (n = 0; n < 3; n++) {
154                 cgi->stdwsi[n] = lws_create_basic_wsi(wsi->context, wsi->tsi);
155                 if (!cgi->stdwsi[n]) {
156                         lwsl_err("%s: unable to create cgi stdwsi\n", __func__);
157                         goto bail2;
158                 }
159                 cgi->stdwsi[n]->cgi_channel = n;
160                 lws_vhost_bind_wsi(wsi->vhost, cgi->stdwsi[n]);
161
162                 lwsl_debug("%s: cgi stdwsi %p: pipe idx %d -> fd %d / %d\n", __func__,
163                            cgi->stdwsi[n], n, cgi->pipe_fds[n][!!(n == 0)],
164                            cgi->pipe_fds[n][!(n == 0)]);
165
166                 /* read side is 0, stdin we want the write side, others read */
167                 cgi->stdwsi[n]->desc.sockfd = cgi->pipe_fds[n][!!(n == 0)];
168                 if (fcntl(cgi->pipe_fds[n][!!(n == 0)], F_SETFL,
169                     O_NONBLOCK) < 0) {
170                         lwsl_err("%s: setting NONBLOCK failed\n", __func__);
171                         goto bail2;
172                 }
173         }
174
175         for (n = 0; n < 3; n++) {
176                 if (wsi->context->event_loop_ops->accept)
177                         if (wsi->context->event_loop_ops->accept(cgi->stdwsi[n]))
178                                 goto bail3;
179
180                 if (__insert_wsi_socket_into_fds(wsi->context, cgi->stdwsi[n]))
181                         goto bail3;
182                 cgi->stdwsi[n]->parent = wsi;
183                 cgi->stdwsi[n]->sibling_list = wsi->child_list;
184                 wsi->child_list = cgi->stdwsi[n];
185         }
186
187         if (lws_change_pollfd(cgi->stdwsi[LWS_STDIN], LWS_POLLIN, LWS_POLLOUT))
188                 goto bail3;
189         if (lws_change_pollfd(cgi->stdwsi[LWS_STDOUT], LWS_POLLOUT, LWS_POLLIN))
190                 goto bail3;
191         if (lws_change_pollfd(cgi->stdwsi[LWS_STDERR], LWS_POLLOUT, LWS_POLLIN))
192                 goto bail3;
193
194         lwsl_debug("%s: fds in %d, out %d, err %d\n", __func__,
195                    cgi->stdwsi[LWS_STDIN]->desc.sockfd,
196                    cgi->stdwsi[LWS_STDOUT]->desc.sockfd,
197                    cgi->stdwsi[LWS_STDERR]->desc.sockfd);
198
199         if (timeout_secs)
200                 lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs);
201
202         /* the cgi stdout is always sending us http1.x header data first */
203         wsi->hdr_state = LCHS_HEADER;
204
205         /* add us to the pt list of active cgis */
206         lwsl_debug("%s: adding cgi %p to list\n", __func__, wsi->http.cgi);
207         cgi->cgi_list = pt->http.cgi_list;
208         pt->http.cgi_list = cgi;
209
210         sum += lws_snprintf(sum, sumend - sum, "%s ", exec_array[0]);
211
212         if (0) {
213                 char *pct = lws_hdr_simple_ptr(wsi,
214                                 WSI_TOKEN_HTTP_CONTENT_ENCODING);
215
216                 if (pct && !strcmp(pct, "gzip"))
217                         wsi->http.cgi->gzip_inflate = 1;
218         }
219
220         /* prepare his CGI env */
221
222         n = 0;
223
224         if (lws_is_ssl(wsi))
225                 env_array[n++] = "HTTPS=ON";
226         if (wsi->http.ah) {
227                 static const unsigned char meths[] = {
228                         WSI_TOKEN_GET_URI,
229                         WSI_TOKEN_POST_URI,
230                         WSI_TOKEN_OPTIONS_URI,
231                         WSI_TOKEN_PUT_URI,
232                         WSI_TOKEN_PATCH_URI,
233                         WSI_TOKEN_DELETE_URI,
234                         WSI_TOKEN_CONNECT,
235                         WSI_TOKEN_HEAD_URI,
236                 #ifdef LWS_WITH_HTTP2
237                         WSI_TOKEN_HTTP_COLON_PATH,
238                 #endif
239                 };
240                 static const char * const meth_names[] = {
241                         "GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE",
242                         "CONNECT", "HEAD", ":path"
243                 };
244
245                 if (script_uri_path_len >= 0)
246                         for (m = 0; m < (int)LWS_ARRAY_SIZE(meths); m++)
247                                 if (lws_hdr_total_length(wsi, meths[m]) >=
248                                                 script_uri_path_len) {
249                                         uritok = meths[m];
250                                         break;
251                                 }
252
253                 if (script_uri_path_len < 0 && uritok < 0)
254                         goto bail3;
255 //              if (script_uri_path_len < 0)
256 //                      uritok = 0;
257
258                 if (m >= 0) {
259                         env_array[n++] = p;
260                         if (m < 8) {
261                                 p += lws_snprintf(p, end - p,
262                                                   "REQUEST_METHOD=%s",
263                                                   meth_names[m]);
264                                 sum += lws_snprintf(sum, sumend - sum, "%s ",
265                                                     meth_names[m]);
266                         } else {
267                                 p += lws_snprintf(p, end - p,
268                                                   "REQUEST_METHOD=%s",
269                           lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD));
270                                 sum += lws_snprintf(sum, sumend - sum, "%s ",
271                                         lws_hdr_simple_ptr(wsi,
272                                                   WSI_TOKEN_HTTP_COLON_METHOD));
273                         }
274                         p++;
275                 }
276
277                 if (uritok >= 0)
278                         sum += lws_snprintf(sum, sumend - sum, "%s ",
279                                             lws_hdr_simple_ptr(wsi, uritok));
280
281                 env_array[n++] = p;
282                 p += lws_snprintf(p, end - p, "QUERY_STRING=");
283                 /* dump the individual URI Arg parameters */
284                 m = 0;
285                 while (script_uri_path_len >= 0) {
286                         i = lws_hdr_copy_fragment(wsi, tok, sizeof(tok),
287                                              WSI_TOKEN_HTTP_URI_ARGS, m);
288                         if (i < 0)
289                                 break;
290                         t = tok;
291                         while (*t && *t != '=' && p < end - 4)
292                                 *p++ = *t++;
293                         if (*t == '=')
294                                 *p++ = *t++;
295                         i = urlencode(t, i- (t - tok), p, end - p);
296                         if (i > 0) {
297                                 p += i;
298                                 *p++ = '&';
299                         }
300                         m++;
301                 }
302                 if (m)
303                         p--;
304                 *p++ = '\0';
305
306                 if (uritok >= 0) {
307                         strcpy(cgi_path, "REQUEST_URI=");
308                         c = lws_hdr_copy(wsi, cgi_path + 12,
309                                          sizeof(cgi_path) - 12, uritok);
310                         if (c < 0)
311                                 goto bail3;
312
313                         cgi_path[sizeof(cgi_path) - 1] = '\0';
314                         env_array[n++] = cgi_path;
315                 }
316
317                 sum += lws_snprintf(sum, sumend - sum, "%s", env_array[n - 1]);
318
319                 if (script_uri_path_len >= 0) {
320                         env_array[n++] = p;
321                         p += lws_snprintf(p, end - p, "PATH_INFO=%s",
322                                       cgi_path + 12 + script_uri_path_len);
323                         p++;
324                 }
325         }
326         if (script_uri_path_len >= 0 &&
327             lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) {
328                 env_array[n++] = p;
329                 p += lws_snprintf(p, end - p, "HTTP_REFERER=%s",
330                               lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER));
331                 p++;
332         }
333         if (script_uri_path_len >= 0 &&
334             lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
335                 env_array[n++] = p;
336                 p += lws_snprintf(p, end - p, "HTTP_HOST=%s",
337                               lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
338                 p++;
339         }
340         if (script_uri_path_len >= 0 &&
341             lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
342                 env_array[n++] = p;
343                 p += lws_snprintf(p, end - p, "HTTP_COOKIE=");
344                 m = lws_hdr_copy(wsi, p, end - p, WSI_TOKEN_HTTP_COOKIE);
345                 if (m > 0)
346                         p += lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE);
347                 *p++ = '\0';
348         }
349         if (script_uri_path_len >= 0 &&
350             lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) {
351                 env_array[n++] = p;
352                 p += lws_snprintf(p, end - p, "HTTP_USER_AGENT=%s",
353                             lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT));
354                 p++;
355         }
356         if (script_uri_path_len >= 0 &&
357             lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING)) {
358                 env_array[n++] = p;
359                 p += lws_snprintf(p, end - p, "HTTP_CONTENT_ENCODING=%s",
360                       lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING));
361                 p++;
362         }
363         if (script_uri_path_len >= 0 &&
364             lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT)) {
365                 env_array[n++] = p;
366                 p += lws_snprintf(p, end - p, "HTTP_ACCEPT=%s",
367                               lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT));
368                 p++;
369         }
370         if (script_uri_path_len >= 0 &&
371             lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING)) {
372                 env_array[n++] = p;
373                 p += lws_snprintf(p, end - p, "HTTP_ACCEPT_ENCODING=%s",
374                       lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING));
375                 p++;
376         }
377         if (script_uri_path_len >= 0 &&
378             uritok == WSI_TOKEN_POST_URI) {
379                 if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
380                         env_array[n++] = p;
381                         p += lws_snprintf(p, end - p, "CONTENT_TYPE=%s",
382                           lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE));
383                         p++;
384                 }
385                 if (!wsi->http.cgi->gzip_inflate &&
386                     lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
387                         env_array[n++] = p;
388                         p += lws_snprintf(p, end - p, "CONTENT_LENGTH=%s",
389                                           lws_hdr_simple_ptr(wsi,
390                                           WSI_TOKEN_HTTP_CONTENT_LENGTH));
391                         p++;
392                 }
393
394                 if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH))
395                         wsi->http.cgi->post_in_expected =
396                                 atoll(lws_hdr_simple_ptr(wsi,
397                                                 WSI_TOKEN_HTTP_CONTENT_LENGTH));
398         }
399
400
401         env_array[n++] = "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin";
402
403         env_array[n++] = p;
404         p += lws_snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]) + 1;
405
406         while (mp_cgienv) {
407                 env_array[n++] = p;
408                 p += lws_snprintf(p, end - p, "%s=%s", mp_cgienv->name,
409                               mp_cgienv->value);
410                 if (!strcmp(mp_cgienv->name, "GIT_PROJECT_ROOT")) {
411                         wsi->http.cgi->implied_chunked = 1;
412                         wsi->http.cgi->explicitly_chunked = 1;
413                 }
414                 lwsl_info("   Applying mount-specific cgi env '%s'\n",
415                            env_array[n - 1]);
416                 p++;
417                 mp_cgienv = mp_cgienv->next;
418         }
419
420         env_array[n++] = "SERVER_SOFTWARE=libwebsockets";
421         env_array[n] = NULL;
422
423 #if 0
424         for (m = 0; m < n; m++)
425                 lwsl_notice("    %s\n", env_array[m]);
426 #endif
427
428         /*
429          * Actually having made the env, as a cgi we don't need the ah
430          * any more
431          */
432         if (script_uri_path_len >= 0)
433                 lws_header_table_detach(wsi, 0);
434
435         /* we are ready with the redirection pipes... run the thing */
436 #if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE)
437         cgi->pid = fork();
438 #else
439         cgi->pid = vfork();
440 #endif
441         if (cgi->pid < 0) {
442                 lwsl_err("fork failed, errno %d", errno);
443                 goto bail3;
444         }
445
446 #if defined(__linux__)
447         prctl(PR_SET_PDEATHSIG, SIGTERM);
448 #endif
449         if (script_uri_path_len >= 0)
450                 /* stops non-daemonized main processess getting SIGINT
451                  * from TTY */
452                 setpgrp();
453
454         if (cgi->pid) {
455                 /* we are the parent process */
456                 wsi->context->count_cgi_spawned++;
457                 lwsl_info("%s: cgi %p spawned PID %d\n", __func__,
458                            cgi, cgi->pid);
459
460                 /*
461                  *  close:                stdin:r, stdout:w, stderr:w
462                  * hide from other forks: stdin:w, stdout:r, stderr:r
463                  */
464                 for (n = 0; n < 3; n++) {
465                         lws_plat_apply_FD_CLOEXEC(cgi->pipe_fds[n][!!(n == 0)]);
466                         close(cgi->pipe_fds[n][!(n == 0)]);
467                 }
468
469                 /* inform cgi owner of the child PID */
470                 n = user_callback_handle_rxflow(wsi->protocol->callback, wsi,
471                                             LWS_CALLBACK_CGI_PROCESS_ATTACH,
472                                             wsi->user_space, NULL, cgi->pid);
473                 (void)n;
474
475                 return 0;
476         }
477
478         /* somewhere we can at least read things and enter it */
479         if (chdir("/tmp"))
480                 lwsl_notice("%s: Failed to chdir\n", __func__);
481
482         /* We are the forked process, redirect and kill inherited things.
483          *
484          * Because of vfork(), we cannot do anything that changes pages in
485          * the parent environment.  Stuff that changes kernel state for the
486          * process is OK.  Stuff that happens after the execvpe() is OK.
487          */
488
489         for (n = 0; n < 3; n++) {
490                 if (dup2(cgi->pipe_fds[n][!(n == 0)], n) < 0) {
491                         lwsl_err("%s: stdin dup2 failed\n", __func__);
492                         goto bail3;
493                 }
494                 close(cgi->pipe_fds[n][0]);
495                 close(cgi->pipe_fds[n][1]);
496         }
497
498 #if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE)
499         for (m = 0; m < n; m++) {
500                 p = strchr(env_array[m], '=');
501                 *p++ = '\0';
502                 setenv(env_array[m], p, 1);
503         }
504         execvp(exec_array[0], (char * const *)&exec_array[0]);
505 #else
506         execvpe(exec_array[0], (char * const *)&exec_array[0], &env_array[0]);
507 #endif
508
509         exit(1);
510
511 bail3:
512         /* drop us from the pt cgi list */
513         pt->http.cgi_list = cgi->cgi_list;
514
515         while (--n >= 0)
516                 __remove_wsi_socket_from_fds(wsi->http.cgi->stdwsi[n]);
517 bail2:
518         for (n = 0; n < 3; n++)
519                 if (wsi->http.cgi->stdwsi[n])
520                         __lws_free_wsi(cgi->stdwsi[n]);
521
522 bail1:
523         for (n = 0; n < 3; n++) {
524                 if (cgi->pipe_fds[n][0] >= 0)
525                         close(cgi->pipe_fds[n][0]);
526                 if (cgi->pipe_fds[n][1] >= 0)
527                         close(cgi->pipe_fds[n][1]);
528         }
529
530         lws_free_set_NULL(wsi->http.cgi);
531
532         lwsl_err("%s: failed\n", __func__);
533
534         return -1;
535 }
536
537 /* we have to parse out these headers in the CGI output */
538
539 static const char * const significant_hdr[SIGNIFICANT_HDR_COUNT] = {
540         "content-length: ",
541         "location: ",
542         "status: ",
543         "transfer-encoding: chunked",
544         "content-encoding: gzip",
545 };
546
547 enum header_recode {
548         HR_NAME,
549         HR_WHITESPACE,
550         HR_ARG,
551         HR_CRLF,
552 };
553
554 LWS_VISIBLE LWS_EXTERN int
555 lws_cgi_write_split_stdout_headers(struct lws *wsi)
556 {
557         int n, m, cmd;
558         unsigned char buf[LWS_PRE + 4096], *start = &buf[LWS_PRE], *p = start,
559                         *end = &buf[sizeof(buf) - 1 - LWS_PRE], *name,
560                         *value = NULL;
561         char c, hrs;
562
563         if (!wsi->http.cgi)
564                 return -1;
565
566         while (wsi->hdr_state != LHCS_PAYLOAD) {
567                 /*
568                  * We have to separate header / finalize and payload chunks,
569                  * since they need to be handled separately
570                  */
571                 switch (wsi->hdr_state) {
572                 case LHCS_RESPONSE:
573                         lwsl_debug("LHCS_RESPONSE: issuing response %d\n",
574                                    wsi->http.cgi->response_code);
575                         if (lws_add_http_header_status(wsi,
576                                                    wsi->http.cgi->response_code,
577                                                        &p, end))
578                                 return 1;
579                         if (!wsi->http.cgi->explicitly_chunked &&
580                             !wsi->http.cgi->content_length &&
581                                 lws_add_http_header_by_token(wsi,
582                                         WSI_TOKEN_HTTP_TRANSFER_ENCODING,
583                                         (unsigned char *)"chunked", 7, &p, end))
584                                 return 1;
585                         if (!(wsi->http2_substream))
586                                 if (lws_add_http_header_by_token(wsi,
587                                                 WSI_TOKEN_CONNECTION,
588                                                 (unsigned char *)"close", 5,
589                                                 &p, end))
590                                         return 1;
591                         n = lws_write(wsi, start, p - start,
592                                       LWS_WRITE_HTTP_HEADERS | LWS_WRITE_NO_FIN);
593
594                         /*
595                          * so we have a bunch of http/1 style ascii headers
596                          * starting from wsi->http.cgi->headers_buf through
597                          * wsi->http.cgi->headers_pos.  These are OK for http/1
598                          * connections, but they're no good for http/2 conns.
599                          *
600                          * Let's redo them at headers_pos forward using the
601                          * correct coding for http/1 or http/2
602                          */
603                         if (!wsi->http2_substream)
604                                 goto post_hpack_recode;
605
606                         p = wsi->http.cgi->headers_start;
607                         wsi->http.cgi->headers_start =
608                                         wsi->http.cgi->headers_pos;
609                         wsi->http.cgi->headers_dumped =
610                                         wsi->http.cgi->headers_start;
611                         hrs = HR_NAME;
612                         name = buf;
613
614                         while (p < wsi->http.cgi->headers_start) {
615                                 switch (hrs) {
616                                 case HR_NAME:
617                                         /*
618                                          * in http/2 upper-case header names
619                                          * are illegal.  So convert to lower-
620                                          * case.
621                                          */
622                                         if (name - buf > 64)
623                                                 return -1;
624                                         if (*p != ':') {
625                                                 if (*p >= 'A' && *p <= 'Z')
626                                                         *name++ = (*p++) +
627                                                                   ('a' - 'A');
628                                                 else
629                                                         *name++ = *p++;
630                                         } else {
631                                                 p++;
632                                                 *name++ = '\0';
633                                                 value = name;
634                                                 hrs = HR_WHITESPACE;
635                                         }
636                                         break;
637                                 case HR_WHITESPACE:
638                                         if (*p == ' ') {
639                                                 p++;
640                                                 break;
641                                         }
642                                         hrs = HR_ARG;
643                                         /* fallthru */
644                                 case HR_ARG:
645                                         if (name > end - 64)
646                                                 return -1;
647
648                                         if (*p != '\x0a' && *p != '\x0d') {
649                                                 *name++ = *p++;
650                                                 break;
651                                         }
652                                         hrs = HR_CRLF;
653                                         /* fallthru */
654                                 case HR_CRLF:
655                                         if ((*p != '\x0a' && *p != '\x0d') ||
656                                             p + 1 == wsi->http.cgi->headers_start) {
657                                                 *name = '\0';
658                                                 if ((strcmp((const char *)buf,
659                                                             "transfer-encoding")
660                                                 )) {
661                                                         lwsl_debug("+ %s: %s\n",
662                                                                    buf, value);
663                                                         if (
664                                         lws_add_http_header_by_name(wsi, buf,
665                                         (unsigned char *)value, name - value,
666                                         (unsigned char **)&wsi->http.cgi->headers_pos,
667                                         (unsigned char *)wsi->http.cgi->headers_end))
668                                                                 return 1;
669                                                         hrs = HR_NAME;
670                                                         name = buf;
671                                                         break;
672                                                 }
673                                         }
674                                         p++;
675                                         break;
676                                 }
677                         }
678 post_hpack_recode:
679                         /* finalize cached headers before dumping them */
680                         if (lws_finalize_http_header(wsi,
681                               (unsigned char **)&wsi->http.cgi->headers_pos,
682                               (unsigned char *)wsi->http.cgi->headers_end)) {
683
684                                 lwsl_notice("finalize failed\n");
685                                 return -1;
686                         }
687
688                         wsi->hdr_state = LHCS_DUMP_HEADERS;
689                         wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_HEADERS;
690                         lws_callback_on_writable(wsi);
691                         /* back to the loop for writeability again */
692                         return 0;
693
694                 case LHCS_DUMP_HEADERS:
695
696                         n = wsi->http.cgi->headers_pos -
697                             wsi->http.cgi->headers_dumped;
698                         if (n > 512)
699                                 n = 512;
700
701                         lwsl_debug("LHCS_DUMP_HEADERS: %d\n", n);
702
703                         cmd = LWS_WRITE_HTTP_HEADERS_CONTINUATION;
704                         if (wsi->http.cgi->headers_dumped + n !=
705                             wsi->http.cgi->headers_pos) {
706                                 lwsl_notice("adding no fin flag\n");
707                                 cmd |= LWS_WRITE_NO_FIN;
708                         }
709
710                         m = lws_write(wsi,
711                                  (unsigned char *)wsi->http.cgi->headers_dumped,
712                                       n, cmd);
713                         if (m < 0) {
714                                 lwsl_debug("%s: write says %d\n", __func__, m);
715                                 return -1;
716                         }
717                         wsi->http.cgi->headers_dumped += n;
718                         if (wsi->http.cgi->headers_dumped ==
719                             wsi->http.cgi->headers_pos) {
720                                 wsi->hdr_state = LHCS_PAYLOAD;
721                                 lws_free_set_NULL(wsi->http.cgi->headers_buf);
722                                 lwsl_debug("freed cgi headers\n");
723                         } else {
724                                 wsi->reason_bf |=
725                                         LWS_CB_REASON_AUX_BF__CGI_HEADERS;
726                                 lws_callback_on_writable(wsi);
727                         }
728
729                         /* writeability becomes uncertain now we wrote
730                          * something, we must return to the event loop
731                          */
732                         return 0;
733                 }
734
735                 if (!wsi->http.cgi->headers_buf) {
736                         /* if we don't already have a headers buf, cook one */
737                         n = 2048;
738                         if (wsi->http2_substream)
739                                 n = 4096;
740                         wsi->http.cgi->headers_buf = lws_malloc(n + LWS_PRE,
741                                                            "cgi hdr buf");
742                         if (!wsi->http.cgi->headers_buf) {
743                                 lwsl_err("OOM\n");
744                                 return -1;
745                         }
746
747                         lwsl_debug("allocated cgi hdrs\n");
748                         wsi->http.cgi->headers_start =
749                                         wsi->http.cgi->headers_buf + LWS_PRE;
750                         wsi->http.cgi->headers_pos = wsi->http.cgi->headers_start;
751                         wsi->http.cgi->headers_dumped = wsi->http.cgi->headers_pos;
752                         wsi->http.cgi->headers_end =
753                                         wsi->http.cgi->headers_buf + n - 1;
754
755                         for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) {
756                                 wsi->http.cgi->match[n] = 0;
757                                 wsi->http.cgi->lp = 0;
758                         }
759                 }
760
761                 n = lws_get_socket_fd(wsi->http.cgi->stdwsi[LWS_STDOUT]);
762                 if (n < 0)
763                         return -1;
764                 n = read(n, &c, 1);
765                 if (n < 0) {
766                         if (errno != EAGAIN) {
767                                 lwsl_debug("%s: read says %d\n", __func__, n);
768                                 return -1;
769                         }
770                         else
771                                 n = 0;
772
773                         if (wsi->http.cgi->headers_pos >=
774                                         wsi->http.cgi->headers_end - 4) {
775                                 lwsl_notice("CGI hdrs > buf size\n");
776
777                                 return -1;
778                         }
779                 }
780                 if (!n)
781                         goto agin;
782
783                 lwsl_debug("-- 0x%02X %c %d %d\n", (unsigned char)c, c,
784                            wsi->http.cgi->match[1], wsi->hdr_state);
785                 if (!c)
786                         return -1;
787                 switch (wsi->hdr_state) {
788                 case LCHS_HEADER:
789                         hdr:
790                         for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) {
791                                 /*
792                                  * significant headers with
793                                  * numeric decimal payloads
794                                  */
795                                 if (!significant_hdr[n][wsi->http.cgi->match[n]] &&
796                                     (c >= '0' && c <= '9') &&
797                                     wsi->http.cgi->lp < (int)sizeof(wsi->http.cgi->l) - 1) {
798                                         wsi->http.cgi->l[wsi->http.cgi->lp++] = c;
799                                         wsi->http.cgi->l[wsi->http.cgi->lp] = '\0';
800                                         switch (n) {
801                                         case SIGNIFICANT_HDR_CONTENT_LENGTH:
802                                                 wsi->http.cgi->content_length =
803                                                         atoll(wsi->http.cgi->l);
804                                                 break;
805                                         case SIGNIFICANT_HDR_STATUS:
806                                                 wsi->http.cgi->response_code =
807                                                         atol(wsi->http.cgi->l);
808                                                 lwsl_debug("Status set to %d\n",
809                                                    wsi->http.cgi->response_code);
810                                                 break;
811                                         default:
812                                                 break;
813                                         }
814                                 }
815                                 /* hits up to the NUL are sticky until next hdr */
816                                 if (significant_hdr[n][wsi->http.cgi->match[n]]) {
817                                         if (tolower(c) ==
818                                             significant_hdr[n][wsi->http.cgi->match[n]])
819                                                 wsi->http.cgi->match[n]++;
820                                         else
821                                                 wsi->http.cgi->match[n] = 0;
822                                 }
823                         }
824
825                         /* some cgi only send us \x0a for EOL */
826                         if (c == '\x0a') {
827                                 wsi->hdr_state = LCHS_SINGLE_0A;
828                                 *wsi->http.cgi->headers_pos++ = '\x0d';
829                         }
830                         *wsi->http.cgi->headers_pos++ = c;
831                         if (c == '\x0d')
832                                 wsi->hdr_state = LCHS_LF1;
833
834                         if (wsi->hdr_state != LCHS_HEADER &&
835                             !significant_hdr[SIGNIFICANT_HDR_TRANSFER_ENCODING]
836                                     [wsi->http.cgi->match[
837                                          SIGNIFICANT_HDR_TRANSFER_ENCODING]]) {
838                                 lwsl_info("cgi produced chunked\n");
839                                 wsi->http.cgi->explicitly_chunked = 1;
840                         }
841
842                         /* presence of Location: mandates 302 retcode */
843                         if (wsi->hdr_state != LCHS_HEADER &&
844                             !significant_hdr[SIGNIFICANT_HDR_LOCATION][
845                               wsi->http.cgi->match[SIGNIFICANT_HDR_LOCATION]]) {
846                                 lwsl_debug("CGI: Location hdr seen\n");
847                                 wsi->http.cgi->response_code = 302;
848                         }
849                         break;
850                 case LCHS_LF1:
851                         *wsi->http.cgi->headers_pos++ = c;
852                         if (c == '\x0a') {
853                                 wsi->hdr_state = LCHS_CR2;
854                                 break;
855                         }
856                         /* we got \r[^\n]... it's unreasonable */
857                         lwsl_debug("%s: funny CRLF 0x%02X\n", __func__,
858                                    (unsigned char)c);
859                         return -1;
860
861                 case LCHS_CR2:
862                         if (c == '\x0d') {
863                                 /* drop the \x0d */
864                                 wsi->hdr_state = LCHS_LF2;
865                                 break;
866                         }
867                         wsi->hdr_state = LCHS_HEADER;
868                         for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++)
869                                 wsi->http.cgi->match[n] = 0;
870                         wsi->http.cgi->lp = 0;
871                         goto hdr;
872
873                 case LCHS_LF2:
874                 case LCHS_SINGLE_0A:
875                         m = wsi->hdr_state;
876                         if (c == '\x0a') {
877                                 lwsl_debug("Content-Length: %lld\n",
878                                         (unsigned long long)
879                                         wsi->http.cgi->content_length);
880                                 wsi->hdr_state = LHCS_RESPONSE;
881                                 /*
882                                  * drop the \0xa ... finalize
883                                  * will add it if needed (HTTP/1)
884                                  */
885                                 break;
886                         }
887                         if (m == LCHS_LF2)
888                                 /* we got \r\n\r[^\n]... unreasonable */
889                                 return -1;
890                         /* we got \x0anext header, it's reasonable */
891                         *wsi->http.cgi->headers_pos++ = c;
892                         wsi->hdr_state = LCHS_HEADER;
893                         for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++)
894                                 wsi->http.cgi->match[n] = 0;
895                         wsi->http.cgi->lp = 0;
896                         break;
897                 case LHCS_PAYLOAD:
898                         break;
899                 }
900
901 agin:
902                 /* ran out of input, ended the hdrs, or filled up the hdrs buf */
903                 if (!n || wsi->hdr_state == LHCS_PAYLOAD)
904                         return 0;
905         }
906
907         /* payload processing */
908
909         m = !wsi->http.cgi->implied_chunked && !wsi->http2_substream &&
910             !wsi->http.cgi->explicitly_chunked &&
911             !wsi->http.cgi->content_length;
912         n = lws_get_socket_fd(wsi->http.cgi->stdwsi[LWS_STDOUT]);
913         if (n < 0)
914                 return -1;
915         if (m) {
916                 uint8_t term[LWS_PRE + 6];
917
918                 lwsl_info("%s: zero chunk\n", __func__);
919
920                 memcpy(term + LWS_PRE, (uint8_t *)"0\x0d\x0a\x0d\x0a", 5);
921
922                 if (lws_write(wsi, term + LWS_PRE, 5,
923                               LWS_WRITE_HTTP_FINAL) != 5)
924                         return -1;
925
926                 wsi->http.cgi->cgi_transaction_over = 1;
927
928                 return 0;
929         }
930
931         n = read(n, start, sizeof(buf) - LWS_PRE);
932
933         if (n < 0 && errno != EAGAIN) {
934                 lwsl_debug("%s: stdout read says %d\n", __func__, n);
935                 return -1;
936         }
937         if (n > 0) {
938 /*
939                 if (!wsi->http2_substream && m) {
940                         char chdr[LWS_HTTP_CHUNK_HDR_SIZE];
941                         m = lws_snprintf(chdr, LWS_HTTP_CHUNK_HDR_SIZE - 3,
942                                          "%X\x0d\x0a", n);
943                         memmove(start + m, start, n);
944                         memcpy(start, chdr, m);
945                         memcpy(start + m + n, "\x0d\x0a", 2);
946                         n += m + 2;
947                 }
948                 */
949
950 #if defined(LWS_WITH_HTTP2)
951                 if (wsi->http2_substream) {
952                         struct lws *nwsi = lws_get_network_wsi(wsi);
953
954                         __lws_set_timeout(wsi,
955                                 PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
956
957                         if (!nwsi->immortal_substream_count)
958                                 __lws_set_timeout(nwsi,
959                                         PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
960                 }
961 #endif
962
963                 cmd = LWS_WRITE_HTTP;
964                 if (wsi->http.cgi->content_length_seen + n ==
965                                                 wsi->http.cgi->content_length)
966                         cmd = LWS_WRITE_HTTP_FINAL;
967
968                 m = lws_write(wsi, (unsigned char *)start, n, cmd);
969                 //lwsl_notice("write %d\n", m);
970                 if (m < 0) {
971                         lwsl_debug("%s: stdout write says %d\n", __func__, m);
972                         return -1;
973                 }
974                 wsi->http.cgi->content_length_seen += n;
975         } else {
976                 if (wsi->cgi_stdout_zero_length) {
977                         lwsl_debug("%s: stdout is POLLHUP'd\n", __func__);
978                         if (wsi->http2_substream)
979                                 m = lws_write(wsi, (unsigned char *)start, 0,
980                                               LWS_WRITE_HTTP_FINAL);
981                         else
982                                 return -1;
983                         return 1;
984                 }
985                 wsi->cgi_stdout_zero_length = 1;
986         }
987         return 0;
988 }
989
990 LWS_VISIBLE LWS_EXTERN int
991 lws_cgi_kill(struct lws *wsi)
992 {
993         struct lws_cgi_args args;
994         int status, n;
995
996         lwsl_debug("%s: %p\n", __func__, wsi);
997
998         if (!wsi->http.cgi)
999                 return 0;
1000
1001         if (wsi->http.cgi->pid > 0) {
1002                 n = waitpid(wsi->http.cgi->pid, &status, WNOHANG);
1003                 if (n > 0) {
1004                         lwsl_debug("%s: PID %d reaped\n", __func__,
1005                                     wsi->http.cgi->pid);
1006                         goto handled;
1007                 }
1008                 /* kill the process group */
1009                 n = kill(-wsi->http.cgi->pid, SIGTERM);
1010                 lwsl_debug("%s: SIGTERM child PID %d says %d (errno %d)\n",
1011                            __func__, wsi->http.cgi->pid, n, errno);
1012                 if (n < 0) {
1013                         /*
1014                          * hum seen errno=3 when process is listed in ps,
1015                          * it seems we don't always retain process grouping
1016                          *
1017                          * Direct these fallback attempt to the exact child
1018                          */
1019                         n = kill(wsi->http.cgi->pid, SIGTERM);
1020                         if (n < 0) {
1021                                 n = kill(wsi->http.cgi->pid, SIGPIPE);
1022                                 if (n < 0) {
1023                                         n = kill(wsi->http.cgi->pid, SIGKILL);
1024                                         if (n < 0)
1025                                                 lwsl_info("%s: SIGKILL PID %d "
1026                                                          "failed errno %d "
1027                                                          "(maybe zombie)\n",
1028                                                          __func__,
1029                                                  wsi->http.cgi->pid, errno);
1030                                 }
1031                         }
1032                 }
1033                 /* He could be unkillable because he's a zombie */
1034                 n = 1;
1035                 while (n > 0) {
1036                         n = waitpid(-wsi->http.cgi->pid, &status, WNOHANG);
1037                         if (n > 0)
1038                                 lwsl_debug("%s: reaped PID %d\n", __func__, n);
1039                         if (n <= 0) {
1040                                 n = waitpid(wsi->http.cgi->pid, &status, WNOHANG);
1041                                 if (n > 0)
1042                                         lwsl_debug("%s: reaped PID %d\n",
1043                                                    __func__, n);
1044                         }
1045                 }
1046         }
1047
1048 handled:
1049         args.stdwsi = &wsi->http.cgi->stdwsi[0];
1050
1051         if (wsi->http.cgi->pid != -1) {
1052                 n = user_callback_handle_rxflow(wsi->protocol->callback, wsi,
1053                                                 LWS_CALLBACK_CGI_TERMINATED,
1054                                                 wsi->user_space, (void *)&args,
1055                                                 wsi->http.cgi->pid);
1056                 wsi->http.cgi->pid = -1;
1057                 if (n && !wsi->http.cgi->being_closed)
1058                         lws_close_free_wsi(wsi, 0, "lws_cgi_kill");
1059         }
1060
1061         return 0;
1062 }
1063
1064 LWS_EXTERN int
1065 lws_cgi_kill_terminated(struct lws_context_per_thread *pt)
1066 {
1067         struct lws_cgi **pcgi, *cgi = NULL;
1068         int status, n = 1;
1069
1070         while (n > 0) {
1071                 /* find finished guys but don't reap yet */
1072                 n = waitpid(-1, &status, WNOHANG);
1073                 if (n <= 0)
1074                         continue;
1075                 lwsl_debug("%s: observed PID %d terminated\n", __func__, n);
1076
1077                 pcgi = &pt->http.cgi_list;
1078
1079                 /* check all the subprocesses on the cgi list */
1080                 while (*pcgi) {
1081                         /* get the next one first as list may change */
1082                         cgi = *pcgi;
1083                         pcgi = &(*pcgi)->cgi_list;
1084
1085                         if (cgi->pid <= 0)
1086                                 continue;
1087
1088                         /* finish sending cached headers */
1089                         if (cgi->headers_buf)
1090                                 continue;
1091
1092                         /* wait for stdout to be drained */
1093                         if (cgi->content_length > cgi->content_length_seen)
1094                                 continue;
1095
1096                         if (cgi->content_length) {
1097                                 lwsl_debug("%s: wsi %p: expected content "
1098                                            "length seen: %lld\n", __func__,
1099                                            cgi->wsi,
1100                                 (unsigned long long)cgi->content_length_seen);
1101                         }
1102
1103                         /* reap it */
1104                         waitpid(n, &status, WNOHANG);
1105                         /*
1106                          * he's already terminated so no need for kill()
1107                          * but we should do the terminated cgi callback
1108                          * and close him if he's not already closing
1109                          */
1110                         if (n == cgi->pid) {
1111                                 lwsl_debug("%s: found PID %d on cgi list\n",
1112                                             __func__, n);
1113
1114                                 if (!cgi->content_length) {
1115                                         /*
1116                                          * well, if he sends chunked...
1117                                          * give him 2s after the
1118                                          * cgi terminated to send buffered
1119                                          */
1120                                         cgi->chunked_grace++;
1121                                         continue;
1122                                 }
1123
1124                                 /* defeat kill() */
1125                                 cgi->pid = 0;
1126                                 lws_cgi_kill(cgi->wsi);
1127
1128                                 break;
1129                         }
1130                         cgi = NULL;
1131                 }
1132                 /* if not found on the cgi list, as he's one of ours, reap */
1133                 if (!cgi) {
1134                         lwsl_debug("%s: reading PID %d although no cgi match\n",
1135                                         __func__, n);
1136                         waitpid(n, &status, WNOHANG);
1137                 }
1138         }
1139
1140         pcgi = &pt->http.cgi_list;
1141
1142         /* check all the subprocesses on the cgi list */
1143         while (*pcgi) {
1144                 /* get the next one first as list may change */
1145                 cgi = *pcgi;
1146                 pcgi = &(*pcgi)->cgi_list;
1147
1148                 if (cgi->pid <= 0)
1149                         continue;
1150
1151                 /* we deferred killing him after reaping his PID */
1152                 if (cgi->chunked_grace) {
1153                         cgi->chunked_grace++;
1154                         if (cgi->chunked_grace < 2)
1155                                 continue;
1156                         goto finish_him;
1157                 }
1158
1159                 /* finish sending cached headers */
1160                 if (cgi->headers_buf)
1161                         continue;
1162
1163                 /* wait for stdout to be drained */
1164                 if (cgi->content_length > cgi->content_length_seen)
1165                         continue;
1166
1167                 if (cgi->content_length)
1168                         lwsl_debug("%s: wsi %p: expected "
1169                                    "content len seen: %lld\n", __func__,
1170                                    cgi->wsi,
1171                                 (unsigned long long)cgi->content_length_seen);
1172
1173                 /* reap it */
1174                 if (waitpid(cgi->pid, &status, WNOHANG) > 0) {
1175
1176                         if (!cgi->content_length) {
1177                                 /*
1178                                  * well, if he sends chunked...
1179                                  * give him 2s after the
1180                                  * cgi terminated to send buffered
1181                                  */
1182                                 cgi->chunked_grace++;
1183                                 continue;
1184                         }
1185 finish_him:
1186                         lwsl_debug("%s: found PID %d on cgi list\n",
1187                                     __func__, cgi->pid);
1188
1189                         /* defeat kill() */
1190                         cgi->pid = 0;
1191                         lws_cgi_kill(cgi->wsi);
1192
1193                         break;
1194                 }
1195         }
1196
1197         return 0;
1198 }
1199
1200 LWS_VISIBLE LWS_EXTERN struct lws *
1201 lws_cgi_get_stdwsi(struct lws *wsi, enum lws_enum_stdinouterr ch)
1202 {
1203         if (!wsi->http.cgi)
1204                 return NULL;
1205
1206         return wsi->http.cgi->stdwsi[ch];
1207 }
1208
1209 void
1210 lws_cgi_remove_and_kill(struct lws *wsi)
1211 {
1212         struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
1213         struct lws_cgi **pcgi = &pt->http.cgi_list;
1214
1215         /* remove us from the cgi list */
1216         lwsl_debug("%s: remove cgi %p from list\n", __func__, wsi->http.cgi);
1217         while (*pcgi) {
1218                 if (*pcgi == wsi->http.cgi) {
1219                         /* drop us from the pt cgi list */
1220                         *pcgi = (*pcgi)->cgi_list;
1221                         break;
1222                 }
1223                 pcgi = &(*pcgi)->cgi_list;
1224         }
1225         if (wsi->http.cgi->headers_buf) {
1226                 lwsl_debug("close: freed cgi headers\n");
1227                 lws_free_set_NULL(wsi->http.cgi->headers_buf);
1228         }
1229         /* we have a cgi going, we must kill it */
1230         wsi->http.cgi->being_closed = 1;
1231         lws_cgi_kill(wsi);
1232 }