From b8e64ccd44290cdd34bdcd3fd85fb1a9cb7486dd Mon Sep 17 00:00:00 2001 From: Omar Polo Date: Wed, 31 Mar 2021 16:32:18 +0000 Subject: [PATCH] list instead of fixed-size array for vhosts and locations saves some bytes of memory and removes the limit on the maximum number of vhosts and location blocks. --- ChangeLog | 2 ++ ex.c | 25 +++++++++++--- gmid.c | 98 ++++++++++++++++++++++++++++++++----------------------- gmid.h | 20 +++++++----- parse.y | 48 ++++++++++++++++----------- sandbox.c | 10 +++--- server.c | 79 ++++++++++++++++++++++++++++++-------------- utils.c | 10 ++++++ 8 files changed, 190 insertions(+), 102 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5e61908..9ad4b87 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2021-03-31 Omar Polo + * gmid.h (struct vhost): remove limits on the number of vhosts and location blocks + * gmid.c (mkdirs): fix recursive mkdirs for configless mode 2021-03-29 Omar Polo diff --git a/ex.c b/ex.c index d553fe7..e08da7b 100644 --- a/ex.c +++ b/ex.c @@ -210,12 +210,27 @@ childerr: _exit(1); } +static struct vhost * +host_nth(size_t n) +{ + struct vhost *h; + + TAILQ_FOREACH(h, &hosts, vhosts) { + if (n == 0) + return h; + n--; + } + + return NULL; +} + static void handle_imsg_cgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) { - struct cgireq req; - struct iri iri; - int fd; + struct vhost *h; + struct cgireq req; + struct iri iri; + int fd; if (datalen != sizeof(req)) abort(); @@ -234,10 +249,10 @@ handle_imsg_cgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) if (*iri.query == '\0') iri.query = NULL; - if (req.host_off > HOSTSLEN || hosts[req.host_off].domain == NULL) + if ((h = host_nth(req.host_off)) == NULL) abort(); - fd = launch_cgi(&iri, &req, &hosts[req.host_off]); + fd = launch_cgi(&iri, &req, h); imsg_compose(ibuf, IMSG_CGI_RES, imsg->hdr.peerid, 0, fd, NULL, 0); imsg_flush(ibuf); } diff --git a/gmid.c b/gmid.c index 1204596..e125d90 100644 --- a/gmid.c +++ b/gmid.c @@ -26,7 +26,7 @@ #include #include -struct vhost hosts[HOSTSLEN]; +struct vhosthead hosts; int sock4, sock6; @@ -95,6 +95,7 @@ void load_local_cert(const char *hostname, const char *dir) { char *cert, *key; + struct vhost *h; if (asprintf(&cert, "%s/%s.cert.pem", dir, hostname) == -1) errx(1, "asprintf"); @@ -104,9 +105,10 @@ load_local_cert(const char *hostname, const char *dir) if (access(cert, R_OK) == -1 || access(key, R_OK) == -1) gen_certificate(hostname, cert, key); - hosts[0].cert = cert; - hosts[0].key = key; - hosts[0].domain = hostname; + h = TAILQ_FIRST(&hosts); + h->cert = cert; + h->key = key; + h->domain = hostname; } void @@ -114,7 +116,7 @@ load_vhosts(void) { struct vhost *h; - for (h = hosts; h->domain != NULL; ++h) { + TAILQ_FOREACH(h, &hosts, vhosts) { if ((h->dirfd = open(h->dir, O_RDONLY | O_DIRECTORY)) == -1) fatal("open %s for domain %s", h->dir, h->domain); } @@ -193,12 +195,14 @@ setup_tls(void) if ((ctx = tls_server()) == NULL) fatal("tls_server failure"); - /* 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)) - fatal("tls_config_set_keypair_file failed for (%s, %s)", - hosts->cert, hosts->key); + h = TAILQ_FIRST(&hosts); - for (h = &hosts[1]; h->domain != NULL; ++h) { + /* we need to set something, then we can add how many key we want */ + if (tls_config_set_keypair_file(tlsconf, h->cert, h->key)) + fatal("tls_config_set_keypair_file failed for (%s, %s)", + h->cert, h->key); + + while ((h = TAILQ_NEXT(h, vhosts)) != NULL) { if (tls_config_add_keypair_file(tlsconf, h->cert, h->key) == -1) fatal("failed to load the keypair (%s, %s)", h->cert, h->key); @@ -221,11 +225,7 @@ listener_main(struct imsgbuf *ibuf) void init_config(void) { - size_t i; - - bzero(hosts, sizeof(hosts)); - for (i = 0; i < HOSTSLEN; ++i) - hosts[i].dirfd = -1; + TAILQ_INIT(&hosts); conf.port = 1965; conf.ipv6 = 0; @@ -236,37 +236,34 @@ init_config(void) conf.chroot = NULL; conf.user = NULL; - /* we'll change this to 0 when running without config. */ conf.prefork = 3; } void free_config(void) { - struct vhost *h; - struct location *l; + struct vhost *h, *th; + struct location *l, *tl; free(conf.chroot); free(conf.user); memset(&conf, 0, sizeof(conf)); - for (h = hosts; h->domain != NULL; ++h) { - free((char*)h->domain); - free((char*)h->cert); - free((char*)h->key); - free((char*)h->dir); - free((char*)h->cgi); - free((char*)h->entrypoint); + TAILQ_FOREACH_SAFE(h, &hosts, vhosts, th) { + TAILQ_FOREACH_SAFE(l, &h->locations, locations, tl) { + TAILQ_REMOVE(&h->locations, l, locations); - for (l = h->locations; l->match != NULL; ++l) { free((char*)l->match); free((char*)l->lang); free((char*)l->default_mime); free((char*)l->index); free((char*)l->block_fmt); + free(l); } + + TAILQ_REMOVE(&hosts, h, vhosts); + free(h); } - memset(hosts, 0, sizeof(hosts)); tls_free(ctx); tls_config_free(tlsconf); @@ -352,8 +349,10 @@ logger_init(void) static int serve(int argc, char **argv, struct imsgbuf *ibuf) { - char path[PATH_MAX]; - int i, p[2]; + char path[PATH_MAX]; + int i, p[2]; + struct vhost *h; + struct location *l; if (config_path == NULL) { if (hostname == NULL) @@ -362,23 +361,26 @@ serve(int argc, char **argv, struct imsgbuf *ibuf) certs_dir = data_dir(); load_local_cert(hostname, certs_dir); - hosts[0].domain = "*"; - hosts[0].locations[0].auto_index = 1; - hosts[0].locations[0].match = "*"; + h = TAILQ_FIRST(&hosts); + h->domain = "*"; + + l = TAILQ_FIRST(&h->locations); + l->auto_index = 1; + l->match = "*"; switch (argc) { case 0: - hosts[0].dir = getcwd(path, sizeof(path)); + h->dir = getcwd(path, sizeof(path)); break; case 1: - hosts[0].dir = absolutify_path(argv[0]); + h->dir = absolutify_path(argv[0]); break; default: usage(getprogname()); return 1; } - log_notice(NULL, "serving %s on port %d", hosts[0].dir, conf.port); + log_notice(NULL, "serving %s on port %d", h->dir, conf.port); } /* setup tls before dropping privileges: we don't want user @@ -409,12 +411,31 @@ serve(int argc, char **argv, struct imsgbuf *ibuf) _exit(executor_main(ibuf)); } +static void +setup_configless(int argc, char **argv, const char *cgi) +{ + struct vhost *host; + struct location *loc; + + host = xcalloc(1, sizeof(*host)); + host->cgi = cgi; + TAILQ_INSERT_HEAD(&hosts, host, vhosts); + + loc = xcalloc(1, sizeof(*loc)); + TAILQ_INSERT_HEAD(&host->locations, loc, locations); + + serve(argc, argv, NULL); + imsg_compose(&logibuf, IMSG_QUIT, 0, 0, -1, NULL, 0); + imsg_flush(&logibuf); +} + int main(int argc, char **argv) { struct imsgbuf exibuf; int ch, conftest = 0, configless = 0; int old_ipv6, old_port; + const char *cgi = NULL; init_config(); @@ -464,7 +485,7 @@ main(int argc, char **argv) /* drop the starting / (if any) */ if (*optarg == '/') optarg++; - hosts[0].cgi = optarg; + cgi = optarg; configless = 1; break; @@ -511,13 +532,10 @@ main(int argc, char **argv) sock6 = make_socket(conf.port, AF_INET6); if (configless) { - serve(argc, argv, NULL); - imsg_compose(&logibuf, IMSG_QUIT, 0, 0, -1, NULL, 0); - imsg_flush(&logibuf); + setup_configless(argc, argv, cgi); return 0; } - /* Linux seems to call the event handlers even when we're * doing a sigwait. These dummy handlers is here to avoid * being terminated on SIGHUP, SIGTERM or SIGINFO. */ diff --git a/gmid.h b/gmid.h index 17a1315..0409136 100644 --- a/gmid.h +++ b/gmid.h @@ -51,15 +51,13 @@ #define MAX_USERS 64 -#define HOSTSLEN 64 -#define LOCLEN 32 - /* maximum hostname and label length, +1 for the NUL-terminator */ #define DOMAIN_NAME_LEN (253+1) #define LABEL_LEN (63+1) #define PROC_MAX 16 +TAILQ_HEAD(lochead, location); struct location { const char *match; const char *lang; @@ -71,8 +69,11 @@ struct location { int strip; X509_STORE *reqca; int disable_log; + + TAILQ_ENTRY(location) locations; }; +extern TAILQ_HEAD(vhosthead, vhost) hosts; struct vhost { const char *domain; const char *cert; @@ -82,13 +83,13 @@ struct vhost { const char *entrypoint; int dirfd; - /* the first location rule is always '*' and holds the default - * settings for the vhost, from locations[1] onwards there are - * the "real" location rules specified in the configuration. */ - struct location locations[LOCLEN]; -}; + TAILQ_ENTRY(vhost) vhosts; -extern struct vhost hosts[HOSTSLEN]; + /* the first location rule is always '*' and holds the default + * settings for the vhost, then follows the "real" location + * rules as specified in the configuration. */ + struct lochead locations; +}; struct etm { /* extension to mime */ const char *mime; @@ -321,6 +322,7 @@ int ends_with(const char*, const char*); ssize_t filesize(int); char *absolutify_path(const char*); char *xstrdup(const char*); +void *xcalloc(size_t, size_t); void gen_certificate(const char*, const char*, const char*); X509_STORE *load_ca(const char*); int validate_against_ca(X509_STORE*, const uint8_t*, size_t); diff --git a/parse.y b/parse.y index 717bcdc..0646f13 100644 --- a/parse.y +++ b/parse.y @@ -30,12 +30,13 @@ */ struct vhost *host; -size_t ihost; struct location *loc; -size_t iloc; int goterror = 0; +static struct vhost *new_vhost(void); +static struct location *new_location(void); + void yyerror(const char*, ...); int parse_portno(const char*); void parse_conf(const char*); @@ -90,8 +91,14 @@ vhosts : /* empty */ | vhosts vhost ; -vhost : TSERVER TSTRING '{' servopts locations '}' { - host->locations[0].match = xstrdup("*"); +vhost : TSERVER TSTRING { + host = new_vhost(); + TAILQ_INSERT_HEAD(&hosts, host, vhosts); + + loc = new_location(); + TAILQ_INSERT_HEAD(&host->locations, loc, locations); + + loc->match = xstrdup("*"); host->domain = $2; if (strstr($2, "xn--") != NULL) { @@ -99,17 +106,11 @@ vhost : TSERVER TSTRING '{' servopts locations '}' { "you should use the decoded hostname.", config_path, yylineno, $2); } + } '{' servopts locations '}' { if (host->cert == NULL || host->key == NULL || host->dir == NULL) yyerror("invalid vhost definition: %s", $2); - - if (++ihost == HOSTSLEN) - errx(1, "too much vhosts defined"); - - host++; - loc = &host->locations[0]; - iloc = 0; } | error '}' { yyerror("error in server directive"); } ; @@ -205,6 +206,18 @@ locopt : TAUTO TINDEX TBOOL { loc->auto_index = $3 ? 1 : -1; } %% +static struct vhost * +new_vhost(void) +{ + return xcalloc(1, sizeof(struct vhost)); +} + +static struct location * +new_location(void) +{ + return xcalloc(1, sizeof(struct location)); +} + void yyerror(const char *msg, ...) { @@ -234,11 +247,6 @@ parse_portno(const char *p) void parse_conf(const char *path) { - host = &hosts[0]; - ihost = 0; - loc = &hosts[0].locations[0]; - iloc = 0; - config_path = path; if ((yyin = fopen(path, "r")) == NULL) fatal("cannot open config: %s: %s", path, strerror(errno)); @@ -247,6 +255,9 @@ parse_conf(const char *path) if (goterror) exit(1); + + if (TAILQ_FIRST(&hosts)->domain == NULL) + fatal("no vhost defined in %s", path); } char * @@ -307,7 +318,6 @@ check_prefork_num(int n) void advance_loc(void) { - if (++iloc == LOCLEN) - errx(1, "too much location rules defined"); - loc++; + loc = new_location(); + TAILQ_INSERT_TAIL(&host->locations, loc, locations); } diff --git a/sandbox.c b/sandbox.c index 1a0dc9a..b689a27 100644 --- a/sandbox.c +++ b/sandbox.c @@ -282,7 +282,7 @@ sandbox_server_process(void) { struct vhost *h; - for (h = hosts; h->domain != NULL; ++h) { + TAILQ_FOREACH(h, &hosts, vhosts) { if (unveil(h->dir, "r") == -1) fatal("unveil %s for domain %s", h->dir, h->domain); } @@ -294,13 +294,13 @@ sandbox_server_process(void) void sandbox_executor_process(void) { - struct vhost *vhost; + struct vhost *h; - for (vhost = hosts; vhost->domain != NULL; ++vhost) { + TAILQ_FOREACH(h, &hosts, vhosts) { /* r so we can chdir into the correct directory */ - if (unveil(vhost->dir, "rx") == -1) + if (unveil(h->dir, "rx") == -1) err(1, "unveil %s for domain %s", - vhost->dir, vhost->domain); + h->dir, h->domain); } /* rpath to chdir into the correct directory */ diff --git a/server.c b/server.c index 0080b17..effc2f7 100644 --- a/server.c +++ b/server.c @@ -50,6 +50,7 @@ static int apply_require_ca(struct client*); static void handle_open_conn(int, short, void*); static void start_reply(struct client*, int, const char*); static void handle_start_reply(int, short, void*); +static size_t host_nth(struct vhost*); static void start_cgi(const char*, const char*, struct client*); static void open_dir(struct client*); static void redirect_canonical_dir(struct client*); @@ -99,14 +100,15 @@ vhost_lang(struct vhost *v, const char *path) if (v == NULL || path == NULL) return NULL; - for (loc = &v->locations[1]; loc->match != NULL; ++loc) { + loc = TAILQ_FIRST(&v->locations); + while ((loc = TAILQ_NEXT(loc, locations)) != NULL) { if (loc->lang != NULL) { if (matches(loc->match, path)) return loc->lang; } } - return v->locations[0].lang; + return TAILQ_FIRST(&v->locations)->lang; } const char * @@ -118,15 +120,17 @@ vhost_default_mime(struct vhost *v, const char *path) if (v == NULL || path == NULL) return default_mime; - for (loc = &v->locations[1]; loc->match != NULL; ++loc) { + loc = TAILQ_FIRST(&v->locations); + while ((loc = TAILQ_NEXT(loc, locations)) != NULL) { if (loc->default_mime != NULL) { if (matches(loc->match, path)) return loc->default_mime; } } - if (v->locations[0].default_mime != NULL) - return v->locations[0].default_mime; + loc = TAILQ_FIRST(&v->locations); + if (loc->default_mime != NULL) + return loc->default_mime; return default_mime; } @@ -139,15 +143,17 @@ vhost_index(struct vhost *v, const char *path) if (v == NULL || path == NULL) return index; - for (loc = &v->locations[1]; loc->match != NULL; ++loc) { + loc = TAILQ_FIRST(&v->locations); + while ((loc = TAILQ_NEXT(loc, locations)) != NULL) { if (loc->index != NULL) { if (matches(loc->match, path)) return loc->index; } } - if (v->locations[0].index != NULL) - return v->locations[0].index; + loc = TAILQ_FIRST(&v->locations); + if (loc->index != NULL) + return loc->index; return index; } @@ -159,14 +165,16 @@ vhost_auto_index(struct vhost *v, const char *path) if (v == NULL || path == NULL) return 0; - for (loc = &v->locations[1]; loc->match != NULL; ++loc) { + loc = TAILQ_FIRST(&v->locations); + while ((loc = TAILQ_NEXT(loc, locations)) != NULL) { if (loc->auto_index != 0) { if (matches(loc->match, path)) return loc->auto_index == 1; } } - return v->locations[0].auto_index == 1; + loc = TAILQ_FIRST(&v->locations); + return loc->auto_index == 1; } int @@ -177,7 +185,8 @@ vhost_block_return(struct vhost *v, const char *path, int *code, const char **fm if (v == NULL || path == NULL) return 0; - for (loc = &v->locations[1]; loc->match != NULL; ++loc) { + loc = TAILQ_FIRST(&v->locations); + while ((loc = TAILQ_NEXT(loc, locations)) != NULL) { if (loc->block_code != 0) { if (matches(loc->match, path)) { *code = loc->block_code; @@ -187,9 +196,10 @@ vhost_block_return(struct vhost *v, const char *path, int *code, const char **fm } } - *code = v->locations[0].block_code; - *fmt = v->locations[0].block_fmt; - return v->locations[0].block_code != 0; + loc = TAILQ_FIRST(&v->locations); + *code = loc->block_code; + *fmt = loc->block_fmt; + return loc->block_code != 0; } int @@ -200,14 +210,16 @@ vhost_strip(struct vhost *v, const char *path) if (v == NULL || path == NULL) return 0; - for (loc = &v->locations[1]; loc->match != NULL; ++loc) { + loc = TAILQ_FIRST(&v->locations); + while ((loc = TAILQ_NEXT(loc, locations)) != NULL) { if (loc->strip != 0) { if (matches(loc->match, path)) return loc->strip; } } - return v->locations[0].strip; + loc = TAILQ_FIRST(&v->locations); + return loc->strip; } X509_STORE * @@ -218,14 +230,16 @@ vhost_require_ca(struct vhost *v, const char *path) if (v == NULL || path == NULL) return NULL; - for (loc = &v->locations[1]; loc->match != NULL; ++loc) { + loc = TAILQ_FIRST(&v->locations); + while ((loc = TAILQ_NEXT(loc, locations)) != NULL) { if (loc->reqca != NULL) { if (matches(loc->match, path)) return loc->reqca; } } - return v->locations[0].reqca; + loc = TAILQ_FIRST(&v->locations); + return loc->reqca; } int @@ -236,12 +250,14 @@ vhost_disable_log(struct vhost *v, const char *path) if (v == NULL || path == NULL) return 0; - for (loc = &v->locations[1]; loc->match != NULL; ++loc) { + loc = TAILQ_FIRST(&v->locations); + while ((loc = TAILQ_NEXT(loc, locations)) != NULL) { if (loc->disable_log && matches(loc->match, path)) return 1; } - return v->locations[0].disable_log; + loc = TAILQ_FIRST(&v->locations); + return loc->disable_log; } static int @@ -399,7 +415,7 @@ handle_handshake(int fd, short ev, void *d) goto err; } - for (h = hosts; h->domain != NULL; ++h) { + TAILQ_FOREACH(h, &hosts, vhosts) { if (matches(h->domain, c->domain)) break; } @@ -407,9 +423,9 @@ handle_handshake(int fd, short ev, void *d) log_debug(c, "handshake: SNI: \"%s\"; decoded: \"%s\"; matched: \"%s\"", servname != NULL ? servname : "(null)", c->domain, - h->domain != NULL ? h->domain : "(null)"); + h != NULL ? h->domain : "(null)"); - if (h->domain != NULL) { + if (h != NULL) { c->host = h; handle_open_conn(fd, ev, c); return; @@ -633,6 +649,21 @@ handle_start_reply(int fd, short ev, void *d) c->next(fd, ev, c); } +static size_t +host_nth(struct vhost *h) +{ + struct vhost *v; + size_t i = 0; + + TAILQ_FOREACH(v, &hosts, vhosts) { + if (v == h) + return i; + i++; + } + + abort(); +} + static void start_cgi(const char *spath, const char *relpath, struct client *c) { @@ -675,7 +706,7 @@ start_cgi(const char *spath, const char *relpath, struct client *c) req.notbefore = tls_peer_cert_notbefore(c->ctx); req.notafter = tls_peer_cert_notafter(c->ctx); - req.host_off = c->host - hosts; + req.host_off = host_nth(c->host); imsg_compose(&exibuf, IMSG_CGI_REQ, c->id, 0, -1, &req, sizeof(req)); imsg_flush(&exibuf); diff --git a/utils.c b/utils.c index 5d831f1..1fda993 100644 --- a/utils.c +++ b/utils.c @@ -96,6 +96,16 @@ xstrdup(const char *s) return d; } +void * +xcalloc(size_t nmemb, size_t size) +{ + void *d; + + if ((d = calloc(nmemb, size)) == NULL) + err(1, "calloc"); + return d; +} + void gen_certificate(const char *hostname, const char *certpath, const char *keypath) {