]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon: support AF_UNIX sockets in the http module
authorVladimír Čunát <vladimir.cunat@nic.cz>
Tue, 23 Apr 2019 12:23:43 +0000 (14:23 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Mon, 3 Jun 2019 13:43:04 +0000 (15:43 +0200)
There's no support for the "dns" and "tls" kinds (for now).

Tested briefly:
> modules = { 'http' }
> net.listen('/tmp/kresd-socket', nil, { kind = 'webmgmt' })
$ socat TCP-LISTEN:5555,reuseaddr,fork UNIX-CLIENT:/tmp/kresd-socket
$ xdg-open http://127.0.0.1:5555

NEWS
daemon/io.c
daemon/main.c
daemon/network.c
daemon/network.h
lib/utils.c
lib/utils.h

diff --git a/NEWS b/NEWS
index 3cfd78dba21c45d9e73980e4a8f01db7d083d089..8db7980fd8409ed77491e5bc3d48c67b3f5efff3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@ Knot Resolver 4.x.y (2019-0m-dd)
 Improvements
 ------------
 - DNS-over-HTTPS: answers include `access-control-allow-origin: *` (!823)
+- support named AF_UNIX stream sockets for the http module (again)
 
 Bugfixes
 --------
index 296804ac2dddd023eed73061a15e4b18ffe1e5ae..6a7ae9589e13822ac3639dd8988bb1f915c3f079 100644 (file)
@@ -373,7 +373,7 @@ int io_listen_tcp(uv_loop_t *loop, uv_tcp_t *handle, int fd, int tcp_backlog, bo
 #ifdef TCP_DEFER_ACCEPT
        val = KR_CONN_RTT_MAX/1000;
        if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val))) {
-               kr_log_error("[ io ] tcp_bind (defer_accept): %s\n", strerror(errno));
+               kr_log_error("[ io ] listen TCP (defer_accept): %s\n", strerror(errno));
        }
 #endif
 
@@ -390,7 +390,7 @@ int io_listen_tcp(uv_loop_t *loop, uv_tcp_t *handle, int fd, int tcp_backlog, bo
        val = 1; /* Accepts on/off */
        #endif
        if (setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &val, sizeof(val))) {
-               kr_log_error("[ io ] tcp_bind (fastopen): %s\n", strerror(errno));
+               kr_log_error("[ io ] listen TCP (fastopen): %s\n", strerror(errno));
        }
 #endif
 
