MEDIUM: config: support per-listener backlog and maxconn

With SSL, connections are much more expensive, so it is important to be
able to limit concurrent connections per listener in order to limit the
memory usage.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 04f3900..00a5238 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1471,11 +1471,12 @@
 bind [<address>]:<port_range> [, ...]
 bind [<address>]:<port_range> [, ...] interface <interface>
 bind [<address>]:<port_range> [, ...] mss <maxseg>
+bind [<address>]:<port_range> [, ...] backlog <backlog>
+bind [<address>]:<port_range> [, ...] maxconn <maxconn>
 bind [<address>]:<port_range> [, ...] transparent
 bind [<address>]:<port_range> [, ...] id <id>
 bind [<address>]:<port_range> [, ...] name <name>
 bind [<address>]:<port_range> [, ...] defer-accept
-bind [<address>]:<port_range> [, ...] accept-proxy
 bind /<path> [, ...]
 bind /<path> [, ...] mode <mode>
 bind /<path> [, ...] [ user <user> | uid <uid> ]
@@ -1545,6 +1546,17 @@
                   connection's advertised MSS for outgoing segments. This
                   parameter is only compatible with TCP sockets.
 
+    <backlog>     sets the socket's backlog to this value. If unspecified, the
+                  frontend's backlog is used instead.
+
+    <maxconn>     limits the socket to this number of concurrent connections.
+                  Extra connections will remain in the system's backlog until a
+                  connection is released. If unspecified, the limit will be the
+                  same as the frontend's maxconn. Note that in case of port
+                  ranges, the same value will be applied to each socket. This
+                  setting enables different limitations on expensive sockets,
+                  for instance SSL entries which may easily eat all memory.
+
     <id>          is a persistent value for socket ID. Must be positive and
                   unique in the proxy. An unused value will automatically be
                   assigned if unset. Can only be used when defining only a
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 6ff166e..a150503 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1807,6 +1807,58 @@
 #endif
 			}
 
+			if (!strcmp(args[cur_arg], "maxconn")) {
+				struct listener *l;
+				int val;
+
+				if (!*args[cur_arg + 1]) {
+					Alert("parsing [%s:%d] : '%s' : missing maxconn value.\n",
+					      file, linenum, args[0]);
+					err_code |= ERR_ALERT | ERR_FATAL;
+					goto out;
+				}
+
+				val = atol(args[cur_arg + 1]);
+				if (val <= 0) {
+					Alert("parsing [%s:%d] : '%s' : invalid maxconn value %d, must be > 0.\n",
+					      file, linenum, args[0], val);
+					err_code |= ERR_ALERT | ERR_FATAL;
+					goto out;
+				}
+
+				for (l = curproxy->listen; l != last_listen; l = l->next)
+					l->maxconn = val;
+
+				cur_arg += 2;
+				continue;
+			}
+
+			if (!strcmp(args[cur_arg], "backlog")) {
+				struct listener *l;
+				int val;
+
+				if (!*args[cur_arg + 1]) {
+					Alert("parsing [%s:%d] : '%s' : missing backlog value.\n",
+					      file, linenum, args[0]);
+					err_code |= ERR_ALERT | ERR_FATAL;
+					goto out;
+				}
+
+				val = atol(args[cur_arg + 1]);
+				if (val <= 0) {
+					Alert("parsing [%s:%d] : '%s' : invalid backlog value %d, must be > 0.\n",
+					      file, linenum, args[0], val);
+					err_code |= ERR_ALERT | ERR_FATAL;
+					goto out;
+				}
+
+				for (l = curproxy->listen; l != last_listen; l = l->next)
+					l->backlog = val;
+
+				cur_arg += 2;
+				continue;
+			}
+
 			if (!strcmp(args[cur_arg], "ssl")) { /* use ssl certificate */
 #ifdef USE_OPENSSL
 				struct listener *l;
@@ -6888,8 +6940,10 @@
 #endif /* USE_OPENSSL */
 			if (curproxy->options & PR_O_TCP_NOLING)
 				listener->options |= LI_O_NOLINGER;
-			listener->maxconn = curproxy->maxconn;
-			listener->backlog = curproxy->backlog;
+			if (!listener->maxconn)
+				listener->maxconn = curproxy->maxconn;
+			if (!listener->backlog)
+				listener->backlog = curproxy->backlog;
 			listener->timeout = &curproxy->timeout.client;
 			listener->accept = session_accept;
 			listener->frontend = curproxy;