Просмотр исходного кода

Support unix domain sockets

Support x/tmp/socket as listening_port to listen to unix domain socket
"/tmp/socket". Need to build with USE_X_DOM_SOCKET define set.
bel2125 4 лет назад
Родитель
Сommit
2f9cfe7f92
3 измененных файлов с 175 добавлено и 35 удалено
  1. 7 0
      Makefile
  2. 167 34
      src/civetweb.c
  3. 1 1
      src/main.c

+ 7 - 0
Makefile

@@ -122,6 +122,7 @@ ifdef WITH_ALL
   WITH_SERVER_STATS = 1
   WITH_ZLIB = 1
   WITH_HTTP2 = 1
+  WITH_X_DOM_SOCKET = 1
   WITH_EXPERIMENTAL = 1
   #WITH_CPP is not defined, ALL means only real features, not wrappers
 endif
@@ -191,6 +192,12 @@ endif
 ifdef WITH_WEBSOCKETS
   CFLAGS += -DUSE_WEBSOCKET
 endif
+ifdef WITH_X_DOM_SOCKET
+  CFLAGS += -DUSE_X_DOM_SOCKET
+endif
+ifdef WITH_X_DOM_SOCKETS
+  CFLAGS += -DUSE_X_DOM_SOCKET
+endif
 
 ifdef WITH_SERVER_STAT
   CFLAGS += -DUSE_SERVER_STATS

+ 167 - 34
src/civetweb.c

@@ -873,6 +873,9 @@ typedef unsigned short int in_port_t;
 #include <sys/wait.h>
 #include <time.h>
 #include <unistd.h>
+#if defined(USE_X_DOM_SOCKET)
+#include <sys/un.h>
+#endif
 #endif
 
 #define vsnprintf_impl vsnprintf
@@ -2477,9 +2480,24 @@ union usa {
 #if defined(USE_IPV6)
 	struct sockaddr_in6 sin6;
 #endif
+#if defined(USE_X_DOM_SOCKET)
+	struct sockaddr_un sun;
+#endif
 };
 
-
+#if defined(USE_X_DOM_SOCKET)
+static unsigned short
+USA_IN_PORT_UNSAFE(union usa *s)
+{
+	if (s->sa.sa_family == AF_INET)
+		return s->sin.sin_port;
+#if defined(USE_IPV6)
+	if (s->sa.sa_family == AF_INET6)
+		return s->sin6.sin6_port;
+#endif
+	return 0;
+}
+#endif
 #if defined(USE_IPV6)
 #define USA_IN_PORT_UNSAFE(s)                                                  \
 	(((s)->sa.sa_family == AF_INET6) ? (s)->sin6.sin6_port : (s)->sin.sin_port)
@@ -3863,6 +3881,10 @@ mg_get_server_ports(const struct mg_context *ctx,
 }
 
 
+#if defined(USE_X_DOM_SOCKET) && !defined(UNIX_DOMAIN_SOCKET_SERVER_NAME)
+#define UNIX_DOMAIN_SOCKET_SERVER_NAME "*"
+#endif
+
 static void
 sockaddr_to_string(char *buf, size_t len, const union usa *usa)
 {
@@ -3892,6 +3914,22 @@ sockaddr_to_string(char *buf, size_t len, const union usa *usa)
 		            NI_NUMERICHOST);
 	}
 #endif
+#if defined(USE_X_DOM_SOCKET)
+	else if (usa->sa.sa_family == AF_UNIX) {
+		/* TODO: Define a remote address for unix domain sockets.
+		 * This code will always return "localhost", identical to http+tcp:
+		getnameinfo(&usa->sa,
+		    sizeof(usa->sun),
+		    buf,
+		    (unsigned)len,
+		    NULL,
+		    0,
+		    NI_NUMERICHOST);
+		 */
+		strncpy(buf, UNIX_DOMAIN_SOCKET_SERVER_NAME, len);
+		buf[len] = 0;
+	}
+#endif
 }
 
 
@@ -4184,6 +4222,31 @@ mg_construct_local_link(const struct mg_connection *conn,
 			return -1;
 		}
 
