MINOR: tools: make str2sa_range() optionally return the fd

If a file descriptor was passed, we can optionally return it. This will
be useful for listening sockets which are both a pre-bound FD and a ready
socket.
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index 2819ad9..d29505a 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -2600,7 +2600,8 @@
 		else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
 			err_code |= ERR_WARN;
 
-		sk = str2sa_range(args[1], NULL, &port1, &port2, &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM | PA_O_XPRT);
+		sk = str2sa_range(args[1], NULL, &port1, &port2, NULL,
+		                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM | PA_O_XPRT);
 		if (!sk) {
 			ha_alert("parsing [%s:%d] : '%s' : %s\n", file, linenum, args[0], errmsg);
 			err_code |= ERR_ALERT | ERR_FATAL;
@@ -2858,7 +2859,8 @@
 		curproxy->conn_src.iface_name = NULL;
 		curproxy->conn_src.iface_len = 0;
 
-		sk = str2sa_range(args[1], NULL, &port1, &port2, &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
+		sk = str2sa_range(args[1], NULL, &port1, &port2, NULL,
+		                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
 		if (!sk) {
 			ha_alert("parsing [%s:%d] : '%s %s' : %s\n",
 				 file, linenum, args[0], args[1], errmsg);
@@ -2936,7 +2938,8 @@
 				} else {
 					struct sockaddr_storage *sk;
 
-					sk = str2sa_range(args[cur_arg + 1], NULL, &port1, &port2, &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
+					sk = str2sa_range(args[cur_arg + 1], NULL, &port1, &port2, NULL,
+					                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
 					if (!sk) {
 						ha_alert("parsing [%s:%d] : '%s %s' : %s\n",
 							 file, linenum, args[cur_arg], args[cur_arg+1], errmsg);
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 1ff555c..59827f4 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -126,7 +126,7 @@
 			*next++ = 0;
 		}
 
-		ss2 = str2sa_range(str, NULL, &port, &end, err,
+		ss2 = str2sa_range(str, NULL, &port, &end, &fd, err,
 		                   curproxy == global.stats_fe ? NULL : global.unix_bind.prefix,
 		                   NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_PORT_RANGE |
 		                          PA_O_SOCKET_FD | PA_O_DGRAM | PA_O_STREAM | PA_O_XPRT);
@@ -1009,7 +1009,7 @@
 		newnameserver->conf.line = linenum;
 		newnameserver->id = strdup(args[1]);
 
-		sk = str2sa_range(args[2], NULL, &port1, &port2, &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_DGRAM);
+		sk = str2sa_range(args[2], NULL, &port1, &port2, NULL, &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_DGRAM);
 		if (!sk) {
 			ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
 			err_code |= ERR_ALERT | ERR_FATAL;
@@ -1385,7 +1385,7 @@
 
 		newmailer->id = strdup(args[1]);
 
-		sk = str2sa_range(args[2], NULL, &port1, &port2, &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM | PA_O_XPRT);
+		sk = str2sa_range(args[2], NULL, &port1, &port2, NULL, &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM | PA_O_XPRT);
 		if (!sk) {
 			ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
 			err_code |= ERR_ALERT | ERR_FATAL;
diff --git a/src/check.c b/src/check.c
index 21978da..40788ae 100644
--- a/src/check.c
+++ b/src/check.c
@@ -2638,7 +2638,7 @@
 		goto error;
 	}
 
-	sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL,
+	sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, errmsg, NULL, NULL,
 	                  PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
 	if (!sk) {
 		memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
diff --git a/src/cli.c b/src/cli.c
index 55d2a03..2892d88 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -2475,7 +2475,8 @@
 		newsrv->conf.line = 0;
 
 		memprintf(&msg, "sockpair@%d", child->ipc_fd[0]);
-		if ((sk = str2sa_range(msg, &port, &port1, &port2, &errmsg, NULL, NULL, PA_O_STREAM)) == 0) {
+		if ((sk = str2sa_range(msg, &port, &port1, &port2, NULL,
+		                       &errmsg, NULL, NULL, PA_O_STREAM)) == 0) {
 			goto error;
 		}
 		free(msg);
diff --git a/src/hlua.c b/src/hlua.c
index b3b93e8..5589482 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -2534,7 +2534,7 @@
 	}
 
 	/* Parse ip address. */
-	addr = str2sa_range(ip, NULL, &low, &high, NULL, NULL, NULL, PA_O_PORT_OK | PA_O_STREAM);
+	addr = str2sa_range(ip, NULL, &low, &high, NULL, NULL, NULL, NULL, PA_O_PORT_OK | PA_O_STREAM);
 	if (!addr) {
 		xref_unlock(&socket->xref, peer);
 		WILL_LJMP(luaL_error(L, "connect: cannot parse destination address '%s'", ip));
diff --git a/src/log.c b/src/log.c
index e31d85b..fcb16f6 100644
--- a/src/log.c
+++ b/src/log.c
@@ -1021,7 +1021,8 @@
 	if (strncmp(args[1], "fd@", 3) == 0)
 		logsrv->type = LOG_TARGET_FD;
 
-	sk = str2sa_range(args[1], NULL, &port1, &port2, err, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_RAW_FD | PA_O_DGRAM);
+	sk = str2sa_range(args[1], NULL, &port1, &port2, NULL,
+	                  err, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_RAW_FD | PA_O_DGRAM);
 	if (!sk)
 		goto error;
 	logsrv->addr = *sk;
diff --git a/src/server.c b/src/server.c
index 5c45718..84b66f7 100644
--- a/src/server.c
+++ b/src/server.c
@@ -659,7 +659,8 @@
 	}
 
 	/* 'sk' is statically allocated (no need to be freed). */
-	sk = str2sa_range(args[*cur_arg + 1], NULL, &port_low, &port_high, &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_RANGE | PA_O_STREAM);
+	sk = str2sa_range(args[*cur_arg + 1], NULL, &port_low, &port_high, NULL,
+	                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_RANGE | PA_O_STREAM);
 	if (!sk) {
 		memprintf(err, "'%s %s' : %s\n", args[*cur_arg], args[*cur_arg + 1], errmsg);
 		goto err;
@@ -744,7 +745,8 @@
 				int port1, port2;
 
 				/* 'sk' is statically allocated (no need to be freed). */
-				sk = str2sa_range(args[*cur_arg + 1], NULL, &port1, &port2, &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
+				sk = str2sa_range(args[*cur_arg + 1], NULL, &port1, &port2, NULL,
+				                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
 				if (!sk) {
 					ha_alert("'%s %s' : %s\n", args[*cur_arg], args[*cur_arg + 1], errmsg);
 					goto err;
@@ -840,7 +842,8 @@
 	}
 
 	/* 'sk' is statically allocated (no need to be freed). */
-	sk = str2sa_range(args[*cur_arg + 1], NULL, &port_low, &port_high, &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM);
+	sk = str2sa_range(args[*cur_arg + 1], NULL, &port_low, &port_high, NULL,
+	                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM);
 	if (!sk) {
 		memprintf(err, "'%s %s' : %s\n", args[*cur_arg], args[*cur_arg + 1], errmsg);
 		goto err;
@@ -2027,7 +2030,8 @@
 			if (!parse_addr)
 				goto skip_addr;
 
-			sk = str2sa_range(args[cur_arg], &port, &port1, &port2, &errmsg, NULL, &fqdn, (initial_resolve ? PA_O_RESOLVE : 0) | PA_O_PORT_OK | PA_O_PORT_OFS | PA_O_STREAM | PA_O_XPRT);
+			sk = str2sa_range(args[cur_arg], &port, &port1, &port2, NULL,
+			                  &errmsg, NULL, &fqdn, (initial_resolve ? PA_O_RESOLVE : 0) | PA_O_PORT_OK | PA_O_PORT_OFS | PA_O_STREAM | PA_O_XPRT);
 			if (!sk) {
 				ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
 				err_code |= ERR_ALERT | ERR_FATAL;
diff --git a/src/tcpcheck.c b/src/tcpcheck.c
index abd92cf..8da8955 100644
--- a/src/tcpcheck.c
+++ b/src/tcpcheck.c
@@ -2224,7 +2224,8 @@
 				goto error;
 			}
 
-			sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
+			sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, NULL,
+			                  errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
 			if (!sk) {
 				memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
 				goto error;
diff --git a/src/tools.c b/src/tools.c
index 4f61ae2..9fffc70 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -863,8 +863,11 @@
  * When a file descriptor is passed, its value is put into the s_addr part of
  * the address when cast to sockaddr_in and the address family is
  * AF_CUST_EXISTING_FD.
+ *
+ * Any known file descriptor is also assigned to <fd> if non-null, otherwise it
+ * is forced to -1.
  */
-struct sockaddr_storage *str2sa_range(const char *str, int *port, int *low, int *high, char **err, const char *pfx, char **fqdn, unsigned int opts)
+struct sockaddr_storage *str2sa_range(const char *str, int *port, int *low, int *high, int *fd, char **err, const char *pfx, char **fqdn, unsigned int opts)
 {
 	static THREAD_LOCAL struct sockaddr_storage ss;
 	struct sockaddr_storage *ret = NULL;
@@ -873,6 +876,7 @@
 	int portl, porth, porta;
 	int abstract = 0;
 	int is_udp = 0;
+	int new_fd = -1;
 
 	portl = porth = porta = 0;
 	if (fqdn)
@@ -941,24 +945,26 @@
 	if (ss.ss_family == AF_CUST_SOCKPAIR) {
 		char *endptr;
 
-		((struct sockaddr_in *)&ss)->sin_addr.s_addr = strtol(str2, &endptr, 10);
-		((struct sockaddr_in *)&ss)->sin_port = 0;
-
-		if (!*str2 || *endptr) {
+		new_fd = strtol(str2, &endptr, 10);
+		if (!*str2 || new_fd < 0 || *endptr) {
 			memprintf(err, "file descriptor '%s' is not a valid integer in '%s'\n", str2, str);
 			goto out;
 		}
+
+		((struct sockaddr_in *)&ss)->sin_addr.s_addr = new_fd;
+		((struct sockaddr_in *)&ss)->sin_port = 0;
 	}
 	else if (ss.ss_family == AF_CUST_EXISTING_FD) {
 		char *endptr;
 
-		((struct sockaddr_in *)&ss)->sin_addr.s_addr = strtol(str2, &endptr, 10);
-		((struct sockaddr_in *)&ss)->sin_port = 0;
-
-		if (!*str2 || *endptr) {
+		new_fd = strtol(str2, &endptr, 10);
+		if (!*str2 || new_fd < 0 || *endptr) {
 			memprintf(err, "file descriptor '%s' is not a valid integer in '%s'\n", str2, str);
 			goto out;
 		}
+
+		((struct sockaddr_in *)&ss)->sin_addr.s_addr = new_fd;
+		((struct sockaddr_in *)&ss)->sin_port = 0;
 	}
 	else if (ss.ss_family == AF_UNIX) {
 		struct sockaddr_un *un = (struct sockaddr_un *)&ss;
@@ -1111,6 +1117,8 @@
 		*low = portl;
 	if (high)
 		*high = porth;
+	if (fd)
+		*fd = new_fd;
 	free(back);
 	return ret;
 }