]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: socket: add zero-terminated ABNS alternative
authorTristan <tristan@mangadex.org>
Mon, 28 Oct 2024 15:23:31 +0000 (16:23 +0100)
committerAurelien DARRAGON <adarragon@haproxy.com>
Tue, 29 Oct 2024 11:15:24 +0000 (12:15 +0100)
When an abstract unix socket is bound by HAProxy (using "abns@" prefix),
NUL bytes are appended at the end of its path until sun_path is filled
(for a total of 108 characters).

Here we add an alternative to pass only the non-NUL length of that path
to connect/bind calls, such that the effective path of the socket's name
is as humanly written. This may be useful to interconnect with existing
softwares that implement abstract sockets with this logic instead of the
default haproxy one.

This is achieved by implementing the "abnsz" socket prefix (instead of
"abns"), which stands for "zero-terminated ABNS". "abnsz" prefix may be
used anywhere "abns" is. Internally, haproxy uses the custom socket
family (AF_CUST_ABNS vs AF_CUST_ABNSZ) to differentiate default abns
sockets from zero-terminated ones.

Documentation was updated and regtest was added.

Fixes GH issues #977 and #2479

Co-authored-by: Aurelien DARRAGON <adarragon@haproxy.com>
doc/configuration.txt
include/haproxy/tools.h
reg-tests/server/abnsz.vtc [new file with mode: 0644]
src/connection.c
src/sock_unix.c

index 3a63798bfda139c98b541b1ee18fcf20162a9320..68777b2b8c9220107d43a8efc50ad3aa07f1babb 100644 (file)
@@ -6073,6 +6073,11 @@ bind /<path> [, ...] [param*]
                       only in log-forward sections.
                     - 'unix@'  -> address is a path to a local unix socket
                     - 'abns@'  -> address is in abstract namespace (Linux only).
+                    - 'abnsz@'  -> address is in abstract namespace (Linux only)
+                       but it is explicitly zero-terminated. This means no \0
+                       padding is used to complete sun_path. It is useful to
+                       interconnect with programs that don't implement the
+                       default abns naming logic that haproxy uses.
                     - 'fd@<n>' -> use file descriptor <n> inherited from the
                       parent. The fd must be bound and may or may not already
                       be listening.
@@ -6165,11 +6170,12 @@ bind /<path> [, ...] [param*]
         listen h3_quic_proxy
             bind quic4@10.0.0.1:8888 ssl crt /etc/mycrt
 
-  Note: regarding Linux's abstract namespace sockets, HAProxy uses the whole
-        sun_path length is used for the address length. Some other programs
-        such as socat use the string length only by default. Pass the option
-        ",unix-tightsocklen=0" to any abstract socket definition in socat to
-        make it compatible with HAProxy's.
+  Note: regarding Linux's abstract namespace sockets, "abns" HAProxy sockets
+        uses the whole sun_path length is used for the address length. Some
+        other programs such as socat use the string length only by default.
+        Pass the option ",unix-tightsocklen=0" to any abstract socket
+        definition in socat to make it compatible with HAProxy's, or use the
+        "abnsz" HAProxy socket family instead.
 
   See also : "source", "option forwardfor", "unix-bind" and the PROXY protocol
              documentation, and section 5 about bind options.
@@ -11586,6 +11592,11 @@ server <name> <address>[:[port]] [param*]
                     - 'ipv6@'  -> address is always IPv6
                     - 'unix@'  -> address is a path to a local unix socket
                     - 'abns@'  -> address is in abstract namespace (Linux only)
+                    - 'abnsz@'  -> address is in abstract namespace (Linux only)
+                       but it is explicitly zero-terminated. This means no \0
+                       padding is used to complete sun_path. It is useful to
+                       interconnect with programs that don't implement the
+                       default abns naming logic that haproxy uses.
                     - 'sockpair@' -> address is the FD of a connected unix
                       socket or of a socketpair. During a connection, the
                       backend creates a pair of connected sockets, and passes
@@ -11620,11 +11631,12 @@ server <name> <address>[:[port]] [param*]
         server www1_dc1 "${LAN_DC1}.101:80"
         server www1_dc2 "${LAN_DC2}.101:80"
 
-  Note: regarding Linux's abstract namespace sockets, HAProxy uses the whole
-        sun_path length is used for the address length. Some other programs
-        such as socat use the string length only by default. Pass the option
-        ",unix-tightsocklen=0" to any abstract socket definition in socat to
-        make it compatible with HAProxy's.
+  Note: regarding Linux's abstract namespace sockets, "abns" HAProxy sockets
+        uses the whole sun_path length is used for the address length. Some
+        other programs such as socat use the string length only by default.
+        Pass the option ",unix-tightsocklen=0" to any abstract socket
+        definition in socat to make it compatible with HAProxy's, or use the
+        "abnsz" HAProxy socket family instead.
 
   See also: "default-server", "http-send-name-header" and section 5 about
              server options
