Commit 38e37d9f authored by Vladimír Čunát's avatar Vladimír Čunát

daemon: support AF_UNIX sockets in the http module

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
parent 81d78f8f
......@@ -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
--------
......
......@@ -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
......
......@@ -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) {
......
......@@ -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)
......
......@@ -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;
......
......@@ -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;
......
......@@ -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)],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment