MEDIUM: tools: make str2sa_range() check that the protocol has ->connect()

Most callers of str2sa_range() need the protocol only to check that it
provides a ->connect() method. It used to be used to verify that it's a
stream protocol, but it might be a bit early to get rid of it. Let's keep
the test for now but move it to str2sa_range() when the new flag PA_O_CONNECT
is present. This way almost all call places could be cleaned from this.

There's a strange test in the server address parsing code that rechecks
the family from the socket which seems to be a duplicate of the previously
removed tests. It will have to be rechecked.
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index a2a9911..bbdf699 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -2590,7 +2590,6 @@
 	else if (!strcmp(args[0], "dispatch")) {  /* dispatch address */
 		struct sockaddr_storage *sk;
 		int port1, port2;
-		struct protocol *proto;
 
 		if (curproxy == &defproxy) {
 			ha_alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
@@ -2600,21 +2599,15 @@
 		else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
 			err_code |= ERR_WARN;
 
-		sk = str2sa_range(args[1], NULL, &port1, &port2, NULL, &proto,
-		                  &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, NULL,
+		                  &errmsg, NULL, NULL,
+		                  PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
 		if (!sk) {
 			ha_alert("parsing [%s:%d] : '%s' : %s\n", file, linenum, args[0], errmsg);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
 
-		if (!proto->connect) {
-			ha_alert("parsing [%s:%d] : '%s %s' : connect() not supported for this address family.\n",
-				 file, linenum, args[0], args[1]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-
 		if (alertif_too_many_args(1, file, linenum, args, &err_code))
 			goto out;
 
@@ -2840,7 +2833,6 @@
 		int cur_arg;
 		int port1, port2;
 		struct sockaddr_storage *sk;
-		struct protocol *proto;
 
 		if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
 			err_code |= ERR_WARN;
@@ -2858,8 +2850,8 @@
 		curproxy->conn_src.iface_name = NULL;
 		curproxy->conn_src.iface_len = 0;
 
-		sk = str2sa_range(args[1], NULL, &port1, &port2, NULL, &proto,
-		                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
+		sk = str2sa_range(args[1], NULL, &port1, &port2, NULL, NULL,
+		                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
 		if (!sk) {
 			ha_alert("parsing [%s:%d] : '%s %s' : %s\n",
 				 file, linenum, args[0], args[1], errmsg);
@@ -2867,13 +2859,6 @@
 			goto out;
 		}
 
-		if (!proto->connect) {
-			ha_alert("parsing [%s:%d] : '%s %s' : connect() not supported for this address family.\n",
-				 file, linenum, args[0], args[1]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-
 		curproxy->conn_src.source_addr = *sk;
 		curproxy->conn_src.opts |= CO_SRC_BIND;
 
@@ -2936,8 +2921,8 @@
 				} else {
 					struct sockaddr_storage *sk;
 
-					sk = str2sa_range(args[cur_arg + 1], NULL, &port1, &port2, NULL, &proto,
-					                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
+					sk = str2sa_range(args[cur_arg + 1], NULL, &port1, &port2, NULL, NULL,
+					                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
 					if (!sk) {
 						ha_alert("parsing [%s:%d] : '%s %s' : %s\n",
 							 file, linenum, args[cur_arg], args[cur_arg+1], errmsg);
@@ -2945,13 +2930,6 @@
 						goto out;
 					}
 
-					if (!proto->connect) {
-						ha_alert("parsing [%s:%d] : '%s %s' : connect() not supported for this address family.\n",
-							 file, linenum, args[cur_arg], args[cur_arg+1]);
-						err_code |= ERR_ALERT | ERR_FATAL;
-						goto out;
-					}
-
 					curproxy->conn_src.tproxy_addr = *sk;
 					curproxy->conn_src.opts |= CO_SRC_TPROXY_ADDR;
 				}
diff --git a/src/cfgparse.c b/src/cfgparse.c
index b876fbd..9d8ef65 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1391,14 +1391,15 @@
 		newmailer->id = strdup(args[1]);
 
 		sk = str2sa_range(args[2], NULL, &port1, &port2, NULL, &proto,
-		                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM | PA_O_XPRT);
+		                  &errmsg, NULL, NULL,
+		                  PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
 		if (!sk) {
 			ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
 
-		if (!proto->connect || proto->sock_prot != IPPROTO_TCP) {
+		if (proto->sock_prot != IPPROTO_TCP) {
 			ha_alert("parsing [%s:%d] : '%s %s' : TCP not supported for this address family.\n",
 				 file, linenum, args[0], args[1]);
 			err_code |= ERR_ALERT | ERR_FATAL;
diff --git a/src/check.c b/src/check.c
index be9461e..8d3cd6f 100644
--- a/src/check.c
+++ b/src/check.c
@@ -2629,7 +2629,6 @@
 			  char **errmsg)
 {
 	struct sockaddr_storage *sk;
-	struct protocol *proto;
 	int port1, port2, err_code = 0;
 
 
@@ -2638,19 +2637,13 @@
 		goto error;
 	}
 
-	sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, &proto, errmsg, NULL, NULL,
-	                  PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
+	sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, NULL, errmsg, NULL, NULL,
+	                  PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
 	if (!sk) {
 		memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
 		goto error;
 	}
 
-	if (!proto->connect) {
-		memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
-		          args[*cur_arg], args[*cur_arg+1]);
-		goto error;
-	}
-
 	srv->check.addr = srv->agent.addr = *sk;
 	srv->flags |= SRV_F_CHECKADDR;
 	srv->flags |= SRV_F_AGENTADDR;
diff --git a/src/server.c b/src/server.c
index 6c5f172..b1656d5 100644
--- a/src/server.c
+++ b/src/server.c
@@ -648,7 +648,6 @@
 	char *errmsg;
 	int port_low, port_high;
 	struct sockaddr_storage *sk;
-	struct protocol *proto;
 
 	errmsg = NULL;
 
@@ -659,19 +658,14 @@
 	}
 
 	/* 'sk' is statically allocated (no need to be freed). */
-	sk = str2sa_range(args[*cur_arg + 1], NULL, &port_low, &port_high, NULL, &proto,
-	                  &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, NULL,
+	                  &errmsg, NULL, NULL,
+		          PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_RANGE | PA_O_STREAM | PA_O_CONNECT);
 	if (!sk) {
 		memprintf(err, "'%s %s' : %s\n", args[*cur_arg], args[*cur_arg + 1], errmsg);
 		goto err;
 	}
 
-	if (!proto->connect) {
-		ha_alert("'%s %s' : connect() not supported for this address family.\n",
-			 args[*cur_arg], args[*cur_arg + 1]);
-		goto err;
-	}
-
 	newsrv->conn_src.opts |= CO_SRC_BIND;
 	newsrv->conn_src.source_addr = *sk;
 
@@ -744,19 +738,14 @@
 				int port1, port2;
 
 				/* 'sk' is statically allocated (no need to be freed). */
-				sk = str2sa_range(args[*cur_arg + 1], NULL, &port1, &port2, NULL, &proto,
-				                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
+				sk = str2sa_range(args[*cur_arg + 1], NULL, &port1, &port2, NULL, NULL,
+				                  &errmsg, NULL, NULL,
+				                  PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
 				if (!sk) {
 					ha_alert("'%s %s' : %s\n", args[*cur_arg], args[*cur_arg + 1], errmsg);
 					goto err;
 				}
 
-				if (!proto->connect) {
-					ha_alert("'%s %s' : connect() not supported for this address family.\n",
-						 args[*cur_arg], args[*cur_arg + 1]);
-					goto err;
-				}
-
 				newsrv->conn_src.tproxy_addr = *sk;
 				newsrv->conn_src.opts |= CO_SRC_TPROXY_ADDR;
 			}
@@ -830,7 +819,6 @@
 	char *errmsg;
 	int port_low, port_high;
 	struct sockaddr_storage *sk;
-	struct protocol *proto;
 
 	errmsg = NULL;
 
@@ -840,18 +828,14 @@
 	}
 
 	/* 'sk' is statically allocated (no need to be freed). */
-	sk = str2sa_range(args[*cur_arg + 1], NULL, &port_low, &port_high, NULL, &proto,
-	                  &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, NULL,
+	                  &errmsg, NULL, NULL,
+	                  PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM | PA_O_CONNECT);
 	if (!sk) {
 		memprintf(err, "'%s %s' : %s\n", args[*cur_arg], args[*cur_arg + 1], errmsg);
 		goto err;
 	}
 
-	if (!proto->connect) {
-		ha_alert("'%s %s' : connect() not supported for this address family.\n", args[*cur_arg], args[*cur_arg + 1]);
-		goto err;
-	}
-
 	newsrv->flags |= SRV_F_SOCKS4_PROXY;
 	newsrv->socks4_addr = *sk;
 
@@ -1989,7 +1973,6 @@
 		if (!defsrv) {
 			struct sockaddr_storage *sk;
 			int port1, port2, port;
-			struct protocol *proto;
 
 			newsrv = new_server(curproxy);
 			if (!newsrv) {
@@ -2027,21 +2010,15 @@
 			if (!parse_addr)
 				goto skip_addr;
 
-			sk = str2sa_range(args[cur_arg], &port, &port1, &port2, NULL, &proto,
-			                  &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, NULL,
+			                  &errmsg, NULL, &fqdn,
+			                  (initial_resolve ? PA_O_RESOLVE : 0) | PA_O_PORT_OK | PA_O_PORT_OFS | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
 			if (!sk) {
 				ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
 				err_code |= ERR_ALERT | ERR_FATAL;
 				goto out;
 			}
 
-			if (!fqdn && !proto->connect) {
-				ha_alert("parsing [%s:%d] : '%s %s' : connect() not supported for this address family.\n",
-				      file, linenum, args[0], args[1]);
-				err_code |= ERR_ALERT | ERR_FATAL;
-				goto out;
-			}
-
 			if (!port1 || !port2) {
 				/* no port specified, +offset, -offset */
 				newsrv->flags |= SRV_F_MAPPORTS;
diff --git a/src/tcpcheck.c b/src/tcpcheck.c
index 7d9569c..5bd237a 100644
--- a/src/tcpcheck.c
+++ b/src/tcpcheck.c
@@ -2217,26 +2217,19 @@
 			conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
 		else if (strcmp(args[cur_arg], "addr") == 0) {
 			int port1, port2;
-			struct protocol *proto;
 
 			if (!*(args[cur_arg+1])) {
 				memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
 				goto error;
 			}
 
-			sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, NULL, &proto,
-			                  errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM);
+			sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, NULL, NULL,
+			                  errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
 			if (!sk) {
 				memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
 				goto error;
 			}
 
-			if (!proto->connect) {
-				memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
-					  args[cur_arg]);
-				goto error;
-			}
-
 			cur_arg++;
 		}
 		else if (strcmp(args[cur_arg], "port") == 0) {
diff --git a/src/tools.c b/src/tools.c
index f2553a6..c4bc821 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -1176,7 +1176,7 @@
 			ss.ss_family = AF_CUST_UDP4;
 	}
 
-	if (proto) {
+	if (proto || (opts & PA_O_CONNECT)) {
 		/* Note: if the caller asks for a proto, we must find one,
 		 * except if we return with an fqdn that will resolve later,
 		 * in which case the address is not known yet (this is only
@@ -1187,6 +1187,11 @@
 			memprintf(err, "unsupported protocol family %d for address '%s'", ss.ss_family, str);
 			goto out;
 		}
+
+		if ((opts & PA_O_CONNECT) && new_proto && !new_proto->connect) {
+			memprintf(err, "connect() not supported for this protocol family %d used by address '%s'", ss.ss_family, str);
+			goto out;
+		}
 	}
 
 	ret = &ss;