From ad23b465f859b84d1d2af78bc2dee570b32ca146 Mon Sep 17 00:00:00 2001 From: Vojtech Vilimek Date: Wed, 17 Jul 2024 12:05:13 +0200 Subject: [PATCH] IO: Changes in socket API Support for active UNIX sockets is added. UNIX socket are now created with sk_open. The socket name/path is passes in host, the same way as SSH address. For passive UNIX socket the filesystem entry is considered as part of the resource and hence is unlinked in rfree. --- lib/socket.h | 17 ++++-- sysdep/unix/io.c | 129 +++++++++++++++++++++++++++++++++------------ sysdep/unix/main.c | 6 +-- sysdep/unix/unix.h | 2 - 4 files changed, 109 insertions(+), 45 deletions(-) diff --git a/lib/socket.h b/lib/socket.h index 3417423f..2802dd5c 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -43,7 +43,7 @@ typedef struct birdsock { int subtype; /* Socket subtype */ void *data; /* User data */ ip_addr saddr, daddr; /* IPA_NONE = unspecified */ - const char *host; /* Alternative to daddr, NULL = unspecified */ + const char *host; /* Alternative to daddr especially for UNIX sockets, NULL = unspecified */ uint sport, dport; /* 0 = unspecified (for IP: protocol type) */ int tos; /* TOS / traffic class, -1 = default */ int priority; /* Local socket priority, -1 = default */ @@ -96,6 +96,7 @@ void sk_dump_all(void); int sk_is_ipv4(sock *s); /* True if socket is IPv4 */ int sk_is_ipv6(sock *s); /* True if socket is IPv6 */ +int sk_is_unix(sock *s); /* True if socket is UNIX socket */ static inline int sk_tx_buffer_empty(sock *sk) { return sk->tbuf == sk->tpos; } @@ -109,6 +110,7 @@ int sk_set_min_ttl(sock *s, int ttl); /* Set minimal accepted TTL for given sock int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int setkey); int sk_set_ipv6_checksum(sock *s, int offset); int sk_set_icmp6_filter(sock *s, int p1, int p2); +int sk_check_unix(const char *path); void sk_log_error(sock *s, const char *p); byte * sk_rx_buffer(sock *s, int *len); /* Temporary */ @@ -143,10 +145,11 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou #define SK_UDP 3 /* ? ? ? ? ? ? ? */ #define SK_IP 5 /* ? - ? * ? ? ? */ #define SK_MAGIC 7 /* Internal use by sysdep code */ -#define SK_UNIX_PASSIVE 8 -#define SK_UNIX 9 -#define SK_SSH_ACTIVE 10 /* - - * * - ? - DA = host */ -#define SK_SSH 11 +#define SK_UNIX_PASSIVE 8 /* - - * - - - - DA = host */ +#define SK_UNIX_ACTIVE 9 /* - - * - - - - DA = host */ +#define SK_UNIX 10 +#define SK_SSH_ACTIVE 11 /* - - * * - ? - DA = host */ +#define SK_SSH 12 /* * Socket subtypes @@ -177,6 +180,10 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou * per-packet basis using platform dependent options (but these are not * available in some corner cases). The first way is used when SKF_BIND is * specified, the second way is used otherwise. + * + * For UNIX sockets (SK_UNIX_PASSIVE, SK_UNIX_ACTIVE), path is passed in host + * string. If the path does not fit into the sockaddr_un sun_path array, the + * sk_open will fail. One can check path length with sk_check_unix. */ #endif diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index ba2e1661..b2812ee9 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -566,6 +566,21 @@ sk_setup_multicast(sock *s) return sk_setup_multicast6(s); } +/** + * sk_check_unix - check path length + * @path: filesystem path + * + * Check if path fits into the sockaddr_un sun_path array. + * + * Result: 0 for success, -1 for an error. + */ + +int +sk_check_unix(const char *path) +{ + return -(strlen(path) >= sizeof(((struct sockaddr_un *)0)->sun_path)); +} + /** * sk_join_group - join multicast group for given socket * @s: socket @@ -831,6 +846,10 @@ sk_free(resource *r) sk_ssh_free(s); #endif + if (s->host && s->type == SK_UNIX_PASSIVE) + /* Return value intentionally ignored */ + (void) unlink(s->host); + if (s->fd < 0) return; @@ -898,7 +917,7 @@ static void sk_dump(resource *r) { sock *s = (sock *) r; - static char *sk_type_names[] = { "TCP<", "TCP>", "TCP", "UDP", NULL, "IP", NULL, "MAGIC", "UNIX<", "UNIX", "SSH>", "SSH", "DEL!" }; + static char *sk_type_names[] = { "TCP<", "TCP>", "TCP", "UDP", NULL, "IP", NULL, "MAGIC", "UNIX<", "UNIX>", "UNIX", "SSH>", "SSH", "DEL!" }; debug("(%s, ud=%p, sa=%I, sp=%d, da=%I, dp=%d, tos=%d, ttl=%d, if=%s)\n", sk_type_names[s->type], @@ -955,6 +974,12 @@ sk_setup(sock *s) if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) ERR("O_NONBLOCK"); + if (s->type == SK_UNIX_ACTIVE || s->type == SK_UNIX_PASSIVE) + return sk_check_unix(s->host); + + if (s->type == SK_UNIX) + return 0; + if (!s->af) return 0; @@ -1089,6 +1114,14 @@ sk_tcp_connected(sock *s) s->tx_hook(s); } +static void +sk_unix_connected(sock *s) +{ + s->type = SK_UNIX; + sk_alloc_bufs(s); + s->tx_hook(s); +} + #ifdef HAVE_LIBSSH static void sk_ssh_connected(sock *s) @@ -1114,6 +1147,7 @@ sk_passive_connected(sock *s, int type) return 0; } + /* Do not copy UNIX address in s->host. */ sock *t = sk_new(s->pool); t->type = type; t->data = s->data; @@ -1367,6 +1401,9 @@ sk_open(sock *s) int bind_port = 0; ip_addr bind_addr = IPA_NONE; sockaddr sa; + struct sockaddr_un un; + struct sockaddr *addr; + socklen_t len; if (s->type <= SK_IP) { @@ -1439,6 +1476,15 @@ sk_open(sock *s) fd = s->fd; break; + case SK_UNIX_ACTIVE: + s->ttx = ""; + /* Fall thru */ + case SK_UNIX_PASSIVE: + af = AF_UNIX; + fd = socket(af, SOCK_STREAM, 0); + do_bind = s->type == SK_UNIX_PASSIVE; + break; + default: bug("sk_open() called for invalid sock type %d", s->type); } @@ -1479,8 +1525,21 @@ sk_open(sock *s) if (sk_set_freebind(s) < 0) log(L_WARN "Socket error: %s%#m", s->err); - sockaddr_fill(&sa, s->af, bind_addr, s->iface, bind_port); - if (bind(fd, &sa.sa, SA_LEN(sa)) < 0) + if (s->type == SK_UNIX_PASSIVE) + { + un.sun_family = AF_UNIX; + strcpy(un.sun_path, s->host); + addr = (struct sockaddr *) &un; + len = SUN_LEN(&un); + } + else + { + sockaddr_fill(&sa, s->af, bind_addr, s->iface, bind_port); + addr = &sa.sa; + len = SA_LEN(sa); + } + + if (bind(fd, addr, len) < 0) ERR2("bind"); } @@ -1511,6 +1570,21 @@ sk_open(sock *s) sk_alloc_bufs(s); break; + case SK_UNIX_ACTIVE: + un.sun_family = AF_UNIX; + strcpy(un.sun_path, s->host); + + if (connect(s->fd, (struct sockaddr *) &un, SUN_LEN(&un)) >= 0) + sk_unix_connected(s); + else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS) + ERR2("connect"); + break; + + case SK_UNIX_PASSIVE: + if (listen(fd, 8) < 0) + ERR2("listen"); + break; + case SK_SSH_ACTIVE: case SK_MAGIC: break; @@ -1530,38 +1604,6 @@ err: return -1; } -int -sk_open_unix(sock *s, const char *name) -{ - struct sockaddr_un sa; - int fd; - - /* We are sloppy during error (leak fd and not set s->err), but we die anyway */ - - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) - return -1; - - if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) - return -1; - - /* Path length checked in test_old_bird() but we may need unix sockets for other reasons in future */ - ASSERT_DIE(strlen(name) < sizeof(sa.sun_path)); - - sa.sun_family = AF_UNIX; - strcpy(sa.sun_path, name); - - if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) - return -1; - - if (listen(fd, 8) < 0) - return -1; - - s->fd = fd; - sk_insert(s); - return 0; -} - #define CMSG_RX_SPACE MAX(CMSG4_SPACE_PKTINFO+CMSG4_SPACE_TTL, \ CMSG6_SPACE_PKTINFO+CMSG6_SPACE_TTL) @@ -1985,6 +2027,20 @@ sk_write_noflush(sock *s) return 0; } + case SK_UNIX_ACTIVE: + { + struct sockaddr_un un; + un.sun_family = AF_UNIX; + strcpy(un.sun_path, s->host); + + if (connect(s->fd, (struct sockaddr *) &un, SUN_LEN(&un)) >= 0 || errno == EISCONN) + sk_unix_connected(s); + else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS) + s->err_hook(s, errno); + + return 0; + } + #ifdef HAVE_LIBSSH case SK_SSH_ACTIVE: { @@ -2031,6 +2087,9 @@ int sk_is_ipv4(sock *s) int sk_is_ipv6(sock *s) { return s->af == AF_INET6; } +int sk_is_unix(sock *s) +{ return s->af == AF_UNIX; } + void sk_err(sock *s, int revents) { diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 880cc3c4..f8acb5c4 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -553,6 +553,7 @@ cli_listen(struct cli_config *cf) l->config = cf; sock *s = l->s = sk_new(cli_pool); s->type = SK_UNIX_PASSIVE; + s->host = cf->name; s->rx_hook = cli_connect; s->err_hook = cli_connect_err; s->data = l; @@ -560,9 +561,9 @@ cli_listen(struct cli_config *cf) s->fast_rx = 1; /* Return value intentionally ignored */ - unlink(cf->name); + (void) unlink(cf->name); - if (sk_open_unix(s, cf->name) < 0) + if (sk_open(s) < 0) { log(L_ERR "Cannot create control socket %s: %m", cf->name); return NULL; @@ -590,7 +591,6 @@ static void cli_deafen(struct cli_listener *l) { rfree(l->s); - unlink(l->config->name); cli_listener_rem_node(&cli_listeners, l); mb_free(l); } diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index 33ece06c..679c3d93 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -53,7 +53,6 @@ typedef struct sockaddr_bird { } sockaddr; - /* This is sloppy hack, it should be detected by configure script */ /* Linux systems have it defined so this is definition for BSD systems */ #ifndef s6_addr32 @@ -107,7 +106,6 @@ extern volatile sig_atomic_t async_shutdown_flag; void io_init(void); void io_loop(void); void io_log_dump(void); -int sk_open_unix(struct birdsock *s, const char *name); struct rfile *rf_open(struct pool *, const char *name, const char *mode); struct rfile *rf_fdopen(pool *p, int fd, const char *mode); void *rf_file(struct rfile *f);