MEDIUM: proxy_protocol: Convert IPs to v6 when protocols are mixed

http-request set-src possibly creates a situation where src and dst
are from different address families. Convert both addresses to IPv6
to avoid a PROXY UNKNOWN.

This patch should be backported to haproxy 1.8.
diff --git a/src/connection.c b/src/connection.c
index 4b1e066..8826706 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -964,73 +964,71 @@
 int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst)
 {
 	int ret = 0;
+	char * protocol;
+	char src_str[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
+	char dst_str[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
+	in_port_t src_port;
+	in_port_t dst_port;
 
-	if (src && dst && src->ss_family == dst->ss_family && src->ss_family == AF_INET) {
-		ret = snprintf(buf + ret, buf_len - ret, "PROXY TCP4 ");
-		if (ret >= buf_len)
-			return 0;
-
-		/* IPv4 src */
-		if (!inet_ntop(src->ss_family, &((struct sockaddr_in *)src)->sin_addr, buf + ret, buf_len - ret))
-			return 0;
-
-		ret += strlen(buf + ret);
+	if (   !src
+	    || !dst
+	    || (src->ss_family != AF_INET && src->ss_family != AF_INET6)
+	    || (dst->ss_family != AF_INET && dst->ss_family != AF_INET6)) {
+		/* unknown family combination */
+		ret = snprintf(buf, buf_len, "PROXY UNKNOWN\r\n");
 		if (ret >= buf_len)
 			return 0;
 
-		buf[ret++] = ' ';
+		return ret;
+	}
 
-		/* IPv4 dst */
-		if (!inet_ntop(dst->ss_family, &((struct sockaddr_in *)dst)->sin_addr, buf + ret, buf_len - ret))
+	/* IPv4 for both src and dst */
+	if (src->ss_family == AF_INET && dst->ss_family == AF_INET) {
+		protocol = "TCP4";
+		if (!inet_ntop(AF_INET, &((struct sockaddr_in *)src)->sin_addr, src_str, sizeof(src_str)))
 			return 0;
-
-		ret += strlen(buf + ret);
-		if (ret >= buf_len)
+		src_port = ((struct sockaddr_in *)src)->sin_port;
+		if (!inet_ntop(AF_INET, &((struct sockaddr_in *)dst)->sin_addr, dst_str, sizeof(dst_str)))
 			return 0;
-
-		/* source and destination ports */
-		ret += snprintf(buf + ret, buf_len - ret, " %u %u\r\n",
-				ntohs(((struct sockaddr_in *)src)->sin_port),
-				ntohs(((struct sockaddr_in *)dst)->sin_port));
-		if (ret >= buf_len)
-			return 0;
+		dst_port = ((struct sockaddr_in *)dst)->sin_port;
 	}
-	else if (src && dst && src->ss_family == dst->ss_family && src->ss_family == AF_INET6) {
-		ret = snprintf(buf + ret, buf_len - ret, "PROXY TCP6 ");
-		if (ret >= buf_len)
-			return 0;
-
-		/* IPv6 src */
-		if (!inet_ntop(src->ss_family, &((struct sockaddr_in6 *)src)->sin6_addr, buf + ret, buf_len - ret))
-			return 0;
+	/* IPv6 for at least one of src and dst */
+	else {
+		struct in6_addr tmp;
 
-		ret += strlen(buf + ret);
-		if (ret >= buf_len)
-			return 0;
+		protocol = "TCP6";
 
-		buf[ret++] = ' ';
+		if (src->ss_family == AF_INET) {
+			/* Convert src to IPv6 */
+			v4tov6(&tmp, &((struct sockaddr_in *)src)->sin_addr);
+			src_port = ((struct sockaddr_in *)src)->sin_port;
+		}
+		else {
+			tmp = ((struct sockaddr_in6 *)src)->sin6_addr;
+			src_port = ((struct sockaddr_in6 *)src)->sin6_port;
+		}
 
-		/* IPv6 dst */
-		if (!inet_ntop(dst->ss_family, &((struct sockaddr_in6 *)dst)->sin6_addr, buf + ret, buf_len - ret))
+		if (!inet_ntop(AF_INET6, &tmp, src_str, sizeof(src_str)))
 			return 0;
 
-		ret += strlen(buf + ret);
-		if (ret >= buf_len)
-			return 0;
+		if (dst->ss_family == AF_INET) {
+			/* Convert dst to IPv6 */
+			v4tov6(&tmp, &((struct sockaddr_in *)dst)->sin_addr);
+			dst_port = ((struct sockaddr_in *)dst)->sin_port;
+		}
+		else {
+			tmp = ((struct sockaddr_in6 *)dst)->sin6_addr;
+			dst_port = ((struct sockaddr_in6 *)dst)->sin6_port;
+		}
 
-		/* source and destination ports */
-		ret += snprintf(buf + ret, buf_len - ret, " %u %u\r\n",
-				ntohs(((struct sockaddr_in6 *)src)->sin6_port),
-				ntohs(((struct sockaddr_in6 *)dst)->sin6_port));
-		if (ret >= buf_len)
-			return 0;
-	}
-	else {
-		/* unknown family combination */
-		ret = snprintf(buf, buf_len, "PROXY UNKNOWN\r\n");
-		if (ret >= buf_len)
+		if (!inet_ntop(AF_INET6, &tmp, dst_str, sizeof(dst_str)))
 			return 0;
 	}
+
+	ret = snprintf(buf, buf_len, "PROXY %s %s %s %u %u\r\n", protocol, src_str, dst_str, ntohs(src_port), ntohs(dst_port));
+	if (ret >= buf_len)
+		return 0;
+
 	return ret;
 }
 
@@ -1071,35 +1069,60 @@
 		dst = &remote->addr.to;
 	}
 
-	if (src && dst && src->ss_family == dst->ss_family && src->ss_family == AF_INET) {
-		if (buf_len < PP2_HDR_LEN_INET)
-			return 0;
-		hdr->ver_cmd = PP2_VERSION | PP2_CMD_PROXY;
-		hdr->fam = PP2_FAM_INET | PP2_TRANS_STREAM;
-		hdr->addr.ip4.src_addr = ((struct sockaddr_in *)src)->sin_addr.s_addr;
-		hdr->addr.ip4.dst_addr = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
-		hdr->addr.ip4.src_port = ((struct sockaddr_in *)src)->sin_port;
-		hdr->addr.ip4.dst_port = ((struct sockaddr_in *)dst)->sin_port;
-		ret = PP2_HDR_LEN_INET;
-	}
-	else if (src && dst && src->ss_family == dst->ss_family && src->ss_family == AF_INET6) {
-		if (buf_len < PP2_HDR_LEN_INET6)
-			return 0;
-		hdr->ver_cmd = PP2_VERSION | PP2_CMD_PROXY;
-		hdr->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
-		memcpy(hdr->addr.ip6.src_addr, &((struct sockaddr_in6 *)src)->sin6_addr, 16);
-		memcpy(hdr->addr.ip6.dst_addr, &((struct sockaddr_in6 *)dst)->sin6_addr, 16);
-		hdr->addr.ip6.src_port = ((struct sockaddr_in6 *)src)->sin6_port;
-		hdr->addr.ip6.dst_port = ((struct sockaddr_in6 *)dst)->sin6_port;
-		ret = PP2_HDR_LEN_INET6;
-	}
-	else {
+	/* At least one of src or dst is not of AF_INET or AF_INET6 */
+	if (  !src
+	   || !dst
+	   || (src->ss_family != AF_INET && src->ss_family != AF_INET6)
+	   || (dst->ss_family != AF_INET && dst->ss_family != AF_INET6)) {
 		if (buf_len < PP2_HDR_LEN_UNSPEC)
 			return 0;
 		hdr->ver_cmd = PP2_VERSION | PP2_CMD_LOCAL;
 		hdr->fam = PP2_FAM_UNSPEC | PP2_TRANS_UNSPEC;
 		ret = PP2_HDR_LEN_UNSPEC;
 	}
+	else {
+		/* IPv4 for both src and dst */
+		if (src->ss_family == AF_INET && dst->ss_family == AF_INET) {
+			if (buf_len < PP2_HDR_LEN_INET)
+				return 0;
+			hdr->ver_cmd = PP2_VERSION | PP2_CMD_PROXY;
+			hdr->fam = PP2_FAM_INET | PP2_TRANS_STREAM;
+			hdr->addr.ip4.src_addr = ((struct sockaddr_in *)src)->sin_addr.s_addr;
+			hdr->addr.ip4.src_port = ((struct sockaddr_in *)src)->sin_port;
+			hdr->addr.ip4.dst_addr = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
+			hdr->addr.ip4.dst_port = ((struct sockaddr_in *)dst)->sin_port;
+			ret = PP2_HDR_LEN_INET;
+		}
+		/* IPv6 for at least one of src and dst */
+		else {
+			struct in6_addr tmp;
+
+			if (buf_len < PP2_HDR_LEN_INET6)
+				return 0;
+			hdr->ver_cmd = PP2_VERSION | PP2_CMD_PROXY;
+			hdr->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
+			if (src->ss_family == AF_INET) {
+				v4tov6(&tmp, &((struct sockaddr_in *)src)->sin_addr);
+				memcpy(hdr->addr.ip6.src_addr, &tmp, 16);
+				hdr->addr.ip6.src_port = ((struct sockaddr_in *)src)->sin_port;
+			}
+			else {
+				memcpy(hdr->addr.ip6.src_addr, &((struct sockaddr_in6 *)src)->sin6_addr, 16);
+				hdr->addr.ip6.src_port = ((struct sockaddr_in6 *)src)->sin6_port;
+			}
+			if (dst->ss_family == AF_INET) {
+				v4tov6(&tmp, &((struct sockaddr_in *)dst)->sin_addr);
+				memcpy(hdr->addr.ip6.dst_addr, &tmp, 16);
+				hdr->addr.ip6.src_port = ((struct sockaddr_in *)src)->sin_port;
+			}
+			else {
+				memcpy(hdr->addr.ip6.dst_addr, &((struct sockaddr_in6 *)dst)->sin6_addr, 16);
+				hdr->addr.ip6.dst_port = ((struct sockaddr_in6 *)dst)->sin6_port;
+			}
+
+			ret = PP2_HDR_LEN_INET6;
+		}
+	}
 
 	if (srv->pp_opts & SRV_PP_V2_CRC32C) {
 		uint32_t zero_crc32c = 0;