diff options
author | Omar Polo <op@omarpolo.com> | 2021-01-17 23:23:58 +0000 |
---|---|---|
committer | Omar Polo <op@omarpolo.com> | 2021-01-17 23:23:58 +0000 |
commit | d3a08f4d172687d4e3e60e7faaa8830bb7f9f9db (patch) | |
tree | 1bcfcd4b2bec16247127914b983e22d5547026f8 /gmid.c | |
parent | 5f564d23e9ea96fd6565ee4099fba6c31b71567e (diff) |
reorganize: move bunch of functions to server.c
cgi.c wasn't really needed; it better to group all the server related
functions together, cgi or not. Now gmid.c contains only startup and
utility code.
Diffstat (limited to 'gmid.c')
-rw-r--r-- | gmid.c | 522 |
1 files changed, 43 insertions, 479 deletions
@@ -14,12 +14,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <sys/mman.h> -#include <sys/stat.h> - -#include <assert.h> #include <err.h> #include <errno.h> + #include <fcntl.h> #include <limits.h> #include <netdb.h> @@ -31,7 +28,6 @@ struct vhost hosts[HOSTSLEN]; -int connected_clients; int goterror; int exfd; @@ -60,9 +56,7 @@ struct etm { /* file extension to mime */ {NULL, NULL} }; -__attribute__ ((format (printf, 1, 2))) -__attribute__ ((__noreturn__)) -static inline void +void fatal(const char *fmt, ...) { va_list ap; @@ -138,31 +132,6 @@ starts_with(const char *str, const char *prefix) return 1; } -int -start_reply(struct pollfd *pfd, struct client *client, int code, const char *reason) -{ - char buf[1030]; /* status + ' ' + max reply len + \r\n\0 */ - int len; - - client->code = code; - client->meta = reason; - client->state = S_INITIALIZING; - - len = snprintf(buf, sizeof(buf), "%d %s\r\n", code, reason); - assert(len < (int)sizeof(buf)); - - switch (tls_write(client->ctx, buf, len)) { - case TLS_WANT_POLLIN: - pfd->events = POLLIN; - return 0; - case TLS_WANT_POLLOUT: - pfd->events = POLLOUT; - return 0; - default: - return 1; - } -} - ssize_t filesize(int fd) { @@ -207,286 +176,74 @@ mime(const char *path) return def; } -int -check_path(struct client *c, const char *path, int *fd) +char * +absolutify_path(const char *path) { - struct stat sb; - - assert(path != NULL); - if ((*fd = openat(c->host->dirfd, *path ? path : ".", - O_RDONLY | O_NOFOLLOW | O_CLOEXEC)) == -1) { - return FILE_MISSING; - } - - if (fstat(*fd, &sb) == -1) { - LOGN(c, "failed stat for %s: %s", path, strerror(errno)); - return FILE_MISSING; - } - - if (S_ISDIR(sb.st_mode)) - return FILE_DIRECTORY; + char *wd, *r; - if (sb.st_mode & S_IXUSR) - return FILE_EXECUTABLE; + if (*path == '/') + return strdup(path); - return FILE_EXISTS; + wd = getcwd(NULL, 0); + if (asprintf(&r, "%s/%s", wd, path) == -1) + err(1, "asprintf"); + free(wd); + return r; } -int -open_file(char *fpath, char *query, struct pollfd *fds, struct client *c) +void +yyerror(const char *msg) { - switch (check_path(c, fpath, &c->fd)) { - case FILE_EXECUTABLE: - if (c->host->cgi != NULL && starts_with(fpath, c->host->cgi)) - return start_cgi(fpath, "", query, fds, c); - - /* fallthrough */ - - case FILE_EXISTS: - if ((c->len = filesize(c->fd)) == -1) { - LOGE(c, "failed to get file size for %s", fpath); - goodbye(fds, c); - return 0; - } - - if ((c->buf = mmap(NULL, c->len, PROT_READ, MAP_PRIVATE, - c->fd, 0)) == MAP_FAILED) { - warn("mmap: %s", fpath); - goodbye(fds, c); - return 0; - } - c->i = c->buf; - return start_reply(fds, c, SUCCESS, mime(fpath)); - - case FILE_DIRECTORY: - LOGD(c, "%s is a directory, trying %s/index.gmi", fpath, fpath); - close(c->fd); - c->fd = -1; - send_dir(fpath, fds, c); - return 0; - - case FILE_MISSING: - if (c->host->cgi != NULL && starts_with(fpath, c->host->cgi)) - return check_for_cgi(fpath, query, fds, c); - - if (!start_reply(fds, c, NOT_FOUND, "not found")) - return 0; - goodbye(fds, c); - return 0; - - default: - /* unreachable */ - abort(); - } + goterror = 1; + fprintf(stderr, "%d: %s\n", yylineno, msg); } -void -send_file(char *path, char *query, struct pollfd *fds, struct client *c) +int +parse_portno(const char *p) { - ssize_t ret, len; - - if (c->fd == -1) { - if (!open_file(path, query, fds, c)) - return; - c->state = S_SENDING; - } - - len = (c->buf + c->len) - c->i; - - while (len > 0) { - switch (ret = tls_write(c->ctx, c->i, len)) { - case -1: - LOGE(c, "tls_write: %s", tls_error(c->ctx)); - goodbye(fds, c); - return; - - case TLS_WANT_POLLIN: - fds->events = POLLIN; - return; - - case TLS_WANT_POLLOUT: - fds->events = POLLOUT; - return; - - default: - c->i += ret; - len -= ret; - break; - } - } + char *ep; + long lval; - goodbye(fds, c); + errno = 0; + lval = strtol(p, &ep, 10); + if (p[0] == '\0' || *ep != '\0') + errx(1, "not a number: %s", p); + if (lval < 0 || lval > UINT16_MAX) + errx(1, "port number out of range for domain %s: %ld", p, lval); + return lval; } void -send_dir(char *path, struct pollfd *fds, struct client *client) +parse_conf(const char *path) { - char fpath[PATHBUF]; - size_t len; - - bzero(fpath, PATHBUF); - - if (path[0] != '.') - fpath[0] = '.'; - - /* this cannot fail since sizeof(fpath) > maxlen of path */ - strlcat(fpath, path, PATHBUF); - len = strlen(fpath); - - /* add a trailing / in case. */ - if (fpath[len-1] != '/') { - fpath[len] = '/'; - } - - strlcat(fpath, "index.gmi", sizeof(fpath)); + if ((yyin = fopen(path, "r")) == NULL) + err(1, "cannot open config %s", path); + yyparse(); + fclose(yyin); - send_file(fpath, NULL, fds, client); + if (goterror) + exit(1); } void -handle_handshake(struct pollfd *fds, struct client *c) +load_vhosts(struct tls_config *tlsconf) { struct vhost *h; - const char *servname; - switch (tls_handshake(c->ctx)) { - case 0: /* success */ - case -1: /* already handshaked */ - break; - case TLS_WANT_POLLIN: - fds->events = POLLIN; - return; - case TLS_WANT_POLLOUT: - fds->events = POLLOUT; - return; - default: - /* unreachable */ - abort(); - } - - servname = tls_conn_servername(c->ctx); - if (servname == NULL) - goto hostnotfound; + /* we need to set something, then we can add how many key we want */ + if (tls_config_set_keypair_file(tlsconf, hosts->cert, hosts->key)) + errx(1, "tls_config_set_keypair_file failed"); for (h = hosts; h->domain != NULL; ++h) { - if (!strcmp(h->domain, servname) || !strcmp(h->domain, "*")) - break; - } - - if (h->domain != NULL) { - c->state = S_OPEN; - c->host = h; - handle_open_conn(fds, c); - return; - } - -hostnotfound: - /* XXX: check the correct response */ - if (!start_reply(fds, c, BAD_REQUEST, "Wrong host or missing SNI")) - return; - goodbye(fds, c); -} - -void -handle_open_conn(struct pollfd *fds, struct client *c) -{ - char buf[GEMINI_URL_LEN]; - const char *parse_err = "invalid request"; - struct iri iri; - - bzero(buf, sizeof(buf)); - - switch (tls_read(c->ctx, buf, sizeof(buf)-1)) { - case -1: - LOGE(c, "tls_read: %s", tls_error(c->ctx)); - goodbye(fds, c); - return; - - case TLS_WANT_POLLIN: - fds->events = POLLIN; - return; - - case TLS_WANT_POLLOUT: - fds->events = POLLOUT; - return; - } - - if (!trim_req_iri(buf) || !parse_iri(buf, &iri, &parse_err)) { - if (!start_reply(fds, c, BAD_REQUEST, parse_err)) - return; - goodbye(fds, c); - return; - } - - if (strcmp(iri.schema, "gemini") || iri.port_no != conf.port) { - if (!start_reply(fds, c, PROXY_REFUSED, "won't proxy request")) - return; - goodbye(fds, c); - return; - } - - LOGI(c, "GET %s%s%s", - *iri.path ? iri.path : "/", - *iri.query ? "?" : "", - *iri.query ? iri.query : ""); - - send_file(iri.path, iri.query, fds, c); -} - -void -handle(struct pollfd *fds, struct client *client) -{ - switch (client->state) { - case S_HANDSHAKE: - handle_handshake(fds, client); - break; - - case S_OPEN: - handle_open_conn(fds, client); - break; - - case S_INITIALIZING: - if (!start_reply(fds, client, client->code, client->meta)) - return; - - if (client->code != SUCCESS) { - /* we don't need a body */ - goodbye(fds, client); - return; - } - - client->state = S_SENDING; - - /* fallthrough */ - - case S_SENDING: - if (client->child) - handle_cgi(fds, client); - else - send_file(NULL, NULL, fds, client); - break; - - case S_CLOSING: - goodbye(fds, client); - break; + if (tls_config_add_keypair_file(tlsconf, h->cert, h->key) == -1) + errx(1, "failed to load the keypair (%s, %s)", + h->cert, h->key); - default: - /* unreachable */ - abort(); + if ((h->dirfd = open(h->dir, O_RDONLY | O_DIRECTORY)) == -1) + err(1, "open %s for domain %s", h->dir, h->domain); } } -void -mark_nonblock(int fd) -{ - int flags; - - if ((flags = fcntl(fd, F_GETFL)) == -1) - fatal("fcntl(F_GETFL): %s", strerror(errno)); - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - fatal("fcntl(F_SETFL): %s", strerror(errno)); -} - int make_socket(int port, int family) { @@ -542,197 +299,6 @@ make_socket(int port, int family) return sock; } -void -do_accept(int sock, struct tls *ctx, struct pollfd *fds, struct client *clients) -{ - int i, fd; - struct sockaddr_storage addr; - socklen_t len; - - len = sizeof(addr); - if ((fd = accept(sock, (struct sockaddr*)&addr, &len)) == -1) { - if (errno == EWOULDBLOCK) - return; - fatal("accept: %s", strerror(errno)); - } - - mark_nonblock(fd); - - for (i = 0; i < MAX_USERS; ++i) { - if (fds[i].fd == -1) { - bzero(&clients[i], sizeof(struct client)); - if (tls_accept_socket(ctx, &clients[i].ctx, fd) == -1) - break; /* goodbye fd! */ - - fds[i].fd = fd; - fds[i].events = POLLIN; - - clients[i].state = S_HANDSHAKE; - clients[i].fd = -1; - clients[i].child = 0; - clients[i].waiting_on_child = 0; - clients[i].buf = MAP_FAILED; - clients[i].af = AF_INET; - clients[i].addr = addr; - - connected_clients++; - return; - } - } - - close(fd); -} - -void -goodbye(struct pollfd *pfd, struct client *c) -{ - c->state = S_CLOSING; - - switch (tls_close(c->ctx)) { - case TLS_WANT_POLLIN: - pfd->events = POLLIN; - return; - case TLS_WANT_POLLOUT: - pfd->events = POLLOUT; - return; - } - - connected_clients--; - - tls_free(c->ctx); - c->ctx = NULL; - - if (c->buf != MAP_FAILED) - munmap(c->buf, c->len); - - if (c->fd != -1) - close(c->fd); - - close(pfd->fd); - pfd->fd = -1; -} - -void -loop(struct tls *ctx, int sock4, int sock6) -{ - int i; - struct client clients[MAX_USERS]; - struct pollfd fds[MAX_USERS]; - - for (i = 0; i < MAX_USERS; ++i) { - fds[i].fd = -1; - fds[i].events = POLLIN; - bzero(&clients[i], sizeof(struct client)); - } - - fds[0].fd = sock4; - fds[1].fd = sock6; - - for (;;) { - if (poll(fds, MAX_USERS, INFTIM) == -1) { - if (errno == EINTR) { - warnx("connected clients: %d", - connected_clients); - continue; - } - fatal("poll: %s", strerror(errno)); - } - - for (i = 0; i < MAX_USERS; i++) { - if (fds[i].revents == 0) - continue; - - if (fds[i].revents & (POLLERR|POLLNVAL)) - fatal("bad fd %d: %s", fds[i].fd, - strerror(errno)); - - if (fds[i].revents & POLLHUP) { - /* fds[i] may be the fd of the stdin - * of a cgi script that has exited. */ - if (!clients[i].waiting_on_child) { - goodbye(&fds[i], &clients[i]); - continue; - } - } - - if (fds[i].fd == sock4) - do_accept(sock4, ctx, fds, clients); - else if (fds[i].fd == sock6) - do_accept(sock6, ctx, fds, clients); - else - handle(&fds[i], &clients[i]); - } - } -} - -char * -absolutify_path(const char *path) -{ - char *wd, *r; - - if (*path == '/') - return strdup(path); - - wd = getcwd(NULL, 0); - if (asprintf(&r, "%s/%s", wd, path) == -1) - err(1, "asprintf"); - free(wd); - return r; -} - -void -yyerror(const char *msg) -{ - goterror = 1; - fprintf(stderr, "%d: %s\n", yylineno, msg); -} - -int -parse_portno(const char *p) -{ - char *ep; - long lval; - - errno = 0; - lval = strtol(p, &ep, 10); - if (p[0] == '\0' || *ep != '\0') - errx(1, "not a number: %s", p); - if (lval < 0 || lval > UINT16_MAX) - errx(1, "port number out of range for domain %s: %ld", p, lval); - return lval; -} - -void -parse_conf(const char *path) -{ - if ((yyin = fopen(path, "r")) == NULL) - err(1, "cannot open config %s", path); - yyparse(); - fclose(yyin); - - if (goterror) - exit(1); -} - -void -load_vhosts(struct tls_config *tlsconf) -{ - struct vhost *h; - - /* we need to set something, then we can add how many key we want */ - if (tls_config_set_keypair_file(tlsconf, hosts->cert, hosts->key)) - errx(1, "tls_config_set_keypair_file failed"); - - for (h = hosts; h->domain != NULL; ++h) { - if (tls_config_add_keypair_file(tlsconf, h->cert, h->key) == -1) - errx(1, "failed to load the keypair (%s, %s)", - h->cert, h->key); - - if ((h->dirfd = open(h->dir, O_RDONLY | O_DIRECTORY)) == -1) - err(1, "open %s for domain %s", h->dir, h->domain); - } -} - int listener_main() { @@ -798,8 +364,6 @@ main(int argc, char **argv) conf.ipv6 = 0; conf.protos = TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3; - connected_clients = 0; - while ((ch = getopt(argc, argv, "6C:c:d:fhK:np:x:")) != -1) { switch (ch) { case '6': |