[MEDIUM] add internal support for IPv6 server addresses

This patch turns internal server addresses to sockaddr_storage to
store IPv6 addresses, and makes the connect() function use it. This
code already works but some caveats with getaddrinfo/gethostbyname
still need to be sorted out while the changes had to be merged at
this stage of internal architecture changes. So for now the config
parser will not emit an IPv6 address yet so that user experience
remains unchanged.

This change should have absolutely zero user-visible effect, otherwise
it's a bug introduced during the merge, that should be reported ASAP.
diff --git a/include/common/standard.h b/include/common/standard.h
index 3990e6f..712d941 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -158,7 +158,7 @@
  * The format is "addr:port", where "addr" can be a dotted IPv4 address,
  * a host name, or empty or "*" to indicate INADDR_ANY.
  */
-struct sockaddr_in *str2sa(char *str);
+struct sockaddr_storage *str2sa(char *str);
 
 /*
  * converts <str> to a struct sockaddr_in* which is locally allocated, and a
@@ -169,7 +169,7 @@
  * "addr[:port[-port]]", where "addr" can be a dotted IPv4 address, a host
  * name, or empty or "*" to indicate INADDR_ANY.
  */
-struct sockaddr_in *str2sa_range(char *str, int *low, int *high);
+struct sockaddr_storage *str2sa_range(char *str, int *low, int *high);
 
 /* converts <str> to a struct in_addr containing a network mask. It can be
  * passed in dotted form (255.255.255.0) or in CIDR form (24). It returns 1
@@ -188,12 +188,12 @@
 /*
  * Parse IP address found in url.
  */
-int url2ip(const char *addr, struct in_addr *dst);
+int url2ipv4(const char *addr, struct in_addr *dst);
 
 /*
- * Resolve destination server from URL. Convert <str> to a sockaddr_in*.
+ * Resolve destination server from URL. Convert <str> to a sockaddr_storage*.
  */
-int url2sa(const char *url, int ulen, struct sockaddr_in *addr);
+int url2sa(const char *url, int ulen, struct sockaddr_storage *addr);
 
 /* will try to encode the string <string> replacing all characters tagged in
  * <map> with the hexadecimal representation of their ASCII-code (2 digits)
@@ -466,4 +466,22 @@
 	return a * 3221225473U;
 }
 
+/* returns non-zero if addr has a valid and non-null IPv4 or IPv6 address,
+ * otherwise zero.
+ */
+static inline int is_addr(struct sockaddr_storage *addr)
+{
+	int i;
+
+	switch (addr->ss_family) {
+	case AF_INET:
+		return *(int *)&((struct sockaddr_in *)&addr)->sin_addr;
+	case AF_INET6:
+		for (i = 0; i < sizeof(struct in6_addr) / sizeof(int); i++)
+			if (((int *)&((struct sockaddr_in6 *)addr)->sin6_addr)[i] != 0)
+				return ((int *)&((struct sockaddr_in6 *)addr)->sin6_addr)[i];
+	}
+	return 0;
+}
+
 #endif /* _COMMON_STANDARD_H */
diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h
index b871003..26e06df 100644
--- a/include/proto/proto_tcp.h
+++ b/include/proto/proto_tcp.h
@@ -27,10 +27,10 @@
 #include <types/task.h>
 #include <proto/stick_table.h>
 
-int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct sockaddr_in *remote);
+int tcp_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct sockaddr_storage *remote);
 void tcpv4_add_listener(struct listener *listener);
 void tcpv6_add_listener(struct listener *listener);
-int tcpv4_connect_server(struct stream_interface *si);
+int tcp_connect_server(struct stream_interface *si);
 int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit);
 int tcp_inspect_response(struct session *s, struct buffer *rep, int an_bit);
 int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit);
diff --git a/include/types/peers.h b/include/types/peers.h
index b5b92c2..24ab804 100644
--- a/include/types/peers.h
+++ b/include/types/peers.h
@@ -72,7 +72,7 @@
 		int line;	  /* line where the section appears */
 	} conf;		  	  /* config information */
 	time_t last_change;
-	struct sockaddr_in addr;  /* peer address */
+	struct sockaddr_storage addr;  /* peer address */
 	struct peer *next;	  /* next peer in the list */
 };
 
diff --git a/include/types/proxy.h b/include/types/proxy.h
index e724a25..95736b1 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -182,7 +182,7 @@
 	unsigned int fe_req_ana, be_req_ana;	/* bitmap of common request protocol analysers for the frontend and backend */
 	unsigned int fe_rsp_ana, be_rsp_ana;	/* bitmap of common response protocol analysers for the frontend and backend */
 	int mode;				/* mode = PR_MODE_TCP, PR_MODE_HTTP or PR_MODE_HEALTH */
