diff --git a/lib/socket.h b/lib/socket.h index 5c69482e..dd9004c2 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -44,7 +44,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 */ @@ -98,6 +98,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; } @@ -111,6 +112,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 23baffb2..e847ef11 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -536,6 +536,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 @@ -866,7 +881,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], @@ -923,6 +938,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; @@ -1045,6 +1066,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) @@ -1070,6 +1099,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; @@ -1327,6 +1357,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) { @@ -1399,6 +1432,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); } @@ -1439,8 +1481,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"); } @@ -1464,6 +1519,21 @@ sk_open(sock *s) ERR2("listen"); 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; @@ -1483,38 +1553,6 @@ err: return -1; } -int -sk_open_unix(sock *s, 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; -} - static void sk_reloop_hook(void *_vs) { @@ -1971,6 +2009,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: { @@ -2017,6 +2069,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 bf9f2be0..f97bca74 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -515,6 +515,7 @@ cli_init_unix(uid_t use_uid, gid_t use_gid) cli_init(); s = cli_sk = sk_new(cli_pool); s->type = SK_UNIX_PASSIVE; + s->host = path_control_socket; s->rx_hook = cli_connect; s->err_hook = cli_connect_err; s->rbsize = 1024; @@ -523,7 +524,7 @@ cli_init_unix(uid_t use_uid, gid_t use_gid) /* Return value intentionally ignored */ unlink(path_control_socket); - if (sk_open_unix(s, path_control_socket) < 0) + if (sk_open(s) < 0) die("Cannot create control socket %s: %m", path_control_socket); if (use_uid || use_gid) diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index ad85d1ea..1ab0b079 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, char *name); struct rfile *rf_open(struct pool *, const char *name, const char *mode); void *rf_file(struct rfile *f); int rf_fileno(struct rfile *f);