From b9a2a87dce460c05024f4657547857e5c141fa62 Mon Sep 17 00:00:00 2001 From: Lev Kats <lev-katz@mail.ru> Date: Wed, 26 Jun 2024 15:14:24 +0300 Subject: [PATCH] sio: fix error message displaying bind address Now `sio_bind` function prints address into error message directly instead of relying on `fd` used in `bind` that failed to execute. `sio_bind` used `sio_socketname_to_buffer` for error message effectively attempting printing address bound to `fd` while there actually was an error in binding that address to that socket in the first place. Fixes #5925 NO_DOC=bugfix NO_CHANGELOG=minor (cherry picked from commit a5214bfcfefc9c14ff8b0a076afbb05cc01db767) --- src/lib/core/sio.c | 62 ++++++++++++++++++++++++++++++++++++---------- src/lib/core/sio.h | 10 ++++++++ test/unit/sio.c | 29 +++++++++++++++++++++- test/unit/unit.h | 2 ++ test/unit/uri.c | 2 -- 5 files changed, 89 insertions(+), 16 deletions(-) diff --git a/src/lib/core/sio.c b/src/lib/core/sio.c index 72f0248c84..c9bfa13b56 100644 --- a/src/lib/core/sio.c +++ b/src/lib/core/sio.c @@ -55,38 +55,49 @@ static_assert(SMALL_STATIC_SIZE > NI_MAXHOST + NI_MAXSERV, * checks and all. */ static int -sio_socketname_to_buffer(int fd, char *buf, int size) +sio_socketname_to_buffer(int fd, + const struct sockaddr *base_addr, socklen_t addrlen, + const struct sockaddr *peer_addr, socklen_t peerlen, + char *buf, int size) { int n = 0; (void)n; SNPRINT(n, snprintf, buf, size, "fd %d", fd); if (fd < 0) return 0; - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - struct sockaddr *base_addr = (struct sockaddr *)&addr; - int rc = getsockname(fd, base_addr, &addrlen); - if (rc == 0) { + if (base_addr != NULL) { SNPRINT(n, snprintf, buf, size, ", aka "); SNPRINT(n, sio_addr_snprintf, buf, size, base_addr, addrlen); } - addrlen = sizeof(addr); - rc = getpeername(fd, (struct sockaddr *) &addr, &addrlen); - if (rc == 0) { + if (peer_addr != NULL) { SNPRINT(n, snprintf, buf, size, ", peer of "); - SNPRINT(n, sio_addr_snprintf, buf, size, base_addr, addrlen); + SNPRINT(n, sio_addr_snprintf, buf, size, + peer_addr, peerlen); } return 0; } const char * -sio_socketname(int fd) +sio_socketname_addr(int fd, const struct sockaddr *base_addr, + socklen_t addrlen) { /* Preserve errno */ int save_errno = errno; int name_size = SERVICE_NAME_MAXLEN; char *name = static_alloc(name_size); - int rc = sio_socketname_to_buffer(fd, name, name_size); + + struct sockaddr_storage peer_addr_storage; + socklen_t peerlen = sizeof(peer_addr_storage); + struct sockaddr *peer_addr = (struct sockaddr *)&peer_addr_storage; + int rcp = getpeername(fd, peer_addr, &peerlen); + if (rcp != 0) { + peer_addr = NULL; + } + + int rc = sio_socketname_to_buffer(fd, + base_addr, addrlen, + peer_addr, peerlen, + name, name_size); /* * Could fail only because of a bad format in snprintf, but it is not * bad, so should not fail. @@ -101,6 +112,29 @@ sio_socketname(int fd) return name; } +const char * +sio_socketname(int fd) +{ + /* Preserve errno */ + int save_errno = errno; + + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + struct sockaddr *base_addr = (struct sockaddr *)&addr; + int rcb = getsockname(fd, base_addr, &addrlen); + if (rcb != 0) { + base_addr = NULL; + } + + const char *name = sio_socketname_addr(fd, base_addr, addrlen); + /* + * Restore the original errno, it might have been reset by + * getsockname(). + */ + errno = save_errno; + return name; +} + /** Get a string representation of a socket option name, * for logging. */ @@ -245,7 +279,9 @@ sio_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { int rc = bind(fd, addr, addrlen); if (rc < 0) - diag_set(SocketError, sio_socketname(fd), "bind"); + diag_set(SocketError, + sio_socketname_addr(fd, addr, addrlen), + "bind"); return rc; } diff --git a/src/lib/core/sio.h b/src/lib/core/sio.h index bb599fa451..1687d3403d 100644 --- a/src/lib/core/sio.h +++ b/src/lib/core/sio.h @@ -236,6 +236,16 @@ ssize_t sio_recvfrom(int fd, void *buf, size_t len, int flags, int sio_uri_to_addr(const char *uri, struct sockaddr *addr, bool *is_host_empty); +/* internals, for unit testing */ + +/** + * Return a string containing fd, base address and peer from arguments. + * Used mostly in error messages. + */ +const char * +sio_socketname_addr(int fd, const struct sockaddr *base_addr, + socklen_t addrlen); + #if defined(__cplusplus) } /* extern "C" */ #endif /* defined(__cplusplus) */ diff --git a/test/unit/sio.c b/test/unit/sio.c index 79426c372b..d2fd3b1129 100644 --- a/test/unit/sio.c +++ b/test/unit/sio.c @@ -89,6 +89,32 @@ check_uri_to_addr(void) footer(); } +static void +check_sio_socketname_addr(void) +{ + header(); + plan(2); + struct sockaddr_storage base_storage; + struct sockaddr *base_addr = (struct sockaddr *)&base_storage; + socklen_t addrlen = sizeof(base_storage); + + bool is_host_empty; + char base_addr_str[] = "192.0.2.255:1"; + sio_uri_to_addr(base_addr_str, base_addr, &is_host_empty); + + const char *name = sio_socketname_addr(-3, + base_addr, addrlen); + + is_str(name, "fd -3", "works for invalid fd"); + + name = sio_socketname_addr(3, base_addr, addrlen); + char expectedbase[] = "fd 3, aka 192.0.2.255:1"; + is_str(name, expectedbase, "works for fd and base_addr only"); + + check_plan(); + footer(); +} + static void check_auto_bind(void) { @@ -118,9 +144,10 @@ main(void) fiber_init(fiber_c_invoke); header(); - plan(2); + plan(3); check_uri_to_addr(); check_auto_bind(); + check_sio_socketname_addr(); int rc = check_plan(); footer(); diff --git a/test/unit/unit.h b/test/unit/unit.h index bb0e53136d..4dd0b290a0 100644 --- a/test/unit/unit.h +++ b/test/unit/unit.h @@ -118,6 +118,8 @@ int check_plan(void); #define is(a, b, ...) _ok0((a) == (b), #a " == " #b, ##__VA_ARGS__) #define isnt(a, b, ...) _ok0((a) != (b), #a " != " #b, ##__VA_ARGS__) +#define is_str(a, b, fmt, args...) ok(strcmp(a, b) == 0, fmt, ##args) + #if UNIT_TAP_COMPATIBLE #define header() \ diff --git a/test/unit/uri.c b/test/unit/uri.c index 34961d6c6c..016558fbc0 100644 --- a/test/unit/uri.c +++ b/test/unit/uri.c @@ -10,8 +10,6 @@ #define URI_PARAM_MAX 10 #define URI_PARAM_VALUE_MAX 10 -#define is_str(a, b, fmt, args...) ok(strcmp(a, b) == 0, fmt, ##args) - static void sample_uri_create(struct uri *uri) { -- GitLab