cgi.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /*
  2. * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
  3. * All rights reserved
  4. *
  5. * "THE BEER-WARE LICENSE" (Revision 42):
  6. * Sergey Lyubka wrote this file. As long as you retain this notice you
  7. * can do whatever you want with this stuff. If we meet some day, and you think
  8. * this stuff is worth it, you can buy me a beer in return.
  9. */
  10. #include "defs.h"
  11. #if !defined(NO_CGI)
  12. struct env_block {
  13. char buf[ENV_MAX]; /* Environment buffer */
  14. int len; /* Space taken */
  15. char *vars[CGI_ENV_VARS]; /* Point into the buffer */
  16. int nvars; /* Number of variables */
  17. };
  18. static void
  19. addenv(struct env_block *block, const char *fmt, ...)
  20. {
  21. int n, space;
  22. va_list ap;
  23. space = sizeof(block->buf) - block->len - 2;
  24. assert(space >= 0);
  25. va_start(ap, fmt);
  26. n = vsnprintf(block->buf + block->len, space, fmt, ap);
  27. va_end(ap);
  28. if (n > 0 && n < space && block->nvars < CGI_ENV_VARS - 2) {
  29. block->vars[block->nvars++] = block->buf + block->len;
  30. block->len += n + 1; /* Include \0 terminator */
  31. }
  32. }
  33. static void
  34. add_http_headers_to_env(struct env_block *b, const char *s, int len)
  35. {
  36. const char *p, *v, *e = s + len;
  37. int space, n, i, ch;
  38. /* Loop through all headers in the request */
  39. while (s < e) {
  40. /* Find where this header ends. Remember where value starts */
  41. for (p = s, v = NULL; p < e && *p != '\n'; p++)
  42. if (v == NULL && *p == ':')
  43. v = p;
  44. /* 2 null terminators and "HTTP_" */
  45. space = (sizeof(b->buf) - b->len) - (2 + 5);
  46. assert(space >= 0);
  47. /* Copy header if enough space in the environment block */
  48. if (v > s && p > v + 2 && space > p - s) {
  49. /* Store var */
  50. if (b->nvars < (int) NELEMS(b->vars) - 1)
  51. b->vars[b->nvars++] = b->buf + b->len;
  52. (void) memcpy(b->buf + b->len, "HTTP_", 5);
  53. b->len += 5;
  54. /* Copy header name. Substitute '-' to '_' */
  55. n = v - s;
  56. for (i = 0; i < n; i++) {
  57. ch = s[i] == '-' ? '_' : s[i];
  58. b->buf[b->len++] = toupper(ch);
  59. }
  60. b->buf[b->len++] = '=';
  61. /* Copy header value */
  62. v += 2;
  63. n = p[-1] == '\r' ? (p - v) - 1 : p - v;
  64. for (i = 0; i < n; i++)
  65. b->buf[b->len++] = v[i];
  66. /* Null-terminate */
  67. b->buf[b->len++] = '\0';
  68. }
  69. s = p + 1; /* Shift to the next header */
  70. }
  71. }
  72. static void
  73. prepare_environment(const struct conn *c, const char *prog,
  74. struct env_block *blk)
  75. {
  76. const struct headers *h = &c->ch;
  77. const char *s, *fname, *root = c->ctx->options[OPT_ROOT];
  78. size_t len;
  79. blk->len = blk->nvars = 0;
  80. /* SCRIPT_FILENAME */
  81. fname = prog;
  82. if ((s = strrchr(prog, '/')))
  83. fname = s + 1;
  84. /* Prepare the environment block */
  85. addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
  86. addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
  87. addenv(blk, "%s", "REDIRECT_STATUS=200"); /* PHP */
  88. addenv(blk, "SERVER_PORT=%d", c->loc_port);
  89. addenv(blk, "SERVER_NAME=%s", c->ctx->options[OPT_AUTH_REALM]);
  90. addenv(blk, "SERVER_ROOT=%s", root);
  91. addenv(blk, "DOCUMENT_ROOT=%s", root);
  92. addenv(blk, "REQUEST_METHOD=%s",
  93. _shttpd_known_http_methods[c->method].ptr);
  94. addenv(blk, "REMOTE_ADDR=%s", inet_ntoa(c->sa.u.sin.sin_addr));
  95. addenv(blk, "REMOTE_PORT=%hu", ntohs(c->sa.u.sin.sin_port));
  96. addenv(blk, "REQUEST_URI=%s", c->uri);
  97. addenv(blk, "SCRIPT_NAME=%s", prog + strlen(root));
  98. addenv(blk, "SCRIPT_FILENAME=%s", fname); /* PHP */
  99. addenv(blk, "PATH_TRANSLATED=%s", prog);
  100. if (h->ct.v_vec.len > 0)
  101. addenv(blk, "CONTENT_TYPE=%.*s",
  102. h->ct.v_vec.len, h->ct.v_vec.ptr);
  103. if (c->query != NULL)
  104. addenv(blk, "QUERY_STRING=%s", c->query);
  105. if (c->path_info != NULL)
  106. addenv(blk, "PATH_INFO=/%s", c->path_info);
  107. if (h->cl.v_big_int > 0)
  108. addenv(blk, "CONTENT_LENGTH=%lu", h->cl.v_big_int);
  109. if ((s = getenv("PATH")) != NULL)
  110. addenv(blk, "PATH=%s", s);
  111. #ifdef _WIN32
  112. if ((s = getenv("COMSPEC")) != NULL)
  113. addenv(blk, "COMSPEC=%s", s);
  114. if ((s = getenv("SYSTEMROOT")) != NULL)
  115. addenv(blk, "SYSTEMROOT=%s", s);
  116. #else
  117. if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
  118. addenv(blk, "LD_LIBRARY_PATH=%s", s);
  119. #endif /* _WIN32 */
  120. if ((s = getenv("PERLLIB")) != NULL)
  121. addenv(blk, "PERLLIB=%s", s);
  122. if (h->user.v_vec.len > 0) {
  123. addenv(blk, "REMOTE_USER=%.*s",
  124. h->user.v_vec.len, h->user.v_vec.ptr);
  125. addenv(blk, "%s", "AUTH_TYPE=Digest");
  126. }
  127. /* Add user-specified variables */
  128. s = c->ctx->options[OPT_CGI_ENVIRONMENT];
  129. FOR_EACH_WORD_IN_LIST(s, len)
  130. addenv(blk, "%.*s", len, s);
  131. /* Add all headers as HTTP_* variables */
  132. add_http_headers_to_env(blk, c->headers,
  133. c->rem.headers_len - (c->headers - c->request));
  134. blk->vars[blk->nvars++] = NULL;
  135. blk->buf[blk->len++] = '\0';
  136. assert(blk->nvars < CGI_ENV_VARS);
  137. assert(blk->len > 0);
  138. assert(blk->len < (int) sizeof(blk->buf));
  139. /* Debug stuff to view passed environment */
  140. DBG(("%s: %d vars, %d env size", prog, blk->nvars, blk->len));
  141. {
  142. int i;
  143. for (i = 0 ; i < blk->nvars; i++)
  144. DBG(("[%s]", blk->vars[i] ? blk->vars[i] : "null"));
  145. }
  146. }
  147. int
  148. _shttpd_run_cgi(struct conn *c, const char *prog)
  149. {
  150. struct env_block blk;
  151. char dir[FILENAME_MAX], *p;
  152. int ret, pair[2];
  153. prepare_environment(c, prog, &blk);
  154. pair[0] = pair[1] = -1;
  155. /* CGI must be executed in its own directory */
  156. (void) _shttpd_snprintf(dir, sizeof(dir), "%s", prog);
  157. for (p = dir + strlen(dir) - 1; p > dir; p--)
  158. if (*p == '/') {
  159. *p++ = '\0';
  160. break;
  161. }
  162. if (shttpd_socketpair(pair) != 0) {
  163. ret = -1;
  164. } else if (_shttpd_spawn_process(c,
  165. prog, blk.buf, blk.vars, pair[1], dir)) {
  166. ret = -1;
  167. (void) closesocket(pair[0]);
  168. (void) closesocket(pair[1]);
  169. } else {
  170. ret = 0;
  171. c->loc.chan.sock = pair[0];
  172. }
  173. return (ret);
  174. }
  175. void
  176. _shttpd_do_cgi(struct conn *c)
  177. {
  178. DBG(("running CGI: [%s]", c->uri));
  179. assert(c->loc.io.size > CGI_REPLY_LEN);
  180. memcpy(c->loc.io.buf, CGI_REPLY, CGI_REPLY_LEN);
  181. c->loc.io.head = c->loc.io.tail = c->loc.io.total = CGI_REPLY_LEN;
  182. c->loc.io_class = &_shttpd_io_cgi;
  183. c->loc.flags = FLAG_R;
  184. if (c->method == METHOD_POST)
  185. c->loc.flags |= FLAG_W;
  186. }
  187. #endif /* !NO_CGI */