diff --git a/NEWS b/NEWS index 3cfd78dba21c45d9e73980e4a8f01db7d083d089..8db7980fd8409ed77491e5bc3d48c67b3f5efff3 100644 --- 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 -------- diff --git a/daemon/io.c b/daemon/io.c index 296804ac2dddd023eed73061a15e4b18ffe1e5ae..6a7ae9589e13822ac3639dd8988bb1f915c3f079 100644 --- a/daemon/io.c +++ b/daemon/io.c @@ -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 diff --git a/daemon/main.c b/daemon/main.c index 8f0e84c7bc1171be013adb3a81cb61bbf0e7c4e0..54b8d83c1b99bcd25270e98be2a13e9f75782f63 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -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) { diff --git a/daemon/network.c b/daemon/network.c index d1a87035022d47806f5de8cf5a3d5df0da2fa915..509fc40acab35a7f448f16e5a125af8e74ad119d 100644 --- a/daemon/network.c +++ b/daemon/network.c @@ -14,13 +14,16 @@ 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) diff --git a/daemon/network.h b/daemon/network.h index 4832f4ceb1af05fbce22ce95c7f44fa40b992757..219011aa25269cd725a8a6edbb8552ac8136710f 100644 --- a/daemon/network.h +++ b/daemon/network.h @@ -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; diff --git a/lib/utils.c b/lib/utils.c index 603758e9dbf73e9ed2f4f44dac392ead50bba88b..780805ea831b0d0eb21f8bcbf79c018d0727c06c 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -14,31 +14,33 @@ 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; diff --git a/lib/utils.h b/lib/utils.h index 4d8530aa7dd3cca05c5d2042bb5af14b7474749f..5b376c6e81c08779736fe8a4eee6df8a6639e9c0 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -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)],