cgi-server.c 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096
  1. /*
  2. * libwebsockets - small server side websockets and web server implementation
  3. *
  4. * Copyright (C) 2010 - 2019 Andy Green <[email protected]>
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to
  8. * deal in the Software without restriction, including without limitation the
  9. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  10. * sell copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  21. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  22. * IN THE SOFTWARE.
  23. */
  24. #if !defined(_GNU_SOURCE)
  25. #define _GNU_SOURCE
  26. #endif
  27. #include "private-lib-core.h"
  28. #if defined(WIN32) || defined(_WIN32)
  29. #else
  30. #include <sys/wait.h>
  31. #endif
  32. static const char *hex = "0123456789ABCDEF";
  33. static int
  34. urlencode(const char *in, int inlen, char *out, int outlen)
  35. {
  36. char *start = out, *end = out + outlen;
  37. while (inlen-- && out < end - 4) {
  38. if ((*in >= 'A' && *in <= 'Z') ||
  39. (*in >= 'a' && *in <= 'z') ||
  40. (*in >= '0' && *in <= '9') ||
  41. *in == '-' ||
  42. *in == '_' ||
  43. *in == '.' ||
  44. *in == '~') {
  45. *out++ = *in++;
  46. continue;
  47. }
  48. if (*in == ' ') {
  49. *out++ = '+';
  50. in++;
  51. continue;
  52. }
  53. *out++ = '%';
  54. *out++ = hex[(*in) >> 4];
  55. *out++ = hex[(*in++) & 15];
  56. }
  57. *out = '\0';
  58. if (out >= end - 4)
  59. return -1;
  60. return out - start;
  61. }
  62. static void
  63. lws_cgi_grace(lws_sorted_usec_list_t *sul)
  64. {
  65. struct lws_cgi *cgi = lws_container_of(sul, struct lws_cgi, sul_grace);
  66. /* act on the reap cb from earlier */
  67. lwsl_info("%s: wsi %p\n", __func__, cgi->wsi);
  68. if (!cgi->wsi->http.cgi->post_in_expected)
  69. cgi->wsi->http.cgi->cgi_transaction_over = 1;
  70. lws_callback_on_writable(cgi->wsi);
  71. }
  72. static void
  73. lws_cgi_reap_cb(void *opaque, lws_usec_t *accounting, siginfo_t *si,
  74. int we_killed_him)
  75. {
  76. struct lws *wsi = (struct lws *)opaque;
  77. /*
  78. * The cgi has come to an end, by itself or with a signal...
  79. */
  80. lwsl_info("%s: wsi %p post_in_expected %d\n", __func__, wsi,
  81. (int)wsi->http.cgi->post_in_expected);
  82. /*
  83. * Grace period to handle the incoming stdout
  84. */
  85. lws_sul_schedule(wsi->a.context, wsi->tsi, &wsi->http.cgi->sul_grace,
  86. lws_cgi_grace, 1 * LWS_US_PER_SEC);
  87. }
  88. int
  89. lws_cgi(struct lws *wsi, const char * const *exec_array,
  90. int script_uri_path_len, int timeout_secs,
  91. const struct lws_protocol_vhost_options *mp_cgienv)
  92. {
  93. struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
  94. struct lws_spawn_piped_info info;
  95. char *env_array[30], cgi_path[500], e[1024], *p = e,
  96. *end = p + sizeof(e) - 1, tok[256], *t, *sum, *sumend;
  97. struct lws_cgi *cgi;
  98. int n, m = 0, i, uritok = -1, c;
  99. /*
  100. * give the master wsi a cgi struct
  101. */
  102. wsi->http.cgi = lws_zalloc(sizeof(*wsi->http.cgi), "new cgi");
  103. if (!wsi->http.cgi) {
  104. lwsl_err("%s: OOM\n", __func__);
  105. return -1;
  106. }
  107. wsi->http.cgi->response_code = HTTP_STATUS_OK;
  108. cgi = wsi->http.cgi;
  109. cgi->wsi = wsi; /* set cgi's owning wsi */
  110. sum = cgi->summary;
  111. sumend = sum + strlen(cgi->summary) - 1;
  112. if (timeout_secs)
  113. lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs);
  114. /* the cgi stdout is always sending us http1.x header data first */
  115. wsi->hdr_state = LCHS_HEADER;
  116. /* add us to the pt list of active cgis */
  117. lwsl_debug("%s: adding cgi %p to list\n", __func__, wsi->http.cgi);
  118. cgi->cgi_list = pt->http.cgi_list;
  119. pt->http.cgi_list = cgi;
  120. sum += lws_snprintf(sum, sumend - sum, "%s ", exec_array[0]);
  121. if (0) {
  122. char *pct = lws_hdr_simple_ptr(wsi,
  123. WSI_TOKEN_HTTP_CONTENT_ENCODING);
  124. if (pct && !strcmp(pct, "gzip"))
  125. wsi->http.cgi->gzip_inflate = 1;
  126. }
  127. /* prepare his CGI env */
  128. n = 0;
  129. if (lws_is_ssl(wsi)) {
  130. env_array[n++] = p;
  131. p += lws_snprintf(p, end - p, "HTTPS=ON");
  132. p++;
  133. }
  134. if (wsi->http.ah) {
  135. static const unsigned char meths[] = {
  136. WSI_TOKEN_GET_URI,
  137. WSI_TOKEN_POST_URI,
  138. #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
  139. WSI_TOKEN_OPTIONS_URI,
  140. WSI_TOKEN_PUT_URI,
  141. WSI_TOKEN_PATCH_URI,
  142. WSI_TOKEN_DELETE_URI,
  143. #endif
  144. WSI_TOKEN_CONNECT,
  145. WSI_TOKEN_HEAD_URI,
  146. #ifdef LWS_WITH_HTTP2
  147. WSI_TOKEN_HTTP_COLON_PATH,
  148. #endif
  149. };
  150. static const char * const meth_names[] = {
  151. "GET", "POST",
  152. #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
  153. "OPTIONS", "PUT", "PATCH", "DELETE",
  154. #endif
  155. "CONNECT", "HEAD", ":path"
  156. };
  157. if (script_uri_path_len >= 0)
  158. for (m = 0; m < (int)LWS_ARRAY_SIZE(meths); m++)
  159. if (lws_hdr_total_length(wsi, meths[m]) >=
  160. script_uri_path_len) {
  161. uritok = meths[m];
  162. break;
  163. }
  164. if (script_uri_path_len < 0 && uritok < 0)
  165. goto bail;
  166. // if (script_uri_path_len < 0)
  167. // uritok = 0;
  168. if (m >= 0) {
  169. env_array[n++] = p;
  170. if (m < (int)LWS_ARRAY_SIZE(meths) - 1) {
  171. p += lws_snprintf(p, end - p,
  172. "REQUEST_METHOD=%s",
  173. meth_names[m]);
  174. sum += lws_snprintf(sum, sumend - sum, "%s ",
  175. meth_names[m]);
  176. #if defined(LWS_ROLE_H2)
  177. } else {
  178. p += lws_snprintf(p, end - p,
  179. "REQUEST_METHOD=%s",
  180. lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD));
  181. sum += lws_snprintf(sum, sumend - sum, "%s ",
  182. lws_hdr_simple_ptr(wsi,
  183. WSI_TOKEN_HTTP_COLON_METHOD));
  184. #endif
  185. }
  186. p++;
  187. }
  188. if (uritok >= 0)
  189. sum += lws_snprintf(sum, sumend - sum, "%s ",
  190. lws_hdr_simple_ptr(wsi, uritok));
  191. env_array[n++] = p;
  192. p += lws_snprintf(p, end - p, "QUERY_STRING=");
  193. /* dump the individual URI Arg parameters */
  194. m = 0;
  195. while (script_uri_path_len >= 0) {
  196. i = lws_hdr_copy_fragment(wsi, tok, sizeof(tok),
  197. WSI_TOKEN_HTTP_URI_ARGS, m);
  198. if (i < 0)
  199. break;
  200. t = tok;
  201. while (*t && *t != '=' && p < end - 4)
  202. *p++ = *t++;
  203. if (*t == '=')
  204. *p++ = *t++;
  205. i = urlencode(t, i- (t - tok), p, end - p);
  206. if (i > 0) {
  207. p += i;
  208. *p++ = '&';
  209. }
  210. m++;
  211. }
  212. if (m)
  213. p--;
  214. *p++ = '\0';
  215. if (uritok >= 0) {
  216. strcpy(cgi_path, "REQUEST_URI=");
  217. c = lws_hdr_copy(wsi, cgi_path + 12,
  218. sizeof(cgi_path) - 12, uritok);
  219. if (c < 0)
  220. goto bail;
  221. cgi_path[sizeof(cgi_path) - 1] = '\0';
  222. env_array[n++] = cgi_path;
  223. }
  224. sum += lws_snprintf(sum, sumend - sum, "%s", env_array[n - 1]);
  225. if (script_uri_path_len >= 0) {
  226. env_array[n++] = p;
  227. p += lws_snprintf(p, end - p, "PATH_INFO=%s",
  228. cgi_path + 12 + script_uri_path_len);
  229. p++;
  230. }
  231. }
  232. #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
  233. if (script_uri_path_len >= 0 &&
  234. lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) {
  235. env_array[n++] = p;
  236. p += lws_snprintf(p, end - p, "HTTP_REFERER=%s",
  237. lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER));
  238. p++;
  239. }
  240. #endif
  241. if (script_uri_path_len >= 0 &&
  242. lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
  243. env_array[n++] = p;
  244. p += lws_snprintf(p, end - p, "HTTP_HOST=%s",
  245. lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
  246. p++;
  247. }
  248. if (script_uri_path_len >= 0 &&
  249. lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
  250. env_array[n++] = p;
  251. p += lws_snprintf(p, end - p, "HTTP_COOKIE=");
  252. m = lws_hdr_copy(wsi, p, end - p, WSI_TOKEN_HTTP_COOKIE);
  253. if (m > 0)
  254. p += lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE);
  255. *p++ = '\0';
  256. }
  257. #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
  258. if (script_uri_path_len >= 0 &&
  259. lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) {
  260. env_array[n++] = p;
  261. p += lws_snprintf(p, end - p, "HTTP_USER_AGENT=%s",
  262. lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT));
  263. p++;
  264. }
  265. #endif
  266. if (script_uri_path_len >= 0 &&
  267. lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING)) {
  268. env_array[n++] = p;
  269. p += lws_snprintf(p, end - p, "HTTP_CONTENT_ENCODING=%s",
  270. lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING));
  271. p++;
  272. }
  273. if (script_uri_path_len >= 0 &&
  274. lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT)) {
  275. env_array[n++] = p;
  276. p += lws_snprintf(p, end - p, "HTTP_ACCEPT=%s",
  277. lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT));
  278. p++;
  279. }
  280. if (script_uri_path_len >= 0 &&
  281. lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING)) {
  282. env_array[n++] = p;
  283. p += lws_snprintf(p, end - p, "HTTP_ACCEPT_ENCODING=%s",
  284. lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING));
  285. p++;
  286. }
  287. if (script_uri_path_len >= 0 &&
  288. uritok == WSI_TOKEN_POST_URI) {
  289. if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
  290. env_array[n++] = p;
  291. p += lws_snprintf(p, end - p, "CONTENT_TYPE=%s",
  292. lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE));
  293. p++;
  294. }
  295. if (!wsi->http.cgi->gzip_inflate &&
  296. lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
  297. env_array[n++] = p;
  298. p += lws_snprintf(p, end - p, "CONTENT_LENGTH=%s",
  299. lws_hdr_simple_ptr(wsi,
  300. WSI_TOKEN_HTTP_CONTENT_LENGTH));
  301. p++;
  302. }
  303. if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH))
  304. wsi->http.cgi->post_in_expected =
  305. atoll(lws_hdr_simple_ptr(wsi,
  306. WSI_TOKEN_HTTP_CONTENT_LENGTH));
  307. }
  308. env_array[n++] = p;
  309. p += lws_snprintf(p, end - p, "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin");
  310. p++;
  311. env_array[n++] = p;
  312. p += lws_snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]);
  313. p++;
  314. while (mp_cgienv) {
  315. env_array[n++] = p;
  316. p += lws_snprintf(p, end - p, "%s=%s", mp_cgienv->name,
  317. mp_cgienv->value);
  318. if (!strcmp(mp_cgienv->name, "GIT_PROJECT_ROOT")) {
  319. wsi->http.cgi->implied_chunked = 1;
  320. wsi->http.cgi->explicitly_chunked = 1;
  321. }
  322. lwsl_info(" Applying mount-specific cgi env '%s'\n",
  323. env_array[n - 1]);
  324. p++;
  325. mp_cgienv = mp_cgienv->next;
  326. }
  327. env_array[n++] = p;
  328. p += lws_snprintf(p, end - p, "SERVER_SOFTWARE=lws");
  329. p++;
  330. env_array[n] = NULL;
  331. #if 0
  332. for (m = 0; m < n; m++)
  333. lwsl_notice(" %s\n", env_array[m]);
  334. #endif
  335. memset(&info, 0, sizeof(info));
  336. info.env_array = env_array;
  337. info.exec_array = exec_array;
  338. info.max_log_lines = 20000;
  339. info.opt_parent = wsi;
  340. info.timeout_us = 5 * 60 * LWS_US_PER_SEC;
  341. info.tsi = wsi->tsi;
  342. info.vh = wsi->a.vhost;
  343. info.ops = &role_ops_cgi;
  344. info.plsp = &wsi->http.cgi->lsp;
  345. info.opaque = wsi;
  346. info.reap_cb = lws_cgi_reap_cb;
  347. /*
  348. * Actually having made the env, as a cgi we don't need the ah
  349. * any more
  350. */
  351. if (script_uri_path_len >= 0) {
  352. lws_header_table_detach(wsi, 0);
  353. info.disable_ctrlc = 1;
  354. }
  355. wsi->http.cgi->lsp = lws_spawn_piped(&info);
  356. if (!wsi->http.cgi->lsp) {
  357. lwsl_err("%s: spawn failed\n", __func__);
  358. goto bail;
  359. }
  360. /* we are the parent process */
  361. wsi->a.context->count_cgi_spawned++;
  362. /* inform cgi owner of the child PID */
  363. n = user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
  364. LWS_CALLBACK_CGI_PROCESS_ATTACH,
  365. wsi->user_space, NULL, cgi->lsp->child_pid);
  366. (void)n;
  367. return 0;
  368. bail:
  369. lws_sul_cancel(&wsi->http.cgi->sul_grace);
  370. lws_free_set_NULL(wsi->http.cgi);
  371. lwsl_err("%s: failed\n", __func__);
  372. return -1;
  373. }
  374. /* we have to parse out these headers in the CGI output */
  375. static const char * const significant_hdr[SIGNIFICANT_HDR_COUNT] = {
  376. "content-length: ",
  377. "location: ",
  378. "status: ",
  379. "transfer-encoding: chunked",
  380. "content-encoding: gzip",
  381. };
  382. enum header_recode {
  383. HR_NAME,
  384. HR_WHITESPACE,
  385. HR_ARG,
  386. HR_CRLF,
  387. };
  388. int
  389. lws_cgi_write_split_stdout_headers(struct lws *wsi)
  390. {
  391. int n, m, cmd;
  392. unsigned char buf[LWS_PRE + 4096], *start = &buf[LWS_PRE], *p = start,
  393. *end = &buf[sizeof(buf) - 1 - LWS_PRE], *name,
  394. *value = NULL;
  395. char c, hrs;
  396. if (!wsi->http.cgi)
  397. return -1;
  398. while (wsi->hdr_state != LHCS_PAYLOAD) {
  399. /*
  400. * We have to separate header / finalize and payload chunks,
  401. * since they need to be handled separately
  402. */
  403. switch (wsi->hdr_state) {
  404. case LHCS_RESPONSE:
  405. lwsl_debug("LHCS_RESPONSE: issuing response %d\n",
  406. wsi->http.cgi->response_code);
  407. if (lws_add_http_header_status(wsi,
  408. wsi->http.cgi->response_code,
  409. &p, end))
  410. return 1;
  411. if (!wsi->http.cgi->explicitly_chunked &&
  412. !wsi->http.cgi->content_length &&
  413. lws_add_http_header_by_token(wsi,
  414. WSI_TOKEN_HTTP_TRANSFER_ENCODING,
  415. (unsigned char *)"chunked", 7, &p, end))
  416. return 1;
  417. if (!(wsi->mux_substream))
  418. if (lws_add_http_header_by_token(wsi,
  419. WSI_TOKEN_CONNECTION,
  420. (unsigned char *)"close", 5,
  421. &p, end))
  422. return 1;
  423. n = lws_write(wsi, start, p - start,
  424. LWS_WRITE_HTTP_HEADERS | LWS_WRITE_NO_FIN);
  425. /*
  426. * so we have a bunch of http/1 style ascii headers
  427. * starting from wsi->http.cgi->headers_buf through
  428. * wsi->http.cgi->headers_pos. These are OK for http/1
  429. * connections, but they're no good for http/2 conns.
  430. *
  431. * Let's redo them at headers_pos forward using the
  432. * correct coding for http/1 or http/2
  433. */
  434. if (!wsi->mux_substream)
  435. goto post_hpack_recode;
  436. p = wsi->http.cgi->headers_start;
  437. wsi->http.cgi->headers_start =
  438. wsi->http.cgi->headers_pos;
  439. wsi->http.cgi->headers_dumped =
  440. wsi->http.cgi->headers_start;
  441. hrs = HR_NAME;
  442. name = buf;
  443. while (p < wsi->http.cgi->headers_start) {
  444. switch (hrs) {
  445. case HR_NAME:
  446. /*
  447. * in http/2 upper-case header names
  448. * are illegal. So convert to lower-
  449. * case.
  450. */
  451. if (name - buf > 64)
  452. return -1;
  453. if (*p != ':') {
  454. if (*p >= 'A' && *p <= 'Z')
  455. *name++ = (*p++) +
  456. ('a' - 'A');
  457. else
  458. *name++ = *p++;
  459. } else {
  460. p++;
  461. *name++ = '\0';
  462. value = name;
  463. hrs = HR_WHITESPACE;
  464. }
  465. break;
  466. case HR_WHITESPACE:
  467. if (*p == ' ') {
  468. p++;
  469. break;
  470. }
  471. hrs = HR_ARG;
  472. /* fallthru */
  473. case HR_ARG:
  474. if (name > end - 64)
  475. return -1;
  476. if (*p != '\x0a' && *p != '\x0d') {
  477. *name++ = *p++;
  478. break;
  479. }
  480. hrs = HR_CRLF;
  481. /* fallthru */
  482. case HR_CRLF:
  483. if ((*p != '\x0a' && *p != '\x0d') ||
  484. p + 1 == wsi->http.cgi->headers_start) {
  485. *name = '\0';
  486. if ((strcmp((const char *)buf,
  487. "transfer-encoding")
  488. )) {
  489. lwsl_debug("+ %s: %s\n",
  490. buf, value);
  491. if (
  492. lws_add_http_header_by_name(wsi, buf,
  493. (unsigned char *)value, name - value,
  494. (unsigned char **)&wsi->http.cgi->headers_pos,
  495. (unsigned char *)wsi->http.cgi->headers_end))
  496. return 1;
  497. hrs = HR_NAME;
  498. name = buf;
  499. break;
  500. }
  501. }
  502. p++;
  503. break;
  504. }
  505. }
  506. post_hpack_recode:
  507. /* finalize cached headers before dumping them */
  508. if (lws_finalize_http_header(wsi,
  509. (unsigned char **)&wsi->http.cgi->headers_pos,
  510. (unsigned char *)wsi->http.cgi->headers_end)) {
  511. lwsl_notice("finalize failed\n");
  512. return -1;
  513. }
  514. wsi->hdr_state = LHCS_DUMP_HEADERS;
  515. wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_HEADERS;
  516. lws_callback_on_writable(wsi);
  517. /* back to the loop for writeability again */
  518. return 0;
  519. case LHCS_DUMP_HEADERS:
  520. n = wsi->http.cgi->headers_pos -
  521. wsi->http.cgi->headers_dumped;
  522. if (n > 512)
  523. n = 512;
  524. lwsl_debug("LHCS_DUMP_HEADERS: %d\n", n);
  525. cmd = LWS_WRITE_HTTP_HEADERS_CONTINUATION;
  526. if (wsi->http.cgi->headers_dumped + n !=
  527. wsi->http.cgi->headers_pos) {
  528. lwsl_notice("adding no fin flag\n");
  529. cmd |= LWS_WRITE_NO_FIN;
  530. }
  531. m = lws_write(wsi,
  532. (unsigned char *)wsi->http.cgi->headers_dumped,
  533. n, cmd);
  534. if (m < 0) {
  535. lwsl_debug("%s: write says %d\n", __func__, m);
  536. return -1;
  537. }
  538. wsi->http.cgi->headers_dumped += n;
  539. if (wsi->http.cgi->headers_dumped ==
  540. wsi->http.cgi->headers_pos) {
  541. wsi->hdr_state = LHCS_PAYLOAD;
  542. lws_free_set_NULL(wsi->http.cgi->headers_buf);
  543. lwsl_debug("%s: freed cgi headers\n", __func__);
  544. if (wsi->http.cgi->post_in_expected) {
  545. lwsl_notice("%s: post data still expected, asking for writeable\n", __func__);
  546. lws_callback_on_writable(wsi);
  547. }
  548. } else {
  549. wsi->reason_bf |=
  550. LWS_CB_REASON_AUX_BF__CGI_HEADERS;
  551. lws_callback_on_writable(wsi);
  552. }
  553. /*
  554. * writeability becomes uncertain now we wrote
  555. * something, we must return to the event loop
  556. */
  557. return 0;
  558. }
  559. if (!wsi->http.cgi->headers_buf) {
  560. /* if we don't already have a headers buf, cook one */
  561. n = 2048;
  562. if (wsi->mux_substream)
  563. n = 4096;
  564. wsi->http.cgi->headers_buf = lws_malloc(n + LWS_PRE,
  565. "cgi hdr buf");
  566. if (!wsi->http.cgi->headers_buf) {
  567. lwsl_err("OOM\n");
  568. return -1;
  569. }
  570. lwsl_debug("allocated cgi hdrs\n");
  571. wsi->http.cgi->headers_start =
  572. wsi->http.cgi->headers_buf + LWS_PRE;
  573. wsi->http.cgi->headers_pos = wsi->http.cgi->headers_start;
  574. wsi->http.cgi->headers_dumped = wsi->http.cgi->headers_pos;
  575. wsi->http.cgi->headers_end =
  576. wsi->http.cgi->headers_buf + n - 1;
  577. for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) {
  578. wsi->http.cgi->match[n] = 0;
  579. wsi->http.cgi->lp = 0;
  580. }
  581. }
  582. n = lws_get_socket_fd(wsi->http.cgi->lsp->stdwsi[LWS_STDOUT]);
  583. if (n < 0)
  584. return -1;
  585. n = read(n, &c, 1);
  586. if (n < 0) {
  587. if (errno != EAGAIN) {
  588. lwsl_debug("%s: read says %d\n", __func__, n);
  589. return -1;
  590. }
  591. else
  592. n = 0;
  593. if (wsi->http.cgi->headers_pos >=
  594. wsi->http.cgi->headers_end - 4) {
  595. lwsl_notice("CGI hdrs > buf size\n");
  596. return -1;
  597. }
  598. }
  599. if (!n)
  600. goto agin;
  601. lwsl_debug("-- 0x%02X %c %d %d\n", (unsigned char)c, c,
  602. wsi->http.cgi->match[1], wsi->hdr_state);
  603. if (!c)
  604. return -1;
  605. switch (wsi->hdr_state) {
  606. case LCHS_HEADER:
  607. hdr:
  608. for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) {
  609. /*
  610. * significant headers with
  611. * numeric decimal payloads
  612. */
  613. if (!significant_hdr[n][wsi->http.cgi->match[n]] &&
  614. (c >= '0' && c <= '9') &&
  615. wsi->http.cgi->lp < (int)sizeof(wsi->http.cgi->l) - 1) {
  616. wsi->http.cgi->l[wsi->http.cgi->lp++] = c;
  617. wsi->http.cgi->l[wsi->http.cgi->lp] = '\0';
  618. switch (n) {
  619. case SIGNIFICANT_HDR_CONTENT_LENGTH:
  620. wsi->http.cgi->content_length =
  621. atoll(wsi->http.cgi->l);
  622. break;
  623. case SIGNIFICANT_HDR_STATUS:
  624. wsi->http.cgi->response_code =
  625. atol(wsi->http.cgi->l);
  626. lwsl_debug("Status set to %d\n",
  627. wsi->http.cgi->response_code);
  628. break;
  629. default:
  630. break;
  631. }
  632. }
  633. /* hits up to the NUL are sticky until next hdr */
  634. if (significant_hdr[n][wsi->http.cgi->match[n]]) {
  635. if (tolower(c) ==
  636. significant_hdr[n][wsi->http.cgi->match[n]])
  637. wsi->http.cgi->match[n]++;
  638. else
  639. wsi->http.cgi->match[n] = 0;
  640. }
  641. }
  642. /* some cgi only send us \x0a for EOL */
  643. if (c == '\x0a') {
  644. wsi->hdr_state = LCHS_SINGLE_0A;
  645. *wsi->http.cgi->headers_pos++ = '\x0d';
  646. }
  647. *wsi->http.cgi->headers_pos++ = c;
  648. if (c == '\x0d')
  649. wsi->hdr_state = LCHS_LF1;
  650. if (wsi->hdr_state != LCHS_HEADER &&
  651. !significant_hdr[SIGNIFICANT_HDR_TRANSFER_ENCODING]
  652. [wsi->http.cgi->match[
  653. SIGNIFICANT_HDR_TRANSFER_ENCODING]]) {
  654. lwsl_info("cgi produced chunked\n");
  655. wsi->http.cgi->explicitly_chunked = 1;
  656. }
  657. /* presence of Location: mandates 302 retcode */
  658. if (wsi->hdr_state != LCHS_HEADER &&
  659. !significant_hdr[SIGNIFICANT_HDR_LOCATION][
  660. wsi->http.cgi->match[SIGNIFICANT_HDR_LOCATION]]) {
  661. lwsl_debug("CGI: Location hdr seen\n");
  662. wsi->http.cgi->response_code = 302;
  663. }
  664. break;
  665. case LCHS_LF1:
  666. *wsi->http.cgi->headers_pos++ = c;
  667. if (c == '\x0a') {
  668. wsi->hdr_state = LCHS_CR2;
  669. break;
  670. }
  671. /* we got \r[^\n]... it's unreasonable */
  672. lwsl_debug("%s: funny CRLF 0x%02X\n", __func__,
  673. (unsigned char)c);
  674. return -1;
  675. case LCHS_CR2:
  676. if (c == '\x0d') {
  677. /* drop the \x0d */
  678. wsi->hdr_state = LCHS_LF2;
  679. break;
  680. }
  681. wsi->hdr_state = LCHS_HEADER;
  682. for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++)
  683. wsi->http.cgi->match[n] = 0;
  684. wsi->http.cgi->lp = 0;
  685. goto hdr;
  686. case LCHS_LF2:
  687. case LCHS_SINGLE_0A:
  688. m = wsi->hdr_state;
  689. if (c == '\x0a') {
  690. lwsl_debug("Content-Length: %lld\n",
  691. (unsigned long long)
  692. wsi->http.cgi->content_length);
  693. wsi->hdr_state = LHCS_RESPONSE;
  694. /*
  695. * drop the \0xa ... finalize
  696. * will add it if needed (HTTP/1)
  697. */
  698. break;
  699. }
  700. if (m == LCHS_LF2)
  701. /* we got \r\n\r[^\n]... unreasonable */
  702. return -1;
  703. /* we got \x0anext header, it's reasonable */
  704. *wsi->http.cgi->headers_pos++ = c;
  705. wsi->hdr_state = LCHS_HEADER;
  706. for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++)
  707. wsi->http.cgi->match[n] = 0;
  708. wsi->http.cgi->lp = 0;
  709. break;
  710. case LHCS_PAYLOAD:
  711. break;
  712. }
  713. agin:
  714. /* ran out of input, ended the hdrs, or filled up the hdrs buf */
  715. if (!n || wsi->hdr_state == LHCS_PAYLOAD)
  716. return 0;
  717. }
  718. /* payload processing */
  719. m = !wsi->http.cgi->implied_chunked && !wsi->mux_substream &&
  720. // !wsi->http.cgi->explicitly_chunked &&
  721. !wsi->http.cgi->content_length;
  722. n = lws_get_socket_fd(wsi->http.cgi->lsp->stdwsi[LWS_STDOUT]);
  723. if (n < 0)
  724. return -1;
  725. n = read(n, start, sizeof(buf) - LWS_PRE);
  726. if (n < 0 && errno != EAGAIN) {
  727. lwsl_debug("%s: stdout read says %d\n", __func__, n);
  728. return -1;
  729. }
  730. if (n > 0) {
  731. // lwsl_hexdump_notice(buf, n);
  732. if (!wsi->mux_substream && m) {
  733. char chdr[LWS_HTTP_CHUNK_HDR_SIZE];
  734. m = lws_snprintf(chdr, LWS_HTTP_CHUNK_HDR_SIZE - 3,
  735. "%X\x0d\x0a", n);
  736. memmove(start + m, start, n);
  737. memcpy(start, chdr, m);
  738. memcpy(start + m + n, "\x0d\x0a", 2);
  739. n += m + 2;
  740. }
  741. #if defined(LWS_WITH_HTTP2)
  742. if (wsi->mux_substream) {
  743. struct lws *nwsi = lws_get_network_wsi(wsi);
  744. __lws_set_timeout(wsi,
  745. PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
  746. if (!nwsi->immortal_substream_count)
  747. __lws_set_timeout(nwsi,
  748. PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
  749. }
  750. #endif
  751. cmd = LWS_WRITE_HTTP;
  752. if (wsi->http.cgi->content_length_seen + n ==
  753. wsi->http.cgi->content_length)
  754. cmd = LWS_WRITE_HTTP_FINAL;
  755. m = lws_write(wsi, (unsigned char *)start, n, cmd);
  756. //lwsl_notice("write %d\n", m);
  757. if (m < 0) {
  758. lwsl_debug("%s: stdout write says %d\n", __func__, m);
  759. return -1;
  760. }
  761. wsi->http.cgi->content_length_seen += n;
  762. } else {
  763. if (!wsi->mux_substream && m) {
  764. uint8_t term[LWS_PRE + 6];
  765. lwsl_info("%s: sent trailer\n", __func__);
  766. memcpy(term + LWS_PRE, (uint8_t *)"0\x0d\x0a\x0d\x0a", 5);
  767. if (lws_write(wsi, term + LWS_PRE, 5,
  768. LWS_WRITE_HTTP_FINAL) != 5)
  769. return -1;
  770. wsi->http.cgi->cgi_transaction_over = 1;
  771. return 0;
  772. }
  773. if (wsi->cgi_stdout_zero_length) {
  774. lwsl_debug("%s: stdout is POLLHUP'd\n", __func__);
  775. if (wsi->mux_substream)
  776. m = lws_write(wsi, (unsigned char *)start, 0,
  777. LWS_WRITE_HTTP_FINAL);
  778. else
  779. return -1;
  780. return 1;
  781. }
  782. wsi->cgi_stdout_zero_length = 1;
  783. }
  784. return 0;
  785. }
  786. int
  787. lws_cgi_kill(struct lws *wsi)
  788. {
  789. struct lws_cgi_args args;
  790. pid_t pid;
  791. int n, m;
  792. lwsl_debug("%s: %p\n", __func__, wsi);
  793. if (!wsi->http.cgi || !wsi->http.cgi->lsp)
  794. return 0;
  795. pid = wsi->http.cgi->lsp->child_pid;
  796. args.stdwsi = &wsi->http.cgi->lsp->stdwsi[0];
  797. lws_spawn_piped_kill_child_process(wsi->http.cgi->lsp);
  798. /* that has invalidated and NULL'd wsi->http.cgi->lsp */
  799. if (pid != -1) {
  800. m = wsi->http.cgi->being_closed;
  801. n = user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
  802. LWS_CALLBACK_CGI_TERMINATED,
  803. wsi->user_space, (void *)&args,
  804. pid);
  805. if (n && !m)
  806. lws_close_free_wsi(wsi, 0, "lws_cgi_kill");
  807. }
  808. return 0;
  809. }
  810. int
  811. lws_cgi_kill_terminated(struct lws_context_per_thread *pt)
  812. {
  813. struct lws_cgi **pcgi, *cgi = NULL;
  814. int status, n = 1;
  815. while (n > 0) {
  816. /* find finished guys but don't reap yet */
  817. n = waitpid(-1, &status, WNOHANG);
  818. if (n <= 0)
  819. continue;
  820. lwsl_debug("%s: observed PID %d terminated\n", __func__, n);
  821. pcgi = &pt->http.cgi_list;
  822. /* check all the subprocesses on the cgi list */
  823. while (*pcgi) {
  824. /* get the next one first as list may change */
  825. cgi = *pcgi;
  826. pcgi = &(*pcgi)->cgi_list;
  827. if (cgi->lsp->child_pid <= 0)
  828. continue;
  829. /* finish sending cached headers */
  830. if (cgi->headers_buf)
  831. continue;
  832. /* wait for stdout to be drained */
  833. if (cgi->content_length > cgi->content_length_seen)
  834. continue;
  835. if (cgi->content_length) {
  836. lwsl_debug("%s: wsi %p: expected content "
  837. "length seen: %lld\n", __func__,
  838. cgi->wsi,
  839. (unsigned long long)cgi->content_length_seen);
  840. }
  841. /* reap it */
  842. waitpid(n, &status, WNOHANG);
  843. /*
  844. * he's already terminated so no need for kill()
  845. * but we should do the terminated cgi callback
  846. * and close him if he's not already closing
  847. */
  848. if (n == cgi->lsp->child_pid) {
  849. lwsl_debug("%s: found PID %d on cgi list\n",
  850. __func__, n);
  851. if (!cgi->content_length) {
  852. /*
  853. * well, if he sends chunked...
  854. * give him 2s after the
  855. * cgi terminated to send buffered
  856. */
  857. cgi->chunked_grace++;
  858. continue;
  859. }
  860. /* defeat kill() */
  861. cgi->lsp->child_pid = 0;
  862. lws_cgi_kill(cgi->wsi);
  863. break;
  864. }
  865. cgi = NULL;
  866. }
  867. /* if not found on the cgi list, as he's one of ours, reap */
  868. if (!cgi) {
  869. lwsl_debug("%s: reading PID %d although no cgi match\n",
  870. __func__, n);
  871. waitpid(n, &status, WNOHANG);
  872. }
  873. }
  874. pcgi = &pt->http.cgi_list;
  875. /* check all the subprocesses on the cgi list */
  876. while (*pcgi) {
  877. /* get the next one first as list may change */
  878. cgi = *pcgi;
  879. pcgi = &(*pcgi)->cgi_list;
  880. if (!cgi || !cgi->lsp || cgi->lsp->child_pid <= 0)
  881. continue;
  882. /* we deferred killing him after reaping his PID */
  883. if (cgi->chunked_grace) {
  884. cgi->chunked_grace++;
  885. if (cgi->chunked_grace < 2)
  886. continue;
  887. goto finish_him;
  888. }
  889. /* finish sending cached headers */
  890. if (cgi->headers_buf)
  891. continue;
  892. /* wait for stdout to be drained */
  893. if (cgi->content_length > cgi->content_length_seen)
  894. continue;
  895. if (cgi->content_length)
  896. lwsl_debug("%s: wsi %p: expected "
  897. "content len seen: %lld\n", __func__,
  898. cgi->wsi,
  899. (unsigned long long)cgi->content_length_seen);
  900. /* reap it */
  901. if (waitpid(cgi->lsp->child_pid, &status, WNOHANG) > 0) {
  902. if (!cgi->content_length) {
  903. /*
  904. * well, if he sends chunked...
  905. * give him 2s after the
  906. * cgi terminated to send buffered
  907. */
  908. cgi->chunked_grace++;
  909. continue;
  910. }
  911. finish_him:
  912. lwsl_debug("%s: found PID %d on cgi list\n",
  913. __func__, cgi->lsp->child_pid);
  914. /* defeat kill() */
  915. cgi->lsp->child_pid = 0;
  916. lws_cgi_kill(cgi->wsi);
  917. break;
  918. }
  919. }
  920. return 0;
  921. }
  922. struct lws *
  923. lws_cgi_get_stdwsi(struct lws *wsi, enum lws_enum_stdinouterr ch)
  924. {
  925. if (!wsi->http.cgi)
  926. return NULL;
  927. return wsi->http.cgi->lsp->stdwsi[ch];
  928. }
  929. void
  930. lws_cgi_remove_and_kill(struct lws *wsi)
  931. {
  932. struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
  933. struct lws_cgi **pcgi = &pt->http.cgi_list;
  934. /* remove us from the cgi list */
  935. lwsl_debug("%s: remove cgi %p from list\n", __func__, wsi->http.cgi);
  936. while (*pcgi) {
  937. if (*pcgi == wsi->http.cgi) {
  938. /* drop us from the pt cgi list */
  939. *pcgi = (*pcgi)->cgi_list;
  940. break;
  941. }
  942. pcgi = &(*pcgi)->cgi_list;
  943. }
  944. if (wsi->http.cgi->headers_buf) {
  945. lwsl_debug("%s: close: freed cgi headers\n", __func__);
  946. lws_free_set_NULL(wsi->http.cgi->headers_buf);
  947. }
  948. /* we have a cgi going, we must kill it */
  949. wsi->http.cgi->being_closed = 1;
  950. lws_cgi_kill(wsi);
  951. }