1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
From: Dirk Jagdmann <doj@cubic.org>
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 <netdb.h>
#include <poll.h>
#include <signal.h>
+#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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;
|