From: Dirk Jagdmann Date: Sun, 6 Mar 2022 21:26:31 -0800 Subject: Add abstract UNIX domain socket support When using '-U' to connect() or bind() to a UNIX domain socket, if the address (path) starts with "@", it is read as an abstract namespace socket (the leading "@" is replaced with a NUL byte before binding). This feature is Linux-only. Forwarded: not-needed --- nc.1 | 3 +++ netcat.c | 75 ++++++++++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 55 insertions(+), 23 deletions(-) diff --git a/nc.1 b/nc.1 index d30389a..8285c10 100644 --- a/nc.1 +++ b/nc.1 @@ -235,6 +235,9 @@ Cannot be used together with .Fl F or .Fl x . +On Linux, if the name starts with an at symbol (`@') it is read as an abstract +namespace socket: the leading `@' is replaced with a \fBNUL\fR byte +before binding or connecting. For details, see \fBunix\fR(7). .It Fl u Use UDP instead of TCP. Cannot be used together with diff --git a/netcat.c b/netcat.c index 061a774..2f8890b 100644 --- a/netcat.c +++ b/netcat.c @@ -98,6 +98,7 @@ #include #include #include +#include #include #include #include @@ -208,6 +209,7 @@ int timeout_connect(int, const struct sockaddr *, socklen_t); int socks_connect(const char *, const char *, struct addrinfo, const char *, const char *, struct addrinfo, int, const char *); int udptest(int); +int unix_setup_sockaddr(char *, struct sockaddr_un *, int *); void connection_info(const char *, const char *, const char *, const char *); int unix_bind(char *, int); int unix_connect(char *); @@ -931,6 +933,46 @@ main(int argc, char *argv[]) return ret; } +int +unix_setup_sockaddr(char *path, struct sockaddr_un *s_un, int *addrlen) +{ + int sun_path_len; + + *addrlen = offsetof(struct sockaddr_un, sun_path); + memset(s_un, 0, *addrlen); + s_un->sun_family = AF_UNIX; + + if (path[0] == '\0') { + /* Always reject the empty path, aka NUL abstract socket on + * Linux (OTOH the *empty* abstract socket is supported and + * specified as @""). */ + errno = EINVAL; + return -1; + } +#ifdef __linux__ + /* If the unix domain socket path starts with '@', + * treat it as a Linux abstract name. */ + else if (path[0] == '@') { + if ((sun_path_len = strlen(path)) <= sizeof(s_un->sun_path)) { + s_un->sun_path[0] = '\0'; + strncpy(s_un->sun_path+1, path+1, sun_path_len-1); + *addrlen += sun_path_len; + } else { + errno = ENAMETOOLONG; + return -1; + } + } +#endif + else if ((sun_path_len = strlcpy(s_un->sun_path, path, sizeof(s_un->sun_path))) < + sizeof(s_un->sun_path)) + *addrlen += sun_path_len + 1; /* account for trailing '\0' */ + else { + errno = ENAMETOOLONG; + return -1; + } + return 0; +} + /* * unix_bind() * Returns a unix socket bound to the given path @@ -939,24 +981,17 @@ int unix_bind(char *path, int flags) { struct sockaddr_un s_un; - int s, save_errno; + int s, save_errno, addrlen; + + if (unix_setup_sockaddr(path, &s_un, &addrlen) == -1) + return -1; /* Create unix domain socket. */ if ((s = socket(AF_UNIX, flags | (uflag ? SOCK_DGRAM : SOCK_STREAM), 0)) == -1) return -1; - memset(&s_un, 0, sizeof(struct sockaddr_un)); - s_un.sun_family = AF_UNIX; - - if (strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path)) >= - sizeof(s_un.sun_path)) { - close(s); - errno = ENAMETOOLONG; - return -1; - } - - if (bind(s, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { + if (bind(s, (struct sockaddr *)&s_un, addrlen) == -1) { save_errno = errno; close(s); errno = save_errno; @@ -1066,7 +1101,10 @@ int unix_connect(char *path) { struct sockaddr_un s_un; - int s, save_errno; + int s, save_errno, addrlen; + + if (unix_setup_sockaddr(path, &s_un, &addrlen) == -1) + return -1; if (uflag) { if ((s = unix_bind(unix_dg_tmp_socket, SOCK_CLOEXEC)) == -1) @@ -1076,16 +1114,7 @@ unix_connect(char *path) return -1; } - memset(&s_un, 0, sizeof(struct sockaddr_un)); - s_un.sun_family = AF_UNIX; - - if (strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path)) >= - sizeof(s_un.sun_path)) { - close(s); - errno = ENAMETOOLONG; - return -1; - } - if (connect(s, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { + if (connect(s, (struct sockaddr *)&s_un, addrlen) == -1) { save_errno = errno; close(s); errno = save_errno;