From a41a8b4ee68170109d20b46ef9439ef8eb80f24c Mon Sep 17 00:00:00 2001
From: willy tarreau <willy@wtap.(none)>
Date: Sat, 17 Dec 2005 14:02:24 +0100
Subject: [PATCH] * released 1.1.22 * 'listen' now supports optionnal
 address:port-range lists * 'bind' introduced to add new listen addresses *
 fixed a bug which caused a session to be kept established on a server till  
 it timed out if the client closed during the DATA phase. * the port part of
 each server address can now be empty to make the proxy   connect to the
 server on the same port it was connected to, be an absolute   unsigned number
 to reflect a single port (as in older versions), or an   explicitly signed
 number (+N/-N) to indicate that this offset must be   applied to the port the
 proxy was connected to, when connecting to the   server. * the 'port' server
 option allows the user to specify a different   health-check port than the
 service one. It is mandatory when only relative   ports have been specified
 and check is required. By default, the checks are   sent to the service port.
 * new 'defaults' section which is rather similar to 'listen' except that all 
  values are only used as default values for future 'listen' sections, until  
 a new 'defaults' resets them. At the moment, server options, regexes,  
 cookie names and captures cannot be set in the 'defaults' section. * Makefile
 now optimizes for Ultrasparc by default on Solaris/Sparc * large
 documentation updates and fixes * new 'tests' directory with some debug files

---
 CHANGELOG          |  20 ++
 Makefile           |   2 +-
 TODO               |  73 +++++++
 doc/haproxy.txt    | 257 +++++++++++++++++++---
 haproxy.c          | 533 ++++++++++++++++++++++++++++++++++-----------
 tests/defaults.cfg |  46 ++++
 tests/sockstat.txt |   7 +
 tests/testinet.c   |  27 +++
 8 files changed, 810 insertions(+), 155 deletions(-)
 create mode 100644 tests/defaults.cfg
 create mode 100644 tests/sockstat.txt
 create mode 100644 tests/testinet.c

diff --git a/CHANGELOG b/CHANGELOG
index 59731cc1a..658224b2b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,26 @@
 ChangeLog :
 ===========
 
+2003/09/10 :
+  - 'listen' now supports optionnal address:port-range lists
+  - 'bind' introduced to add new listen addresses
+  - fixed a bug which caused a session to be kept established on a server till
+    it timed out if the client closed during the DATA phase.
+  - the port part of each server address can now be empty to make the proxy
+    connect to the server on the same port it was connected to, be an absolute
+    unsigned number to reflect a single port (as in older versions), or an
+    explicitly signed number (+N/-N) to indicate that this offset must be
+    applied to the port the proxy was connected to, when connecting to the
+    server.
+  - the 'port' server option allows the user to specify a different
+    health-check port than the service one. It is mandatory when only relative
+    ports have been specified and check is required. By default, the checks are
+    sent to the service port.
+  - new 'defaults' section which is rather similar to 'listen' except that all
+    values are only used as default values for future 'listen' sections, until
+    a new 'defaults' resets them. At the moment, server options, regexes,
+    cookie names and captures cannot be set in the 'defaults' section.
+
 2003/05/06 : 1.1.21
   - changed the debug output format so that it now includes the session unique
     ID followed by the instance name at the beginning of each line.
diff --git a/Makefile b/Makefile
index c3cb104d8..45fcad51c 100644
--- a/Makefile
+++ b/Makefile
@@ -31,7 +31,7 @@ COPTS.solaris = -O2 -fomit-frame-pointer -DSOLARIS
 LIBS.solaris = -lnsl -lsocket
 
 # This is for Solaris 8 on UltraSparc2 processor
-COPTS.solarisv9 = -O6 -mcpu=v9 -fomit-frame-pointer -DSOLARIS
+COPTS.solarisv9 = -O6 -mcpu=v9 -mtune=ultrasparc -fomit-frame-pointer -DSOLARIS
 LIBS.solarisv9 = -lnsl -lsocket
 
 # This is for OpenBSD 3.0
diff --git a/TODO b/TODO
index 0eb301614..72301b2bf 100644
--- a/TODO
+++ b/TODO
@@ -27,3 +27,76 @@
 - support environment variables in config file
 - support keep-alive
 - support SSL
+
+===================== demandes ==========================
+ok> 1) �coute sur une plage de ports :
+ok>   listen XXX 1.2.3.4:21000-21060
+ok> 
+ok> 2) �coutes multiples :
+ok>   listen XXX 1.2.3.4:21000
+ok>   bind 2.3.4.5:21001
+ok>   bind 2.3.4.5:21000-21060
+ok> 
+ok> => on en arrive � ceci :
+ok> 
+ok> listen XXX [ address:port ]
+ok>    bind addr:plage-port[,[addr:]plage-port]*
+ok>    bind ...
+ok>    ...
+ok> 
+ok>    => proxy->listen_fd et proxy->listen_addr doivent �tre
+ok> 	  mis dans des listes
+ok>    => OK pour listen, impl�menter le BIND.
+ok> 
+ok> 3) reconnexion sur le m�me port sur le serveur :
+ok> 
+ok>    server XXX 1.2.3.4[:port]
+ok>    si <port> n'est pas sp�cifi�, on utilise le m�me port que celui qui a re�u
+ok>    la connexion. Dans ce cas, il faut pouvoir forcer le port du health-check
+ok>    par un nouveau parametre "port".
+ok> 
+ok> => �a permet les forwardings de plages :
+ok> 
+ok> listen XXX
+ok>    bind 1.2.3.4:10000-11000
+ok>    server 1.2.3.5
+ok> 
+4) param�tres par d�faut :
+   cr�er une section "defaults" qui pr�cise les param�tres par d�faut pour les
+   sections suivantes, concernant les param�tres suivants :
+     ok- les logs
+     ok- les modes (tcp/http)
+     ok- le balancing (round-robin/source)
+     ok- les time-outs
+     ok- maxconn
+     ok- redisp
+     ok- les options
+     ok- le retry
+     ok- les checks
+       - les options des serveurs ?
+       - les filtres et regex ?
+       - les cookies/captures
+
+5) impl�menter "balance source" pour faire un hash sur la source.
+
+6) possibilit� d'un process s�par� par listen :
+   listen XXX
+      fork [ group_id ]
+
+le fait de sp�cifier group_id fera que toutes les instances utilisant le m�me
+identifiant de groupe seront g�r�es par un m�me processus.
+
+7) g�rer un chroot/uid/gid diff�rents par process :
+   listen XXX
+      chroot /truc
+      uid 123
+      gid 456
+
+8) beaucoup de param�tres pourraient �tre sp�cifiques aux serveurs et non
+   aux instances. Exemples :
+   - adresse IP source pour atteindre le serveur
+   - m�thode de health-check (proto, port, ...)
+   - poids
+   - alerte en cas de disparition
+   - le nombre max de sessions � lui envoyer
+
diff --git a/doc/haproxy.txt b/doc/haproxy.txt
index c20c2447f..e6bcfb360 100644
--- a/doc/haproxy.txt
+++ b/doc/haproxy.txt
@@ -1,9 +1,9 @@
 
          		     H A - P r o x y
          		     ---------------
-         		      version 1.1.21
+         		      version 1.1.22
 			      willy tarreau
-			       2003/05/06
+			       2003/09/11
 
 ================
 | Introduction |
@@ -73,6 +73,7 @@ tels que :
 
   - 'global'
   - 'listen'
