ksgi
/
fcgi.c
760 строк · 17.2 Кб
1/* $Id$ */
2/*
3* Copyright (c) 2015--2016, 2018 Kristaps Dzonsons <kristaps@bsd.lv>
4*
5* Permission to use, copy, modify, and distribute this software for any
6* purpose with or without fee is hereby granted, provided that the above
7* copyright notice and this permission notice appear in all copies.
8*
9* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*/
17#include "config.h"
18
19#include <sys/ioctl.h>
20#include <sys/socket.h>
21
22#include <assert.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <inttypes.h>
26#include <limits.h>
27#include <poll.h>
28#include <signal.h>
29#include <stdarg.h>
30#include <stdint.h>
31#include <stdlib.h>
32#include <stdio.h> /* BUFSIZ */
33#include <string.h>
34#include <unistd.h>
35
36#include "kcgi.h"
37#include "extern.h"
38
39struct kfcgi {
40const struct kvalid *keys;
41size_t keysz;
42const char *const *mimes;
43size_t mimesz;
44size_t defmime;
45unsigned int debugging;
46const char *const *pages;
47size_t pagesz;
48size_t defpage;
49const struct kmimemap *mimemap;
50pid_t work_pid;
51pid_t sock_pid;
52int work_dat;
53int sock_ctl;
54struct kopts opts;
55void *arg;
56};
57
58static volatile sig_atomic_t sig = 0;
59
60static void
61dosignal(int arg)
62{
63
64sig = 1;
65}
66
67/*
68* This is our control process.
69* It listens for FastCGI connections on the manager connection in
70* traditional mode ("fdaccept") xor extended mode ("fdfiled").
71* When it has one, it reads and passes to the worker (sibling) process,
72* which will be parsing the data.
73* When the worker has finished, it passes back the request identifier,
74* which this passes to the main application for output.
75* If the current FastCGI connection closes, abandon it and wait for the
76* next.
77* This exits with the manager connection closes.
78* On exit, it will close the fdaccept or fdfiled descriptor.
79*/
80static int
81kfcgi_control(int work, int ctrl,
82int fdaccept, int fdfiled, pid_t worker)
83{
84struct sockaddr_storage ss;
85socklen_t sslen;
86int fd = -1, rc, ourfd, erc = EXIT_FAILURE;
87uint64_t magic;
88uint32_t cookie, test;
89struct pollfd pfd[2];
90char buf[BUFSIZ];
91ssize_t ssz;
92enum kcgi_err kerr;
93uint16_t rid, rtest;
94
95ourfd = fdaccept == -1 ? fdfiled : fdaccept;
96assert(ourfd != -1);
97
98if (kxsocketprep(ourfd) != KCGI_OK)
99goto out;
100
101for (;;) {
102pfd[0].fd = ourfd;
103pfd[0].events = POLLIN;
104pfd[0].revents = 0;
105pfd[1].fd = ctrl;
106pfd[1].events = POLLIN;
107pfd[1].revents = 0;
108
109/*
110* If either the worker or manager disconnect, then exit
111* cleanly.
112* The calling application will check the worker's exit
113* code, which will say whether it did something bad, so
114* we don't really care.
115*/
116
117if ((rc = poll(pfd, 2, INFTIM)) < 0) {
118kutil_warn(NULL, NULL, "poll");
119goto out;
120} else if (rc == 0) {
121kutil_warnx(NULL, NULL, "poll: timeout!?");
122continue;
123}
124
125if ((pfd[1].revents & POLLHUP) ||
126!(pfd[0].revents & POLLIN))
127break;
128
129/*
130* Accept a new connection.
131* In the old way, a blocking accept from FastCGI
132* socket.
133* This will be round-robined by the kernel so that
134* other control processes are fairly notified.
135* In the new one, accepting a descriptor from the
136* caller.
137*/
138
139if (fdaccept != -1) {
140assert(fdfiled == -1);
141sslen = sizeof(ss);
142fd = accept(fdaccept,
143(struct sockaddr *)&ss, &sslen);
144if (fd < 0) {
145if (errno == EAGAIN ||
146errno == EWOULDBLOCK)
147continue;
148kutil_warn(NULL, NULL, "accept");
149goto out;
150}
151} else {
152assert(fdfiled != -1);
153rc = fullreadfd(fdfiled,
154&fd, &magic, sizeof(uint64_t));
155if (rc < 0)
156goto out;
157else if (rc == 0)
158break;
159}
160
161/*
162* We then set that the FastCGI socket is non-blocking,
163* making it consistent with the behaviour of the CGI
164* socket, which is also set as such.
165*/
166
167if (kxsocketprep(fd) != KCGI_OK)
168goto out;
169
170/* This doesn't need to be crypto quality. */
171
172#if HAVE_ARC4RANDOM
173cookie = arc4random();
174#else
175cookie = random();
176#endif
177
178/* Write a header cookie to the work. */
179
180fullwrite(work, &cookie, sizeof(uint32_t));
181
182/*
183* Keep pushing data into the worker til it has read it
184* all, at which point it will write to us.
185* If at any point we have errors (e.g., the connection
186* closes), then write a zero-length frame.
187* Write a zero-length frame at the end anyway.
188*/
189
190pfd[0].fd = fd;
191pfd[0].events = POLLIN;
192pfd[1].fd = work;
193pfd[1].events = POLLIN;
194
195for (;;) {
196if ((rc = poll(pfd, 2, INFTIM)) < 0) {
197kutil_warn(NULL, NULL, "poll");
198goto out;
199} else if (rc == 0) {
200kutil_warnx(NULL, NULL, "poll: timeout!?");
201continue;
202}
203
204/*
205* If the child responds, that means that the
206* full request has been read and processed.
207* If not, we probably still have data to write
208* to it from the connection.
209*/
210
211if (pfd[1].revents & POLLIN) {
212ssz = 0;
213kerr = fullwritenoerr
214(pfd[1].fd, &ssz, sizeof(size_t));
215if (kerr != KCGI_OK)
216goto out;
217break;
218}
219
220if (!(pfd[0].revents & POLLIN)) {
221kutil_warnx(NULL, NULL, "poll: no input");
222goto out;
223} else if ((ssz = read(fd, buf, BUFSIZ)) < 0) {
224kutil_warn(NULL, NULL, "read");
225goto out;
226}
227
228/*
229* Send the child the amount of data we've read.
230* This will let the child see if the connection
231* abruptly closes, at which point we'll have a
232* read size of zero, and error out.
233*/
234
235kerr = fullwritenoerr
236(pfd[1].fd, &ssz, sizeof(size_t));
237if (KCGI_OK != kerr)
238goto out;
239
240kerr = fullwritenoerr(pfd[1].fd, buf, ssz);
241if (KCGI_OK != kerr)
242goto out;
243
244/*
245* If we wrote a zero-sized buffer, it means
246* that the connection has unexpectedly closed.
247* The child will stop all processing for the
248* request and will not return to the parsing
249* routine for the given session.
250*/
251
252if (ssz == 0) {
253kutil_warnx(NULL, NULL, "read: "
254"connection closed");
255break;
256}
257}
258
259/* Now verify that the worker is sane. */
260
261if (fullread(pfd[1].fd, &rc,
262sizeof(int), 0, &kerr) < 0)
263goto out;
264
265if (rc == 0) {
266kutil_warnx(NULL, NULL, "FastCGI: bad code");
267goto recover;
268}
269
270/*
271* We have a non-zero return code.
272* Check our cookie and responseId values.
273*/
274
275if (fullread(pfd[1].fd, &test,
276sizeof(uint32_t), 0, &kerr) < 0)
277goto out;
278
279if (cookie != test) {
280kutil_warnx(NULL, NULL, "FastCGI: bad cookie");
281goto out;
282}
283
284if (fullread(pfd[1].fd, &rid,
285sizeof(uint16_t), 0, &kerr) < 0)
286goto out;
287
288/*
289* Pass the file descriptor, which has had its data
290* sucked dry, to the main application.
291* It will do output, so it also needs the FastCGI
292* socket request identifier.
293*/
294
295if (!fullwritefd(ctrl, fd, &rid, sizeof(uint16_t)))
296goto out;
297
298/*
299* This will wait til the application is finished.
300* It will then double-check the requestId.
301*/
302
303if (fullread(ctrl, &rtest,
304sizeof(uint16_t), 0, &kerr) < 0)
305goto out;
306
307if (rid != rtest) {
308kutil_warnx(NULL, NULL, "FastCGI: bad cookie");
309goto out;
310}
311
312recover:
313/*
314* If we are being passed descriptors (instead of
315* waiting on the accept()), then notify the manager
316* that we've finished processing this request.
317* We also jump to here if the connection fails in any
318* way whilst being transcribed to the worker.
319*/
320
321if (fdfiled != -1) {
322kerr = fullwritenoerr(fdfiled,
323&magic, sizeof(uint64_t));
324if (KCGI_OK != kerr)
325goto out;
326}
327
328close(fd);
329fd = -1;
330}
331
332erc = EXIT_SUCCESS;
333out:
334if (fd != -1)
335close(fd);
336close(ourfd);
337return erc;
338}
339
340void
341khttp_fcgi_child_free(struct kfcgi *fcgi)
342{
343
344close(fcgi->sock_ctl);
345close(fcgi->work_dat);
346free(fcgi);
347}
348
349enum kcgi_err
350khttp_fcgi_free(struct kfcgi *fcgi)
351{
352
353if (fcgi == NULL)
354return KCGI_OK;
355
356close(fcgi->sock_ctl);
357close(fcgi->work_dat);
358kxwaitpid(fcgi->work_pid);
359kxwaitpid(fcgi->sock_pid);
360free(fcgi);
361return KCGI_OK;
362}
363
364enum kcgi_err
365khttp_fcgi_initx(struct kfcgi **fcgip,
366const char *const *mimes, size_t mimesz,
367const struct kvalid *keys, size_t keysz,
368const struct kmimemap *mimemap, size_t defmime,
369const char *const *pages, size_t pagesz,
370size_t defpage, void *arg, void (*argfree)(void *),
371unsigned int debugging, const struct kopts *opts)
372{
373struct kfcgi *fcgi;
374int er, fdaccept, fdfiled;
375int work_ctl[2], work_dat[2], sock_ctl[2];
376pid_t work_pid, sock_pid;
377const char *cp, *ercp;
378sigset_t mask;
379enum sandtype st;
380
381/*
382* Determine whether we're supposed to accept() on a socket or,
383* rather, we're supposed to receive file descriptors from a
384* kfcgi-like manager.
385*/
386
387st = SAND_CONTROL_OLD;
388fdaccept = fdfiled = -1;
389
390if ((cp = getenv("FCGI_LISTENSOCK_DESCRIPTORS")) != NULL) {
391fdfiled = strtonum(cp, 0, INT_MAX, &ercp);
392if (ercp != NULL) {
393fdaccept = STDIN_FILENO;
394fdfiled = -1;
395} else
396st = SAND_CONTROL_NEW;
397} else
398fdaccept = STDIN_FILENO;
399
400/*
401* Block this signal unless we're right at the fullreadfd
402* function, at which point unblock and let it interrupt us.
403* We don't save the signal mask because we're allowed free
404* reign on the SIGTERM value.
405*/
406
407if (signal(SIGTERM, dosignal) == SIG_ERR) {
408kutil_warn(NULL, NULL, "signal");
409return KCGI_SYSTEM;
410}
411
412sigemptyset(&mask);
413sigaddset(&mask, SIGTERM);
414sigprocmask(SIG_BLOCK, &mask, NULL);
415sig = 0;
416
417if (kxsocketpair(work_ctl) != KCGI_OK)
418return KCGI_SYSTEM;
419
420if (kxsocketpair(work_dat) != KCGI_OK) {
421close(work_ctl[KWORKER_PARENT]);
422close(work_ctl[KWORKER_CHILD]);
423return KCGI_SYSTEM;
424}
425
426if ((work_pid = fork()) == -1) {
427er = errno;
428kutil_warn(NULL, NULL, "fork");
429close(work_ctl[KWORKER_PARENT]);
430close(work_ctl[KWORKER_CHILD]);
431close(work_dat[KWORKER_PARENT]);
432close(work_dat[KWORKER_CHILD]);
433return (er == EAGAIN) ? KCGI_EAGAIN : KCGI_ENOMEM;
434} else if (work_pid == 0) {
435if (signal(SIGTERM, SIG_IGN) == SIG_ERR) {
436kutil_warn(NULL, NULL, "signal");
437_exit(EXIT_FAILURE);
438}
439
440if (argfree != NULL)
441argfree(arg);
442
443/*
444* STDIN_FILENO isn't really stdin, it's the control
445* socket used to pass input sockets to us.
446* Thus, close the parent's control socket.
447*/
448
449if (fdaccept != -1)
450close(fdaccept);
451if (fdfiled != -1)
452close(fdfiled);
453
454close(STDOUT_FILENO);
455close(work_dat[KWORKER_PARENT]);
456close(work_ctl[KWORKER_PARENT]);
457
458/*
459* Prep child sandbox and run worker process.
460* The worker code will exit on failure, so no need to
461* check any return codes on it.
462*/
463
464er = EXIT_SUCCESS;
465if (!ksandbox_init_child(SAND_WORKER,
466work_dat[KWORKER_CHILD],
467work_ctl[KWORKER_CHILD], -1, -1))
468er = EXIT_FAILURE;
469else
470kworker_fcgi_child
471(work_dat[KWORKER_CHILD],
472work_ctl[KWORKER_CHILD],
473keys, keysz, mimes, mimesz,
474debugging);
475
476close(work_dat[KWORKER_CHILD]);
477close(work_ctl[KWORKER_CHILD]);
478_exit(er);
479/* NOTREACHED */
480}
481
482close(work_dat[KWORKER_CHILD]);
483close(work_ctl[KWORKER_CHILD]);
484
485if (kxsocketpair(sock_ctl) != KCGI_OK) {
486close(work_dat[KWORKER_PARENT]);
487close(work_ctl[KWORKER_PARENT]);
488kxwaitpid(work_pid);
489return KCGI_SYSTEM;
490}
491
492if ((sock_pid = fork()) == -1) {
493er = errno;
494kutil_warn(NULL, NULL, "fork");
495close(work_dat[KWORKER_PARENT]);
496close(work_ctl[KWORKER_PARENT]);
497close(sock_ctl[KWORKER_CHILD]);
498close(sock_ctl[KWORKER_PARENT]);
499kxwaitpid(work_pid);
500return (er == EAGAIN) ? KCGI_EAGAIN : KCGI_ENOMEM;
501} else if (sock_pid == 0) {
502if (signal(SIGTERM, SIG_IGN) == SIG_ERR) {
503kutil_warn(NULL, NULL, "signal");
504_exit(EXIT_FAILURE);
505}
506
507if (argfree != NULL)
508argfree(arg);
509
510close(STDOUT_FILENO);
511close(work_dat[KWORKER_PARENT]);
512close(sock_ctl[KWORKER_PARENT]);
513
514if (!ksandbox_init_child(st,
515sock_ctl[KWORKER_CHILD], -1, fdfiled, fdaccept))
516er = EXIT_FAILURE;
517else
518er = kfcgi_control
519(work_ctl[KWORKER_PARENT],
520sock_ctl[KWORKER_CHILD],
521fdaccept, fdfiled, work_pid);
522
523close(work_ctl[KWORKER_PARENT]);
524close(sock_ctl[KWORKER_CHILD]);
525_exit(er);
526/* NOTREACHED */
527}
528
529close(sock_ctl[KWORKER_CHILD]);
530close(work_ctl[KWORKER_PARENT]);
531
532if (fdaccept != -1)
533close(fdaccept);
534if (fdfiled != -1)
535close(fdfiled);
536
537/* Now allocate our device. */
538
539*fcgip = fcgi = kxcalloc(1, sizeof(struct kfcgi));
540if (fcgi == NULL) {
541close(sock_ctl[KWORKER_PARENT]);
542close(work_dat[KWORKER_PARENT]);
543kxwaitpid(work_pid);
544kxwaitpid(sock_pid);
545return KCGI_ENOMEM;
546}
547
548if (opts == NULL)
549fcgi->opts.sndbufsz = -1;
550else
551memcpy(&fcgi->opts, opts, sizeof(struct kopts));
552
553if (fcgi->opts.sndbufsz < 0)
554fcgi->opts.sndbufsz = UINT16_MAX;
555
556fcgi->work_pid = work_pid;
557fcgi->work_dat = work_dat[KWORKER_PARENT];
558fcgi->sock_pid = sock_pid;
559fcgi->sock_ctl = sock_ctl[KWORKER_PARENT];
560fcgi->arg = arg;
561fcgi->mimes = mimes;
562fcgi->mimesz = mimesz;
563fcgi->defmime = defmime;
564fcgi->keys = keys;
565fcgi->keysz = keysz;
566fcgi->mimemap = mimemap;
567fcgi->pages = pages;
568fcgi->pagesz = pagesz;
569fcgi->defpage = defpage;
570fcgi->debugging = debugging;
571return KCGI_OK;
572}
573
574enum kcgi_err
575khttp_fcgi_init(struct kfcgi **fcgi,
576const struct kvalid *keys, size_t keysz,
577const char *const *pages, size_t pagesz,
578size_t defpage)
579{
580
581return khttp_fcgi_initx(fcgi, kmimetypes,
582KMIME__MAX, keys, keysz, ksuffixmap,
583KMIME_TEXT_HTML, pages, pagesz, defpage,
584NULL, NULL, 0, NULL);
585}
586
587/*
588* Here we wait for the next FastCGI connection in such a way that, if
589* we're notified that we must exit via a SIGTERM, we'll properly close
590* down without spurious warnings.
591*/
592static enum kcgi_err
593fcgi_waitread(int fd)
594{
595int rc;
596struct pollfd pfd;
597sigset_t mask;
598
599again:
600pfd.fd = fd;
601pfd.events = POLLIN;
602
603/*
604* Unblock SIGTERM around the poll().
605* XXX: this could be drastically simplified with ppoll(), but
606* it's not available on many systems.
607* TODO: provide a shim in wrappers.c.
608*/
609
610sigemptyset(&mask);
611sigaddset(&mask, SIGTERM);
612sigprocmask(SIG_UNBLOCK, &mask, NULL);
613rc = poll(&pfd, 1, 1000);
614sigprocmask(SIG_BLOCK, &mask, NULL);
615
616/* Exit signal has been set. */
617
618if (sig)
619return KCGI_EXIT;
620
621/* Problems? Exit. Timeout? Retry. */
622
623if (rc < 0) {
624kutil_warn(NULL, NULL, "poll");
625return KCGI_SYSTEM;
626} else if (rc == 0)
627goto again;
628
629/* Only POLLIN is a "good" exit from this. */
630
631if (pfd.revents & POLLIN)
632return KCGI_OK;
633else if (pfd.revents & POLLHUP)
634return KCGI_EXIT;
635
636kutil_warnx(NULL, NULL, "poll: error");
637return KCGI_SYSTEM;
638}
639
640int
641khttp_fcgi_getfd(const struct kfcgi *fcgi)
642{
643return fcgi->sock_ctl;
644}
645
646enum kcgi_err
647khttp_fcgi_parse(struct kfcgi *fcgi, struct kreq *req)
648{
649enum kcgi_err kerr;
650const struct kmimemap *mm;
651int c, fd = -1;
652uint16_t rid;
653
654memset(req, 0, sizeof(struct kreq));
655
656/*
657* Blocking wait until our control process sends us the file
658* descriptor and requestId of the current sequence.
659* It may also decide to exit, which we note by seeing that
660* "sig" has been set (inheriting the SIGTERM) or the channel
661* closed gracefully.
662*/
663
664if ((kerr = fcgi_waitread(fcgi->sock_ctl)) != KCGI_OK)
665return kerr;
666
667c = fullreadfd(fcgi->sock_ctl, &fd, &rid, sizeof(uint16_t));
668if (c < 0)
669return KCGI_SYSTEM;
670else if (c == 0)
671return KCGI_EXIT;
672
673/* Now get ready to receive data from the child. */
674
675req->arg = fcgi->arg;
676req->keys = fcgi->keys;
677req->keysz = fcgi->keysz;
678req->kdata = kdata_alloc(fcgi->sock_ctl,
679fd, rid, fcgi->debugging, &fcgi->opts);
680
681if (req->kdata == NULL) {
682close(fd);
683goto err;
684}
685
686if (fcgi->keysz) {
687req->cookiemap = kxcalloc
688(fcgi->keysz, sizeof(struct kpair *));
689if (req->cookiemap == NULL)
690goto err;
691req->cookienmap = kxcalloc
692(fcgi->keysz, sizeof(struct kpair *));
693if (req->cookienmap == NULL)
694goto err;
695req->fieldmap = kxcalloc
696(fcgi->keysz, sizeof(struct kpair *));
697if (req->fieldmap == NULL)
698goto err;
699req->fieldnmap = kxcalloc
700(fcgi->keysz, sizeof(struct kpair *));
701if (req->fieldnmap == NULL)
702goto err;
703}
704
705/*
706* Read the request itself from the worker child.
707* We'll wait perpetually on data until the channel closes or
708* until we're interrupted during a read by the parent.
709*/
710
711kerr = kworker_parent(fcgi->work_dat, req, 0, fcgi->mimesz);
712if (KCGI_OK != kerr)
713goto err;
714
715/* Look up page type from component. */
716
717req->page = fcgi->defpage;
718if (*req->pagename != '\0')
719for (req->page = 0; req->page < fcgi->pagesz; req->page++)
720if (strcasecmp(fcgi->pages[req->page], req->pagename) == 0)
721break;
722
723/* Start with the default. */
724
725req->mime = fcgi->defmime;
726if (*req->suffix != '\0') {
727for (mm = fcgi->mimemap; NULL != mm->name; mm++)
728if (strcasecmp(mm->name, req->suffix) == 0) {
729req->mime = mm->mime;
730break;
731}
732/* Could not find this mime type! */
733if (mm->name == NULL)
734req->mime = fcgi->mimesz;
735}
736
737return kerr;
738err:
739kdata_free(req->kdata, 0);
740req->kdata = NULL;
741kreq_free(req);
742return kerr;
743}
744
745int
746khttp_fcgi_test(void)
747{
748socklen_t len = 0;
749const char *cp, *ercp = NULL;
750
751if ((cp = getenv("FCGI_LISTENSOCK_DESCRIPTORS")) != NULL) {
752strtonum(cp, 0, INT_MAX, &ercp);
753if (ercp == NULL)
754return 1;
755}
756
757if (getpeername(STDIN_FILENO, NULL, &len) != -1)
758return 0;
759return errno == ENOTCONN;
760}
761