uhttpd-cgi.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. /*
  2. * uhttpd - Tiny single-threaded httpd - CGI handler
  3. *
  4. * Copyright (C) 2010 Jo-Philipp Wich <[email protected]>
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. #include "uhttpd.h"
  19. #include "uhttpd-utils.h"
  20. #include "uhttpd-cgi.h"
  21. static struct http_response * uh_cgi_header_parse(char *buf, int len, int *off)
  22. {
  23. char *bufptr = NULL;
  24. char *hdrname = NULL;
  25. int hdrcount = 0;
  26. int pos = 0;
  27. static struct http_response res;
  28. if( ((bufptr = strfind(buf, len, "\r\n\r\n", 4)) != NULL) ||
  29. ((bufptr = strfind(buf, len, "\n\n", 2)) != NULL)
  30. ) {
  31. *off = (int)(bufptr - buf) + ((bufptr[0] == '\r') ? 4 : 2);
  32. memset(&res, 0, sizeof(res));
  33. res.statuscode = 200;
  34. res.statusmsg = "OK";
  35. bufptr = &buf[0];
  36. for( pos = 0; pos < len; pos++ )
  37. {
  38. if( !hdrname && (buf[pos] == ':') )
  39. {
  40. buf[pos++] = 0;
  41. if( (pos < len) && (buf[pos] == ' ') )
  42. pos++;
  43. if( pos < len )
  44. {
  45. hdrname = bufptr;
  46. bufptr = &buf[pos];
  47. }
  48. }
  49. else if( (buf[pos] == '\r') || (buf[pos] == '\n') )
  50. {
  51. buf[pos++] = 0;
  52. if( ! hdrname )
  53. break;
  54. if( (pos < len) && (buf[pos] == '\n') )
  55. pos++;
  56. if( pos <= len )
  57. {
  58. if( (hdrcount + 1) < array_size(res.headers) )
  59. {
  60. if( ! strcasecmp(hdrname, "Status") )
  61. {
  62. res.statuscode = atoi(bufptr);
  63. if( res.statuscode < 100 )
  64. res.statuscode = 200;
  65. if( ((bufptr = strchr(bufptr, ' ')) != NULL) && (&bufptr[1] != 0) )
  66. res.statusmsg = &bufptr[1];
  67. }
  68. else
  69. {
  70. res.headers[hdrcount++] = hdrname;
  71. res.headers[hdrcount++] = bufptr;
  72. }
  73. bufptr = &buf[pos];
  74. hdrname = NULL;
  75. }
  76. else
  77. {
  78. return NULL;
  79. }
  80. }
  81. }
  82. }
  83. return &res;
  84. }
  85. return NULL;
  86. }
  87. static char * uh_cgi_header_lookup(struct http_response *res, const char *hdrname)
  88. {
  89. int i;
  90. foreach_header(i, res->headers)
  91. {
  92. if( ! strcasecmp(res->headers[i], hdrname) )
  93. return res->headers[i+1];
  94. }
  95. return NULL;
  96. }
  97. static int uh_cgi_error_500(struct client *cl, struct http_request *req, const char *message)
  98. {
  99. if( uh_http_sendf(cl, NULL,
  100. "HTTP/%.1f 500 Internal Server Error\r\n"
  101. "Content-Type: text/plain\r\n%s\r\n",
  102. req->version,
  103. (req->version > 1.0)
  104. ? "Transfer-Encoding: chunked\r\n" : ""
  105. ) >= 0
  106. ) {
  107. return uh_http_send(cl, req, message, -1);
  108. }
  109. return -1;
  110. }
  111. void uh_cgi_request(
  112. struct client *cl, struct http_request *req,
  113. struct path_info *pi, struct interpreter *ip
  114. ) {
  115. int i, hdroff, bufoff, rv;
  116. int hdrlen = 0;
  117. int buflen = 0;
  118. int fd_max = 0;
  119. int content_length = 0;
  120. int header_sent = 0;
  121. int rfd[2] = { 0, 0 };
  122. int wfd[2] = { 0, 0 };
  123. char buf[UH_LIMIT_MSGHEAD];
  124. char hdr[UH_LIMIT_MSGHEAD];
  125. pid_t child;
  126. fd_set reader;
  127. fd_set writer;
  128. sigset_t ss;
  129. struct sigaction sa;
  130. struct timeval timeout;
  131. struct http_response *res;
  132. /* spawn pipes for me->child, child->me */
  133. if( (pipe(rfd) < 0) || (pipe(wfd) < 0) )
  134. {
  135. uh_http_sendhf(cl, 500, "Internal Server Error",
  136. "Failed to create pipe: %s", strerror(errno));
  137. if( rfd[0] > 0 ) close(rfd[0]);
  138. if( rfd[1] > 0 ) close(rfd[1]);
  139. if( wfd[0] > 0 ) close(wfd[0]);
  140. if( wfd[1] > 0 ) close(wfd[1]);
  141. return;
  142. }
  143. /* fork off child process */
  144. switch( (child = fork()) )
  145. {
  146. /* oops */
  147. case -1:
  148. uh_http_sendhf(cl, 500, "Internal Server Error",
  149. "Failed to fork child: %s", strerror(errno));
  150. return;
  151. /* exec child */
  152. case 0:
  153. /* unblock signals */
  154. sigemptyset(&ss);
  155. sigprocmask(SIG_SETMASK, &ss, NULL);
  156. /* restore SIGTERM */
  157. sa.sa_flags = 0;
  158. sa.sa_handler = SIG_DFL;
  159. sigemptyset(&sa.sa_mask);
  160. sigaction(SIGTERM, &sa, NULL);
  161. /* close loose pipe ends */
  162. close(rfd[0]);
  163. close(wfd[1]);
  164. /* patch stdout and stdin to pipes */
  165. dup2(rfd[1], 1);
  166. dup2(wfd[0], 0);
  167. /* avoid leaking our pipe into child-child processes */
  168. fd_cloexec(rfd[1]);
  169. fd_cloexec(wfd[0]);
  170. /* check for regular, world-executable file _or_ interpreter */
  171. if( ((pi->stat.st_mode & S_IFREG) &&
  172. (pi->stat.st_mode & S_IXOTH)) || (ip != NULL)
  173. ) {
  174. /* build environment */
  175. clearenv();
  176. /* common information */
  177. setenv("GATEWAY_INTERFACE", "CGI/1.1", 1);
  178. setenv("SERVER_SOFTWARE", "uHTTPd", 1);
  179. setenv("PATH", "/sbin:/usr/sbin:/bin:/usr/bin", 1);
  180. #ifdef HAVE_TLS
  181. /* https? */
  182. if( cl->tls )
  183. setenv("HTTPS", "on", 1);
  184. #endif
  185. /* addresses */
  186. setenv("SERVER_NAME", sa_straddr(&cl->servaddr), 1);
  187. setenv("SERVER_ADDR", sa_straddr(&cl->servaddr), 1);
  188. setenv("SERVER_PORT", sa_strport(&cl->servaddr), 1);
  189. setenv("REMOTE_HOST", sa_straddr(&cl->peeraddr), 1);
  190. setenv("REMOTE_ADDR", sa_straddr(&cl->peeraddr), 1);
  191. setenv("REMOTE_PORT", sa_strport(&cl->peeraddr), 1);
  192. /* path information */
  193. setenv("SCRIPT_NAME", pi->name, 1);
  194. setenv("SCRIPT_FILENAME", pi->phys, 1);
  195. setenv("DOCUMENT_ROOT", pi->root, 1);
  196. setenv("QUERY_STRING", pi->query ? pi->query : "", 1);
  197. if( pi->info )
  198. setenv("PATH_INFO", pi->info, 1);
  199. /* REDIRECT_STATUS, php-cgi wants it */
  200. switch( req->redirect_status )
  201. {
  202. case 404:
  203. setenv("REDIRECT_STATUS", "404", 1);
  204. break;
  205. default:
  206. setenv("REDIRECT_STATUS", "200", 1);
  207. break;
  208. }
  209. /* http version */
  210. if( req->version > 1.0 )
  211. setenv("SERVER_PROTOCOL", "HTTP/1.1", 1);
  212. else
  213. setenv("SERVER_PROTOCOL", "HTTP/1.0", 1);
  214. /* request method */
  215. switch( req->method )
  216. {
  217. case UH_HTTP_MSG_GET:
  218. setenv("REQUEST_METHOD", "GET", 1);
  219. break;
  220. case UH_HTTP_MSG_HEAD:
  221. setenv("REQUEST_METHOD", "HEAD", 1);
  222. break;
  223. case UH_HTTP_MSG_POST:
  224. setenv("REQUEST_METHOD", "POST", 1);
  225. break;
  226. }
  227. /* request url */
  228. setenv("REQUEST_URI", req->url, 1);
  229. /* remote user */
  230. if( req->realm )
  231. setenv("REMOTE_USER", req->realm->user, 1);
  232. /* request message headers */
  233. foreach_header(i, req->headers)
  234. {
  235. if( ! strcasecmp(req->headers[i], "Accept") )
  236. setenv("HTTP_ACCEPT", req->headers[i+1], 1);
  237. else if( ! strcasecmp(req->headers[i], "Accept-Charset") )
  238. setenv("HTTP_ACCEPT_CHARSET", req->headers[i+1], 1);
  239. else if( ! strcasecmp(req->headers[i], "Accept-Encoding") )
  240. setenv("HTTP_ACCEPT_ENCODING", req->headers[i+1], 1);
  241. else if( ! strcasecmp(req->headers[i], "Accept-Language") )
  242. setenv("HTTP_ACCEPT_LANGUAGE", req->headers[i+1], 1);
  243. else if( ! strcasecmp(req->headers[i], "Authorization") )
  244. setenv("HTTP_AUTHORIZATION", req->headers[i+1], 1);
  245. else if( ! strcasecmp(req->headers[i], "Connection") )
  246. setenv("HTTP_CONNECTION", req->headers[i+1], 1);
  247. else if( ! strcasecmp(req->headers[i], "Cookie") )
  248. setenv("HTTP_COOKIE", req->headers[i+1], 1);
  249. else if( ! strcasecmp(req->headers[i], "Host") )
  250. setenv("HTTP_HOST", req->headers[i+1], 1);
  251. else if( ! strcasecmp(req->headers[i], "Referer") )
  252. setenv("HTTP_REFERER", req->headers[i+1], 1);
  253. else if( ! strcasecmp(req->headers[i], "User-Agent") )
  254. setenv("HTTP_USER_AGENT", req->headers[i+1], 1);
  255. else if( ! strcasecmp(req->headers[i], "Content-Type") )
  256. setenv("CONTENT_TYPE", req->headers[i+1], 1);
  257. else if( ! strcasecmp(req->headers[i], "Content-Length") )
  258. setenv("CONTENT_LENGTH", req->headers[i+1], 1);
  259. }
  260. /* execute child code ... */
  261. if( chdir(pi->root) )
  262. perror("chdir()");
  263. if( ip != NULL )
  264. execl(ip->path, ip->path, pi->phys, NULL);
  265. else
  266. execl(pi->phys, pi->phys, NULL);
  267. /* in case it fails ... */
  268. printf(
  269. "Status: 500 Internal Server Error\r\n\r\n"
  270. "Unable to launch the requested CGI program:\n"
  271. " %s: %s\n",
  272. ip ? ip->path : pi->phys, strerror(errno)
  273. );
  274. }
  275. /* 403 */
  276. else
  277. {
  278. printf(
  279. "Status: 403 Forbidden\r\n\r\n"
  280. "Access to this resource is forbidden\n"
  281. );
  282. }
  283. close(wfd[0]);
  284. close(rfd[1]);
  285. exit(0);
  286. break;
  287. /* parent; handle I/O relaying */
  288. default:
  289. /* close unneeded pipe ends */
  290. close(rfd[1]);
  291. close(wfd[0]);
  292. /* max watch fd */
  293. fd_max = max(rfd[0], wfd[1]) + 1;
  294. /* find content length */
  295. if( req->method == UH_HTTP_MSG_POST )
  296. {
  297. foreach_header(i, req->headers)
  298. {
  299. if( ! strcasecmp(req->headers[i], "Content-Length") )
  300. {
  301. content_length = atoi(req->headers[i+1]);
  302. break;
  303. }
  304. }
  305. }
  306. memset(hdr, 0, sizeof(hdr));
  307. /* I/O loop, watch our pipe ends and dispatch child reads/writes from/to socket */
  308. while( 1 )
  309. {
  310. FD_ZERO(&reader);
  311. FD_ZERO(&writer);
  312. FD_SET(rfd[0], &reader);
  313. FD_SET(wfd[1], &writer);
  314. timeout.tv_sec = (header_sent < 1) ? cl->server->conf->script_timeout : 3;
  315. timeout.tv_usec = 0;
  316. ensure_out(rv = select_intr(fd_max, &reader,
  317. (content_length > -1) ? &writer : NULL, NULL, &timeout));
  318. /* timeout */
  319. if( rv == 0 )
  320. {
  321. ensure_out(kill(child, 0));
  322. }
  323. /* wait until we can read or write or both */
  324. else if( rv > 0 )
  325. {
  326. /* ready to write to cgi program */
  327. if( FD_ISSET(wfd[1], &writer) )
  328. {
  329. /* there is unread post data waiting */
  330. if( content_length > 0 )
  331. {
  332. /* read it from socket ... */
  333. ensure_out(buflen = uh_tcp_recv(cl, buf,
  334. min(content_length, sizeof(buf))));
  335. if( buflen > 0 )
  336. {
  337. /* ... and write it to child's stdin */
  338. if( write(wfd[1], buf, buflen) < 0 )
  339. perror("write()");
  340. content_length -= buflen;
  341. }
  342. /* unexpected eof! */
  343. else
  344. {
  345. if( write(wfd[1], "", 0) < 0 )
  346. perror("write()");
  347. content_length = 0;
  348. }
  349. }
  350. /* there is no more post data, close pipe to child's stdin */
  351. else if( content_length > -1 )
  352. {
  353. close(wfd[1]);
  354. content_length = -1;
  355. }
  356. }
  357. /* ready to read from cgi program */
  358. if( FD_ISSET(rfd[0], &reader) )
  359. {
  360. /* read data from child ... */
  361. if( (buflen = read(rfd[0], buf, sizeof(buf))) > 0 )
  362. {
  363. /* we have not pushed out headers yet, parse input */
  364. if( ! header_sent )
  365. {
  366. /* head buffer not full and no end yet */
  367. if( hdrlen < sizeof(hdr) )
  368. {
  369. bufoff = min(buflen, sizeof(hdr) - hdrlen);
  370. memcpy(&hdr[hdrlen], buf, bufoff);
  371. hdrlen += bufoff;
  372. }
  373. else
  374. {
  375. bufoff = 0;
  376. }
  377. /* try to parse header ... */
  378. if( (res = uh_cgi_header_parse(hdr, hdrlen, &hdroff)) != NULL )
  379. {
  380. /* write status */
  381. ensure_out(uh_http_sendf(cl, NULL,
  382. "HTTP/%.1f %03d %s\r\n"
  383. "Connection: close\r\n",
  384. req->version, res->statuscode,
  385. res->statusmsg));
  386. /* add Content-Type if no Location or Content-Type */
  387. if( !uh_cgi_header_lookup(res, "Location") &&
  388. !uh_cgi_header_lookup(res, "Content-Type")
  389. ) {
  390. ensure_out(uh_http_send(cl, NULL,
  391. "Content-Type: text/plain\r\n", -1));
  392. }
  393. /* if request was HTTP 1.1 we'll respond chunked */
  394. if( (req->version > 1.0) &&
  395. !uh_cgi_header_lookup(res, "Transfer-Encoding")
  396. ) {
  397. ensure_out(uh_http_send(cl, NULL,
  398. "Transfer-Encoding: chunked\r\n", -1));
  399. }
  400. /* write headers from CGI program */
  401. foreach_header(i, res->headers)
  402. {
  403. ensure_out(uh_http_sendf(cl, NULL, "%s: %s\r\n",
  404. res->headers[i], res->headers[i+1]));
  405. }
  406. /* terminate header */
  407. ensure_out(uh_http_send(cl, NULL, "\r\n", -1));
  408. /* push out remaining head buffer */
  409. if( hdroff < hdrlen )
  410. ensure_out(uh_http_send(cl, req, &hdr[hdroff], hdrlen - hdroff));
  411. }
  412. /* ... failed and head buffer exceeded */
  413. else if( hdrlen >= sizeof(hdr) )
  414. {
  415. ensure_out(uh_cgi_error_500(cl, req,
  416. "The CGI program generated an invalid response:\n\n"));
  417. ensure_out(uh_http_send(cl, req, hdr, hdrlen));
  418. }
  419. /* ... failed but free buffer space, try again */
  420. else
  421. {
  422. continue;
  423. }
  424. /* push out remaining read buffer */
  425. if( bufoff < buflen )
  426. ensure_out(uh_http_send(cl, req, &buf[bufoff], buflen - bufoff));
  427. header_sent = 1;
  428. continue;
  429. }
  430. /* headers complete, pass through buffer to socket */
  431. ensure_out(uh_http_send(cl, req, buf, buflen));
  432. }
  433. /* looks like eof from child */
  434. else
  435. {
  436. /* cgi script did not output useful stuff at all */
  437. if( ! header_sent )
  438. {
  439. /* I would do this ...
  440. *
  441. * uh_cgi_error_500(cl, req,
  442. * "The CGI program generated an "
  443. * "invalid response:\n\n");
  444. *
  445. * ... but in order to stay as compatible as possible,
  446. * treat whatever we got as text/plain response and
  447. * build the required headers here.
  448. */
  449. ensure_out(uh_http_sendf(cl, NULL,
  450. "HTTP/%.1f 200 OK\r\n"
  451. "Content-Type: text/plain\r\n"
  452. "%s\r\n",
  453. req->version, (req->version > 1.0)
  454. ? "Transfer-Encoding: chunked\r\n" : ""
  455. ));
  456. ensure_out(uh_http_send(cl, req, hdr, hdrlen));
  457. }
  458. /* send final chunk if we're in chunked transfer mode */
  459. ensure_out(uh_http_send(cl, req, "", 0));
  460. break;
  461. }
  462. }
  463. }
  464. /* timeout exceeded or interrupted by SIGCHLD */
  465. else
  466. {
  467. if( (errno != EINTR) && ! header_sent )
  468. {
  469. ensure_out(uh_http_sendhf(cl, 504, "Gateway Timeout",
  470. "The CGI script took too long to produce "
  471. "a response"));
  472. }
  473. /* send final chunk if we're in chunked transfer mode */
  474. ensure_out(uh_http_send(cl, req, "", 0));
  475. break;
  476. }
  477. }
  478. out:
  479. close(rfd[0]);
  480. close(wfd[1]);
  481. if( !kill(child, 0) )
  482. {
  483. kill(child, SIGTERM);
  484. waitpid(child, NULL, 0);
  485. }
  486. break;
  487. }
  488. }