REORG/MEDIUM: replace stream interface protocol functions by a proto pointer

The stream interface now makes use of the socket protocol pointer instead
of the direct functions.
diff --git a/src/backend.c b/src/backend.c
index f125931..4652fba 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -36,6 +36,7 @@
 #include <proto/lb_fwlc.h>
 #include <proto/lb_fwrr.h>
 #include <proto/lb_map.h>
+#include <proto/protocols.h>
 #include <proto/proto_http.h>
 #include <proto/proto_tcp.h>
 #include <proto/queue.h>
@@ -974,9 +975,7 @@
 	 * session's freshly assigned target with the stream interface's.
 	 */
 	stream_interface_prepare(s->req->cons, &stream_sock);
-	s->req->cons->connect = tcp_connect_server;
-	s->req->cons->get_src = getsockname;
-	s->req->cons->get_dst = getpeername;
+
 	/* the target was only on the session, assign it to the SI now */
 	copy_target(&s->req->cons->target, &s->target);
 
@@ -987,13 +986,22 @@
 		stream_sock_get_to_addr(s->req->prod);
 	}
 
+	/* set the correct protocol on the output stream interface */
+	if (s->target.type == TARG_TYPE_SERVER)
+		s->req->cons->proto = target_srv(&s->target)->proto;
+	else if (s->target.type == TARG_TYPE_PROXY) {
+		s->req->cons->proto = protocol_by_family(s->req->cons->addr.to.ss_family);
+		if (!s->req->cons->proto)
+			return SN_ERR_INTERNAL;
+	}
+
 	assign_tproxy_address(s);
 
 	/* flag for logging source ip/port */
 	if (s->fe->options2 & PR_O2_SRC_ADDR)
 		s->req->cons->flags |= SI_FL_SRC_ADDR;
 
-	err = s->req->cons->connect(s->req->cons);
+	err = s->req->cons->proto->connect(s->req->cons);
 
 	if (err != SN_ERR_NONE)
 		return err;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 8bfa4b2..e4ee9a0 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1266,6 +1266,15 @@
 			goto out;
 		}
 		newpeer->addr = *sk;