+  - 'defaults'
 
 Tous les param�tres font r�f�rence � la section d�finie par le dernier mot cl�
 reconnu.
@@ -245,27 +246,54 @@ Exemple :
 
 Les sections de service d�butent par le mot cl� "listen" :
 
-    listen <nom_instance> <adresse_IP>:<port>
+    listen <nom_instance> [ <adresse_IP>:<plage_ports>[,...] ]
 
 - <nom_instance> est le nom de l'instance d�crite. Ce nom sera envoy� dans les
   logs, donc il est souhaitable d'utiliser un nom relatif au service relay�. Aucun
   test n'est effectu� concernant l'unicit� de ce nom, qui n'est pas obligatoire,
-  mais fortement recommand�e. 
+  mais fortement recommand�e.
 
-- <adresse_IP> est l'adresse IP sur laquelle le relais attend ses
-  connexions. L'adresse 0.0.0.0 signifie que les connexions pourront s'effectuer
-  sur toutes les adresses de la machine.
+- <adresse_IP> est l'adresse IP sur laquelle le relais attend ses connexions.
+  L'absence d'adresse ainsi que l'adresse 0.0.0.0 signifient que les connexions
+  pourront s'effectuer sur toutes les adresses de la machine.
 
-- <port> est le num�ro de port TCP sur lequel le relais attend ses
-  connexions. Le couple <adresse_IP>:<port> doit �tre unique pour toutes les
-  instances d'une m�me machine. L'attachement � un port inf�rieur � 1024
-  n�cessite un niveau de privil�ge particulier lors du lancement du programme
-  (ind�pendamment du param�tre 'uid' de la section 'global').
+- <plage_ports> correspond soit � un port, soit � une plage de ports sur
+  lesquels le relais acceptera des connexions pour l'adresse IP sp�cifi�e.
+  Cette plage peut �tre :
+    - soit un port num�rique (ex: '80')
+    - soit une plage constitu�e de deux valeurs s�par�es par un tiret
+      (ex: '2000-2100') repr�sentant les extr�mit�s incluses dans la
+      plage.
+  Il faut faire attention � l'usage des plages, car chaque combinaison
+  <adresse_IP>:<port> consomme une socket, donc un descripteur de fichier.
+  Le couple <adresse_IP>:<port> doit �tre unique pour toutes les  instances
+  d'une m�me machine. L'attachement � un port inf�rieur � 1024 n�cessite un
+  niveau de privil�ge particulier lors du lancement du programme (ind�pendamment
+  du param�tre 'uid' de la section 'global').
 
-Exemple :
+- le couple <adresse_IP>:<plage_ports> peut �tre r�p�t� ind�finiment pour
+  demander au relais d'�couter �galement sur d'autres adresses et/ou d'autres
+  plages de ports. Pour cela, il suffit de s�parer les couples par une virgule.
+
+Exemples :
 ---------
-    listen http_proxy 127.0.0.1:80
+    listen http_proxy :80
+    listen x11_proxy 127.0.0.1:6000-6009
+    listen smtp_proxy 127.0.0.1:25,127.0.0.1:587
+    listen ldap_proxy :389,:663
 
+Si toutes les adresses ne tiennent pas sur une ligne, il est possible d'en
+rajouter � l'aide du mot cl� 'bind'. Dans ce cas, il n'est m�me pas n�cessaire
+de sp�cifier la premi�re adresse sur la ligne listen, ce qui facilite parfois
+l'�criture de configurations :
+
+    bind [ <adresse_IP>:<plage_ports>[,...] ]
+
+Exemples :
+----------
+    listen http_proxy
+        bind :80,:443
+	bind 10.0.0.1:10080,10.0.0.1:10443
 
 2.1) Inhibition d'un service
 ----------------------------
@@ -417,7 +445,9 @@ Le serveur vers lequel sont redirig
 le param�tre "dispatch" sous la forme <adresse_ip>:<port>. Il correspond � un
 serveur d'assignation de cookie dans le cas o� le service consiste � assurer
 uniquement une persistence HTTP, ou bien simplement au serveur destination dans
-le cas de relayage TCP simple.
+le cas de relayage TCP simple. Cet ancien mode ne permet pas de tester l'�tat
+du serveur distant, et il est maintenant recommand� d'utiliser de pr�f�rence
+le mode 'balance'.
 
 Exemple :
 ---------
@@ -432,24 +462,24 @@ Ce param
 
 2.8) Adresse de sortie
 ----------------------
-Il est possible de forcer l'adresse utilis�e pour �tablir les connexions
-vers les serveurs � l'aide du param�tre "source". Il est m�me possible de
-forcer le port, bien que cette fonctionnalit� se limite � des usages tr�s
-sp�cifiques. C'est particuli�rement utile en cas d'adressage multiple, et
-plus g�n�ralement pour permettre aux serveurs de trouver le chemin de
-retour dans des contextes de routage difficiles. Si l'adresse est 0.0.0.0,
-elle sera choisie librement par le systeme. Si le port est 0, il
-sera choisi librement par le syst�me. Il est � noter que depuis la version
-1.1.18, les tests de bon foncitonnement des serveurs seront aussi effectu�s �
-partir de la source sp�cifi�e par ce param�tre.
+Il est possible de forcer l'adresse utilis�e pour �tablir les connexions vers
+les serveurs � l'aide du param�tre "source". Il est m�me possible de forcer le
+port, bien que cette fonctionnalit� se limite � des usages tr�s sp�cifiques.
+C'est particuli�rement utile en cas d'adressage multiple, et plus g�n�ralement
+pour permettre aux serveurs de trouver le chemin de retour dans des contextes de
+routage difficiles. Si l'adresse est '0.0.0.0' ou '*' ou vide, elle sera choisie
+librement par le systeme. Si le port est '0' ou vide, il sera choisi librement
+par le syst�me. Il est � noter que depuis la version 1.1.18, les tests de bon
+foncitonnement des serveurs seront aussi effectu�s � partir de la source
+sp�cifi�e par ce param�tre.
 
 Exemples :
 ----------
-    listen http_proxy 0.0.0.0:80
+    listen http_proxy *:80
    	# toutes les connexions prennent l'adresse 192.168.1.200
 	source 192.168.1.200:0
 
-    listen rlogin_proxy 0.0.0.0:513
+    listen rlogin_proxy *:513
    	# utiliser l'adresse 192.168.1.200 et le port r�serv� 900
 	source 192.168.1.200:900
 
@@ -462,7 +492,7 @@ par le param
 
 Exemple :
 ---------
-    listen http_proxy 0.0.0.0:80
+    listen http_proxy :80
 	mode http
 	cookie SERVERID
 
@@ -538,7 +568,7 @@ par le param
 
 Exemple : le cookie SERVERID peut contenir server01 ou server02
 ---------
-    listen http_proxy 0.0.0.0:80
+    listen http_proxy :80
 	mode http
 	cookie SERVERID
 	dispatch 192.168.1.100:80
@@ -548,7 +578,7 @@ Exemple : le cookie SERVERID peut contenir server01 ou server02
 Attention : la syntaxe a chang� depuis la version 1.0.
 -----------
 
-3) R�partiteur de charge interne
+3) R�partiteur de charge autonome
 =================================
 
 Le relais peut effectuer lui-m�me la r�partition de charge entre les diff�rents
@@ -562,13 +592,63 @@ sp
 Exemple : m�me que pr�c�demment en r�partition interne
 ---------
 
