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).
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 @@
 			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;