-	struct sockaddr_in dispatch_addr;	/* the default address to connect to */
+	struct sockaddr_storage dispatch_addr;	/* the default address to connect to */
 	union {
 		struct proxy *be;		/* default backend, or NULL if none set */
 		char *name;			/* default backend name during config parse */
@@ -271,9 +271,9 @@
 
 	int conn_retries;			/* maximum number of connect retries */
 	int cap;				/* supported capabilities (PR_CAP_*) */
-	struct sockaddr_in source_addr;		/* the address to which we want to bind for connect() */
+	struct sockaddr_storage source_addr;	/* the address to which we want to bind for connect() */
 #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
-	struct sockaddr_in tproxy_addr;		/* non-local address we want to bind to for connect() */
+	struct sockaddr_storage tproxy_addr;	/* non-local address we want to bind to for connect() */
 	char *bind_hdr_name;			/* bind to this header name if defined */
 	int bind_hdr_len;			/* length of the name of the header above */
 	int bind_hdr_occ;			/* occurrence number of header above: >0 = from first, <0 = from end, 0=disabled */
diff --git a/include/types/server.h b/include/types/server.h
index 00251d8..c697457 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -102,10 +102,10 @@
 	struct list pendconns;			/* pending connections */
 	struct task *check;                     /* the task associated to the health check processing */
 
-	struct sockaddr_in addr;		/* the address to connect to */
-	struct sockaddr_in source_addr;		/* the address to which we want to bind for connect() */
+	struct sockaddr_storage addr;		/* the address to connect to */
+	struct sockaddr_storage source_addr;	/* the address to which we want to bind for connect() */
 #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
-	struct sockaddr_in tproxy_addr;		/* non-local address we want to bind to for connect() */
+	struct sockaddr_storage tproxy_addr;	/* non-local address we want to bind to for connect() */
 	char *bind_hdr_name;			/* bind to this header name if defined */
 	int bind_hdr_len;			/* length of the name of the header above */
 	int bind_hdr_occ;			/* occurrence number of header above: >0 = from first, <0 = from end, 0=disabled */
@@ -116,7 +116,7 @@
 
 	struct server *tracknext, *tracked;	/* next server in a tracking list, tracked server */
 	char *trackit;				/* temporary variable to make assignment deferrable */
-	struct sockaddr_in check_addr;		/* the address to check, if different from <addr> */
+	struct sockaddr_storage check_addr;	/* the address to check, if different from <addr> */
 	short check_port;			/* the port to use for the health checks */
 	int health;				/* 0->rise-1 = bad; rise->rise+fall-1 = good */
 	int consecutive_errors;			/* current number of consecutive errors */
diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h
index 39caf8f..16a261f 100644
--- a/include/types/stream_interface.h
+++ b/include/types/stream_interface.h
@@ -183,8 +183,8 @@
 			struct sockaddr_storage to;	/* the address reached by the client if SN_FRT_ADDR_SET is set */
 		} c; /* client side */
 		struct {
-			struct sockaddr_in from;	/* the address to spoof when connecting to the server (transparent mode) */
-			struct sockaddr_in to;		/* the address to connect to */
+			struct sockaddr_storage from;	/* the address to spoof when connecting to the server (transparent mode) */
+			struct sockaddr_storage to;	/* the address to connect to */
 		} s; /* server side */
 	} addr; /* addresses of the remote side */
 };
diff --git a/src/backend.c b/src/backend.c
index 7a9e0c6..e2ad5b2 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -621,7 +621,8 @@
 	else if ((s->be->options2 & PR_O2_DISPATCH) || (s->be->options & PR_O_TRANSP)) {
 		set_target_proxy(&s->target, s->be);
 	}