-    listen http_proxy 0.0.0.0:80
+    listen http_proxy :80
 	mode http
 	cookie SERVERID
 	balance roundrobin
 	server web1 192.168.1.1:80 cookie server01
 	server web2 192.168.1.2:80 cookie server02
 
+Depuis la version 1.1.22, il est possible de d�terminer automatiquement le port
+du serveur vers lequel sera envoy�e la connexion, en fonction du port d'�coute
+sur lequel le client s'est connect�. En effet, il y a 4 possibilit�s pour le
+champ <port> de l'adresse serveur :
+
+  - non sp�cifi� ou nul :
+    la connexion sera envoy�e au serveur sur le m�me port que celui sur
+    lequel le relais a re�u la connexion.
+
+  - valeur num�rique (seul cas support� pour les versions ant�rieures) :
+    le serveur recevra la connexion sur le port d�sign�.
+
+  - valeur num�rique pr�c�d�e d'un signe '+' :
+    la connexion sera envoy�e au serveur sur le m�me port que celui sur
+    lequel le relais a re�u la connexion, auquel on ajoute la valeur d�sign�e.
+    
+  - valeur num�rique pr�c�d�e d'un signe '-' :
+    la connexion sera envoy�e au serveur sur le m�me port que celui sur
+    lequel le relais a re�u la connexion, duquel on soustrait la valeur
+    d�sign�e.
+    
+Exemples :
+----------
+
+# m�me que pr�c�demment
+
+    listen http_proxy :80
+	mode http
+	cookie SERVERID
+	balance roundrobin
+	server web1 192.168.1.1 cookie server01
+	server web2 192.168.1.2 cookie server02
+
+# relayage simultan� des ports 80 et 81 et 8080-8089
+
+    listen http_proxy :80,:81,:8080-8089
+	mode http
+	cookie SERVERID
+	balance roundrobin
+	server web1 192.168.1.1 cookie server01
+	server web2 192.168.1.2 cookie server02
+
+# relayage TCP des ports 25, 389 et 663 vers les ports 1025, 1389 et 1663
+
+    listen http_proxy :25,:389,:663
+	mode tcp
+	balance roundrobin
+	server srv1 192.168.1.1:+1000
+	server srv2 192.168.1.2:+1000
+
 
 3.1) Surveillance des serveurs
 ------------------------------
@@ -583,6 +663,7 @@ param
  - inter : 2000
  - rise  : 2
  - fall  : 3
+ - port  : port de connexion du serveur
 
 Le mode par d�faut consiste � �tablir des connexions TCP uniquement. Dans
 certains cas de pannes, des serveurs peuvent continuer � accepter les connexions
@@ -613,6 +694,12 @@ rencontrent n'y soient pas affect
 de cookie envoie un cookie vide, ce qui a pour effet de supprimer un �ventuel
 cookie affect� pr�c�demment.
 
+Depuis la version 1.1.22, il est possible d'envoyer les tests de fonctionnement
+vers un port diff�rent de celui de service. C'est n�cessaire principalement
+pour les configurations o� le serveur n'a pas de port pr�d�fini, par exemple
+lorsqu'il est d�duit du port d'acceptation de la connexion. Pour cela, utiliser
+le param�tre 'port' suivi du num�ro de port devant r�pondre aux requ�tes.
+
 Enfin, depuis la version 1.1.17, il est possible de visualiser rapidement l'�tat
 courant de tous les serveurs. Pour cela, il suffit d'envoyer un signal SIGHUP au
 processus proxy. L'�tat de tous les serveurs de tous les proxies est envoy� dans
@@ -622,7 +709,7 @@ niveau notice.
 
 Exemples :
 ----------
-# m�me que pr�c�demment avec surveillance TCP
+# conf du paragraphe 3) avec surveillance TCP
     listen http_proxy 0.0.0.0:80
 	mode http
 	cookie SERVERID
@@ -668,6 +755,14 @@ Exemples :
 	server web-backup 192.168.1.3:80 cookie server03 check backup
 	server web-excuse 192.168.1.4:80 check backup
 
+# relayage SMTP+TLS avec test du serveur et serveur de backup
+
+    listen http_proxy :25,:587
+	mode tcp
+	balance roundrobin
+	server srv1 192.168.1.1 check port 25 inter 30000 rise 1 fall 2
+	server srv2 192.168.1.2 backup
+
 
 3.2) Reconnexion vers un r�partiteur en cas d'�chec direct
 ----------------------------------------------------------
@@ -731,6 +826,22 @@ Exemple :
     # iptables -t nat -A PREROUTING -i eth0 -p tcp -d 192.168.1.100 \
       --dport 80 -j REDIRECT --to-ports 65000
 
+Remarque :
+----------
+Si le port n'est pas sp�cifi� sur le serveur, c'est le port auquel s'est adress�
+le client qui sera utilis�. Cela permet de relayer tous les ports TCP d'une m�me
+adresse avec une m�me instance et sans utiliser directement le mode transparent.
+
+Exemple :
+---------
+    listen http_proxy 0.0.0.0:65000
+	mode tcp
+	server server01 192.168.1.1 check port 60000
+	server server02 192.168.1.2 check port 60000
+
+    # iptables -t nat -A PREROUTING -i eth0 -p tcp -d 192.168.1.100 \
+      -j REDIRECT --to-ports 65000
+
 
 4.2) Journalisation des connexions
 ----------------------------------
@@ -1003,6 +1114,88 @@ Exemple :
         errorloc 503 http://192.168.114.58/error50x.html
         errorloc 504 http://192.168.114.58/error50x.html
 
+4.6) Changement des valeurs par d�faut
+--------------------------------------
+
+Dans la version 1.1.22 est apparue la notion de valeurs par d�faut, ce qui �vite
+de r�p�ter des param�tres communs � toutes les instances, tels que les timeouts,
+adresses de log, modes de fonctionnement, etc.
+
+Les valeurs par d�faut sont positionn�es dans la derni�re section 'defaults'
+pr�c�dent l'instance qui les utilisera. On peut donc mettre autant de sections
+'defaults' que l'on veut. Il faut juste se rappeler que la pr�sence d'une telle
+section implique une annulation de tous les param�tres par d�faut positionn�s
+pr�c�demment, dans le but de les remplacer.
+
+La section 'defaults' utilise la m�me syntaxe que la section 'listen', aux
+param�tres pr�s qui ne sont pas support�s. Le mot cl� 'defaults' peut accepter
+un commentaire en guise param�tre.
+
+Dans la version 1.1.22, seuls les param�tres suivants peuvent �tre positionn�s
+dans une section 'defaults' :
+  - log (le premier et le second)
+  - mode { tcp, http, health }
+  - balance { roundrobin }
+  - disabled (pour d�sactiver toutes les instances qui suivent)
+  - enabled (pour faire l'op�ration inverse, mais c'est le cas par d�faut)
+  - contimeout, clitimeout, srvtimeout, grace, retries, maxconn
+  - option { redispatch, transparent, keepalive, forwardfor, httplog,
+             dontlognull, persist }
+  - redispatch, redisp, transparent, source { addr:port }
+
+Ne sont pas support�s dans cette version, d'une mani�re g�n�rale, tous les
+param�tres qui n�cessitent de m�moriser autre chose que des adresses IP uniques
+et valeurs num�riques simples :
+  - dispatch, server,
+  - cookie, capture
+  - errorloc
+  - req*, rsp*, 
+
+Enfin, il n'y a pas le moyen, pour le moment, d'invalider un param�tre bool�en
+positionn� par d�faut. Donc si une option est sp�cifi�e dans les param�tres par
+d�faut, le seul moyen de la d�sactiver pour une instance, c'est de changer les
+param�tres par d�faut avant la d�claration de l'instance.
+
+Exemples :
+----------
+    defaults applications TCP
+	log global
+	mode tcp
+	balance roundrobin
+	clitimeout 180000
+	srvtimeout 180000
+	contimeout 4000
+	retries 3
+	redispatch
+
+    listen app_tcp1 10.0.0.1:6000-6063
+	server srv1 192.168.1.1 check port 6000 inter 10000
+	server srv2 192.168.1.2 backup
+
+    listen app_tcp2 10.0.0.2:6000-6063
+	server srv1 192.168.2.1 check port 6000 inter 10000
+	server srv2 192.168.2.2 backup
+    
+    defaults applications HTTP
+	log global
+	mode http
+	option httplog
+	option forwardfor
+	option dontlognull
+	balance roundrobin
+	clitimeout 20000
+	srvtimeout 20000
+	contimeout 4000
+	retries 3
+
+    listen app_http1 10.0.0.1:80-81
+	cookie SERVERID postonly insert indirect
+	capture cookie userid= len 10
+	server srv1 192.168.1.1:+8000 cookie srv1 check port 8080 inter 1000
+	server srv1 192.168.1.2:+8000 cookie srv2 check port 8080 inter 1000
+
+    defaults
+	# section vide qui annule tous les param�tes par d�faut.
 
 =======================
 | Param�trage syst�me |