index 8f0e84c7bc1171be013adb3a81cb61bbf0e7c4e0..54b8d83c1b99bcd25270e98be2a13e9f75782f63 100644 (file)
@@ -586,28 +586,42 @@ static int parse_args(int argc, char **argv, struct args *args)
 }
 
 /** Just convert addresses to file-descriptors; clear *addrs on success.
+ * @note AF_UNIX is supported (starting with '/').
  * @return zero or exit code for main()
  */
 static int bind_sockets(addr_array_t *addrs, bool tls, flagged_fd_array_t *fds)
 {
+       bool has_error = false;
        for (size_t i = 0; i < addrs->len; ++i) {
+               /* Get port and separate address string. */
                uint16_t port = tls ? KR_DNS_TLS_PORT : KR_DNS_PORT;
-               char addr_str[INET6_ADDRSTRLEN + 1];
-               int ret = kr_straddr_split(addrs->at[i], addr_str, &port);
+               char addr_buf[INET6_ADDRSTRLEN + 1];
+               int ret;
+               const char *addr_str;
+               const int family = kr_straddr_family(addrs->at[i]);
+               if (family == AF_UNIX) {
+                       ret = 0;
+                       addr_str = addrs->at[i];
+               } else { /* internet socket (or garbage) */
+                       ret = kr_straddr_split(addrs->at[i], addr_buf, &port);
+                       addr_str = addr_buf;
+               }
+               /* Get sockaddr. */
                struct sockaddr *sa = NULL;
                if (ret == 0) {
                        sa = kr_straddr_socket(addr_str, port, NULL);
                        if (!sa) ret = kr_error(EINVAL); /* could be ENOMEM but unlikely */
                }
                flagged_fd_t ffd = { .flags = { .tls = tls } };
-               if (ret == 0 && !tls) {
+               if (ret == 0 && !tls && family != AF_UNIX) {
+                       /* AF_UNIX can do SOCK_DGRAM, but let's not support that *here*. */
                        ffd.fd = io_bind(sa, SOCK_DGRAM);
                        if (ffd.fd < 0)
                                ret = ffd.fd;
                        else if (array_push(*fds, ffd) < 0)
                                ret = kr_error(ENOMEM);
                }
-               if (ret == 0) { /* common for TCP and TLS */
+               if (ret == 0) { /* common for TCP and TLS, including AF_UNIX cases */
                        ffd.fd = io_bind(sa, SOCK_STREAM);
                        if (ffd.fd < 0)
                                ret = ffd.fd;
@@ -616,13 +630,13 @@ static int bind_sockets(addr_array_t *addrs, bool tls, flagged_fd_array_t *fds)
                }
                free(sa);
                if (ret != 0) {
-                       kr_log_error("[system] bind to '%s' %s%s\n",
-                               addrs->at[i], tls ? "(TLS) " : "", kr_strerror(ret));
-                       return EXIT_FAILURE;
+                       kr_log_error("[system] bind to '%s'%s: %s\n",
+                               addrs->at[i], tls ? " (TLS)" : "", kr_strerror(ret));
+                       has_error = true;
                }
        }
        array_clear(*addrs);
-       return kr_ok();
+       return has_error ? EXIT_FAILURE : kr_ok();
 }
 
 static int start_listening(struct network *net, flagged_fd_array_t *fds) {
index d1a87035022d47806f5de8cf5a3d5df0da2fa915..509fc40acab35a7f448f16e5a125af8e74ad119d 100644 (file)
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-#include <unistd.h>
-#include <assert.h>
-#include "daemon/bindings/impl.h"
 #include "daemon/network.h"
-#include "daemon/worker.h"
+
+#include "daemon/bindings/impl.h"
 #include "daemon/io.h"
 #include "daemon/tls.h"
+#include "daemon/worker.h"
+
+#include <assert.h>
+#include <sys/un.h>
+#include <unistd.h>
 
 void network_init(struct network *net, uv_loop_t *loop, int tcp_backlog)
 {
@@ -137,6 +140,19 @@ static void endpoint_close_lua_cb(struct network *net, struct endpoint *ep)
 static void endpoint_close(struct network *net, struct endpoint *ep, bool force)
 {
        assert(!ep->handle != !ep->flags.kind);
+       if (ep->family == AF_UNIX) { /* The FS name would be left behind. */
+               /* Extract local address for this socket. */
+               struct sockaddr_un sa;
+               sa.sun_path[0] = '\0'; /*< probably only for lint:scan-build */
+               socklen_t addr_len = sizeof(sa);
+               if (getsockname(ep->fd, (struct sockaddr *)&sa, &addr_len)
+                   || unlink(sa.sun_path)) {
+                       kr_log_error("error (ignored) when closing unix socket (fd = %d): %s\n",
+                                       ep->fd, strerror(errno));
+                       return;
+               }
+       }
+
        if (ep->flags.kind) { /* Special endpoint. */
                if (ep->engaged) {
                        endpoint_close_lua_cb(net, ep);
@@ -260,6 +276,16 @@ static int open_endpoint(struct network *net, struct endpoint *ep,
                /* .engaged seems not really meaningful with .kind == NULL, but... */
        }
 
+       if (ep->family == AF_UNIX) {
+               /* Some parts of connection handling would need more work,
+                * so let's support AF_UNIX only with .kind != NULL for now. */
+               kr_log_error("[system] AF_UNIX only supported with set { kind = '...' }\n");
+               return kr_error(EAFNOSUPPORT);
+               /*
+               uv_pipe_t *ep_handle = malloc(sizeof(uv_pipe_t));
+               */
+       }
+
        if (ep->flags.sock_type == SOCK_DGRAM) {
                if (ep->flags.tls) {
                        assert(!EINVAL);
@@ -310,7 +336,6 @@ static struct endpoint * endpoint_get(struct network *net, const char *addr,
 static int create_endpoint(struct network *net, const char *addr_str,
                                struct endpoint *ep, const struct sockaddr *sa)
 {
-       /* Bind interfaces */
        int ret = open_endpoint(net, ep, sa, addr_str);
        if (ret == 0) {
                ret = insert_endpoint(net, addr_str, ep);
@@ -351,16 +376,28 @@ int network_listen_fd(struct network *net, int fd, endpoint_flags_t flags)
                .fd = fd,
        };
        /* Extract address string and port. */
-       char addr_str[INET6_ADDRSTRLEN]; /* https://tools.ietf.org/html/rfc4291 */
-       if (ss.ss_family == AF_INET) {
-               uv_ip4_name((const struct sockaddr_in*)&ss, addr_str, sizeof(addr_str));
+       char addr_buf[INET6_ADDRSTRLEN]; /* https://tools.ietf.org/html/rfc4291 */
+       const char *addr_str;
+       switch (ep.family) {
+       case AF_INET:
+               ret = uv_ip4_name((const struct sockaddr_in*)&ss, addr_buf, sizeof(addr_buf));
+               addr_str = addr_buf;
                ep.port = ntohs(((struct sockaddr_in *)&ss)->sin_port);
-       } else if (ss.ss_family == AF_INET6) {
-               uv_ip6_name((const struct sockaddr_in6*)&ss, addr_str, sizeof(addr_str));
+               break;
+       case AF_INET6:
+               ret = uv_ip6_name((const struct sockaddr_in6*)&ss, addr_buf, sizeof(addr_buf));
+               addr_str = addr_buf;
                ep.port = ntohs(((struct sockaddr_in6 *)&ss)->sin6_port);
-       } else {
-               return kr_error(EAFNOSUPPORT);
+               break;
+       case AF_UNIX:
+               /* No SOCK_DGRAM with AF_UNIX support, at least for now. */
+               ret = flags.sock_type == SOCK_STREAM ? kr_ok() : kr_error(EAFNOSUPPORT);
+               addr_str = ((struct sockaddr_un *)&ss)->sun_path;
+               break;
+       default:
+               ret = kr_error(EAFNOSUPPORT);
        }
+       if (ret) return ret;
 
        /* always create endpoint for supervisor supplied fd
         * even if addr+port is not unique */
@@ -379,23 +416,19 @@ int network_listen(struct network *net, const char *addr, uint16_t port,
        }
 
        /* Parse address. */
-       int ret = 0;
-       union inaddr sa;
-       if (strchr(addr, ':') != NULL) {
-               ret = uv_ip6_addr(addr, port, &sa.ip6);
-       } else {
-               ret = uv_ip4_addr(addr, port, &sa.ip4);
-       }
-       if (ret != 0) {
-               return ret;
+       const struct sockaddr *sa = kr_straddr_socket(addr, port, NULL);
+       if (!sa) {
+               return kr_error(EINVAL);
        }
        struct endpoint ep = {
                .flags = flags,
                .fd = -1,
                .port = port,
-               .family = sa.ip.sa_family,
+               .family = sa->sa_family,
        };
-       return create_endpoint(net, addr, &ep, &sa.ip);
+       int ret = create_endpoint(net, addr, &ep, sa);
+       free_const(sa);
+       return ret;
 }
 
 int network_close(struct network *net, const char *addr, int port)
index 4832f4ceb1af05fbce22ce95c7f44fa40b992757..219011aa25269cd725a8a6edbb8552ac8136710f 100644 (file)
@@ -49,11 +49,12 @@ static inline bool endpoint_flags_eq(endpoint_flags_t f1, endpoint_flags_t f2)
  * There are two types: normal have handle, special have flags.kind (and never both).
  *
  * LATER: .family might be unexpected for IPv4-in-IPv6 addresses.
+ * ATM AF_UNIX is only supported with flags.kind != NULL
  */
 struct endpoint {
        uv_handle_t *handle; /**< uv_udp_t or uv_tcp_t; NULL in case flags.kind != NULL */
        int fd;              /**< POSIX file-descriptor; always used. */
-       int family;          /**< AF_INET or AF_INET6 or (in future) AF_UNIX */
+       int family;          /**< AF_INET or AF_INET6 or AF_UNIX */
        uint16_t port;       /**< TCP/UDP port.  Meaningless with AF_UNIX. */
        bool engaged;        /**< to some module or internally */
        endpoint_flags_t flags;
index 603758e9dbf73e9ed2f4f44dac392ead50bba88b..780805ea831b0d0eb21f8bcbf79c018d0727c06c 100644 (file)
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-#include <stdarg.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <arpa/inet.h>
-#include <sys/time.h>
-#include <contrib/cleanup.h>
-#include <contrib/ccan/asprintf/asprintf.h>
-#include <ucw/mempool.h>
-#include <gnutls/gnutls.h>
-#include <libknot/descriptor.h>
-#include <libknot/dname.h>
-#include <libknot/rrtype/rrsig.h>
-#include <libknot/rrset-dump.h>
-#include <libknot/version.h>
-#include <uv.h>
+#include "lib/utils.h"
 
+#include "contrib/ccan/asprintf/asprintf.h"
+#include "contrib/cleanup.h"
+#include "contrib/ucw/mempool.h"
 #include "kresconfig.h"
 #include "lib/defines.h"
-#include "lib/utils.h"
 #include "lib/generic/array.h"
-#include "lib/nsrep.h"
 #include "lib/module.h"
+#include "lib/nsrep.h"
 #include "lib/resolve.h"
 
+#include <gnutls/gnutls.h>
+#include <libknot/descriptor.h>
+#include <libknot/dname.h>
+#include <libknot/rrset-dump.h>
+#include <libknot/rrtype/rrsig.h>
+#include <libknot/version.h>
+#include <uv.h>
+
+#include <arpa/inet.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/un.h>
 
 /* Always compile-in log symbols, even if disabled. */
 #undef kr_verbose_status
@@ -343,6 +345,7 @@ int kr_sockaddr_len(const struct sockaddr *addr)
        switch (addr->sa_family) {
        case AF_INET:  return sizeof(struct sockaddr_in);
        case AF_INET6: return sizeof(struct sockaddr_in6);
+       case AF_UNIX:  return sizeof(struct sockaddr_un);
        default:       return kr_error(EINVAL);
        }
 }
@@ -440,6 +443,9 @@ int kr_straddr_family(const char *addr)
        if (!addr) {
                return kr_error(EINVAL);
        }
+       if (addr[0] == '/') {
+               return AF_UNIX;
+       }
        if (strchr(addr, ':')) {
                return AF_INET6;
        }
@@ -476,6 +482,17 @@ struct sockaddr * kr_straddr_socket(const char *addr, int port, knot_mm_t *pool)
                        return NULL;
                }
        }
+       case AF_UNIX: {
+               struct sockaddr_un *res;
+               const size_t alen = strlen(addr) + 1;
+               if (alen > sizeof(res->sun_path)) {
+                       return NULL;
+               }
+               res = mm_alloc(pool, sizeof(*res));
+               res->sun_family = AF_UNIX;
+               memcpy(res->sun_path, addr, alen);
+               return (struct sockaddr *)res;
+       }
        default:
                assert(!EINVAL);
                return NULL;
index 4d8530aa7dd3cca05c5d2042bb5af14b7474749f..5b376c6e81c08779736fe8a4eee6df8a6639e9c0 100644 (file)
@@ -346,7 +346,8 @@ int kr_straddr_family(const char *addr);
 KR_EXPORT KR_CONST
 int kr_family_len(int family);
 
-/** Create a sockaddr* from string+port representation (also accepts IPv6 link-local). */
+/** Create a sockaddr* from string+port representation.
+ * Also accepts IPv6 link-local and AF_UNIX starting with "/" (ignoring port) */
 KR_EXPORT
 struct sockaddr * kr_straddr_socket(const char *addr, int port, knot_mm_t *pool);
 
@@ -362,6 +363,7 @@ int kr_straddr_subnet(void *dst, const char *addr);
  * \param port[out] written in case it's specified in instr
  * \return error code
  * \note Typically you follow this by kr_straddr_socket().
+ * \note Only internet addresses are supported, i.e. no AF_UNIX sockets.
  */
 KR_EXPORT
 int kr_straddr_split(const char *instr, char ipaddr[static restrict (INET6_ADDRSTRLEN + 1)],