diff options
Diffstat (limited to 'multipart-replace-http-server.c')
-rw-r--r-- | multipart-replace-http-server.c | 170 |
1 files changed, 140 insertions, 30 deletions
diff --git a/multipart-replace-http-server.c b/multipart-replace-http-server.c index c3e6219..a5b1c8b 100644 --- a/multipart-replace-http-server.c +++ b/multipart-replace-http-server.c @@ -1,14 +1,17 @@ /* Copyright 2016 Luke Shumaker */ -#include <error.h> +#include <ctype.h> /* for isdigit */ #include <errno.h> +#include <error.h> +#include <fcntl.h> /* for open */ #include <netdb.h> /* for {get,free}addrinfo() */ +#include <stdio.h> #include <stdlib.h> /* for EXIT_FAILURE */ +#include <string.h> #include <sys/socket.h> -#include <stdio.h> -#include <unistd.h> #include <sys/stat.h> /* for open */ -#include <fcntl.h> /* for open */ +#include <sys/un.h> +#include <unistd.h> #include "util.h" #include "multipart-replace.h" @@ -68,32 +71,123 @@ struct multipart_replace_stream *file_get(const char *filename) { return NULL; } -int tcp4_parse_listen(const char *port) { - int r; - struct addrinfo *addr = NULL; - struct addrinfo hints = { 0 }; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; +/* same error codes values as -getaddrinfo(); */ +int sockstream_listen(const char *type, const char *addr) { + int sock; + if (strcmp(type, "fd") == 0) { + if (strcmp(addr, "stdin") == 0) { + sock = 0; + } else if (strcmp(addr, "stdout") == 0) { + sock = 1; + } else if (strcmp(addr, "stderr") == 0) { + sock = 2; + } else if (strcmp(addr, "systemd") == 0) { + sock = 3; /* <systemd/sd-daemon.h>:SD_LISTEN_FDS_START */ + } else { + for (size_t i = 0; addr[i] != '\0'; i++) + if (!isdigit(addr[i])) { + errno = EINVAL; + return -EAI_SYSTEM; + } + if (addr[0] == '\0') { + errno = EINVAL; + return -EAI_SYSTEM; + } + sock = atoi(addr); + } + /* make sure it's a socket */ + struct stat st; + if (fstat(sock, &st) < 0) + return -EAI_SYSTEM; + if (!S_ISSOCK(st.st_mode)) { + errno = ENOTSOCK; + return -EAI_SYSTEM; + } + /* make sure the socket is a stream */ + int fd_socktype = 0; + socklen_t l = sizeof(fd_socktype); + if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &fd_socktype, &l) < 0) + return -EAI_SYSTEM; + if (l != sizeof(fd_socktype)) { + errno = EINVAL; + return -EAI_SYSTEM; + } + if (fd_socktype != SOCK_STREAM) { + errno = ENOSTR; + return -EAI_SYSTEM; + } + /* make sure the socket is listening */ + int fd_listening = 0; + l = sizeof(fd_listening); + if (getsockopt(sock, SOL_SOCKET, SO_ACCEPTCONN, &fd_listening, &l) < 0) + return -EAI_SYSTEM; + if (l != sizeof(fd_listening)) { + errno = EINVAL; + return -EAI_SYSTEM; + } + if (!fd_listening) + listen(sock, SOMAXCONN); + /* return the socket */ + return sock; + } else if (strcmp(type, "unix") == 0) { + struct sockaddr_un un_addr = { 0 }; + if (strlen(addr)+1 > sizeof(un_addr.sun_path)) { + errno = ENAMETOOLONG; + return -EAI_SYSTEM; + } + un_addr.sun_family = AF_UNIX; + strcpy(un_addr.sun_path, addr); + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return -EAI_SYSTEM; + unlink(addr); + if (bind(sock, (struct sockaddr *)&un_addr, sizeof(un_addr)) < 0) + return -EAI_SYSTEM; + if (listen(sock, SOMAXCONN) < 0) + return -EAI_SYSTEM; + return sock; + } else if (strcmp(type, "tcp4") == 0 || strcmp(type, "tcp6") == 0) { + struct addrinfo *ai_addr = NULL; + struct addrinfo ai_hints = { 0 }; + + char *col = strrchr(addr, ':'); + if (col == NULL) { + errno = EINVAL; + return -EAI_SYSTEM; + } + char *host = strdupa(addr); + char *port = &col[1]; + *col = '\0'; + if (host[0] == '\0') + host = NULL; - if ((r = getaddrinfo(NULL, port, &hints, &addr)) != 0) - error(EXIT_FAILURE, r, _("Could not resolve TCP4 port %s"), port); + ai_hints.ai_family = (strcmp(type, "tcp6") == 0) ? AF_INET6 : AF_INET; + ai_hints.ai_socktype = SOCK_STREAM; + ai_hints.ai_flags = AI_PASSIVE; - int sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); - if (sock < 0) - error(EXIT_FAILURE, errno, _("Could not create a TCP4 socket")); + int r = getaddrinfo(host, port, &ai_hints, &ai_addr); + if (r != 0) + return -r; - int yes = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + int sock = socket(ai_addr->ai_family, ai_addr->ai_socktype, ai_addr->ai_protocol); + if (sock < 0) + return -EAI_SYSTEM; - if (bind(sock, addr->ai_addr, addr->ai_addrlen) < 0) - error(EXIT_FAILURE, errno, _("Could not bind to TCP4 port %s"), port); + int yes = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); - if (listen(sock, 5) < 0) - error(EXIT_FAILURE, errno, _("Could not listen on TCP4 port %s"), port); + if (bind(sock, ai_addr->ai_addr, ai_addr->ai_addrlen) < 0) + return -EAI_SYSTEM; - freeaddrinfo(addr); - return sock; + if (listen(sock, SOMAXCONN) < 0) + return -EAI_SYSTEM; + + freeaddrinfo(ai_addr); + return sock; + } else { + errno = EINVAL; + return -EAI_SYSTEM; + } } void connection_handler(int fd) { @@ -153,20 +247,36 @@ void *connection_thread(void *arg_anon) { } void usage() { - printf("Usage: %s [-h] PORT [FILENAME...]\n", program_invocation_name); + printf("Usage: %s [-h] ADDRTYPE ADDR [FILENAME...]\n", program_invocation_name); + printf("Multiplex several multipart/x-mixed-replace streams over HTTP.\n" + "\n" + "ADDRTYPE is \"tcp4\", \"tcp6\", \"fd\", or \"unix\". For tcp4/6, ADDR\n" + "is a socket address in standard notation. For fd, ADDR is \"stdin\",\n" + "\"stdout\", \"stder\", \"systemd\", or an unsigned integer. For unix,\n" + "ADDR is a file path.\n" + "\n" + "The leading directories of the FILENAMEs are stripped; they are served\n" + "at just the base filename. As common sense should eventually suggest,\n" + "the FILENAMEs should be pipes or sockets.\n"); } int main(int argc, char *argv[]) { - if (argc < 2) { + if (strcmp(argv[1], "-h") == 0) { + usage(); + return EXIT_SUCCESS; + } + if (argc < 3) { dup2(2, 1); usage(); return EXIT_FAILURE; } - if (strcmp(argv[1], "-h") == 0) { - usage(); - return EXIT_SUCCESS; + int sock = sockstream_listen(argv[1], argv[2]); + if (sock < 0) { + if (sock == -EAI_SYSTEM) + error(1, errno, "Opening socket %s %s", argv[1], argv[2]); + else + error(1, 0, "Opening socket %s %s: %s", argv[1], argv[2], gai_strerror(-sock)); } - int sock = tcp4_parse_listen(argv[1]); for (int i = 2; i < argc; i++) file_add(argv[i]); |