]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon/network: add freebind support
authorTomas Krizek <tomas.krizek@nic.cz>
Fri, 29 Nov 2019 14:24:17 +0000 (15:24 +0100)
committerPetr Špaček <petr.spacek@nic.cz>
Fri, 20 Dec 2019 14:13:18 +0000 (15:13 +0100)
daemon/bindings/net.c
daemon/io.c
daemon/io.h
daemon/lua/kres-gen.lua
daemon/main.c
daemon/network.c
daemon/network.h

index de7d308385fbe93351e63738fae930aecbc9bf33..34a329c2ba505d70aced8661d12185eb97662a8f 100644 (file)
@@ -107,7 +107,7 @@ static int net_list(lua_State *L)
 /** Listen on an address list represented by the top of lua stack.
  * \note kind ownership is not transferred
  * \return success */
-static bool net_listen_addrs(lua_State *L, int port, bool tls, const char *kind)
+static bool net_listen_addrs(lua_State *L, int port, bool tls, const char *kind, bool freebind)
 {
        /* Case: table with 'addr' field; only follow that field directly. */
        lua_getfield(L, -1, "addr");
@@ -122,7 +122,7 @@ static bool net_listen_addrs(lua_State *L, int port, bool tls, const char *kind)
        if (str != NULL) {
                struct engine *engine = engine_luaget(L);
                int ret = 0;
-               endpoint_flags_t flags = { .tls = tls };
+               endpoint_flags_t flags = { .tls = tls, .freebind = freebind };
                if (!kind && !flags.tls) { /* normal UDP */
                        flags.sock_type = SOCK_DGRAM;
                        ret = network_listen(&engine->net, str, port, flags);
@@ -149,7 +149,7 @@ static bool net_listen_addrs(lua_State *L, int port, bool tls, const char *kind)
                lua_error_p(L, "bad type for address");
        lua_pushnil(L);
        while (lua_next(L, -2)) {
-               if (!net_listen_addrs(L, port, tls, kind))
+               if (!net_listen_addrs(L, port, tls, kind, freebind))
                        return false;
                lua_pop(L, 1);
        }
@@ -189,11 +189,13 @@ static int net_listen(lua_State *L)
        }
 
        bool tls = (port == KR_DNS_TLS_PORT);
+       bool freebind = false;
        const char *kind = NULL;
        if (n > 2 && !lua_isnil(L, 3)) {
                if (!lua_istable(L, 3))
                        lua_error_p(L, "wrong type of third parameter (table expected)");
                tls = table_get_flag(L, 3, "tls", tls);
+               freebind = table_get_flag(L, 3, "freebind", tls);
 
                lua_getfield(L, 3, "kind");
                const char *k = lua_tostring(L, -1);
@@ -219,7 +221,7 @@ static int net_listen(lua_State *L)
 
        /* Now focus on the first argument. */
        lua_settop(L, 1);
-       const bool res = net_listen_addrs(L, port, tls, kind);
+       const bool res = net_listen_addrs(L, port, tls, kind, freebind);
        lua_pushboolean(L, res);
        return 1;
 }
index bca3afe4559d89e8ce88d4d3f40eaff1c45d7657..be1e264bec1c9e89f71dae243fd5d6e91f97a7d0 100644 (file)
@@ -104,7 +104,37 @@ void udp_recv(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf,
        mp_flush(worker->pkt_pool.ctx);
 }
 
-int io_bind(const struct sockaddr *addr, int type)
+int freebind_option(const struct sockaddr *addr, int *level, int *name)
+{
+       switch (addr->sa_family) {
+       case AF_INET:
+               *level = IPPROTO_IP;
+#if defined(IP_FREEBIND)
+               *name = IP_FREEBIND;
+#elif defined(IP_BINDANY)
+               *name = IP_BINDANY;
+#else
+               return kr_error(ENOTSUP);
+#endif
+               break;
+       case AF_INET6:
+#if defined(IP_FREEBIND)
+               *level = IPPROTO_IP;
+               *name = IP_FREEBIND;
+#elif defined(IPV6_BINDANY)
+               *level = IPPROTO_IPV6;
+               *name = IPV6_BINDANY;
+#else
+               return kr_error(ENOTSUP);
+#endif
+               break;
+       default:
+               return kr_error(ENOTSUP);
+       }
+       return kr_ok();
+}
+
+int io_bind(const struct sockaddr *addr, int type, const endpoint_flags_t *flags)
 {
        const int fd = socket(addr->sa_family, type, 0);
        if (fd < 0) return kr_error(errno);
@@ -126,6 +156,14 @@ int io_bind(const struct sockaddr *addr, int type)
            && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)))
                return kr_error(errno);
 #endif
