diff options
author | Yorhel <git@yorhel.nl> | 2015-12-23 15:36:33 +0100 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2015-12-23 15:36:33 +0100 |
commit | fe28e88caea2a7826fddb43181ad5263faf55034 (patch) | |
tree | bbd696a8b66f10108ea6a51aa47f707cfe04e749 | |
parent | 7cd0659ea033d74535550019bf09fa0437279fd0 (diff) |
Add support for TCP & stdio listeners
Somewhat untested.
-rw-r--r-- | src/app.c | 87 | ||||
-rw-r--r-- | src/app.h | 9 | ||||
-rw-r--r-- | src/main.h | 2 | ||||
-rw-r--r-- | src/util.c | 69 | ||||
-rw-r--r-- | src/util.h | 41 |
5 files changed, 147 insertions, 61 deletions
@@ -44,6 +44,8 @@ static void front_accept_cb(EV_P_ ev_io *w, int revents) { fd = -1; } + /* TODO: Honor FCGI_WEB_SERVER_ADDRS, if set and this is an stdio frontend */ + if(fd < 0) { fprintf(stderr, "Error accept()'ing: %s.\n", strerror(errno)); ev_io_stop(EV_A_ w); @@ -57,13 +59,17 @@ static void front_accept_cb(EV_P_ ev_io *w, int revents) { } -static fcgy_front *front_create(fcgy_app *app, fcgy_front_type t, const char *addr, uint16_t port) { +/* TODO: Error reporting */ +static fcgy_front *front_create(fcgy_app *app, const char *addr) { + util_sockaddr a; + if(util_parse_addr(addr, &a) < 0) + return NULL; + fcgy_front *f = calloc(1, sizeof(fcgy_front)); + memcpy(&f->addr, &a, sizeof(a)); f->app = app; - f->type = t; - f->port = port; f->fd = -1; - f->addr = strdup(addr); + f->type = FCGY_FRONT_FASTCGI; ev_init(f->listener, front_accept_cb); ev_init(f->backoff, front_timer_cb); f->listener->data = f; @@ -73,46 +79,23 @@ static fcgy_front *front_create(fcgy_app *app, fcgy_front_type t, const char *ad static int front_bind(fcgy_front *f, char *err, size_t errlen) { - switch(f->type) { - - case FCGY_FRONT_UNIX: { - struct sockaddr_un addr; - addr.sun_family = AF_UNIX; - int r = snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", f->addr); - if(r < 0 || r >= (int)sizeof(addr.sun_path)) { - snprintf(err, errlen, "invalid socket path"); - return -1; - } - - f->fd = util_serversock(AF_UNIX, &addr, sizeof(addr), FCGY_CLOEXEC|FCGY_NONBLOCK); - if(f->fd < 0) { - snprintf(err, errlen, "can't bind socket: %s", strerror(errno)); - return -1; - } - } - break; - - case FCGY_FRONT_TCP: - case FCGY_FRONT_STDIO: - break; /* TODO */ + if(f->addr.generic.sa_family == 0) { + f->fd = 0; + return 0; + } + f->fd = util_serversock(f->addr.generic.sa_family, &f->addr, util_sockaddr_len(f->addr.generic.sa_family), FCGY_CLOEXEC|FCGY_NONBLOCK); + if(f->fd < 0) { + snprintf(err, errlen, "can't bind socket: %s", strerror(errno)); + return -1; } return 0; } static void front_start(fcgy_front *f) { - switch(f->type) { - case FCGY_FRONT_UNIX: - case FCGY_FRONT_TCP: - ev_io_set(f->listener, f->fd, EV_READ); - ev_io_start(EV_DEFAULT_UC_ f->listener); - break; - - case FCGY_FRONT_STDIO: - /* TODO: Create connection struct */ - break; - } + ev_io_set(f->listener, f->fd, EV_READ); + ev_io_start(EV_DEFAULT_UC_ f->listener); } @@ -120,18 +103,11 @@ static void front_stop(fcgy_front *f) { if(f->fd < 0) return; - switch(f->type) { - case FCGY_FRONT_UNIX: - unlink(f->addr); - case FCGY_FRONT_TCP: - ev_io_stop(EV_DEFAULT_UC_ f->listener); - ev_timer_stop(EV_DEFAULT_UC_ f->backoff); - break; - - case FCGY_FRONT_STDIO: - /* TODO: Nothing? */ - break; - } + if(f->addr.generic.sa_family == AF_UNIX) + unlink(f->addr.un.sun_path); + + ev_io_stop(EV_DEFAULT_UC_ f->listener); + ev_timer_stop(EV_DEFAULT_UC_ f->backoff); close(f->fd); f->fd = -1; @@ -149,8 +125,12 @@ fcgy_app *app_create() { int app_config(fcgy_app *app, fcgy_config_name name, const char *val, char *err, size_t errlen) { fcgy_front *f; switch(name) { - case FCGY_CONFIG_APP_UNIX: - f = front_create(app, FCGY_FRONT_UNIX, val, 0); + case FCGY_CONFIG_APP_LISTEN: + f = front_create(app, val); + if(!f) { + snprintf(err, errlen, "invalid address '%s'", val); + return -1; + } hlist_prepend(app->fronts, f); return 0; default: @@ -162,10 +142,11 @@ int app_config(fcgy_app *app, fcgy_config_name name, const char *val, char *err, int app_bind(fcgy_app *app, char *err, size_t errlen) { fcgy_front *f; - char ebuff[256]; + char ebuff[256], addrbuf[UTIL_ADDRSTRLEN]; for(f=app->fronts; f; f=f->next) if(front_bind(f, ebuff, sizeof(ebuff)) < 0) { - snprintf(err, errlen, "%s: %s", f->addr, ebuff); + util_format_addr(&f->addr, addrbuf); + snprintf(err, errlen, "%s: %s", addrbuf, ebuff); return -1; } return 0; @@ -24,22 +24,17 @@ #define FCGY_APP_H enum fcgy_front_type { - FCGY_FRONT_STDIO, - FCGY_FRONT_UNIX, - FCGY_FRONT_TCP + FCGY_FRONT_FASTCGI }; -/* TODO: Replace addr/port with a struct sockaddr (/union), to cause invalid - * addresses to fail at config reading time and simplify the binding logic. */ struct fcgy_front { fcgy_front *next, *prev; fcgy_app *app; fcgy_front_type type; - uint16_t port; int fd; - char *addr; + util_sockaddr addr; ev_io listener[1]; ev_timer backoff[1]; }; @@ -30,7 +30,7 @@ /* Application options. * X(NAME, "name") */ #define FCGY_CONFIG_APP \ - X(UNIX, "unix") + X(LISTEN, "listen") /* Command-line options. * X(NAME, "yopt-style-name", needarg) */ @@ -59,3 +59,72 @@ int util_serversock(int domain, void *addr, socklen_t addrlen, uint32_t flags) { } return fd; } + + +int util_parse_addr(const char *a, util_sockaddr *sock) { + char buf[128]; + int n; + unsigned short port = 0; + + memset(sock, 0, sizeof(util_sockaddr)); + if(strcmp(a, "-") == 0) + return 0; + + /* IPv6 */ + if(sscanf(a, "[%127[a-fA-Z0-9:]]:%hu%n", buf, &port, &n) == 2 && (size_t)n == strlen(a)) { + sock->in6.sin6_family = AF_INET6; + sock->in6.sin6_port = htons(port); + if(inet_pton(AF_INET6, buf, &sock->in6.sin6_addr) != 1) + return -1; + return 0; + } + + /* IPv4 */ + if(sscanf(a, "%127[0-9.]:%hu%n", buf, &port, &n) == 2 && (size_t)n == strlen(a)) { + sock->in.sin_family = AF_INET; + sock->in.sin_port = htons(port); + if(inet_pton(AF_INET, buf, &sock->in.sin_addr) != 1) + return -1; + return 0; + } + + /* port */ + if(sscanf(a, "%hu%n", &port, &n) == 1 && (size_t)n == strlen(a)) { + /* We already zero-initialze the sockaddr struct, so the 0.0.0.0 address is set */ + sock->in.sin_family = AF_INET; + sock->in.sin_port = htons(port); + return 0; + } + + /* UNIX socket path */ + if(*a == '/') { + sock->un.sun_family = AF_UNIX; + n = snprintf(sock->un.sun_path, sizeof(sock->un.sun_path), "%s", a); + if(n < 0 || (size_t)n >= sizeof(sock->un.sun_path)) + return -1; + return 0; + } + + return -1; +} + + +void util_format_addr(const util_sockaddr *s, char *dst) { + switch(s->generic.sa_family) { + case 0: + strcpy(dst, "-"); + break; + case AF_INET: + inet_ntop(AF_INET, &s->in.sin_addr, dst, UTIL_ADDRSTRLEN); + snprintf(dst+strlen(dst), UTIL_ADDRSTRLEN-strlen(dst), ":%"PRIu16, ntohs(s->in.sin_port)); + break; + case AF_INET6: + *dst = '['; + inet_ntop(AF_INET6, &s->in6.sin6_addr, dst+1, UTIL_ADDRSTRLEN-1); + snprintf(dst+strlen(dst), UTIL_ADDRSTRLEN-strlen(dst), "]:%"PRIu16, ntohs(s->in6.sin6_port)); + break; + case AF_UNIX: + strcpy(dst, s->un.sun_path); + break; + } +} @@ -23,11 +23,52 @@ #ifndef FCGY_UTIL_H #define FCGY_UTIL_H +/* This is pretty much a struct sockaddr_storage, but also incorporates UNIX + * sockets. */ +typedef union { + struct sockaddr generic; + struct sockaddr_in in; + struct sockaddr_in6 in6; + struct sockaddr_un un; +} util_sockaddr; + +/* Should be large enough to hold a full IPv6 addr + port or a complete UNIX + * path. The latter tends to have a larger maximum limit. */ +#define UTIL_ADDRSTRLEN sizeof(struct sockaddr_un) + + #define FCGY_NONBLOCK 1 #define FCGY_CLOEXEC 2 int util_fd_flags(int, uint32_t); int util_serversock(int, void *, socklen_t, uint32_t); + +/* Parse a socket address specification, supports the following formats: + * [ipv6]:port + * ipv4:port + * port (interpreted as 0.0.0.0:port) + * /unix/path (must be absolute) + * - (standard I/O) + * Writes the address into *sock and returns 0 on success. + * Standard I/O is indicated as sock->generic.sa_family = 0. + * TODO: Support and resolve hostnames? (Can't support hostnames with multiple + * A/AAAA records, however). */ +int util_parse_addr(const char *, util_sockaddr *); + +/* Reverse of util_parse_addr(). Destination buffer must be large enough to + * hold UTIL_ADDRSTRLEN bytes. */ +void util_format_addr(const util_sockaddr *, char *); + + +static inline socklen_t util_sockaddr_len(int type) { + switch(type) { + case AF_INET: return sizeof(struct sockaddr_in); break; + case AF_INET6: return sizeof(struct sockaddr_in6); break; + case AF_UNIX: return sizeof(struct sockaddr_un); break; + } + return 0; +} + #endif |