-	else if ((s->be->options & PR_O_HTTP_PROXY) && s->req->cons->addr.s.to.sin_addr.s_addr) {
+	else if ((s->be->options & PR_O_HTTP_PROXY) &&
+		 is_addr(&s->req->cons->addr.s.to)) {
 		/* in proxy mode, we need a valid destination address */
 		set_target_proxy(&s->target, s->be);
 	}
@@ -677,7 +678,7 @@
 
 		s->req->cons->addr.s.to = target_srv(&s->target)->addr;
 
-		if (!s->req->cons->addr.s.to.sin_addr.s_addr) {
+		if (!is_addr(&s->req->cons->addr.s.to)) {
 			/* if the server has no address, we use the same address
 			 * the client asked, which is handy for remapping ports
 			 * locally on multiple addresses at once.
@@ -686,22 +687,36 @@
 				get_frt_addr(s);
 
 			if (s->req->prod->addr.c.to.ss_family == AF_INET) {
-				s->req->cons->addr.s.to.sin_addr = ((struct sockaddr_in *)&s->req->prod->addr.c.to)->sin_addr;
+				((struct sockaddr_in *)&s->req->cons->addr.s.to)->sin_addr = ((struct sockaddr_in *)&s->req->prod->addr.c.to)->sin_addr;
+			} else if (s->req->prod->addr.c.to.ss_family == AF_INET6) {
+				((struct sockaddr_in6 *)&s->req->cons->addr.s.to)->sin6_addr = ((struct sockaddr_in6 *)&s->req->prod->addr.c.to)->sin6_addr;
 			}
 		}
 
 		/* if this server remaps proxied ports, we'll use
 		 * the port the client connected to with an offset. */
 		if (target_srv(&s->target)->state & SRV_MAPPORTS) {
+			int base_port;
+
 			if (!(s->be->options & PR_O_TRANSP) && !(s->flags & SN_FRT_ADDR_SET))
 				get_frt_addr(s);
-			if (s->req->prod->addr.c.to.ss_family == AF_INET) {
-				s->req->cons->addr.s.to.sin_port = htons(ntohs(s->req->cons->addr.s.to.sin_port) +
-							     ntohs(((struct sockaddr_in *)&s->req->prod->addr.c.to)->sin_port));
+
+			/* First, retrieve the port from the incoming connection */
+			if (s->req->prod->addr.c.to.ss_family == AF_INET)
+				base_port = ntohs(((struct sockaddr_in *)&s->req->prod->addr.c.to)->sin_port);
+			else if (s->req->prod->addr.c.to.ss_family == AF_INET6)
+				base_port = ntohs(((struct sockaddr_in6 *)&s->req->prod->addr.c.to)->sin6_port);
+			else
+				base_port = 0;
+
+			/* Second, assign the outgoing connection's port */
+			if (s->req->cons->addr.c.to.ss_family == AF_INET) {
+				((struct sockaddr_in *)&s->req->cons->addr.s.to)->sin_port =
+					htons(base_port + ntohs(((struct sockaddr_in *)&s->req->cons->addr.s.to)->sin_port));
 			}
 			else if (s->req->prod->addr.c.to.ss_family == AF_INET6) {
-				s->req->cons->addr.s.to.sin_port = htons(ntohs(s->req->cons->addr.s.to.sin_port) +
-							     ntohs(((struct sockaddr_in6 *)&s->req->prod->addr.c.to)->sin6_port));
+				((struct sockaddr_in6 *)&s->req->cons->addr.s.to)->sin6_port =
+					htons(base_port + ntohs(((struct sockaddr_in6 *)&s->req->cons->addr.s.to)->sin6_port));
 			}
 		}
 	}
@@ -714,7 +729,7 @@
 		if (!(s->flags & SN_FRT_ADDR_SET))
 			get_frt_addr(s);
 
-		if (s->req->prod->addr.c.to.ss_family == AF_INET) {
+		if (s->req->prod->addr.c.to.ss_family == AF_INET || s->req->prod->addr.c.to.ss_family == AF_INET6) {
 			memcpy(&s->req->cons->addr.s.to, &s->req->prod->addr.c.to, MIN(sizeof(s->req->cons->addr.s.to), sizeof(s->req->prod->addr.c.to)));
 		}
 		/* when we support IPv6 on the backend, we may add other tests */
@@ -869,22 +884,23 @@
 	if (srv && srv->state & SRV_BIND_SRC) {
 		switch (srv->state & SRV_TPROXY_MASK) {
 		case SRV_TPROXY_ADDR:
-			s->req->cons->addr.s.from = *(struct sockaddr_in *)&srv->tproxy_addr;
+			s->req->cons->addr.s.from = srv->tproxy_addr;
 			break;
 		case SRV_TPROXY_CLI:
 		case SRV_TPROXY_CIP:
 			/* FIXME: what can we do if the client connects in IPv6 or unix socket ? */
-			s->req->cons->addr.s.from = *(struct sockaddr_in *)&s->req->prod->addr.c.from;
+			s->req->cons->addr.s.from = s->req->prod->addr.c.from;
 			break;
 		case SRV_TPROXY_DYN:
 			if (srv->bind_hdr_occ) {
 				/* bind to the IP in a header */
-				s->req->cons->addr.s.from.sin_port = 0;
-				s->req->cons->addr.s.from.sin_addr.s_addr = htonl(get_ip_from_hdr2(&s->txn.req,
-										srv->bind_hdr_name,
-										srv->bind_hdr_len,
-										&s->txn.hdr_idx,
-										srv->bind_hdr_occ));
+				((struct sockaddr_in *)&s->req->cons->addr.s.from)->sin_port = 0;
+				((struct sockaddr_in *)&s->req->cons->addr.s.from)->sin_addr.s_addr =
+					htonl(get_ip_from_hdr2(&s->txn.req,
+					                       srv->bind_hdr_name,
+					                       srv->bind_hdr_len,
+					                       &s->txn.hdr_idx,
+					                       srv->bind_hdr_occ));
 			}
 			break;
 		default:
@@ -894,22 +910,23 @@
 	else if (s->be->options & PR_O_BIND_SRC) {
 		switch (s->be->options & PR_O_TPXY_MASK) {
 		case PR_O_TPXY_ADDR:
-			s->req->cons->addr.s.from = *(struct sockaddr_in *)&s->be->tproxy_addr;
+			s->req->cons->addr.s.from = s->be->tproxy_addr;
 			break;
 		case PR_O_TPXY_CLI:
 		case PR_O_TPXY_CIP:
 			/* FIXME: what can we do if the client connects in IPv6 or socket unix? */
-			s->req->cons->addr.s.from = *(struct sockaddr_in *)&s->req->prod->addr.c.from;
+			s->req->cons->addr.s.from = s->req->prod->addr.c.from;
 			break;
 		case PR_O_TPXY_DYN:
 			if (s->be->bind_hdr_occ) {
 				/* bind to the IP in a header */
-				s->req->cons->addr.s.from.sin_port = 0;
-				s->req->cons->addr.s.from.sin_addr.s_addr = htonl(get_ip_from_hdr2(&s->txn.req,
-										s->be->bind_hdr_name,
-										s->be->bind_hdr_len,
-										&s->txn.hdr_idx,
-										s->be->bind_hdr_occ));
+				((struct sockaddr_in *)&s->req->cons->addr.s.from)->sin_port = 0;
+				((struct sockaddr_in *)&s->req->cons->addr.s.from)->sin_addr.s_addr =
+					htonl(get_ip_from_hdr2(&s->txn.req,
+							       s->be->bind_hdr_name,
+							       s->be->bind_hdr_len,
+							       &s->txn.hdr_idx,
+							       s->be->bind_hdr_occ));
 			}
 			break;
 		default:
@@ -951,7 +968,7 @@
 	 * session's freshly assigned target with the stream interface's.
 	 */
 	stream_sock_prepare_interface(s->req->cons);
-	s->req->cons->connect = tcpv4_connect_server;
+	s->req->cons->connect = tcp_connect_server;
 	copy_target(&s->req->cons->target, &s->target);
 
 	assign_tproxy_address(s);
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 18cfda3..aeec87b 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -937,13 +937,13 @@
 			logsrv.u.un = *sk;
 			logsrv.u.addr.sa_family = AF_UNIX;
 		} else {
-			struct sockaddr_in *sk = str2sa(args[1]);
-			if (!sk) {
+			struct sockaddr_storage *sk = str2sa(args[1]);
+			if (!sk || sk->ss_family != AF_INET) {
 				Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[1]);
 				err_code |= ERR_ALERT | ERR_FATAL;
 				goto out;
 			}
-			logsrv.u.in = *sk;
+			logsrv.u.in = *(struct sockaddr_in *)sk;
 			logsrv.u.addr.sa_family = AF_INET;
 			if (!logsrv.u.in.sin_port)
 				logsrv.u.in.sin_port = htons(SYSLOG_PORT);
@@ -1233,7 +1233,7 @@
 	else if (strcmp(args[0], "peer") == 0) { /* peer definition */
 		char *rport, *raddr;
 		short realport = 0;
-		struct sockaddr_in *sk;
+		struct sockaddr_storage *sk;
 
 		if (!*args[2]) {
 			Alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
@@ -1287,7 +1287,15 @@
 			goto out;
 		}
 		newpeer->addr = *sk;
-		newpeer->addr.sin_port = htons(realport);
+
+		switch (newpeer->addr.ss_family) {
+		case AF_INET:
+			((struct sockaddr_in *)&newpeer->addr)->sin_port = htons(realport);
+			break;
+		case AF_INET6:
+			((struct sockaddr_in6 *)&newpeer->addr)->sin6_port = htons(realport);
+			break;
+		}
 
 		if (strcmp(newpeer->id, localpeer) == 0) {
 			/* Current is local peer, it define a frontend */
@@ -3843,7 +3851,7 @@
 		curproxy->grace = val;
 	}
 	else if (!strcmp(args[0], "dispatch")) {  /* dispatch address */
-		struct sockaddr_in *sk;
+		struct sockaddr_storage *sk;
 		if (curproxy == &defproxy) {
 			Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
 			err_code |= ERR_ALERT | ERR_FATAL;
@@ -3929,7 +3937,7 @@
 		}
 
 		if (!defsrv) {
-			struct sockaddr_in *sk;
+			struct sockaddr_storage *sk;
 
 			if ((newsrv = (struct server *)calloc(1, sizeof(struct server))) == NULL) {
 				Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
@@ -3975,7 +3983,15 @@
 				goto out;
 			}
 			newsrv->addr = *sk;
-			newsrv->addr.sin_port = htons(realport);
+
+			switch (newsrv->addr.ss_family) {
+			case AF_INET:
+				((struct sockaddr_in *)&newsrv->addr)->sin_port = htons(realport);
+				break;
+			case AF_INET6:
+				((struct sockaddr_in6 *)&newsrv->addr)->sin6_port = htons(realport);
+				break;
+			}
 
 			newsrv->check_port	= curproxy->defsrv.check_port;
 			newsrv->inter		= curproxy->defsrv.inter;
@@ -4135,7 +4151,7 @@
 				cur_arg += 2;
 			}
 			else if (!defsrv && !strcmp(args[cur_arg], "addr")) {
-				struct sockaddr_in *sk = str2sa(args[cur_arg + 1]);
+				struct sockaddr_storage *sk = str2sa(args[cur_arg + 1]);
 				if (!sk) {
 					Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[cur_arg + 1]);
 					err_code |= ERR_ALERT | ERR_FATAL;
@@ -4280,7 +4296,7 @@
 			}
 			else if (!defsrv && !strcmp(args[cur_arg], "source")) {  /* address to which we bind when connecting */
 				int port_low, port_high;
-				struct sockaddr_in *sk;
+				struct sockaddr_storage *sk;
 
 				if (!*args[cur_arg + 1]) {
 #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
@@ -4381,7 +4397,7 @@
 								goto out;
 							}
 						} else {
-							struct sockaddr_in *sk = str2sa(args[cur_arg + 1]);
+							struct sockaddr_storage *sk = str2sa(args[cur_arg + 1]);
 							if (!sk) {
 								Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[cur_arg + 1]);
 								err_code |= ERR_ALERT | ERR_FATAL;
@@ -4458,8 +4474,16 @@
 				goto out;
 			}
 
-			if (!newsrv->check_port && newsrv->check_addr.sin_port)
-				newsrv->check_port = newsrv->check_addr.sin_port;
+			switch (newsrv->check_addr.ss_family) {
+				case AF_INET:
+					if (!newsrv->check_port && ((struct sockaddr_in *)&newsrv->check_addr)->sin_port)
+						newsrv->check_port = ntohs(((struct sockaddr_in *)&newsrv->check_addr)->sin_port);
+					break;
+				case AF_INET6:
+					if (!newsrv->check_port && ((struct sockaddr_in6 *)&newsrv->check_addr)->sin6_port)
+						newsrv->check_port = ntohs(((struct sockaddr_in6 *)&newsrv->check_addr)->sin6_port);
+					break;
+			}
 
 			if (!newsrv->check_port && !(newsrv->state & SRV_MAPPORTS))
 				newsrv->check_port = realport; /* by default */
@@ -4557,13 +4581,13 @@
 				logsrv.u.un = *sk;
 				logsrv.u.addr.sa_family = AF_UNIX;
 			} else {
-				struct sockaddr_in *sk = str2sa(args[1]);
-				if (!sk) {
+				struct sockaddr_storage *sk = str2sa(args[1]);
+				if (!sk || sk->ss_family != AF_INET) {
 					Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[1]);
 					err_code |= ERR_ALERT | ERR_FATAL;
 					goto out;
 				}
-				logsrv.u.in = *sk;
+				logsrv.u.in = *(struct sockaddr_in *)sk;
 				logsrv.u.addr.sa_family = AF_INET;
 				if (!logsrv.u.in.sin_port) {
 					logsrv.u.in.sin_port =
@@ -4598,7 +4622,7 @@
 	}
 	else if (!strcmp(args[0], "source")) {  /* address to which we bind when connecting */
 		int cur_arg;
-		struct sockaddr_in *sk;
+		struct sockaddr_storage *sk;
 
 		if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
 			err_code |= ERR_WARN;
@@ -4690,7 +4714,7 @@
 						goto out;
 					}
 				} else {
-					struct sockaddr_in *sk = str2sa(args[cur_arg + 1]);
+					struct sockaddr_storage *sk = str2sa(args[cur_arg + 1]);
 					if (!sk) {
 						Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[cur_arg + 1]);
 						err_code |= ERR_ALERT | ERR_FATAL;
diff --git a/src/checks.c b/src/checks.c
index ba3e745..e4982ca 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -810,10 +810,21 @@
 			 *  - connected (EISCONN, 0)
 			 */
 
-			struct sockaddr_in sa;
+			struct sockaddr_storage sa;
 
-			sa = (s->check_addr.sin_addr.s_addr) ? s->check_addr : s->addr;
-			sa.sin_port = htons(s->check_port);
+			if (is_addr(&s->check_addr))
+				sa = s->check_addr;
+			else
+				sa = s->addr;
+
+			switch (s->check_addr.ss_family) {
+			case AF_INET:
+				((struct sockaddr_in *)&sa)->sin_port = htons(s->check_port);
+				break;
+			case AF_INET6:
+				((struct sockaddr_in6 *)&sa)->sin6_port = htons(s->check_port);
+				break;
+			}
 
 			if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == 0)
 				errno = 0;
@@ -1193,7 +1204,7 @@
 {
 	int attempts = 0;
 	struct server *s = t->context;
-	struct sockaddr_in sa;
+	struct sockaddr_storage sa;
 	int fd;
 	int rv;
 
@@ -1223,7 +1234,7 @@
 
 		/* we'll initiate a new check */
 		set_server_check_status(s, HCHK_STATUS_START, NULL);
-		if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) {
+		if ((fd = socket(s->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) != -1) {
 			if ((fd < global.maxsock) &&
 			    (fcntl(fd, F_SETFL, O_NONBLOCK) != -1) &&
 			    (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != -1)) {
@@ -1233,28 +1244,35 @@
 					/* We don't want to useless data */
 					setsockopt(fd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger));
 				}
-				
-				if (s->check_addr.sin_addr.s_addr)
+
+				if (is_addr(&s->check_addr))
 					/* we'll connect to the check addr specified on the server */
 					sa = s->check_addr;
 				else
 					/* we'll connect to the addr on the server */
 					sa = s->addr;
 
-				/* we'll connect to the check port on the server */
-				sa.sin_port = htons(s->check_port);
-
+				switch (s->check_addr.ss_family) {
+				case AF_INET:
+					/* we'll connect to the check port on the server */
+					((struct sockaddr_in *)&sa)->sin_port = htons(s->check_port);
+					break;
+				case AF_INET6:
+					/* we'll connect to the check port on the server */
+					((struct sockaddr_in6 *)&sa)->sin6_port = htons(s->check_port);
+					break;
+				}
 				/* allow specific binding :
 				 * - server-specific at first
 				 * - proxy-specific next
 				 */
 				if (s->state & SRV_BIND_SRC) {
-					struct sockaddr_in *remote = NULL;
+					struct sockaddr_storage *remote = NULL;
 					int ret, flags = 0;
 
 #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
 					if ((s->state & SRV_TPROXY_MASK) == SRV_TPROXY_ADDR) {
-						remote = (struct sockaddr_in *)&s->tproxy_addr;
+						remote = &s->tproxy_addr;
 						flags  = 3;
 					}
 #endif
@@ -1266,7 +1284,7 @@
 #endif
 					if (s->sport_range) {
 						int bind_attempts = 10; /* should be more than enough to find a spare port */
-						struct sockaddr_in src;
+						struct sockaddr_storage src;
 
 						ret = 1;
 						src = s->source_addr;
@@ -1287,13 +1305,21 @@
 								break;
 
 							fdinfo[fd].port_range = s->sport_range;
-							src.sin_port = htons(fdinfo[fd].local_port);
+
+							switch (src.ss_family) {
+							case AF_INET:
+								((struct sockaddr_in *)&src)->sin_port = htons(fdinfo[fd].local_port);
+								break;
+							case AF_INET6:
+								((struct sockaddr_in6 *)&src)->sin6_port = htons(fdinfo[fd].local_port);
+								break;
+							}
 
-							ret = tcpv4_bind_socket(fd, flags, &src, remote);
+							ret = tcp_bind_socket(fd, flags, &src, remote);
 						} while (ret != 0); /* binding NOK */
 					}
 					else {
-						ret = tcpv4_bind_socket(fd, flags, &s->source_addr, remote);
+						ret = tcp_bind_socket(fd, flags, &s->source_addr, remote);
 					}
 
 					if (ret) {
@@ -1311,12 +1337,12 @@
 					}
 				}
 				else if (s->proxy->options & PR_O_BIND_SRC) {
-					struct sockaddr_in *remote = NULL;
+					struct sockaddr_storage *remote = NULL;
 					int ret, flags = 0;
 
 #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
 					if ((s->proxy->options & PR_O_TPXY_MASK) == PR_O_TPXY_ADDR) {
-						remote = (struct sockaddr_in *)&s->proxy->tproxy_addr;
+						remote = &s->proxy->tproxy_addr;
 						flags  = 3;
 					}
 #endif
@@ -1326,7 +1352,7 @@
 						setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
 							   s->proxy->iface_name, s->proxy->iface_len + 1);
 #endif
-					ret = tcpv4_bind_socket(fd, flags, &s->proxy->source_addr, remote);
+					ret = tcp_bind_socket(fd, flags, &s->proxy->source_addr, remote);
 					if (ret) {
 						set_server_check_status(s, HCHK_STATUS_SOCKERR, NULL);
 						switch (ret) {
diff --git a/src/dumpstats.c b/src/dumpstats.c
index ed577ad..b04b297 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -2111,10 +2111,20 @@
 					chunk_printf(&msg, " title=\"IP: ");
 
 					/* IP */
-					if (inet_ntop(sv->addr.sin_family, &sv->addr.sin_addr, str, sizeof(str)))
-						chunk_printf(&msg, "%s:%d", str, htons(sv->addr.sin_port));
-					else
-						chunk_printf(&msg, "(%s)", strerror(errno));
+					switch (sv->addr.ss_family) {
+					case AF_INET:
+						if (inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)&sv->addr)->sin_addr, str, sizeof(str)))
+							chunk_printf(&msg, "%s:%d", str, htons(((struct sockaddr_in *)&sv->addr)->sin_port));
+						else
+							chunk_printf(&msg, "(%s)", strerror(errno));
+						break;
+					case AF_INET6:
+						if (inet_ntop(AF_INET6, (const void *)&((struct sockaddr_in6 *)&sv->addr)->sin6_addr, str, sizeof(str)))
+							chunk_printf(&msg, "%s:%d", str, htons(((struct sockaddr_in6 *)&sv->addr)->sin6_port));
+						else
+							chunk_printf(&msg, "(%s)", strerror(errno));
+						break;
+					}
 
 					/* id */
 					chunk_printf(&msg, ", id: %d", sv->puid);
diff --git a/src/peers.c b/src/peers.c
index 5b8e749..739c09b 100644
--- a/src/peers.c
+++ b/src/peers.c
@@ -1174,7 +1174,7 @@
 	s->si[1].conn_retries = p->conn_retries;
 	s->si[1].err_type = SI_ET_NONE;
 	s->si[1].err_loc = NULL;
-	s->si[1].connect = tcpv4_connect_server;
+	s->si[1].connect = tcp_connect_server;
 	set_target_proxy(&s->si[1].target, s->be);
 	s->si[1].exp = TICK_ETERNITY;
 	s->si[1].flags = SI_FL_NONE;
diff --git a/src/proto_http.c b/src/proto_http.c
index cbfdca2..ff2f44c 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -8037,9 +8037,9 @@
 		test->flags |= ACL_TEST_F_FETCH_MORE;
 		test->flags |= ACL_TEST_F_VOL_HDR;
 		/* Same optimization as url_ip */
-		memset(&l4->req->cons->addr.s.to.sin_addr, 0, sizeof(l4->req->cons->addr.s.to.sin_addr));
-		url2ip((char *)ctx->line + ctx->val, &l4->req->cons->addr.s.to.sin_addr);
-		test->ptr = (void *)&l4->req->cons->addr.s.to.sin_addr;
+		memset(&((struct sockaddr_in *)&l4->req->cons->addr.s.to)->sin_addr, 0, sizeof(((struct sockaddr_in *)&l4->req->cons->addr.s.to)->sin_addr));
+		url2ipv4((char *)ctx->line + ctx->val, &((struct sockaddr_in *)&l4->req->cons->addr.s.to)->sin_addr);
+		test->ptr = (void *)&((struct sockaddr_in *)&l4->req->cons->addr.s.to)->sin_addr;
 		test->i = AF_INET;
 		return 1;
 	}
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 2d79e22..04562f1 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -100,7 +100,7 @@
 };
 
 
-/* Binds ipv4 address <local> to socket <fd>, unless <flags> is set, in which
+/* Binds ipv4/ipv6 address <local> to socket <fd>, unless <flags> is set, in which
  * case we try to bind <remote>. <flags> is a 2-bit field consisting of :
  *  - 0 : ignore remote address (may even be a NULL pointer)
  *  - 1 : use provided address
@@ -114,9 +114,9 @@
  * This function returns 0 when everything's OK, 1 if it could not bind, to the
  * local address, 2 if it could not bind to the foreign address.
  */
-int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct sockaddr_in *remote)
+int tcp_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct sockaddr_storage *remote)
 {
-	struct sockaddr_in bind_addr;
+	struct sockaddr_storage bind_addr;
 	int foreign_ok = 0;
 	int ret;
 
@@ -132,10 +132,20 @@
 #endif
 	if (flags) {
 		memset(&bind_addr, 0, sizeof(bind_addr));
-		if (flags & 1)
-			bind_addr.sin_addr = remote->sin_addr;
-		if (flags & 2)
-			bind_addr.sin_port = remote->sin_port;
+		switch (remote->ss_family) {
+		case AF_INET:
+			if (flags & 1)
+				((struct sockaddr_in *)&bind_addr)->sin_addr = ((struct sockaddr_in *)remote)->sin_addr;
+			if (flags & 2)
+				((struct sockaddr_in *)&bind_addr)->sin_port = ((struct sockaddr_in *)remote)->sin_port;
+			break;
+		case AF_INET6:
+			if (flags & 1)
+				((struct sockaddr_in6 *)&bind_addr)->sin6_addr = ((struct sockaddr_in6 *)remote)->sin6_addr;
+			if (flags & 2)
+				((struct sockaddr_in6 *)&bind_addr)->sin6_port = ((struct sockaddr_in6 *)remote)->sin6_port;
+			break;
+		}
 	}
 
 	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
@@ -198,7 +208,7 @@
  * Additionnally, in the case of SN_ERR_RESOURCE, an emergency log will be emitted.
  */
 
-int tcpv4_connect_server(struct stream_interface *si)
+int tcp_connect_server(struct stream_interface *si)
 {
 	int fd;
 	struct server *srv;
@@ -217,7 +227,7 @@
 		return SN_ERR_INTERNAL;
 	}
 
-	if ((fd = si->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+	if ((fd = si->fd = socket(si->addr.s.to.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
 		qfprintf(stderr, "Cannot get a server socket.\n");
 
 		if (errno == ENFILE)
@@ -284,7 +294,7 @@
 
 		if (srv->sport_range) {
 			int attempts = 10; /* should be more than enough to find a spare port */
-			struct sockaddr_in src;
+			struct sockaddr_storage src;
 
 			ret = 1;
 			src = srv->source_addr;
@@ -305,13 +315,20 @@
 					break;
 
 				fdinfo[fd].port_range = srv->sport_range;
-				src.sin_port = htons(fdinfo[fd].local_port);
+				switch (src.ss_family) {
+				case AF_INET:
+					((struct sockaddr_in *)&src)->sin_port = htons(fdinfo[fd].local_port);
+					break;
+				case AF_INET6:
+					((struct sockaddr_in6 *)&src)->sin6_port = htons(fdinfo[fd].local_port);
+					break;
+				}
 
-				ret = tcpv4_bind_socket(fd, flags, &src, (struct sockaddr_in *)&si->addr.s.from);
+				ret = tcp_bind_socket(fd, flags, &src, &si->addr.s.from);
 			} while (ret != 0); /* binding NOK */
 		}
 		else {
-			ret = tcpv4_bind_socket(fd, flags, &srv->source_addr, (struct sockaddr_in *)&si->addr.s.from);
+			ret = tcp_bind_socket(fd, flags, &srv->source_addr, &si->addr.s.from);
 		}
 
 		if (ret) {
@@ -354,7 +371,7 @@
 		if (be->iface_name)
 			setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, be->iface_name, be->iface_len + 1);
 #endif
-		ret = tcpv4_bind_socket(fd, flags, &be->source_addr, (struct sockaddr_in *)&si->addr.s.from);
+		ret = tcp_bind_socket(fd, flags, &be->source_addr, &si->addr.s.from);
 		if (ret) {
 			close(fd);
 			if (ret == 1) {
@@ -389,7 +406,7 @@
 	if (global.tune.server_rcvbuf)
                 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
 
-	if ((connect(fd, (struct sockaddr *)&si->addr.s.to, sizeof(struct sockaddr_in)) == -1) &&
+	if ((connect(fd, (struct sockaddr *)&si->addr.s.to, sizeof(struct sockaddr_storage)) == -1) &&
 	    (errno != EINPROGRESS) && (errno != EALREADY) && (errno != EISCONN)) {
 
 		if (errno == EAGAIN || errno == EADDRINUSE) {
@@ -432,7 +449,7 @@
 	fdtab[fd].cb[DIR_WR].b = si->ob;
 
 	fdinfo[fd].peeraddr = (struct sockaddr *)&si->addr.s.to;
-	fdinfo[fd].peerlen = sizeof(struct sockaddr_in);
+	fdinfo[fd].peerlen = sizeof(struct sockaddr_storage);
 
 	fd_insert(fd);
 	EV_FD_SET(fd, DIR_WR);  /* for connect status */
diff --git a/src/standard.c b/src/standard.c
index 1ab2194..27fa374 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -219,10 +219,10 @@
  * a host name, or empty or "*" to indicate INADDR_ANY. NULL is returned
  * if the host part cannot be resolved.
  */
-struct sockaddr_in *str2sa(char *str)
+struct sockaddr_storage *str2sa(char *str)
 {
-	static struct sockaddr_in sa;
-	struct sockaddr_in *ret = NULL;
+	static struct sockaddr_storage sa;
+	struct sockaddr_storage *ret = NULL;
 	char *c;
 	int port;
 
@@ -238,17 +238,17 @@
 	else
 		port = 0;
 
+	sa.ss_family = AF_INET;
+	((struct sockaddr_in *)&sa)->sin_port = htons(port);
 	if (*str == '*' || *str == '\0') { /* INADDR_ANY */
-		sa.sin_addr.s_addr = INADDR_ANY;
+		((struct sockaddr_in *)&sa)->sin_addr.s_addr = INADDR_ANY;
 	}
-	else if (!inet_pton(AF_INET, str, &sa.sin_addr)) {
+	else if (!inet_pton(sa.ss_family, str, &((struct sockaddr_in *)&sa)->sin_addr)) {
 		struct hostent *he = gethostbyname(str);
 		if (!he)
 			goto out;
-		sa.sin_addr = *(struct in_addr *) *(he->h_addr_list);
+		((struct sockaddr_in *)&sa)->sin_addr = *(struct in_addr *) *(he->h_addr_list);
 	}
-	sa.sin_port   = htons(port);
-	sa.sin_family = AF_INET;
 	ret = &sa;
  out:
 	free(str);
@@ -265,10 +265,10 @@
  * name, or empty or "*" to indicate INADDR_ANY. NULL is returned if the host
  * part cannot be resolved.
  */
-struct sockaddr_in *str2sa_range(char *str, int *low, int *high)
+struct sockaddr_storage *str2sa_range(char *str, int *low, int *high)
 {
-	static struct sockaddr_in sa;
-	struct sockaddr_in *ret = NULL;
+	static struct sockaddr_storage sa;
+	struct sockaddr_storage *ret = NULL;
 	char *c;
 	int portl, porth;
 
@@ -293,17 +293,17 @@
 		porth = 0;
 	}
 
+	sa.ss_family = AF_INET;
+	((struct sockaddr_in *)&sa)->sin_port = htonl(portl);
 	if (*str == '*' || *str == '\0') { /* INADDR_ANY */
-		sa.sin_addr.s_addr = INADDR_ANY;
+		((struct sockaddr_in *)&sa)->sin_addr.s_addr = INADDR_ANY;
 	}
-	else if (!inet_pton(AF_INET, str, &sa.sin_addr)) {
+	else if (!inet_pton(sa.ss_family, str, &((struct sockaddr_in *)&sa)->sin_addr)) {
 		struct hostent *he = gethostbyname(str);
 		if (!he)
 			goto out;
-		sa.sin_addr = *(struct in_addr *) *(he->h_addr_list);
+		((struct sockaddr_in *)&sa)->sin_addr = *(struct in_addr *) *(he->h_addr_list);
 	}
-	sa.sin_port   = htons(portl);
-	sa.sin_family = AF_INET;
 	ret = &sa;
 
 	*low = portl;
@@ -387,9 +387,9 @@
 
 
 /*
- * Parse IP address found in url.
+ * Parse IPv4 address found in url.
  */
-int url2ip(const char *addr, struct in_addr *dst)
+int url2ipv4(const char *addr, struct in_addr *dst)
 {
 	int saw_digit, octets, ch;
 	u_char tmp[4], *tp;
@@ -430,18 +430,20 @@
 }
 
 /*
- * Resolve destination server from URL. Convert <str> to a sockaddr_in*.
+ * Resolve destination server from URL. Convert <str> to a sockaddr_storage*.
  */
-int url2sa(const char *url, int ulen, struct sockaddr_in *addr)
+int url2sa(const char *url, int ulen, struct sockaddr_storage *addr)
 {
 	const char *curr = url, *cp = url;
 	int ret, url_code = 0;
 	unsigned int http_code = 0;
 
 	/* Cleanup the room */
-	addr->sin_family = AF_INET;
-	addr->sin_addr.s_addr = 0;
-	addr->sin_port = 0;
+
+	/* FIXME: assume IPv4 only for now */
+	((struct sockaddr_in *)addr)->sin_family = AF_INET;
+	((struct sockaddr_in *)addr)->sin_addr.s_addr = 0;
+	((struct sockaddr_in *)addr)->sin_port = 0;
 
 	/* Firstly, try to find :// pattern */
 	while (curr < url+ulen && url_code != 0x3a2f2f) {
@@ -467,12 +469,12 @@
 			 * be warned this can slow down global daemon performances
 			 * while handling lagging dns responses.
 			 */
-			ret = url2ip(curr, &addr->sin_addr);
+			ret = url2ipv4(curr, &((struct sockaddr_in *)&addr)->sin_addr);
 			if (!ret)
 				return -1;
 			curr += ret;
-			addr->sin_port = (*curr == ':') ? str2uic(++curr) : 80;
-			addr->sin_port = htons(addr->sin_port);
+			((struct sockaddr_in *)addr)->sin_port = (*curr == ':') ? str2uic(++curr) : 80;
+			((struct sockaddr_in *)addr)->sin_port = htons(((struct sockaddr_in *)&addr)->sin_port);
 		}
 		return 0;
 	}