diff --git a/config.c b/config.c index 3f563ed..8209760 100644 --- a/config.c +++ b/config.c @@ -37,9 +37,8 @@ config_new(void) TAILQ_INIT(&conf->fcgi); TAILQ_INIT(&conf->hosts); TAILQ_INIT(&conf->pkis); + TAILQ_INIT(&conf->addrs); - conf->port = 1965; - conf->ipv6 = 0; conf->protos = TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3; init_mime(&conf->mime); @@ -50,9 +49,6 @@ config_new(void) conf->use_privsep_crypto = 1; #endif - conf->sock4 = -1; - conf->sock6 = -1; - return conf; } @@ -67,21 +63,12 @@ config_purge(struct conf *conf) struct envlist *e, *te; struct alist *a, *ta; struct pki *pki, *tpki; + struct address *addr, *taddr; int use_privsep_crypto; ps = conf->ps; use_privsep_crypto = conf->use_privsep_crypto; - if (conf->sock4 != -1) { - event_del(&conf->evsock4); - close(conf->sock4); - } - - if (conf->sock6 != -1) { - event_del(&conf->evsock6); - close(conf->sock6); - } - free_mime(&conf->mime); TAILQ_FOREACH_SAFE(f, &conf->fcgi, fcgi, tf) { TAILQ_REMOVE(&conf->fcgi, f, fcgi); @@ -96,6 +83,11 @@ config_purge(struct conf *conf) free(h->key); free(h->ocsp); + TAILQ_FOREACH_SAFE(addr, &h->addrs, addrs, taddr) { + TAILQ_REMOVE(&h->addrs, addr, addrs); + free(addr); + } + TAILQ_FOREACH_SAFE(l, &h->locations, locations, tl) { TAILQ_REMOVE(&h->locations, l, locations); @@ -139,11 +131,19 @@ config_purge(struct conf *conf) free(pki); } + TAILQ_FOREACH_SAFE(addr, &conf->addrs, addrs, taddr) { + TAILQ_REMOVE(&conf->addrs, addr, addrs); + if (addr->sock != -1) { + close(addr->sock); + event_del(&addr->evsock); + } + free(addr); + } + memset(conf, 0, sizeof(*conf)); conf->ps = ps; conf->use_privsep_crypto = use_privsep_crypto; - conf->sock4 = conf->sock6 = -1; conf->protos = TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3; init_mime(&conf->mime); TAILQ_INIT(&conf->fcgi); @@ -222,83 +222,48 @@ config_send_kp(struct privsep *ps, int cert_type, int key_type, return 0; } -static int -make_socket(int port, int family) -{ - int sock, v; - struct sockaddr_in addr4; - struct sockaddr_in6 addr6; - struct sockaddr *addr; - socklen_t len; - - switch (family) { - case AF_INET: - memset(&addr4, 0, sizeof(addr4)); - addr4.sin_family = family; - addr4.sin_port = htons(port); - addr4.sin_addr.s_addr = INADDR_ANY; - addr = (struct sockaddr*)&addr4; - len = sizeof(addr4); - break; - - case AF_INET6: - memset(&addr6, 0, sizeof(addr6)); - addr6.sin6_family = AF_INET6; - addr6.sin6_port = htons(port); - addr6.sin6_addr = in6addr_any; - addr = (struct sockaddr*)&addr6; - len = sizeof(addr6); - break; - - default: - /* unreachable */ - abort(); - } - - if ((sock = socket(family, SOCK_STREAM, 0)) == -1) - fatal("socket"); - - v = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) == -1) - fatal("setsockopt(SO_REUSEADDR)"); - - v = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v)) == -1) - fatal("setsockopt(SO_REUSEPORT)"); - - mark_nonblock(sock); - - if (bind(sock, addr, len) == -1) - fatal("bind"); - - if (listen(sock, 16) == -1) - fatal("listen"); - - return sock; -} - static int config_send_socks(struct conf *conf) { struct privsep *ps = conf->ps; - int sock; + struct address *addr, a; + int sock, v; - if ((sock = make_socket(conf->port, AF_INET)) == -1) - return -1; + TAILQ_FOREACH(addr, &conf->addrs, addrs) { + sock = socket(addr->ai_family, addr->ai_socktype, + addr->ai_protocol); + if (sock == -1) + fatal("socket"); - if (config_send_file(ps, PROC_SERVER, IMSG_RECONF_SOCK4, sock, - NULL, 0) == -1) - return -1; + v = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) + == -1) + fatal("setsockopt(SO_REUSEADDR)"); - if (!conf->ipv6) - return 0; + v = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v)) + == -1) + fatal("setsockopt(SO_REUSEPORT)"); - if ((sock = make_socket(conf->port, AF_INET6)) == -1) - return -1; + mark_nonblock(sock); - if (config_send_file(ps, PROC_SERVER, IMSG_RECONF_SOCK6, sock, - NULL, 0) == -1) - return -1; + if (bind(sock, (struct sockaddr *)&addr->ss, addr->slen) + == -1) + fatal("bind"); + + if (listen(sock, 16) == -1) + fatal("listen"); + + memcpy(&a, addr, sizeof(a)); + a.conf = NULL; + a.sock = -1; + memset(&a.evsock, 0, sizeof(a.evsock)); + memset(&a.addrs, 0, sizeof(a.addrs)); + + if (config_send_file(ps, PROC_SERVER, IMSG_RECONF_SOCK, sock, + &a, sizeof(a)) == -1) + return -1; + } return 0; } @@ -327,10 +292,6 @@ config_send(struct conf *conf) &conf->protos, sizeof(conf->protos)) == -1) return -1; - if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_PORT, - &conf->port, sizeof(conf->port)) == -1) - return -1; - if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1) return -1; @@ -549,6 +510,7 @@ config_recv(struct conf *conf, struct imsg *imsg) struct envlist *env; struct alist *alias; struct proxy *proxy; + struct address *addr; uint8_t *d; size_t len, datalen; @@ -577,29 +539,17 @@ config_recv(struct conf *conf, struct imsg *imsg) memcpy(&conf->protos, imsg->data, datalen); break; - case IMSG_RECONF_PORT: - IMSG_SIZE_CHECK(imsg, &conf->port); - memcpy(&conf->port, imsg->data, datalen); - break; - - case IMSG_RECONF_SOCK4: - if (conf->sock4 != -1) - fatalx("socket ipv4 already recv'd"); + case IMSG_RECONF_SOCK: + addr = xcalloc(1, sizeof(*addr)); + IMSG_SIZE_CHECK(imsg, addr); + memcpy(addr, imsg->data, sizeof(*addr)); if (imsg->fd == -1) fatalx("missing socket for IMSG_RECONF_SOCK4"); - conf->sock4 = imsg->fd; - event_set(&conf->evsock4, conf->sock4, EV_READ|EV_PERSIST, - do_accept, conf); - break; - - case IMSG_RECONF_SOCK6: - if (conf->sock6 != -1) - fatalx("socket ipv6 already recv'd"); - if (imsg->fd == -1) - fatalx("missing socket for IMSG_RECONF_SOCK6"); - conf->sock6 = imsg->fd; - event_set(&conf->evsock6, conf->sock6, EV_READ|EV_PERSIST, - do_accept, conf); + addr->conf = conf; + addr->sock = imsg->fd; + event_set(&addr->evsock, addr->sock, EV_READ|EV_PERSIST, + do_accept, addr); + TAILQ_INSERT_HEAD(&conf->addrs, addr, addrs); break; case IMSG_RECONF_FCGI: diff --git a/fcgi.c b/fcgi.c index bf3cae0..1d55b4a 100644 --- a/fcgi.c +++ b/fcgi.c @@ -350,7 +350,7 @@ fcgi_req(struct client *c) struct tm tminfo; struct envlist *p; - e = getnameinfo((struct sockaddr*)&c->addr, sizeof(c->addr), + e = getnameinfo((struct sockaddr*)&c->raddr, c->raddrlen, addr, sizeof(addr), NULL, 0, NI_NUMERICHOST); diff --git a/ge.c b/ge.c index f45d9b2..4978c2c 100644 --- a/ge.c +++ b/ge.c @@ -159,9 +159,14 @@ static int serve(struct conf *conf, const char *host, int port, const char *dir) { struct addrinfo hints, *res, *res0; + struct vhost *vh = TAILQ_FIRST(&conf->hosts); + struct address *addr, *acp; int r, error, saved_errno, sock = -1; const char *cause = NULL; char service[32]; + int any = 0; + + event_init(); r = snprintf(service, sizeof(service), "%d", port); if (r < 0 || (size_t)r >= sizeof(service)) @@ -193,24 +198,34 @@ serve(struct conf *conf, const char *host, int port, const char *dir) if (listen(sock, 5) == -1) fatal("listen"); - /* - * for the time being, we're happy as soon as - * something binds. - */ - break; + any = 1; + + addr = xcalloc(1, sizeof(*addr)); + addr->ai_flags = res->ai_flags; + addr->ai_family = res->ai_family; + addr->ai_socktype = res->ai_socktype; + addr->ai_protocol = res->ai_protocol; + addr->slen = res->ai_addrlen; + memcpy(&addr->ss, res->ai_addr, res->ai_addrlen); + + addr->conf = conf; + addr->sock = sock; + event_set(&addr->evsock, addr->sock, EV_READ|EV_PERSIST, + do_accept, addr); + + TAILQ_INSERT_HEAD(&conf->addrs, addr, addrs); + + acp = xcalloc(1, sizeof(*acp)); + memcpy(acp, addr, sizeof(*acp)); + acp->sock = -1; + memset(&acp->evsock, 0, sizeof(acp->evsock)); + TAILQ_INSERT_HEAD(&vh->addrs, addr, addrs); } - if (sock == -1) + if (!any) fatal("%s", cause); freeaddrinfo(res0); - event_init(); - - /* cheating */ - conf->sock4 = sock; - event_set(&conf->evsock4, conf->sock4, EV_READ|EV_PERSIST, - do_accept, conf); - server_init(NULL, NULL, NULL); if (server_configure_done(conf) == -1) fatalx("server configuration failed"); @@ -239,7 +254,7 @@ main(int argc, char **argv) struct location *loc; const char *errstr, *certs_dir = NULL, *hostname = "localhost"; char path[PATH_MAX]; - int ch; + int ch, port = 1965; setlocale(LC_CTYPE, ""); @@ -262,7 +277,7 @@ main(int argc, char **argv) usage(); break; case 'p': - conf->port = strtonum(optarg, 0, UINT16_MAX, &errstr); + port = strtonum(optarg, 0, UINT16_MAX, &errstr); if (errstr) fatalx("port number is %s: %s", errstr, optarg); @@ -316,5 +331,5 @@ main(int argc, char **argv) /* start the server */ signal(SIGPIPE, SIG_IGN); setproctitle("%s", loc->dir); - return serve(conf, hostname, conf->port, loc->dir); + return serve(conf, hostname, port, loc->dir); } diff --git a/gmid.c b/gmid.c index d3e4cb2..b85fd4a 100644 --- a/gmid.c +++ b/gmid.c @@ -484,9 +484,7 @@ main_print_conf(struct conf *conf) if (*conf->chroot != '\0') printf("chroot \"%s\"\n", conf->chroot); - printf("ipv6 %s\n", conf->ipv6 ? "on" : "off"); /* XXX: defined mimes? */ - printf("port %d\n", conf->port); printf("prefork %d\n", conf->prefork); /* XXX: protocols? */ if (*conf->user != '\0') diff --git a/gmid.h b/gmid.h index 644788b..8947e68 100644 --- a/gmid.h +++ b/gmid.h @@ -104,6 +104,25 @@ struct parser { const char *err; }; +struct conf; +TAILQ_HEAD(addrhead, address); +struct address { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + struct sockaddr_storage ss; + socklen_t slen; + int16_t port; + + /* used in the server */ + struct conf *conf; + int sock; + struct event evsock; /* set if sock != -1 */ + + TAILQ_ENTRY(address) addrs; +}; + TAILQ_HEAD(fcgihead, fcgi); struct fcgi { int id; @@ -188,6 +207,8 @@ struct vhost { TAILQ_ENTRY(vhost) vhosts; + struct addrhead addrs; + /* * the first location rule is always '*' and holds the default * settings for the vhost, then follows the "real" location @@ -220,8 +241,6 @@ struct pki { struct conf { struct privsep *ps; - int port; - int ipv6; uint32_t protos; struct mime mime; char chroot[PATH_MAX]; @@ -230,14 +249,10 @@ struct conf { int reload; int use_privsep_crypto; - int sock4; - struct event evsock4; - int sock6; - struct event evsock6; - struct fcgihead fcgi; struct vhosthead hosts; struct pkihead pkis; + struct addrhead addrs; }; extern const char *config_path; @@ -258,6 +273,7 @@ enum { struct client { struct conf *conf; + struct address *addr; uint32_t id; struct tls *ctx; char *req; @@ -324,9 +340,7 @@ enum imsg_type { IMSG_RECONF_START, /* 7 */ IMSG_RECONF_MIME, IMSG_RECONF_PROTOS, - IMSG_RECONF_PORT, - IMSG_RECONF_SOCK4, - IMSG_RECONF_SOCK6, + IMSG_RECONF_SOCK, IMSG_RECONF_FCGI, IMSG_RECONF_HOST, IMSG_RECONF_CERT, diff --git a/parse.y b/parse.y index 8ba06d3..2f85a66 100644 --- a/parse.y +++ b/parse.y @@ -37,6 +37,9 @@ struct conf *conf; +static const char *default_host = "*"; +static uint16_t default_port = 1965; + TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); static struct file { TAILQ_ENTRY(file) entry; @@ -96,6 +99,7 @@ void parsehp(char *, char **, const char **, const char *); int fastcgi_conf(const char *, const char *); void add_param(char *, char *); int getservice(const char *); +void listen_on(const char *, const char *); static struct vhost *host; static struct location *loc; @@ -125,7 +129,7 @@ typedef struct { %token FASTCGI FOR_HOST %token INCLUDE INDEX IPV6 %token KEY -%token LANG LOCATION LOG +%token LANG LISTEN LOCATION LOG %token OCSP OFF ON %token PARAM PORT PREFORK PROTO PROTOCOLS PROXY %token RELAY_TO REQUIRE RETURN ROOT @@ -220,8 +224,19 @@ option : CHROOT string { yyerror("chroot path too long"); free($2); } - | IPV6 bool { conf->ipv6 = $2; } - | PORT NUM { conf->port = check_port_num($2); } + | IPV6 bool { + yywarn("option `ipv6' is deprecated," + " please use `listen on'"); + if ($2) + default_host = "*"; + else + default_host = "0.0.0.0"; + } + | PORT NUM { + yywarn("option `port' is deprecated," + " please use `listen on'"); + default_port = $2; + } | PREFORK NUM { conf->prefork = check_prefork_num($2); } | PROTOCOLS string { if (tls_config_parse_protocols(&conf->protos, $2) == -1) @@ -259,6 +274,20 @@ vhost : SERVER string { host->key_path == NULL) yyerror("invalid vhost definition: %s", host->domain); + if (TAILQ_EMPTY(&host->addrs)) { + char portno[32]; + int r; + + r = snprintf(portno, sizeof(portno), "%d", + default_port); + if (r < 0 || (size_t)r >= sizeof(portno)) + fatal("snprintf"); + + yywarn("missing `listen on' in server %s," + " assuming %s port %d", $2, default_host, + default_port); + listen_on(default_host, portno); + } } | error '}' { yyerror("bad server directive"); } ; @@ -295,6 +324,22 @@ servopt : ALIAS string { | PARAM string '=' string { add_param($2, $4); } + | LISTEN ON STRING PORT STRING { + listen_on($3, $5); + free($3); + free($5); + } + | LISTEN ON STRING PORT NUM { + char portno[32]; + int r; + + r = snprintf(portno, sizeof(portno), "%d", $5); + if (r < 0 || (size_t)r >= sizeof(portno)) + fatal("snprintf"); + + listen_on($3, portno); + free($3); + } | locopt ; @@ -515,6 +560,7 @@ static const struct keyword { {"ipv6", IPV6}, {"key", KEY}, {"lang", LANG}, + {"listen", LISTEN}, {"location", LOCATION}, {"log", LOG}, {"ocsp", OCSP}, @@ -913,6 +959,9 @@ parse_conf(struct conf *c, const char *filename) { struct sym *sym, *next; + default_host = "*"; + default_port = 1965; + conf = c; file = pushfile(filename, 0); @@ -1153,3 +1202,78 @@ getservice(const char *n) return ((unsigned short)llval); } + +static void +add_to_addr_queue(struct addrhead *a, struct addrinfo *ai) +{ + struct address *addr; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + if (ai->ai_addrlen > sizeof(addr->ss)) + fatalx("ai_addrlen larger than a sockaddr_storage"); + + TAILQ_FOREACH(addr, a, addrs) { + if (addr->ai_flags == ai->ai_flags && + addr->ai_family == ai->ai_family && + addr->ai_socktype == ai->ai_socktype && + addr->ai_protocol == ai->ai_protocol && + addr->slen == ai->ai_addrlen && + !memcmp(&addr->ss, ai->ai_addr, addr->slen)) + return; + } + + addr = xcalloc(1, sizeof(*addr)); + addr->ai_flags = ai->ai_flags; + addr->ai_family = ai->ai_family; + addr->ai_socktype = ai->ai_socktype; + addr->ai_protocol = ai->ai_protocol; + addr->slen = ai->ai_addrlen; + memcpy(&addr->ss, ai->ai_addr, ai->ai_addrlen); + + /* for commodity */ + switch (addr->ai_family) { + case AF_INET: + sin = (struct sockaddr_in *)&addr->ss; + addr->port = ntohs(sin->sin_port); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)&addr->ss; + addr->port = ntohs(sin6->sin6_port); + break; + default: + fatalx("unknown socket family %d", addr->ai_family); + } + + addr->sock = -1; + + TAILQ_INSERT_HEAD(a, addr, addrs); +} + +void +listen_on(const char *hostname, const char *servname) +{ + struct addrinfo hints, *res, *res0; + int error; + + if (!strcmp(hostname, "*")) + hostname = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(hostname, servname, &hints, &res0); + if (error) { + yyerror("listen on \"%s\" port %s: %s", hostname, servname, + gai_strerror(errno)); + return; + } + + for (res = res0; res; res = res->ai_next) { + add_to_addr_queue(&host->addrs, res); + add_to_addr_queue(&conf->addrs, res); + } + + freeaddrinfo(res0); +} diff --git a/regress/lib.sh b/regress/lib.sh index c61285b..06cb7a7 100644 --- a/regress/lib.sh +++ b/regress/lib.sh @@ -10,9 +10,7 @@ current_test= run_test() { ggflags= port=10965 - config_common="ipv6 off -port $port -" + config_common="" hdr= body= dont_check_server_alive=no @@ -64,6 +62,7 @@ server "localhost" { cert "$PWD/cert.pem" key "$PWD/key.pem" root "$PWD/testdata" + listen on localhost port $port $2 } EOF @@ -78,6 +77,7 @@ set_proxy() { server "localhost.local" { cert "$PWD/cert.pem" key "$PWD/key.pem" + listen on localhost port $port proxy { relay-to localhost port $port $1 diff --git a/regress/tests.sh b/regress/tests.sh index 5ebc33b..a38e4ba 100644 --- a/regress/tests.sh +++ b/regress/tests.sh @@ -239,13 +239,13 @@ test_fastcgi() { test_macro_expansion() { cat < reg.conf pwd = "$PWD" -$config_common server "localhost" { # the quoting of \$ is for sh cert \$pwd "/cert.pem" key \$pwd "/key.pem" root \$pwd "/testdata" + listen on localhost port $port } EOF diff --git a/server.c b/server.c index 05c1d4b..18e8c91 100644 --- a/server.c +++ b/server.c @@ -489,7 +489,6 @@ strip_path(const char *path, int strip) static void fmt_sbuf(const char *fmt, struct client *c, const char *path) { - struct conf *conf = c->conf; size_t i; char buf[32]; @@ -519,7 +518,7 @@ fmt_sbuf(const char *fmt, struct client *c, const char *path) strlcat(c->sbuf, c->iri.query, sizeof(c->sbuf)); break; case 'P': - snprintf(buf, sizeof(buf), "%d", conf->port); + snprintf(buf, sizeof(buf), "%d", c->addr->port); strlcat(c->sbuf, buf, sizeof(c->sbuf)); memset(buf, 0, sizeof(buf)); break; @@ -1313,7 +1312,7 @@ client_close(struct client *c) void do_accept(int sock, short et, void *d) { - struct conf *conf = d; + struct address *addr = d; struct client *c; struct sockaddr_storage raddr; struct sockaddr *sraddr; @@ -1332,7 +1331,8 @@ do_accept(int sock, short et, void *d) mark_nonblock(fd); c = xcalloc(1, sizeof(*c)); - c->conf = conf; + c->conf = addr->conf; + c->addr = addr; c->id = ++server_client_id; c->fd = fd; c->pfd = -1; @@ -1486,15 +1486,18 @@ server_init(struct privsep *ps, struct privsep_proc *p, void *arg) int server_configure_done(struct conf *conf) { + struct address *addr; + if (load_default_mime(&conf->mime) == -1) fatal("can't load default mime"); sort_mime(&conf->mime); setup_tls(conf); load_vhosts(conf); - if (conf->sock4 != -1) - event_add(&conf->evsock4, NULL); - if (conf->sock6 != -1) - event_add(&conf->evsock6, NULL); + + TAILQ_FOREACH(addr, &conf->addrs, addrs) { + if (addr->sock != -1) + event_add(&addr->evsock, NULL); + } return 0; } @@ -1509,9 +1512,7 @@ server_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) case IMSG_RECONF_START: case IMSG_RECONF_MIME: case IMSG_RECONF_PROTOS: - case IMSG_RECONF_PORT: - case IMSG_RECONF_SOCK4: - case IMSG_RECONF_SOCK6: + case IMSG_RECONF_SOCK: case IMSG_RECONF_FCGI: case IMSG_RECONF_HOST: case IMSG_RECONF_CERT: diff --git a/utils.c b/utils.c index 43c1f4d..bf28329 100644 --- a/utils.c +++ b/utils.c @@ -346,6 +346,7 @@ new_vhost(void) struct vhost *h; h = xcalloc(1, sizeof(*h)); + TAILQ_INIT(&h->addrs); TAILQ_INIT(&h->locations); TAILQ_INIT(&h->params); TAILQ_INIT(&h->aliases);