+       if (flags != NULL && flags->freebind) {
+               int optlevel;
+               int optname;
+               int ret = freebind_option(addr, &optlevel, &optname);
+               if (ret) return kr_error(ret);
+               if (setsockopt(fd, optlevel, optname, &yes, sizeof(yes)))
+                       return kr_error(errno);
+       }
 
        if (bind(fd, addr, kr_sockaddr_len(addr)))
                return kr_error(errno);
@@ -225,7 +263,7 @@ static void tcp_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf)
 {
        struct session *s = handle->data;
        assert(s && session_get_handle(s) == (uv_handle_t *)handle &&
-              handle->type == UV_TCP); 
+              handle->type == UV_TCP);
 
        if (session_flags(s)->closing) {
                return;
index 711194fb1c4f5291e363af19f9c137dbfe310cf6..b8d39dd983179777c5e4df3ebcfc6c43710fc3f9 100644 (file)
@@ -26,7 +26,7 @@ struct tls_ctx_t;
 struct tls_client_ctx_t;
 
 /** Bind address into a file-descriptor (only, no libuv).  type is e.g. SOCK_DGRAM */
-int io_bind(const struct sockaddr *addr, int type);
+int io_bind(const struct sockaddr *addr, int type, const endpoint_flags_t *flags);
 /** Initialize a UDP handle and start listening. */
 int io_listen_udp(uv_loop_t *loop, uv_udp_t *handle, int fd);
 /** Initialize a TCP handle and start listening. */
index 3afd239e03bcc8dbe5a4bbe6da01100861a64896..311b3007e76757e1ccd702ff019303726e48f8f1 100644 (file)
@@ -384,6 +384,7 @@ typedef struct {
        int sock_type;
        _Bool tls;
        const char *kind;
+       _Bool freebind;
 } endpoint_flags_t;
 struct endpoint {
        void *handle;
index bfc1b44eb4b7b81e150091990dc99958211e6275..94ef69bdf4907b192ce20c7bca3f2d40e8e30080 100644 (file)
@@ -628,14 +628,14 @@ static int bind_sockets(addr_array_t *addrs, bool tls, flagged_fd_array_t *fds)
                flagged_fd_t ffd = { .flags = { .tls = 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);
+                       ffd.fd = io_bind(sa, SOCK_DGRAM, NULL);
                        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, including AF_UNIX cases */
-                       ffd.fd = io_bind(sa, SOCK_STREAM);
+                       ffd.fd = io_bind(sa, SOCK_STREAM, NULL);
                        if (ffd.fd < 0)
                                ret = ffd.fd;
                        else if (array_push(*fds, ffd) < 0)
index 62e1c4deeebd71ad7c1b052ceb10ec4618f954d8..d4f9fe363a7404d915f6a848fe261e8f07162e2e 100644 (file)
@@ -262,7 +262,7 @@ static int open_endpoint(struct network *net, struct endpoint *ep,
        }
 
        if (sa) {
-               ep->fd = io_bind(sa, ep->flags.sock_type);
+               ep->fd = io_bind(sa, ep->flags.sock_type, &ep->flags);
                if (ep->fd < 0) return ep->fd;
        }
        if (ep->flags.kind) {
index 219011aa25269cd725a8a6edbb8552ac8136710f..db56940eca19c227a10b0bcf28de715d9d6fcb76 100644 (file)
@@ -33,6 +33,7 @@ typedef struct {
        int sock_type;    /**< SOCK_DGRAM or SOCK_STREAM */
        bool tls;         /**< only used together with .kind == NULL and .tcp */
        const char *kind; /**< tag for other types than the three usual */
+       bool freebind;    /**< used for binding to non-local address **/
 } endpoint_flags_t;
 
 static inline bool endpoint_flags_eq(endpoint_flags_t f1, endpoint_flags_t f2)