From: Tristan Date: Mon, 28 Oct 2024 15:23:31 +0000 (+0100) Subject: MEDIUM: socket: add zero-terminated ABNS alternative X-Git-Tag: v3.1-dev11~28 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=18582ede0592f375c176735ee1384a4b3b8ae145;p=thirdparty%2Fhaproxy.git MEDIUM: socket: add zero-terminated ABNS alternative 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 --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 3a63798bfd..68777b2b8c 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -6073,6 +6073,11 @@ bind / [, ...] [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@' -> use file descriptor inherited from the parent. The fd must be bound and may or may not already be listening. @@ -6165,11 +6170,12 @@ bind / [, ...] [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
[:[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
[:[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 [:] [interface ] - '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@' following is an abstract namespace (Linux only). +'abnsz@' following is a zero-terminated abstract namespace + (Linux only). + 'fd@' following address is a file descriptor inherited from the parent. The fd must be bound and may or may not already be listening. diff --git a/include/haproxy/tools.h b/include/haproxy/tools.h index c47e5cd1fe..8e29d38cc2 100644 --- a/include/haproxy/tools.h +++ b/include/haproxy/tools.h @@ -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 index 0000000000..072cba98a5 --- /dev/null +++ b/reg-tests/server/abnsz.vtc @@ -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" +} diff --git a/src/connection.c b/src/connection.c index 07655934b5..1b644b0be5 100644 --- a/src/connection.c +++ b/src/connection.c @@ -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, diff --git a/src/sock_unix.c b/src/sock_unix.c index 22ab862f5e..0d00a15a4d 100644 --- a/src/sock_unix.c +++ b/src/sock_unix.c @@ -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 on the file system */ if (errno == EADDRINUSE) { /* the old process might still own it, let's retry */