summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2016-03-15 17:49:22 -0400
committerLuke Shumaker <lukeshu@sbcglobal.net>2016-03-15 17:49:22 -0400
commit1d3ea2976f0c71d95c4bb400d90f6bd84c31be1a (patch)
treed5e8b9371f41b951947b6a100d938d0c7c9e9e8d
parent116c393eb5a770988bf1abf0be8f24d0e29748b1 (diff)
multipart-replace-http-server: be more flexible about getting sockets
-rw-r--r--multipart-replace-http-server.c170
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]);