summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2015-12-23 15:36:33 +0100
committerYorhel <git@yorhel.nl>2015-12-23 15:36:33 +0100
commitfe28e88caea2a7826fddb43181ad5263faf55034 (patch)
treebbd696a8b66f10108ea6a51aa47f707cfe04e749
parent7cd0659ea033d74535550019bf09fa0437279fd0 (diff)
Add support for TCP & stdio listeners
Somewhat untested.
-rw-r--r--src/app.c87
-rw-r--r--src/app.h9
-rw-r--r--src/main.h2
-rw-r--r--src/util.c69
-rw-r--r--src/util.h41
5 files changed, 147 insertions, 61 deletions
diff --git a/src/app.c b/src/app.c
index 3303472..84673aa 100644
--- a/src/app.c
+++ b/src/app.c
@@ -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;
diff --git a/src/app.h b/src/app.h
index f584efd..1963d7b 100644
--- a/src/app.h
+++ b/src/app.h
@@ -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];
};
diff --git a/src/main.h b/src/main.h
index c3b313e..6962670 100644
--- a/src/main.h
+++ b/src/main.h
@@ -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) */
diff --git a/src/util.c b/src/util.c
index 4f594df..a2ab156 100644
--- a/src/util.c
+++ b/src/util.c
@@ -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;
+ }
+}
diff --git a/src/util.h b/src/util.h
index d0c1867..fb2ba27 100644
--- a/src/util.h
+++ b/src/util.h
@@ -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