fcgi-responder.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /*
  2. * simple and trivial FastCGI server w/ hard-coded results for use in unit tests
  3. * - processes a single FastCGI request at a time (serially)
  4. * - listens on FCGI_LISTENSOCK_FILENO
  5. * (socket on FCGI_LISTENSOCK_FILENO must be set up by invoker)
  6. * expects to be started w/ listening socket already on FCGI_LISTENSOCK_FILENO
  7. * - expect recv data for request headers every 10ms or less
  8. * - no read timeouts for request body; might block reading request body
  9. * - no write timeouts; might block writing response
  10. *
  11. * Copyright(c) 2020 Glenn Strauss gstrauss()gluelogic.com All rights reserved
  12. * License: BSD 3-clause (same as lighttpd)
  13. */
  14. #if defined(__sun)
  15. #define __EXTENSIONS__
  16. #endif
  17. #include <sys/types.h>
  18. #include <sys/socket.h>
  19. #include <assert.h>
  20. #include <errno.h>
  21. #include <fcntl.h>
  22. #include <limits.h>
  23. #include <poll.h>
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26. #include <stdint.h>
  27. #include <string.h>
  28. #include <unistd.h>
  29. #ifdef HAVE_SIGNAL /* XXX: must be defined; config.h not included here */
  30. #include <signal.h>
  31. #endif
  32. #ifndef MSG_DONTWAIT
  33. #define MSG_DONTWAIT 0
  34. #endif
  35. #include "../src/compat/fastcgi.h"
  36. static int finished;
  37. static unsigned char buf[65536];
  38. static void
  39. fcgi_header (FCGI_Header * const header, const unsigned char type, const int request_id, const int contentLength, const unsigned char paddingLength)
  40. {
  41. /*force_assert(contentLength <= FCGI_MAX_LENGTH);*/
  42. header->version = FCGI_VERSION_1;
  43. header->type = type;
  44. header->requestIdB1 = (request_id >> 8) & 0xff;
  45. header->requestIdB0 = request_id & 0xff;
  46. header->contentLengthB1 = (contentLength >> 8) & 0xff;
  47. header->contentLengthB0 = contentLength & 0xff;
  48. header->paddingLength = paddingLength;
  49. header->reserved = 0;
  50. }
  51. static void
  52. fcgi_unknown_type_rec (FCGI_UnknownTypeRecord * const rec, const int req_id, const unsigned char type)
  53. {
  54. fcgi_header(&rec->header, FCGI_UNKNOWN_TYPE, req_id, sizeof(rec->header), 0);
  55. memset(&rec->body.reserved, 0, sizeof(rec->body.reserved));
  56. rec->body.type = type;
  57. }
  58. static void
  59. fcgi_end_request_rec (FCGI_EndRequestRecord * const rec, const int req_id, const uint32_t appStatus, const unsigned char protocolStatus)
  60. {
  61. fcgi_header(&rec->header, FCGI_END_REQUEST, req_id, sizeof(rec->header), 0);
  62. rec->body.appStatusB3 = (appStatus >> 24) & 0xff;
  63. rec->body.appStatusB2 = (appStatus >> 16) & 0xff;
  64. rec->body.appStatusB1 = (appStatus >> 8) & 0xff;
  65. rec->body.appStatusB0 = appStatus & 0xff;
  66. rec->body.protocolStatus = protocolStatus;
  67. rec->body.reserved[0] = 0;
  68. rec->body.reserved[1] = 0;
  69. rec->body.reserved[2] = 0;
  70. }
  71. static int
  72. fcgi_puts (const int req_id, const char * const str, size_t len, FILE * const stream)
  73. {
  74. if (NULL == str) return -1;
  75. FCGI_Header header;
  76. for (size_t offset = 0, part; offset != len; offset += part) {
  77. part = len - offset > FCGI_MAX_LENGTH ? FCGI_MAX_LENGTH : len - offset;
  78. fcgi_header(&header, FCGI_STDOUT, req_id, part, 0);
  79. if (1 != fwrite(&header, sizeof(header), 1, stream))
  80. return -1;
  81. if (part != fwrite(str+offset, 1, part, stream))
  82. return -1;
  83. }
  84. return 0;
  85. }
  86. static const char *
  87. fcgi_getenv(const unsigned char * const r, const uint32_t rlen, const char * const name, int nlen, int *len)
  88. {
  89. /* simple search;
  90. * if many lookups are done, then should use more efficient data structure*/
  91. for (uint32_t i = 0; i < rlen; ) {
  92. int klen = r[i];
  93. if (!(r[i] & 0x80))
  94. ++i;
  95. else {
  96. klen = ((r[i] & ~0x80)<<24) | (r[i+1]<<16) | (r[i+2]<<8) | r[i+3];
  97. i += 4;
  98. }
  99. int vlen = r[i];
  100. if (!(r[i] & 0x80))
  101. ++i;
  102. else {
  103. vlen = ((r[i] & ~0x80)<<24) | (r[i+1]<<16) | (r[i+2]<<8) | r[i+3];
  104. i += 4;
  105. }
  106. if (klen == nlen && 0 == memcmp(r+i, name, klen)) {
  107. *len = vlen;
  108. return (const char *)r+i+klen;
  109. }
  110. i += klen + vlen;
  111. }
  112. char s[256];
  113. if (nlen > (int)sizeof(s)-1)
  114. return NULL;
  115. memcpy(s, name, nlen);
  116. s[nlen] = '\0';
  117. char *e = getenv(s);
  118. if (e) *len = strlen(e);
  119. return e;
  120. }
  121. static int
  122. fcgi_process_params (FILE * const stream, int req_id, int role, unsigned char * const r, uint32_t rlen)
  123. {
  124. const char *p = NULL;
  125. int len;
  126. /* (FCGI_STDIN currently ignored in these FastCGI unit test responses, so
  127. * generate response here based on query string values (indicating test) */
  128. const char *cdata = NULL;
  129. if (NULL != (p = fcgi_getenv(r, rlen, "QUERY_STRING", 12, &len))) {
  130. if (2 == len && 0 == memcmp(p, "lf", 2))
  131. cdata = "Status: 200 OK\n\n";
  132. else if (4 == len && 0 == memcmp(p, "crlf", 4))
  133. cdata = "Status: 200 OK\r\n\r\n";
  134. else if (7 == len && 0 == memcmp(p, "slow-lf", 7)) {
  135. cdata = "Status: 200 OK\n";
  136. if (0 != fcgi_puts(req_id, cdata, strlen(cdata), stream))
  137. return -1;
  138. fflush(stdout);
  139. cdata = "\n";
  140. }
  141. else if (9 == len && 0 == memcmp(p, "slow-crlf", 9)) {
  142. cdata = "Status: 200 OK\r\n";
  143. if (0 != fcgi_puts(req_id, cdata, strlen(cdata), stream))
  144. return -1;
  145. fflush(stdout);
  146. cdata = "\r\n";
  147. }
  148. else if (10 == len && 0 == memcmp(p, "die-at-end", 10)) {
  149. cdata = "Status: 200 OK\r\n\r\n";
  150. finished = 1;
  151. }
  152. else if (role == FCGI_AUTHORIZER
  153. && len >= 5 && 0 == memcmp(p, "auth-", 5)) {
  154. if (7 == len && 0 == memcmp(p, "auth-ok", 7))
  155. cdata = "Status: 200 OK\r\n\r\n";
  156. else if (8 == len && 0 == memcmp(p, "auth-var", 8)) {
  157. /* Status: 200 OK to allow access is implied
  158. * if Status header is not included in response */
  159. cdata = "Variable-X-LIGHTTPD-FCGI-AUTH: "
  160. "LighttpdTestContent\r\n\r\n";
  161. p = NULL;
  162. }
  163. else {
  164. cdata = "Status: 403 Forbidden\r\n\r\n";
  165. p = NULL;
  166. }
  167. }
  168. else
  169. cdata = "Status: 200 OK\r\n\r\n";
  170. }
  171. else {
  172. cdata = "Status: 500 Internal Foo\r\n\r\n";
  173. p = NULL;
  174. }
  175. if (cdata && 0 != fcgi_puts(req_id, cdata, strlen(cdata), stream))
  176. return -1;
  177. if (NULL == p)
  178. cdata = NULL;
  179. else if (len > 4 && 0 == memcmp(p, "env=", 4))
  180. cdata = fcgi_getenv(r, rlen, p+4, len-4, &len);
  181. else if (8 == len && 0 == memcmp(p, "auth-var", 8))
  182. cdata = fcgi_getenv(r, rlen, "X_LIGHTTPD_FCGI_AUTH", 20, &len);
  183. else {
  184. cdata = "test123";
  185. len = sizeof("test123")-1;
  186. }
  187. if (cdata && 0 != fcgi_puts(req_id, cdata, (size_t)len, stream))
  188. return -1;
  189. /*(XXX: always sending appStatus 0)*/
  190. FCGI_EndRequestRecord endrec;
  191. fcgi_end_request_rec(&endrec, req_id, 0, FCGI_REQUEST_COMPLETE);
  192. if (1 != fwrite(&endrec, sizeof(endrec), 1, stream))
  193. return -1; /* error writing FCGI_END_REQUEST; ignore */
  194. return -2; /* done */
  195. }
  196. static int
  197. fcgi_dispatch_packet (FILE *stream, ssize_t offset, uint32_t len)
  198. {
  199. FCGI_Header * const h = (FCGI_Header *)(buf+offset);
  200. int req_id = (h->requestIdB1 << 8) | h->requestIdB0;
  201. int type = h->type;
  202. if (type > FCGI_MAXTYPE) {
  203. FCGI_UnknownTypeRecord unkrec;
  204. fcgi_unknown_type_rec(&unkrec, req_id, type);
  205. if (1 != fwrite(&unkrec, sizeof(unkrec), 1, stream))
  206. return -1;
  207. return 0;
  208. }
  209. if (0 == req_id) {
  210. /* not implemented: FCGI_GET_VALUES
  211. * FCGI_GET_VALUES_RESULT
  212. * FCGI_MAX_CONNS: 1
  213. * FCGI_MAX_REQS: 1
  214. * FCGI_MPXS_CONNS: 0
  215. * ...
  216. */
  217. return 0;
  218. }
  219. /* XXX: save role from FCGI_BEGIN_REQUEST; should keep independent state */
  220. static int role;
  221. switch (type) {
  222. case FCGI_BEGIN_REQUEST:
  223. role = (buf[offset+FCGI_HEADER_LEN] << 8)
  224. | buf[offset+FCGI_HEADER_LEN+1];
  225. return 0; /* ignore; could save req_id and match further packets */
  226. case FCGI_ABORT_REQUEST:
  227. return -2; /* done */
  228. case FCGI_END_REQUEST:
  229. return -1; /* unexpected; this server is not sending FastCGI requests */
  230. case FCGI_PARAMS:
  231. return fcgi_process_params(stream, req_id, role,
  232. buf+offset+FCGI_HEADER_LEN, len);
  233. case FCGI_STDIN:
  234. /* XXX: TODO read and discard request body
  235. * (currently ignored in these FastCGI unit tests)
  236. * (make basic effort to read body; ignore any timeouts or errors) */
  237. return -1; /* unexpected; this server is not expecting request body */
  238. case FCGI_STDOUT:
  239. return -1; /* unexpected; this server is not sending FastCGI requests */
  240. case FCGI_STDERR:
  241. return -1; /* unexpected; this server is not sending FastCGI requests */
  242. case FCGI_DATA:
  243. return -1; /* unexpected; this server is not sending FastCGI requests */
  244. case FCGI_GET_VALUES:
  245. return 0; /* ignore; not implemented */
  246. case FCGI_GET_VALUES_RESULT:
  247. return 0; /* ignore; not implemented */
  248. default:
  249. return -1; /* unexpected */
  250. }
  251. }
  252. static ssize_t
  253. fcgi_recv_packet (FILE * const stream, ssize_t sz)
  254. {
  255. ssize_t offset = 0;
  256. while (sz - offset >= (ssize_t)FCGI_HEADER_LEN) {
  257. FCGI_Header * const h = (FCGI_Header *)(buf+offset);
  258. uint32_t pad = h->paddingLength;
  259. uint32_t len = (h->contentLengthB1 << 8) | h->contentLengthB0;
  260. if (sz - offset < (ssize_t)(FCGI_HEADER_LEN + len + pad))
  261. break;
  262. int rc = fcgi_dispatch_packet(stream, offset, len);
  263. if (rc < 0)
  264. return rc;
  265. offset += (ssize_t)(FCGI_HEADER_LEN + len + pad);
  266. }
  267. return offset;
  268. }
  269. static int
  270. fcgi_recv (const int fd, FILE * const stream)
  271. {
  272. ssize_t rd = 0, offset = 0;
  273. /* XXX: remain blocking */
  274. /*fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);*/
  275. do {
  276. struct pollfd pfd = { fd, POLLIN, 0 };
  277. switch (poll(&pfd, 1, 10)) { /* 10ms timeout */
  278. default: /* 1; the only pfd has revents */
  279. break;
  280. case -1: /* error */
  281. case 0: /* timeout */
  282. pfd.revents |= POLLERR;
  283. break;
  284. }
  285. if (!(pfd.revents & POLLIN))
  286. break;
  287. do {
  288. rd = recv(fd, buf+offset, sizeof(buf)-offset, MSG_DONTWAIT);
  289. } while (rd < 0 && errno == EINTR);
  290. if (rd > 0) {
  291. offset += rd;
  292. rd = fcgi_recv_packet(stream, offset);
  293. if (rd < 0)
  294. return (-2 == rd) ? 0 : -1; /*(-2 indicates done)*/
  295. if (rd > 0) {
  296. offset -= rd;
  297. if (offset)
  298. memmove(buf, buf+rd, offset);
  299. }
  300. }
  301. else if (0 == rd || (errno != EAGAIN && errno != EWOULDBLOCK))
  302. break;
  303. } while (offset < (ssize_t)sizeof(buf));
  304. return -1;
  305. }
  306. int
  307. main (void)
  308. {
  309. int fd;
  310. fcntl(FCGI_LISTENSOCK_FILENO, F_SETFL,
  311. fcntl(FCGI_LISTENSOCK_FILENO, F_GETFL) & ~O_NONBLOCK);
  312. #ifdef HAVE_SIGNAL
  313. signal(SIGINT, SIG_IGN);
  314. signal(SIGUSR1, SIG_IGN);
  315. #endif
  316. do {
  317. fd = accept(FCGI_LISTENSOCK_FILENO, NULL, NULL);
  318. if (fd < 0)
  319. continue;
  320. /* XXX: skip checking FCGI_WEB_SERVER_ADDRS; not implemented */
  321. /* uses stdio to retain prior behavior of output buffering (default)
  322. * and flushing with fflush() at specific points */
  323. FILE *stream = fdopen(fd, "r+");
  324. if (NULL == stream) {
  325. close(fd);
  326. continue;
  327. }
  328. fcgi_recv(fd, stream);
  329. fflush(stream);
  330. fclose(stream);
  331. } while (fd > 0 ? !finished : errno == EINTR);
  332. return 0;
  333. }