@@ -11721,6 +11733,9 @@ source <addr>[:<port>] [interface <name>]
                 - 'ipv6@' -> address is always IPv6
                 - 'unix@' -> address is a path to a local unix socket
                 - 'abns@' -> address is in abstract namespace (Linux only)
+                - 'abnsz@'  -> address is in zero-terminated abstract namespace
+                               (Linux only)
+
               You may want to reference some environment variables in the
               address parameter, see section 2.3 about environment variables.
 
@@ -28564,6 +28579,9 @@ socket type and the transport method.
 
 'abns@<name>' following <name> is an abstract namespace (Linux only).
 
+'abnsz@<name>' following <name> is a zero-terminated abstract namespace
+               (Linux only).
+
 'fd@<n>'      following address is a file descriptor <n> inherited from the
               parent. The fd must be bound and may or may not already be
               listening.
index c47e5cd1feee61fa38571754e55f138634c4d09b..8e29d38cc2097968f725cb2a51a88acf7e39d99e 100644 (file)
@@ -785,8 +785,15 @@ static inline int get_addr_len(const struct sockaddr_storage *addr)
                return sizeof(struct sockaddr_in6);
        case AF_UNIX:
        case AF_CUST_ABNS:
-       case AF_CUST_ABNSZ:
                return sizeof(struct sockaddr_un);
+       case AF_CUST_ABNSZ:
+               {
+                       const struct sockaddr_un *un = (struct sockaddr_un *)addr;
+
+                       /* stop at first NULL-byte */
+                       return offsetof(struct sockaddr_un, sun_path) + 1 +
+                              strnlen2(un->sun_path + 1, sizeof(un->sun_path) - 1);
+               }
        }
        return 0;
 }
diff --git a/reg-tests/server/abnsz.vtc b/reg-tests/server/abnsz.vtc
new file mode 100644 (file)
index 0000000..072cba9
--- /dev/null
@@ -0,0 +1,37 @@
+varnishtest "Abstract unix socket - zero terminated"
+feature ignore_unknown_macro
+feature cmd "command -v curl"
+
+# abns@ sockets are not available on freebsd
+#EXCLUDE_TARGETS=freebsd,osx,generic
+#REGTEST_TYPE=devel
+
+haproxy h1 -W -S -conf {
+  global
+    stats socket "${tmpdir}/h1/stats" level admin expose-fd listeners
+
+  defaults
+    mode http
+    log global
+    option httplog
+    timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+    timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+    timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+  listen testme
+    bind "fd@${testme}"
+    server f2 abnsz@hap-f2
+
+  frontend f2
+    bind abnsz@hap-f2
+    http-request return status 200 content-type text/plain string "ok"
+} -start
+
+client c1 -connect ${h1_testme_sock} {
+    txreq -url "/"
+    rxresp
+} -run
+
+shell {
+    curl -sfS --abstract-unix-socket hap-f2 "http://host/" | grep "ok"
+}
index 07655934b5eafafd1bc00deb9fbd7daa9ff7e154..1b644b0be55c4774871f1ba9f9df4ae655d71a40 100644 (file)
@@ -2684,13 +2684,22 @@ static void conn_calculate_hash_sockaddr(const struct sockaddr_storage *ss,
                break;
 
        case AF_CUST_ABNS:
-       case AF_CUST_ABNSZ:
                conn_hash_update(hash,
                                 &((struct sockaddr_un *)ss)->sun_path,
                                 sizeof(((struct sockaddr_un *)ss)->sun_path),
                                 hash_flags, param_type_addr);
                break;
 
+       case AF_CUST_ABNSZ:
+       {
+               const struct sockaddr_un *un = (struct sockaddr_un *)ss;
+               conn_hash_update(hash,
+                                &un->sun_path,
+                                1 + strnlen2(un->sun_path + 1, sizeof(un->sun_path) - 1),
+                                hash_flags, param_type_addr);
+               break;
+       }
+
        case AF_CUST_SOCKPAIR:
                /* simply hash the fd */
                conn_hash_update(hash,
index 22ab862f5e9f60e089344cc27010b3af54b938c7..0d00a15a4df35bf995193217cca22459804cef27 100644 (file)
@@ -362,7 +362,7 @@ int sock_unix_bind_receiver(struct receiver *rx, char **errmsg)
                goto bind_close_return;
        }
 
-       if (!ext && bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+       if (!ext && bind(fd, (struct sockaddr *)&addr, get_addr_len(&rx->addr)) < 0) {
                /* note that bind() creates the socket <tempname> on the file system */
                if (errno == EADDRINUSE) {
                        /* the old process might still own it, let's retry */