+#if defined(USE_X_DOM_SOCKET)
+		if (conn->client.lsa.sa.sa_family == AF_UNIX) {
+			/* TODO: Define and document a link for UNIX domain sockets. */
+			/* There seems to be no official standard for this.
+			 * Common uses seem to be "httpunix://", "http.unix://" or
+			 * "http+unix://" as a protocol definition string, followed by
+			 * "localhost" or "127.0.0.1" or "/tmp/unix/path" or
+			 * "%2Ftmp%2Funix%2Fpath" (url % encoded) or
+			 * "localhost:%2Ftmp%2Funix%2Fpath" (domain socket path as port) or
+			 * "" (completely skipping the server name part). In any case, the
+			 * last part is the server local path. */
+			const char *server_name = UNIX_DOMAIN_SOCKET_SERVER_NAME;
+			mg_snprintf(conn,
+			            &truncated,
+			            buf,
+			            buflen,
+			            "%s.unix://%s%s",
+			            proto,
+			            server_name,
+			            ri->local_uri);
+			default_port = 0;
+			return 0;
+		}
+#endif
+
 		if (define_proto) {
 			/* If we got a protocol name, use the default port accordingly. */
 			if ((0 == strcmp(define_proto, "https"))
@@ -9496,6 +9559,13 @@ connect_socket(struct mg_context *ctx /* may be NULL */,
 		*sock = socket(PF_INET6, SOCK_STREAM, 0);
 	}
 #endif
+#if 0 /* Not available as client */
+#if defined(USE_X_DOM_SOCKET)
+	else if (ip_ver == 99) {
+		*sock = socket(AF_UNIX, SOCK_STREAM, 0);
+	}
+#endif
+#endif
 
 	if (*sock == INVALID_SOCKET) {
 		mg_snprintf(NULL,
@@ -14900,6 +14970,18 @@ close_all_listening_sockets(struct mg_context *ctx)
 
 	for (i = 0; i < ctx->num_listening_sockets; i++) {
 		closesocket(ctx->listening_sockets[i].sock);
+#if defined(USE_X_DOM_SOCKET)
+		/* For unix domain sockets, the socket name represents a file that has
+		 * to be deleted. */
+		/* See
+		 * https://stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix
+		 */
+		if ((ctx->listening_sockets[i].lsa.sin.sin_family == AF_UNIX)
+		    && (ctx->listening_sockets[i].sock != INVALID_SOCKET)) {
+			IGNORE_UNUSED_RESULT(
+			    remove(ctx->listening_sockets[i].lsa.sun.sun_path));
+		}
+#endif
 		ctx->listening_sockets[i].sock = INVALID_SOCKET;
 	}
 	mg_free(ctx->listening_sockets);
@@ -15042,6 +15124,23 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
 			len = 0;
 		}
 
+#if defined(USE_X_DOM_SOCKET)
+
+	} else if (vec->ptr[0] == 'x') {
+		/* unix (linux) domain socket */
+		if (vec->len < sizeof(so->lsa.sun.sun_path)) {
+			len = vec->len;
+			so->lsa.sun.sun_family = AF_UNIX;
+			memset(so->lsa.sun.sun_path, 0, sizeof(so->lsa.sun.sun_path));
+			memcpy(so->lsa.sun.sun_path, (char *)vec->ptr + 1, vec->len - 1);
+			port = 0;
+			*ip_version = 99;
+		} else {
+			/* String too long */
+			len = 0;
+		}
+#endif
+
 	} else {
 		/* Parsing failure. */
 		len = 0;
@@ -15179,8 +15278,14 @@ set_ports_option(struct mg_context *phys_ctx)
 			continue;
 		}
 #endif
-
-		if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6))
+		/* Create socket. */
+		/* For a list of protocol numbers (e.g., TCP==6) see:
+		 * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
+		 */
+		if ((so.sock =
+		         socket(so.lsa.sa.sa_family,
+		                SOCK_STREAM,
+		                (ip_version == 99) ? (/* LOCAL */ 0) : (/* TCP */ 6)))
 		    == INVALID_SOCKET) {
 
 			mg_cry_ctx_internal(phys_ctx,
@@ -15228,7 +15333,13 @@ set_ports_option(struct mg_context *phys_ctx)
 		}
 #endif
 
-		if (ip_version > 4) {
+#if defined(USE_X_DOM_SOCKET)
+		if (ip_version == 99) {
+			/* Unix domain socket */
+		} else
+#endif
+
+		    if (ip_version > 4) {
 /* Could be 6 for IPv6 onlyor 10 (4+6) for IPv4+IPv6 */
 #if defined(USE_IPV6)
 			if (ip_version > 6) {
@@ -15302,7 +15413,22 @@ set_ports_option(struct mg_context *phys_ctx)
 			}
 		}
 #endif
+#if defined(USE_X_DOM_SOCKET)
+		else if (so.lsa.sa.sa_family == AF_UNIX) {
 
+			len = sizeof(so.lsa.sun);
+			if (bind(so.sock, &so.lsa.sa, len) != 0) {
+				mg_cry_ctx_internal(phys_ctx,
+				                    "cannot bind to unix socket %s: %d (%s)",
+				                    so.lsa.sun.sun_path,
+				                    (int)ERRNO,
+				                    strerror(errno));
+				closesocket(so.sock);
+				so.sock = INVALID_SOCKET;
+				continue;
+			}
+		}
+#endif
 		else {
 			mg_cry_ctx_internal(
 			    phys_ctx,
@@ -16969,16 +17095,20 @@ reset_per_request_attributes(struct mg_connection *conn)
 
 
 static int
-set_tcp_nodelay(SOCKET sock, int nodelay_on)
-{
-	if (setsockopt(sock,
-	               IPPROTO_TCP,
-	               TCP_NODELAY,
-	               (SOCK_OPT_TYPE)&nodelay_on,
-	               sizeof(nodelay_on))
-	    != 0) {
-		/* Error */
-		return 1;
+set_tcp_nodelay(const struct socket *so, int nodelay_on)
+{
+	if ((so->lsa.sa.sa_family == AF_INET)
+	    || (so->lsa.sa.sa_family == AF_INET6)) {
+		/* Only for TCP sockets */
+		if (setsockopt(so->sock,
+		               IPPROTO_TCP,
+		               TCP_NODELAY,
+		               (SOCK_OPT_TYPE)&nodelay_on,
+		               sizeof(nodelay_on))
+		    != 0) {
+			/* Error */
+			return 1;
+		}
 	}
 	/* OK */
 	return 0;
@@ -19149,24 +19279,27 @@ accept_new_connection(const struct socket *listener, struct mg_context *ctx)
 		}
 
 #if !defined(__ZEPHYR__)
-		/* Set TCP keep-alive. This is needed because if HTTP-level
-		 * keep-alive
-		 * is enabled, and client resets the connection, server won't get
-		 * TCP FIN or RST and will keep the connection open forever. With
-		 * TCP keep-alive, next keep-alive handshake will figure out that
-		 * the client is down and will close the server end.
-		 * Thanks to Igor Klopov who suggested the patch. */
-		if (setsockopt(so.sock,
-		               SOL_SOCKET,
-		               SO_KEEPALIVE,
-		               (SOCK_OPT_TYPE)&on,
-		               sizeof(on))
-		    != 0) {
-			mg_cry_ctx_internal(
-			    ctx,
-			    "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
-			    __func__,
-			    strerror(ERRNO));
+		if ((so.lsa.sa.sa_family == AF_INET)
+		    || (so.lsa.sa.sa_family == AF_INET6)) {
+			/* Set TCP keep-alive for TCP sockets (IPv4 and IPv6).
+			 * This is needed because if HTTP-level keep-alive
+			 * is enabled, and client resets the connection, server won't get
+			 * TCP FIN or RST and will keep the connection open forever. With
+			 * TCP keep-alive, next keep-alive handshake will figure out that
+			 * the client is down and will close the server end.
+			 * Thanks to Igor Klopov who suggested the patch. */
+			if (setsockopt(so.sock,
+			               SOL_SOCKET,
+			               SO_KEEPALIVE,
+			               (SOCK_OPT_TYPE)&on,
+			               sizeof(on))
+			    != 0) {
+				mg_cry_ctx_internal(
+				    ctx,
+				    "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
+				    __func__,
+				    strerror(ERRNO));
+			}
 		}
 #endif
 
@@ -19179,7 +19312,7 @@ accept_new_connection(const struct socket *listener, struct mg_context *ctx)
 		 */
 		if ((ctx->dd.config[CONFIG_TCP_NODELAY] != NULL)
 		    && (!strcmp(ctx->dd.config[CONFIG_TCP_NODELAY], "1"))) {
-			if (set_tcp_nodelay(so.sock, 1) != 0) {
+			if (set_tcp_nodelay(&so, 1) != 0) {
 				mg_cry_ctx_internal(
 				    ctx,
 				    "%s: setsockopt(IPPROTO_TCP TCP_NODELAY) failed: %s",
@@ -20681,7 +20814,7 @@ mg_get_system_info(char *buffer, int buflen)
 #pragma GCC diagnostic ignored "-Wdate-time"
 #endif
 #endif
-#ifdef BUILD_DATE
+#if defined(BUILD_DATE)
 		const char *bd = BUILD_DATE;
 #else
 		const char *bd = __DATE__;

+ 1 - 1
src/main.c

@@ -295,7 +295,7 @@ static int MakeConsole(void);
 static void
 show_server_name(void)
 {
-#ifdef BUILD_DATE
+#if defined(BUILD_DATE)
 	const char *bd = BUILD_DATE;
 #else
 	const char *bd = __DATE__;