+		newpeer->proto = protocol_by_family(newpeer->addr.ss_family);
+
+		if (!sk) {
+			Alert("parsing [%s:%d] : Unknown protocol family %d '%s'\n",
+			      file, linenum, newpeer->addr.ss_family, args[2]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+
 		set_host_port(&newpeer->addr, realport);
 
 		if (strcmp(newpeer->id, localpeer) == 0) {
@@ -4080,6 +4089,14 @@
 				goto out;
 			}
 			newsrv->addr = *sk;
+			newsrv->proto = protocol_by_family(newsrv->addr.ss_family);
+
+			if (!sk) {
+				Alert("parsing [%s:%d] : Unknown protocol family %d '%s'\n",
+				      file, linenum, newsrv->addr.ss_family, args[2]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
 			set_host_port(&newsrv->addr, realport);
 
 			newsrv->check_port	= curproxy->defsrv.check_port;
diff --git a/src/peers.c b/src/peers.c
index 31fb8d9..ba57e18 100644
--- a/src/peers.c
+++ b/src/peers.c
@@ -1154,9 +1154,8 @@
 	s->si[0].state = s->si[0].prev_state = SI_ST_EST;
 	s->si[0].err_type = SI_ET_NONE;
 	s->si[0].err_loc = NULL;
-	s->si[0].connect   = NULL;
-	s->si[0].get_src   = NULL;
-	s->si[0].get_dst   = NULL;
+	s->si[0].proto   = NULL;
+	s->si[0].release = NULL;
 	clear_target(&s->si[0].target);
 	s->si[0].exp = TICK_ETERNITY;
 	s->si[0].flags = SI_FL_NONE;
@@ -1173,9 +1172,8 @@
 	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 = tcp_connect_server;
-	s->si[1].get_src = getsockname;
-	s->si[1].get_dst = getpeername;
+	s->si[1].proto = peer->proto;
+	s->si[1].release = NULL;
 	set_target_proxy(&s->si[1].target, s->be);
 	s->si[1].exp = TICK_ETERNITY;
 	s->si[1].flags = SI_FL_NONE;
@@ -1183,7 +1181,6 @@
 		s->si[1].flags |= SI_FL_INDEP_STR;
 
 	stream_interface_prepare(&s->si[1], &stream_sock);
-	s->si[1].release = NULL;
 
 	session_init_srv_conn(s);
 	set_target_proxy(&s->target, s->be);
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index f709e73..478bff5 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -70,6 +70,7 @@
 	.sock_addrlen = sizeof(struct sockaddr_in),
 	.l3_addrlen = 32/8,
 	.accept = &stream_sock_accept,
+	.connect = tcp_connect_server,
 	.bind = tcp_bind_listener,
 	.bind_all = tcp_bind_listeners,
 	.unbind_all = unbind_all_listeners,
@@ -88,6 +89,7 @@
 	.sock_addrlen = sizeof(struct sockaddr_in6),
 	.l3_addrlen = 128/8,
 	.accept = &stream_sock_accept,
+	.connect = tcp_connect_server,
 	.bind = tcp_bind_listener,
 	.bind_all = tcp_bind_listeners,
 	.unbind_all = unbind_all_listeners,
diff --git a/src/protocols.c b/src/protocols.c
index 5b3e081..155636f 100644
--- a/src/protocols.c
+++ b/src/protocols.c
@@ -319,6 +319,18 @@
 	return err;
 }
 
+/* Returns the protocol handler for socket family <family> or NULL if not found */
+struct protocol *protocol_by_family(int family)
+{
+	struct protocol *proto;
+
+	list_for_each_entry(proto, &protocols, list) {
+		if (proto->sock_domain == family)
+			return proto;
+	}
+	return NULL;
+}
+
 /************************************************************************/
 /*           All supported ACL keywords must be declared here.          */
 /************************************************************************/
diff --git a/src/session.c b/src/session.c
index 5cddabd..af3a464 100644
--- a/src/session.c
+++ b/src/session.c
@@ -169,10 +169,8 @@
 	s->si[0].state     = s->si[0].prev_state = SI_ST_EST;
 	s->si[0].err_type  = SI_ET_NONE;
 	s->si[0].err_loc   = NULL;
-	s->si[0].connect   = NULL;
+	s->si[0].proto     = l->proto;
 	s->si[0].release   = NULL;
-	s->si[0].get_src   = getpeername;
-	s->si[0].get_dst   = getsockname;
 	clear_target(&s->si[0].target);
 	s->si[0].exp       = TICK_ETERNITY;
 	s->si[0].flags     = SI_FL_NONE;
@@ -195,10 +193,8 @@
 	s->si[1].err_type  = SI_ET_NONE;
 	s->si[1].conn_retries = 0;  /* used for logging too */
 	s->si[1].err_loc   = NULL;
-	s->si[1].connect   = NULL;
+	s->si[1].proto     = NULL;
 	s->si[1].release   = NULL;
-	s->si[1].get_src   = NULL;
-	s->si[1].get_dst   = NULL;
 	clear_target(&s->si[1].target);
 	s->si[1].sock.shutr= stream_int_shutr;
 	s->si[1].sock.shutw= stream_int_shutw;
@@ -685,7 +681,7 @@
 /*
  * This function handles the transition between the SI_ST_CON state and the
  * SI_ST_EST state. It must only be called after switching from SI_ST_CON (or
- * SI_ST_INI) to SI_ST_EST, but only when a ->connect function is defined.
+ * SI_ST_INI) to SI_ST_EST, but only when a ->proto is defined.
  */
 static void sess_establish(struct session *s, struct stream_interface *si)
 {
@@ -713,7 +709,7 @@
 
 	rep->analysers |= s->fe->fe_rsp_ana | s->be->be_rsp_ana;
 	rep->flags |= BF_READ_ATTACHED; /* producer is now attached */
-	if (si->connect) {
+	if (si->proto) {
 		/* real connections have timeouts */
 		req->wto = s->be->timeout.server;
 		rep->rto = s->be->timeout.server;
@@ -1920,7 +1916,8 @@
 				 */
 				s->req->cons->state = SI_ST_REQ; /* new connection requested */
 				s->req->cons->conn_retries = s->be->conn_retries;
-				if (unlikely(s->req->cons->target.type == TARG_TYPE_APPLET && !s->req->cons->connect)) {
+				if (unlikely(s->req->cons->target.type == TARG_TYPE_APPLET &&
+					     !(s->req->cons->proto && s->req->cons->proto->connect))) {
 					s->req->cons->state = SI_ST_EST; /* connection established */
 					s->rep->flags |= BF_READ_ATTACHED; /* producer is now attached */
 					s->req->wex = TICK_ETERNITY;
diff --git a/src/stream_interface.c b/src/stream_interface.c
index 35ebc30..543cfee 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -331,7 +331,7 @@
 	DPRINTF(stderr, "registering handler %p for si %p (was %p)\n", app, si, si->owner);
 
 	stream_interface_prepare(si, &stream_int_embedded);
-	si->connect = NULL;
+	si->proto = NULL;
 	set_target_applet(&si->target, app);
 	si->applet.state = 0;
 	si->release   = app->release;
@@ -354,7 +354,7 @@
 	DPRINTF(stderr, "registering handler %p for si %p (was %p)\n", fct, si, si->owner);
 
 	stream_interface_prepare(si, &stream_int_task);
-	si->connect = NULL;
+	si->proto = NULL;
 	clear_target(&si->target);
 	si->release   = NULL;
 	si->flags |= SI_FL_WAIT_DATA;