blob: 290432be3236e0c3ddc26db68ae6d4360f3a0753 [file] [log] [blame]
willy tarreau0f7af912005-12-17 12:21:26 +01001/*
willy tarreau5cbea6f2005-12-17 12:48:26 +01002 * HA-Proxy : High Availability-enabled HTTP/TCP proxy
3 * 2000-2002 - Willy Tarreau - willy AT meta-x DOT org.
willy tarreau0f7af912005-12-17 12:21:26 +01004 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version
8 * 2 of the License, or (at your option) any later version.
9 *
willy tarreauef900ab2005-12-17 12:52:52 +010010 * Pending bugs :
willy tarreaua1598082005-12-17 13:08:06 +010011 * - solaris only : sometimes, an HTTP proxy with only a dispatch address causes
12 * the proxy to terminate (no core) if the client breaks the connection during
13 * the response. Seen on 1.1.8pre4, but never reproduced.
willy tarreauef900ab2005-12-17 12:52:52 +010014 * - cookie in insert+indirect mode sometimes segfaults !
15 * - a proxy with an invalid config will prevent the startup even if disabled.
16 *
willy tarreau0f7af912005-12-17 12:21:26 +010017 * ChangeLog :
18 *
willy tarreaua1598082005-12-17 13:08:06 +010019 * 2002/04/18 : 1.1.8
20 * - option "dontlognull"
21 * - fixed "double space" bug in config parser
22 * - fixed an uninitialized server field in case of dispatch
23 * with no existing server which could cause a segfault during
24 * logging.
25 * - the pid logged was always the father's, which was wrong for daemons.
26 * - fixed wrong level "LOG_INFO" for message "proxy started".
27 * 2002/04/13 :
28 * - http logging is now complete :
29 * - ip:port, date, proxy, server
30 * - req_time, conn_time, hdr_time, tot_time
31 * - status, size, request
32 * - source address
willy tarreau9fe663a2005-12-17 13:02:59 +010033 * 2002/04/12 : 1.1.7
34 * - added option forwardfor
35 * - added reqirep, reqidel, reqiallow, reqideny, rspirep, rspidel
36 * - added "log global" in "listen" section.
37 * 2002/04/09 :
38 * - added a new "global" section :
39 * - logs
40 * - debug, quiet, daemon modes
41 * - uid, gid, chroot, nbproc, maxconn
willy tarreaue39cd132005-12-17 13:00:18 +010042 * 2002/04/08 : 1.1.6
43 * - regex are now chained and not limited anymore.
44 * - unavailable server now returns HTTP/502.
45 * - increased per-line args limit to 40
46 * - added reqallow/reqdeny to block some request on matches
47 * - added HTTP 400/403 responses
48 * 2002/04/03 : 1.1.5
willy tarreau535ae7a2005-12-17 12:58:00 +010049 * - connection logging displayed incorrect source address.
50 * - added proxy start/stop and server up/down log events.
51 * - replaced log message short buffers with larger trash.
52 * - enlarged buffer to 8 kB and replace buffer to 4 kB.
willy tarreaue39cd132005-12-17 13:00:18 +010053 * 2002/03/25 : 1.1.4
willy tarreaue47c8d72005-12-17 12:55:52 +010054 * - made rise/fall/interval time configurable
willy tarreaue39cd132005-12-17 13:00:18 +010055 * 2002/03/22 : 1.1.3
willy tarreaub719f002005-12-17 12:55:07 +010056 * - fixed a bug : cr_expire and cw_expire were inverted in CL_STSHUT[WR]
57 * which could lead to loops.
willy tarreaue39cd132005-12-17 13:00:18 +010058 * 2002/03/21 : 1.1.2
willy tarreauef900ab2005-12-17 12:52:52 +010059 * - fixed a bug in buffer management where we could have a loop
60 * between event_read() and process_{cli|srv} if R==BUFSIZE-MAXREWRITE.
61 * => implemented an adjustable buffer limit.
62 * - fixed a bug : expiration of tasks in wait queue timeout is used again,
63 * and running tasks are skipped.
64 * - added some debug lines for accept events.
65 * - send warnings for servers up/down.
willy tarreaue39cd132005-12-17 13:00:18 +010066 * 2002/03/12 : 1.1.1
willy tarreauefae1842005-12-17 12:51:03 +010067 * - fixed a bug in total failure handling
68 * - fixed a bug in timestamp comparison within same second (tv_cmp_ms)
willy tarreaue39cd132005-12-17 13:00:18 +010069 * 2002/03/10 : 1.1.0
willy tarreau5cbea6f2005-12-17 12:48:26 +010070 * - fixed a few timeout bugs
71 * - rearranged the task scheduler subsystem to improve performance,
72 * add new tasks, and make it easier to later port to librt ;
73 * - allow multiple accept() for one select() wake up ;
74 * - implemented internal load balancing with basic health-check ;
75 * - cookie insertion and header add/replace/delete, with better strings
76 * support.
77 * 2002/03/08
78 * - reworked buffer handling to fix a few rewrite bugs, and
79 * improve overall performance.
80 * - implement the "purge" option to delete server cookies in direct mode.
81 * 2002/03/07
82 * - fixed some error cases where the maxfd was not decreased.
83 * 2002/02/26
84 * - now supports transparent proxying, at least on linux 2.4.
85 * 2002/02/12
86 * - soft stop works again (fixed select timeout computation).
87 * - it seems that TCP proxies sometimes cannot timeout.
88 * - added a "quiet" mode.
89 * - enforce file descriptor limitation on socket() and accept().
90 * 2001/12/30 : release of version 1.0.2 : fixed a bug in header processing
willy tarreau3242e862005-12-17 12:27:53 +010091 * 2001/12/19 : release of version 1.0.1 : no MSG_NOSIGNAL on solaris
willy tarreau0f7af912005-12-17 12:21:26 +010092 * 2001/12/16 : release of version 1.0.0.
93 * 2001/12/16 : added syslog capability for each accepted connection.
94 * 2001/11/19 : corrected premature end of files and occasional SIGPIPE.
95 * 2001/10/31 : added health-check type servers (mode health) which replies OK then closes.
96 * 2001/10/30 : added the ability to support standard TCP proxies and HTTP proxies
97 * with or without cookies (use keyword http for this).
98 * 2001/09/01 : added client/server header replacing with regexps.
99 * eg:
100 * cliexp ^(Host:\ [^:]*).* Host:\ \1:80
101 * srvexp ^Server:\ .* Server:\ Apache
102 * 2000/11/29 : first fully working release with complete FSMs and timeouts.
103 * 2000/11/28 : major rewrite
104 * 2000/11/26 : first write
105 *
willy tarreau5cbea6f2005-12-17 12:48:26 +0100106 * TODO:
107 * - handle properly intermediate incomplete server headers. Done ?
108 * - log proxies start/stop
109 * - handle hot-reconfiguration
willy tarreau0f7af912005-12-17 12:21:26 +0100110 *
111 */
112
113#include <stdio.h>
114#include <stdlib.h>
115#include <unistd.h>
116#include <string.h>
117#include <ctype.h>
118#include <sys/time.h>
119#include <sys/types.h>
120#include <sys/socket.h>
121#include <netinet/tcp.h>
122#include <netinet/in.h>
123#include <arpa/inet.h>
124#include <netdb.h>
125#include <fcntl.h>
126#include <errno.h>
127#include <signal.h>
128#include <stdarg.h>
129#include <sys/resource.h>
130#include <time.h>
131#include <regex.h>
132#include <syslog.h>
willy tarreaua1598082005-12-17 13:08:06 +0100133#if defined(TPROXY) && defined(NETFILTER)
willy tarreau5cbea6f2005-12-17 12:48:26 +0100134#include <linux/netfilter_ipv4.h>
135#endif
willy tarreau0f7af912005-12-17 12:21:26 +0100136
willy tarreaua1598082005-12-17 13:08:06 +0100137#define HAPROXY_VERSION "1.1.8"
138#define HAPROXY_DATE "2002/04/18"
willy tarreau0f7af912005-12-17 12:21:26 +0100139
140/* this is for libc5 for example */
141#ifndef TCP_NODELAY
142#define TCP_NODELAY 1
143#endif
144
145#ifndef SHUT_RD
146#define SHUT_RD 0
147#endif
148
149#ifndef SHUT_WR
150#define SHUT_WR 1
151#endif
152
willy tarreau535ae7a2005-12-17 12:58:00 +0100153#define BUFSIZE 8192
willy tarreau0f7af912005-12-17 12:21:26 +0100154
155// reserved buffer space for header rewriting
willy tarreau535ae7a2005-12-17 12:58:00 +0100156#define MAXREWRITE 4096
willy tarreau9fe663a2005-12-17 13:02:59 +0100157#define REQURI_LEN 1024
willy tarreau0f7af912005-12-17 12:21:26 +0100158
willy tarreau5cbea6f2005-12-17 12:48:26 +0100159// max # args on a configuration line
willy tarreaue39cd132005-12-17 13:00:18 +0100160#define MAX_LINE_ARGS 40
willy tarreau5cbea6f2005-12-17 12:48:26 +0100161
willy tarreaue39cd132005-12-17 13:00:18 +0100162// max # of added headers per request
163#define MAX_NEWHDR 10
willy tarreau0f7af912005-12-17 12:21:26 +0100164
165// max # of matches per regexp
166#define MAX_MATCH 10
167
willy tarreau5cbea6f2005-12-17 12:48:26 +0100168/* FIXME: serverid_len and cookiename_len are no longer checked in configuration file */
willy tarreau0f7af912005-12-17 12:21:26 +0100169#define COOKIENAME_LEN 16
170#define SERVERID_LEN 16
171#define CONN_RETRIES 3
172
willy tarreau5cbea6f2005-12-17 12:48:26 +0100173#define CHK_CONNTIME 2000
willy tarreaue47c8d72005-12-17 12:55:52 +0100174#define DEF_CHKINTR 2000
175#define DEF_FALLTIME 3
176#define DEF_RISETIME 2
willy tarreau5cbea6f2005-12-17 12:48:26 +0100177
willy tarreau9fe663a2005-12-17 13:02:59 +0100178/* default connections limit */
179#define DEFAULT_MAXCONN 2000
180
willy tarreau0f7af912005-12-17 12:21:26 +0100181/* how many bits are needed to code the size of an int (eg: 32bits -> 5) */
182#define INTBITS 5
183
184/* show stats this every millisecond, 0 to disable */
185#ifndef STATTIME
186#define STATTIME 2000
187#endif
188
willy tarreau5cbea6f2005-12-17 12:48:26 +0100189/* this reduces the number of calls to select() by choosing appropriate
190 * sheduler precision in milliseconds. It should be near the minimum
191 * time that is needed by select() to collect all events. All timeouts
192 * are rounded up by adding this value prior to pass it to select().
193 */
194#define SCHEDULER_RESOLUTION 9
195
willy tarreau0f7af912005-12-17 12:21:26 +0100196#define MINTIME(old, new) (((new)<0)?(old):(((old)<0||(new)<(old))?(new):(old)))
197#define SETNOW(a) (*a=now)
198
willy tarreau9da061b2005-12-17 12:29:56 +0100199/****** string-specific macros and functions ******/
200/* if a > max, then bound <a> to <max>. The macro returns the new <a> */
201#define UBOUND(a, max) ({ typeof(a) b = (max); if ((a) > b) (a) = b; (a); })
202
203/* if a < min, then bound <a> to <min>. The macro returns the new <a> */
204#define LBOUND(a, min) ({ typeof(a) b = (min); if ((a) < b) (a) = b; (a); })
205
206
207#ifndef HAVE_STRLCPY
208/*
209 * copies at most <size-1> chars from <src> to <dst>. Last char is always
210 * set to 0, unless <size> is 0. The number of chars copied is returned
211 * (excluding the terminating zero).
212 * This code has been optimized for size and speed : on x86, it's 45 bytes
213 * long, uses only registers, and consumes only 4 cycles per char.
214 */
215int strlcpy(char *dst, const char *src, int size) {
216 char *orig = dst;
217 if (size) {
218 while (--size && (*dst = *src)) {
219 src++; dst++;
220 }
221 *dst = 0;
222 }
223 return dst - orig;
224}
225#endif
226
227
willy tarreau0f7af912005-12-17 12:21:26 +0100228#define MEM_OPTIM
229#ifdef MEM_OPTIM
230/*
231 * Returns a pointer to type <type> taken from the
232 * pool <pool_type> or dynamically allocated. In the
233 * first case, <pool_type> is updated to point to the
234 * next element in the list.
235 */
236#define pool_alloc(type) ({ \
237 void *p; \
238 if ((p = pool_##type) == NULL) \
239 p = malloc(sizeof_##type); \
240 else { \
241 pool_##type = *(void **)pool_##type; \
242 } \
243 p; \
244})
245
246/*
247 * Puts a memory area back to the corresponding pool.
248 * Items are chained directly through a pointer that
249 * is written in the beginning of the memory area, so
willy tarreau9da061b2005-12-17 12:29:56 +0100250 * there's no need for any carrier cell. This implies
willy tarreau0f7af912005-12-17 12:21:26 +0100251 * that each memory area is at least as big as one
252 * pointer.
253 */
254#define pool_free(type, ptr) ({ \
255 *(void **)ptr = (void *)pool_##type; \
256 pool_##type = (void *)ptr; \
257})
258
259#else
260#define pool_alloc(type) (calloc(1,sizeof_##type));
261#define pool_free(type, ptr) (free(ptr));
262#endif /* MEM_OPTIM */
263
willy tarreau5cbea6f2005-12-17 12:48:26 +0100264#define sizeof_task sizeof(struct task)
265#define sizeof_session sizeof(struct session)
willy tarreau0f7af912005-12-17 12:21:26 +0100266#define sizeof_buffer sizeof(struct buffer)
267#define sizeof_fdtab sizeof(struct fdtab)
willy tarreau9fe663a2005-12-17 13:02:59 +0100268#define sizeof_requri REQURI_LEN
willy tarreau0f7af912005-12-17 12:21:26 +0100269
willy tarreau5cbea6f2005-12-17 12:48:26 +0100270/* different possible states for the sockets */
willy tarreau0f7af912005-12-17 12:21:26 +0100271#define FD_STCLOSE 0
272#define FD_STLISTEN 1
273#define FD_STCONN 2
274#define FD_STREADY 3
275#define FD_STERROR 4
276
willy tarreau5cbea6f2005-12-17 12:48:26 +0100277/* values for task->state */
willy tarreau0f7af912005-12-17 12:21:26 +0100278#define TASK_IDLE 0
279#define TASK_RUNNING 1
280
willy tarreau5cbea6f2005-12-17 12:48:26 +0100281/* values for proxy->state */
willy tarreau0f7af912005-12-17 12:21:26 +0100282#define PR_STNEW 0
283#define PR_STIDLE 1
284#define PR_STRUN 2
285#define PR_STDISABLED 3
286
willy tarreau5cbea6f2005-12-17 12:48:26 +0100287/* values for proxy->mode */
willy tarreau0f7af912005-12-17 12:21:26 +0100288#define PR_MODE_TCP 0
289#define PR_MODE_HTTP 1
290#define PR_MODE_HEALTH 2
291
willy tarreau5cbea6f2005-12-17 12:48:26 +0100292/* bits for proxy->options */
293#define PR_O_REDISP 1 /* allow reconnection to dispatch in case of errors */
294#define PR_O_TRANSP 2 /* transparent mode : use original DEST as dispatch */
295#define PR_O_COOK_RW 4 /* rewrite all direct cookies with the right serverid */
296#define PR_O_COOK_IND 8 /* keep only indirect cookies */
297#define PR_O_COOK_INS 16 /* insert cookies when not accessing a server directly */
298#define PR_O_COOK_ANY (PR_O_COOK_RW | PR_O_COOK_IND | PR_O_COOK_INS)
299#define PR_O_BALANCE_RR 32 /* balance in round-robin mode */
300#define PR_O_BALANCE (PR_O_BALANCE_RR)
willy tarreau9fe663a2005-12-17 13:02:59 +0100301#define PR_O_KEEPALIVE 64 /* follow keep-alive sessions */
302#define PR_O_FWDFOR 128 /* insert x-forwarded-for with client address */
willy tarreaua1598082005-12-17 13:08:06 +0100303#define PR_O_BIND_SRC 256 /* bind to a specific source address when connect()ing */
304#define PR_O_NULLNOLOG 512 /* a connect without request will not be logged */
willy tarreau9fe663a2005-12-17 13:02:59 +0100305
willy tarreau5cbea6f2005-12-17 12:48:26 +0100306
willy tarreaue39cd132005-12-17 13:00:18 +0100307/* various session flags */
308#define SN_DIRECT 1 /* connection made on the server matching the client cookie */
309#define SN_CLDENY 2 /* a client header matches a deny regex */
310#define SN_CLALLOW 4 /* a client header matches an allow regex */
311#define SN_SVDENY 8 /* a server header matches a deny regex */
312#define SN_SVALLOW 16 /* a server header matches an allow regex */
willy tarreau5cbea6f2005-12-17 12:48:26 +0100313
314/* different possible states for the client side */
willy tarreau0f7af912005-12-17 12:21:26 +0100315#define CL_STHEADERS 0
316#define CL_STDATA 1
317#define CL_STSHUTR 2
318#define CL_STSHUTW 3
319#define CL_STCLOSE 4
320
willy tarreau5cbea6f2005-12-17 12:48:26 +0100321/* different possible states for the server side */
willy tarreau0f7af912005-12-17 12:21:26 +0100322#define SV_STIDLE 0
323#define SV_STCONN 1
324#define SV_STHEADERS 2
325#define SV_STDATA 3
326#define SV_STSHUTR 4
327#define SV_STSHUTW 5
328#define SV_STCLOSE 6
329
330/* result of an I/O event */
331#define RES_SILENT 0 /* didn't happen */
332#define RES_DATA 1 /* data were sent or received */
333#define RES_NULL 2 /* result is 0 (read == 0), or connect without need for writing */
334#define RES_ERROR 3 /* result -1 or error on the socket (eg: connect()) */
335
willy tarreau9fe663a2005-12-17 13:02:59 +0100336/* modes of operation (global.mode) */
willy tarreau0f7af912005-12-17 12:21:26 +0100337#define MODE_DEBUG 1
338#define MODE_STATS 2
339#define MODE_LOG 4
340#define MODE_DAEMON 8
willy tarreau5cbea6f2005-12-17 12:48:26 +0100341#define MODE_QUIET 16
342
343/* server flags */
344#define SRV_RUNNING 1
willy tarreau0f7af912005-12-17 12:21:26 +0100345
willy tarreaue39cd132005-12-17 13:00:18 +0100346/* what to do when a header matches a regex */
347#define ACT_ALLOW 0 /* allow the request */
348#define ACT_REPLACE 1 /* replace the matching header */
349#define ACT_REMOVE 2 /* remove the matching header */
350#define ACT_DENY 3 /* deny the request */
351
willy tarreau9fe663a2005-12-17 13:02:59 +0100352/* configuration sections */
353#define CFG_NONE 0
354#define CFG_GLOBAL 1
355#define CFG_LISTEN 2
356
willy tarreaua1598082005-12-17 13:08:06 +0100357/* fields that need to be logged. They appear as flags in session->logs.logwait */
willy tarreau9fe663a2005-12-17 13:02:59 +0100358#define LW_DATE 1 /* date */
359#define LW_CLIP 2 /* CLient IP */
360#define LW_SVIP 4 /* SerVer IP */
361#define LW_SVID 8 /* server ID */
362#define LW_REQ 16 /* http REQuest */
363#define LW_RESP 32 /* http RESPonse */
364#define LW_PXIP 64 /* proxy IP */
365#define LW_PXID 128 /* proxy ID */
willy tarreaua1598082005-12-17 13:08:06 +0100366#define LW_BYTES 256 /* bytes read from server */
willy tarreau9fe663a2005-12-17 13:02:59 +0100367
willy tarreau0f7af912005-12-17 12:21:26 +0100368/*********************************************************************/
369
370#define LIST_HEAD(a) ((void *)(&(a)))
371
372/*********************************************************************/
373
374struct hdr_exp {
willy tarreaue39cd132005-12-17 13:00:18 +0100375 struct hdr_exp *next;
376 regex_t *preg; /* expression to look for */
377 int action; /* ACT_ALLOW, ACT_REPLACE, ACT_REMOVE, ACT_DENY */
378 char *replace; /* expression to set instead */
willy tarreau0f7af912005-12-17 12:21:26 +0100379};
380
381struct buffer {
382 unsigned int l; /* data length */
383 char *r, *w, *h, *lr; /* read ptr, write ptr, last header ptr, last read */
willy tarreauef900ab2005-12-17 12:52:52 +0100384 char *rlim; /* read limit, used for header rewriting */
willy tarreaua1598082005-12-17 13:08:06 +0100385 unsigned long long total; /* total data read */
willy tarreau0f7af912005-12-17 12:21:26 +0100386 char data[BUFSIZE];
387};
388
389struct server {
390 struct server *next;
willy tarreau5cbea6f2005-12-17 12:48:26 +0100391 int state; /* server state (SRV_*) */
392 int cklen; /* the len of the cookie, to speed up checks */
393 char *cookie; /* the id set in the cookie */
394 char *id; /* just for identification */
willy tarreau0f7af912005-12-17 12:21:26 +0100395 struct sockaddr_in addr; /* the address to connect to */
willy tarreau5cbea6f2005-12-17 12:48:26 +0100396 int health; /* 0->rise-1 = bad; rise->rise+fall-1 = good */
willy tarreaue47c8d72005-12-17 12:55:52 +0100397 int rise, fall; /* time in iterations */
398 int inter; /* time in milliseconds */
willy tarreau5cbea6f2005-12-17 12:48:26 +0100399 int result; /* 0 = connect OK, -1 = connect KO */
400 int curfd; /* file desc used for current test, or -1 if not in test */
willy tarreau535ae7a2005-12-17 12:58:00 +0100401 struct proxy *proxy; /* the proxy this server belongs to */
willy tarreau0f7af912005-12-17 12:21:26 +0100402};
403
willy tarreau5cbea6f2005-12-17 12:48:26 +0100404/* The base for all tasks */
willy tarreau0f7af912005-12-17 12:21:26 +0100405struct task {
406 struct task *next, *prev; /* chaining ... */
407 struct task *rqnext; /* chaining in run queue ... */
willy tarreau5cbea6f2005-12-17 12:48:26 +0100408 struct task *wq; /* the wait queue this task is in */
willy tarreau0f7af912005-12-17 12:21:26 +0100409 int state; /* task state : IDLE or RUNNING */
410 struct timeval expire; /* next expiration time for this task, use only for fast sorting */
willy tarreau5cbea6f2005-12-17 12:48:26 +0100411 int (*process)(struct task *t); /* the function which processes the task */
412 void *context; /* the task's context */
413};
414
415/* WARNING: if new fields are added, they must be initialized in event_accept() */
416struct session {
417 struct task *task; /* the task associated with this session */
willy tarreau0f7af912005-12-17 12:21:26 +0100418 /* application specific below */
419 struct timeval crexpire; /* expiration date for a client read */
420 struct timeval cwexpire; /* expiration date for a client write */
421 struct timeval srexpire; /* expiration date for a server read */
422 struct timeval swexpire; /* expiration date for a server write */
423 struct timeval cnexpire; /* expiration date for a connect */
424 char res_cr, res_cw, res_sr, res_sw;/* results of some events */
425 struct proxy *proxy; /* the proxy this socket belongs to */
426 int cli_fd; /* the client side fd */
427 int srv_fd; /* the server side fd */
428 int cli_state; /* state of the client side */
429 int srv_state; /* state of the server side */
430 int conn_retries; /* number of connect retries left */
willy tarreau5cbea6f2005-12-17 12:48:26 +0100431 int flags; /* some flags describing the session */
willy tarreau0f7af912005-12-17 12:21:26 +0100432 struct buffer *req; /* request buffer */
433 struct buffer *rep; /* response buffer */
434 struct sockaddr_in cli_addr; /* the client address */
435 struct sockaddr_in srv_addr; /* the address to connect to */
willy tarreau5cbea6f2005-12-17 12:48:26 +0100436 struct server *srv; /* the server being used */
willy tarreaua1598082005-12-17 13:08:06 +0100437 struct {
438 int logwait; /* log fields waiting to be collected : LW_* */
439 struct timeval tv_accept; /* date of the accept() (beginning of the session) */
440 long t_request; /* delay before the end of the request arrives, -1 if never occurs */
441 long t_connect; /* delay before the connect() to the server succeeds, -1 if never occurs */
442 long t_data; /* delay before the first data byte from the server ... */
443 unsigned long t_close; /* total session duration */
444 char *uri; /* first line if log needed, NULL otherwise */
445 int status; /* HTTP status from the server, negative if from proxy */
446 long long bytes; /* number of bytes transferred from the server */
447 } logs;
willy tarreau0f7af912005-12-17 12:21:26 +0100448};
449
450struct proxy {
451 int listen_fd; /* the listen socket */
452 int state; /* proxy state */
453 struct sockaddr_in listen_addr; /* the address we listen to */
454 struct sockaddr_in dispatch_addr; /* the default address to connect to */
willy tarreau5cbea6f2005-12-17 12:48:26 +0100455 struct server *srv, *cursrv; /* known servers, current server */
456 int nbservers; /* # of servers */
willy tarreau0f7af912005-12-17 12:21:26 +0100457 char *cookie_name; /* name of the cookie to look for */
458 int clitimeout; /* client I/O timeout (in milliseconds) */
459 int srvtimeout; /* server I/O timeout (in milliseconds) */
460 int contimeout; /* connect timeout (in milliseconds) */
461 char *id; /* proxy id */
462 int nbconn; /* # of active sessions */
463 int maxconn; /* max # of active sessions */
willy tarreau5cbea6f2005-12-17 12:48:26 +0100464 int conn_retries; /* maximum number of connect retries */
465 int options; /* PR_O_REDISP, PR_O_TRANSP */
466 int mode; /* mode = PR_MODE_TCP, PR_MODE_HTTP or PR_MODE_HEALTH */
willy tarreaua1598082005-12-17 13:08:06 +0100467 struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */
willy tarreau0f7af912005-12-17 12:21:26 +0100468 struct proxy *next;
469 struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */
470 char logfac1, logfac2; /* log facility for both servers. -1 = disabled */
willy tarreau9fe663a2005-12-17 13:02:59 +0100471 int to_log; /* things to be logged (LW_*) */
willy tarreau0f7af912005-12-17 12:21:26 +0100472 struct timeval stop_time; /* date to stop listening, when stopping != 0 */
willy tarreaue39cd132005-12-17 13:00:18 +0100473 int nb_reqadd, nb_rspadd;
474 struct hdr_exp *req_exp; /* regular expressions for request headers */
475 struct hdr_exp *rsp_exp; /* regular expressions for response headers */
476 char *req_add[MAX_NEWHDR], *rsp_add[MAX_NEWHDR]; /* headers to be added */
willy tarreau0f7af912005-12-17 12:21:26 +0100477 int grace; /* grace time after stop request */
478};
479
480/* info about one given fd */
481struct fdtab {
482 int (*read)(int fd); /* read function */
483 int (*write)(int fd); /* write function */
484 struct task *owner; /* the session (or proxy) associated with this fd */
485 int state; /* the state of this fd */
486};
487
488/*********************************************************************/
489
willy tarreau0f7af912005-12-17 12:21:26 +0100490int cfg_maxpconn = 2000; /* # of simultaneous connections per proxy (-N) */
willy tarreau0f7af912005-12-17 12:21:26 +0100491char *cfg_cfgfile = NULL; /* configuration file */
492char *progname = NULL; /* program name */
493int pid; /* current process id */
willy tarreau9fe663a2005-12-17 13:02:59 +0100494
495/* global options */
496static struct {
497 int uid;
498 int gid;
499 int nbproc;
500 int maxconn;
501 int maxsock; /* max # of sockets */
502 int mode;
503 char *chroot;
504 int logfac1, logfac2;
505 struct sockaddr_in logsrv1, logsrv2;
506} global = {
507 logfac1 : -1,
508 logfac2 : -1,
509 /* others NULL OK */
510};
511
willy tarreau0f7af912005-12-17 12:21:26 +0100512/*********************************************************************/
513
514fd_set *ReadEvent,
515 *WriteEvent,
516 *StaticReadEvent,
517 *StaticWriteEvent;
518
519void **pool_session = NULL,
520 **pool_buffer = NULL,
521 **pool_fdtab = NULL,
willy tarreau9fe663a2005-12-17 13:02:59 +0100522 **pool_requri = NULL,
willy tarreau5cbea6f2005-12-17 12:48:26 +0100523 **pool_task = NULL;
willy tarreau0f7af912005-12-17 12:21:26 +0100524
525struct proxy *proxy = NULL; /* list of all existing proxies */
526struct fdtab *fdtab = NULL; /* array of all the file descriptors */
willy tarreau5cbea6f2005-12-17 12:48:26 +0100527struct task *rq = NULL; /* global run queue */
528struct task wait_queue = { /* global wait queue */
529 prev:LIST_HEAD(wait_queue),
530 next:LIST_HEAD(wait_queue)
531};
willy tarreau0f7af912005-12-17 12:21:26 +0100532
willy tarreau0f7af912005-12-17 12:21:26 +0100533static int totalconn = 0; /* total # of terminated sessions */
534static int actconn = 0; /* # of active sessions */
535static int maxfd = 0; /* # of the highest fd + 1 */
536static int listeners = 0; /* # of listeners */
537static int stopping = 0; /* non zero means stopping in progress */
538static struct timeval now = {0,0}; /* the current date at any moment */
539
540static regmatch_t pmatch[MAX_MATCH]; /* rm_so, rm_eo for regular expressions */
541static char trash[BUFSIZE];
542
543/*
544 * Syslog facilities and levels
545 */
546
547#define MAX_SYSLOG_LEN 1024
548#define NB_LOG_FACILITIES 24
549const char *log_facilities[NB_LOG_FACILITIES] = {
550 "kern", "user", "mail", "daemon",
551 "auth", "syslog", "lpr", "news",
552 "uucp", "cron", "auth2", "ftp",
553 "ntp", "audit", "alert", "cron2",
554 "local0", "local1", "local2", "local3",
555 "local4", "local5", "local6", "local7"
556};
557
558
559#define NB_LOG_LEVELS 8
560const char *log_levels[NB_LOG_LEVELS] = {
561 "emerg", "alert", "crit", "err",
562 "warning", "notice", "info", "debug"
563};
564
565#define SYSLOG_PORT 514
566
567const char *monthname[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
568 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
569#define MAX_HOSTNAME_LEN 32
570static char hostname[MAX_HOSTNAME_LEN] = "";
571
willy tarreaua1598082005-12-17 13:08:06 +0100572const char *HTTP_400 =
573 "HTTP/1.0 400 Bad request\r\n"
willy tarreaue39cd132005-12-17 13:00:18 +0100574 "Cache-Control: no-cache\r\n"
575 "Connection: close\r\n"
576 "\r\n"
willy tarreaua1598082005-12-17 13:08:06 +0100577 "400 Bad request : Your browser sent an invalid request.\r\n";
willy tarreaue39cd132005-12-17 13:00:18 +0100578
willy tarreaua1598082005-12-17 13:08:06 +0100579const char *HTTP_403 =
580 "HTTP/1.0 403 Forbidden\r\n"
willy tarreaue39cd132005-12-17 13:00:18 +0100581 "Cache-Control: no-cache\r\n"
582 "Connection: close\r\n"
583 "\r\n"
willy tarreaua1598082005-12-17 13:08:06 +0100584 "403 Forbidden : Request forbidden by administrative rules.\r\n";
willy tarreaue39cd132005-12-17 13:00:18 +0100585
586const char *HTTP_502 =
587 "HTTP/1.0 502 Proxy Error\r\n"
588 "Cache-Control: no-cache\r\n"
589 "Connection: close\r\n"
590 "\r\n"
591 "502 Proxy Error : No server is available to handle this request.\r\n";
592
willy tarreau0f7af912005-12-17 12:21:26 +0100593/*********************************************************************/
594/* statistics ******************************************************/
595/*********************************************************************/
596
597static int stats_tsk_lsrch, stats_tsk_rsrch,
598 stats_tsk_good, stats_tsk_right, stats_tsk_left,
599 stats_tsk_new, stats_tsk_nsrch;
600
601
602/*********************************************************************/
603/* function prototypes *********************************************/
604/*********************************************************************/
605
606int event_accept(int fd);
607int event_cli_read(int fd);
608int event_cli_write(int fd);
609int event_srv_read(int fd);
610int event_srv_write(int fd);
willy tarreau5cbea6f2005-12-17 12:48:26 +0100611int process_session(struct task *t);
willy tarreau0f7af912005-12-17 12:21:26 +0100612
613/*********************************************************************/
614/* general purpose functions ***************************************/
615/*********************************************************************/
616
617void display_version() {
618 printf("HA-Proxy version " HAPROXY_VERSION " " HAPROXY_DATE"\n");
willy tarreau5cbea6f2005-12-17 12:48:26 +0100619 printf("Copyright 2000-2002 Willy Tarreau <willy AT meta-x DOT org>\n\n");
willy tarreau0f7af912005-12-17 12:21:26 +0100620}
621
622/*
623 * This function prints the command line usage and exits
624 */
625void usage(char *name) {
626 display_version();
627 fprintf(stderr,
628 "Usage : %s -f <cfgfile> [ -vd"
629#if STATTIME > 0
630 "sl"
631#endif
632 "D ] [ -n <maxconn> ] [ -N <maxpconn> ]\n"
633 " -v displays version\n"
634 " -d enters debug mode\n"
635#if STATTIME > 0
636 " -s enables statistics output\n"
637 " -l enables long statistics format\n"
638#endif
willy tarreau5cbea6f2005-12-17 12:48:26 +0100639 " -D goes daemon ; implies -q\n"
640 " -q quiet mode : don't display messages\n"
willy tarreau0f7af912005-12-17 12:21:26 +0100641 " -n sets the maximum total # of connections (%d)\n"
642 " -N sets the default, per-proxy maximum # of connections (%d)\n\n",
willy tarreau9fe663a2005-12-17 13:02:59 +0100643 name, DEFAULT_MAXCONN, cfg_maxpconn);
willy tarreau0f7af912005-12-17 12:21:26 +0100644 exit(1);
645}
646
647
648/*
649 * Displays the message on stderr with the date and pid.
650 */
651void Alert(char *fmt, ...) {
652 va_list argp;
653 struct timeval tv;
654 struct tm *tm;
655
willy tarreau9fe663a2005-12-17 13:02:59 +0100656 if (!(global.mode & MODE_QUIET)) {
willy tarreau5cbea6f2005-12-17 12:48:26 +0100657 va_start(argp, fmt);
willy tarreau0f7af912005-12-17 12:21:26 +0100658
willy tarreau5cbea6f2005-12-17 12:48:26 +0100659 gettimeofday(&tv, NULL);
660 tm=localtime(&tv.tv_sec);
661 fprintf(stderr, "[ALERT] %03d/%02d%02d%02d (%d) : ",
willy tarreaua1598082005-12-17 13:08:06 +0100662 tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)getpid());
willy tarreau5cbea6f2005-12-17 12:48:26 +0100663 vfprintf(stderr, fmt, argp);
664 fflush(stderr);
665 va_end(argp);
666 }
willy tarreau0f7af912005-12-17 12:21:26 +0100667}
668
669
670/*
671 * Displays the message on stderr with the date and pid.
672 */
673void Warning(char *fmt, ...) {
674 va_list argp;
675 struct timeval tv;
676 struct tm *tm;
677
willy tarreau9fe663a2005-12-17 13:02:59 +0100678 if (!(global.mode & MODE_QUIET)) {
willy tarreau5cbea6f2005-12-17 12:48:26 +0100679 va_start(argp, fmt);
willy tarreau0f7af912005-12-17 12:21:26 +0100680
willy tarreau5cbea6f2005-12-17 12:48:26 +0100681 gettimeofday(&tv, NULL);
682 tm=localtime(&tv.tv_sec);
683 fprintf(stderr, "[WARNING] %03d/%02d%02d%02d (%d) : ",
willy tarreaua1598082005-12-17 13:08:06 +0100684 tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)getpid());
willy tarreau5cbea6f2005-12-17 12:48:26 +0100685 vfprintf(stderr, fmt, argp);
686 fflush(stderr);
687 va_end(argp);
688 }
689}
690
691/*
692 * Displays the message on <out> only if quiet mode is not set.
693 */
694void qfprintf(FILE *out, char *fmt, ...) {
695 va_list argp;
696
willy tarreau9fe663a2005-12-17 13:02:59 +0100697 if (!(global.mode & MODE_QUIET)) {
willy tarreau5cbea6f2005-12-17 12:48:26 +0100698 va_start(argp, fmt);
699 vfprintf(out, fmt, argp);
700 fflush(out);
701 va_end(argp);
702 }
willy tarreau0f7af912005-12-17 12:21:26 +0100703}
704
705
706/*
707 * converts <str> to a struct sockaddr_in* which is locally allocated.
708 * The format is "addr:port", where "addr" can be empty or "*" to indicate
709 * INADDR_ANY.
710 */
711struct sockaddr_in *str2sa(char *str) {
712 static struct sockaddr_in sa;
713 char *c;
714 int port;
715
willy tarreaua1598082005-12-17 13:08:06 +0100716 memset(&sa, 0, sizeof(sa));
willy tarreau0f7af912005-12-17 12:21:26 +0100717 str=strdup(str);
718
719 if ((c=strrchr(str,':')) != NULL) {
720 *c++=0;
721 port=atol(c);
722 }
723 else
724 port=0;
725
726 if (*str == '*' || *str == '\0') { /* INADDR_ANY */
727 sa.sin_addr.s_addr = INADDR_ANY;
728 }
729 else if (
730#ifndef SOLARIS
731 !inet_aton(str, &sa.sin_addr)
732#else
733 !inet_pton(AF_INET, str, &sa.sin_addr)
734#endif
735 ) {
736 struct hostent *he;
737
738 if ((he = gethostbyname(str)) == NULL) {
willy tarreauef900ab2005-12-17 12:52:52 +0100739 Alert("Invalid server name: <%s>\n", str);
willy tarreau0f7af912005-12-17 12:21:26 +0100740 }
741 else
742 sa.sin_addr = *(struct in_addr *) *(he->h_addr_list);
743 }
744 sa.sin_port=htons(port);
745 sa.sin_family=AF_INET;
746
747 free(str);
748 return &sa;
749}
750
751/*
752 * This function tries to send a syslog message to the syslog server at
753 * address <sa>. It doesn't care about errors nor does it report them.
754 * WARNING! no check is made on the prog+hostname+date length, so the
755 * local hostname + the prog name must be shorter than MAX_SYSLOG_LEN-19.
756 * the message will be truncated to fit the maximum length.
757 */
758void send_syslog(struct sockaddr_in *sa,
759 int facility, int level, char *message)
760{
761
762 static int logfd = -1; /* syslog UDP socket */
763 struct timeval tv;
764 struct tm *tm;
765 static char logmsg[MAX_SYSLOG_LEN];
766 char *p;
767
768 if (logfd < 0) {
769 if ((logfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
770 return;
771 }
772
773 if (facility < 0 || level < 0
774 || sa == NULL || progname == NULL || message == NULL)
775 return;
776
777 gettimeofday(&tv, NULL);
778 tm = localtime(&tv.tv_sec);
779
780 p = logmsg;
781 //p += sprintf(p, "<%d>%s %2d %02d:%02d:%02d %s %s[%d]: ",
782 // facility * 8 + level,
783 // monthname[tm->tm_mon],
784 // tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
785 // hostname, progname, pid);
786 /* 20011216/WT : other progs don't set the hostname, and syslogd
787 * systematically repeats it which is contrary to RFC3164.
788 */
willy tarreau9fe663a2005-12-17 13:02:59 +0100789 /*
790 * warning: buffer overflow possible on progname.
791 */
willy tarreau0f7af912005-12-17 12:21:26 +0100792 p += sprintf(p, "<%d>%s %2d %02d:%02d:%02d %s[%d]: ",
793 facility * 8 + level,
794 monthname[tm->tm_mon],
795 tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
796 progname, pid);
797
798 if (((char *)&logmsg - p + MAX_SYSLOG_LEN) > 0) {
799 int len = strlen(message);
800 if (len > ((char *)&logmsg + MAX_SYSLOG_LEN - p))
801 len = ((char *)&logmsg + MAX_SYSLOG_LEN - p);
802 memcpy(p, message, len);
803 p += len;
804 }
willy tarreau3242e862005-12-17 12:27:53 +0100805#ifndef MSG_NOSIGNAL
806 sendto(logfd, logmsg, p - logmsg, MSG_DONTWAIT,
807 (struct sockaddr *)sa, sizeof(*sa));
808#else
willy tarreau0f7af912005-12-17 12:21:26 +0100809 sendto(logfd, logmsg, p - logmsg, MSG_DONTWAIT | MSG_NOSIGNAL,
810 (struct sockaddr *)sa, sizeof(*sa));
willy tarreau3242e862005-12-17 12:27:53 +0100811#endif
willy tarreau9fe663a2005-12-17 13:02:59 +0100812}
813
814
815/*
816 * This function sends a syslog message to both log servers of a proxy,
817 * or to global log servers if the proxy is NULL.
818 * It also tries not to waste too much time computing the message header.
819 * It doesn't care about errors nor does it report them.
820 * WARNING! no check is made on the prog+hostname+date length, so the
821 * local hostname + the prog name must be shorter than MAX_SYSLOG_LEN-19.
822 * the message will be truncated to fit the maximum length.
823 */
824void send_log(struct proxy *p, int level, char *message, ...) {
825 static int logfd = -1; /* syslog UDP socket */
826 static long tvsec = -1; /* to force the string to be initialized */
827 struct timeval tv;
828 va_list argp;
829 static char logmsg[MAX_SYSLOG_LEN];
830 static char *dataptr = NULL;
831 int fac_level;
832 int hdr_len, data_len;
833 struct sockaddr_in *sa[2];
834 int facilities[2];
835 int nbloggers = 0;
836 char *log_ptr;
837
838 if (logfd < 0) {
839 if ((logfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
840 return;
841 }
842
843 if (level < 0 || progname == NULL || message == NULL)
844 return;
845
846 gettimeofday(&tv, NULL);
847 if (tv.tv_sec != tvsec) {
848 /* this string is rebuild only once a second */
849 struct tm *tm = localtime(&tv.tv_sec);
850 tvsec = tv.tv_sec;
851
852 /*
853 * warning: buffer overflow possible on progname.
854 */
855 dataptr = logmsg + snprintf(logmsg, sizeof(logmsg),
856 "<<<<>%s %2d %02d:%02d:%02d %s[%d]: ",
857 monthname[tm->tm_mon],
858 tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
859 progname, pid);
860 }
861
862 va_start(argp, message);
863 data_len = vsnprintf(dataptr, logmsg + sizeof(logmsg) - dataptr, message, argp);
864 dataptr[data_len - 1] = '\n'; /* force a break on ultra-long lines */
865 va_end(argp);
866
867 if (p == NULL) {
868 if (global.logfac1 >= 0) {
869 sa[nbloggers] = &global.logsrv1;
870 facilities[nbloggers] = global.logfac1;
871 nbloggers++;
872 }
873 if (global.logfac2 >= 0) {
874 sa[nbloggers] = &global.logsrv2;
875 facilities[nbloggers] = global.logfac2;
876 nbloggers++;
877 }
878 } else {
879 if (p->logfac1 >= 0) {
880 sa[nbloggers] = &p->logsrv1;
881 facilities[nbloggers] = p->logfac1;
882 nbloggers++;
883 }
884 if (p->logfac2 >= 0) {
885 sa[nbloggers] = &p->logsrv2;
886 facilities[nbloggers] = p->logfac2;
887 nbloggers++;
888 }
889 }
890
891 while (nbloggers-- > 0) {
892 /* do this for each log target */
893 fac_level = (facilities[nbloggers] << 3) + level;
894 log_ptr = logmsg + 3; /* last digit of the log level */
895 do {
896 *log_ptr = '0' + fac_level % 10;
897 fac_level /= 10;
898 log_ptr--;
899 } while (fac_level && log_ptr > logmsg);
900 *log_ptr = '<';
901 hdr_len = dataptr - log_ptr;
902
903 /* the total syslog message now starts at p, for hdr_len+data_len */
904
905#ifndef MSG_NOSIGNAL
906 sendto(logfd, log_ptr, hdr_len + data_len, MSG_DONTWAIT,
907 (struct sockaddr *)sa[nbloggers], sizeof(**sa));
908#else
909 sendto(logfd, log_ptr, hdr_len + data_len, MSG_DONTWAIT | MSG_NOSIGNAL,
910 (struct sockaddr *)sa[nbloggers], sizeof(**sa));
911#endif
912 }
willy tarreau0f7af912005-12-17 12:21:26 +0100913}
914
915
916/* sets <tv> to the current time */
917static inline struct timeval *tv_now(struct timeval *tv) {
918 if (tv)
919 gettimeofday(tv, NULL);
920 return tv;
921}
922
923/*
924 * adds <ms> ms to <from>, set the result to <tv> and returns a pointer <tv>
925 */
926static inline struct timeval *tv_delayfrom(struct timeval *tv, struct timeval *from, int ms) {
927 if (!tv || !from)
928 return NULL;
929 tv->tv_usec = from->tv_usec + (ms%1000)*1000;
930 tv->tv_sec = from->tv_sec + (ms/1000);
931 while (tv->tv_usec >= 1000000) {
932 tv->tv_usec -= 1000000;
933 tv->tv_sec++;
934 }
935 return tv;
936}
937
938/*
939 * compares <tv1> and <tv2> : returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2
940 */
941static inline int tv_cmp(struct timeval *tv1, struct timeval *tv2) {
942 if (tv1->tv_sec > tv2->tv_sec)
943 return 1;
944 else if (tv1->tv_sec < tv2->tv_sec)
945 return -1;
946 else if (tv1->tv_usec > tv2->tv_usec)
947 return 1;
948 else if (tv1->tv_usec < tv2->tv_usec)
949 return -1;
950 else
951 return 0;
952}
953
954/*
955 * returns the absolute difference, in ms, between tv1 and tv2
956 */
957unsigned long tv_delta(struct timeval *tv1, struct timeval *tv2) {
958 int cmp;
959 unsigned long ret;
960
961
willy tarreauef900ab2005-12-17 12:52:52 +0100962 cmp = tv_cmp(tv1, tv2);
willy tarreau0f7af912005-12-17 12:21:26 +0100963 if (!cmp)
964 return 0; /* same dates, null diff */
965 else if (cmp<0) {
willy tarreauef900ab2005-12-17 12:52:52 +0100966 struct timeval *tmp = tv1;
967 tv1 = tv2;
968 tv2 = tmp;
willy tarreau0f7af912005-12-17 12:21:26 +0100969 }
willy tarreauef900ab2005-12-17 12:52:52 +0100970 ret = (tv1->tv_sec - tv2->tv_sec) * 1000;
willy tarreau0f7af912005-12-17 12:21:26 +0100971 if (tv1->tv_usec > tv2->tv_usec)
willy tarreauef900ab2005-12-17 12:52:52 +0100972 ret += (tv1->tv_usec - tv2->tv_usec) / 1000;
willy tarreau0f7af912005-12-17 12:21:26 +0100973 else
willy tarreauef900ab2005-12-17 12:52:52 +0100974 ret -= (tv2->tv_usec - tv1->tv_usec) / 1000;
willy tarreau0f7af912005-12-17 12:21:26 +0100975 return (unsigned long) ret;
976}
977
978/*
979 * compares <tv1> and <tv2> modulo 1ms: returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2
980 */
981static inline int tv_cmp_ms(struct timeval *tv1, struct timeval *tv2) {
willy tarreauefae1842005-12-17 12:51:03 +0100982 if (tv1->tv_sec == tv2->tv_sec) {
willy tarreauef900ab2005-12-17 12:52:52 +0100983 if (tv1->tv_usec > tv2->tv_usec + 1000)
willy tarreauefae1842005-12-17 12:51:03 +0100984 return 1;
willy tarreauef900ab2005-12-17 12:52:52 +0100985 else if (tv2->tv_usec > tv1->tv_usec + 1000)
willy tarreauefae1842005-12-17 12:51:03 +0100986 return -1;
987 else
988 return 0;
989 }
990 else if ((tv1->tv_sec > tv2->tv_sec + 1) ||
willy tarreauef900ab2005-12-17 12:52:52 +0100991 ((tv1->tv_sec == tv2->tv_sec + 1) && (tv1->tv_usec + 1000000 > tv2->tv_usec + 1000)))
willy tarreau0f7af912005-12-17 12:21:26 +0100992 return 1;
993 else if ((tv2->tv_sec > tv1->tv_sec + 1) ||
willy tarreauef900ab2005-12-17 12:52:52 +0100994 ((tv2->tv_sec == tv1->tv_sec + 1) && (tv2->tv_usec + 1000000 > tv1->tv_usec + 1000)))
willy tarreau0f7af912005-12-17 12:21:26 +0100995 return -1;
996 else
997 return 0;
998}
999
1000/*
1001 * returns the remaining time between tv1=now and event=tv2
1002 * if tv2 is passed, 0 is returned.
1003 */
1004static inline unsigned long tv_remain(struct timeval *tv1, struct timeval *tv2) {
1005 unsigned long ret;
1006
willy tarreau0f7af912005-12-17 12:21:26 +01001007 if (tv_cmp_ms(tv1, tv2) >= 0)
1008 return 0; /* event elapsed */
1009
willy tarreauef900ab2005-12-17 12:52:52 +01001010 ret = (tv2->tv_sec - tv1->tv_sec) * 1000;
willy tarreau0f7af912005-12-17 12:21:26 +01001011 if (tv2->tv_usec > tv1->tv_usec)
willy tarreauef900ab2005-12-17 12:52:52 +01001012 ret += (tv2->tv_usec - tv1->tv_usec) / 1000;
willy tarreau0f7af912005-12-17 12:21:26 +01001013 else
willy tarreauef900ab2005-12-17 12:52:52 +01001014 ret -= (tv1->tv_usec - tv2->tv_usec) / 1000;
willy tarreau0f7af912005-12-17 12:21:26 +01001015 return (unsigned long) ret;
1016}
1017
1018
1019/*
1020 * zeroes a struct timeval
1021 */
1022
1023static inline struct timeval *tv_eternity(struct timeval *tv) {
1024 tv->tv_sec = tv->tv_usec = 0;
1025 return tv;
1026}
1027
1028/*
1029 * returns 1 if tv is null, else 0
1030 */
1031static inline int tv_iseternity(struct timeval *tv) {
1032 if (tv->tv_sec == 0 && tv->tv_usec == 0)
1033 return 1;
1034 else
1035 return 0;
1036}
1037
1038/*
1039 * compares <tv1> and <tv2> : returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2,
1040 * considering that 0 is the eternity.
1041 */
1042static inline int tv_cmp2(struct timeval *tv1, struct timeval *tv2) {
1043 if (tv_iseternity(tv1))
1044 if (tv_iseternity(tv2))
1045 return 0; /* same */
1046 else
1047 return 1; /* tv1 later than tv2 */
1048 else if (tv_iseternity(tv2))
1049 return -1; /* tv2 later than tv1 */
1050
1051 if (tv1->tv_sec > tv2->tv_sec)
1052 return 1;
1053 else if (tv1->tv_sec < tv2->tv_sec)
1054 return -1;
1055 else if (tv1->tv_usec > tv2->tv_usec)
1056 return 1;
1057 else if (tv1->tv_usec < tv2->tv_usec)
1058 return -1;
1059 else
1060 return 0;
1061}
1062
1063/*
1064 * compares <tv1> and <tv2> modulo 1 ms: returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2,
1065 * considering that 0 is the eternity.
1066 */
1067static inline int tv_cmp2_ms(struct timeval *tv1, struct timeval *tv2) {
1068 if (tv_iseternity(tv1))
1069 if (tv_iseternity(tv2))
1070 return 0; /* same */
1071 else
1072 return 1; /* tv1 later than tv2 */
1073 else if (tv_iseternity(tv2))
1074 return -1; /* tv2 later than tv1 */
1075
willy tarreauefae1842005-12-17 12:51:03 +01001076 if (tv1->tv_sec == tv2->tv_sec) {
willy tarreauef900ab2005-12-17 12:52:52 +01001077 if (tv1->tv_usec > tv2->tv_usec + 1000)
willy tarreauefae1842005-12-17 12:51:03 +01001078 return 1;
willy tarreauef900ab2005-12-17 12:52:52 +01001079 else if (tv2->tv_usec > tv1->tv_usec + 1000)
willy tarreauefae1842005-12-17 12:51:03 +01001080 return -1;
1081 else
1082 return 0;
1083 }
1084 else if ((tv1->tv_sec > tv2->tv_sec + 1) ||
willy tarreauef900ab2005-12-17 12:52:52 +01001085 ((tv1->tv_sec == tv2->tv_sec + 1) && (tv1->tv_usec + 1000000 > tv2->tv_usec + 1000)))
willy tarreau0f7af912005-12-17 12:21:26 +01001086 return 1;
1087 else if ((tv2->tv_sec > tv1->tv_sec + 1) ||
willy tarreauef900ab2005-12-17 12:52:52 +01001088 ((tv2->tv_sec == tv1->tv_sec + 1) && (tv2->tv_usec + 1000000 > tv1->tv_usec + 1000)))
willy tarreau0f7af912005-12-17 12:21:26 +01001089 return -1;
1090 else
1091 return 0;
1092}
1093
1094/*
1095 * returns the first event between tv1 and tv2 into tvmin.
1096 * a zero tv is ignored. tvmin is returned.
1097 */
1098static inline struct timeval *tv_min(struct timeval *tvmin,
1099 struct timeval *tv1, struct timeval *tv2) {
1100
1101 if (tv_cmp2(tv1, tv2) <= 0)
1102 *tvmin = *tv1;
1103 else
1104 *tvmin = *tv2;
1105
1106 return tvmin;
1107}
1108
1109
1110
1111/***********************************************************/
1112/* fd management ***************************************/
1113/***********************************************************/
1114
1115
1116
willy tarreau5cbea6f2005-12-17 12:48:26 +01001117/* Deletes an FD from the fdsets, and recomputes the maxfd limit.
1118 * The file descriptor is also closed.
1119 */
willy tarreau0f7af912005-12-17 12:21:26 +01001120static inline void fd_delete(int fd) {
willy tarreau0f7af912005-12-17 12:21:26 +01001121 FD_CLR(fd, StaticReadEvent);
1122 FD_CLR(fd, StaticWriteEvent);
willy tarreau5cbea6f2005-12-17 12:48:26 +01001123 close(fd);
1124 fdtab[fd].state = FD_STCLOSE;
willy tarreau0f7af912005-12-17 12:21:26 +01001125
1126 while ((maxfd-1 >= 0) && (fdtab[maxfd-1].state == FD_STCLOSE))
1127 maxfd--;
1128}
1129
1130/* recomputes the maxfd limit from the fd */
1131static inline void fd_insert(int fd) {
1132 if (fd+1 > maxfd)
1133 maxfd = fd+1;
1134}
1135
1136/*************************************************************/
1137/* task management ***************************************/
1138/*************************************************************/
1139
willy tarreau5cbea6f2005-12-17 12:48:26 +01001140/* puts the task <t> in run queue <q>, and returns <t> */
1141static inline struct task *task_wakeup(struct task **q, struct task *t) {
1142 if (t->state == TASK_RUNNING)
1143 return t;
willy tarreau0f7af912005-12-17 12:21:26 +01001144 else {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001145 t->rqnext = *q;
1146 t->state = TASK_RUNNING;
1147 return *q = t;
willy tarreau0f7af912005-12-17 12:21:26 +01001148 }
1149}
1150
willy tarreau5cbea6f2005-12-17 12:48:26 +01001151/* removes the task <t> from the queue <q>
1152 * <s> MUST be <q>'s first task.
willy tarreau0f7af912005-12-17 12:21:26 +01001153 * set the run queue to point to the next one, and return it
1154 */
willy tarreau5cbea6f2005-12-17 12:48:26 +01001155static inline struct task *task_sleep(struct task **q, struct task *t) {
1156 if (t->state == TASK_RUNNING) {
1157 *q = t->rqnext;
1158 t->state = TASK_IDLE; /* tell that s has left the run queue */
willy tarreau0f7af912005-12-17 12:21:26 +01001159 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01001160 return *q; /* return next running task */
willy tarreau0f7af912005-12-17 12:21:26 +01001161}
1162
1163/*
willy tarreau5cbea6f2005-12-17 12:48:26 +01001164 * removes the task <t> from its wait queue. It must have already been removed
willy tarreau0f7af912005-12-17 12:21:26 +01001165 * from the run queue. A pointer to the task itself is returned.
1166 */
willy tarreau5cbea6f2005-12-17 12:48:26 +01001167static inline struct task *task_delete(struct task *t) {
1168 t->prev->next = t->next;
1169 t->next->prev = t->prev;
1170 return t;
willy tarreau0f7af912005-12-17 12:21:26 +01001171}
1172
1173/*
willy tarreau5cbea6f2005-12-17 12:48:26 +01001174 * frees a task. Its context must have been freed since it will be lost.
willy tarreau0f7af912005-12-17 12:21:26 +01001175 */
1176static inline void task_free(struct task *t) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001177 pool_free(task, t);
willy tarreau0f7af912005-12-17 12:21:26 +01001178}
1179
willy tarreau5cbea6f2005-12-17 12:48:26 +01001180/* inserts <task> into its assigned wait queue, where it may already be. In this case, it
willy tarreau0f7af912005-12-17 12:21:26 +01001181 * may be only moved or left where it was, depending on its timing requirements.
1182 * <task> is returned.
1183 */
willy tarreau5cbea6f2005-12-17 12:48:26 +01001184struct task *task_queue(struct task *task) {
1185 struct task *list = task->wq;
willy tarreau0f7af912005-12-17 12:21:26 +01001186 struct task *start_from;
1187
1188 /* first, test if the task was already in a list */
1189 if (task->prev == NULL) {
1190 // start_from = list;
1191 start_from = list->prev;
1192 stats_tsk_new++;
1193
1194 /* insert the unlinked <task> into the list, searching back from the last entry */
1195 while (start_from != list && tv_cmp2(&task->expire, &start_from->expire) < 0) {
1196 start_from = start_from->prev;
1197 stats_tsk_nsrch++;
1198 }
1199
1200 // while (start_from->next != list && tv_cmp2(&task->expire, &start_from->next->expire) > 0) {
1201 // start_from = start_from->next;
1202 // stats_tsk_nsrch++;
1203 // }
1204 }
1205 else if (task->prev == list ||
1206 tv_cmp2(&task->expire, &task->prev->expire) >= 0) { /* walk right */
1207 start_from = task->next;
1208 if (start_from == list || tv_cmp2(&task->expire, &start_from->expire) <= 0) {
1209 stats_tsk_good++;
1210 return task; /* it's already in the right place */
1211 }
1212
1213 stats_tsk_right++;
1214 /* insert the unlinked <task> into the list, searching after position <start_from> */
1215 while (start_from->next != list && tv_cmp2(&task->expire, &start_from->next->expire) > 0) {
1216 start_from = start_from->next;
1217 stats_tsk_rsrch++;
1218 }
1219 /* we need to unlink it now */
1220 task_delete(task);
1221 }
1222 else { /* walk left. */
1223 stats_tsk_left++;
1224#ifdef LEFT_TO_TOP /* not very good */
1225 start_from = list;
1226 while (start_from->next != list && tv_cmp2(&task->expire, &start_from->next->expire) > 0) {
1227 start_from = start_from->next;
1228 stats_tsk_lsrch++;
1229 }
1230#else
1231 start_from = task->prev->prev; /* valid because of the previous test above */
1232 while (start_from != list && tv_cmp2(&task->expire, &start_from->expire) < 0) {
1233 start_from = start_from->prev;
1234 stats_tsk_lsrch++;
1235 }
1236#endif
1237 /* we need to unlink it now */
1238 task_delete(task);
1239 }
1240 task->prev = start_from;
1241 task->next = start_from->next;
1242 task->next->prev = task;
1243 start_from->next = task;
1244 return task;
1245}
1246
1247
1248/*********************************************************************/
1249/* more specific functions ***************************************/
1250/*********************************************************************/
1251
1252/* some prototypes */
1253static int maintain_proxies(void);
1254
willy tarreau5cbea6f2005-12-17 12:48:26 +01001255/* this either returns the sockname or the original destination address. Code
1256 * inspired from Patrick Schaaf's example of nf_getsockname() implementation.
1257 */
1258static int get_original_dst(int fd, struct sockaddr_in *sa, int *salen) {
willy tarreaua1598082005-12-17 13:08:06 +01001259#if defined(TPROXY) && defined(SO_ORIGINAL_DST)
willy tarreau5cbea6f2005-12-17 12:48:26 +01001260 return getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, (void *)sa, salen);
1261#else
willy tarreaua1598082005-12-17 13:08:06 +01001262#if defined(TPROXY) && defined(USE_GETSOCKNAME)
willy tarreau5cbea6f2005-12-17 12:48:26 +01001263 return getsockname(fd, (struct sockaddr *)sa, salen);
1264#else
1265 return -1;
1266#endif
1267#endif
1268}
1269
1270/*
1271 * frees the context associated to a session. It must have been removed first.
1272 */
1273static inline void session_free(struct session *s) {
1274 if (s->req)
1275 pool_free(buffer, s->req);
1276 if (s->rep)
1277 pool_free(buffer, s->rep);
willy tarreaua1598082005-12-17 13:08:06 +01001278 if (s->logs.uri)
1279 pool_free(requri, s->logs.uri);
willy tarreau9fe663a2005-12-17 13:02:59 +01001280
willy tarreau5cbea6f2005-12-17 12:48:26 +01001281 pool_free(session, s);
1282}
1283
willy tarreau0f7af912005-12-17 12:21:26 +01001284
1285/*
willy tarreau5cbea6f2005-12-17 12:48:26 +01001286 * This function initiates a connection to the current server (s->srv) if (s->direct)
1287 * is set, or to the dispatch server if (s->direct) is 0. It returns 0 if
willy tarreau0f7af912005-12-17 12:21:26 +01001288 * it's OK, -1 if it's impossible.
1289 */
willy tarreau5cbea6f2005-12-17 12:48:26 +01001290int connect_server(struct session *s) {
willy tarreau0f7af912005-12-17 12:21:26 +01001291 int one = 1;
1292 int fd;
1293
1294 // fprintf(stderr,"connect_server : s=%p\n",s);
1295
willy tarreaue39cd132005-12-17 13:00:18 +01001296 if (s->flags & SN_DIRECT) { /* srv cannot be null */
willy tarreau5cbea6f2005-12-17 12:48:26 +01001297 s->srv_addr = s->srv->addr;
1298 }
1299 else if (s->proxy->options & PR_O_BALANCE) {
1300 if (s->proxy->options & PR_O_BALANCE_RR) {
1301 int retry = s->proxy->nbservers;
willy tarreauefae1842005-12-17 12:51:03 +01001302 while (retry) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001303 if (s->proxy->cursrv == NULL)
1304 s->proxy->cursrv = s->proxy->srv;
1305 if (s->proxy->cursrv->state & SRV_RUNNING)
1306 break;
1307 s->proxy->cursrv = s->proxy->cursrv->next;
willy tarreauefae1842005-12-17 12:51:03 +01001308 retry--;
1309 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01001310
1311 if (retry == 0) /* no server left */
1312 return -1;
1313
1314 s->srv = s->proxy->cursrv;
1315 s->srv_addr = s->srv->addr;
1316 s->proxy->cursrv = s->proxy->cursrv->next;
willy tarreau0f7af912005-12-17 12:21:26 +01001317 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01001318 else /* unknown balancing algorithm */
1319 return -1;
willy tarreau0f7af912005-12-17 12:21:26 +01001320 }
willy tarreaua1598082005-12-17 13:08:06 +01001321 else if (*(int *)&s->proxy->dispatch_addr.sin_addr) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001322 /* connect to the defined dispatch addr */
willy tarreau0f7af912005-12-17 12:21:26 +01001323 s->srv_addr = s->proxy->dispatch_addr;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001324 }
1325 else if (s->proxy->options & PR_O_TRANSP) {
1326 /* in transparent mode, use the original dest addr if no dispatch specified */
1327 int salen = sizeof(struct sockaddr_in);
1328 if (get_original_dst(s->cli_fd, &s->srv_addr, &salen) == -1) {
1329 qfprintf(stderr, "Cannot get original server address.\n");
1330 return -1;
1331 }
1332 }
willy tarreau0f7af912005-12-17 12:21:26 +01001333
1334 if ((fd = s->srv_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001335 qfprintf(stderr, "Cannot get a server socket.\n");
willy tarreau0f7af912005-12-17 12:21:26 +01001336 return -1;
1337 }
1338
willy tarreau9fe663a2005-12-17 13:02:59 +01001339 if (fd >= global.maxsock) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001340 Alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n");
1341 close(fd);
1342 return -1;
1343 }
1344
willy tarreau0f7af912005-12-17 12:21:26 +01001345 if ((fcntl(fd, F_SETFL, O_NONBLOCK)==-1) ||
1346 (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) == -1)) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001347 qfprintf(stderr,"Cannot set client socket to non blocking mode.\n");
willy tarreau0f7af912005-12-17 12:21:26 +01001348 close(fd);
1349 return -1;
1350 }
1351
willy tarreaua1598082005-12-17 13:08:06 +01001352 /* allow specific binding */
1353 if (s->proxy->options & PR_O_BIND_SRC &&
1354 bind(fd, (struct sockaddr *)&s->proxy->source_addr, sizeof(s->proxy->source_addr)) == -1) {
1355 Alert("Cannot bind to source address before connect() for proxy %s. Aborting.\n", s->proxy->id);
1356 close(fd);
1357 return -1;
1358 }
1359
willy tarreau0f7af912005-12-17 12:21:26 +01001360 if ((connect(fd, (struct sockaddr *)&s->srv_addr, sizeof(s->srv_addr)) == -1) && (errno != EINPROGRESS)) {
1361 if (errno == EAGAIN) { /* no free ports left, try again later */
willy tarreau5cbea6f2005-12-17 12:48:26 +01001362 qfprintf(stderr,"Cannot connect, no free ports.\n");
willy tarreau0f7af912005-12-17 12:21:26 +01001363 close(fd);
1364 return -1;
1365 }
1366 else if (errno != EALREADY && errno != EISCONN) {
1367 close(fd);
1368 return -1;
1369 }
1370 }
1371
willy tarreau5cbea6f2005-12-17 12:48:26 +01001372 fdtab[fd].owner = s->task;
willy tarreau0f7af912005-12-17 12:21:26 +01001373 fdtab[fd].read = &event_srv_read;
1374 fdtab[fd].write = &event_srv_write;
1375 fdtab[fd].state = FD_STCONN; /* connection in progress */
1376
1377 FD_SET(fd, StaticWriteEvent); /* for connect status */
1378
1379 fd_insert(fd);
1380
1381 if (s->proxy->contimeout)
1382 tv_delayfrom(&s->cnexpire, &now, s->proxy->contimeout);
1383 else
1384 tv_eternity(&s->cnexpire);
1385 return 0;
1386}
1387
1388/*
1389 * this function is called on a read event from a client socket.
1390 * It returns 0.
1391 */
1392int event_cli_read(int fd) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001393 struct task *t = fdtab[fd].owner;
1394 struct session *s = t->context;
willy tarreau0f7af912005-12-17 12:21:26 +01001395 struct buffer *b = s->req;
1396 int ret, max;
willy tarreau0f7af912005-12-17 12:21:26 +01001397
1398 // fprintf(stderr,"event_cli_read : fd=%d, s=%p\n", fd, s);
1399
willy tarreau0f7af912005-12-17 12:21:26 +01001400 if (fdtab[fd].state != FD_STERROR) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001401 while (1) {
1402 if (b->l == 0) { /* let's realign the buffer to optimize I/O */
1403 b->r = b->w = b->h = b->lr = b->data;
willy tarreauef900ab2005-12-17 12:52:52 +01001404 max = b->rlim - b->data;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001405 }
1406 else if (b->r > b->w) {
willy tarreauef900ab2005-12-17 12:52:52 +01001407 max = b->rlim - b->r;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001408 }
1409 else {
1410 max = b->w - b->r;
willy tarreauef900ab2005-12-17 12:52:52 +01001411 /* FIXME: theorically, if w>0, we shouldn't have rlim < data+size anymore
1412 * since it means that the rewrite protection has been removed. This
1413 * implies that the if statement can be removed.
1414 */
1415 if (max > b->rlim - b->data)
1416 max = b->rlim - b->data;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001417 }
1418
1419 if (max == 0) { /* not anymore room to store data */
1420 FD_CLR(fd, StaticReadEvent);
willy tarreauef900ab2005-12-17 12:52:52 +01001421 break;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001422 }
1423
willy tarreau3242e862005-12-17 12:27:53 +01001424#ifndef MSG_NOSIGNAL
willy tarreau5cbea6f2005-12-17 12:48:26 +01001425 {
1426 int skerr, lskerr;
1427
1428 lskerr = sizeof(skerr);
1429 getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
1430 if (skerr)
1431 ret = -1;
1432 else
1433 ret = recv(fd, b->r, max, 0);
1434 }
willy tarreau3242e862005-12-17 12:27:53 +01001435#else
willy tarreau5cbea6f2005-12-17 12:48:26 +01001436 ret = recv(fd, b->r, max, MSG_NOSIGNAL);
willy tarreau3242e862005-12-17 12:27:53 +01001437#endif
willy tarreau5cbea6f2005-12-17 12:48:26 +01001438 if (ret > 0) {
1439 b->r += ret;
1440 b->l += ret;
1441 s->res_cr = RES_DATA;
1442
1443 if (b->r == b->data + BUFSIZE) {
1444 b->r = b->data; /* wrap around the buffer */
1445 }
willy tarreaua1598082005-12-17 13:08:06 +01001446
1447 b->total += ret;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001448 /* we hope to read more data or to get a close on next round */
1449 continue;
willy tarreau0f7af912005-12-17 12:21:26 +01001450 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01001451 else if (ret == 0) {
1452 s->res_cr = RES_NULL;
1453 break;
1454 }
1455 else if (errno == EAGAIN) {/* ignore EAGAIN */
1456 break;
1457 }
1458 else {
1459 s->res_cr = RES_ERROR;
1460 fdtab[fd].state = FD_STERROR;
1461 break;
1462 }
1463 } /* while(1) */
willy tarreau0f7af912005-12-17 12:21:26 +01001464 }
1465 else {
1466 s->res_cr = RES_ERROR;
1467 fdtab[fd].state = FD_STERROR;
1468 }
1469
willy tarreau5cbea6f2005-12-17 12:48:26 +01001470 if (s->res_cr != RES_SILENT) {
1471 if (s->proxy->clitimeout)
1472 tv_delayfrom(&s->crexpire, &now, s->proxy->clitimeout);
1473 else
1474 tv_eternity(&s->crexpire);
1475
1476 task_wakeup(&rq, t);
1477 }
willy tarreau0f7af912005-12-17 12:21:26 +01001478
willy tarreau0f7af912005-12-17 12:21:26 +01001479 return 0;
1480}
1481
1482
1483/*
1484 * this function is called on a read event from a server socket.
1485 * It returns 0.
1486 */
1487int event_srv_read(int fd) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001488 struct task *t = fdtab[fd].owner;
1489 struct session *s = t->context;
willy tarreau0f7af912005-12-17 12:21:26 +01001490 struct buffer *b = s->rep;
1491 int ret, max;
willy tarreau0f7af912005-12-17 12:21:26 +01001492
1493 // fprintf(stderr,"event_srv_read : fd=%d, s=%p\n", fd, s);
1494
willy tarreau0f7af912005-12-17 12:21:26 +01001495 if (fdtab[fd].state != FD_STERROR) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001496 while (1) {
1497 if (b->l == 0) { /* let's realign the buffer to optimize I/O */
1498 b->r = b->w = b->h = b->lr = b->data;
willy tarreauef900ab2005-12-17 12:52:52 +01001499 max = b->rlim - b->data;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001500 }
1501 else if (b->r > b->w) {
willy tarreauef900ab2005-12-17 12:52:52 +01001502 max = b->rlim - b->r;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001503 }
1504 else {
1505 max = b->w - b->r;
willy tarreauef900ab2005-12-17 12:52:52 +01001506 /* FIXME: theorically, if w>0, we shouldn't have rlim < data+size anymore
1507 * since it means that the rewrite protection has been removed. This
1508 * implies that the if statement can be removed.
1509 */
1510 if (max > b->rlim - b->data)
1511 max = b->rlim - b->data;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001512 }
1513
1514 if (max == 0) { /* not anymore room to store data */
1515 FD_CLR(fd, StaticReadEvent);
1516 break;
1517 }
1518
willy tarreau3242e862005-12-17 12:27:53 +01001519#ifndef MSG_NOSIGNAL
willy tarreau5cbea6f2005-12-17 12:48:26 +01001520 {
1521 int skerr, lskerr;
1522
1523 lskerr = sizeof(skerr);
1524 getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
1525 if (skerr)
1526 ret = -1;
1527 else
1528 ret = recv(fd, b->r, max, 0);
1529 }
willy tarreau3242e862005-12-17 12:27:53 +01001530#else
willy tarreau5cbea6f2005-12-17 12:48:26 +01001531 ret = recv(fd, b->r, max, MSG_NOSIGNAL);
willy tarreau3242e862005-12-17 12:27:53 +01001532#endif
willy tarreau5cbea6f2005-12-17 12:48:26 +01001533 if (ret > 0) {
1534 b->r += ret;
1535 b->l += ret;
1536 s->res_sr = RES_DATA;
willy tarreau0f7af912005-12-17 12:21:26 +01001537
willy tarreau5cbea6f2005-12-17 12:48:26 +01001538 if (b->r == b->data + BUFSIZE) {
1539 b->r = b->data; /* wrap around the buffer */
1540 }
willy tarreaua1598082005-12-17 13:08:06 +01001541
1542 b->total += ret;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001543 /* we hope to read more data or to get a close on next round */
1544 continue;
willy tarreau0f7af912005-12-17 12:21:26 +01001545 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01001546 else if (ret == 0) {
1547 s->res_sr = RES_NULL;
1548 break;
1549 }
1550 else if (errno == EAGAIN) {/* ignore EAGAIN */
1551 break;
1552 }
1553 else {
1554 s->res_sr = RES_ERROR;
1555 fdtab[fd].state = FD_STERROR;
1556 break;
1557 }
1558 } /* while(1) */
willy tarreau0f7af912005-12-17 12:21:26 +01001559 }
1560 else {
1561 s->res_sr = RES_ERROR;
1562 fdtab[fd].state = FD_STERROR;
1563 }
1564
willy tarreau5cbea6f2005-12-17 12:48:26 +01001565 if (s->res_sr != RES_SILENT) {
1566 if (s->proxy->srvtimeout)
1567 tv_delayfrom(&s->srexpire, &now, s->proxy->srvtimeout);
1568 else
1569 tv_eternity(&s->srexpire);
1570
1571 task_wakeup(&rq, t);
1572 }
willy tarreau0f7af912005-12-17 12:21:26 +01001573
willy tarreau0f7af912005-12-17 12:21:26 +01001574 return 0;
1575}
1576
1577/*
1578 * this function is called on a write event from a client socket.
1579 * It returns 0.
1580 */
1581int event_cli_write(int fd) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001582 struct task *t = fdtab[fd].owner;
1583 struct session *s = t->context;
willy tarreau0f7af912005-12-17 12:21:26 +01001584 struct buffer *b = s->rep;
1585 int ret, max;
willy tarreau0f7af912005-12-17 12:21:26 +01001586
1587 // fprintf(stderr,"event_cli_write : fd=%d, s=%p\n", fd, s);
1588
1589 if (b->l == 0) { /* let's realign the buffer to optimize I/O */
willy tarreau9da061b2005-12-17 12:29:56 +01001590 b->r = b->w = b->h = b->lr = b->data;
willy tarreau0f7af912005-12-17 12:21:26 +01001591 // max = BUFSIZE; BUG !!!!
1592 max = 0;
1593 }
1594 else if (b->r > b->w) {
1595 max = b->r - b->w;
1596 }
1597 else
1598 max = b->data + BUFSIZE - b->w;
1599
willy tarreau0f7af912005-12-17 12:21:26 +01001600 if (fdtab[fd].state != FD_STERROR) {
willy tarreau3242e862005-12-17 12:27:53 +01001601#ifndef MSG_NOSIGNAL
1602 int skerr, lskerr;
1603#endif
willy tarreauef900ab2005-12-17 12:52:52 +01001604
1605 if (max == 0) {
1606 s->res_cw = RES_NULL;
1607 task_wakeup(&rq, t);
1608 return 0;
willy tarreau0f7af912005-12-17 12:21:26 +01001609 }
1610
willy tarreau3242e862005-12-17 12:27:53 +01001611#ifndef MSG_NOSIGNAL
1612 lskerr=sizeof(skerr);
1613 getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
1614 if (skerr)
1615 ret = -1;
1616 else
1617 ret = send(fd, b->w, max, MSG_DONTWAIT);
1618#else
willy tarreau0f7af912005-12-17 12:21:26 +01001619 ret = send(fd, b->w, max, MSG_DONTWAIT | MSG_NOSIGNAL);
willy tarreau3242e862005-12-17 12:27:53 +01001620#endif
willy tarreau0f7af912005-12-17 12:21:26 +01001621
1622 if (ret > 0) {
1623 b->l -= ret;
1624 b->w += ret;
1625
1626 s->res_cw = RES_DATA;
1627
1628 if (b->w == b->data + BUFSIZE) {
1629 b->w = b->data; /* wrap around the buffer */
1630 }
1631 }
1632 else if (ret == 0) {
1633 /* nothing written, just make as if we were never called */
1634// s->res_cw = RES_NULL;
1635 return 0;
1636 }
1637 else if (errno == EAGAIN) /* ignore EAGAIN */
1638 return 0;
1639 else {
1640 s->res_cw = RES_ERROR;
1641 fdtab[fd].state = FD_STERROR;
1642 }
1643 }
1644 else {
1645 s->res_cw = RES_ERROR;
1646 fdtab[fd].state = FD_STERROR;
1647 }
1648
1649 if (s->proxy->clitimeout)
1650 tv_delayfrom(&s->cwexpire, &now, s->proxy->clitimeout);
1651 else
1652 tv_eternity(&s->cwexpire);
1653
willy tarreau5cbea6f2005-12-17 12:48:26 +01001654 task_wakeup(&rq, t);
willy tarreau0f7af912005-12-17 12:21:26 +01001655 return 0;
1656}
1657
1658
1659/*
1660 * this function is called on a write event from a server socket.
1661 * It returns 0.
1662 */
1663int event_srv_write(int fd) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001664 struct task *t = fdtab[fd].owner;
1665 struct session *s = t->context;
willy tarreau0f7af912005-12-17 12:21:26 +01001666 struct buffer *b = s->req;
1667 int ret, max;
willy tarreau0f7af912005-12-17 12:21:26 +01001668
1669 //fprintf(stderr,"event_srv_write : fd=%d, s=%p\n", fd, s);
1670
1671 if (b->l == 0) { /* let's realign the buffer to optimize I/O */
willy tarreau9da061b2005-12-17 12:29:56 +01001672 b->r = b->w = b->h = b->lr = b->data;
willy tarreau0f7af912005-12-17 12:21:26 +01001673 // max = BUFSIZE; BUG !!!!
1674 max = 0;
1675 }
1676 else if (b->r > b->w) {
1677 max = b->r - b->w;
1678 }
1679 else
1680 max = b->data + BUFSIZE - b->w;
1681
willy tarreau0f7af912005-12-17 12:21:26 +01001682 if (fdtab[fd].state != FD_STERROR) {
willy tarreau3242e862005-12-17 12:27:53 +01001683#ifndef MSG_NOSIGNAL
1684 int skerr, lskerr;
1685#endif
willy tarreauef900ab2005-12-17 12:52:52 +01001686 if (max == 0) {
1687 /* may be we have received a connection acknowledgement in TCP mode without data */
willy tarreau0f7af912005-12-17 12:21:26 +01001688 s->res_sw = RES_NULL;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001689 task_wakeup(&rq, t);
willy tarreauef900ab2005-12-17 12:52:52 +01001690 fdtab[fd].state = FD_STREADY;
willy tarreau0f7af912005-12-17 12:21:26 +01001691 return 0;
1692 }
1693
willy tarreauef900ab2005-12-17 12:52:52 +01001694
willy tarreau3242e862005-12-17 12:27:53 +01001695#ifndef MSG_NOSIGNAL
1696 lskerr=sizeof(skerr);
1697 getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
1698 if (skerr)
1699 ret = -1;
1700 else
1701 ret = send(fd, b->w, max, MSG_DONTWAIT);
1702#else
willy tarreau0f7af912005-12-17 12:21:26 +01001703 ret = send(fd, b->w, max, MSG_DONTWAIT | MSG_NOSIGNAL);
willy tarreau3242e862005-12-17 12:27:53 +01001704#endif
willy tarreauef900ab2005-12-17 12:52:52 +01001705 fdtab[fd].state = FD_STREADY;
willy tarreau0f7af912005-12-17 12:21:26 +01001706 if (ret > 0) {
1707 b->l -= ret;
1708 b->w += ret;
1709
1710 s->res_sw = RES_DATA;
1711
1712 if (b->w == b->data + BUFSIZE) {
1713 b->w = b->data; /* wrap around the buffer */
1714 }
1715 }
1716 else if (ret == 0) {
1717 /* nothing written, just make as if we were never called */
1718 // s->res_sw = RES_NULL;
1719 return 0;
1720 }
1721 else if (errno == EAGAIN) /* ignore EAGAIN */
1722 return 0;
1723 else {
1724 s->res_sw = RES_ERROR;
1725 fdtab[fd].state = FD_STERROR;
1726 }
1727 }
1728 else {
1729 s->res_sw = RES_ERROR;
1730 fdtab[fd].state = FD_STERROR;
1731 }
1732
1733 if (s->proxy->srvtimeout)
1734 tv_delayfrom(&s->swexpire, &now, s->proxy->srvtimeout);
1735 else
1736 tv_eternity(&s->swexpire);
1737
willy tarreau5cbea6f2005-12-17 12:48:26 +01001738 task_wakeup(&rq, t);
willy tarreau0f7af912005-12-17 12:21:26 +01001739 return 0;
1740}
1741
1742
1743/*
willy tarreaue39cd132005-12-17 13:00:18 +01001744 * returns a message to the client ; the connection is shut down for read,
1745 * and the request is cleared so that no server connection can be initiated.
1746 * The client must be in a valid state for this (HEADER, DATA ...).
1747 * Nothing is performed on the server side.
1748 * The reply buffer must be empty before this.
1749 */
1750void client_retnclose(struct session *s, int len, const char *msg) {
1751 FD_CLR(s->cli_fd, StaticReadEvent);
1752 FD_SET(s->cli_fd, StaticWriteEvent);
1753 tv_eternity(&s->crexpire);
1754 shutdown(s->cli_fd, SHUT_RD);
1755 s->cli_state = CL_STSHUTR;
1756 strcpy(s->rep->data, msg);
1757 s->rep->l = len;
1758 s->rep->r += len;
1759 s->req->l = 0;
1760}
1761
1762
1763/*
1764 * returns a message into the rep buffer, and flushes the req buffer.
1765 * The reply buffer must be empty before this.
1766 */
1767void client_return(struct session *s, int len, const char *msg) {
1768 strcpy(s->rep->data, msg);
1769 s->rep->l = len;
1770 s->rep->r += len;
1771 s->req->l = 0;
1772}
1773
willy tarreau9fe663a2005-12-17 13:02:59 +01001774/*
1775 * send a log for the session when we have enough info about it
1776 */
1777void sess_log(struct session *s) {
1778 unsigned char *pn;
1779 struct proxy *p = s->proxy;
1780 int log;
1781 char *uri;
1782 char *pxid;
1783 char *srv;
1784
1785 /* This is a first attempt at a better logging system.
1786 * For now, we rely on send_log() to provide the date, although it obviously
1787 * is the date of the log and not of the request, and most fields are not
1788 * computed.
1789 */
1790
willy tarreaua1598082005-12-17 13:08:06 +01001791 log = p->to_log & ~s->logs.logwait;
willy tarreau9fe663a2005-12-17 13:02:59 +01001792
1793 pn = (log & LW_CLIP) ?
1794 (unsigned char *)&s->cli_addr.sin_addr :
1795 (unsigned char *)"\0\0\0\0";
1796
willy tarreaua1598082005-12-17 13:08:06 +01001797 uri = (log & LW_REQ) ? s->logs.uri : "<BADREQ>";
willy tarreau9fe663a2005-12-17 13:02:59 +01001798 pxid = p->id;
1799 //srv = (log & LW_SVID) ? s->srv->id : "<svid>";
willy tarreaua1598082005-12-17 13:08:06 +01001800 srv = ((p->to_log & LW_SVID) && s->srv != NULL) ? s->srv->id : "<NOSRV>";
1801
1802 if (p->to_log & LW_DATE) {
1803 struct tm *tm = localtime(&s->logs.tv_accept.tv_sec);
1804
1805 send_log(p, LOG_INFO, "%d.%d.%d.%d:%d [%02d/%s/%04d:%02d:%02d:%02d] %s %s %d/%d/%d/%d %d %lld \"%s\"\n",
1806 pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
1807 tm->tm_mday, monthname[tm->tm_mon], tm->tm_year+1900,
1808 tm->tm_hour, tm->tm_min, tm->tm_sec,
1809 pxid, srv,
1810 s->logs.t_request,
1811 (s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_request : -1,
1812 (s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
1813 s->logs.t_close,
1814 s->logs.status, s->logs.bytes,
1815 uri);
1816 }
1817 else {
1818 send_log(p, LOG_INFO, "%d.%d.%d.%d:%d %s %s %d/%d/%d/%d %d %lld \"%s\"\n",
1819 pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
1820 pxid, srv,
1821 s->logs.t_request,
1822 (s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_request : -1,
1823 (s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
1824 s->logs.t_close,
1825 s->logs.status, s->logs.bytes,
1826 uri);
1827 }
1828
1829 s->logs.logwait = 0;
willy tarreau9fe663a2005-12-17 13:02:59 +01001830}
1831
willy tarreaue39cd132005-12-17 13:00:18 +01001832
1833/*
willy tarreau0f7af912005-12-17 12:21:26 +01001834 * this function is called on a read event from a listen socket, corresponding
willy tarreau5cbea6f2005-12-17 12:48:26 +01001835 * to an accept. It tries to accept as many connections as possible.
1836 * It returns 0.
willy tarreau0f7af912005-12-17 12:21:26 +01001837 */
1838int event_accept(int fd) {
1839 struct proxy *p = (struct proxy *)fdtab[fd].owner;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001840 struct session *s;
1841 struct task *t;
willy tarreau0f7af912005-12-17 12:21:26 +01001842 int cfd;
1843 int one = 1;
1844
willy tarreau5cbea6f2005-12-17 12:48:26 +01001845 while (p->nbconn < p->maxconn) {
1846 struct sockaddr_in addr;
1847 int laddr = sizeof(addr);
1848 if ((cfd = accept(fd, (struct sockaddr *)&addr, &laddr)) == -1)
1849 return 0; /* nothing more to accept */
willy tarreau0f7af912005-12-17 12:21:26 +01001850
willy tarreau5cbea6f2005-12-17 12:48:26 +01001851 if ((s = pool_alloc(session)) == NULL) { /* disable this proxy for a while */
1852 Alert("out of memory in event_accept().\n");
1853 FD_CLR(fd, StaticReadEvent);
1854 p->state = PR_STIDLE;
1855 close(cfd);
1856 return 0;
1857 }
willy tarreau0f7af912005-12-17 12:21:26 +01001858
willy tarreau5cbea6f2005-12-17 12:48:26 +01001859 if ((t = pool_alloc(task)) == NULL) { /* disable this proxy for a while */
1860 Alert("out of memory in event_accept().\n");
1861 FD_CLR(fd, StaticReadEvent);
1862 p->state = PR_STIDLE;
1863 close(cfd);
1864 pool_free(session, s);
1865 return 0;
1866 }
willy tarreau0f7af912005-12-17 12:21:26 +01001867
willy tarreau5cbea6f2005-12-17 12:48:26 +01001868 s->cli_addr = addr;
willy tarreau9fe663a2005-12-17 13:02:59 +01001869 if (cfd >= global.maxsock) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01001870 Alert("accept(): not enough free sockets. Raise -n argument. Giving up.\n");
1871 close(cfd);
1872 pool_free(task, t);
1873 pool_free(session, s);
1874 return 0;
1875 }
willy tarreau0f7af912005-12-17 12:21:26 +01001876
willy tarreau5cbea6f2005-12-17 12:48:26 +01001877 if ((fcntl(cfd, F_SETFL, O_NONBLOCK) == -1) ||
1878 (setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY,
1879 (char *) &one, sizeof(one)) == -1)) {
1880 Alert("accept(): cannot set the socket in non blocking mode. Giving up\n");
1881 close(cfd);
1882 pool_free(task, t);
1883 pool_free(session, s);
1884 return 0;
1885 }
willy tarreau0f7af912005-12-17 12:21:26 +01001886
willy tarreau9fe663a2005-12-17 13:02:59 +01001887 t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
1888 t->wq = LIST_HEAD(wait_queue); /* but already has a wait queue assigned */
1889 t->state = TASK_IDLE;
1890 t->process = process_session;
1891 t->context = s;
1892
1893 s->task = t;
1894 s->proxy = p;
1895 s->cli_state = (p->mode == PR_MODE_HTTP) ? CL_STHEADERS : CL_STDATA; /* no HTTP headers for non-HTTP proxies */
1896 s->srv_state = SV_STIDLE;
1897 s->req = s->rep = NULL; /* will be allocated later */
1898 s->flags = 0;
1899 s->res_cr = s->res_cw = s->res_sr = s->res_sw = RES_SILENT;
1900 s->cli_fd = cfd;
1901 s->srv_fd = -1;
willy tarreaua1598082005-12-17 13:08:06 +01001902 s->srv = NULL;
willy tarreau9fe663a2005-12-17 13:02:59 +01001903 s->conn_retries = p->conn_retries;
willy tarreaua1598082005-12-17 13:08:06 +01001904
1905 s->logs.logwait = p->to_log;
1906 s->logs.tv_accept = now;
1907 s->logs.t_request = -1;
1908 s->logs.t_connect = -1;
1909 s->logs.t_data = -1;
1910 s->logs.t_close = 0;
1911 s->logs.uri = NULL;
1912 s->logs.status = -1;
1913 s->logs.bytes = 0;
willy tarreau9fe663a2005-12-17 13:02:59 +01001914
willy tarreau5cbea6f2005-12-17 12:48:26 +01001915 if ((p->mode == PR_MODE_TCP || p->mode == PR_MODE_HTTP)
1916 && (p->logfac1 >= 0 || p->logfac2 >= 0)) {
willy tarreau535ae7a2005-12-17 12:58:00 +01001917 struct sockaddr_in sockname;
willy tarreau5cbea6f2005-12-17 12:48:26 +01001918 unsigned char *pn, *sn;
1919 int namelen;
willy tarreau0f7af912005-12-17 12:21:26 +01001920
willy tarreau5cbea6f2005-12-17 12:48:26 +01001921 namelen = sizeof(sockname);
1922 if (get_original_dst(cfd, (struct sockaddr_in *)&sockname, &namelen) == -1)
1923 getsockname(cfd, (struct sockaddr *)&sockname, &namelen);
1924 sn = (unsigned char *)&sockname.sin_addr;
willy tarreau535ae7a2005-12-17 12:58:00 +01001925 pn = (unsigned char *)&s->cli_addr.sin_addr;
willy tarreau0f7af912005-12-17 12:21:26 +01001926
willy tarreau9fe663a2005-12-17 13:02:59 +01001927 if (p->to_log) {
1928 /* we have the client ip */
willy tarreaua1598082005-12-17 13:08:06 +01001929 if (s->logs.logwait & LW_CLIP)
1930 if (!(s->logs.logwait &= ~LW_CLIP))
willy tarreau9fe663a2005-12-17 13:02:59 +01001931 sess_log(s);
1932 }
1933 else
1934 send_log(p, LOG_INFO, "Connect from %d.%d.%d.%d:%d to %d.%d.%d.%d:%d (%s/%s)\n",
1935 pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
1936 sn[0], sn[1], sn[2], sn[3], ntohs(sockname.sin_port),
1937 p->id, (p->mode == PR_MODE_HTTP) ? "HTTP" : "TCP");
willy tarreau5cbea6f2005-12-17 12:48:26 +01001938 }
willy tarreau0f7af912005-12-17 12:21:26 +01001939
willy tarreau9fe663a2005-12-17 13:02:59 +01001940 if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
willy tarreauef900ab2005-12-17 12:52:52 +01001941 int len;
1942 len = sprintf(trash, "accept(%04x)=%04x\n", (unsigned short)fd, (unsigned short)cfd);
1943 write(1, trash, len);
1944 }
willy tarreau0f7af912005-12-17 12:21:26 +01001945
willy tarreau5cbea6f2005-12-17 12:48:26 +01001946 if ((s->req = pool_alloc(buffer)) == NULL) { /* no memory */
1947 close(cfd); /* nothing can be done for this fd without memory */
1948 pool_free(task, t);
1949 pool_free(session, s);
1950 return 0;
1951 }
1952 s->req->l = 0;
willy tarreaua1598082005-12-17 13:08:06 +01001953 s->req->total = 0;
willy tarreauef900ab2005-12-17 12:52:52 +01001954 s->req->h = s->req->r = s->req->lr = s->req->w = s->req->data; /* r and w will be reset further */
1955 s->req->rlim = s->req->data + BUFSIZE;
1956 if (s->cli_state == CL_STHEADERS) /* reserver some space for header rewriting */
1957 s->req->rlim -= MAXREWRITE;
willy tarreau0f7af912005-12-17 12:21:26 +01001958
willy tarreau5cbea6f2005-12-17 12:48:26 +01001959 if ((s->rep = pool_alloc(buffer)) == NULL) { /* no memory */
1960 pool_free(buffer, s->req);
1961 close(cfd); /* nothing can be done for this fd without memory */
1962 pool_free(task, t);
1963 pool_free(session, s);
1964 return 0;
1965 }
1966 s->rep->l = 0;
willy tarreaua1598082005-12-17 13:08:06 +01001967 s->rep->total = 0;
willy tarreauef900ab2005-12-17 12:52:52 +01001968 s->rep->h = s->rep->r = s->rep->lr = s->rep->w = s->rep->rlim = s->rep->data;
willy tarreau0f7af912005-12-17 12:21:26 +01001969
willy tarreau5cbea6f2005-12-17 12:48:26 +01001970 fdtab[cfd].read = &event_cli_read;
1971 fdtab[cfd].write = &event_cli_write;
1972 fdtab[cfd].owner = t;
1973 fdtab[cfd].state = FD_STREADY;
willy tarreau0f7af912005-12-17 12:21:26 +01001974
willy tarreau5cbea6f2005-12-17 12:48:26 +01001975 if (p->mode == PR_MODE_HEALTH) { /* health check mode, no client reading */
willy tarreaue39cd132005-12-17 13:00:18 +01001976 client_retnclose(s, 3, "OK\n"); /* forge an "OK" response */
willy tarreau5cbea6f2005-12-17 12:48:26 +01001977 }
1978 else {
1979 FD_SET(cfd, StaticReadEvent);
1980 }
1981
1982 fd_insert(cfd);
1983
1984 tv_eternity(&s->cnexpire);
1985 tv_eternity(&s->srexpire);
1986 tv_eternity(&s->swexpire);
1987 tv_eternity(&s->cwexpire);
1988
1989 if (s->proxy->clitimeout)
1990 tv_delayfrom(&s->crexpire, &now, s->proxy->clitimeout);
1991 else
1992 tv_eternity(&s->crexpire);
1993
1994 t->expire = s->crexpire;
1995
1996 task_queue(t);
willy tarreauef900ab2005-12-17 12:52:52 +01001997
1998 if (p->mode != PR_MODE_HEALTH)
1999 task_wakeup(&rq, t);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002000
2001 p->nbconn++;
2002 actconn++;
2003 totalconn++;
2004
2005 // fprintf(stderr, "accepting from %p => %d conn, %d total\n", p, actconn, totalconn);
2006 } /* end of while (p->nbconn < p->maxconn) */
2007 return 0;
2008}
willy tarreau0f7af912005-12-17 12:21:26 +01002009
willy tarreau0f7af912005-12-17 12:21:26 +01002010
willy tarreau5cbea6f2005-12-17 12:48:26 +01002011/*
2012 * This function is used only for server health-checks. It handles
2013 * the connection acknowledgement and returns 1 if the socket is OK,
2014 * or -1 if an error occured.
2015 */
2016int event_srv_hck(int fd) {
2017 struct task *t = fdtab[fd].owner;
2018 struct server *s = t->context;
willy tarreau0f7af912005-12-17 12:21:26 +01002019
willy tarreau5cbea6f2005-12-17 12:48:26 +01002020 int skerr, lskerr;
willy tarreauef900ab2005-12-17 12:52:52 +01002021 lskerr = sizeof(skerr);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002022 getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
2023 if (skerr)
2024 s->result = -1;
2025 else
2026 s->result = 1;
willy tarreau0f7af912005-12-17 12:21:26 +01002027
willy tarreau5cbea6f2005-12-17 12:48:26 +01002028 task_wakeup(&rq, t);
willy tarreau0f7af912005-12-17 12:21:26 +01002029 return 0;
2030}
2031
2032
2033/*
2034 * this function writes the string <str> at position <pos> which must be in buffer <b>,
2035 * and moves <end> just after the end of <str>.
2036 * <b>'s parameters (l, r, w, h, lr) are recomputed to be valid after the shift.
2037 * the shift value (positive or negative) is returned.
2038 * If there's no space left, the move is not done.
2039 *
2040 */
willy tarreau5cbea6f2005-12-17 12:48:26 +01002041int buffer_replace(struct buffer *b, char *pos, char *end, char *str) {
willy tarreau0f7af912005-12-17 12:21:26 +01002042 int delta;
2043 int len;
2044
2045 len = strlen(str);
2046 delta = len - (end - pos);
2047
2048 if (delta + b->r >= b->data + BUFSIZE)
2049 return 0; /* no space left */
2050
2051 /* first, protect the end of the buffer */
2052 memmove(end + delta, end, b->data + b->l - end);
2053
2054 /* now, copy str over pos */
2055 memcpy(pos, str,len);
2056
willy tarreau5cbea6f2005-12-17 12:48:26 +01002057 /* we only move data after the displaced zone */
2058 if (b->r > pos) b->r += delta;
2059 if (b->w > pos) b->w += delta;
2060 if (b->h > pos) b->h += delta;
2061 if (b->lr > pos) b->lr += delta;
willy tarreau0f7af912005-12-17 12:21:26 +01002062 b->l += delta;
2063
2064 return delta;
2065}
2066
2067/* same except that the string len is given */
willy tarreau5cbea6f2005-12-17 12:48:26 +01002068int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len) {
willy tarreau0f7af912005-12-17 12:21:26 +01002069 int delta;
2070
2071 delta = len - (end - pos);
2072
2073 if (delta + b->r >= b->data + BUFSIZE)
2074 return 0; /* no space left */
2075
2076 /* first, protect the end of the buffer */
2077 memmove(end + delta, end, b->data + b->l - end);
2078
2079 /* now, copy str over pos */
2080 memcpy(pos, str,len);
2081
willy tarreau5cbea6f2005-12-17 12:48:26 +01002082 /* we only move data after the displaced zone */
2083 if (b->r > pos) b->r += delta;
2084 if (b->w > pos) b->w += delta;
2085 if (b->h > pos) b->h += delta;
2086 if (b->lr > pos) b->lr += delta;
willy tarreau0f7af912005-12-17 12:21:26 +01002087 b->l += delta;
2088
2089 return delta;
2090}
2091
2092
2093int exp_replace(char *dst, char *src, char *str, regmatch_t *matches) {
2094 char *old_dst = dst;
2095
2096 while (*str) {
2097 if (*str == '\\') {
2098 str++;
2099 if (isdigit(*str)) {
2100 int len, num;
2101
2102 num = *str - '0';
2103 str++;
2104
2105 if (matches[num].rm_so > -1) {
2106 len = matches[num].rm_eo - matches[num].rm_so;
2107 memcpy(dst, src + matches[num].rm_so, len);
2108 dst += len;
2109 }
2110
2111 }
2112 else if (*str == 'x') {
2113 unsigned char hex1, hex2;
2114 str++;
2115
2116 hex1=toupper(*str++) - '0'; hex2=toupper(*str++) - '0';
2117
2118 if (hex1 > 9) hex1 -= 'A' - '9' - 1;
2119 if (hex2 > 9) hex2 -= 'A' - '9' - 1;
2120 *dst++ = (hex1<<4) + hex2;
2121 }
2122 else
2123 *dst++ = *str++;
2124 }
2125 else
2126 *dst++ = *str++;
2127 }
2128 *dst = 0;
2129 return dst - old_dst;
2130}
2131
willy tarreau9fe663a2005-12-17 13:02:59 +01002132
willy tarreau0f7af912005-12-17 12:21:26 +01002133/*
2134 * manages the client FSM and its socket. BTW, it also tries to handle the
2135 * cookie. It returns 1 if a state has changed (and a resync may be needed),
2136 * 0 else.
2137 */
willy tarreau5cbea6f2005-12-17 12:48:26 +01002138int process_cli(struct session *t) {
willy tarreau0f7af912005-12-17 12:21:26 +01002139 int s = t->srv_state;
2140 int c = t->cli_state;
2141 struct buffer *req = t->req;
2142 struct buffer *rep = t->rep;
2143
2144 //fprintf(stderr,"process_cli: c=%d, s=%d, cr=%d, cw=%d, sr=%d, sw=%d\n", c, s,
2145 //FD_ISSET(t->cli_fd, StaticReadEvent), FD_ISSET(t->cli_fd, StaticWriteEvent),
2146 //FD_ISSET(t->srv_fd, StaticReadEvent), FD_ISSET(t->srv_fd, StaticWriteEvent)
2147 //);
2148 if (c == CL_STHEADERS) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01002149 /* now parse the partial (or complete) headers */
2150 while (req->lr < req->r) { /* this loop only sees one header at each iteration */
2151 char *ptr;
2152 int delete_header;
willy tarreau0f7af912005-12-17 12:21:26 +01002153
willy tarreau5cbea6f2005-12-17 12:48:26 +01002154 ptr = req->lr;
willy tarreau0f7af912005-12-17 12:21:26 +01002155
willy tarreau0f7af912005-12-17 12:21:26 +01002156 /* look for the end of the current header */
2157 while (ptr < req->r && *ptr != '\n' && *ptr != '\r')
2158 ptr++;
2159
willy tarreau5cbea6f2005-12-17 12:48:26 +01002160 if (ptr == req->h) { /* empty line, end of headers */
2161 char newhdr[MAXREWRITE + 1];
2162 int line, len;
2163 /* we can only get here after an end of headers */
2164 /* we'll have something else to do here : add new headers ... */
willy tarreau0f7af912005-12-17 12:21:26 +01002165
willy tarreaue39cd132005-12-17 13:00:18 +01002166 if (t->flags & SN_CLDENY) {
2167 /* no need to go further */
willy tarreaua1598082005-12-17 13:08:06 +01002168 t->logs.status = 403;
willy tarreaue39cd132005-12-17 13:00:18 +01002169 client_retnclose(t, strlen(HTTP_403), HTTP_403);
2170 return 1;
2171 }
2172
willy tarreau5cbea6f2005-12-17 12:48:26 +01002173 for (line = 0; line < t->proxy->nb_reqadd; line++) {
2174 len = sprintf(newhdr, "%s\r\n", t->proxy->req_add[line]);
2175 buffer_replace2(req, req->h, req->h, newhdr, len);
2176 }
willy tarreau0f7af912005-12-17 12:21:26 +01002177
willy tarreau9fe663a2005-12-17 13:02:59 +01002178 if (t->proxy->options & PR_O_FWDFOR) {
2179 /* insert an X-Forwarded-For header */
2180 unsigned char *pn;
2181 pn = (unsigned char *)&t->cli_addr.sin_addr;
2182 len = sprintf(newhdr, "X-Forwarded-For: %d.%d.%d.%d\r\n",
2183 pn[0], pn[1], pn[2], pn[3]);
2184 buffer_replace2(req, req->h, req->h, newhdr, len);
2185 }
2186
willy tarreau5cbea6f2005-12-17 12:48:26 +01002187 t->cli_state = CL_STDATA;
willy tarreauef900ab2005-12-17 12:52:52 +01002188 req->rlim = req->data + BUFSIZE; /* no more rewrite needed */
willy tarreau0f7af912005-12-17 12:21:26 +01002189
willy tarreaua1598082005-12-17 13:08:06 +01002190 t->logs.t_request = tv_delta(&t->logs.tv_accept, &now);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002191 /* FIXME: we'll set the client in a wait state while we try to
2192 * connect to the server. Is this really needed ? wouldn't it be
2193 * better to release the maximum of system buffers instead ? */
willy tarreauef900ab2005-12-17 12:52:52 +01002194 //FD_CLR(t->cli_fd, StaticReadEvent);
2195 //tv_eternity(&t->crexpire);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002196 break;
2197 }
willy tarreau0f7af912005-12-17 12:21:26 +01002198
willy tarreau5cbea6f2005-12-17 12:48:26 +01002199 /* to get a complete header line, we need the ending \r\n, \n\r, \r or \n too */
2200 if (ptr > req->r - 2) {
2201 /* this is a partial header, let's wait for more to come */
2202 req->lr = ptr;
2203 break;
2204 }
willy tarreau0f7af912005-12-17 12:21:26 +01002205
willy tarreau5cbea6f2005-12-17 12:48:26 +01002206 /* now we know that *ptr is either \r or \n,
2207 * and that there are at least 1 char after it.
2208 */
2209 if ((ptr[0] == ptr[1]) || (ptr[1] != '\r' && ptr[1] != '\n'))
2210 req->lr = ptr + 1; /* \r\r, \n\n, \r[^\n], \n[^\r] */
2211 else
2212 req->lr = ptr + 2; /* \r\n or \n\r */
willy tarreau0f7af912005-12-17 12:21:26 +01002213
willy tarreau5cbea6f2005-12-17 12:48:26 +01002214 /*
2215 * now we know that we have a full header ; we can do whatever
2216 * we want with these pointers :
2217 * req->h = beginning of header
2218 * ptr = end of header (first \r or \n)
2219 * req->lr = beginning of next line (next rep->h)
2220 * req->r = end of data (not used at this stage)
2221 */
willy tarreau0f7af912005-12-17 12:21:26 +01002222
willy tarreaua1598082005-12-17 13:08:06 +01002223 if (t->logs.logwait & LW_REQ &&
2224 t->proxy->mode & PR_MODE_HTTP) {
willy tarreau9fe663a2005-12-17 13:02:59 +01002225 /* we have a complete HTTP request that we must log */
2226 int urilen;
2227
willy tarreaua1598082005-12-17 13:08:06 +01002228 if ((t->logs.uri = pool_alloc(requri)) == NULL) {
willy tarreau9fe663a2005-12-17 13:02:59 +01002229 Alert("HTTP logging : out of memory.\n");
willy tarreaua1598082005-12-17 13:08:06 +01002230 t->logs.status = 502;
2231 client_retnclose(t, strlen(HTTP_502), HTTP_502);
willy tarreau9fe663a2005-12-17 13:02:59 +01002232 return 1;
2233 }
2234
2235 urilen = ptr - req->h;
2236 if (urilen >= REQURI_LEN)
2237 urilen = REQURI_LEN - 1;
willy tarreaua1598082005-12-17 13:08:06 +01002238 memcpy(t->logs.uri, req->h, urilen);
2239 t->logs.uri[urilen] = 0;
willy tarreau9fe663a2005-12-17 13:02:59 +01002240
willy tarreaua1598082005-12-17 13:08:06 +01002241 if (!(t->logs.logwait &= ~LW_REQ))
willy tarreau9fe663a2005-12-17 13:02:59 +01002242 sess_log(t);
2243 }
2244
willy tarreau5cbea6f2005-12-17 12:48:26 +01002245 delete_header = 0;
willy tarreau0f7af912005-12-17 12:21:26 +01002246
willy tarreau9fe663a2005-12-17 13:02:59 +01002247 if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01002248 int len, max;
2249 len = sprintf(trash, "clihdr[%04x:%04x]: ", (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
2250 max = ptr - req->h;
2251 UBOUND(max, sizeof(trash) - len - 1);
2252 len += strlcpy(trash + len, req->h, max + 1);
2253 trash[len++] = '\n';
2254 write(1, trash, len);
2255 }
willy tarreau0f7af912005-12-17 12:21:26 +01002256
willy tarreau5cbea6f2005-12-17 12:48:26 +01002257 /* try headers regexps */
willy tarreaue39cd132005-12-17 13:00:18 +01002258 if (t->proxy->req_exp != NULL && !(t->flags & SN_CLDENY)) {
2259 struct hdr_exp *exp;
willy tarreau5cbea6f2005-12-17 12:48:26 +01002260 char term;
2261
2262 term = *ptr;
2263 *ptr = '\0';
willy tarreaue39cd132005-12-17 13:00:18 +01002264 exp = t->proxy->req_exp;
2265 do {
2266 if (regexec(exp->preg, req->h, MAX_MATCH, pmatch, 0) == 0) {
2267 switch (exp->action) {
2268 case ACT_ALLOW:
2269 if (!(t->flags & SN_CLDENY))
2270 t->flags |= SN_CLALLOW;
2271 break;
2272 case ACT_REPLACE:
2273 if (!(t->flags & SN_CLDENY)) {
2274 int len = exp_replace(trash, req->h, exp->replace, pmatch);
2275 ptr += buffer_replace2(req, req->h, ptr, trash, len);
2276 }
2277 break;
2278 case ACT_REMOVE:
2279 if (!(t->flags & SN_CLDENY))
2280 delete_header = 1;
2281 break;
2282 case ACT_DENY:
2283 if (!(t->flags & SN_CLALLOW))
2284 t->flags |= SN_CLDENY;
2285 break;
willy tarreau0f7af912005-12-17 12:21:26 +01002286 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01002287 break;
willy tarreau0f7af912005-12-17 12:21:26 +01002288 }
willy tarreaue39cd132005-12-17 13:00:18 +01002289 } while ((exp = exp->next) != NULL);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002290 *ptr = term; /* restore the string terminator */
willy tarreau0f7af912005-12-17 12:21:26 +01002291 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01002292
2293 /* now look for cookies */
2294 if (!delete_header && (req->r >= req->h + 8) && (t->proxy->cookie_name != NULL)
2295 && (strncmp(req->h, "Cookie: ", 8) == 0)) {
2296 char *p1, *p2, *p3, *p4;
2297
2298 p1 = req->h + 8; /* first char after 'Cookie: ' */
2299
2300 while (p1 < ptr) {
2301 while (p1 < ptr && (isspace(*p1) || *p1 == ';'))
2302 p1++;
2303
2304 if (p1 == ptr)
2305 break;
2306 else if (*p1 == ';') { /* next cookie */
2307 ++p1;
2308 continue;
2309 }
2310
2311 /* p1 is at the beginning of the cookie name */
2312 p2 = p1;
2313
2314 while (p2 < ptr && *p2 != '=' && *p2 != ';')
2315 p2++;
2316
2317 if (p2 == ptr)
2318 break;
2319 else if (*p2 == ';') { /* next cookie */
2320 p1=++p2;
2321 continue;
2322 }
2323
2324 p3 = p2 + 1; /* skips the '=' sign */
2325 if (p3 == ptr)
2326 break;
2327
2328 p4=p3;
2329 while (p4 < ptr && !isspace(*p4) && *p4 != ';')
2330 p4++;
2331
2332 /* here, we have the cookie name between p1 and p2,
2333 * and its value between p3 and p4.
2334 * we can process it.
2335 */
2336
2337 if ((p2 - p1 == strlen(t->proxy->cookie_name)) &&
2338 (strncmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) {
2339 /* Cool... it's the right one */
2340 struct server *srv = t->proxy->srv;
2341
2342 while (srv &&
2343 ((srv->cklen != p4 - p3) || memcmp(p3, srv->cookie, p4 - p3))) {
2344 srv = srv->next;
2345 }
2346
2347 if (srv) { /* we found the server */
willy tarreaue39cd132005-12-17 13:00:18 +01002348 t->flags |= SN_DIRECT;
willy tarreau5cbea6f2005-12-17 12:48:26 +01002349 t->srv = srv;
2350 }
2351
2352 break;
2353 }
2354 else {
2355 // fprintf(stderr,"Ignoring unknown cookie : ");
2356 // write(2, p1, p2-p1);
2357 // fprintf(stderr," = ");
2358 // write(2, p3, p4-p3);
2359 // fprintf(stderr,"\n");
2360 }
2361 /* we'll have to look for another cookie ... */
2362 p1 = p4;
2363 } /* while (p1 < ptr) */
2364 } /* end of cookie processing */
2365
2366 /* let's look if we have to delete this header */
willy tarreaue39cd132005-12-17 13:00:18 +01002367 if (delete_header && !(t->flags & SN_CLDENY)) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01002368 buffer_replace2(req, req->h, req->lr, "", 0);
willy tarreau0f7af912005-12-17 12:21:26 +01002369 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01002370 req->h = req->lr;
2371 } /* while (req->lr < req->r) */
2372
2373 /* end of header processing (even if incomplete) */
2374
willy tarreauef900ab2005-12-17 12:52:52 +01002375 if ((req->l < req->rlim - req->data) && ! FD_ISSET(t->cli_fd, StaticReadEvent)) {
2376 /* fd in StaticReadEvent was disabled, perhaps because of a previous buffer
2377 * full. We cannot loop here since event_cli_read will disable it only if
2378 * req->l == rlim-data
2379 */
willy tarreau5cbea6f2005-12-17 12:48:26 +01002380 FD_SET(t->cli_fd, StaticReadEvent);
2381 if (t->proxy->clitimeout)
2382 tv_delayfrom(&t->crexpire, &now, t->proxy->clitimeout);
2383 else
2384 tv_eternity(&t->crexpire);
2385 }
2386
willy tarreaue39cd132005-12-17 13:00:18 +01002387 /* Since we are in header mode, if there's no space left for headers, we
willy tarreauef900ab2005-12-17 12:52:52 +01002388 * won't be able to free more later, so the session will never terminate.
2389 */
willy tarreaue39cd132005-12-17 13:00:18 +01002390 if (req->l >= req->rlim - req->data) {
willy tarreaua1598082005-12-17 13:08:06 +01002391 t->logs.status = 400;
willy tarreaue39cd132005-12-17 13:00:18 +01002392 client_retnclose(t, strlen(HTTP_400), HTTP_400);
2393 return 1;
2394 }
2395 else if (t->res_cr == RES_ERROR || t->res_cr == RES_NULL
2396 || tv_cmp2_ms(&t->crexpire, &now) <= 0) {
2397
2398 /* read timeout, read error, or last read : give up.
2399 * since we are in header mode, if there's no space left for headers, we
2400 * won't be able to free more later, so the session will never terminate.
2401 */
willy tarreau5cbea6f2005-12-17 12:48:26 +01002402 tv_eternity(&t->crexpire);
2403 fd_delete(t->cli_fd);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002404 t->cli_state = CL_STCLOSE;
2405 return 1;
willy tarreau0f7af912005-12-17 12:21:26 +01002406 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01002407
2408 return t->cli_state != CL_STHEADERS;
willy tarreau0f7af912005-12-17 12:21:26 +01002409 }
2410 else if (c == CL_STDATA) {
2411 /* read or write error */
2412 if (t->res_cw == RES_ERROR || t->res_cr == RES_ERROR) {
willy tarreau0f7af912005-12-17 12:21:26 +01002413 tv_eternity(&t->crexpire);
2414 tv_eternity(&t->cwexpire);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002415 fd_delete(t->cli_fd);
willy tarreau0f7af912005-12-17 12:21:26 +01002416 t->cli_state = CL_STCLOSE;
2417 return 1;
2418 }
2419 /* read timeout, last read, or end of server write */
2420 else if (t->res_cr == RES_NULL || s == SV_STSHUTW || s == SV_STCLOSE
2421 || tv_cmp2_ms(&t->crexpire, &now) <= 0) {
willy tarreau0f7af912005-12-17 12:21:26 +01002422 FD_CLR(t->cli_fd, StaticReadEvent);
2423 // if (req->l == 0) /* nothing to write on the server side */
2424 // FD_CLR(t->srv_fd, StaticWriteEvent);
2425 tv_eternity(&t->crexpire);
2426 shutdown(t->cli_fd, SHUT_RD);
2427 t->cli_state = CL_STSHUTR;
2428 return 1;
2429 }
2430 /* write timeout, or last server read and buffer empty */
2431 else if (((s == SV_STSHUTR || s == SV_STCLOSE) && (rep->l == 0))
2432 ||(tv_cmp2_ms(&t->cwexpire, &now) <= 0)) {
willy tarreau0f7af912005-12-17 12:21:26 +01002433 FD_CLR(t->cli_fd, StaticWriteEvent);
2434 tv_eternity(&t->cwexpire);
2435 shutdown(t->cli_fd, SHUT_WR);
2436 t->cli_state = CL_STSHUTW;
2437 return 1;
2438 }
2439
willy tarreauef900ab2005-12-17 12:52:52 +01002440 if (req->l >= req->rlim - req->data) {
2441 /* no room to read more data */
willy tarreau0f7af912005-12-17 12:21:26 +01002442 if (FD_ISSET(t->cli_fd, StaticReadEvent)) {
willy tarreauef900ab2005-12-17 12:52:52 +01002443 /* stop reading until we get some space */
willy tarreau0f7af912005-12-17 12:21:26 +01002444 FD_CLR(t->cli_fd, StaticReadEvent);
2445 tv_eternity(&t->crexpire);
2446 }
2447 }
2448 else {
willy tarreauef900ab2005-12-17 12:52:52 +01002449 /* there's still some space in the buffer */
willy tarreau0f7af912005-12-17 12:21:26 +01002450 if (! FD_ISSET(t->cli_fd, StaticReadEvent)) {
2451 FD_SET(t->cli_fd, StaticReadEvent);
2452 if (t->proxy->clitimeout)
2453 tv_delayfrom(&t->crexpire, &now, t->proxy->clitimeout);
2454 else
2455 tv_eternity(&t->crexpire);
2456 }
2457 }
2458
2459 if ((rep->l == 0) ||
willy tarreau5cbea6f2005-12-17 12:48:26 +01002460 ((s == SV_STHEADERS) /* FIXME: this may be optimized && (rep->w == rep->h)*/)) {
willy tarreau0f7af912005-12-17 12:21:26 +01002461 if (FD_ISSET(t->cli_fd, StaticWriteEvent)) {
2462 FD_CLR(t->cli_fd, StaticWriteEvent); /* stop writing */
2463 tv_eternity(&t->cwexpire);
2464 }
2465 }
2466 else { /* buffer not empty */
2467 if (! FD_ISSET(t->cli_fd, StaticWriteEvent)) {
2468 FD_SET(t->cli_fd, StaticWriteEvent); /* restart writing */
2469 if (t->proxy->clitimeout)
2470 tv_delayfrom(&t->cwexpire, &now, t->proxy->clitimeout);
2471 else
2472 tv_eternity(&t->cwexpire);
2473 }
2474 }
2475 return 0; /* other cases change nothing */
2476 }
2477 else if (c == CL_STSHUTR) {
2478 if ((t->res_cw == RES_ERROR) ||
2479 ((s == SV_STSHUTR || s == SV_STCLOSE) && (rep->l == 0))
willy tarreaub719f002005-12-17 12:55:07 +01002480 || (tv_cmp2_ms(&t->cwexpire, &now) <= 0)) {
willy tarreau0f7af912005-12-17 12:21:26 +01002481 tv_eternity(&t->cwexpire);
2482 fd_delete(t->cli_fd);
willy tarreau0f7af912005-12-17 12:21:26 +01002483 t->cli_state = CL_STCLOSE;
2484 return 1;
2485 }
2486 else if ((rep->l == 0) ||
willy tarreau5cbea6f2005-12-17 12:48:26 +01002487 ((s == SV_STHEADERS) /* FIXME: this may be optimized && (rep->w == rep->h)*/)) {
willy tarreau0f7af912005-12-17 12:21:26 +01002488 if (FD_ISSET(t->cli_fd, StaticWriteEvent)) {
2489 FD_CLR(t->cli_fd, StaticWriteEvent); /* stop writing */
2490 tv_eternity(&t->cwexpire);
2491 }
2492 }
2493 else { /* buffer not empty */
2494 if (! FD_ISSET(t->cli_fd, StaticWriteEvent)) {
2495 FD_SET(t->cli_fd, StaticWriteEvent); /* restart writing */
2496 if (t->proxy->clitimeout)
2497 tv_delayfrom(&t->cwexpire, &now, t->proxy->clitimeout);
2498 else
2499 tv_eternity(&t->cwexpire);
2500 }
2501 }
2502 return 0;
2503 }
2504 else if (c == CL_STSHUTW) {
2505 if (t->res_cr == RES_ERROR || t->res_cr == RES_NULL || s == SV_STSHUTW ||
willy tarreaub719f002005-12-17 12:55:07 +01002506 s == SV_STCLOSE || tv_cmp2_ms(&t->crexpire, &now) <= 0) {
willy tarreau0f7af912005-12-17 12:21:26 +01002507 tv_eternity(&t->crexpire);
2508 fd_delete(t->cli_fd);
willy tarreau0f7af912005-12-17 12:21:26 +01002509 t->cli_state = CL_STCLOSE;
2510 return 1;
2511 }
willy tarreauef900ab2005-12-17 12:52:52 +01002512 else if (req->l >= req->rlim - req->data) {
2513 /* no room to read more data */
willy tarreau0f7af912005-12-17 12:21:26 +01002514 if (FD_ISSET(t->cli_fd, StaticReadEvent)) {
willy tarreauef900ab2005-12-17 12:52:52 +01002515 /* stop reading until we get some space */
willy tarreau0f7af912005-12-17 12:21:26 +01002516 FD_CLR(t->cli_fd, StaticReadEvent);
2517 tv_eternity(&t->crexpire);
2518 }
2519 }
2520 else {
willy tarreauef900ab2005-12-17 12:52:52 +01002521 /* there's still some space in the buffer */
willy tarreau0f7af912005-12-17 12:21:26 +01002522 if (! FD_ISSET(t->cli_fd, StaticReadEvent)) {
2523 FD_SET(t->cli_fd, StaticReadEvent);
2524 if (t->proxy->clitimeout)
2525 tv_delayfrom(&t->crexpire, &now, t->proxy->clitimeout);
2526 else
2527 tv_eternity(&t->crexpire);
2528 }
2529 }
2530 return 0;
2531 }
2532 else { /* CL_STCLOSE: nothing to do */
willy tarreau9fe663a2005-12-17 13:02:59 +01002533 if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
willy tarreau0f7af912005-12-17 12:21:26 +01002534 int len;
willy tarreau5cbea6f2005-12-17 12:48:26 +01002535 len = sprintf(trash, "clicls[%04x:%04x]\n", (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
willy tarreau0f7af912005-12-17 12:21:26 +01002536 write(1, trash, len);
2537 }
2538 return 0;
2539 }
2540 return 0;
2541}
2542
2543
2544/*
2545 * manages the server FSM and its socket. It returns 1 if a state has changed
2546 * (and a resync may be needed), 0 else.
2547 */
willy tarreau5cbea6f2005-12-17 12:48:26 +01002548int process_srv(struct session *t) {
willy tarreau0f7af912005-12-17 12:21:26 +01002549 int s = t->srv_state;
2550 int c = t->cli_state;
2551 struct buffer *req = t->req;
2552 struct buffer *rep = t->rep;
2553
willy tarreau5cbea6f2005-12-17 12:48:26 +01002554 //fprintf(stderr,"process_srv: c=%d, s=%d\n", c, s);
2555 //fprintf(stderr,"process_srv: c=%d, s=%d, cr=%d, cw=%d, sr=%d, sw=%d\n", c, s,
2556 //FD_ISSET(t->cli_fd, StaticReadEvent), FD_ISSET(t->cli_fd, StaticWriteEvent),
2557 //FD_ISSET(t->srv_fd, StaticReadEvent), FD_ISSET(t->srv_fd, StaticWriteEvent)
2558 //);
willy tarreau0f7af912005-12-17 12:21:26 +01002559 if (s == SV_STIDLE) {
2560 if (c == CL_STHEADERS)
2561 return 0; /* stay in idle, waiting for data to reach the client side */
2562 else if (c == CL_STCLOSE ||
2563 c == CL_STSHUTW ||
2564 (c == CL_STSHUTR && t->req->l == 0)) { /* give up */
2565 tv_eternity(&t->cnexpire);
2566 t->srv_state = SV_STCLOSE;
2567 return 1;
2568 }
2569 else { /* go to SV_STCONN */
willy tarreau5cbea6f2005-12-17 12:48:26 +01002570 if (connect_server(t) == 0) { /* initiate a connection to the server */
willy tarreau0f7af912005-12-17 12:21:26 +01002571 //fprintf(stderr,"0: c=%d, s=%d\n", c, s);
2572 t->srv_state = SV_STCONN;
2573 }
2574 else { /* try again */
2575 while (t->conn_retries-- > 0) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01002576 if ((t->proxy->options & PR_O_REDISP) && (t->conn_retries == 0)) {
willy tarreaue39cd132005-12-17 13:00:18 +01002577 t->flags &= ~SN_DIRECT; /* ignore cookie and force to use the dispatcher */
willy tarreau5cbea6f2005-12-17 12:48:26 +01002578 t->srv = NULL; /* it's left to the dispatcher to choose a server */
2579 }
2580
2581 if (connect_server(t) == 0) {
willy tarreau0f7af912005-12-17 12:21:26 +01002582 t->srv_state = SV_STCONN;
2583 break;
2584 }
2585 }
2586 if (t->conn_retries < 0) {
2587 /* if conn_retries < 0 or other error, let's abort */
2588 tv_eternity(&t->cnexpire);
2589 t->srv_state = SV_STCLOSE;
willy tarreaua1598082005-12-17 13:08:06 +01002590 t->logs.status = 502;
willy tarreaue39cd132005-12-17 13:00:18 +01002591 client_return(t, strlen(HTTP_502), HTTP_502);
willy tarreau0f7af912005-12-17 12:21:26 +01002592 }
2593 }
2594 return 1;
2595 }
2596 }
2597 else if (s == SV_STCONN) { /* connection in progress */
2598 if (t->res_sw == RES_SILENT && tv_cmp2_ms(&t->cnexpire, &now) > 0) {
2599 //fprintf(stderr,"1: c=%d, s=%d\n", c, s);
2600 return 0; /* nothing changed */
2601 }
2602 else if (t->res_sw == RES_SILENT || t->res_sw == RES_ERROR) {
2603 //fprintf(stderr,"2: c=%d, s=%d\n", c, s);
2604 /* timeout, connect error or first write error */
willy tarreau5cbea6f2005-12-17 12:48:26 +01002605 //FD_CLR(t->srv_fd, StaticWriteEvent);
willy tarreau0f7af912005-12-17 12:21:26 +01002606 fd_delete(t->srv_fd);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002607 //close(t->srv_fd);
willy tarreau0f7af912005-12-17 12:21:26 +01002608 t->conn_retries--;
willy tarreau5cbea6f2005-12-17 12:48:26 +01002609 if (t->conn_retries >= 0) {
2610 if ((t->proxy->options & PR_O_REDISP) && (t->conn_retries == 0)) {
willy tarreaue39cd132005-12-17 13:00:18 +01002611 t->flags &= ~SN_DIRECT; /* ignore cookie and force to use the dispatcher */
willy tarreau5cbea6f2005-12-17 12:48:26 +01002612 t->srv = NULL; /* it's left to the dispatcher to choose a server */
2613 }
2614 if (connect_server(t) == 0)
2615 return 0; /* no state changed */
willy tarreau0f7af912005-12-17 12:21:26 +01002616 }
2617 /* if conn_retries < 0 or other error, let's abort */
2618 tv_eternity(&t->cnexpire);
2619 t->srv_state = SV_STCLOSE;
2620 return 1;
2621 }
2622 else { /* no error or write 0 */
willy tarreaua1598082005-12-17 13:08:06 +01002623 t->logs.t_connect = tv_delta(&t->logs.tv_accept, &now);
2624
willy tarreau0f7af912005-12-17 12:21:26 +01002625 //fprintf(stderr,"3: c=%d, s=%d\n", c, s);
2626 if (req->l == 0) /* nothing to write */
2627 FD_CLR(t->srv_fd, StaticWriteEvent);
2628 else /* need the right to write */
2629 FD_SET(t->srv_fd, StaticWriteEvent);
2630
2631 if (t->proxy->mode == PR_MODE_TCP) { /* let's allow immediate data connection in this case */
2632 FD_SET(t->srv_fd, StaticReadEvent);
2633 if (t->proxy->srvtimeout)
2634 tv_delayfrom(&t->srexpire, &now, t->proxy->srvtimeout);
2635 else
2636 tv_eternity(&t->srexpire);
2637
2638 t->srv_state = SV_STDATA;
willy tarreauef900ab2005-12-17 12:52:52 +01002639 rep->rlim = rep->data + BUFSIZE; /* no rewrite needed */
willy tarreau0f7af912005-12-17 12:21:26 +01002640 }
willy tarreauef900ab2005-12-17 12:52:52 +01002641 else {
willy tarreau0f7af912005-12-17 12:21:26 +01002642 t->srv_state = SV_STHEADERS;
willy tarreauef900ab2005-12-17 12:52:52 +01002643 rep->rlim = rep->data + BUFSIZE - MAXREWRITE; /* rewrite needed */
2644 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01002645 tv_eternity(&t->cnexpire);
willy tarreau0f7af912005-12-17 12:21:26 +01002646 return 1;
2647 }
2648 }
2649 else if (s == SV_STHEADERS) { /* receiving server headers */
willy tarreau5cbea6f2005-12-17 12:48:26 +01002650
2651 /* now parse the partial (or complete) headers */
2652 while (rep->lr < rep->r) { /* this loop only sees one header at each iteration */
2653 char *ptr;
2654 int delete_header;
2655
2656 ptr = rep->lr;
2657
2658 /* look for the end of the current header */
2659 while (ptr < rep->r && *ptr != '\n' && *ptr != '\r')
2660 ptr++;
2661
2662 if (ptr == rep->h) {
2663 char newhdr[MAXREWRITE + 1];
2664 int line, len;
2665
2666 /* we can only get here after an end of headers */
2667 /* we'll have something else to do here : add new headers ... */
2668
willy tarreaue39cd132005-12-17 13:00:18 +01002669 if ((t->srv) && !(t->flags & SN_DIRECT) && (t->proxy->options & PR_O_COOK_INS)) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01002670 /* the server is known, it's not the one the client requested, we have to
2671 * insert a set-cookie here.
2672 */
2673 len = sprintf(newhdr, "Set-Cookie: %s=%s; path=/\r\n",
2674 t->proxy->cookie_name, t->srv->cookie);
2675 buffer_replace2(rep, rep->h, rep->h, newhdr, len);
2676 }
2677
2678 /* headers to be added */
2679 for (line = 0; line < t->proxy->nb_rspadd; line++) {
2680 len = sprintf(newhdr, "%s\r\n", t->proxy->rsp_add[line]);
2681 buffer_replace2(rep, rep->h, rep->h, newhdr, len);
2682 }
2683
2684 t->srv_state = SV_STDATA;
willy tarreauef900ab2005-12-17 12:52:52 +01002685 rep->rlim = rep->data + BUFSIZE; /* no more rewrite needed */
willy tarreaua1598082005-12-17 13:08:06 +01002686 t->logs.t_data = tv_delta(&t->logs.tv_accept, &now);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002687 break;
2688 }
2689
2690 /* to get a complete header line, we need the ending \r\n, \n\r, \r or \n too */
2691 if (ptr > rep->r - 2) {
2692 /* this is a partial header, let's wait for more to come */
2693 rep->lr = ptr;
2694 break;
2695 }
2696
2697 // fprintf(stderr,"h=%p, ptr=%p, lr=%p, r=%p, *h=", rep->h, ptr, rep->lr, rep->r);
2698 // write(2, rep->h, ptr - rep->h); fprintf(stderr,"\n");
2699
2700 /* now we know that *ptr is either \r or \n,
2701 * and that there are at least 1 char after it.
2702 */
2703 if ((ptr[0] == ptr[1]) || (ptr[1] != '\r' && ptr[1] != '\n'))
2704 rep->lr = ptr + 1; /* \r\r, \n\n, \r[^\n], \n[^\r] */
2705 else
2706 rep->lr = ptr + 2; /* \r\n or \n\r */
2707
2708 /*
2709 * now we know that we have a full header ; we can do whatever
2710 * we want with these pointers :
2711 * rep->h = beginning of header
2712 * ptr = end of header (first \r or \n)
2713 * rep->lr = beginning of next line (next rep->h)
2714 * rep->r = end of data (not used at this stage)
2715 */
2716
willy tarreaua1598082005-12-17 13:08:06 +01002717
2718 if (t->logs.logwait & LW_RESP) {
2719 t->logs.logwait &= ~LW_RESP;
2720 t->logs.status = atoi(rep->h + 9);
2721 }
2722
willy tarreau5cbea6f2005-12-17 12:48:26 +01002723 delete_header = 0;
2724
willy tarreau9fe663a2005-12-17 13:02:59 +01002725 if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01002726 int len, max;
2727 len = sprintf(trash, "srvhdr[%04x:%04x]: ", (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
2728 max = ptr - rep->h;
2729 UBOUND(max, sizeof(trash) - len - 1);
2730 len += strlcpy(trash + len, rep->h, max + 1);
2731 trash[len++] = '\n';
2732 write(1, trash, len);
2733 }
2734
2735 /* try headers regexps */
willy tarreaue39cd132005-12-17 13:00:18 +01002736 if (t->proxy->rsp_exp != NULL && !(t->flags & SN_SVDENY)) {
2737 struct hdr_exp *exp;
willy tarreau5cbea6f2005-12-17 12:48:26 +01002738 char term;
2739
2740 term = *ptr;
2741 *ptr = '\0';
willy tarreaue39cd132005-12-17 13:00:18 +01002742 exp = t->proxy->rsp_exp;
2743 do {
2744 if (regexec(exp->preg, rep->h, MAX_MATCH, pmatch, 0) == 0) {
2745 switch (exp->action) {
2746 case ACT_ALLOW:
2747 if (!(t->flags & SN_SVDENY))
2748 t->flags |= SN_SVALLOW;
2749 break;
2750 case ACT_REPLACE:
2751 if (!(t->flags & SN_SVDENY)) {
2752 int len = exp_replace(trash, rep->h, exp->replace, pmatch);
2753 ptr += buffer_replace2(rep, rep->h, ptr, trash, len);
2754 }
2755 break;
2756 case ACT_REMOVE:
2757 if (!(t->flags & SN_SVDENY))
2758 delete_header = 1;
2759 break;
2760 case ACT_DENY:
2761 if (!(t->flags & SN_SVALLOW))
2762 t->flags |= SN_SVDENY;
2763 break;
willy tarreau5cbea6f2005-12-17 12:48:26 +01002764 }
2765 break;
2766 }
willy tarreaue39cd132005-12-17 13:00:18 +01002767 } while ((exp = exp->next) != NULL);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002768 *ptr = term; /* restore the string terminator */
2769 }
2770
2771 /* check for server cookies */
2772 if (!delete_header && (t->proxy->options & PR_O_COOK_ANY) && (rep->r >= rep->h + 12) &&
2773 (t->proxy->cookie_name != NULL) && (strncmp(rep->h, "Set-Cookie: ", 12) == 0)) {
2774 char *p1, *p2, *p3, *p4;
2775
2776 p1 = rep->h + 12; /* first char after 'Set-Cookie: ' */
2777
2778 while (p1 < ptr) { /* in fact, we'll break after the first cookie */
2779 while (p1 < ptr && (isspace(*p1)))
2780 p1++;
2781
2782 if (p1 == ptr || *p1 == ';') /* end of cookie */
2783 break;
2784
2785 /* p1 is at the beginning of the cookie name */
2786 p2 = p1;
2787
2788 while (p2 < ptr && *p2 != '=' && *p2 != ';')
2789 p2++;
2790
2791 if (p2 == ptr || *p2 == ';') /* next cookie */
2792 break;
2793
2794 p3 = p2 + 1; /* skips the '=' sign */
2795 if (p3 == ptr)
2796 break;
2797
2798 p4 = p3;
2799 while (p4 < ptr && !isspace(*p4) && *p4 != ';')
2800 p4++;
2801
2802 /* here, we have the cookie name between p1 and p2,
2803 * and its value between p3 and p4.
2804 * we can process it.
2805 */
2806
2807 if ((p2 - p1 == strlen(t->proxy->cookie_name)) &&
2808 (strncmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) {
2809 /* Cool... it's the right one */
2810
2811 /* If the cookie is in insert mode on a known server, we'll delete
2812 * this occurrence because we'll insert another one later.
2813 * We'll delete it too if the "indirect" option is set and we're in
2814 * a direct access. */
2815 if (((t->srv) && (t->proxy->options & PR_O_COOK_INS)) ||
willy tarreaue39cd132005-12-17 13:00:18 +01002816 ((t->flags & SN_DIRECT) && (t->proxy->options & PR_O_COOK_IND))) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01002817 /* this header must be deleted */
2818 delete_header = 1;
2819 }
2820 else if ((t->srv) && (t->proxy->options & PR_O_COOK_RW)) {
2821 /* replace bytes p3->p4 with the cookie name associated
2822 * with this server since we know it.
2823 */
2824 buffer_replace2(rep, p3, p4, t->srv->cookie, t->srv->cklen);
2825 }
2826 break;
2827 }
2828 else {
2829 // fprintf(stderr,"Ignoring unknown cookie : ");
2830 // write(2, p1, p2-p1);
2831 // fprintf(stderr," = ");
2832 // write(2, p3, p4-p3);
2833 // fprintf(stderr,"\n");
2834 }
2835 break; /* we don't want to loop again since there cannot be another cookie on the same line */
2836 } /* we're now at the end of the cookie value */
2837 } /* end of cookie processing */
2838
2839 /* let's look if we have to delete this header */
willy tarreaue39cd132005-12-17 13:00:18 +01002840 if (delete_header && !(t->flags & SN_SVDENY))
willy tarreau5cbea6f2005-12-17 12:48:26 +01002841 buffer_replace2(rep, rep->h, rep->lr, "", 0);
willy tarreaue39cd132005-12-17 13:00:18 +01002842
willy tarreau5cbea6f2005-12-17 12:48:26 +01002843 rep->h = rep->lr;
2844 } /* while (rep->lr < rep->r) */
2845
2846 /* end of header processing (even if incomplete) */
2847
willy tarreauef900ab2005-12-17 12:52:52 +01002848 if ((rep->l < rep->rlim - rep->data) && ! FD_ISSET(t->srv_fd, StaticReadEvent)) {
2849 /* fd in StaticReadEvent was disabled, perhaps because of a previous buffer
2850 * full. We cannot loop here since event_srv_read will disable it only if
2851 * rep->l == rlim-data
2852 */
willy tarreau5cbea6f2005-12-17 12:48:26 +01002853 FD_SET(t->srv_fd, StaticReadEvent);
2854 if (t->proxy->srvtimeout)
2855 tv_delayfrom(&t->srexpire, &now, t->proxy->srvtimeout);
2856 else
2857 tv_eternity(&t->srexpire);
2858 }
willy tarreau0f7af912005-12-17 12:21:26 +01002859
2860 /* read or write error */
2861 if (t->res_sw == RES_ERROR || t->res_sr == RES_ERROR) {
willy tarreau0f7af912005-12-17 12:21:26 +01002862 tv_eternity(&t->srexpire);
2863 tv_eternity(&t->swexpire);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002864 fd_delete(t->srv_fd);
willy tarreau0f7af912005-12-17 12:21:26 +01002865 t->srv_state = SV_STCLOSE;
2866 return 1;
2867 }
willy tarreauef900ab2005-12-17 12:52:52 +01002868 /* read timeout, last read, or end of client write
2869 * since we are in header mode, if there's no space left for headers, we
2870 * won't be able to free more later, so the session will never terminate.
2871 */
2872 else if (t->res_sr == RES_NULL || c == CL_STSHUTW || c == CL_STCLOSE
2873 || rep->l >= rep->rlim - rep->data || tv_cmp2_ms(&t->srexpire, &now) <= 0) {
willy tarreau0f7af912005-12-17 12:21:26 +01002874 FD_CLR(t->srv_fd, StaticReadEvent);
2875 tv_eternity(&t->srexpire);
2876 shutdown(t->srv_fd, SHUT_RD);
2877 t->srv_state = SV_STSHUTR;
2878 return 1;
2879
2880 }
2881 /* write timeout, or last client read and buffer empty */
2882 else if (((c == CL_STSHUTR || c == CL_STCLOSE) && (req->l == 0)) ||
2883 (tv_cmp2_ms(&t->swexpire, &now) <= 0)) {
2884 FD_CLR(t->srv_fd, StaticWriteEvent);
2885 tv_eternity(&t->swexpire);
2886 shutdown(t->srv_fd, SHUT_WR);
2887 t->srv_state = SV_STSHUTW;
2888 return 1;
2889 }
2890
2891 if (req->l == 0) {
2892 if (FD_ISSET(t->srv_fd, StaticWriteEvent)) {
2893 FD_CLR(t->srv_fd, StaticWriteEvent); /* stop writing */
2894 tv_eternity(&t->swexpire);
2895 }
2896 }
2897 else { /* client buffer not empty */
2898 if (! FD_ISSET(t->srv_fd, StaticWriteEvent)) {
2899 FD_SET(t->srv_fd, StaticWriteEvent); /* restart writing */
2900 if (t->proxy->srvtimeout)
2901 tv_delayfrom(&t->swexpire, &now, t->proxy->srvtimeout);
2902 else
2903 tv_eternity(&t->swexpire);
2904 }
2905 }
2906
willy tarreau5cbea6f2005-12-17 12:48:26 +01002907 /* be nice with the client side which would like to send a complete header
2908 * FIXME: COMPLETELY BUGGY !!! not all headers may be processed because the client
2909 * would read all remaining data at once ! The client should not write past rep->lr
2910 * when the server is in header state.
2911 */
2912 //return header_processed;
2913 return t->srv_state != SV_STHEADERS;
willy tarreau0f7af912005-12-17 12:21:26 +01002914 }
2915 else if (s == SV_STDATA) {
2916 /* read or write error */
2917 if (t->res_sw == RES_ERROR || t->res_sr == RES_ERROR) {
willy tarreau0f7af912005-12-17 12:21:26 +01002918 tv_eternity(&t->srexpire);
2919 tv_eternity(&t->swexpire);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002920 fd_delete(t->srv_fd);
willy tarreau0f7af912005-12-17 12:21:26 +01002921 t->srv_state = SV_STCLOSE;
2922 return 1;
2923 }
2924 /* read timeout, last read, or end of client write */
willy tarreauef900ab2005-12-17 12:52:52 +01002925 else if (t->res_sr == RES_NULL || c == CL_STSHUTW || c == CL_STCLOSE
2926 || tv_cmp2_ms(&t->srexpire, &now) <= 0) {
willy tarreau0f7af912005-12-17 12:21:26 +01002927 FD_CLR(t->srv_fd, StaticReadEvent);
2928 tv_eternity(&t->srexpire);
2929 shutdown(t->srv_fd, SHUT_RD);
2930 t->srv_state = SV_STSHUTR;
2931 return 1;
2932
2933 }
2934 /* write timeout, or last client read and buffer empty */
willy tarreauef900ab2005-12-17 12:52:52 +01002935 else if (((c == CL_STSHUTR || c == CL_STCLOSE) && (req->l == 0))
2936 || (tv_cmp2_ms(&t->swexpire, &now) <= 0)) {
willy tarreau0f7af912005-12-17 12:21:26 +01002937 FD_CLR(t->srv_fd, StaticWriteEvent);
2938 tv_eternity(&t->swexpire);
2939 shutdown(t->srv_fd, SHUT_WR);
2940 t->srv_state = SV_STSHUTW;
2941 return 1;
2942 }
2943 else if (req->l == 0) {
2944 if (FD_ISSET(t->srv_fd, StaticWriteEvent)) {
2945 FD_CLR(t->srv_fd, StaticWriteEvent); /* stop writing */
2946 tv_eternity(&t->swexpire);
2947 }
2948 }
2949 else { /* buffer not empty */
2950 if (! FD_ISSET(t->srv_fd, StaticWriteEvent)) {
2951 FD_SET(t->srv_fd, StaticWriteEvent); /* restart writing */
2952 if (t->proxy->srvtimeout)
2953 tv_delayfrom(&t->swexpire, &now, t->proxy->srvtimeout);
2954 else
2955 tv_eternity(&t->swexpire);
2956 }
2957 }
2958
2959 if (rep->l == BUFSIZE) { /* no room to read more data */
2960 if (FD_ISSET(t->srv_fd, StaticReadEvent)) {
2961 FD_CLR(t->srv_fd, StaticReadEvent);
2962 tv_eternity(&t->srexpire);
2963 }
2964 }
2965 else {
2966 if (! FD_ISSET(t->srv_fd, StaticReadEvent)) {
2967 FD_SET(t->srv_fd, StaticReadEvent);
2968 if (t->proxy->srvtimeout)
2969 tv_delayfrom(&t->srexpire, &now, t->proxy->srvtimeout);
2970 else
2971 tv_eternity(&t->srexpire);
2972 }
2973 }
2974
2975 return 0; /* other cases change nothing */
2976 }
2977 else if (s == SV_STSHUTR) {
2978 if ((t->res_sw == RES_ERROR) ||
2979 ((c == CL_STSHUTR || c == CL_STCLOSE) && (req->l == 0)) ||
2980 (tv_cmp2_ms(&t->swexpire, &now) <= 0)) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01002981 //FD_CLR(t->srv_fd, StaticWriteEvent);
willy tarreau0f7af912005-12-17 12:21:26 +01002982 tv_eternity(&t->swexpire);
2983 fd_delete(t->srv_fd);
willy tarreau5cbea6f2005-12-17 12:48:26 +01002984 //close(t->srv_fd);
willy tarreau0f7af912005-12-17 12:21:26 +01002985 t->srv_state = SV_STCLOSE;
2986 return 1;
2987 }
2988 else if (req->l == 0) {
2989 if (FD_ISSET(t->srv_fd, StaticWriteEvent)) {
2990 FD_CLR(t->srv_fd, StaticWriteEvent); /* stop writing */
2991 tv_eternity(&t->swexpire);
2992 }
2993 }
2994 else { /* buffer not empty */
2995 if (! FD_ISSET(t->srv_fd, StaticWriteEvent)) {
2996 FD_SET(t->srv_fd, StaticWriteEvent); /* restart writing */
2997 if (t->proxy->srvtimeout)
2998 tv_delayfrom(&t->swexpire, &now, t->proxy->srvtimeout);
2999 else
3000 tv_eternity(&t->swexpire);
3001 }
3002 }
3003 return 0;
3004 }
3005 else if (s == SV_STSHUTW) {
3006 if (t->res_sr == RES_ERROR || t->res_sr == RES_NULL ||
3007 c == CL_STSHUTW || c == CL_STCLOSE ||
3008 tv_cmp2_ms(&t->srexpire, &now) <= 0) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01003009 //FD_CLR(t->srv_fd, StaticReadEvent);
willy tarreau0f7af912005-12-17 12:21:26 +01003010 tv_eternity(&t->srexpire);
3011 fd_delete(t->srv_fd);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003012 //close(t->srv_fd);
willy tarreau0f7af912005-12-17 12:21:26 +01003013 t->srv_state = SV_STCLOSE;
3014 return 1;
3015 }
3016 else if (rep->l == BUFSIZE) { /* no room to read more data */
3017 if (FD_ISSET(t->srv_fd, StaticReadEvent)) {
3018 FD_CLR(t->srv_fd, StaticReadEvent);
3019 tv_eternity(&t->srexpire);
3020 }
3021 }
3022 else {
3023 if (! FD_ISSET(t->srv_fd, StaticReadEvent)) {
3024 FD_SET(t->srv_fd, StaticReadEvent);
3025 if (t->proxy->srvtimeout)
3026 tv_delayfrom(&t->srexpire, &now, t->proxy->srvtimeout);
3027 else
3028 tv_eternity(&t->srexpire);
3029 }
3030 }
3031 return 0;
3032 }
3033 else { /* SV_STCLOSE : nothing to do */
willy tarreau9fe663a2005-12-17 13:02:59 +01003034 if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
willy tarreau0f7af912005-12-17 12:21:26 +01003035 int len;
willy tarreau5cbea6f2005-12-17 12:48:26 +01003036 len = sprintf(trash, "srvcls[%04x:%04x]\n", (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
willy tarreau0f7af912005-12-17 12:21:26 +01003037 write(1, trash, len);
3038 }
3039 return 0;
3040 }
3041 return 0;
3042}
3043
3044
willy tarreau5cbea6f2005-12-17 12:48:26 +01003045/* Processes the client and server jobs of a session task, then
3046 * puts it back to the wait queue in a clean state, or
3047 * cleans up its resources if it must be deleted. Returns
3048 * the time the task accepts to wait, or -1 for infinity
willy tarreau0f7af912005-12-17 12:21:26 +01003049 */
willy tarreau5cbea6f2005-12-17 12:48:26 +01003050int process_session(struct task *t) {
3051 struct session *s = t->context;
3052 int fsm_resync = 0;
willy tarreau0f7af912005-12-17 12:21:26 +01003053
willy tarreau5cbea6f2005-12-17 12:48:26 +01003054 do {
3055 fsm_resync = 0;
3056 //fprintf(stderr,"before_cli:cli=%d, srv=%d\n", t->cli_state, t->srv_state);
3057 fsm_resync |= process_cli(s);
3058 //fprintf(stderr,"cli/srv:cli=%d, srv=%d\n", t->cli_state, t->srv_state);
3059 fsm_resync |= process_srv(s);
3060 //fprintf(stderr,"after_srv:cli=%d, srv=%d\n", t->cli_state, t->srv_state);
3061 } while (fsm_resync);
3062
3063 if (s->cli_state != CL_STCLOSE || s->srv_state != SV_STCLOSE) {
willy tarreau0f7af912005-12-17 12:21:26 +01003064 struct timeval min1, min2;
willy tarreau5cbea6f2005-12-17 12:48:26 +01003065 s->res_cw = s->res_cr = s->res_sw = s->res_sr = RES_SILENT;
willy tarreau0f7af912005-12-17 12:21:26 +01003066
willy tarreau5cbea6f2005-12-17 12:48:26 +01003067 tv_min(&min1, &s->crexpire, &s->cwexpire);
3068 tv_min(&min2, &s->srexpire, &s->swexpire);
3069 tv_min(&min1, &min1, &s->cnexpire);
willy tarreau0f7af912005-12-17 12:21:26 +01003070 tv_min(&t->expire, &min1, &min2);
3071
3072 /* restore t to its place in the task list */
willy tarreau5cbea6f2005-12-17 12:48:26 +01003073 task_queue(t);
willy tarreau0f7af912005-12-17 12:21:26 +01003074
willy tarreau5cbea6f2005-12-17 12:48:26 +01003075 return tv_remain(&now, &t->expire); /* nothing more to do */
willy tarreau0f7af912005-12-17 12:21:26 +01003076 }
3077
willy tarreau5cbea6f2005-12-17 12:48:26 +01003078 s->proxy->nbconn--;
willy tarreau0f7af912005-12-17 12:21:26 +01003079 actconn--;
3080
willy tarreau9fe663a2005-12-17 13:02:59 +01003081 if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
willy tarreau0f7af912005-12-17 12:21:26 +01003082 int len;
willy tarreau5cbea6f2005-12-17 12:48:26 +01003083 len = sprintf(trash, "closed[%04x:%04x]\n", (unsigned short)s->cli_fd, (unsigned short)s->srv_fd);
willy tarreau0f7af912005-12-17 12:21:26 +01003084 write(1, trash, len);
3085 }
3086
willy tarreaua1598082005-12-17 13:08:06 +01003087 s->logs.t_close = tv_delta(&s->logs.tv_accept, &now);
3088 if (s->rep != NULL)
3089 s->logs.bytes = s->rep->total;
3090
willy tarreau9fe663a2005-12-17 13:02:59 +01003091 /* let's do a final log if we need it */
willy tarreaua1598082005-12-17 13:08:06 +01003092 if (s->logs.logwait && (!(s->proxy->options & PR_O_NULLNOLOG) || s->req->total))
willy tarreau9fe663a2005-12-17 13:02:59 +01003093 sess_log(s);
3094
willy tarreau0f7af912005-12-17 12:21:26 +01003095 /* the task MUST not be in the run queue anymore */
3096 task_delete(t);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003097 session_free(s);
willy tarreau0f7af912005-12-17 12:21:26 +01003098 task_free(t);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003099 return -1; /* rest in peace for eternity */
3100}
3101
3102
3103
3104/*
3105 * manages a server health-check. Returns
3106 * the time the task accepts to wait, or -1 for infinity.
3107 */
3108int process_chk(struct task *t) {
3109 struct server *s = t->context;
3110 int fd = s->curfd;
3111 int one = 1;
3112
willy tarreauef900ab2005-12-17 12:52:52 +01003113 //fprintf(stderr, "process_chk: task=%p\n", t);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003114
3115 if (fd < 0) { /* no check currently running */
3116 //fprintf(stderr, "process_chk: 2\n");
3117 if (tv_cmp2_ms(&t->expire, &now) > 0) { /* not good time yet */
3118 task_queue(t); /* restore t to its place in the task list */
3119 return tv_remain(&now, &t->expire);
3120 }
3121
3122 /* we'll initiate a new check */
3123 s->result = 0; /* no result yet */
3124 if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) {
willy tarreau9fe663a2005-12-17 13:02:59 +01003125 if ((fd < global.maxsock) &&
willy tarreau5cbea6f2005-12-17 12:48:26 +01003126 (fcntl(fd, F_SETFL, O_NONBLOCK) != -1) &&
3127 (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != -1)) {
3128 //fprintf(stderr, "process_chk: 3\n");
3129
3130 if ((connect(fd, (struct sockaddr *)&s->addr, sizeof(s->addr)) != -1) || (errno == EINPROGRESS)) {
3131 /* OK, connection in progress or established */
3132
3133 //fprintf(stderr, "process_chk: 4\n");
3134
3135 s->curfd = fd; /* that's how we know a test is in progress ;-) */
3136 fdtab[fd].owner = t;
3137 fdtab[fd].read = NULL;
3138 fdtab[fd].write = &event_srv_hck;
3139 fdtab[fd].state = FD_STCONN; /* connection in progress */
3140 FD_SET(fd, StaticWriteEvent); /* for connect status */
3141 fd_insert(fd);
willy tarreaue47c8d72005-12-17 12:55:52 +01003142 /* FIXME: we allow up to <inter> for a connection to establish, but we should use another parameter */
3143 tv_delayfrom(&t->expire, &now, s->inter);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003144 task_queue(t); /* restore t to its place in the task list */
3145 return tv_remain(&now, &t->expire);
3146 }
3147 else if (errno != EALREADY && errno != EISCONN && errno != EAGAIN) {
3148 s->result = -1; /* a real error */
3149 }
3150 }
3151 //fprintf(stderr, "process_chk: 5\n");
3152 close(fd);
3153 }
3154
3155 if (!s->result) { /* nothing done */
3156 //fprintf(stderr, "process_chk: 6\n");
willy tarreaue47c8d72005-12-17 12:55:52 +01003157 tv_delayfrom(&t->expire, &now, s->inter);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003158 task_queue(t); /* restore t to its place in the task list */
3159 return tv_remain(&now, &t->expire);
3160 }
3161
3162 /* here, we have seen a failure */
willy tarreaue47c8d72005-12-17 12:55:52 +01003163 if (s->health > s->rise)
willy tarreau5cbea6f2005-12-17 12:48:26 +01003164 s->health--; /* still good */
3165 else {
willy tarreau535ae7a2005-12-17 12:58:00 +01003166 if (s->health == s->rise) {
willy tarreau9fe663a2005-12-17 13:02:59 +01003167 if (!(global.mode & MODE_QUIET))
willy tarreau535ae7a2005-12-17 12:58:00 +01003168 Warning("server %s DOWN.\n", s->id);
3169
willy tarreau9fe663a2005-12-17 13:02:59 +01003170// sprintf(trash, "Server %s/%s is DOWN.\n",
3171// s->proxy->id, s->id);
3172//
3173// if (s->proxy->logfac1 >= 0)
3174// send_syslog(&s->proxy->logsrv1, s->proxy->logfac1, LOG_ALERT, trash);
3175// if (s->proxy->logfac2 >= 0)
3176// send_syslog(&s->proxy->logsrv2, s->proxy->logfac2, LOG_ALERT, trash);
3177 send_log(s->proxy, LOG_ALERT, "Server %s/%s is DOWN.\n", s->proxy->id, s->id);
willy tarreau535ae7a2005-12-17 12:58:00 +01003178 }
willy tarreauef900ab2005-12-17 12:52:52 +01003179
willy tarreau5cbea6f2005-12-17 12:48:26 +01003180 s->health = 0; /* failure */
3181 s->state &= ~SRV_RUNNING;
3182 }
3183
3184 //fprintf(stderr, "process_chk: 7\n");
willy tarreaue47c8d72005-12-17 12:55:52 +01003185 /* FIXME: we allow up to <inter> for a connection to establish, but we should use another parameter */
3186 tv_delayfrom(&t->expire, &now, s->inter);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003187 }
3188 else {
3189 //fprintf(stderr, "process_chk: 8\n");
3190 /* there was a test running */
3191 if (s->result > 0) { /* good server detected */
3192 //fprintf(stderr, "process_chk: 9\n");
3193 s->health++; /* was bad, stays for a while */
willy tarreaue47c8d72005-12-17 12:55:52 +01003194 if (s->health >= s->rise) {
willy tarreau535ae7a2005-12-17 12:58:00 +01003195 if (s->health == s->rise) {
willy tarreau9fe663a2005-12-17 13:02:59 +01003196 if (!(global.mode & MODE_QUIET))
willy tarreau535ae7a2005-12-17 12:58:00 +01003197 Warning("server %s UP.\n", s->id);
willy tarreau9fe663a2005-12-17 13:02:59 +01003198// sprintf(trash, "Server %s/%s is UP.\n", s->proxy->id, s->id);
willy tarreau535ae7a2005-12-17 12:58:00 +01003199
willy tarreau9fe663a2005-12-17 13:02:59 +01003200// if (s->proxy->logfac1 >= 0)
3201// send_syslog(&s->proxy->logsrv1, s->proxy->logfac1, LOG_NOTICE, trash);
3202// if (s->proxy->logfac2 >= 0)
3203// send_syslog(&s->proxy->logsrv2, s->proxy->logfac2, LOG_NOTICE, trash);
3204 send_log(s->proxy, LOG_NOTICE, "Server %s/%s is UP.\n", s->proxy->id, s->id);
willy tarreau535ae7a2005-12-17 12:58:00 +01003205 }
willy tarreauef900ab2005-12-17 12:52:52 +01003206
willy tarreaue47c8d72005-12-17 12:55:52 +01003207 s->health = s->rise + s->fall - 1; /* OK now */
willy tarreau5cbea6f2005-12-17 12:48:26 +01003208 s->state |= SRV_RUNNING;
3209 }
willy tarreauef900ab2005-12-17 12:52:52 +01003210 s->curfd = -1; /* no check running anymore */
3211 //FD_CLR(fd, StaticWriteEvent);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003212 fd_delete(fd);
willy tarreaue47c8d72005-12-17 12:55:52 +01003213 tv_delayfrom(&t->expire, &now, s->inter);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003214 }
3215 else if (s->result < 0 || tv_cmp2_ms(&t->expire, &now) <= 0) {
3216 //fprintf(stderr, "process_chk: 10\n");
3217 /* failure or timeout detected */
willy tarreaue47c8d72005-12-17 12:55:52 +01003218 if (s->health > s->rise)
willy tarreau5cbea6f2005-12-17 12:48:26 +01003219 s->health--; /* still good */
3220 else {
willy tarreau535ae7a2005-12-17 12:58:00 +01003221 if (s->health == s->rise) {
willy tarreau9fe663a2005-12-17 13:02:59 +01003222 if (!(global.mode & MODE_QUIET))
willy tarreau535ae7a2005-12-17 12:58:00 +01003223 Warning("server %s DOWN.\n", s->id);
willy tarreau9fe663a2005-12-17 13:02:59 +01003224// sprintf(trash, "Server %s/%s is DOWN.\n",
3225// s->proxy->id, s->id);
3226//
3227// if (s->proxy->logfac1 >= 0)
3228// send_syslog(&s->proxy->logsrv1, s->proxy->logfac1, LOG_ALERT, trash);
3229// if (s->proxy->logfac2 >= 0)
3230// send_syslog(&s->proxy->logsrv2, s->proxy->logfac2, LOG_ALERT, trash);
3231
3232 send_log(s->proxy, LOG_ALERT, "Server %s/%s is DOWN.\n", s->proxy->id, s->id);
willy tarreau535ae7a2005-12-17 12:58:00 +01003233 }
willy tarreauef900ab2005-12-17 12:52:52 +01003234
willy tarreau5cbea6f2005-12-17 12:48:26 +01003235 s->health = 0; /* failure */
3236 s->state &= ~SRV_RUNNING;
3237 }
3238 s->curfd = -1;
willy tarreauef900ab2005-12-17 12:52:52 +01003239 //FD_CLR(fd, StaticWriteEvent);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003240 fd_delete(fd);
willy tarreaue47c8d72005-12-17 12:55:52 +01003241 tv_delayfrom(&t->expire, &now, s->inter);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003242 }
3243 /* if result is 0 and there's no timeout, we have to wait again */
3244 }
3245 //fprintf(stderr, "process_chk: 11\n");
3246 s->result = 0;
3247 task_queue(t); /* restore t to its place in the task list */
3248 return tv_remain(&now, &t->expire);
willy tarreau0f7af912005-12-17 12:21:26 +01003249}
3250
3251
willy tarreau5cbea6f2005-12-17 12:48:26 +01003252
willy tarreau0f7af912005-12-17 12:21:26 +01003253#if STATTIME > 0
3254int stats(void);
3255#endif
3256
3257/*
3258 * Main select() loop.
3259 */
3260
3261void select_loop() {
3262 int next_time;
willy tarreau0f7af912005-12-17 12:21:26 +01003263 int time2;
willy tarreau0f7af912005-12-17 12:21:26 +01003264 int status;
3265 int fd,i;
3266 struct timeval delta;
3267 int readnotnull, writenotnull;
willy tarreau5cbea6f2005-12-17 12:48:26 +01003268 struct task *t, *tnext;
willy tarreau0f7af912005-12-17 12:21:26 +01003269
willy tarreau5cbea6f2005-12-17 12:48:26 +01003270 tv_now(&now);
3271
3272 while (1) {
3273 next_time = -1; /* set the timer to wait eternally first */
willy tarreau0f7af912005-12-17 12:21:26 +01003274
willy tarreau5cbea6f2005-12-17 12:48:26 +01003275 /* look for expired tasks and add them to the run queue.
3276 */
3277 tnext = ((struct task *)LIST_HEAD(wait_queue))->next;
3278 while ((t = tnext) != LIST_HEAD(wait_queue)) { /* we haven't looped ? */
3279 tnext = t->next;
willy tarreauef900ab2005-12-17 12:52:52 +01003280 if (t->state & TASK_RUNNING)
3281 continue;
willy tarreau5cbea6f2005-12-17 12:48:26 +01003282
3283 /* wakeup expired entries. It doesn't matter if they are
3284 * already running because of a previous event
3285 */
3286 if (tv_cmp2_ms(&t->expire, &now) <= 0) {
willy tarreauef900ab2005-12-17 12:52:52 +01003287 //fprintf(stderr,"task_wakeup(%p, %p)\n", &rq, t);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003288 task_wakeup(&rq, t);
3289 }
3290 else {
willy tarreauef900ab2005-12-17 12:52:52 +01003291 /* first non-runnable task. Use its expiration date as an upper bound */
3292 int temp_time = tv_remain(&now, &t->expire);
3293 if (temp_time)
3294 next_time = temp_time;
3295 //fprintf(stderr,"no_task_wakeup(%p, %p) : expire in %d ms\n", &rq, t, temp_time);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003296 break;
3297 }
3298 }
3299
3300 /* process each task in the run queue now. Each task may be deleted
3301 * since we only use tnext.
3302 */
3303 tnext = rq;
3304 while ((t = tnext) != NULL) {
3305 int temp_time;
3306
3307 tnext = t->rqnext;
3308 task_sleep(&rq, t);
willy tarreauef900ab2005-12-17 12:52:52 +01003309 //fprintf(stderr,"task %p\n",t);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003310 temp_time = t->process(t);
3311 next_time = MINTIME(temp_time, next_time);
willy tarreauef900ab2005-12-17 12:52:52 +01003312 //fprintf(stderr,"process(%p)=%d -> next_time=%d)\n", t, temp_time, next_time);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003313 }
3314
willy tarreauef900ab2005-12-17 12:52:52 +01003315 //fprintf(stderr,"---end of run---\n");
willy tarreau5cbea6f2005-12-17 12:48:26 +01003316
3317 /* maintain all proxies in a consistent state. This should quickly become a task */
3318 time2 = maintain_proxies();
3319 next_time = MINTIME(time2, next_time);
3320
3321 /* stop when there's no connection left and we don't allow them anymore */
3322 if (!actconn && listeners == 0)
3323 break;
3324
willy tarreau0f7af912005-12-17 12:21:26 +01003325
3326#if STATTIME > 0
3327 time2 = stats();
3328 // fprintf(stderr," stats = %d\n", time2);
3329 next_time = MINTIME(time2, next_time);
3330#endif
3331
willy tarreau5cbea6f2005-12-17 12:48:26 +01003332 if (next_time > 0) { /* FIXME */
willy tarreau0f7af912005-12-17 12:21:26 +01003333 /* Convert to timeval */
willy tarreau5cbea6f2005-12-17 12:48:26 +01003334 /* to avoid eventual select loops due to timer precision */
3335 next_time += SCHEDULER_RESOLUTION;
3336 delta.tv_sec = next_time / 1000;
3337 delta.tv_usec = (next_time % 1000) * 1000;
3338 }
3339 else if (next_time == 0) { /* allow select to return immediately when needed */
3340 delta.tv_sec = delta.tv_usec = 0;
willy tarreau0f7af912005-12-17 12:21:26 +01003341 }
3342
3343
3344 /* let's restore fdset state */
3345
3346 readnotnull = 0; writenotnull = 0;
willy tarreau9fe663a2005-12-17 13:02:59 +01003347 for (i = 0; i < (global.maxsock + FD_SETSIZE - 1)/(8*sizeof(int)); i++) {
willy tarreau0f7af912005-12-17 12:21:26 +01003348 readnotnull |= (*(((int*)ReadEvent)+i) = *(((int*)StaticReadEvent)+i)) != 0;
3349 writenotnull |= (*(((int*)WriteEvent)+i) = *(((int*)StaticWriteEvent)+i)) != 0;
3350 }
3351
3352// /* just a verification code, needs to be removed for performance */
3353// for (i=0; i<maxfd; i++) {
3354// if (FD_ISSET(i, ReadEvent) != FD_ISSET(i, StaticReadEvent))
3355// abort();
3356// if (FD_ISSET(i, WriteEvent) != FD_ISSET(i, StaticWriteEvent))
3357// abort();
3358//
3359// }
3360
3361 status=select(maxfd,
3362 readnotnull ? ReadEvent : NULL,
3363 writenotnull ? WriteEvent : NULL,
3364 NULL,
3365 (next_time >= 0) ? &delta : NULL);
3366
willy tarreau5cbea6f2005-12-17 12:48:26 +01003367 /* this is an experiment on the separation of the select work */
3368 // status = (readnotnull ? select(maxfd, ReadEvent, NULL, NULL, (next_time >= 0) ? &delta : NULL) : 0);
3369 // status |= (writenotnull ? select(maxfd, NULL, WriteEvent, NULL, (next_time >= 0) ? &delta : NULL) : 0);
3370
willy tarreau0f7af912005-12-17 12:21:26 +01003371 tv_now(&now);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003372
willy tarreau0f7af912005-12-17 12:21:26 +01003373 if (status > 0) { /* must proceed with events */
3374
3375 int fds;
3376 char count;
3377
3378 for (fds = 0; (fds << INTBITS) < maxfd; fds++)
3379 if ((((int *)(ReadEvent))[fds] | ((int *)(WriteEvent))[fds]) != 0)
3380 for (count = 1<<INTBITS, fd = fds << INTBITS; count && fd < maxfd; count--, fd++) {
3381
willy tarreau5cbea6f2005-12-17 12:48:26 +01003382 /* if we specify read first, the accepts and zero reads will be
3383 * seen first. Moreover, system buffers will be flushed faster.
3384 */
willy tarreau0f7af912005-12-17 12:21:26 +01003385 if (fdtab[fd].state == FD_STCLOSE)
3386 continue;
willy tarreau0f7af912005-12-17 12:21:26 +01003387
3388 if (FD_ISSET(fd, ReadEvent))
3389 fdtab[fd].read(fd);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003390
willy tarreau5cbea6f2005-12-17 12:48:26 +01003391 if (FD_ISSET(fd, WriteEvent))
3392 fdtab[fd].write(fd);
willy tarreau0f7af912005-12-17 12:21:26 +01003393 }
3394 }
3395 else {
3396 // fprintf(stderr,"select returned %d, maxfd=%d\n", status, maxfd);
3397 }
willy tarreau0f7af912005-12-17 12:21:26 +01003398 }
3399}
3400
3401
3402#if STATTIME > 0
3403/*
3404 * Display proxy statistics regularly. It is designed to be called from the
3405 * select_loop().
3406 */
3407int stats(void) {
3408 static int lines;
3409 static struct timeval nextevt;
3410 static struct timeval lastevt;
3411 static struct timeval starttime = {0,0};
3412 unsigned long totaltime, deltatime;
3413 int ret;
3414
3415 if (tv_remain(&now, &nextevt) == 0) {
3416 deltatime = (tv_delta(&now, &lastevt)?:1);
3417 totaltime = (tv_delta(&now, &starttime)?:1);
3418
willy tarreau9fe663a2005-12-17 13:02:59 +01003419 if (global.mode & MODE_STATS) {
3420 if ((lines++ % 16 == 0) && !(global.mode & MODE_LOG))
willy tarreau5cbea6f2005-12-17 12:48:26 +01003421 qfprintf(stderr,
willy tarreau0f7af912005-12-17 12:21:26 +01003422 "\n active total tsknew tskgood tskleft tskrght tsknsch tsklsch tskrsch\n");
3423 if (lines>1) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01003424 qfprintf(stderr,"%07d %07d %07d %07d %07d %07d %07d %07d %07d\n",
willy tarreau0f7af912005-12-17 12:21:26 +01003425 actconn, totalconn,
3426 stats_tsk_new, stats_tsk_good,
3427 stats_tsk_left, stats_tsk_right,
3428 stats_tsk_nsrch, stats_tsk_lsrch, stats_tsk_rsrch);
3429 }
3430 }
3431
3432 tv_delayfrom(&nextevt, &now, STATTIME);
3433
3434 lastevt=now;
3435 }
3436 ret = tv_remain(&now, &nextevt);
3437 return ret;
3438}
3439#endif
3440
3441
3442/*
3443 * this function enables proxies when there are enough free sessions,
3444 * or stops them when the table is full. It is designed to be called from the
willy tarreau5cbea6f2005-12-17 12:48:26 +01003445 * select_loop(). It returns the time left before next expiration event
3446 * during stop time, -1 otherwise.
willy tarreau0f7af912005-12-17 12:21:26 +01003447 */
3448static int maintain_proxies(void) {
3449 struct proxy *p;
willy tarreau5cbea6f2005-12-17 12:48:26 +01003450 int tleft; /* time left */
willy tarreau0f7af912005-12-17 12:21:26 +01003451
3452 p = proxy;
willy tarreau5cbea6f2005-12-17 12:48:26 +01003453 tleft = -1; /* infinite time */
willy tarreau0f7af912005-12-17 12:21:26 +01003454
3455 /* if there are enough free sessions, we'll activate proxies */
willy tarreau9fe663a2005-12-17 13:02:59 +01003456 if (actconn < global.maxconn) {
willy tarreau0f7af912005-12-17 12:21:26 +01003457 while (p) {
3458 if (p->nbconn < p->maxconn) {
3459 if (p->state == PR_STIDLE) {
3460 FD_SET(p->listen_fd, StaticReadEvent);
3461 p->state = PR_STRUN;
3462 }
3463 }
3464 else {
3465 if (p->state == PR_STRUN) {
3466 FD_CLR(p->listen_fd, StaticReadEvent);
3467 p->state = PR_STIDLE;
3468 }
3469 }
3470 p = p->next;
3471 }
3472 }
3473 else { /* block all proxies */
3474 while (p) {
3475 if (p->state == PR_STRUN) {
3476 FD_CLR(p->listen_fd, StaticReadEvent);
3477 p->state = PR_STIDLE;
3478 }
3479 p = p->next;
3480 }
3481 }
3482
willy tarreau5cbea6f2005-12-17 12:48:26 +01003483 if (stopping) {
3484 p = proxy;
3485 while (p) {
3486 if (p->state != PR_STDISABLED) {
3487 int t;
3488 t = tv_remain(&now, &p->stop_time);
3489 if (t == 0) {
willy tarreau535ae7a2005-12-17 12:58:00 +01003490 Warning("Proxy %s stopped.\n", p->id);
willy tarreau9fe663a2005-12-17 13:02:59 +01003491// sprintf(trash, "Proxy %s stopped.\n", p->id);
willy tarreau535ae7a2005-12-17 12:58:00 +01003492
willy tarreau9fe663a2005-12-17 13:02:59 +01003493// if (p->logfac1 >= 0)
3494// send_syslog(&p->logsrv1, p->logfac1, LOG_WARNING, trash);
3495// if (p->logfac2 >= 0)
3496// send_syslog(&p->logsrv2, p->logfac2, LOG_WARNING, trash);
3497 send_log(p, LOG_WARNING, "Proxy %s stopped.\n", p->id);
willy tarreau535ae7a2005-12-17 12:58:00 +01003498
willy tarreau5cbea6f2005-12-17 12:48:26 +01003499 fd_delete(p->listen_fd);
3500 p->state = PR_STDISABLED;
3501 listeners--;
3502 }
3503 else {
3504 tleft = MINTIME(t, tleft);
3505 }
3506 }
3507 p = p->next;
3508 }
3509 }
3510 return tleft;
willy tarreau0f7af912005-12-17 12:21:26 +01003511}
3512
3513/*
3514 * this function disables health-check servers so that the process will quickly be ignored
3515 * by load balancers.
3516 */
3517static void soft_stop(void) {
3518 struct proxy *p;
3519
3520 stopping = 1;
3521 p = proxy;
willy tarreau5cbea6f2005-12-17 12:48:26 +01003522 tv_now(&now); /* else, the old time before select will be used */
willy tarreau0f7af912005-12-17 12:21:26 +01003523 while (p) {
willy tarreau535ae7a2005-12-17 12:58:00 +01003524 if (p->state != PR_STDISABLED) {
3525 Warning("Stopping proxy %s in %d ms.\n", p->id, p->grace);
willy tarreau9fe663a2005-12-17 13:02:59 +01003526// sprintf(trash, "Stopping proxy %s in %d ms.\n", p->id, p->grace);
willy tarreau535ae7a2005-12-17 12:58:00 +01003527
willy tarreau9fe663a2005-12-17 13:02:59 +01003528// if (p->logfac1 >= 0)
3529// send_syslog(&p->logsrv1, p->logfac1, LOG_WARNING, trash);
3530// if (p->logfac2 >= 0)
3531// send_syslog(&p->logsrv2, p->logfac2, LOG_WARNING, trash);
3532
3533 send_log(p, LOG_WARNING, "Stopping proxy %s in %d ms.\n", p->id, p->grace);
willy tarreau0f7af912005-12-17 12:21:26 +01003534 tv_delayfrom(&p->stop_time, &now, p->grace);
willy tarreau535ae7a2005-12-17 12:58:00 +01003535 }
willy tarreau0f7af912005-12-17 12:21:26 +01003536 p = p->next;
3537 }
3538}
3539
3540/*
3541 * upon SIGUSR1, let's have a soft stop.
3542 */
3543void sig_soft_stop(int sig) {
3544 soft_stop();
3545 signal(sig, SIG_IGN);
3546}
3547
3548
3549void dump(int sig) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01003550 struct task *t, *tnext;
3551 struct session *s;
willy tarreau0f7af912005-12-17 12:21:26 +01003552
willy tarreau5cbea6f2005-12-17 12:48:26 +01003553 tnext = ((struct task *)LIST_HEAD(wait_queue))->next;
3554 while ((t = tnext) != LIST_HEAD(wait_queue)) { /* we haven't looped ? */
3555 tnext = t->next;
3556 s = t->context;
3557 qfprintf(stderr,"[dump] wq: task %p, still %ld ms, "
3558 "cli=%d, srv=%d, cr=%d, cw=%d, sr=%d, sw=%d, "
3559 "req=%d, rep=%d, clifd=%d\n",
3560 s, tv_remain(&now, &t->expire),
3561 s->cli_state,
3562 s->srv_state,
3563 FD_ISSET(s->cli_fd, StaticReadEvent),
3564 FD_ISSET(s->cli_fd, StaticWriteEvent),
3565 FD_ISSET(s->srv_fd, StaticReadEvent),
3566 FD_ISSET(s->srv_fd, StaticWriteEvent),
3567 s->req->l, s->rep?s->rep->l:0, s->cli_fd
3568 );
willy tarreau0f7af912005-12-17 12:21:26 +01003569 }
3570}
3571
willy tarreaue39cd132005-12-17 13:00:18 +01003572void chain_regex(struct hdr_exp **head, regex_t *preg, int action, char *replace) {
3573 struct hdr_exp *exp;
3574
3575 while (*head != NULL)
3576 head = &(*head)->next;
3577
3578 exp = calloc(1, sizeof(struct hdr_exp));
3579
3580 exp->preg = preg;
3581 exp->replace = replace;
3582 exp->action = action;
3583 *head = exp;
3584}
3585
willy tarreau9fe663a2005-12-17 13:02:59 +01003586
willy tarreau0f7af912005-12-17 12:21:26 +01003587/*
willy tarreau9fe663a2005-12-17 13:02:59 +01003588 * parse a line in a <global> section. Returns 0 if OK, -1 if error.
willy tarreau0f7af912005-12-17 12:21:26 +01003589 */
willy tarreau9fe663a2005-12-17 13:02:59 +01003590int cfg_parse_global(char *file, int linenum, char **args) {
willy tarreau0f7af912005-12-17 12:21:26 +01003591
willy tarreau9fe663a2005-12-17 13:02:59 +01003592 if (!strcmp(args[0], "global")) { /* new section */
3593 /* no option, nothing special to do */
3594 return 0;
3595 }
3596 else if (!strcmp(args[0], "daemon")) {
3597 global.mode |= MODE_DAEMON;
3598 }
3599 else if (!strcmp(args[0], "debug")) {
3600 global.mode |= MODE_DEBUG;
3601 }
3602 else if (!strcmp(args[0], "quiet")) {
3603 global.mode |= MODE_QUIET;
3604 }
3605 else if (!strcmp(args[0], "stats")) {
3606 global.mode |= MODE_STATS;
3607 }
3608 else if (!strcmp(args[0], "uid")) {
3609 if (global.uid != 0) {
3610 Alert("parsing [%s:%d] : <uid> already specified. Continuing.\n", file, linenum);
3611 return 0;
willy tarreau0f7af912005-12-17 12:21:26 +01003612 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003613 if (*(args[1]) == 0) {
3614 Alert("parsing [%s:%d] : <uid> expects an integer argument.\n", file, linenum);
3615 return -1;
willy tarreau5cbea6f2005-12-17 12:48:26 +01003616 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003617 global.uid = atol(args[1]);
3618 }
3619 else if (!strcmp(args[0], "gid")) {
3620 if (global.gid != 0) {
3621 Alert("parsing [%s:%d] : <gid> already specified. Continuing.\n", file, linenum);
3622 return 0;
willy tarreau0f7af912005-12-17 12:21:26 +01003623 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003624 if (*(args[1]) == 0) {
3625 Alert("parsing [%s:%d] : <gid> expects an integer argument.\n", file, linenum);
willy tarreau0f7af912005-12-17 12:21:26 +01003626 return -1;
3627 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003628 global.gid = atol(args[1]);
3629 }
3630 else if (!strcmp(args[0], "nbproc")) {
3631 if (global.nbproc != 0) {
3632 Alert("parsing [%s:%d] : <nbproc> already specified. Continuing.\n", file, linenum);
3633 return 0;
willy tarreau0f7af912005-12-17 12:21:26 +01003634 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003635 if (*(args[1]) == 0) {
3636 Alert("parsing [%s:%d] : <gid> expects an integer argument.\n", file, linenum);
3637 return -1;
willy tarreau0f7af912005-12-17 12:21:26 +01003638 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003639 global.nbproc = atol(args[1]);
3640 }
3641 else if (!strcmp(args[0], "maxconn")) {
3642 if (global.maxconn != 0) {
3643 Alert("parsing [%s:%d] : <maxconn> already specified. Continuing.\n", file, linenum);
3644 return 0;
willy tarreau0f7af912005-12-17 12:21:26 +01003645 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003646 if (*(args[1]) == 0) {
3647 Alert("parsing [%s:%d] : <maxconn> expects an integer argument.\n", file, linenum);
3648 return -1;
willy tarreau0f7af912005-12-17 12:21:26 +01003649 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003650 global.maxconn = atol(args[1]);
3651 }
3652 else if (!strcmp(args[0], "chroot")) {
3653 if (global.chroot != NULL) {
3654 Alert("parsing [%s:%d] : <chroot> already specified. Continuing.\n", file, linenum);
3655 return 0;
3656 }
3657 if (*(args[1]) == 0) {
3658 Alert("parsing [%s:%d] : <chroot> expects a directory as an argument.\n", file, linenum);
3659 return -1;
3660 }
3661 global.chroot = strdup(args[1]);
3662 }
3663 else if (!strcmp(args[0], "log")) { /* syslog server address */
3664 struct sockaddr_in *sa;
3665 int facility;
3666
3667 if (*(args[1]) == 0 || *(args[2]) == 0) {
3668 Alert("parsing [%s:%d] : <log> expects <address> and <facility> as arguments.\n", file, linenum);
3669 return -1;
3670 }
3671
3672 for (facility = 0; facility < NB_LOG_FACILITIES; facility++)
3673 if (!strcmp(log_facilities[facility], args[2]))
3674 break;
3675
3676 if (facility >= NB_LOG_FACILITIES) {
3677 Alert("parsing [%s:%d] : unknown log facility <%s>\n", file, linenum, args[2]);
3678 exit(1);
3679 }
3680
3681 sa = str2sa(args[1]);
3682 if (!sa->sin_port)
3683 sa->sin_port = htons(SYSLOG_PORT);
3684
3685 if (global.logfac1 == -1) {
3686 global.logsrv1 = *sa;
3687 global.logfac1 = facility;
3688 }
3689 else if (global.logfac2 == -1) {
3690 global.logsrv2 = *sa;
3691 global.logfac2 = facility;
3692 }
3693 else {
3694 Alert("parsing [%s:%d] : too many syslog servers\n", file, linenum);
3695 return -1;
3696 }
3697
3698 }
3699 else {
3700 Alert("parsing [%s:%d] : unknown keyword <%s> in <global> section\n", file, linenum, args[0]);
3701 return -1;
3702 }
3703 return 0;
3704}
3705
3706
3707/*
3708 * parse a line in a <listen> section. Returns 0 if OK, -1 if error.
3709 */
3710int cfg_parse_listen(char *file, int linenum, char **args) {
3711 static struct proxy *curproxy = NULL;
3712 struct server *newsrv = NULL;
3713
3714 if (!strcmp(args[0], "listen")) { /* new proxy */
3715 if (strchr(args[2], ':') == NULL) {
3716 Alert("parsing [%s:%d] : <listen> expects <id> and <addr:port> as arguments.\n",
3717 file, linenum);
3718 return -1;
3719 }
3720
3721 if ((curproxy = (struct proxy *)calloc(1, sizeof(struct proxy))) == NULL) {
3722 Alert("parsing [%s:%d] : out of memory\n", file, linenum);
3723 return -1;
3724 }
3725 curproxy->next = proxy;
3726 proxy = curproxy;
3727 curproxy->id = strdup(args[1]);
3728 curproxy->listen_addr = *str2sa(args[2]);
3729 curproxy->state = PR_STNEW;
3730 /* set default values */
3731 curproxy->maxconn = cfg_maxpconn;
3732 curproxy->conn_retries = CONN_RETRIES;
3733 curproxy->options = 0;
3734 curproxy->clitimeout = curproxy->contimeout = curproxy->srvtimeout = 0;
3735 curproxy->mode = PR_MODE_TCP;
3736 curproxy->logfac1 = curproxy->logfac2 = -1; /* log disabled */
3737 curproxy->to_log = 0;
3738 return 0;
3739 }
3740 else if (curproxy == NULL) {
3741 Alert("parsing [%s:%d] : <listen> expected.\n", file, linenum);
3742 return -1;
3743 }
3744
3745 if (!strcmp(args[0], "mode")) { /* sets the proxy mode */
3746 if (!strcmp(args[1], "http")) curproxy->mode = PR_MODE_HTTP;
3747 else if (!strcmp(args[1], "tcp")) curproxy->mode = PR_MODE_TCP;
3748 else if (!strcmp(args[1], "health")) curproxy->mode = PR_MODE_HEALTH;
3749 else {
3750 Alert("parsing [%s:%d] : unknown proxy mode <%s>.\n", file, linenum, args[1]);
3751 return -1;
3752 }
3753 }
3754 else if (!strcmp(args[0], "disabled")) { /* disables this proxy */
3755 curproxy->state = PR_STDISABLED;
3756 }
3757 else if (!strcmp(args[0], "cookie")) { /* cookie name */
3758 int cur_arg;
3759 if (curproxy->cookie_name != NULL) {
3760 Alert("parsing [%s:%d] : cookie name already specified. Continuing.\n",
3761 file, linenum);
3762 return 0;
3763 }
3764
3765 if (*(args[1]) == 0) {
3766 Alert("parsing [%s:%d] : <cookie> expects <cookie_name> as argument.\n",
3767 file, linenum);
3768 return -1;
3769 }
3770 curproxy->cookie_name = strdup(args[1]);
3771
3772 cur_arg = 2;
3773 while (*(args[cur_arg])) {
3774 if (!strcmp(args[cur_arg], "rewrite")) {
3775 curproxy->options |= PR_O_COOK_RW;
willy tarreau0f7af912005-12-17 12:21:26 +01003776 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003777 else if (!strcmp(args[cur_arg], "indirect")) {
3778 curproxy->options |= PR_O_COOK_IND;
willy tarreau0f7af912005-12-17 12:21:26 +01003779 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003780 else if (!strcmp(args[cur_arg], "insert")) {
3781 curproxy->options |= PR_O_COOK_INS;
willy tarreau0f7af912005-12-17 12:21:26 +01003782 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003783 else {
3784 Alert("parsing [%s:%d] : <cookie> supports 'rewrite', 'insert' and 'indirect' options.\n",
willy tarreau0f7af912005-12-17 12:21:26 +01003785 file, linenum);
3786 return -1;
3787 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003788 cur_arg++;
3789 }
3790 if ((curproxy->options & (PR_O_COOK_RW|PR_O_COOK_IND)) == (PR_O_COOK_RW|PR_O_COOK_IND)) {
3791 Alert("parsing [%s:%d] : <cookie> 'rewrite' and 'indirect' mode are incompatibles.\n",
3792 file, linenum);
3793 return -1;
3794 }
3795 }
3796 else if (!strcmp(args[0], "contimeout")) { /* connect timeout */
3797 if (curproxy->contimeout != 0) {
3798 Alert("parsing [%s:%d] : contimeout already specified. Continuing.\n", file, linenum);
3799 return 0;
3800 }
3801 if (*(args[1]) == 0) {
3802 Alert("parsing [%s:%d] : <contimeout> expects an integer <time_in_ms> as argument.\n",
3803 file, linenum);
3804 return -1;
willy tarreau0f7af912005-12-17 12:21:26 +01003805 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003806 curproxy->contimeout = atol(args[1]);
3807 }
3808 else if (!strcmp(args[0], "clitimeout")) { /* client timeout */
3809 if (curproxy->clitimeout != 0) {
3810 Alert("parsing [%s:%d] : clitimeout already specified. Continuing.\n",
3811 file, linenum);
3812 return 0;
3813 }
3814 if (*(args[1]) == 0) {
3815 Alert("parsing [%s:%d] : <clitimeout> expects an integer <time_in_ms> as argument.\n",
3816 file, linenum);
3817 return -1;
3818 }
3819 curproxy->clitimeout = atol(args[1]);
3820 }
3821 else if (!strcmp(args[0], "srvtimeout")) { /* server timeout */
3822 if (curproxy->srvtimeout != 0) {
3823 Alert("parsing [%s:%d] : srvtimeout already specified. Continuing.\n", file, linenum);
3824 return 0;
3825 }
3826 if (*(args[1]) == 0) {
3827 Alert("parsing [%s:%d] : <srvtimeout> expects an integer <time_in_ms> as argument.\n",
willy tarreau0f7af912005-12-17 12:21:26 +01003828 file, linenum);
3829 return -1;
willy tarreau0f7af912005-12-17 12:21:26 +01003830 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003831 curproxy->srvtimeout = atol(args[1]);
3832 }
3833 else if (!strcmp(args[0], "retries")) { /* connection retries */
3834 if (*(args[1]) == 0) {
3835 Alert("parsing [%s:%d] : <retries> expects an integer argument (dispatch counts for one).\n",
3836 file, linenum);
3837 return -1;
3838 }
3839 curproxy->conn_retries = atol(args[1]);
3840 }
3841 else if (!strcmp(args[0], "option")) {
3842 if (*(args[1]) == 0) {
3843 Alert("parsing [%s:%d] : <option> expects an option name.\n", file, linenum);
3844 return -1;
3845 }
3846 if (!strcmp(args[1], "redispatch"))
willy tarreau5cbea6f2005-12-17 12:48:26 +01003847 /* enable reconnections to dispatch */
3848 curproxy->options |= PR_O_REDISP;
willy tarreaua1598082005-12-17 13:08:06 +01003849#ifdef TPROXY
willy tarreau9fe663a2005-12-17 13:02:59 +01003850 else if (!strcmp(args[1], "transparent"))
willy tarreau5cbea6f2005-12-17 12:48:26 +01003851 /* enable transparent proxy connections */
3852 curproxy->options |= PR_O_TRANSP;
willy tarreau9fe663a2005-12-17 13:02:59 +01003853#endif
3854 else if (!strcmp(args[1], "keepalive"))
3855 /* enable keep-alive */
3856 curproxy->options |= PR_O_KEEPALIVE;
3857 else if (!strcmp(args[1], "forwardfor"))
3858 /* insert x-forwarded-for field */
3859 curproxy->options |= PR_O_FWDFOR;
3860 else if (!strcmp(args[1], "httplog")) {
3861 /* generate a complete HTTP log */
willy tarreaua1598082005-12-17 13:08:06 +01003862 curproxy->to_log |= LW_DATE | LW_CLIP | LW_SVID | LW_REQ | LW_PXID | LW_RESP;
3863 }
3864 else if (!strcmp(args[1], "dontlognull")) {
3865 /* don't log empty requests */
3866 curproxy->options |= PR_O_NULLNOLOG;
willy tarreau5cbea6f2005-12-17 12:48:26 +01003867 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003868 else {
3869 Alert("parsing [%s:%d] : unknown option <%s>.\n", file, linenum, args[1]);
3870 return -1;
3871 }
3872 return 0;
3873 }
3874 else if (!strcmp(args[0], "redispatch") || !strcmp(args[0], "redisp")) {
3875 /* enable reconnections to dispatch */
3876 curproxy->options |= PR_O_REDISP;
3877 }
willy tarreaua1598082005-12-17 13:08:06 +01003878#ifdef TPROXY
willy tarreau9fe663a2005-12-17 13:02:59 +01003879 else if (!strcmp(args[0], "transparent")) {
3880 /* enable transparent proxy connections */
3881 curproxy->options |= PR_O_TRANSP;
3882 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01003883#endif
willy tarreau9fe663a2005-12-17 13:02:59 +01003884 else if (!strcmp(args[0], "maxconn")) { /* maxconn */
3885 if (*(args[1]) == 0) {
3886 Alert("parsing [%s:%d] : <maxconn> expects an integer argument.\n", file, linenum);
3887 return -1;
willy tarreau0f7af912005-12-17 12:21:26 +01003888 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003889 curproxy->maxconn = atol(args[1]);
3890 }
3891 else if (!strcmp(args[0], "grace")) { /* grace time (ms) */
3892 if (*(args[1]) == 0) {
3893 Alert("parsing [%s:%d] : <grace> expects a time in milliseconds.\n", file, linenum);
3894 return -1;
willy tarreau0f7af912005-12-17 12:21:26 +01003895 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003896 curproxy->grace = atol(args[1]);
3897 }
3898 else if (!strcmp(args[0], "dispatch")) { /* dispatch address */
3899 if (strchr(args[1], ':') == NULL) {
3900 Alert("parsing [%s:%d] : <dispatch> expects <addr:port> as argument.\n", file, linenum);
3901 return -1;
willy tarreau0f7af912005-12-17 12:21:26 +01003902 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003903 curproxy->dispatch_addr = *str2sa(args[1]);
3904 }
3905 else if (!strcmp(args[0], "balance")) { /* set balancing with optionnal algorithm */
3906 if (*(args[1])) {
3907 if (!strcmp(args[1], "roundrobin")) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01003908 curproxy->options |= PR_O_BALANCE_RR;
willy tarreau9fe663a2005-12-17 13:02:59 +01003909 }
3910 else {
3911 Alert("parsing [%s:%d] : <balance> supports 'roundrobin' options.\n", file, linenum);
3912 return -1;
3913 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01003914 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003915 else /* if no option is set, use round-robin by default */
3916 curproxy->options |= PR_O_BALANCE_RR;
3917 }
3918 else if (!strcmp(args[0], "server")) { /* server address */
3919 int cur_arg;
willy tarreau5cbea6f2005-12-17 12:48:26 +01003920
willy tarreau9fe663a2005-12-17 13:02:59 +01003921 if (strchr(args[2], ':') == NULL) {
3922 Alert("parsing [%s:%d] : <server> expects <name> and <addr:port> as arguments.\n",
3923 file, linenum);
3924 return -1;
3925 }
3926 if ((newsrv = (struct server *)calloc(1, sizeof(struct server))) == NULL) {
3927 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3928 return -1;
3929 }
3930 newsrv->next = curproxy->srv;
3931 curproxy->srv = newsrv;
3932 newsrv->proxy = curproxy;
3933 newsrv->id = strdup(args[1]);
3934 newsrv->addr = *str2sa(args[2]);
3935 newsrv->state = SRV_RUNNING; /* early server setup */
3936 newsrv->curfd = -1; /* no health-check in progress */
3937 newsrv->inter = DEF_CHKINTR;
3938 newsrv->rise = DEF_RISETIME;
3939 newsrv->fall = DEF_FALLTIME;
3940 newsrv->health = newsrv->rise; /* up, but will fall down at first failure */
3941 cur_arg = 3;
3942 while (*args[cur_arg]) {
3943 if (!strcmp(args[cur_arg], "cookie")) {
3944 newsrv->cookie = strdup(args[cur_arg + 1]);
3945 newsrv->cklen = strlen(args[cur_arg + 1]);
3946 cur_arg += 2;
willy tarreau0f7af912005-12-17 12:21:26 +01003947 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003948 else if (!strcmp(args[cur_arg], "rise")) {
3949 newsrv->rise = atol(args[cur_arg + 1]);
3950 newsrv->health = newsrv->rise;
3951 cur_arg += 2;
willy tarreau0f7af912005-12-17 12:21:26 +01003952 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003953 else if (!strcmp(args[cur_arg], "fall")) {
3954 newsrv->fall = atol(args[cur_arg + 1]);
3955 cur_arg += 2;
3956 }
3957 else if (!strcmp(args[cur_arg], "inter")) {
3958 newsrv->inter = atol(args[cur_arg + 1]);
3959 cur_arg += 2;
3960 }
3961 else if (!strcmp(args[cur_arg], "check")) {
3962 struct task *t;
3963
3964 if ((t = pool_alloc(task)) == NULL) { /* disable this proxy for a while */
3965 Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
willy tarreau5cbea6f2005-12-17 12:48:26 +01003966 return -1;
3967 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003968
3969 t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
3970 t->wq = LIST_HEAD(wait_queue); /* but already has a wait queue assigned */
3971 t->state = TASK_IDLE;
3972 t->process = process_chk;
3973 t->context = newsrv;
3974
3975 if (curproxy->state != PR_STDISABLED) {
3976 tv_delayfrom(&t->expire, &now, newsrv->inter); /* check this every ms */
3977 task_queue(t);
3978 task_wakeup(&rq, t);
3979 }
3980
3981 cur_arg += 1;
willy tarreau5cbea6f2005-12-17 12:48:26 +01003982 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003983 else {
3984 Alert("parsing [%s:%d] : server %s only supports options 'cookie' and 'check'.\n",
3985 file, linenum, newsrv->id);
willy tarreau0f7af912005-12-17 12:21:26 +01003986 return -1;
3987 }
willy tarreau9fe663a2005-12-17 13:02:59 +01003988 }
3989 curproxy->nbservers++;
3990 }
3991 else if (!strcmp(args[0], "log")) { /* syslog server address */
3992 struct sockaddr_in *sa;
3993 int facility;
3994
3995 if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
3996 curproxy->logfac1 = global.logfac1;
3997 curproxy->logsrv1 = global.logsrv1;
3998 curproxy->logfac2 = global.logfac2;
3999 curproxy->logsrv2 = global.logsrv2;
4000 }
4001 else if (*(args[1]) && *(args[2])) {
willy tarreau0f7af912005-12-17 12:21:26 +01004002 for (facility = 0; facility < NB_LOG_FACILITIES; facility++)
4003 if (!strcmp(log_facilities[facility], args[2]))
4004 break;
willy tarreau9fe663a2005-12-17 13:02:59 +01004005
willy tarreau0f7af912005-12-17 12:21:26 +01004006 if (facility >= NB_LOG_FACILITIES) {
4007 Alert("parsing [%s:%d] : unknown log facility <%s>\n", file, linenum, args[2]);
4008 exit(1);
4009 }
willy tarreau9fe663a2005-12-17 13:02:59 +01004010
willy tarreau0f7af912005-12-17 12:21:26 +01004011 sa = str2sa(args[1]);
4012 if (!sa->sin_port)
4013 sa->sin_port = htons(SYSLOG_PORT);
willy tarreau9fe663a2005-12-17 13:02:59 +01004014
willy tarreau0f7af912005-12-17 12:21:26 +01004015 if (curproxy->logfac1 == -1) {
4016 curproxy->logsrv1 = *sa;
4017 curproxy->logfac1 = facility;
4018 }
4019 else if (curproxy->logfac2 == -1) {
4020 curproxy->logsrv2 = *sa;
4021 curproxy->logfac2 = facility;
4022 }
4023 else {
4024 Alert("parsing [%s:%d] : too many syslog servers\n", file, linenum);
willy tarreau9fe663a2005-12-17 13:02:59 +01004025 return -1;
willy tarreau0f7af912005-12-17 12:21:26 +01004026 }
willy tarreau9fe663a2005-12-17 13:02:59 +01004027 }
4028 else {
4029 Alert("parsing [%s:%d] : <log> expects either <address[:port]> and <facility> or 'global' as arguments.\n",
4030 file, linenum);
4031 return -1;
4032 }
4033 }
willy tarreaua1598082005-12-17 13:08:06 +01004034 else if (!strcmp(args[0], "source")) { /* address to which we bind when connecting */
4035 if (strchr(args[1], ':') == NULL) {
4036 Alert("parsing [%s:%d] : <source> expects <addr:port> as argument.\n",
4037 file, linenum);
4038 return -1;
4039 }
4040
4041 curproxy->source_addr = *str2sa(args[1]);
4042 curproxy->options |= PR_O_BIND_SRC;
4043 }
willy tarreau9fe663a2005-12-17 13:02:59 +01004044 else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) { /* replace request header from a regex */
4045 regex_t *preg;
4046
4047 if (*(args[1]) == 0 || *(args[2]) == 0) {
4048 Alert("parsing [%s:%d] : <reqrep> expects <search> and <replace> as arguments.\n",
4049 file, linenum);
4050 return -1;
4051 }
4052
4053 preg = calloc(1, sizeof(regex_t));
4054 if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
4055 Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
4056 return -1;
4057 }
4058
4059 chain_regex(&curproxy->req_exp, preg, ACT_REPLACE, strdup(args[2]));
4060 }
4061 else if (!strcmp(args[0], "reqdel")) { /* delete request header from a regex */
4062 regex_t *preg;
4063
4064 if (*(args[1]) == 0) {
4065 Alert("parsing [%s:%d] : <reqdel> expects <regex> as an argument.\n", file, linenum);
4066 return -1;
4067 }
4068
4069 preg = calloc(1, sizeof(regex_t));
4070 if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
4071 Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
4072 return -1;
4073 }
4074
4075 chain_regex(&curproxy->req_exp, preg, ACT_REMOVE, NULL);
4076 }
4077 else if (!strcmp(args[0], "reqdeny")) { /* deny a request if a header matches this regex */
4078 regex_t *preg;
4079
4080 if (*(args[1]) == 0) {
4081 Alert("parsing [%s:%d] : <reqdeny> expects <regex> as an argument.\n", file, linenum);
4082 return -1;
4083 }
4084
4085 preg = calloc(1, sizeof(regex_t));
4086 if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
4087 Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
4088 return -1;
4089 }
4090
4091 chain_regex(&curproxy->req_exp, preg, ACT_DENY, NULL);
4092 }
4093 else if (!strcmp(args[0], "reqallow")) { /* allow a request if a header matches this regex */
4094 regex_t *preg;
4095
4096 if (*(args[1]) == 0) {
4097 Alert("parsing [%s:%d] : <reqallow> expects <regex> as an argument.\n", file, linenum);
4098 return -1;
4099 }
4100
4101 preg = calloc(1, sizeof(regex_t));
4102 if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
4103 Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
4104 return -1;
4105 }
4106
4107 chain_regex(&curproxy->req_exp, preg, ACT_ALLOW, NULL);
4108 }
4109 else if (!strcmp(args[0], "reqirep")) { /* replace request header from a regex, ignoring case */
4110 regex_t *preg;
4111
4112 if (*(args[1]) == 0 || *(args[2]) == 0) {
4113 Alert("parsing [%s:%d] : <reqirep> expects <search> and <replace> as arguments.\n",
4114 file, linenum);
4115 return -1;
4116 }
4117
4118 preg = calloc(1, sizeof(regex_t));
4119 if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
4120 Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
4121 return -1;
4122 }
4123
4124 chain_regex(&curproxy->req_exp, preg, ACT_REPLACE, strdup(args[2]));
4125 }
4126 else if (!strcmp(args[0], "reqidel")) { /* delete request header from a regex ignoring case */
4127 regex_t *preg;
4128
4129 if (*(args[1]) == 0) {
4130 Alert("parsing [%s:%d] : <reqidel> expects <regex> as an argument.\n", file, linenum);
4131 return -1;
4132 }
4133
4134 preg = calloc(1, sizeof(regex_t));
4135 if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
4136 Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
4137 return -1;
4138 }
4139
4140 chain_regex(&curproxy->req_exp, preg, ACT_REMOVE, NULL);
4141 }
4142 else if (!strcmp(args[0], "reqideny")) { /* deny a request if a header matches this regex ignoring case */
4143 regex_t *preg;
4144
4145 if (*(args[1]) == 0) {
4146 Alert("parsing [%s:%d] : <reqideny> expects <regex> as an argument.\n", file, linenum);
4147 return -1;
4148 }
4149
4150 preg = calloc(1, sizeof(regex_t));
4151 if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
4152 Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
4153 return -1;
4154 }
4155
4156 chain_regex(&curproxy->req_exp, preg, ACT_DENY, NULL);
4157 }
4158 else if (!strcmp(args[0], "reqiallow")) { /* allow a request if a header matches this regex ignoring case */
4159 regex_t *preg;
4160
4161 if (*(args[1]) == 0) {
4162 Alert("parsing [%s:%d] : <reqiallow> expects <regex> as an argument.\n", file, linenum);
4163 return -1;
4164 }
4165
4166 preg = calloc(1, sizeof(regex_t));
4167 if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
4168 Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
4169 return -1;
4170 }
4171
4172 chain_regex(&curproxy->req_exp, preg, ACT_ALLOW, NULL);
4173 }
4174 else if (!strcmp(args[0], "reqadd")) { /* add request header */
4175 if (curproxy->nb_reqadd >= MAX_NEWHDR) {
4176 Alert("parsing [%s:%d] : too many `reqadd'. Continuing.\n", file, linenum);
4177 return 0;
4178 }
4179
4180 if (*(args[1]) == 0) {
4181 Alert("parsing [%s:%d] : <reqadd> expects <header> as an argument.\n", file, linenum);
4182 return -1;
4183 }
4184
4185 curproxy->req_add[curproxy->nb_reqadd++] = strdup(args[1]);
willy tarreau0f7af912005-12-17 12:21:26 +01004186 }
willy tarreau9fe663a2005-12-17 13:02:59 +01004187 else if (!strcmp(args[0], "srvexp") || !strcmp(args[0], "rsprep")) { /* replace response header from a regex */
willy tarreau0f7af912005-12-17 12:21:26 +01004188 regex_t *preg;
willy tarreau0f7af912005-12-17 12:21:26 +01004189
4190 if (*(args[1]) == 0 || *(args[2]) == 0) {
willy tarreau9fe663a2005-12-17 13:02:59 +01004191 Alert("parsing [%s:%d] : <rsprep> expects <search> and <replace> as arguments.\n",
willy tarreau0f7af912005-12-17 12:21:26 +01004192 file, linenum);
4193 return -1;
4194 }
4195
4196 preg = calloc(1, sizeof(regex_t));
4197 if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
4198 Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
4199 return -1;
4200 }
willy tarreau9fe663a2005-12-17 13:02:59 +01004201
4202 chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
4203 }
4204 else if (!strcmp(args[0], "rspdel")) { /* delete response header from a regex */
4205 regex_t *preg;
4206
4207 if (*(args[1]) == 0) {
4208 Alert("parsing [%s:%d] : <rspdel> expects <search> as an argument.\n", file, linenum);
4209 return -1;
4210 }
willy tarreaue39cd132005-12-17 13:00:18 +01004211
willy tarreau9fe663a2005-12-17 13:02:59 +01004212 preg = calloc(1, sizeof(regex_t));
4213 if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
4214 Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
4215 return -1;
willy tarreau0f7af912005-12-17 12:21:26 +01004216 }
willy tarreau9fe663a2005-12-17 13:02:59 +01004217
4218 chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
4219 }
4220 else if (!strcmp(args[0], "rspirep")) { /* replace response header from a regex ignoring case */
willy tarreau0f7af912005-12-17 12:21:26 +01004221 regex_t *preg;
willy tarreaue39cd132005-12-17 13:00:18 +01004222
willy tarreau9fe663a2005-12-17 13:02:59 +01004223 if (*(args[1]) == 0 || *(args[2]) == 0) {
4224 Alert("parsing [%s:%d] : <rspirep> expects <search> and <replace> as arguments.\n",
willy tarreau5cbea6f2005-12-17 12:48:26 +01004225 file, linenum);
willy tarreaue39cd132005-12-17 13:00:18 +01004226 return -1;
4227 }
4228
4229 preg = calloc(1, sizeof(regex_t));
willy tarreau9fe663a2005-12-17 13:02:59 +01004230 if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
willy tarreaue39cd132005-12-17 13:00:18 +01004231 Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
4232 return -1;
willy tarreau5cbea6f2005-12-17 12:48:26 +01004233 }
willy tarreau9fe663a2005-12-17 13:02:59 +01004234
4235 chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
4236 }
4237 else if (!strcmp(args[0], "rspidel")) { /* delete response header from a regex ignoring case */
4238 regex_t *preg;
4239
4240 if (*(args[1]) == 0) {
4241 Alert("parsing [%s:%d] : <rspidel> expects <search> as an argument.\n", file, linenum);
4242 return -1;
4243 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01004244
willy tarreau9fe663a2005-12-17 13:02:59 +01004245 preg = calloc(1, sizeof(regex_t));
4246 if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
4247 Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
4248 return -1;
willy tarreaue39cd132005-12-17 13:00:18 +01004249 }
willy tarreau9fe663a2005-12-17 13:02:59 +01004250
4251 chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
4252 }
4253 else if (!strcmp(args[0], "rspadd")) { /* add response header */
4254 if (curproxy->nb_rspadd >= MAX_NEWHDR) {
4255 Alert("parsing [%s:%d] : too many `rspadd'. Continuing.\n", file, linenum);
4256 return 0;
4257 }
4258
4259 if (*(args[1]) == 0) {
4260 Alert("parsing [%s:%d] : <rspadd> expects <header> as an argument.\n", file, linenum);
4261 return -1;
4262 }
4263
4264 curproxy->rsp_add[curproxy->nb_rspadd++] = strdup(args[1]);
4265 }
4266 else {
4267 Alert("parsing [%s:%d] : unknown keyword <%s> in <listen> section\n", file, linenum, args[0]);
4268 return -1;
4269 }
4270 return 0;
4271}
willy tarreaue39cd132005-12-17 13:00:18 +01004272
willy tarreau5cbea6f2005-12-17 12:48:26 +01004273
willy tarreau9fe663a2005-12-17 13:02:59 +01004274/*
4275 * This function reads and parses the configuration file given in the argument.
4276 * returns 0 if OK, -1 if error.
4277 */
4278int readcfgfile(char *file) {
4279 char thisline[256];
4280 char *line;
4281 FILE *f;
4282 int linenum = 0;
4283 char *end;
4284 char *args[MAX_LINE_ARGS];
4285 int arg;
4286 int cfgerr = 0;
4287 int confsect = CFG_NONE;
willy tarreaue39cd132005-12-17 13:00:18 +01004288
willy tarreau9fe663a2005-12-17 13:02:59 +01004289 struct proxy *curproxy = NULL;
4290 struct server *newsrv = NULL;
willy tarreaue39cd132005-12-17 13:00:18 +01004291
willy tarreau9fe663a2005-12-17 13:02:59 +01004292 if ((f=fopen(file,"r")) == NULL)
4293 return -1;
willy tarreaue39cd132005-12-17 13:00:18 +01004294
willy tarreau9fe663a2005-12-17 13:02:59 +01004295 while (fgets(line = thisline, sizeof(thisline), f) != NULL) {
4296 linenum++;
willy tarreau5cbea6f2005-12-17 12:48:26 +01004297
willy tarreau9fe663a2005-12-17 13:02:59 +01004298 end = line + strlen(line);
willy tarreau5cbea6f2005-12-17 12:48:26 +01004299
willy tarreau9fe663a2005-12-17 13:02:59 +01004300 /* skip leading spaces */
4301 while (isspace(*line))
4302 line++;
4303
4304 arg = 0;
4305 args[arg] = line;
willy tarreau0f7af912005-12-17 12:21:26 +01004306
willy tarreau9fe663a2005-12-17 13:02:59 +01004307 while (*line && arg < MAX_LINE_ARGS) {
4308 /* first, we'll replace \\, \<space>, \#, \r, \n, \t, \xXX with their
4309 * C equivalent value. Other combinations left unchanged (eg: \1).
4310 */
4311 if (*line == '\\') {
4312 int skip = 0;
4313 if (line[1] == ' ' || line[1] == '\\' || line[1] == '#') {
4314 *line = line[1];
4315 skip = 1;
4316 }
4317 else if (line[1] == 'r') {
4318 *line = '\r';
4319 skip = 1;
4320 }
4321 else if (line[1] == 'n') {
4322 *line = '\n';
4323 skip = 1;
4324 }
4325 else if (line[1] == 't') {
4326 *line = '\t';
4327 skip = 1;
4328 }
4329 else if (line[1] == 'x' && (line + 3 < end )) {
4330 unsigned char hex1, hex2;
4331 hex1 = toupper(line[2]) - '0'; hex2 = toupper(line[3]) - '0';
4332 if (hex1 > 9) hex1 -= 'A' - '9' - 1;
4333 if (hex2 > 9) hex2 -= 'A' - '9' - 1;
4334 *line = (hex1<<4) + hex2;
4335 skip = 3;
4336 }
4337 if (skip) {
4338 memmove(line + 1, line + 1 + skip, end - (line + skip + 1));
4339 end -= skip;
4340 }
4341 line++;
willy tarreau0f7af912005-12-17 12:21:26 +01004342 }
willy tarreaua1598082005-12-17 13:08:06 +01004343 else if (*line == '#' || *line == '\n' || *line == '\r') {
4344 /* end of string, end of loop */
4345 *line = 0;
4346 break;
4347 }
4348 else if (isspace(*line)) {
willy tarreau9fe663a2005-12-17 13:02:59 +01004349 /* a non-escaped space is an argument separator */
willy tarreaua1598082005-12-17 13:08:06 +01004350 *line++ = 0;
4351 while (isspace(*line))
4352 line++;
4353 args[++arg] = line;
4354 }
4355 else {
4356 line++;
willy tarreau0f7af912005-12-17 12:21:26 +01004357 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01004358 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01004359
willy tarreau9fe663a2005-12-17 13:02:59 +01004360 /* empty line */
4361 if (!**args)
4362 continue;
willy tarreaue39cd132005-12-17 13:00:18 +01004363
willy tarreau9fe663a2005-12-17 13:02:59 +01004364 /* zero out remaining args */
4365 while (++arg < MAX_LINE_ARGS) {
4366 args[arg] = line;
willy tarreau5cbea6f2005-12-17 12:48:26 +01004367 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01004368
willy tarreau9fe663a2005-12-17 13:02:59 +01004369 if (!strcmp(args[0], "listen")) /* new proxy */
4370 confsect = CFG_LISTEN;
4371 else if (!strcmp(args[0], "global")) /* global config */
4372 confsect = CFG_GLOBAL;
4373 /* else it's a section keyword */
willy tarreau5cbea6f2005-12-17 12:48:26 +01004374
willy tarreau9fe663a2005-12-17 13:02:59 +01004375 switch (confsect) {
4376 case CFG_LISTEN:
4377 if (cfg_parse_listen(file, linenum, args) < 0)
4378 return -1;
4379 break;
4380 case CFG_GLOBAL:
4381 if (cfg_parse_global(file, linenum, args) < 0)
4382 return -1;
4383 break;
4384 default:
4385 Alert("parsing [%s:%d] : unknown keyword <%s> out of section.\n", file, linenum, args[0]);
4386 return -1;
willy tarreau0f7af912005-12-17 12:21:26 +01004387 }
willy tarreau9fe663a2005-12-17 13:02:59 +01004388
4389
willy tarreau0f7af912005-12-17 12:21:26 +01004390 }
4391 fclose(f);
4392
4393 /*
4394 * Now, check for the integrity of all that we have collected.
4395 */
4396
4397 if ((curproxy = proxy) == NULL) {
4398 Alert("parsing %s : no <listen> line. Nothing to do !\n",
4399 file);
4400 return -1;
4401 }
4402
4403 while (curproxy != NULL) {
willy tarreauef900ab2005-12-17 12:52:52 +01004404 if (curproxy->state == PR_STDISABLED) {
4405 curproxy = curproxy->next;
4406 continue;
4407 }
willy tarreau5cbea6f2005-12-17 12:48:26 +01004408 if ((curproxy->mode != PR_MODE_HEALTH) &&
4409 !(curproxy->options & (PR_O_TRANSP | PR_O_BALANCE)) &&
willy tarreaua1598082005-12-17 13:08:06 +01004410 (*(int *)&curproxy->dispatch_addr.sin_addr == 0)) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01004411 Alert("parsing %s : listener %s has no dispatch address and is not in transparent or balance mode.\n",
4412 file, curproxy->id);
4413 cfgerr++;
4414 }
4415 else if ((curproxy->mode != PR_MODE_HEALTH) && (curproxy->options & PR_O_BALANCE)) {
4416 if (curproxy->options & PR_O_TRANSP) {
4417 Alert("parsing %s : listener %s cannot use both transparent and balance mode.\n",
4418 file, curproxy->id);
4419 cfgerr++;
4420 }
4421 else if (curproxy->srv == NULL) {
4422 Alert("parsing %s : listener %s needs at least 1 server in balance mode.\n",
4423 file, curproxy->id);
4424 cfgerr++;
4425 }
willy tarreaua1598082005-12-17 13:08:06 +01004426 else if (*(int *)&curproxy->dispatch_addr.sin_addr != 0) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01004427 Warning("parsing %s : dispatch address of listener %s will be ignored in balance mode.\n",
4428 file, curproxy->id);
4429 }
4430 }
4431 else if (curproxy->mode == PR_MODE_TCP || curproxy->mode == PR_MODE_HEALTH) { /* TCP PROXY or HEALTH CHECK */
willy tarreau0f7af912005-12-17 12:21:26 +01004432 if (curproxy->cookie_name != NULL) {
4433 Warning("parsing %s : cookie will be ignored for listener %s.\n",
4434 file, curproxy->id);
4435 }
4436 if ((newsrv = curproxy->srv) != NULL) {
4437 Warning("parsing %s : servers will be ignored for listener %s.\n",
4438 file, curproxy->id);
4439 }
willy tarreaue39cd132005-12-17 13:00:18 +01004440 if (curproxy->rsp_exp != NULL) {
willy tarreau0f7af912005-12-17 12:21:26 +01004441 Warning("parsing %s : server regular expressions will be ignored for listener %s.\n",
4442 file, curproxy->id);
4443 }
willy tarreaue39cd132005-12-17 13:00:18 +01004444 if (curproxy->req_exp != NULL) {
willy tarreau0f7af912005-12-17 12:21:26 +01004445 Warning("parsing %s : client regular expressions will be ignored for listener %s.\n",
4446 file, curproxy->id);
4447 }
4448 }
4449 else if (curproxy->mode == PR_MODE_HTTP) { /* HTTP PROXY */
4450 if ((curproxy->cookie_name != NULL) && ((newsrv = curproxy->srv) == NULL)) {
4451 Alert("parsing %s : HTTP proxy %s has a cookie but no server list !\n",
4452 file, curproxy->id);
4453 cfgerr++;
4454 }
4455 else {
4456 while (newsrv != NULL) {
4457 /* nothing to check for now */
4458 newsrv = newsrv->next;
4459 }
4460 }
4461 }
4462 curproxy = curproxy->next;
4463 }
4464 if (cfgerr > 0) {
4465 Alert("Errors found in configuration file, aborting.\n");
4466 return -1;
4467 }
4468 else
4469 return 0;
4470}
4471
4472
4473/*
4474 * This function initializes all the necessary variables. It only returns
4475 * if everything is OK. If something fails, it exits.
4476 */
4477void init(int argc, char **argv) {
4478 int i;
willy tarreau9fe663a2005-12-17 13:02:59 +01004479 int arg_mode = 0; /* MODE_DEBUG, ... */
willy tarreau0f7af912005-12-17 12:21:26 +01004480 char *old_argv = *argv;
4481 char *tmp;
willy tarreau9fe663a2005-12-17 13:02:59 +01004482 int cfg_maxconn = 0; /* # of simultaneous connections, (-n) */
willy tarreau0f7af912005-12-17 12:21:26 +01004483
4484 if (1<<INTBITS != sizeof(int)*8) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01004485 qfprintf(stderr,
willy tarreau0f7af912005-12-17 12:21:26 +01004486 "Error: wrong architecture. Recompile so that sizeof(int)=%d\n",
4487 sizeof(int)*8);
4488 exit(1);
4489 }
4490
4491 pid = getpid();
4492 progname = *argv;
4493 while ((tmp = strchr(progname, '/')) != NULL)
4494 progname = tmp + 1;
4495
4496 argc--; argv++;
4497 while (argc > 0) {
4498 char *flag;
4499
4500 if (**argv == '-') {
4501 flag = *argv+1;
4502
4503 /* 1 arg */
4504 if (*flag == 'v') {
4505 display_version();
4506 exit(0);
4507 }
4508 else if (*flag == 'd')
willy tarreau9fe663a2005-12-17 13:02:59 +01004509 arg_mode |= MODE_DEBUG;
willy tarreau0f7af912005-12-17 12:21:26 +01004510 else if (*flag == 'D')
willy tarreau9fe663a2005-12-17 13:02:59 +01004511 arg_mode |= MODE_DAEMON | MODE_QUIET;
willy tarreau5cbea6f2005-12-17 12:48:26 +01004512 else if (*flag == 'q')
willy tarreau9fe663a2005-12-17 13:02:59 +01004513 arg_mode |= MODE_QUIET;
willy tarreau0f7af912005-12-17 12:21:26 +01004514#if STATTIME > 0
4515 else if (*flag == 's')
willy tarreau9fe663a2005-12-17 13:02:59 +01004516 arg_mode |= MODE_STATS;
willy tarreau0f7af912005-12-17 12:21:26 +01004517 else if (*flag == 'l')
willy tarreau9fe663a2005-12-17 13:02:59 +01004518 arg_mode |= MODE_LOG;
willy tarreau0f7af912005-12-17 12:21:26 +01004519#endif
4520 else { /* >=2 args */
4521 argv++; argc--;
4522 if (argc == 0)
4523 usage(old_argv);
4524
4525 switch (*flag) {
4526 case 'n' : cfg_maxconn = atol(*argv); break;
4527 case 'N' : cfg_maxpconn = atol(*argv); break;
4528 case 'f' : cfg_cfgfile = *argv; break;
4529 default: usage(old_argv);
4530 }
4531 }
4532 }
4533 else
4534 usage(old_argv);
4535 argv++; argc--;
4536 }
4537
willy tarreau0f7af912005-12-17 12:21:26 +01004538 if (!cfg_cfgfile)
4539 usage(old_argv);
4540
4541 gethostname(hostname, MAX_HOSTNAME_LEN);
4542
4543 if (readcfgfile(cfg_cfgfile) < 0) {
4544 Alert("Error reading configuration file : %s\n", cfg_cfgfile);
4545 exit(1);
4546 }
4547
willy tarreau9fe663a2005-12-17 13:02:59 +01004548 if (cfg_maxconn > 0)
4549 global.maxconn = cfg_maxconn;
4550
4551 if (global.maxconn == 0)
4552 global.maxconn = DEFAULT_MAXCONN;
4553
4554 global.maxsock = global.maxconn * 2; /* each connection needs two sockets */
4555
4556 if (arg_mode & MODE_DEBUG) {
4557 /* command line debug mode inhibits configuration mode */
4558 global.mode &= ~(MODE_DAEMON | MODE_QUIET);
4559 }
4560 global.mode |= (arg_mode & (MODE_DAEMON | MODE_QUIET | MODE_DEBUG));
4561
4562 if ((global.mode & MODE_DEBUG) && (global.mode & (MODE_DAEMON | MODE_QUIET))) {
4563 Warning("<debug> mode incompatible with <quiet> and <daemon>. Keeping <debug> only.\n");
4564 global.mode &= ~(MODE_DAEMON | MODE_QUIET);
4565 }
4566
4567 if ((global.nbproc > 1) && !(global.mode & MODE_DAEMON)) {
4568 Warning("<nbproc> is only meaningful in daemon mode. Setting limit to 1 process.\n");
4569 global.nbproc = 1;
4570 }
4571
4572 if (global.nbproc < 1)
4573 global.nbproc = 1;
4574
willy tarreau0f7af912005-12-17 12:21:26 +01004575 ReadEvent = (fd_set *)calloc(1,
4576 sizeof(fd_set) *
willy tarreau9fe663a2005-12-17 13:02:59 +01004577 (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
willy tarreau0f7af912005-12-17 12:21:26 +01004578 WriteEvent = (fd_set *)calloc(1,
4579 sizeof(fd_set) *
willy tarreau9fe663a2005-12-17 13:02:59 +01004580 (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
willy tarreau0f7af912005-12-17 12:21:26 +01004581 StaticReadEvent = (fd_set *)calloc(1,
4582 sizeof(fd_set) *
willy tarreau9fe663a2005-12-17 13:02:59 +01004583 (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
willy tarreau0f7af912005-12-17 12:21:26 +01004584 StaticWriteEvent = (fd_set *)calloc(1,
4585 sizeof(fd_set) *
willy tarreau9fe663a2005-12-17 13:02:59 +01004586 (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
willy tarreau0f7af912005-12-17 12:21:26 +01004587
4588 fdtab = (struct fdtab *)calloc(1,
willy tarreau9fe663a2005-12-17 13:02:59 +01004589 sizeof(struct fdtab) * (global.maxsock));
4590 for (i = 0; i < global.maxsock; i++) {
willy tarreau0f7af912005-12-17 12:21:26 +01004591 fdtab[i].state = FD_STCLOSE;
4592 }
4593}
4594
4595/*
4596 * this function starts all the proxies. It returns 0 if OK, -1 if not.
4597 */
4598int start_proxies() {
4599 struct proxy *curproxy;
4600 int one = 1;
4601 int fd;
4602
4603 for (curproxy = proxy; curproxy != NULL; curproxy = curproxy->next) {
4604
4605 if (curproxy->state == PR_STDISABLED)
4606 continue;
4607
4608 if ((fd = curproxy->listen_fd =
4609 socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
4610 Alert("cannot create listening socket for proxy %s. Aborting.\n",
4611 curproxy->id);
4612 return -1;
4613 }
4614
willy tarreau9fe663a2005-12-17 13:02:59 +01004615 if (fd >= global.maxsock) {
willy tarreau5cbea6f2005-12-17 12:48:26 +01004616 Alert("socket(): not enough free sockets for proxy %s. Raise -n argument. Aborting.\n",
4617 curproxy->id);
4618 close(fd);
4619 return -1;
4620 }
4621
willy tarreau0f7af912005-12-17 12:21:26 +01004622 if ((fcntl(fd, F_SETFL, O_NONBLOCK) == -1) ||
4623 (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
4624 (char *) &one, sizeof(one)) == -1)) {
4625 Alert("cannot make socket non-blocking for proxy %s. Aborting.\n",
4626 curproxy->id);
4627 close(fd);
4628 return -1;
4629 }
4630
4631 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) == -1) {
4632 Alert("cannot do so_reuseaddr for proxy %s. Continuing.\n",
4633 curproxy->id);
4634 }
4635
4636 if (bind(fd,
4637 (struct sockaddr *)&curproxy->listen_addr,
4638 sizeof(curproxy->listen_addr)) == -1) {
4639 Alert("cannot bind socket for proxy %s. Aborting.\n",
4640 curproxy->id);
4641 close(fd);
4642 return -1;
4643 }
4644
4645 if (listen(fd, curproxy->maxconn) == -1) {
4646 Alert("cannot listen to socket for proxy %s. Aborting.\n",
4647 curproxy->id);
4648 close(fd);
4649 return -1;
4650 }
4651
4652 /* the function for the accept() event */
4653 fdtab[fd].read = &event_accept;
4654 fdtab[fd].write = NULL; /* never called */
willy tarreau5cbea6f2005-12-17 12:48:26 +01004655 fdtab[fd].owner = (struct task *)curproxy; /* reference the proxy instead of a task */
willy tarreau0f7af912005-12-17 12:21:26 +01004656 curproxy->state = PR_STRUN;
4657 fdtab[fd].state = FD_STLISTEN;
4658 FD_SET(fd, StaticReadEvent);
4659 fd_insert(fd);
4660 listeners++;
4661// fprintf(stderr,"Proxy %s : socket bound.\n", curproxy->id);
willy tarreau535ae7a2005-12-17 12:58:00 +01004662
willy tarreau9fe663a2005-12-17 13:02:59 +01004663// sprintf(trash, "Proxy %s started.\n", curproxy->id);
4664//
4665// if (curproxy->logfac1 >= 0)
4666// send_syslog(&curproxy->logsrv1, curproxy->logfac1, LOG_INFO, trash);
4667// if (curproxy->logfac2 >= 0)
4668// send_syslog(&curproxy->logsrv2, curproxy->logfac2, LOG_INFO, trash);
4669
willy tarreaua1598082005-12-17 13:08:06 +01004670 send_log(curproxy, LOG_NOTICE, "Proxy %s started.\n", curproxy->id);
willy tarreau535ae7a2005-12-17 12:58:00 +01004671
willy tarreau0f7af912005-12-17 12:21:26 +01004672 }
4673 return 0;
4674}
4675
4676
4677int main(int argc, char **argv) {
4678 init(argc, argv);
4679
willy tarreau9fe663a2005-12-17 13:02:59 +01004680 if (global.mode & MODE_QUIET) {
willy tarreau0f7af912005-12-17 12:21:26 +01004681 /* detach from the tty */
willy tarreau5cbea6f2005-12-17 12:48:26 +01004682 fclose(stdin); fclose(stdout); fclose(stderr);
willy tarreau0f7af912005-12-17 12:21:26 +01004683 close(0); close(1); close(2);
willy tarreau0f7af912005-12-17 12:21:26 +01004684 }
4685
4686 signal(SIGQUIT, dump);
4687 signal(SIGUSR1, sig_soft_stop);
4688
4689 /* on very high loads, a sigpipe sometimes happen just between the
4690 * getsockopt() which tells "it's OK to write", and the following write :-(
4691 */
willy tarreau3242e862005-12-17 12:27:53 +01004692#ifndef MSG_NOSIGNAL
4693 signal(SIGPIPE, SIG_IGN);
4694#endif
willy tarreau0f7af912005-12-17 12:21:26 +01004695
4696 if (start_proxies() < 0)
4697 exit(1);
4698
willy tarreau9fe663a2005-12-17 13:02:59 +01004699 /* open log files */
4700
4701 /* chroot if needed */
4702 if (global.chroot != NULL) {
4703 if (chroot(global.chroot) == -1) {
4704 Alert("[%s.main()] Cannot chroot(%s).\n", argv[0], global.chroot);
4705 exit(1);
4706 }
4707 chdir("/");
4708 }
4709
4710 /* setgid / setuid */
4711 if (global.gid && setregid(global.gid, global.gid) == -1) {
4712 Alert("[%s.main()] Cannot set gid %d.\n", argv[0], global.gid);
4713 exit(1);
4714 }
4715
4716 if (global.uid && setreuid(global.uid, global.uid) == -1) {
4717 Alert("[%s.main()] Cannot set uid %d.\n", argv[0], global.uid);
4718 exit(1);
4719 }
4720
4721 if (global.mode & MODE_DAEMON) {
4722 int ret = 0;
4723 int proc;
4724
4725 /* the father launches the required number of processes */
4726 for (proc = 0; proc < global.nbproc; proc++) {
4727 ret = fork();
4728 if (ret < 0) {
4729 Alert("[%s.main()] Cannot fork.\n", argv[0]);
4730 exit(1); /* there has been an error */
4731 }
4732 else if (ret == 0) /* child breaks here */
4733 break;
4734 }
4735 if (proc == global.nbproc)
4736 exit(0); /* parent must leave */
4737
willy tarreaua1598082005-12-17 13:08:06 +01004738 pid = getpid(); /* update child's pid */
willy tarreau9fe663a2005-12-17 13:02:59 +01004739 setpgid(1, 0);
4740 }
4741
willy tarreau0f7af912005-12-17 12:21:26 +01004742 select_loop();
4743
4744 exit(0);
4745}
willy tarreaua1598082005-12-17 13:08:06 +01004746