MAJOR: listeners: use dual-linked lists to chain listeners with frontends

Navigating through listeners was very inconvenient and error-prone. Not to
mention that listeners were linked in reverse order and reverted afterwards.
In order to definitely get rid of these issues, we now do the following :
  - frontends have a dual-linked list of bind_conf
  - frontends have a dual-linked list of listeners
  - bind_conf have a dual-linked list of listeners
  - listeners have a pointer to their bind_conf

This way we can now navigate from anywhere to anywhere and always find the
proper bind_conf for a given listener, as well as find the list of listeners
for a current bind_conf.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index d2f0dcf..07cc837 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -192,7 +192,7 @@
  * This can be repeated as many times as necessary, separated by a coma.
  * Function returns 1 for success or 0 if error.
  */
-static int str2listener(char *str, struct proxy *curproxy, const char *file, int line)
+static int str2listener(char *str, struct proxy *curproxy, struct bind_conf *bind_conf, const char *file, int line)
 {
 	struct listener *l;
 	char *next, *dupstr;
@@ -266,8 +266,10 @@
 
 		for (; port <= end; port++) {
 			l = (struct listener *)calloc(1, sizeof(struct listener));
-			l->next = curproxy->listen;
-			curproxy->listen = l;
+			LIST_ADDQ(&curproxy->conf.listeners, &l->by_fe);
+			LIST_ADDQ(&bind_conf->listeners, &l->by_bind);
+			l->frontend = curproxy;
+			l->bind_conf = bind_conf;
 
 			l->fd = -1;
 			l->addr = ss;
@@ -1212,6 +1214,8 @@
 	static struct peers *curpeers = NULL;
 	struct peer *newpeer = NULL;
 	const char *err;
+	struct bind_conf *bind_conf;
+	struct listener *l;
 	int err_code = 0;
 
 	if (strcmp(args[0], "peers") == 0) { /* new peers section */
@@ -1340,19 +1344,24 @@
 				curpeers->peers_fe->timeout.connect = 5000;
 				curpeers->peers_fe->accept = peer_accept;
 				curpeers->peers_fe->options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
-				if (!str2listener(args[2], curpeers->peers_fe, file, linenum)) {
+
+				bind_conf = bind_conf_alloc(&curpeers->peers_fe->conf.bind, file, linenum, args[2]);
+
+				if (!str2listener(args[2], curpeers->peers_fe, bind_conf, file, linenum)) {
 					err_code |= ERR_FATAL;
 					goto out;
 				}
+
+				list_for_each_entry(l, &bind_conf->listeners, by_bind) {
+					l->maxconn = ((struct proxy *)curpeers->peers_fe)->maxconn;
+					l->backlog = ((struct proxy *)curpeers->peers_fe)->backlog;
+					l->timeout = &((struct proxy *)curpeers->peers_fe)->timeout.client;
+					l->accept = session_accept;
+					l->handler = process_session;
+					l->analysers |=  ((struct proxy *)curpeers->peers_fe)->fe_req_ana;
+					l->options |= LI_O_UNLIMITED; /* don't make the peers subject to global limits */
+					global.maxsock += l->maxconn;
+				}
-				curpeers->peers_fe->listen->maxconn = ((struct proxy *)curpeers->peers_fe)->maxconn;
-				curpeers->peers_fe->listen->backlog = ((struct proxy *)curpeers->peers_fe)->backlog;
-				curpeers->peers_fe->listen->timeout = &((struct proxy *)curpeers->peers_fe)->timeout.client;
-				curpeers->peers_fe->listen->accept = session_accept;
-				curpeers->peers_fe->listen->frontend =  ((struct proxy *)curpeers->peers_fe);
-				curpeers->peers_fe->listen->handler = process_session;
-				curpeers->peers_fe->listen->analysers |=  ((struct proxy *)curpeers->peers_fe)->fe_req_ana;
-				curpeers->peers_fe->listen->options |= LI_O_UNLIMITED; /* don't make the peers subject to global limits */
-				global.maxsock += curpeers->peers_fe->listen->maxconn;
 			}
 		}
 	} /* neither "peer" nor "peers" */
@@ -1446,19 +1455,16 @@
 
 		/* parse the listener address if any */
 		if ((curproxy->cap & PR_CAP_FE) && *args[2]) {
-			struct listener *new, *last = curproxy->listen;
+			struct listener *l;
 
-			if (!str2listener(args[2], curproxy, file, linenum)) {
+			bind_conf = bind_conf_alloc(&curproxy->conf.bind, file, linenum, args[2]);
+
+			if (!str2listener(args[2], curproxy, bind_conf, file, linenum)) {
 				err_code |= ERR_FATAL;
 				goto out;
 			}
 
-			bind_conf = bind_conf_alloc(&curproxy->conf.bind, file, linenum, args[2]);
-
-			new = curproxy->listen;
-			while (new != last) {
-				new->bind_conf = bind_conf;
-				new = new->next;
+			list_for_each_entry(l, &bind_conf->listeners, by_bind) {
 				global.maxsock++;
 			}
 		}
@@ -1671,7 +1677,7 @@
 
 	/* Now let's parse the proxy-specific keywords */
 	if (!strcmp(args[0], "bind")) {  /* new listen addresses */
-		struct listener *new_listen, *last_listen;
+		struct listener *l;
 		int cur_arg;
 
 		if (curproxy == &defproxy) {
@@ -1689,29 +1695,24 @@
 			goto out;
 		}
 
-		last_listen = curproxy->listen;
 		bind_conf = bind_conf_alloc(&curproxy->conf.bind, file, linenum, args[1]);
 
 		/* NOTE: the following line might create several listeners if there
 		 * are comma-separated IPs or port ranges. So all further processing
 		 * will have to be applied to all listeners created after last_listen.
 		 */
-		if (!str2listener(args[1], curproxy, file, linenum)) {
+		if (!str2listener(args[1], curproxy, bind_conf, file, linenum)) {
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
 
-		new_listen = curproxy->listen;
-		while (new_listen != last_listen) {
-			new_listen->bind_conf = bind_conf;
-			new_listen = new_listen->next;
+		list_for_each_entry(l, &bind_conf->listeners, by_bind) {
+			/* Set default global rights and owner for unix bind  */
+			if (l->addr.ss_family == AF_UNIX)
+				memcpy(&(l->perm.ux), &(global.unix_bind.ux), sizeof(global.unix_bind.ux));
 			global.maxsock++;
 		}
 
-		/* Set default global rights and owner for unix bind  */
-		if (curproxy->listen->addr.ss_family == AF_UNIX) {
-			memcpy(&(curproxy->listen->perm.ux), &(global.unix_bind.ux), sizeof(global.unix_bind.ux));
-		}
 		cur_arg = 2;
 		while (*(args[cur_arg])) {
 			static int bind_dumped;
@@ -1731,7 +1732,7 @@
 					goto out;
 				}
 
-				code = kw->parse(args, cur_arg, curproxy, last_listen, &err);
+				code = kw->parse(args, cur_arg, curproxy, bind_conf, &err);
 				err_code |= code;
 
 				if (code) {
@@ -4517,9 +4518,13 @@
 				 * the server either. We'll check if we have
 				 * a known port on the first listener.
 				 */
-				struct listener *l = curproxy->listen;
-				while (l && !(newsrv->check_port = get_host_port(&l->addr)))
-					l = l->next;
+				struct listener *l;
+
+				list_for_each_entry(l, &curproxy->conf.listeners, by_fe) {
+					newsrv->check_port = get_host_port(&l->addr);
+					if (newsrv->check_port)
+						break;
+				}
 			}
 			if (!newsrv->check_port) {
 				Alert("parsing [%s:%d] : server %s has neither service port nor check port. Check has been disabled.\n",
@@ -5683,7 +5688,7 @@
 			break;
 		}
 
-		if ((curproxy->cap & PR_CAP_FE) && (curproxy->listen == NULL))  {
+		if ((curproxy->cap & PR_CAP_FE) && LIST_ISEMPTY(&curproxy->conf.listeners))  {
 			Alert("config : %s '%s' has no listen address. Please either specify a valid address on the <listen> line, or use the <bind> keyword.\n",
 			      proxy_type_str(curproxy), curproxy->id);
 			cfgerr++;
@@ -6495,20 +6500,6 @@
 				curproxy->be_req_ana |= AN_REQ_PRST_RDP_COOKIE;
 		}
 
-		listener = NULL;
-		while (curproxy->listen) {
-			struct listener *next;
-
-			next = curproxy->listen->next;
-			curproxy->listen->next = listener;
-			listener = curproxy->listen;
-
-			if (!next)
-				break;
-
-			curproxy->listen = next;
-		}
-
 		/* Configure SSL for each bind line.
 		 * Note: if configuration fails at some point, the ->ctx member
 		 * remains NULL so that listeners can later detach.
@@ -6537,8 +6528,7 @@
 
 		/* adjust this proxy's listeners */
 		next_id = 1;
-		listener = curproxy->listen;
-		while (listener) {
+		list_for_each_entry(listener, &curproxy->conf.listeners, by_fe) {
 			if (!listener->luid) {
 				/* listener ID not set, use automatic numbering with first
 				 * spare entry starting with next_luid.
@@ -6569,7 +6559,6 @@
 				listener->backlog = curproxy->backlog;
 			listener->timeout = &curproxy->timeout.client;
 			listener->accept = session_accept;
-			listener->frontend = curproxy;
 			listener->handler = process_session;
 			listener->analysers |= curproxy->fe_req_ana;
 
@@ -6584,9 +6573,6 @@
 			    ((curproxy->mode == PR_MODE_HTTP || listener->bind_conf->is_ssl) &&
 			     !(curproxy->no_options2 & PR_O2_SMARTACC)))
 				listener->options |= LI_O_NOQUICKACK;
-
-			/* We want the use_backend and default_backend rules to apply */
-			listener = listener->next;
 		}
 
 		/* Release unused SSL configs */
diff --git a/src/dumpstats.c b/src/dumpstats.c
index ab4c416..5cd903b 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -181,6 +181,8 @@
                               struct proxy *defpx, const char *file, int line,
                               char **err)
 {
+	struct bind_conf *bind_conf;
+
 	if (!strcmp(args[1], "socket")) {
 		struct sockaddr_un *su;
 		int cur_arg;
@@ -209,6 +211,8 @@
 			}
 		}
 
+		bind_conf = bind_conf_alloc(&global.stats_fe->conf.bind, file, line, args[2]);
+
 		global.stats_sock.state = LI_INIT;
 		global.stats_sock.options = LI_O_UNLIMITED;
 		global.stats_sock.accept = session_accept;
@@ -220,9 +224,9 @@
 		global.stats_sock.perm.ux.level = ACCESS_LVL_OPER; /* default access level */
 		global.stats_sock.maxconn = global.stats_fe->maxconn;
 		global.stats_sock.timeout = &global.stats_fe->timeout.client;
-
-		global.stats_sock.next  = global.stats_fe->listen;
-		global.stats_fe->listen = &global.stats_sock;
+		global.stats_sock.bind_conf = bind_conf;
+		LIST_ADDQ(&global.stats_fe->conf.listeners, &global.stats_sock.by_fe);
+		LIST_ADDQ(&bind_conf->listeners, &global.stats_sock.by_bind);
 
 		cur_arg = 3;
 		while (*args[cur_arg]) {
@@ -969,7 +973,7 @@
 						sv->counters.sps_max = 0;
 					}
 
-				for (li = px->listen; li; li = li->next)
+				list_for_each_entry(li, &px->conf.listeners, by_bind)
 					if (li->counters) {
 						if (clrall)
 							memset(li->counters, 0, sizeof(*li->counters));
@@ -1149,7 +1153,7 @@
 				 * its listeners. The blocked ones will be dequeued.
 				 */
 				px->maxconn = v;
-				for (l = px->listen; l != NULL; l = l->next) {
+				list_for_each_entry(l, &px->conf.listeners, by_bind) {
 					l->maxconn = v;
 					if (l->state == LI_FULL)
 						resume_listener(l);
@@ -2508,17 +2512,17 @@
 				return 0;
 		}
 
-		si->applet.ctx.stats.l = px->listen; /* may be NULL */
+		si->applet.ctx.stats.l = px->conf.listeners.n;
 		si->applet.ctx.stats.px_st = STAT_PX_ST_LI;
 		/* fall through */
 
 	case STAT_PX_ST_LI:
 		/* stats.l has been initialized above */
-		for (; si->applet.ctx.stats.l != NULL; si->applet.ctx.stats.l = l->next) {
+		for (; si->applet.ctx.stats.l != &px->conf.listeners; si->applet.ctx.stats.l = l->by_fe.n) {
 			if (buffer_almost_full(&rep->buf))
 				return 0;
 
-			l = si->applet.ctx.stats.l;
+			l = LIST_ELEM(si->applet.ctx.stats.l, struct listener *, by_fe);
 			if (!l->counters)
 				continue;
 
diff --git a/src/haproxy.c b/src/haproxy.c
index 05df3da..07c4f3f 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -605,7 +605,7 @@
 				break;
 
 		for (px = proxy; px; px = px->next)
-			if (px->state == PR_STNEW && px->listen)
+			if (px->state == PR_STNEW && !LIST_ISEMPTY(&px->conf.listeners))
 				break;
 
 		if (pr || px) {
@@ -1032,21 +1032,17 @@
 			s = s_next;
 		}/* end while(s) */
 
-		l = p->listen;
-		while (l) {
-			l_next = l->next;
+		list_for_each_entry_safe(l, l_next, &p->conf.listeners, by_fe) {
 			unbind_listener(l);
 			delete_listener(l);
-			l->bind_conf = NULL;
+			LIST_DEL(&l->by_fe);
+			LIST_DEL(&l->by_bind);
 			free(l->name);
 			free(l->counters);
 			free(l);
-			l = l_next;
-		}/* end while(l) */
+		}
 
-		bind_back = bind_conf = NULL;
-		/* Release unused SSL configs.
-		 */
+		/* Release unused SSL configs. */
 		list_for_each_entry_safe(bind_conf, bind_back, &p->conf.bind, by_fe) {
 #ifdef USE_OPENSSL
 			ssl_sock_free_all_ctx(bind_conf);
diff --git a/src/listener.c b/src/listener.c
index 09a0257..802ddab 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -504,18 +504,18 @@
 }
 
 /* parse the "accept-proxy" bind keyword */
-static int bind_parse_accept_proxy(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_accept_proxy(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 
-	for (l = px->listen; l != last; l = l->next)
+	list_for_each_entry(l, &conf->listeners, by_bind)
 		l->options |= LI_O_ACC_PROXY;
 
 	return 0;
 }
 
 /* parse the "backlog" bind keyword */
-static int bind_parse_backlog(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_backlog(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 	int val;
@@ -533,19 +533,19 @@
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	for (l = px->listen; l != last; l = l->next)
+	list_for_each_entry(l, &conf->listeners, by_bind)
 		l->backlog = val;
 
 	return 0;
 }
 
 /* parse the "id" bind keyword */
-static int bind_parse_id(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_id(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct eb32_node *node;
-	struct listener *l;
+	struct listener *l, *new;
 
-	if (px->listen->next != last) {
+	if (conf->listeners.n != conf->listeners.p) {
 		if (err)
 			memprintf(err, "'%s' can only be used with a single socket", args[cur_arg]);
 		return ERR_ALERT | ERR_FATAL;
@@ -557,16 +557,17 @@
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	px->listen->luid = atol(args[cur_arg + 1]);
-	px->listen->conf.id.key = px->listen->luid;
+	new = LIST_NEXT(&conf->listeners, struct listener *, by_bind);
+	new->luid = atol(args[cur_arg + 1]);
+	new->conf.id.key = new->luid;
 
-	if (px->listen->luid <= 0) {
+	if (new->luid <= 0) {
 		if (err)
 			memprintf(err, "'%s' : custom id has to be > 0", args[cur_arg]);
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	node = eb32_lookup(&px->conf.used_listener_id, px->listen->luid);
+	node = eb32_lookup(&px->conf.used_listener_id, new->luid);
 	if (node) {
 		l = container_of(node, struct listener, conf.id);
 		if (err)
@@ -576,12 +577,12 @@
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	eb32_insert(&px->conf.used_listener_id, &px->listen->conf.id);
+	eb32_insert(&px->conf.used_listener_id, &new->conf.id);
 	return 0;
 }
 
 /* parse the "maxconn" bind keyword */
-static int bind_parse_maxconn(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_maxconn(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 	int val;
@@ -599,14 +600,14 @@
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	for (l = px->listen; l != last; l = l->next)
+	list_for_each_entry(l, &conf->listeners, by_bind)
 		l->maxconn = val;
 
 	return 0;
 }
 
 /* parse the "name" bind keyword */
-static int bind_parse_name(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_name(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 
@@ -616,14 +617,14 @@
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	for (l = px->listen; l != last; l = l->next)
+	list_for_each_entry(l, &conf->listeners, by_bind)
 		l->name = strdup(args[cur_arg + 1]);
 
 	return 0;
 }
 
 /* parse the "nice" bind keyword */
-static int bind_parse_nice(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_nice(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 	int val;
@@ -641,7 +642,7 @@
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	for (l = px->listen; l != last; l = l->next)
+	list_for_each_entry(l, &conf->listeners, by_bind)
 		l->nice = val;
 
 	return 0;
diff --git a/src/peers.c b/src/peers.c
index 09e45f7..62329dc 100644
--- a/src/peers.c
+++ b/src/peers.c
@@ -1104,7 +1104,7 @@
  */
 static struct session *peer_session_create(struct peer *peer, struct peer_session *ps)
 {
-	struct listener *l = ((struct proxy *)peer->peers->peers_fe)->listen;
+	struct listener *l = LIST_NEXT(&peer->peers->peers_fe->conf.listeners, struct listener *, by_fe);
 	struct proxy *p = (struct proxy *)l->frontend; /* attached frontend */
 	struct session *s;
 	struct http_txn *txn;
@@ -1459,6 +1459,7 @@
 	struct shared_table *st;
 	struct peer * curpeer;
 	struct peer_session *ps;
+	struct listener *listener;
 
 	st = (struct shared_table *)calloc(1,sizeof(struct shared_table));
 	st->table = table;
@@ -1478,7 +1479,8 @@
 		peers->peers_fe->maxconn += 3;
 	}
 
-	peers->peers_fe->listen->maxconn = peers->peers_fe->maxconn;
+	list_for_each_entry(listener, &peers->peers_fe->conf.listeners, by_fe)
+		listener->maxconn = peers->peers_fe->maxconn;
 	st->sync_task = task_new();
 	st->sync_task->process = process_peer_sync;
 	st->sync_task->expire = TICK_ETERNITY;
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 79e4d0d..c197245 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -1701,55 +1701,41 @@
 
 #ifdef CONFIG_HAP_LINUX_TPROXY
 /* parse the "transparent" bind keyword */
-static int bind_parse_transparent(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_transparent(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 
-	if (px->listen->addr.ss_family != AF_INET && px->listen->addr.ss_family != AF_INET6) {
-		if (err)
-			memprintf(err, "'%s' option is only supported on IPv4 and IPv6 sockets", args[cur_arg]);
-		return ERR_ALERT | ERR_FATAL;
+	list_for_each_entry(l, &conf->listeners, by_bind) {
+		if (l->addr.ss_family == AF_INET || l->addr.ss_family == AF_INET6)
+			l->options |= LI_O_FOREIGN;
 	}
 
-	for (l = px->listen; l != last; l = l->next)
-		l->options |= LI_O_FOREIGN;
-
 	return 0;
 }
 #endif
 
 #ifdef TCP_DEFER_ACCEPT
 /* parse the "defer-accept" bind keyword */
-static int bind_parse_defer_accept(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_defer_accept(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 
-	if (px->listen->addr.ss_family != AF_INET && px->listen->addr.ss_family != AF_INET6) {
-		if (err)
-			memprintf(err, "'%s' option is only supported on IPv4 and IPv6 sockets", args[cur_arg]);
-		return ERR_ALERT | ERR_FATAL;
+	list_for_each_entry(l, &conf->listeners, by_bind) {
+		if (l->addr.ss_family == AF_INET || l->addr.ss_family == AF_INET6)
+			l->options |= LI_O_DEF_ACCEPT;
 	}
 
-	for (l = px->listen; l != last; l = l->next)
-		l->options |= LI_O_DEF_ACCEPT;
-
 	return 0;
 }
 #endif
 
 #ifdef TCP_MAXSEG
 /* parse the "mss" bind keyword */
-static int bind_parse_mss(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_mss(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 	int mss;
 
-	if (px->listen->addr.ss_family != AF_INET && px->listen->addr.ss_family != AF_INET6) {
-		if (err)
-			memprintf(err, "'%s' option is only supported on IPv4 and IPv6 sockets", args[cur_arg]);
-		return ERR_ALERT | ERR_FATAL;
-	}
-
 	if (!*args[cur_arg + 1]) {
 		if (err)
 			memprintf(err, "'%s' : missing MSS value", args[cur_arg]);
@@ -1763,8 +1749,10 @@
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	for (l = px->listen; l != last; l = l->next)
-		l->maxseg = mss;
+	list_for_each_entry(l, &conf->listeners, by_bind) {
+		if (l->addr.ss_family == AF_INET || l->addr.ss_family == AF_INET6)
+			l->maxseg = mss;
+	}
 
 	return 0;
 }
@@ -1772,24 +1760,20 @@
 
 #ifdef SO_BINDTODEVICE
 /* parse the "mss" bind keyword */
-static int bind_parse_interface(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_interface(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 
-	if (px->listen->addr.ss_family != AF_INET && px->listen->addr.ss_family != AF_INET6) {
-		if (err)
-			memprintf(err, "'%s' option is only supported on IPv4 and IPv6 sockets", args[cur_arg]);
-		return ERR_ALERT | ERR_FATAL;
-	}
-
 	if (!*args[cur_arg + 1]) {
 		if (err)
 			memprintf(err, "'%s' : missing interface name", args[cur_arg]);
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	for (l = px->listen; l != last; l = l->next)
-		l->interface = strdup(args[cur_arg + 1]);
+	list_for_each_entry(l, &conf->listeners, by_bind) {
+		if (l->addr.ss_family == AF_INET || l->addr.ss_family == AF_INET6)
+			l->interface = strdup(args[cur_arg + 1]);
+	}
 
 	global.last_checks |= LSTCHK_NETADM;
 	return 0;
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index 5f56ad5..58fd06d 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -349,17 +349,11 @@
 }
 
 /* parse the "mode" bind keyword */
-static int bind_parse_mode(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_mode(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 	int val;
 
-	if (px->listen->addr.ss_family != AF_UNIX) {
-		if (err)
-			memprintf(err, "'%s' option is only supported on unix sockets", args[cur_arg]);
-		return ERR_ALERT | ERR_FATAL;
-	}
-
 	if (!*args[cur_arg + 1]) {
 		if (err)
 			memprintf(err, "'%s' : missing mode (octal integer expected)", args[cur_arg]);
@@ -368,24 +362,20 @@
 
 	val = strtol(args[cur_arg + 1], NULL, 8);
 
-	for (l = px->listen; l != last; l = l->next)
-		l->perm.ux.mode = val;
+	list_for_each_entry(l, &conf->listeners, by_bind) {
+		if (l->addr.ss_family == AF_UNIX)
+			l->perm.ux.mode = val;
+	}
 
 	return 0;
 }
 
 /* parse the "gid" bind keyword */
-static int bind_parse_gid(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_gid(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 	int val;
 
-	if (px->listen->addr.ss_family != AF_UNIX) {
-		if (err)
-			memprintf(err, "'%s' option is only supported on unix sockets", args[cur_arg]);
-		return ERR_ALERT | ERR_FATAL;
-	}
-
 	if (!*args[cur_arg + 1]) {
 		if (err)
 			memprintf(err, "'%s' : missing value", args[cur_arg]);
@@ -393,24 +383,20 @@
 	}
 
 	val = atol(args[cur_arg + 1]);
-	for (l = px->listen; l != last; l = l->next)
-		l->perm.ux.gid = val;
+	list_for_each_entry(l, &conf->listeners, by_bind) {
+		if (l->addr.ss_family == AF_UNIX)
+			l->perm.ux.gid = val;
+	}
 
 	return 0;
 }
 
 /* parse the "group" bind keyword */
-static int bind_parse_group(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_group(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 	struct group *group;
 
-	if (px->listen->addr.ss_family != AF_UNIX) {
-		if (err)
-			memprintf(err, "'%s' option is only supported on unix sockets", args[cur_arg]);
-		return ERR_ALERT | ERR_FATAL;
-	}
-
 	if (!*args[cur_arg + 1]) {
 		if (err)
 			memprintf(err, "'%s' : missing group name", args[cur_arg]);
@@ -424,24 +410,20 @@
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	for (l = px->listen; l != last; l = l->next)
-		l->perm.ux.gid = group->gr_gid;
+	list_for_each_entry(l, &conf->listeners, by_bind) {
+		if (l->addr.ss_family == AF_UNIX)
+			l->perm.ux.gid = group->gr_gid;
+	}
 
 	return 0;
 }
 
 /* parse the "uid" bind keyword */
-static int bind_parse_uid(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_uid(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 	int val;
 
-	if (px->listen->addr.ss_family != AF_UNIX) {
-		if (err)
-			memprintf(err, "'%s' option is only supported on unix sockets", args[cur_arg]);
-		return ERR_ALERT | ERR_FATAL;
-	}
-
 	if (!*args[cur_arg + 1]) {
 		if (err)
 			memprintf(err, "'%s' : missing value", args[cur_arg]);
@@ -449,24 +431,20 @@
 	}
 
 	val = atol(args[cur_arg + 1]);
-	for (l = px->listen; l != last; l = l->next)
-		l->perm.ux.uid = val;
+	list_for_each_entry(l, &conf->listeners, by_bind) {
+		if (l->addr.ss_family == AF_UNIX)
+			l->perm.ux.uid = val;
+	}
 
 	return 0;
 }
 
 /* parse the "user" bind keyword */
-static int bind_parse_user(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_user(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	struct listener *l;
 	struct passwd *user;
 
-	if (px->listen->addr.ss_family != AF_UNIX) {
-		if (err)
-			memprintf(err, "'%s' option is only supported on unix sockets", args[cur_arg]);
-		return ERR_ALERT | ERR_FATAL;
-	}
-
 	if (!*args[cur_arg + 1]) {
 		if (err)
 			memprintf(err, "'%s' : missing user name", args[cur_arg]);
@@ -480,8 +458,10 @@
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	for (l = px->listen; l != last; l = l->next)
-		l->perm.ux.uid = user->pw_uid;
+	list_for_each_entry(l, &conf->listeners, by_bind) {
+		if (l->addr.ss_family == AF_UNIX)
+			l->perm.ux.uid = user->pw_uid;
+	}
 
 	return 0;
 }
diff --git a/src/proxy.c b/src/proxy.c
index 35e58b0..8db70b6 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -446,6 +446,7 @@
 	LIST_INIT(&p->logformat);
 	LIST_INIT(&p->format_unique_id);
 	LIST_INIT(&p->conf.bind);
+	LIST_INIT(&p->conf.listeners);
 
 	/* Timeouts are defined as -1 */
 	proxy_reset_timeouts(p);
@@ -475,7 +476,7 @@
 			continue; /* already initialized */
 
 		pxerr = 0;
-		for (listener = curproxy->listen; listener != NULL; listener = listener->next) {
+		list_for_each_entry(listener, &curproxy->conf.listeners, by_fe) {
 			if (listener->state != LI_ASSIGNED)
 				continue; /* already started */
 
@@ -639,7 +640,7 @@
 	Warning("Pausing %s %s.\n", proxy_cap_str(p->cap), p->id);
 	send_log(p, LOG_WARNING, "Pausing %s %s.\n", proxy_cap_str(p->cap), p->id);
 
-	for (l = p->listen; l != NULL; l = l->next) {
+	list_for_each_entry(l, &p->conf.listeners, by_fe) {
 		if (!pause_listener(l))
 			p->state = PR_STERROR;
 	}
@@ -666,7 +667,7 @@
 {
 	struct listener *l;
 
-	for (l = p->listen; l != NULL; l = l->next) {
+	list_for_each_entry(l, &p->conf.listeners, by_fe) {
 		unbind_listener(l);
 		if (l->state >= LI_ASSIGNED) {
 			delete_listener(l);
@@ -694,7 +695,7 @@
 	send_log(p, LOG_WARNING, "Enabling %s %s.\n", proxy_cap_str(p->cap), p->id);
 
 	fail = 0;
-	for (l = p->listen; l != NULL; l = l->next) {
+	list_for_each_entry(l, &p->conf.listeners, by_fe) {
 		if (!resume_listener(l)) {
 			int port;
 
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 2d5e666..056a4e3 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -819,7 +819,7 @@
 }
 
 /* parse the "ciphers" bind keyword */
-static int bind_parse_ciphers(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_ciphers(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	if (!*args[cur_arg + 1]) {
 		if (err)
@@ -827,12 +827,12 @@
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	px->listen->bind_conf->ciphers = strdup(args[cur_arg + 1]);
+	conf->ciphers = strdup(args[cur_arg + 1]);
 	return 0;
 }
 
 /* parse the "crt" bind keyword */
-static int bind_parse_crt(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_crt(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
 	if (!*args[cur_arg + 1]) {
 		if (err)
@@ -840,37 +840,37 @@
 		return ERR_ALERT | ERR_FATAL;
 	}
 
-	if (ssl_sock_load_cert(args[cur_arg + 1], px->listen->bind_conf, px, err) > 0)
+	if (ssl_sock_load_cert(args[cur_arg + 1], conf, px, err) > 0)
 		return ERR_ALERT | ERR_FATAL;
 
 	return 0;
 }
 
 /* parse the "nosslv3" bind keyword */
-static int bind_parse_nosslv3(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_nosslv3(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
-	px->listen->bind_conf->nosslv3 = 1;
+	conf->nosslv3 = 1;
 	return 0;
 }
 
 /* parse the "notlsv1" bind keyword */
-static int bind_parse_notlsv1(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_notlsv1(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
-	px->listen->bind_conf->notlsv1 = 1;
+	conf->notlsv1 = 1;
 	return 0;
 }
 
 /* parse the "prefer-server-ciphers" bind keyword */
-static int bind_parse_psc(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_psc(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
-	px->listen->bind_conf->prefer_server_ciphers = 1;
+	conf->prefer_server_ciphers = 1;
 	return 0;
 }
 
 /* parse the "ssl" bind keyword */
-static int bind_parse_ssl(char **args, int cur_arg, struct proxy *px, struct listener *last, char **err)
+static int bind_parse_ssl(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
-	px->listen->bind_conf->is_ssl = 1;
+	conf->is_ssl = 1;
 	return 0;
 }