diff --git a/haproxy.c b/haproxy.c
index ff880a6ed..1d24a912b 100644
--- a/haproxy.c
+++ b/haproxy.c
@@ -53,8 +53,8 @@
 #include <linux/netfilter_ipv4.h>
 #endif
 
-#define HAPROXY_VERSION "1.1.21"
-#define HAPROXY_DATE	"2003/05/06"
+#define HAPROXY_VERSION "1.1.22"
+#define HAPROXY_DATE	"2003/09/11"
 
 /* this is for libc5 for example */
 #ifndef TCP_NODELAY
@@ -295,8 +295,9 @@ int strlcpy2(char *dst, const char *src, int size) {
 #define	MODE_QUIET	16
 
 /* server flags */
-#define SRV_RUNNING	1
-#define SRV_BACKUP	2
+#define SRV_RUNNING	1	/* the server is UP */
+#define SRV_BACKUP	2	/* this server is a backup server */
+#define	SRV_MAPPORTS	4	/* this server uses mapped ports */
 
 /* what to do when a header matches a regex */
 #define ACT_ALLOW	0	/* allow the request */
@@ -349,6 +350,7 @@ struct server {
     char *cookie;			/* the id set in the cookie */
     char *id;				/* just for identification */
     struct sockaddr_in addr;		/* the address to connect to */
+    short check_port;			/* the port to use for the health checks */
     int health;				/* 0->rise-1 = bad; rise->rise+fall-1 = good */
     int rise, fall;			/* time in iterations */
     int inter;				/* time in milliseconds */
@@ -406,10 +408,16 @@ struct session {
     unsigned int uniq_id;		/* unique ID used for the traces */
 };
 
+struct listener {
+    int fd;			/* the listen socket */
+    struct sockaddr_in addr;	/* the address we listen to */
+    struct listener *next;	/* next address or NULL */
+};
+    
+    
 struct proxy {
-    int listen_fd;			/* the listen socket */
+    struct listener *listen;		/* the listen addresses and sockets */
     int state;				/* proxy state */
-    struct sockaddr_in listen_addr;	/* the address we listen to */
     struct sockaddr_in dispatch_addr;	/* the default address to connect to */
     struct server *srv, *cursrv;	/* known servers, current server */
     int nbservers;			/* # of servers */
@@ -522,6 +530,7 @@ static int maxfd = 0;		/* # of the highest fd + 1 */
 static int listeners = 0;	/* # of listeners */
 static int stopping = 0;	/* non zero means stopping in progress */
 static struct timeval now = {0,0};	/* the current date at any moment */
+static struct proxy defproxy;		/* fake proxy used to assign default values on all instances */
 
 static regmatch_t pmatch[MAX_MATCH];  /* rm_so, rm_eo for regular expressions */
 /* this is used to drain data, and as a temporary buffer for sprintf()... */
@@ -788,6 +797,79 @@ struct sockaddr_in *str2sa(char *str) {
 }
 
 
+/*
+ * converts <str> to a list of listeners which are dynamically allocated.
+ * The format is "{addr|'*'}:port[-end][,{addr|'*'}:port[-end]]*", where :
+ *  - <addr> can be empty or "*" to indicate INADDR_ANY ;
+ *  - <port> is a numerical port from 1 to 65535 ;
+ *  - <end> indicates to use the range from <port> to <end> instead (inclusive).
+ * This can be repeated as many times as necessary, separated by a coma.
+ * The <tail> argument is a pointer to a current list which should be appended
+ * to the tail of the new list. The pointer to the new list is returned.
+ */
+struct listener *str2listener(char *str, struct listener *tail) {
+    struct listener *l;
+    char *c, *next, *range, *dupstr;
+    int port, end;
+
+    next = dupstr = strdup(str);
+    
+    while (next && *next) {
+	str = next;
+	/* 1) look for the end of the first address */
+	if ((next = strrchr(str, ',')) != NULL) {
+	    *next++ = 0;
+	}
+
+	/* 2) look for the addr/port delimiter */
+	if ((range = strrchr(str, ':')) != NULL) {
+	    *range++ = 0;
+	}
+	else {
+		Alert("Missing port number: '%s'\n", str);
+	}	    
+
+	/* 3) look for the port-end delimiter */
+	if ((c = strchr(range, '-')) != NULL) {
+	    *c++ = 0;
+	    end = atol(c);
+	}
+	else {
+	    end = atol(range);
+	}
+
+	for (port = atol(range); port <= end; port++) {
+	    l = (struct listener *)calloc(1, sizeof(struct listener));
+	    l->next = tail;
+	    tail = l;
+
+	    if (*str == '*' || *str == '\0') { /* INADDR_ANY */
+		l->addr.sin_addr.s_addr = INADDR_ANY;
+	    }
+	    else if (
+#ifndef SOLARIS
+		     !inet_aton(str, &l->addr.sin_addr)
+#else
+		     !inet_pton(AF_INET, str, &l->addr.sin_addr)
+#endif
+		     ) {
+		struct hostent *he;
+		
+		if ((he = gethostbyname(str)) == NULL) {
+		    Alert("Invalid server name: '%s'\n", str);
+		}
+		else
+		    l->addr.sin_addr = *(struct in_addr *) *(he->h_addr_list);
+	    }
+	    l->addr.sin_port=htons(port);
+	    l->addr.sin_family=AF_INET;
+	} /* end for(port) */
+    } /* end while(next) */
+    free(dupstr);
+    return tail;
+}
+
+
 /*
  * This function sends a syslog message to both log servers of a proxy,
  * or to global log servers if the proxy is NULL.
@@ -1391,6 +1473,18 @@ int connect_server(struct session *s) {
 	}
     }
 
+    /* if this server remaps proxied ports, we'll use
+     * the port the client connected to with an offset. */
+    if (s->srv->state & SRV_MAPPORTS) {
+	struct sockaddr_in sockname;
+	int namelen;
+
+	namelen = sizeof(sockname);
+	if (get_original_dst(s->cli_fd, (struct sockaddr_in *)&sockname, &namelen) == -1)
+	    getsockname(s->cli_fd, (struct sockaddr *)&sockname, &namelen);
+	s->srv_addr.sin_port = htons(ntohs(s->srv_addr.sin_port) + ntohs(sockname.sin_port));
+    }
+
     if ((fd = s->srv_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
 	qfprintf(stderr, "Cannot get a server socket.\n");
 	return -1;
@@ -3395,7 +3489,15 @@ int process_srv(struct session *t) {
 	    shutdown(t->srv_fd, SHUT_RD);
 	    t->srv_state = SV_STSHUTR;
 	    return 1;
-	}	
+	}
+	/* end of client read and no more data to send */
+	else if ((c == CL_STSHUTR || c == CL_STCLOSE) && (req->l == 0)) {
+	    FD_CLR(t->srv_fd, StaticWriteEvent);
+	    tv_eternity(&t->swexpire);
+	    shutdown(t->srv_fd, SHUT_WR);
+	    t->srv_state = SV_STSHUTW;
+	    return 1;
+	}
 	/* read timeout */
 	else if (tv_cmp2_ms(&t->srexpire, &now) <= 0) {
 	    FD_CLR(t->srv_fd, StaticReadEvent);
@@ -3640,6 +3742,7 @@ int process_session(struct task *t) {
  */
 int process_chk(struct task *t) {
     struct server *s = t->context;
+    struct sockaddr_in sa;
     int fd = s->curfd;
     int one = 1;
 
@@ -3660,6 +3763,10 @@ int process_chk(struct task *t) {
 		(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != -1)) {
 		//fprintf(stderr, "process_chk: 3\n");
 
+		/* we'll connect to the check port on the server */
+		sa = s->addr;
+		sa.sin_port = htons(s->check_port);
+
 		/* allow specific binding */
 		if (s->proxy->options & PR_O_BIND_SRC &&
 		    bind(fd, (struct sockaddr *)&s->proxy->source_addr, sizeof(s->proxy->source_addr)) == -1) {
@@ -3667,7 +3774,7 @@ int process_chk(struct task *t) {
 		    close(fd);
 		    s->result = -1;
 		}
-		else if ((connect(fd, (struct sockaddr *)&s->addr, sizeof(s->addr)) != -1) || (errno == EINPROGRESS)) {
+		else if ((connect(fd, (struct sockaddr *)&sa, sizeof(sa)) != -1) || (errno == EINPROGRESS)) {
 		    /* OK, connection in progress or established */
 
 		    //fprintf(stderr, "process_chk: 4\n");
@@ -3967,6 +4074,7 @@ int stats(void) {
  */
 static int maintain_proxies(void) {
     struct proxy *p;
+    struct listener *l;
     int tleft; /* time left */
 
     p = proxy;
@@ -3977,13 +4085,17 @@ static int maintain_proxies(void) {
 	while (p) {
 	    if (p->nbconn < p->maxconn) {
 		if (p->state == PR_STIDLE) {
-		    FD_SET(p->listen_fd, StaticReadEvent);
+		    for (l = p->listen; l != NULL; l = l->next) {
+			FD_SET(l->fd, StaticReadEvent);
+		    }
 		    p->state = PR_STRUN;
 		}
 	    }
 	    else {
 		if (p->state == PR_STRUN) {
-		    FD_CLR(p->listen_fd, StaticReadEvent);
+		    for (l = p->listen; l != NULL; l = l->next) {
+			FD_CLR(l->fd, StaticReadEvent);
+		    }
 		    p->state = PR_STIDLE;
 		}
 	    }
@@ -3993,7 +4105,9 @@ static int maintain_proxies(void) {
     else {  /* block all proxies */
 	while (p) {
 	    if (p->state == PR_STRUN) {
-		FD_CLR(p->listen_fd, StaticReadEvent);
+		for (l = p->listen; l != NULL; l = l->next) {
+		    FD_CLR(l->fd, StaticReadEvent);
+		}
 		p->state = PR_STIDLE;
 	    }
 	    p = p->next;
@@ -4010,9 +4124,11 @@ static int maintain_proxies(void) {
 		    Warning("Proxy %s stopped.\n", p->id);
 		    send_log(p, LOG_WARNING, "Proxy %s stopped.\n", p->id);
 
-		    fd_delete(p->listen_fd);
+		    for (l = p->listen; l != NULL; l = l->next) {
+			fd_delete(l->fd);
+			listeners--;
+		    }
 		    p->state = PR_STDISABLED;
-		    listeners--;
 		}
 		else {
 		    tleft = MINTIME(t, tleft);
@@ -4250,6 +4366,15 @@ int cfg_parse_global(char *file, int linenum, char **args) {
 }
 
 
+void init_default_instance() {
+    memset(&defproxy, 0, sizeof(defproxy));
+    defproxy.mode = PR_MODE_TCP;
+    defproxy.state = PR_STNEW;
+    defproxy.maxconn = cfg_maxpconn;
+    defproxy.conn_retries = CONN_RETRIES;
+    defproxy.logfac1 = defproxy.logfac2 = -1; /* log disabled */
+}
+
 /*
  * parse a line in a <listen> section. Returns 0 if OK, -1 if error.
  */
@@ -4258,8 +4383,9 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     struct server *newsrv = NULL;
 
     if (!strcmp(args[0], "listen")) {  /* new proxy */
-	if (strchr(args[2], ':') == NULL) {
-	    Alert("parsing [%s:%d] : '%s' expects <id> and <addr:port> as arguments.\n",
+	if (!*args[1]) {
+	    Alert("parsing [%s:%d] : '%s' expects an <id> argument and\n"
+		  "  optionnally supports [addr1]:port1[-end1]{,[addr]:port[-end]}...\n",
 		  file, linenum, args[0]);
 	    return -1;
 	}
@@ -4271,24 +4397,53 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	curproxy->next = proxy;
 	proxy = curproxy;
 	curproxy->id = strdup(args[1]);
-	curproxy->listen_addr = *str2sa(args[2]);
-	curproxy->state = PR_STNEW;
+	if (strchr(args[2], ':') != NULL)
+	    curproxy->listen = str2listener(args[2], curproxy->listen);
+
 	/* set default values */
-	curproxy->maxconn = cfg_maxpconn;
-	curproxy->conn_retries = CONN_RETRIES;
-	curproxy->options = 0;
-	curproxy->clitimeout = curproxy->contimeout = curproxy->srvtimeout = 0;
-	curproxy->mode = PR_MODE_TCP;
-	curproxy->logfac1 = curproxy->logfac2 = -1; /* log disabled */
-	curproxy->to_log = 0;
+	curproxy->state = defproxy.state;
+	curproxy->maxconn = defproxy.maxconn;
+	curproxy->conn_retries = defproxy.conn_retries;
+	curproxy->options = defproxy.options;
+	curproxy->clitimeout = defproxy.clitimeout;
+	curproxy->contimeout = defproxy.contimeout;
+	curproxy->srvtimeout = defproxy.srvtimeout;
+	curproxy->mode = defproxy.mode;
+	curproxy->logfac1 = defproxy.logfac1;
+	curproxy->logsrv1 = defproxy.logsrv1;
+	curproxy->loglev1 = defproxy.loglev1;
+	curproxy->logfac2 = defproxy.logfac2;
+	curproxy->logsrv2 = defproxy.logsrv2;
+	curproxy->loglev2 = defproxy.loglev2;
+	curproxy->to_log = defproxy.to_log;
+	curproxy->grace  = defproxy.grace;
+	curproxy->source_addr = defproxy.source_addr;
+	return 0;
+    }
+    else if (!strcmp(args[0], "defaults")) {  /* use this one to assign default values */
+	curproxy = &defproxy;
 	return 0;
     }
     else if (curproxy == NULL) {
-	Alert("parsing [%s:%d] : 'listen' expected.\n", file, linenum);
+	Alert("parsing [%s:%d] : 'listen' or 'defaults' expected.\n", file, linenum);
 	return -1;
     }
     
-    if (!strcmp(args[0], "mode")) {  /* sets the proxy mode */
+    if (!strcmp(args[0], "bind")) {  /* new listen addresses */
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
+
+	if (strchr(args[1], ':') == NULL) {
+	    Alert("parsing [%s:%d] : '%s' expects [addr1]:port1[-end1]{,[addr]:port[-end]}... as arguments.\n",
+		  file, linenum, args[0]);
+	    return -1;
+	}
+	curproxy->listen = str2listener(args[1], curproxy->listen);
+	return 0;
+    }
+    else if (!strcmp(args[0], "mode")) {  /* sets the proxy mode */
 	if (!strcmp(args[1], "http")) curproxy->mode = PR_MODE_HTTP;
 	else if (!strcmp(args[1], "tcp")) curproxy->mode = PR_MODE_TCP;
 	else if (!strcmp(args[1], "health")) curproxy->mode = PR_MODE_HEALTH;
@@ -4300,8 +4455,16 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     else if (!strcmp(args[0], "disabled")) {  /* disables this proxy */
 	curproxy->state = PR_STDISABLED;
     }
+    else if (!strcmp(args[0], "enabled")) {  /* enables this proxy (used to revert a disabled default) */
+	curproxy->state = PR_STNEW;
+    }
     else if (!strcmp(args[0], "cookie")) {  /* cookie name */
 	int cur_arg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
+
 	if (curproxy->cookie_name != NULL) {
 	    Alert("parsing [%s:%d] : cookie name already specified. Continuing.\n",
 		  file, linenum);
@@ -4341,12 +4504,17 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	    cur_arg++;
 	}
 	if ((curproxy->options & (PR_O_COOK_RW|PR_O_COOK_IND)) == (PR_O_COOK_RW|PR_O_COOK_IND)) {
-	    Alert("parsing [%s:%d] : cookie 'rewrite' and 'indirect' mode are incompatibles.\n",
+	    Alert("parsing [%s:%d] : cookie 'rewrite' and 'indirect' mode are incompatible.\n",
 		  file, linenum);
 	    return -1;
 	}
     }
     else if (!strcmp(args[0], "capture")) {  /* name of a cookie to capture */
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
+
 	if (curproxy->capture_name != NULL) {
 	    Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n",
 		  file, linenum, args[0]);
@@ -4368,7 +4536,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	}
     }
     else if (!strcmp(args[0], "contimeout")) {  /* connect timeout */
-	if (curproxy->contimeout != 0) {
+	if (curproxy->contimeout != defproxy.contimeout) {
 	    Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
 	    return 0;
 	}
@@ -4380,7 +4548,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	curproxy->contimeout = atol(args[1]);
     }
     else if (!strcmp(args[0], "clitimeout")) {  /*  client timeout */
-	if (curproxy->clitimeout != 0) {
+	if (curproxy->clitimeout != defproxy.clitimeout) {
 	    Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n",
 		  file, linenum, args[0]);
 	    return 0;
@@ -4393,7 +4561,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	curproxy->clitimeout = atol(args[1]);
     }
     else if (!strcmp(args[0], "srvtimeout")) {  /*  server timeout */
-	if (curproxy->srvtimeout != 0) {
+	if (curproxy->srvtimeout != defproxy.srvtimeout) {
 	    Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
 	    return 0;
 	}
@@ -4487,6 +4655,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	curproxy->grace = atol(args[1]);
     }
     else if (!strcmp(args[0], "dispatch")) {  /* dispatch address */
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	if (strchr(args[1], ':') == NULL) {
 	    Alert("parsing [%s:%d] : '%s' expects <addr:port> as argument.\n", file, linenum, args[0]);
 	    return -1;
@@ -4508,9 +4680,18 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     }
     else if (!strcmp(args[0], "server")) {  /* server address */
 	int cur_arg;
+	char *rport;
+	char *raddr;
+	short realport;
+	int do_check;
 
-	if (strchr(args[2], ':') == NULL) {
-	    Alert("parsing [%s:%d] : '%s' expects <name> and <addr:port> as arguments.\n",
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
+
+	if (!*args[2]) {
+	    Alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
 		  file, linenum, args[0]);
 	    return -1;
 	}
@@ -4521,9 +4702,34 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	newsrv->next = curproxy->srv;
 	curproxy->srv = newsrv;
 	newsrv->proxy = curproxy;
-	newsrv->id = strdup(args[1]);
-	newsrv->addr = *str2sa(args[2]);
+
+	do_check = 0;
 	newsrv->state = SRV_RUNNING; /* early server setup */
+	newsrv->id = strdup(args[1]);
+
+	/* several ways to check the port component :
+	 *  - IP    => port=+0, relative
+	 *  - IP:   => port=+0, relative
+	 *  - IP:N  => port=N, absolute
+	 *  - IP:+N => port=+N, relative
+	 *  - IP:-N => port=-N, relative
+	 */
+	raddr = strdup(args[2]);
+	rport = strchr(raddr, ':');
+	if (rport) {
+	    *rport++ = 0;
+	    realport = atol(rport);
+	    if (!isdigit((int)*rport))
+		newsrv->state |= SRV_MAPPORTS;
+	} else {
+	    realport = 0;
+	    newsrv->state |= SRV_MAPPORTS;
+	}	    
+
+	newsrv->addr = *str2sa(raddr);
+	newsrv->addr.sin_port = htons(realport);
+	free(raddr);
+
 	newsrv->curfd = -1; /* no health-check in progress */
 	newsrv->inter = DEF_CHKINTR;
 	newsrv->rise = DEF_RISETIME;
@@ -4549,38 +4755,54 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 		newsrv->inter = atol(args[cur_arg + 1]);
 		cur_arg += 2;
 	    }
+	    else if (!strcmp(args[cur_arg], "port")) {
+		newsrv->check_port = atol(args[cur_arg + 1]);
+		cur_arg += 2;
+	    }
 	    else if (!strcmp(args[cur_arg], "backup")) {
 		newsrv->state |= SRV_BACKUP;
 		cur_arg ++;
 	    }
 	    else if (!strcmp(args[cur_arg], "check")) {
-		struct task *t;
-		
-		if ((t = pool_alloc(task)) == NULL) { /* disable this proxy for a while */
-		    Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
-		    return -1;
-		}
-		
-		t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
-		t->wq = LIST_HEAD(wait_queue); /* but already has a wait queue assigned */
-		t->state = TASK_IDLE;
-		t->process = process_chk;
-		t->context = newsrv;
-		
-		if (curproxy->state != PR_STDISABLED) {
-		    tv_delayfrom(&t->expire, &now, newsrv->inter); /* check this every ms */
-		    task_queue(t);
-		    task_wakeup(&rq, t);
-		}
-		
+		do_check = 1;
 		cur_arg += 1;
 	    }
 	    else {
-		Alert("parsing [%s:%d] : server %s only supports options 'cookie', 'check', 'inter', 'rise' and 'fall'.\n",
+		Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise' and 'fall'.\n",
 		      file, linenum, newsrv->id);
 		return -1;
 	    }
 	}
+
+	if (do_check) {
+	    struct task *t;
+
+	    if (!newsrv->check_port && !(newsrv->state & SRV_MAPPORTS))
+		newsrv->check_port = realport; /* by default */
+	    if (!newsrv->check_port) {
+		Alert("parsing [%s:%d] : server %s has neither service port nor check port. Check has been disabled.\n",
+		      file, linenum, newsrv->id);
+		return -1;
+	    }
+
+	    if ((t = pool_alloc(task)) == NULL) {
+		Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
+		return -1;
+	    }
+		
+	    t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
+	    t->wq = LIST_HEAD(wait_queue); /* but already has a wait queue assigned */
+	    t->state = TASK_IDLE;
+	    t->process = process_chk;
+	    t->context = newsrv;
+		
+	    if (curproxy->state != PR_STDISABLED) {
+		tv_delayfrom(&t->expire, &now, newsrv->inter); /* check this every ms */
+		task_queue(t);
+		task_wakeup(&rq, t);
+	    }
+	}
+
 	curproxy->nbservers++;
     }
     else if (!strcmp(args[0], "log")) {  /* syslog server address */
@@ -4643,8 +4865,8 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	}
     }
     else if (!strcmp(args[0], "source")) {  /* address to which we bind when connecting */
-	if (strchr(args[1], ':') == NULL) {
-	    Alert("parsing [%s:%d] : '%s' expects <addr:port> as argument.\n",
+	if (!*args[1]) {
+	    Alert("parsing [%s:%d] : '%s' expects <addr>[:<port>] as argument.\n",
 		  file, linenum, "source");
 	    return -1;
 	}
@@ -4654,6 +4876,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     }
     else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) {  /* replace request header from a regex */
 	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	
 	if (*(args[1]) == 0 || *(args[2]) == 0) {
 	    Alert("parsing [%s:%d] : '%s' expects <search> and <replace> as arguments.\n",
@@ -4671,6 +4897,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     }
     else if (!strcmp(args[0], "reqdel")) {  /* delete request header from a regex */
 	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	
 	if (*(args[1]) == 0) {
 	    Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@@ -4687,6 +4917,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     }
     else if (!strcmp(args[0], "reqdeny")) {  /* deny a request if a header matches this regex */
 	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	
 	if (*(args[1]) == 0) {
 	    Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@@ -4703,6 +4937,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     }
     else if (!strcmp(args[0], "reqpass")) {  /* pass this header without allowing or denying the request */
 	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	
 	if (*(args[1]) == 0) {
 	    Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@@ -4719,6 +4957,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     }
     else if (!strcmp(args[0], "reqallow")) {  /* allow a request if a header matches this regex */
 	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	
 	if (*(args[1]) == 0) {
 	    Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@@ -4735,6 +4977,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     }
     else if (!strcmp(args[0], "reqirep")) {  /* replace request header from a regex, ignoring case */
 	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	
 	if (*(args[1]) == 0 || *(args[2]) == 0) {
 	    Alert("parsing [%s:%d] : '%s' expects <search> and <replace> as arguments.\n",
@@ -4752,6 +4998,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     }
     else if (!strcmp(args[0], "reqidel")) {  /* delete request header from a regex ignoring case */
 	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	
 	if (*(args[1]) == 0) {
 	    Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@@ -4768,6 +5018,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     }
     else if (!strcmp(args[0], "reqideny")) {  /* deny a request if a header matches this regex ignoring case */
 	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	
 	if (*(args[1]) == 0) {
 	    Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@@ -4784,6 +5038,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     }
     else if (!strcmp(args[0], "reqipass")) {  /* pass this header without allowing or denying the request */
 	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	
 	if (*(args[1]) == 0) {
 	    Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@@ -4800,6 +5058,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
     }
     else if (!strcmp(args[0], "reqiallow")) {  /* allow a request if a header matches this regex ignoring case */
 	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	
 	if (*(args[1]) == 0) {
 	    Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@@ -4815,6 +5077,11 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	chain_regex(&curproxy->req_exp, preg, ACT_ALLOW, NULL);
     }
     else if (!strcmp(args[0], "reqadd")) {  /* add request header */
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
+
 	if (curproxy->nb_reqadd >= MAX_NEWHDR) {
 	    Alert("parsing [%s:%d] : too many '%s'. Continuing.\n", file, linenum, args[0]);
 	    return 0;
@@ -4846,6 +5113,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	}
     else if (!strcmp(args[0], "rspdel")) {  /* delete response header from a regex */
 	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	
 	if (*(args[1]) == 0) {
 	    Alert("parsing [%s:%d] : '%s' expects <search> as an argument.\n", file, linenum, args[0]);
@@ -4861,24 +5132,32 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
     }
     else if (!strcmp(args[0], "rspirep")) {  /* replace response header from a regex ignoring case */
-	    regex_t *preg;
-
-	    if (*(args[1]) == 0 || *(args[2]) == 0) {
-		Alert("parsing [%s:%d] : '%s' expects <search> and <replace> as arguments.\n",
-		      file, linenum, args[0]);
-		return -1;
-	    }
-
-	    preg = calloc(1, sizeof(regex_t));
-	    if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
-		Alert("parsing [%s:%d] : bad regular expression '%s'.\n", file, linenum, args[1]);
-		return -1;
-	    }
-	    
-	    chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
+	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
 	}
+
+	if (*(args[1]) == 0 || *(args[2]) == 0) {
+	    Alert("parsing [%s:%d] : '%s' expects <search> and <replace> as arguments.\n",
+		  file, linenum, args[0]);
+	    return -1;
+	}
+
+	preg = calloc(1, sizeof(regex_t));
+	if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
+	    Alert("parsing [%s:%d] : bad regular expression '%s'.\n", file, linenum, args[1]);
+	    return -1;
+	}
+	    
+	chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
+    }
     else if (!strcmp(args[0], "rspidel")) {  /* delete response header from a regex ignoring case */
 	regex_t *preg;
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
 	
 	if (*(args[1]) == 0) {
 	    Alert("parsing [%s:%d] : '%s' expects <search> as an argument.\n", file, linenum, args[0]);
@@ -4894,6 +5173,11 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
     }
     else if (!strcmp(args[0], "rspadd")) {  /* add response header */
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
+
 	if (curproxy->nb_rspadd >= MAX_NEWHDR) {
 	    Alert("parsing [%s:%d] : too many '%s'. Continuing.\n", file, linenum, args[0]);
 	    return 0;
@@ -4910,6 +5194,11 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
 	int errnum;
 	char *err;
 
+	if (curproxy == &defproxy) {
+	    Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+	    return -1;
+	}
+
 	if (*(args[2]) == 0) {
 	    Alert("parsing [%s:%d] : <errorloc> expects <error> and <url> as arguments.\n", file, linenum);
 	    return -1;
@@ -5083,7 +5372,7 @@ int readcfgfile(char *file) {
 	    args[arg] = line;
 	}
 
-	if (!strcmp(args[0], "listen"))  /* new proxy */
+	if (!strcmp(args[0], "listen") || !strcmp(args[0], "defaults"))  /* new proxy */
 	    confsect = CFG_LISTEN;
 	else if (!strcmp(args[0], "global"))  /* global config */
 	    confsect = CFG_GLOBAL;
@@ -5342,70 +5631,70 @@ void init(int argc, char **argv) {
  */
 int start_proxies() {
     struct proxy *curproxy;
+    struct listener *listener;
     int one = 1;
     int fd;
 
     for (curproxy = proxy; curproxy != NULL; curproxy = curproxy->next) {
-
 	if (curproxy->state == PR_STDISABLED)
 	    continue;
 
-	if ((fd = curproxy->listen_fd =
-	     socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-	    Alert("cannot create listening socket for proxy %s. Aborting.\n",
-		  curproxy->id);
-	    return -1;
-	}
+	for (listener = curproxy->listen; listener != NULL; listener = listener->next) {
+	    if ((fd = listener->fd =
+		 socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+		Alert("cannot create listening socket for proxy %s. Aborting.\n",
+		      curproxy->id);
+		return -1;
+	    }
 	
-	if (fd >= global.maxsock) {
-	    Alert("socket(): not enough free sockets for proxy %s. Raise -n argument. Aborting.\n",
-		  curproxy->id);
-	    close(fd);
-	    return -1;
-	}
+	    if (fd >= global.maxsock) {
+		Alert("socket(): not enough free sockets for proxy %s. Raise -n argument. Aborting.\n",
+		      curproxy->id);
+		close(fd);
+		return -1;
+	    }
 
-	if ((fcntl(fd, F_SETFL, O_NONBLOCK) == -1) ||
-	    (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
-			(char *) &one, sizeof(one)) == -1)) {
-	    Alert("cannot make socket non-blocking for proxy %s. Aborting.\n",
-		  curproxy->id);
-	    close(fd);
-	    return -1;
-	}
+	    if ((fcntl(fd, F_SETFL, O_NONBLOCK) == -1) ||
+		(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
+			    (char *) &one, sizeof(one)) == -1)) {
+		Alert("cannot make socket non-blocking for proxy %s. Aborting.\n",
+		      curproxy->id);
+		close(fd);
+		return -1;
+	    }
 
-	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) == -1) {
-	    Alert("cannot do so_reuseaddr for proxy %s. Continuing.\n",
-		  curproxy->id);
-	}
+	    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) == -1) {
+		Alert("cannot do so_reuseaddr for proxy %s. Continuing.\n",
+		      curproxy->id);
+	    }
 	
-	if (bind(fd,
-		 (struct sockaddr *)&curproxy->listen_addr,
-		 sizeof(curproxy->listen_addr)) == -1) {
-	    Alert("cannot bind socket for proxy %s. Aborting.\n",
-		  curproxy->id);
-	    close(fd);
-	    return -1;
-	}
+	    if (bind(fd,
+		     (struct sockaddr *)&listener->addr,
+		     sizeof(listener->addr)) == -1) {
+		Alert("cannot bind socket for proxy %s. Aborting.\n",
+		      curproxy->id);
+		close(fd);
+		return -1;
+	    }
 	
-	if (listen(fd, curproxy->maxconn) == -1) {
-	    Alert("cannot listen to socket for proxy %s. Aborting.\n",
-		  curproxy->id);
-	    close(fd);
-	    return -1;
-	}
+	    if (listen(fd, curproxy->maxconn) == -1) {
+		Alert("cannot listen to socket for proxy %s. Aborting.\n",
+		      curproxy->id);
+		close(fd);
+		return -1;
+	    }
 	
-	/* the function for the accept() event */
-	fdtab[fd].read  = &event_accept;
-	fdtab[fd].write = NULL; /* never called */
-	fdtab[fd].owner = (struct task *)curproxy; /* reference the proxy instead of a task */
-	curproxy->state = PR_STRUN;
-	fdtab[fd].state = FD_STLISTEN;
-	FD_SET(fd, StaticReadEvent);
-	fd_insert(fd);
-	listeners++;
-
+	    /* the function for the accept() event */
+	    fdtab[fd].read  = &event_accept;
+	    fdtab[fd].write = NULL; /* never called */
+	    fdtab[fd].owner = (struct task *)curproxy; /* reference the proxy instead of a task */
+	    curproxy->state = PR_STRUN;
+	    fdtab[fd].state = FD_STLISTEN;
+	    FD_SET(fd, StaticReadEvent);
+	    fd_insert(fd);
+	    listeners++;
+	}
 	send_log(curproxy, LOG_NOTICE, "Proxy %s started.\n", curproxy->id);
-	
     }
     return 0;
 }
diff --git a/tests/defaults.cfg b/tests/defaults.cfg
new file mode 100644
index 000000000..463ed6c6f
--- /dev/null
+++ b/tests/defaults.cfg
@@ -0,0 +1,46 @@
+global
+	#log 127.0.0.1	local0
+	#log 127.0.0.1	local1 notice
+	#log loghost	local0 info
+	maxconn 4096
+	#chroot /var/empty
+	#uid 11
+	#gid 2
+	#daemon
+	#debug
+	#quiet
+
+defaults applications TCP # :10000-10100,192.64.19.22:3129
+	#bind 127.0.0.1:1234-1237
+	log	global
+       	mode	tcp
+       	#mode	http
+	#option	httplog
+	#option	dontlognull
+	balance roundrobin
+        #server  app1_0 127.0.0.2:0# check inter 100 rise 2 fall 5
+	#        server  app1_1 127.0.0.1:8000 check inter 100 rise 2 fall 5
+        #server  app1_2 127.0.0.1:8001 check inter 100 rise 2 fall 5 backup
+	retries	1
+	maxconn	2000
+	contimeout	5000
+	clitimeout	20000
+	srvtimeout	20000
+
+listen	appli3-relais 127.0.0.1:10000-10100#,192.64.19.22:3129
+	#bind 127.0.0.1:1234-1237
+	#log	global
+       	#mode	tcp
+       	#mode	http
+	#option	httplog
+	#option	dontlognull
+	#balance roundrobin
+        server  app1_0 127.0.0.2 check port 113 inter 100 rise 2 fall 5
+	#        server  app1_1 127.0.0.1:8000 check inter 100 rise 2 fall 5
+        #server  app1_2 127.0.0.1:8001 check inter 100 rise 2 fall 5 backup
+	#retries	1
+	#maxconn	2000
+	#contimeout	5000
+	#clitimeout	20000
+	#srvtimeout	20000
+
diff --git a/tests/sockstat.txt b/tests/sockstat.txt
new file mode 100644
index 000000000..3962ae98b
--- /dev/null
+++ b/tests/sockstat.txt
@@ -0,0 +1,7 @@
+willy@pcw:haproxy-1.1.17-pre3$ cat /proc/net/sockstat 
+sockets: used 117
+TCP: inuse 82 orphan 15 tw 561836 alloc 88 mem 75
+UDP: inuse 13
+RAW: inuse 0
+FRAG: inuse 0 memory 0
+
diff --git a/tests/testinet.c b/tests/testinet.c
new file mode 100644
index 000000000..2140115dc
--- /dev/null
+++ b/tests/testinet.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <sys/resource.h>
+#include <time.h>
+#include <regex.h>
+#include <syslog.h>
+
+
+main() {
+    printf("sizeof sockaddr=%d\n", sizeof(struct sockaddr));
+    printf("sizeof sockaddr_in=%d\n", sizeof(struct sockaddr_in));
+    printf("sizeof sockaddr_in6=%d\n", sizeof(struct sockaddr_in6));
+}