MINOR: tcp: make set-src/set-src-port and set-dst/set-dst-port commutative

When the tcp/http actions above were introduced in 1.7-dev4, we used to
proceed like this :

  - set-src/set-dst would force the port to zero
  - set-src-port/set-dst-port would not do anything if the address family is
    neither AF_INET nor AF_INET6.

It was a stupid idea of mine to request this behaviour because it ensures
that these functions cannot be used in a wide number of situations. Because
of the first rule, it is necessary to save the source port one way or
another if only the address has to be changed (so you have to use an
variable). Due to the second rule, there's no way to set the source port
on a unix socket without first overwriting the address. And sometimes it's
really not convenient, especially when there's no way to guarantee that all
fields will properly be set.

In order to fix all this, this small change does the following :
  - set-src/set-dst always preserve the original port even if the address
    family changes. If the previous address family didn't have a port (eg:
    AF_UNIX), then the port is set to zero ;

  - set-src-port/set-dst-port always preserve the original address. If the
    address doesn't have a port, then the family is forced to IPv4 and the
    address to "0.0.0.0".

Thanks to this it now becomes possible to perform one action, the other or
both in any order.
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 424731a..c3c998e 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -1438,7 +1438,9 @@
 }
 
 /*
- * Execute the "set-src" action. May be called from {tcp,http}request
+ * Execute the "set-src" action. May be called from {tcp,http}request.
+ * It only changes the address and tries to preserve the original port. If the
+ * previous family was neither AF_INET nor AF_INET6, the port is set to zero.
  */
 enum act_return tcp_action_req_set_src(struct act_rule *rule, struct proxy *px,
                                               struct session *sess, struct stream *s, int flags)
@@ -1450,14 +1452,16 @@
 
 		smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_ADDR);
 		if (smp) {
+			int port = get_net_port(&cli_conn->addr.from);
+
 			if (smp->data.type == SMP_T_IPV4) {
 				((struct sockaddr_in *)&cli_conn->addr.from)->sin_family = AF_INET;
 				((struct sockaddr_in *)&cli_conn->addr.from)->sin_addr.s_addr = smp->data.u.ipv4.s_addr;
-				((struct sockaddr_in *)&cli_conn->addr.from)->sin_port = 0;
+				((struct sockaddr_in *)&cli_conn->addr.from)->sin_port = port;
 			} else if (smp->data.type == SMP_T_IPV6) {
 				((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_family = AF_INET6;
 				memcpy(&((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_addr, &smp->data.u.ipv6, sizeof(struct in6_addr));
-				((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_port = 0;
+				((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_port = port;
 			}
 		}
 		cli_conn->flags |= CO_FL_ADDR_FROM_SET;
@@ -1466,7 +1470,9 @@
 }
 
 /*
- * Execute the "set-dst" action. May be called from {tcp,http}request
+ * Execute the "set-dst" action. May be called from {tcp,http}request.
+ * It only changes the address and tries to preserve the original port. If the
+ * previous family was neither AF_INET nor AF_INET6, the port is set to zero.
  */
 enum act_return tcp_action_req_set_dst(struct act_rule *rule, struct proxy *px,
                                               struct session *sess, struct stream *s, int flags)
@@ -1478,13 +1484,15 @@
 
 		smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_ADDR);
 		if (smp) {
+			int port = get_net_port(&cli_conn->addr.to);
+
 			if (smp->data.type == SMP_T_IPV4) {
 				((struct sockaddr_in *)&cli_conn->addr.to)->sin_family = AF_INET;
 				((struct sockaddr_in *)&cli_conn->addr.to)->sin_addr.s_addr = smp->data.u.ipv4.s_addr;
 			} else if (smp->data.type == SMP_T_IPV6) {
 				((struct sockaddr_in6 *)&cli_conn->addr.to)->sin6_family = AF_INET6;
 				memcpy(&((struct sockaddr_in6 *)&cli_conn->addr.to)->sin6_addr, &smp->data.u.ipv6, sizeof(struct in6_addr));
-				((struct sockaddr_in6 *)&cli_conn->addr.to)->sin6_port = 0;
+				((struct sockaddr_in6 *)&cli_conn->addr.to)->sin6_port = port;
 			}
 			cli_conn->flags |= CO_FL_ADDR_TO_SET;
 		}
@@ -1493,8 +1501,10 @@
 }
 
 /*
- * Execute the "set-src-port" action. May be called from {tcp,http}request
- * We must test the sin_family before setting the port
+ * Execute the "set-src-port" action. May be called from {tcp,http}request.
+ * We must test the sin_family before setting the port. If the address family
+ * is neither AF_INET nor AF_INET6, the address is forced to AF_INET "0.0.0.0"
+ * and the port is assigned.
  */
 enum act_return tcp_action_req_set_src_port(struct act_rule *rule, struct proxy *px,
                                               struct session *sess, struct stream *s, int flags)
@@ -1508,10 +1518,14 @@
 
 		smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_SINT);
 		if (smp) {
-			if (((struct sockaddr_storage *)&cli_conn->addr.from)->ss_family == AF_INET) {
-				((struct sockaddr_in *)&cli_conn->addr.from)->sin_port = htons(smp->data.u.sint);
-			} else if (((struct sockaddr_storage *)&cli_conn->addr.from)->ss_family == AF_INET6) {
+			if (cli_conn->addr.from.ss_family == AF_INET6) {
 				((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_port = htons(smp->data.u.sint);
+			} else {
+				if (cli_conn->addr.from.ss_family != AF_INET) {
+					cli_conn->addr.from.ss_family = AF_INET;
+					((struct sockaddr_in *)&cli_conn->addr.from)->sin_addr.s_addr = 0;
+				}
+				((struct sockaddr_in *)&cli_conn->addr.from)->sin_port = htons(smp->data.u.sint);
 			}
 		}
 	}
@@ -1519,8 +1533,10 @@
 }
 
 /*
- * Execute the "set-dst-port" action. May be called from {tcp,http}request
- * We must test the sin_family before setting the port
+ * Execute the "set-dst-port" action. May be called from {tcp,http}request.
+ * We must test the sin_family before setting the port. If the address family
+ * is neither AF_INET nor AF_INET6, the address is forced to AF_INET "0.0.0.0"
+ * and the port is assigned.
  */
 enum act_return tcp_action_req_set_dst_port(struct act_rule *rule, struct proxy *px,
                                               struct session *sess, struct stream *s, int flags)
@@ -1534,10 +1550,14 @@
 
 		smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_SINT);
 		if (smp) {
-			if (((struct sockaddr_storage *)&cli_conn->addr.to)->ss_family == AF_INET) {
-				((struct sockaddr_in *)&cli_conn->addr.to)->sin_port = htons(smp->data.u.sint);
-			} else if (((struct sockaddr_storage *)&cli_conn->addr.to)->ss_family == AF_INET6) {
-				((struct sockaddr_in6 *)&cli_conn->addr.to)->sin6_port = htons(smp->data.u.sint);
+			if (cli_conn->addr.from.ss_family == AF_INET6) {
+				((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_port = htons(smp->data.u.sint);
+			} else {
+				if (cli_conn->addr.from.ss_family != AF_INET) {
+					cli_conn->addr.from.ss_family = AF_INET;
+					((struct sockaddr_in *)&cli_conn->addr.from)->sin_addr.s_addr = 0;
+				}
+				((struct sockaddr_in *)&cli_conn->addr.from)->sin_port = htons(smp->data.u.sint);
 			}
 		}
 	}