Blob


1 /*
2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <sys/mman.h>
18 #include <sys/stat.h>
20 #include <netdb.h>
22 #include <assert.h>
23 #include <errno.h>
24 #include <event.h>
25 #include <fcntl.h>
26 #include <fnmatch.h>
27 #include <limits.h>
28 #include <string.h>
30 #include "gmid.h"
32 struct server {
33 struct client clients[MAX_USERS];
34 struct tls *ctx;
35 };
37 struct server_events {
38 struct event e4;
39 int has_ipv6;
40 struct event e6;
41 struct event sighup;
42 };
44 int connected_clients;
46 static inline void reschedule_read(int, struct client*, statefn);
47 static inline void reschedule_write(int, struct client*, statefn);
49 static int check_path(struct client*, const char*, int*);
50 static void open_file(struct client*);
51 static void load_file(struct client*);
52 static void check_for_cgi(struct client*);
53 static void handle_handshake(int, short, void*);
54 static char *strip_path(char*, int);
55 static void fmt_sbuf(const char*, struct client*, const char*);
56 static int apply_block_return(struct client*);
57 static int apply_require_ca(struct client*);
58 static void handle_open_conn(int, short, void*);
59 static void start_reply(struct client*, int, const char*);
60 static void handle_start_reply(int, short, void*);
61 static void start_cgi(const char*, const char*, struct client*);
62 static void send_file(int, short, void*);
63 static void open_dir(struct client*);
64 static void redirect_canonical_dir(struct client*);
65 static void enter_handle_dirlist(int, short, void*);
66 static void handle_dirlist(int, short, void*);
67 static int read_next_dir_entry(struct client*);
68 static void send_directory_listing(int, short, void*);
69 static void handle_cgi_reply(int, short, void*);
70 static void handle_cgi(int, short, void*);
71 static void close_conn(int, short, void*);
72 static void do_accept(int, short, void*);
73 static void handle_sighup(int, short, void*);
75 static inline void
76 reschedule_read(int fd, struct client *c, statefn fn)
77 {
78 event_once(fd, EV_READ, fn, c, NULL);
79 }
81 void
82 reschedule_write(int fd, struct client *c, statefn fn)
83 {
84 event_once(fd, EV_WRITE, fn, c, NULL);
85 }
87 const char *
88 vhost_lang(struct vhost *v, const char *path)
89 {
90 struct location *loc;
92 if (v == NULL || path == NULL)
93 return NULL;
95 for (loc = &v->locations[1]; loc->match != NULL; ++loc) {
96 if (loc->lang != NULL) {
97 if (!fnmatch(loc->match, path, 0))
98 return loc->lang;
99 }
102 return v->locations[0].lang;
105 const char *
106 vhost_default_mime(struct vhost *v, const char *path)
108 struct location *loc;
109 const char *default_mime = "application/octet-stream";
111 if (v == NULL || path == NULL)
112 return default_mime;
114 for (loc = &v->locations[1]; loc->match != NULL; ++loc) {
115 if (loc->default_mime != NULL) {
116 if (!fnmatch(loc->match, path, 0))
117 return loc->default_mime;
121 if (v->locations[0].default_mime != NULL)
122 return v->locations[0].default_mime;
123 return default_mime;
126 const char *
127 vhost_index(struct vhost *v, const char *path)
129 struct location *loc;
130 const char *index = "index.gmi";
132 if (v == NULL || path == NULL)
133 return index;
135 for (loc = &v->locations[1]; loc->match != NULL; ++loc) {
136 if (loc->index != NULL) {
137 if (!fnmatch(loc->match, path, 0))
138 return loc->index;
142 if (v->locations[0].index != NULL)
143 return v->locations[0].index;
144 return index;
147 int
148 vhost_auto_index(struct vhost *v, const char *path)
150 struct location *loc;
152 if (v == NULL || path == NULL)
153 return 0;
155 for (loc = &v->locations[1]; loc->match != NULL; ++loc) {
156 if (loc->auto_index != 0) {
157 if (!fnmatch(loc->match, path, 0))
158 return loc->auto_index == 1;
162 return v->locations[0].auto_index == 1;
165 int
166 vhost_block_return(struct vhost *v, const char *path, int *code, const char **fmt)
168 struct location *loc;
170 if (v == NULL || path == NULL)
171 return 0;
173 for (loc = &v->locations[1]; loc->match != NULL; ++loc) {
174 if (loc->block_code != 0) {
175 if (!fnmatch(loc->match, path, 0)) {
176 *code = loc->block_code;
177 *fmt = loc->block_fmt;
178 return 1;
183 *code = v->locations[0].block_code;
184 *fmt = v->locations[0].block_fmt;
185 return v->locations[0].block_code != 0;
188 int
189 vhost_strip(struct vhost *v, const char *path)
191 struct location *loc;
193 if (v == NULL || path == NULL)
194 return 0;
196 for (loc = &v->locations[1]; loc->match != NULL; ++loc) {
197 if (loc->strip != 0) {
198 if (!fnmatch(loc->match, path, 0))
199 return loc->strip;
203 return v->locations[0].strip;
206 X509_STORE *
207 vhost_require_ca(struct vhost *v, const char *path)
209 struct location *loc;
211 if (v == NULL || path == NULL)
212 return NULL;
214 for (loc = &v->locations[1]; loc->match != NULL; ++loc) {
215 if (loc->reqca != NULL) {
216 if (!fnmatch(loc->match, path, 0))
217 return loc->reqca;
221 return v->locations[0].reqca;
224 static int
225 check_path(struct client *c, const char *path, int *fd)
227 struct stat sb;
228 const char *p;
229 int flags;
231 assert(path != NULL);
233 if (*path == '\0')
234 p = ".";
235 else if (*path == '/')
236 /* in send_dir we add an initial / (to be
237 * redirect-friendly), but here we want to skip it */
238 p = path+1;
239 else
240 p = path;
242 flags = O_RDONLY | O_NOFOLLOW;
244 if (*fd == -1 && (*fd = openat(c->host->dirfd, p, flags)) == -1)
245 return FILE_MISSING;
247 if (fstat(*fd, &sb) == -1) {
248 log_notice(c, "failed stat for %s: %s", path, strerror(errno));
249 return FILE_MISSING;
252 if (S_ISDIR(sb.st_mode))
253 return FILE_DIRECTORY;
255 if (sb.st_mode & S_IXUSR)
256 return FILE_EXECUTABLE;
258 return FILE_EXISTS;
261 static void
262 open_file(struct client *c)
264 switch (check_path(c, c->iri.path, &c->pfd)) {
265 case FILE_EXECUTABLE:
266 if (c->host->cgi != NULL && !fnmatch(c->host->cgi, c->iri.path, 0)) {
267 start_cgi(c->iri.path, "", c);
268 return;
271 /* fallthrough */
273 case FILE_EXISTS:
274 load_file(c);
275 return;
277 case FILE_DIRECTORY:
278 open_dir(c);
279 return;
281 case FILE_MISSING:
282 if (c->host->cgi != NULL && !fnmatch(c->host->cgi, c->iri.path, 0)) {
283 check_for_cgi(c);
284 return;
286 start_reply(c, NOT_FOUND, "not found");
287 return;
289 default:
290 /* unreachable */
291 abort();
295 static void
296 load_file(struct client *c)
298 if ((c->len = filesize(c->pfd)) == -1) {
299 log_err(c, "failed to get file size for %s: %s",
300 c->iri.path, strerror(errno));
301 start_reply(c, TEMP_FAILURE, "internal server error");
302 return;
305 if ((c->buf = mmap(NULL, c->len, PROT_READ, MAP_PRIVATE,
306 c->pfd, 0)) == MAP_FAILED) {
307 log_err(c, "mmap: %s: %s", c->iri.path, strerror(errno));
308 start_reply(c, TEMP_FAILURE, "internal server error");
309 return;
311 c->i = c->buf;
312 c->next = send_file;
313 start_reply(c, SUCCESS, mime(c->host, c->iri.path));
316 /*
317 * the inverse of this algorithm, i.e. starting from the start of the
318 * path + strlen(cgi), and checking if each component, should be
319 * faster. But it's tedious to write. This does the opposite: starts
320 * from the end and strip one component at a time, until either an
321 * executable is found or we emptied the path.
322 */
323 static void
324 check_for_cgi(struct client *c)
326 char path[PATH_MAX];
327 char *end;
329 strlcpy(path, c->iri.path, sizeof(path));
330 end = strchr(path, '\0');
332 while (end > path) {
333 /* go up one level. UNIX paths are simple and POSIX
334 * dirname, with its ambiguities on if the given path
335 * is changed or not, gives me headaches. */
336 while (*end != '/')
337 end--;
338 *end = '\0';
340 switch (check_path(c, path, &c->pfd)) {
341 case FILE_EXECUTABLE:
342 start_cgi(path, end+1, c);
343 return;
344 case FILE_MISSING:
345 break;
346 default:
347 goto err;
350 *end = '/';
351 end--;
354 err:
355 start_reply(c, NOT_FOUND, "not found");
356 return;
359 void
360 mark_nonblock(int fd)
362 int flags;
364 if ((flags = fcntl(fd, F_GETFL)) == -1)
365 fatal("fcntl(F_GETFL): %s", strerror(errno));
366 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
367 fatal("fcntl(F_SETFL): %s", strerror(errno));
370 static void
371 handle_handshake(int fd, short ev, void *d)
373 struct client *c = d;
374 struct vhost *h;
375 const char *servname;
376 const char *parse_err = "unknown error";
378 switch (tls_handshake(c->ctx)) {
379 case 0: /* success */
380 case -1: /* already handshaked */
381 break;
382 case TLS_WANT_POLLIN:
383 reschedule_read(fd, c, &handle_handshake);
384 return;
385 case TLS_WANT_POLLOUT:
386 reschedule_write(fd, c, &handle_handshake);
387 return;
388 default:
389 /* unreachable */
390 abort();
393 servname = tls_conn_servername(c->ctx);
394 if (!puny_decode(servname, c->domain, sizeof(c->domain), &parse_err)) {
395 log_info(c, "puny_decode: %s", parse_err);
396 goto err;
399 for (h = hosts; h->domain != NULL; ++h) {
400 if (!fnmatch(h->domain, c->domain, 0))
401 break;
404 log_debug(c, "handshake: SNI: \"%s\"; decoded: \"%s\"; matched: \"%s\"",
405 servname != NULL ? servname : "(null)",
406 c->domain,
407 h->domain != NULL ? h->domain : "(null)");
409 if (h->domain != NULL) {
410 c->host = h;
411 handle_open_conn(fd, ev, c);
412 return;
415 err:
416 if (servname != NULL)
417 strncpy(c->req, servname, sizeof(c->req));
418 else
419 strncpy(c->req, "null", sizeof(c->req));
421 start_reply(c, BAD_REQUEST, "Wrong/malformed host or missing SNI");
424 static char *
425 strip_path(char *path, int strip)
427 char *t;
429 while (strip > 0) {
430 if ((t = strchr(path, '/')) == NULL) {
431 path = strchr(path, '\0');
432 break;
434 path = t;
435 strip--;
438 return path;
441 static void
442 fmt_sbuf(const char *fmt, struct client *c, const char *path)
444 size_t i;
445 char buf[32];
447 memset(buf, 0, sizeof(buf));
448 for (i = 0; *fmt; ++fmt) {
449 if (i == sizeof(buf)-1 || *fmt == '%') {
450 strlcat(c->sbuf, buf, sizeof(c->sbuf));
451 memset(buf, 0, sizeof(buf));
452 i = 0;
455 if (*fmt != '%') {
456 buf[i++] = *fmt;
457 continue;
460 switch (*++fmt) {
461 case '%':
462 strlcat(c->sbuf, "%", sizeof(c->sbuf));
463 break;
464 case 'p':
465 strlcat(c->sbuf, path, sizeof(c->sbuf));
466 break;
467 case 'q':
468 strlcat(c->sbuf, c->iri.query, sizeof(c->sbuf));
469 break;
470 case 'P':
471 snprintf(buf, sizeof(buf), "%d", conf.port);
472 strlcat(c->sbuf, buf, sizeof(c->sbuf));
473 memset(buf, 0, sizeof(buf));
474 break;
475 case 'N':
476 strlcat(c->sbuf, c->domain, sizeof(c->sbuf));
477 break;
478 default:
479 fatal("%s: unknown fmt specifier %c",
480 __func__, *fmt);
484 if (i != 0)
485 strlcat(c->sbuf, buf, sizeof(c->sbuf));
488 /* 1 if a matching `block return' (and apply it), 0 otherwise */
489 static int
490 apply_block_return(struct client *c)
492 const char *fmt, *path;
493 int code;
495 if (!vhost_block_return(c->host, c->iri.path, &code, &fmt))
496 return 0;
498 path = strip_path(c->iri.path, vhost_strip(c->host, c->iri.path));
499 fmt_sbuf(fmt, c, path);
501 start_reply(c, code, c->sbuf);
502 return 1;
505 /* 1 if matching `require client ca' fails (and apply it), 0 otherwise */
506 static int
507 apply_require_ca(struct client *c)
509 X509_STORE *store;
510 const uint8_t *cert;
511 size_t len;
513 if ((store = vhost_require_ca(c->host, c->iri.path)) == NULL)
514 return 0;
516 if (!tls_peer_cert_provided(c->ctx)) {
517 start_reply(c, CLIENT_CERT_REQ, "client certificate required");
518 return 1;
521 cert = tls_peer_cert_chain_pem(c->ctx, &len);
522 if (!validate_against_ca(store, cert, len)) {
523 start_reply(c, CERT_NOT_AUTH, "certificate not authorised");
524 return 1;
527 return 0;
530 static void
531 handle_open_conn(int fd, short ev, void *d)
533 struct client *c = d;
534 const char *parse_err = "invalid request";
535 char decoded[DOMAIN_NAME_LEN];
537 bzero(c->req, sizeof(c->req));
538 bzero(&c->iri, sizeof(c->iri));
540 switch (tls_read(c->ctx, c->req, sizeof(c->req)-1)) {
541 case -1:
542 log_err(c, "tls_read: %s", tls_error(c->ctx));
543 close_conn(fd, ev, c);
544 return;
546 case TLS_WANT_POLLIN:
547 reschedule_read(fd, c, &handle_open_conn);
548 return;
550 case TLS_WANT_POLLOUT:
551 reschedule_write(fd, c, &handle_open_conn);
552 return;
555 if (!trim_req_iri(c->req, &parse_err)
556 || !parse_iri(c->req, &c->iri, &parse_err)
557 || !puny_decode(c->iri.host, decoded, sizeof(decoded), &parse_err)) {
558 log_info(c, "iri parse error: %s", parse_err);
559 start_reply(c, BAD_REQUEST, "invalid request");
560 return;
563 if (c->iri.port_no != conf.port
564 || strcmp(c->iri.schema, "gemini")
565 || strcmp(decoded, c->domain)) {
566 start_reply(c, PROXY_REFUSED, "won't proxy request");
567 return;
570 if (apply_require_ca(c))
571 return;
573 if (apply_block_return(c))
574 return;
576 if (c->host->entrypoint != NULL) {
577 start_cgi(c->host->entrypoint, c->iri.path, c);
578 return;
581 open_file(c);
584 static void
585 start_reply(struct client *c, int code, const char *meta)
587 c->code = code;
588 c->meta = meta;
589 handle_start_reply(c->fd, 0, c);
592 static void
593 handle_start_reply(int fd, short ev, void *d)
595 struct client *c = d;
596 char buf[1030]; /* status + ' ' + max reply len + \r\n\0 */
597 const char *lang;
598 size_t len;
600 lang = vhost_lang(c->host, c->iri.path);
602 snprintf(buf, sizeof(buf), "%d ", c->code);
603 strlcat(buf, c->meta, sizeof(buf));
604 if (!strcmp(c->meta, "text/gemini") && lang != NULL) {
605 strlcat(buf, "; lang=", sizeof(buf));
606 strlcat(buf, lang, sizeof(buf));
609 len = strlcat(buf, "\r\n", sizeof(buf));
610 assert(len < sizeof(buf));
612 switch (tls_write(c->ctx, buf, len)) {
613 case -1:
614 close_conn(fd, ev, c);
615 return;
616 case TLS_WANT_POLLIN:
617 reschedule_read(fd, c, &handle_start_reply);
618 return;
619 case TLS_WANT_POLLOUT:
620 reschedule_write(fd, c, &handle_start_reply);
621 return;
624 log_request(c, buf, sizeof(buf));
626 if (c->code != SUCCESS)
627 close_conn(fd, ev, c);
628 else
629 c->next(fd, ev, c);
632 static void
633 start_cgi(const char *spath, const char *relpath, struct client *c)
635 char addr[NI_MAXHOST];
636 int e;
638 e = getnameinfo((struct sockaddr*)&c->addr, sizeof(c->addr),
639 addr, sizeof(addr),
640 NULL, 0,
641 NI_NUMERICHOST);
642 if (e != 0)
643 goto err;
645 if (!send_iri(exfd, &c->iri)
646 || !send_string(exfd, spath)
647 || !send_string(exfd, relpath)
648 || !send_string(exfd, addr)
649 || !send_string(exfd, tls_peer_cert_subject(c->ctx))
650 || !send_string(exfd, tls_peer_cert_issuer(c->ctx))
651 || !send_string(exfd, tls_peer_cert_hash(c->ctx))
652 || !send_time(exfd, tls_peer_cert_notbefore(c->ctx))
653 || !send_time(exfd, tls_peer_cert_notafter(c->ctx))
654 || !send_vhost(exfd, c->host))
655 goto err;
657 close(c->pfd);
658 if ((c->pfd = recv_fd(exfd)) == -1) {
659 start_reply(c, TEMP_FAILURE, "internal server error");
660 return;
663 reschedule_read(c->pfd, c, &handle_cgi_reply);
664 return;
666 err:
667 /* fatal("cannot talk to the executor process: %s", strerror(errno)); */
668 fatal("cannot talk to the executor process");
671 static void
672 send_file(int fd, short ev, void *d)
674 struct client *c = d;
675 ssize_t ret, len;
677 len = (c->buf + c->len) - c->i;
679 while (len > 0) {
680 switch (ret = tls_write(c->ctx, c->i, len)) {
681 case -1:
682 log_err(c, "tls_write: %s", tls_error(c->ctx));
683 close_conn(fd, ev, c);
684 return;
686 case TLS_WANT_POLLIN:
687 reschedule_read(fd, c, &send_file);
688 return;
690 case TLS_WANT_POLLOUT:
691 reschedule_write(fd, c, &send_file);
692 return;
694 default:
695 c->i += ret;
696 len -= ret;
697 break;
701 close_conn(fd, ev, c);
704 static void
705 open_dir(struct client *c)
707 size_t len;
708 int dirfd;
709 char *before_file;
711 len = strlen(c->iri.path);
712 if (len > 0 && !ends_with(c->iri.path, "/")) {
713 redirect_canonical_dir(c);
714 return;
717 strlcpy(c->sbuf, "/", sizeof(c->sbuf));
718 strlcat(c->sbuf, c->iri.path, sizeof(c->sbuf));
719 if (!ends_with(c->sbuf, "/"))
720 strlcat(c->sbuf, "/", sizeof(c->sbuf));
721 before_file = strchr(c->sbuf, '\0');
722 len = strlcat(c->sbuf, vhost_index(c->host, c->iri.path),
723 sizeof(c->sbuf));
724 if (len >= sizeof(c->sbuf)) {
725 start_reply(c, TEMP_FAILURE, "internal server error");
726 return;
729 c->iri.path = c->sbuf;
731 /* close later unless we have to generate the dir listing */
732 dirfd = c->pfd;
733 c->pfd = -1;
735 switch (check_path(c, c->iri.path, &c->pfd)) {
736 case FILE_EXECUTABLE:
737 if (c->host->cgi != NULL && !fnmatch(c->host->cgi, c->iri.path, 0)) {
738 start_cgi(c->iri.path, "", c);
739 break;
742 /* fallthrough */
744 case FILE_EXISTS:
745 load_file(c);
746 break;
748 case FILE_DIRECTORY:
749 start_reply(c, TEMP_REDIRECT, c->sbuf);
750 break;
752 case FILE_MISSING:
753 *before_file = '\0';
755 if (!vhost_auto_index(c->host, c->iri.path)) {
756 start_reply(c, NOT_FOUND, "not found");
757 break;
760 c->pfd = dirfd;
761 c->next = enter_handle_dirlist;
763 if ((c->dir = fdopendir(c->pfd)) == NULL) {
764 log_err(c, "fdopendir(%d) (vhost:%s) %s: %s",
765 c->pfd, c->host->domain, c->iri.path, strerror(errno));
766 start_reply(c, TEMP_FAILURE, "internal server error");
767 return;
769 c->off = 0;
771 start_reply(c, SUCCESS, "text/gemini");
772 return;
774 default:
775 /* unreachable */
776 abort();
779 close(dirfd);
782 static void
783 redirect_canonical_dir(struct client *c)
785 size_t len;
787 strlcpy(c->sbuf, "/", sizeof(c->sbuf));
788 strlcat(c->sbuf, c->iri.path, sizeof(c->sbuf));
789 len = strlcat(c->sbuf, "/", sizeof(c->sbuf));
791 if (len >= sizeof(c->sbuf)) {
792 start_reply(c, TEMP_FAILURE, "internal server error");
793 return;
796 start_reply(c, TEMP_REDIRECT, c->sbuf);
799 static void
800 enter_handle_dirlist(int fd, short ev, void *d)
802 struct client *c = d;
803 char b[PATH_MAX];
804 size_t l;
806 strlcpy(b, c->iri.path, sizeof(b));
807 l = snprintf(c->sbuf, sizeof(c->sbuf),
808 "# Index of %s\n\n", b);
809 if (l >= sizeof(c->sbuf)) {
810 /* this is impossible, given that we have enough space
811 * in c->sbuf to hold the ancilliary string plus the
812 * full path; but it wouldn't read nice without some
813 * error checking, and I'd like to avoid a strlen. */
814 close_conn(fd, ev, c);
815 return;
818 c->len = l;
819 handle_dirlist(fd, ev, c);
822 static void
823 handle_dirlist(int fd, short ev, void *d)
825 struct client *c = d;
826 ssize_t r;
828 while (c->len > 0) {
829 switch (r = tls_write(c->ctx, c->sbuf + c->off, c->len)) {
830 case -1:
831 close_conn(fd, ev, c);
832 return;
833 case TLS_WANT_POLLOUT:
834 reschedule_read(fd, c, &handle_dirlist);
835 return;
836 case TLS_WANT_POLLIN:
837 reschedule_write(fd, c, &handle_dirlist);
838 return;
839 default:
840 c->off += r;
841 c->len -= r;
845 send_directory_listing(fd, ev, c);
848 static int
849 read_next_dir_entry(struct client *c)
851 struct dirent *d;
853 do {
854 errno = 0;
855 if ((d = readdir(c->dir)) == NULL) {
856 if (errno != 0)
857 log_err(c, "readdir: %s", strerror(errno));
858 return 0;
860 } while (!strcmp(d->d_name, "."));
862 /* XXX: url escape */
863 snprintf(c->sbuf, sizeof(c->sbuf), "=> %s %s\n",
864 d->d_name, d->d_name);
865 c->len = strlen(c->sbuf);
866 c->off = 0;
868 return 1;
871 static void
872 send_directory_listing(int fd, short ev, void *d)
874 struct client *c = d;
875 ssize_t r;
877 while (1) {
878 if (c->len == 0) {
879 if (!read_next_dir_entry(c))
880 goto end;
883 while (c->len > 0) {
884 switch (r = tls_write(c->ctx, c->sbuf + c->off, c->len)) {
885 case -1:
886 goto end;
888 case TLS_WANT_POLLOUT:
889 reschedule_read(fd, c, &send_directory_listing);
890 return;
892 case TLS_WANT_POLLIN:
893 reschedule_write(fd, c, &send_directory_listing);
894 return;
896 default:
897 c->off += r;
898 c->len -= r;
899 break;
904 end:
905 close_conn(fd, ev, d);
908 /* accumulate the meta line from the cgi script. */
909 static void
910 handle_cgi_reply(int fd, short ev, void *d)
912 struct client *c = d;
913 void *buf, *e;
914 size_t len;
915 ssize_t r;
918 buf = c->sbuf + c->len;
919 len = sizeof(c->sbuf) - c->len;
921 r = read(c->pfd, buf, len);
922 if (r == 0 || r == -1) {
923 start_reply(c, CGI_ERROR, "CGI error");
924 return;
927 c->len += r;
929 /* TODO: error if the CGI script don't reply correctly */
930 e = strchr(c->sbuf, '\n');
931 if (e != NULL || c->len == sizeof(c->sbuf)) {
932 log_request(c, c->sbuf, c->len);
934 c->off = 0;
935 handle_cgi(fd, ev, c);
936 return;
939 reschedule_read(fd, c, &handle_cgi_reply);
942 static void
943 handle_cgi(int fd, short ev, void *d)
945 struct client *c = d;
946 ssize_t r;
948 while (1) {
949 if (c->len == 0) {
950 switch (r = read(c->pfd, c->sbuf, sizeof(c->sbuf))) {
951 case 0:
952 goto end;
953 case -1:
954 if (errno == EAGAIN || errno == EWOULDBLOCK) {
955 reschedule_read(c->pfd, c, &handle_cgi);
956 return;
958 goto end;
959 default:
960 c->len = r;
961 c->off = 0;
965 while (c->len > 0) {
966 switch (r = tls_write(c->ctx, c->sbuf + c->off, c->len)) {
967 case -1:
968 goto end;
970 case TLS_WANT_POLLOUT:
971 reschedule_read(c->fd, c, &handle_cgi);
972 return;
974 case TLS_WANT_POLLIN:
975 reschedule_write(c->fd, c, &handle_cgi);
976 return;
978 default:
979 c->off += r;
980 c->len -= r;
981 break;
986 end:
987 close_conn(c->fd, ev, d);
990 static void
991 close_conn(int fd, short ev, void *d)
993 struct client *c = d;
995 switch (tls_close(c->ctx)) {
996 case TLS_WANT_POLLIN:
997 reschedule_read(fd, c, &close_conn);
998 return;
999 case TLS_WANT_POLLOUT:
1000 reschedule_read(fd, c, &close_conn);
1001 return;
1004 connected_clients--;
1006 tls_free(c->ctx);
1007 c->ctx = NULL;
1009 if (c->buf != MAP_FAILED)
1010 munmap(c->buf, c->len);
1012 if (c->pfd != -1)
1013 close(c->pfd);
1015 if (c->dir != NULL)
1016 closedir(c->dir);
1018 close(c->fd);
1019 c->fd = -1;
1022 static void
1023 do_accept(int sock, short et, void *d)
1025 struct client *c;
1026 struct server *s = d;
1027 struct sockaddr_storage addr;
1028 struct sockaddr *saddr;
1029 socklen_t len;
1030 int i, fd;
1032 (void)et;
1035 saddr = (struct sockaddr*)&addr;
1036 len = sizeof(addr);
1037 if ((fd = accept4(sock, saddr, &len, SOCK_NONBLOCK)) == -1) {
1038 if (errno == EWOULDBLOCK || errno == EAGAIN)
1039 return;
1040 fatal("accept: %s", strerror(errno));
1043 for (i = 0; i < MAX_USERS; ++i) {
1044 c = &s->clients[i];
1045 if (c->fd == -1) {
1046 memset(c, 0, sizeof(*c));
1047 if (tls_accept_socket(s->ctx, &c->ctx, fd) == -1)
1048 break; /* goodbye fd! */
1050 c->fd = fd;
1051 c->pfd = -1;
1052 c->buf = MAP_FAILED;
1053 c->dir = NULL;
1054 c->addr = addr;
1056 reschedule_read(fd, c, &handle_handshake);
1057 connected_clients++;
1058 return;
1062 close(fd);
1065 static void
1066 handle_sighup(int fd, short ev, void *d)
1068 struct server_events *events = d;
1070 (void)fd;
1071 (void)ev;
1073 event_del(&events->e4);
1074 if (events->has_ipv6)
1075 event_del(&events->e6);
1076 signal_del(&events->sighup);
1079 static void
1080 handle_siginfo(int fd, short ev, void *d)
1082 (void)fd;
1083 (void)ev;
1084 (void)d;
1086 log_info(NULL, "%d connected clients", connected_clients);
1089 void
1090 loop(struct tls *ctx, int sock4, int sock6)
1092 struct server_events events;
1093 struct server server;
1094 struct event info;
1095 size_t i;
1097 event_init();
1099 memset(&events, 0, sizeof(events));
1100 memset(&server, 0, sizeof(server));
1101 for (i = 0; i < MAX_USERS; ++i)
1102 server.clients[i].fd = -1;
1104 event_set(&events.e4, sock4, EV_READ | EV_PERSIST, &do_accept, &server);
1105 event_add(&events.e4, NULL);
1107 if (sock6 != -1) {
1108 events.has_ipv6 = 1;
1109 event_set(&events.e6, sock6, EV_READ | EV_PERSIST, &do_accept, &server);
1110 event_add(&events.e6, NULL);
1113 signal_set(&events.sighup, SIGHUP, &handle_sighup, &events);
1114 signal_add(&events.sighup, NULL);
1116 #ifdef SIGINFO
1117 signal_set(&info, SIGINFO, &handle_siginfo, NULL);
1118 signal_add(&info, NULL);
1119 #endif
1120 signal_set(&info, SIGUSR2, &handle_siginfo, NULL);
1121 signal_add(&info, NULL);
1123 server.ctx = ctx;
1125 sandbox();
1126 event_dispatch();
1127 _exit(0);