diff --git a/example/multi_process_server.c b/example/multi_process_server.c index efc76e8..9d8319b 100644 --- a/example/multi_process_server.c +++ b/example/multi_process_server.c @@ -59,9 +59,8 @@ static void usage(const char *prog) fprintf(stderr, "Usage: %s [option]\n" " -h docroot # Document root, default is .\n" " -i index_page # Index page, default is index.html\n" - " -a addr # Default addr is localhost\n" - " -p port # Default port is 8080\n" - " -s # SSl on\n" + " -a addr # address to listen\n" + " -s addr # address to listen with ssl\n" " -P # plugin path\n" " -w # worker process number, default is equal to available CPUs\n" " -v # verbose\n", prog); @@ -75,16 +74,17 @@ int main(int argc, char **argv) struct uh_server *srv = NULL; const char *plugin_path = NULL; bool verbose = false; - bool ssl = false; const char *docroot = "."; const char *index_page = "index.html"; - const char *addr = "localhost"; pid_t workers[MAX_WORKER] = {}; int nworker = get_nprocs(); - int port = 8080; int opt, i; - while ((opt = getopt(argc, argv, "h:i:a:p:sfP:w:v")) != -1) { + srv = uh_server_new(loop); + if (!srv) + return -1; + + while ((opt = getopt(argc, argv, "h:i:a:s:P:w:v")) != -1) { switch (opt) { case 'h': docroot = optarg; @@ -93,13 +93,12 @@ int main(int argc, char **argv) index_page = optarg; break; case 'a': - addr = optarg; + if (srv->listen(srv, optarg, false) < 1) + goto err; break; - case 'p': - port = atoi(optarg); - break; - case 's': - ssl = true; + case 's': + if (srv->listen(srv, optarg, true) < 1) + goto err; break; case 'P': plugin_path = optarg; @@ -124,13 +123,8 @@ int main(int argc, char **argv) signal(SIGPIPE, SIG_IGN); - srv = uh_server_new(loop, addr, port); - if (!srv) - return -1; - #if UHTTPD_SSL_SUPPORT - if (ssl && srv->ssl_init(srv, "server-cert.pem", "server-key.pem") < 0) - goto err; + srv->ssl_init(srv, "cert.pem", "key.pem"); #endif srv->set_docroot(srv, docroot); diff --git a/example/multi_process_server_reuseport.c b/example/multi_process_server_reuseport.c index ad95f7e..ca360f8 100644 --- a/example/multi_process_server_reuseport.c +++ b/example/multi_process_server_reuseport.c @@ -58,28 +58,37 @@ static void usage(const char *prog) fprintf(stderr, "Usage: %s [option]\n" " -h docroot # Document root, default is .\n" " -i index_page # Index page, default is index.html\n" - " -a addr # Default addr is localhost\n" - " -p port # Default port is 8080\n" - " -s # SSl on\n" + " -a addr # address to listen\n" + " -s addr # address to listen with ssl\n" " -P # plugin path\n" " -w # worker process number, default is equal to available CPUs\n" " -v # verbose\n", prog); exit(1); } -static void start_server(const char *addr, int port, const char *docroot, const char *index_page, const char *plugin, bool ssl) +static void start_server(const char *addr, const char *addrs, const char *docroot, const char *index_page, const char *plugin, bool ssl) { struct ev_loop *loop = ev_loop_new(0); struct uh_server *srv = NULL; signal(SIGPIPE, SIG_IGN); - srv = uh_server_new(loop, addr, port); + srv = uh_server_new(loop); if (!srv) return; + if (addr) { + if (srv->listen(srv, addrs, false) < 0) + return; + } else if (addrs) { + if (srv->listen(srv, addrs, true) < 0) + return; + } else { + return; + } + #if UHTTPD_SSL_SUPPORT - if (ssl && srv->ssl_init(srv, "server-cert.pem", "server-key.pem") < 0) + if (ssl && srv->ssl_init(srv, "cert.pem", "key.pem") < 0) return; #endif @@ -105,13 +114,13 @@ int main(int argc, char **argv) bool ssl = false; const char *docroot = "."; const char *index_page = "index.html"; - const char *addr = "localhost"; + const char *addr = NULL; + const char *addrs = NULL; pid_t workers[MAX_WORKER] = {}; int nworker = get_nprocs(); - int port = 8080; int opt, i; - while ((opt = getopt(argc, argv, "h:i:a:p:sP:w:v")) != -1) { + while ((opt = getopt(argc, argv, "h:i:a:s:P:w:v")) != -1) { switch (opt) { case 'h': docroot = optarg; @@ -122,11 +131,8 @@ int main(int argc, char **argv) case 'a': addr = optarg; break; - case 'p': - port = atoi(optarg); - break; case 's': - ssl = true; + addrs = optarg; break; case 'P': plugin_path = optarg; @@ -164,7 +170,7 @@ int main(int argc, char **argv) if (pid == 0) { prctl(PR_SET_PDEATHSIG, SIGKILL); - start_server(addr, port, docroot, index_page, plugin_path, ssl); + start_server(addr, addrs, docroot, index_page, plugin_path, ssl); return 0; } diff --git a/example/simple_server.c b/example/simple_server.c index fd3fe2a..d675006 100644 --- a/example/simple_server.c +++ b/example/simple_server.c @@ -48,9 +48,8 @@ static void usage(const char *prog) fprintf(stderr, "Usage: %s [option]\n" " -h docroot # Document root, default is .\n" " -i index_page # Index page, default is index.html\n" - " -a addr # Default addr is localhost\n" - " -p port # Default port is 8080\n" - " -s # SSl on\n" + " -a addr # address to listen\n" + " -s addr # address to listen with ssl\n" " -P # plugin path\n" " -v # verbose\n", prog); exit(1); @@ -63,14 +62,15 @@ int main(int argc, char **argv) struct uh_server *srv = NULL; const char *plugin_path = NULL; bool verbose = false; - bool ssl = false; const char *docroot = "."; const char *index_page = "index.html"; - const char *addr = "localhost"; - int port = 8080; int opt; - while ((opt = getopt(argc, argv, "h:i:a:p:sP:v")) != -1) { + srv = uh_server_new(loop); + if (!srv) + return -1; + + while ((opt = getopt(argc, argv, "h:i:a:s:P:v")) != -1) { switch (opt) { case 'h': docroot = optarg; @@ -79,13 +79,12 @@ int main(int argc, char **argv) index_page = optarg; break; case 'a': - addr = optarg; + if (srv->listen(srv, optarg, false) < 1) + goto err; break; - case 'p': - port = atoi(optarg); - break; - case 's': - ssl = true; + case 's': + if (srv->listen(srv, optarg, true) < 1) + goto err; break; case 'P': plugin_path = optarg; @@ -93,7 +92,7 @@ int main(int argc, char **argv) case 'v': verbose = true; break; - default: /* '?' */ + default: usage(argv[0]); } } @@ -105,13 +104,8 @@ int main(int argc, char **argv) signal(SIGPIPE, SIG_IGN); - srv = uh_server_new(loop, addr, port); - if (!srv) - return -1; - #if UHTTPD_SSL_SUPPORT - if (ssl && srv->ssl_init(srv, "server-cert.pem", "server-key.pem") < 0) - goto err; + srv->ssl_init(srv, "cert.pem", "key.pem"); #endif srv->set_docroot(srv, docroot); diff --git a/gen_cert.sh b/gen_cert.sh index 5eac75b..2d3d408 100755 --- a/gen_cert.sh +++ b/gen_cert.sh @@ -1,3 +1,3 @@ #!/bin/sh -openssl req -x509 -newkey rsa:2048 -nodes -keyout server-key.pem -out server-cert.pem -subj "/C=CZ/O=Acme Inc./OU=ACME/CN=ACME-DEV-123" +openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -subj "/C=CZ/O=Acme Inc./OU=ACME/CN=ACME-DEV-123" diff --git a/src/connection.c b/src/connection.c index ea76315..58870e4 100644 --- a/src/connection.c +++ b/src/connection.c @@ -794,8 +794,9 @@ static void conn_init_cb(struct uh_connection *conn) conn->close = conn_close; } -struct uh_connection_internal *uh_new_connection(struct uh_server_internal *srv, int sock, struct sockaddr *addr) +struct uh_connection_internal *uh_new_connection(struct uh_listener *l, int sock, struct sockaddr *addr) { + struct uh_server_internal *srv = l->srv; struct uh_connection_internal *conn; conn = calloc(1, sizeof(struct uh_connection_internal)); @@ -822,7 +823,7 @@ struct uh_connection_internal *uh_new_connection(struct uh_server_internal *srv, ev_timer_start(srv->loop, &conn->timer); #if UHTTPD_SSL_SUPPORT - if (srv->ssl_ctx) + if (l->ssl) conn->ssl = uh_ssl_new(srv->ssl_ctx, sock); #endif diff --git a/src/connection.h b/src/connection.h index aaead79..0b249e8 100644 --- a/src/connection.h +++ b/src/connection.h @@ -97,7 +97,7 @@ struct uh_connection_internal { void (*handler)(struct uh_connection *conn, int event); }; -struct uh_connection_internal *uh_new_connection(struct uh_server_internal *srv, int sock, struct sockaddr *addr); +struct uh_connection_internal *uh_new_connection(struct uh_listener *l, int sock, struct sockaddr *addr); void conn_free(struct uh_connection_internal *conn); diff --git a/src/uhttpd.c b/src/uhttpd.c index cda979f..33a6fee 100644 --- a/src/uhttpd.c +++ b/src/uhttpd.c @@ -42,15 +42,11 @@ static void uh_server_free(struct uh_server *srv) struct uh_server_internal *srvi = (struct uh_server_internal *)srv; struct uh_connection_internal *conn = srvi->conns; struct uh_path_handler *h = srvi->handlers; + struct uh_listener *l = srvi->listeners; #ifdef HAVE_DLOPEN struct uh_plugin *p = srvi->plugins; #endif - ev_io_stop(srvi->loop, &srvi->ior); - - if (srvi->sock > 0) - close(srvi->sock); - if (srvi->docroot) free(srvi->docroot); @@ -69,6 +65,18 @@ static void uh_server_free(struct uh_server *srv) free(temp); } + while (l) { + struct uh_listener *temp = l; + + ev_io_stop(srvi->loop, &l->ior); + + if (l->sock > 0) + close(l->sock); + + l = l->next; + free(temp); + } + #ifdef HAVE_DLOPEN while (p) { struct uh_plugin *temp = p; @@ -85,7 +93,8 @@ static void uh_server_free(struct uh_server *srv) static void uh_accept_cb(struct ev_loop *loop, struct ev_io *w, int revents) { - struct uh_server_internal *srv = container_of(w, struct uh_server_internal, ior); + struct uh_listener *l = container_of(w, struct uh_listener, ior); + struct uh_server_internal *srv = l->srv; struct uh_connection_internal *conn; union { struct sockaddr sa; @@ -97,7 +106,7 @@ static void uh_accept_cb(struct ev_loop *loop, struct ev_io *w, int revents) int port; int sock; - sock = accept4(srv->sock, (struct sockaddr *)&addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC); + sock = accept4(l->sock, (struct sockaddr *)&addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC); if (sock < 0) { if (errno != EAGAIN) uh_log_err("accept: %s\n", strerror(errno)); @@ -109,7 +118,21 @@ static void uh_accept_cb(struct ev_loop *loop, struct ev_io *w, int revents) uh_log_debug("New Connection from: %s %d\n", addr_str, port); } - conn = uh_new_connection(srv, sock, &addr.sa); + if (l->ssl) { +#if UHTTPD_SSL_SUPPORT + if (!srv->ssl_ctx) { + uh_log_err("SSL not initialized\n"); + close(sock); + return; + } +#else + close(sock); + uh_log_err("SSL not enabled when build\n"); + return; +#endif + } + + conn = uh_new_connection(l, sock, &addr.sa); if (!conn) return; @@ -123,7 +146,7 @@ static void uh_accept_cb(struct ev_loop *loop, struct ev_io *w, int revents) srv->conns = conn; } -struct uh_server *uh_server_new(struct ev_loop *loop, const char *host, int port) +struct uh_server *uh_server_new(struct ev_loop *loop) { struct uh_server *srv; @@ -133,10 +156,7 @@ struct uh_server *uh_server_new(struct ev_loop *loop, const char *host, int port return NULL; } - if (uh_server_init(srv, loop, host, port) < 0) { - free(srv); - return NULL; - } + uh_server_init(srv, loop); return srv; } @@ -279,93 +299,155 @@ static struct ev_loop *uh_get_loop(struct uh_server *srv) return srvi->loop; } -int uh_server_init(struct uh_server *srv, struct ev_loop *loop, const char *host, int port) +static int parse_address(const char *addr, char **host, char **port) +{ + static char buf[256]; + char *s; + int l; + + strcpy(buf, addr); + + *host = NULL; + *port = buf; + + s = strrchr(buf, ':'); + if (!s) + return -1; + + *host = buf; + *port = s + 1; + *s = 0; + + if (*host && **host == '[') { + l = strlen(*host); + if (l >= 2) { + (*host)[l - 1] = 0; + (*host)++; + } + } + + if ((*host)[0] == '\0') + *host = "0"; + + return 0; +} + +static int uh_server_listen(struct uh_server *srv, const char *addr, bool ssl) { struct uh_server_internal *srvi = (struct uh_server_internal *)srv; - union { - struct sockaddr sa; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - } addr; + struct uh_listener *l; + char *host, *port; + struct addrinfo *addrs = NULL, *p = NULL; + static struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_PASSIVE, + }; char addr_str[INET6_ADDRSTRLEN]; - socklen_t addrlen; - int sock = -1; + int bound = 0; int on = 1; + int status; + int sock; - if (!host || *host == '\0') { - addr.sin.sin_family = AF_INET; - addr.sin.sin_addr.s_addr = htonl(INADDR_ANY); - } - - if (inet_pton(AF_INET, host, &addr.sin.sin_addr) == 1) { - addr.sa.sa_family = AF_INET; - } else if (inet_pton(AF_INET6, host, &addr.sin6.sin6_addr) == 1) { - addr.sa.sa_family = AF_INET6; - } else { - static struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_flags = AI_PASSIVE - }; - struct addrinfo *ais; - int status; - - status = getaddrinfo(host, NULL, &hints, &ais); - if (status != 0) { - uh_log_err("getaddrinfo(): %s\n", gai_strerror(status)); - return -1; - } - - memcpy(&addr, ais->ai_addr, ais->ai_addrlen); - freeaddrinfo(ais); - } - - if (addr.sa.sa_family == AF_INET) { - addr.sin.sin_port = ntohs(port); - addrlen = sizeof(addr.sin); - inet_ntop(AF_INET, &addr.sin.sin_addr, addr_str, sizeof(addr_str)); - } else { - addr.sin6.sin6_port = ntohs(port); - addrlen = sizeof(addr.sin6); - inet_ntop(AF_INET6, &addr.sin6.sin6_addr, addr_str, sizeof(addr_str)); - } - - sock = socket(addr.sa.sa_family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); - if (sock < 0) { - uh_log_err("socket: %s\n", strerror(errno)); + if (parse_address(addr, &host, &port) < 0) { + uh_log_err("invalid address\n"); return -1; } - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)) < 0) { - uh_log_err("setsockopt: %s\n", strerror(errno)); - goto err; + status = getaddrinfo(host, port, &hints, &addrs); + if (status != 0) { + uh_log_err("getaddrinfo(): %s\n", gai_strerror(status)); + return -1; } - setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int)); + /* try to bind a new socket to each found address */ + for (p = addrs; p; p = p->ai_next) { + sock = socket(p->ai_family, p->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC, p->ai_protocol); + if (sock < 0) { + uh_log_err("socket: %s\n", strerror(errno)); + continue; + } - if (bind(sock, &addr.sa, addrlen) < 0) { - uh_log_err("bind: %s\n", strerror(errno)); - goto err; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)) < 0) { + uh_log_err("setsockopt: %s\n", strerror(errno)); + goto err; + } + + /* required to get parallel v4 + v6 working */ + if (p->ai_family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(int)) < 0) { + uh_log_err("setsockopt: %s\n", strerror(errno)); + goto err; + } + + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int)); + + if (bind(sock, p->ai_addr, p->ai_addrlen) < 0) { + uh_log_err("bind: %s\n", strerror(errno)); + goto err; + } + + if (listen(sock, SOMAXCONN) < 0) { + uh_log_err("bind: %s\n", strerror(errno)); + goto err; + } + + l = calloc(1, sizeof(struct uh_listener)); + if (!l) { + uh_log_err("calloc: %s\n", strerror(errno)); + goto err; + } + + l->sock = sock; + l->ssl = ssl; + l->srv = srvi; + + ev_io_init(&l->ior, uh_accept_cb, sock, EV_READ); + ev_io_start(srvi->loop, &l->ior); + + if (!srvi->listeners) { + srvi->listeners = l; + } else { + l->next = srvi->listeners; + srvi->listeners = l; + } + + if (p->ai_family == AF_INET) { + struct sockaddr_in *ina = (struct sockaddr_in *)p->ai_addr; + inet_ntop(p->ai_family, &ina->sin_addr, addr_str, sizeof(addr_str)); + uh_log_debug("Listen on: %s:%d with ssl %s\n", addr_str, ntohs(ina->sin_port), ssl ? "on" : "off"); + } else { + struct sockaddr_in6 *in6a = (struct sockaddr_in6 *)p->ai_addr; + inet_ntop(p->ai_family, &in6a->sin6_addr, addr_str, sizeof(addr_str)); + uh_log_debug("Listen on: [%s]:%d with ssl %s\n", addr_str, ntohs(in6a->sin6_port), ssl ? "on" : "off"); + } + + bound++; + + continue; + +err: + if (sock > -1) + close(sock); } - if (listen(sock, SOMAXCONN) < 0) { - uh_log_err("bind: %s\n", strerror(errno)); - goto err; - } + freeaddrinfo(addrs); - if (uh_log_get_threshold() == LOG_DEBUG) { - saddr2str(&addr.sa, addr_str, sizeof(addr_str), &port); - uh_log_debug("Listen on: %s %d\n", addr_str, port); - } + return bound; +} + +void uh_server_init(struct uh_server *srv, struct ev_loop *loop) +{ + struct uh_server_internal *srvi = (struct uh_server_internal *)srv; memset(srvi, 0, sizeof(struct uh_server_internal)); srvi->loop = loop ? loop : EV_DEFAULT; - srvi->sock = sock; srv->get_loop = uh_get_loop; srv->free = uh_server_free; + srv->listen = uh_server_listen; + #if UHTTPD_SSL_SUPPORT srv->ssl_init = uh_server_ssl_init; #endif @@ -378,13 +460,4 @@ int uh_server_init(struct uh_server *srv, struct ev_loop *loop, const char *host srv->set_docroot = uh_set_docroot; srv->set_index_page = uh_set_index_page; - - ev_io_init(&srvi->ior, uh_accept_cb, sock, EV_READ); - ev_io_start(srvi->loop, &srvi->ior); - - return 0; - -err: - close(sock); - return -1; } diff --git a/src/uhttpd.h b/src/uhttpd.h index 49acfd9..bcc77fa 100644 --- a/src/uhttpd.h +++ b/src/uhttpd.h @@ -89,6 +89,14 @@ typedef void (*uh_path_handler_prototype)(struct uh_connection *conn, int event) struct uh_server { struct ev_loop *(*get_loop)(struct uh_server *srv); void (*free)(struct uh_server *srv); + /* + ** listen an address, multiple call allowed + ** returns the number of successful listen + ** + ** :80 0:80 0.0.0.0:80 [::]:80 + ** localhost:80 [::1]:80 + */ + int (*listen)(struct uh_server *srv, const char *addr, bool ssl); #if UHTTPD_SSL_SUPPORT int (*ssl_init)(struct uh_server *srv, const char *cert, const char *key); #endif @@ -120,11 +128,9 @@ struct uh_path_handler { /* * uh_server_new - creat an uh_server struct and init it * @loop: If NULL will use EV_DEFAULT - * @host: If NULL will listen on "0.0.0.0" - * @port: port to listen on */ -struct uh_server *uh_server_new(struct ev_loop *loop, const char *host, int port); +struct uh_server *uh_server_new(struct ev_loop *loop); -int uh_server_init(struct uh_server *srv, struct ev_loop *loop, const char *host, int port); +void uh_server_init(struct uh_server *srv, struct ev_loop *loop); #endif diff --git a/src/uhttpd_internal.h b/src/uhttpd_internal.h index 4ce99a9..4e9f80d 100644 --- a/src/uhttpd_internal.h +++ b/src/uhttpd_internal.h @@ -29,15 +29,23 @@ #include "uhttpd.h" +struct uh_server_internal; struct uh_connection_internal; +struct uh_listener { + int sock; + bool ssl; + struct ev_io ior; + struct uh_server_internal *srv; + struct uh_listener *next; +}; + struct uh_server_internal { struct uh_server com; - int sock; char *docroot; char *index_page; struct ev_loop *loop; - struct ev_io ior; + struct uh_listener *listeners; struct uh_connection_internal *conns; void (*conn_closed_cb)(struct uh_connection *conn); void (*default_handler)(struct uh_connection *conn, int event); @@ -48,9 +56,4 @@ struct uh_server_internal { struct uh_path_handler *handlers; }; -struct worker { - struct ev_child w; - int i; -}; - #endif \ No newline at end of file