From 6edc7220937250a7a2d86bc420b879f80065f518 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 15 Sep 2020 17:41:56 +0200 Subject: [PATCH] MEDIUM: tools: make str2sa_range() resolve pre-bound listeners When str2sa_range() is invoked for a bind or log line, and it gets a file descriptor number, it will immediately resolve the socket's address (when it's a socket) so that the address family, address and port are correctly set. This will later allow to resolve some transport protocols that are attached to existing FDs. For raw FDs (e.g. logs) and for socket pairs, the FD number is still returned in the address, because we need the underlying address management to complete the bind/listen/connect/whatever needed. One immediate benefit is that passing a bad FD will now result in one of these errors: 'bind' : cannot use file descriptor '3' : Socket operation on non-socket. 'bind' : socket on file descriptor '3' is of the wrong type. Note that as of now, we never return a listening socket with a family of AF_CUST_EXISTING_FD. The only case where this family is seen is for a raw FD (e.g. logs). --- src/tools.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/tools.c b/src/tools.c index 9fffc70..db17fda 100644 --- a/src/tools.c +++ b/src/tools.c @@ -963,8 +963,31 @@ struct sockaddr_storage *str2sa_range(const char *str, int *port, int *low, int goto out; } - ((struct sockaddr_in *)&ss)->sin_addr.s_addr = new_fd; - ((struct sockaddr_in *)&ss)->sin_port = 0; + if (opts & PA_O_SOCKET_FD) { + socklen_t addr_len; + int type; + + addr_len = sizeof(ss); + if (getsockname(new_fd, (struct sockaddr *)&ss, &addr_len) == -1) { + memprintf(err, "cannot use file descriptor '%d' : %s.\n", new_fd, strerror(errno)); + goto out; + } + + addr_len = sizeof(type); + if (getsockopt(new_fd, SOL_SOCKET, SO_TYPE, &type, &addr_len) != 0 || + (type == SOCK_STREAM) != !!(opts & PA_O_STREAM)) { + memprintf(err, "socket on file descriptor '%d' is of the wrong type.\n", new_fd); + goto out; + } + + porta = portl = porth = get_host_port(&ss); + } else if (opts & PA_O_RAW_FD) { + ((struct sockaddr_in *)&ss)->sin_addr.s_addr = new_fd; + ((struct sockaddr_in *)&ss)->sin_port = 0; + } else { + memprintf(err, "a file descriptor is not acceptable here in '%s'\n", str); + goto out; + } } else if (ss.ss_family == AF_UNIX) { struct sockaddr_un *un = (struct sockaddr_un *)&ss; -- 1.7.10.4