2006-06-25 20:48:02 -04:00
|
|
|
/*
|
|
|
|
|
* HTTP protocol analyzer
|
|
|
|
|
*
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
* Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
|
2006-06-25 20:48:02 -04:00
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <syslog.h>
|
2007-03-31 19:30:43 -04:00
|
|
|
#include <time.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
2006-06-29 11:53:05 -04:00
|
|
|
#include <common/appsession.h>
|
|
|
|
|
#include <common/compat.h>
|
|
|
|
|
#include <common/config.h>
|
2006-12-16 13:57:26 -05:00
|
|
|
#include <common/debug.h>
|
2006-06-29 11:53:05 -04:00
|
|
|
#include <common/memory.h>
|
|
|
|
|
#include <common/mini-clist.h>
|
|
|
|
|
#include <common/standard.h>
|
2008-07-06 18:09:58 -04:00
|
|
|
#include <common/ticks.h>
|
2006-06-29 11:53:05 -04:00
|
|
|
#include <common/time.h>
|
|
|
|
|
#include <common/uri_auth.h>
|
|
|
|
|
#include <common/version.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
#include <types/capture.h>
|
|
|
|
|
#include <types/global.h>
|
|
|
|
|
|
2007-05-06 18:55:35 -04:00
|
|
|
#include <proto/acl.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/backend.h>
|
|
|
|
|
#include <proto/buffers.h>
|
2009-04-17 12:53:21 -04:00
|
|
|
#include <proto/client.h>
|
2007-10-17 11:06:05 -04:00
|
|
|
#include <proto/dumpstats.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/fd.h>
|
|
|
|
|
#include <proto/log.h>
|
2006-12-03 20:26:12 -05:00
|
|
|
#include <proto/hdr_idx.h>
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
#include <proto/proto_tcp.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/proto_http.h>
|
2009-03-05 12:43:00 -05:00
|
|
|
#include <proto/proxy.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/queue.h>
|
2009-03-05 12:43:00 -05:00
|
|
|
#include <proto/server.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/session.h>
|
[MAJOR] add a connection error state to the stream_interface
Tracking connection status changes was hard, and some code was
redundant. A new SI_ST_CER state was added to the stream interface
to indicate a past connection error, and an SI_FL_ERR flag was
added to report past I/O error. The stream_sock code does not set
the connection to SI_ST_CLO anymore in case of I/O error, it's
the upper layer which does it. This makes it possible to know
exactly when the file descriptors are allocated.
The new SI_ST_CER state permitted to split tcp_connection_status()
in two parts, one processing SI_ST_CON and the other one SI_ST_CER.
Synchronous connection errors now make use of this last state, hence
eliminating duplicate code.
Some ib<->ob copy paste errors were found and fixed, and all entities
setting SI_ST_CLO also shut the buffers down.
Some of these stream_interface specific functions and structures
have migrated to a new stream_interface.c file.
Some types of errors are still not detected by the buffers. For
instance, let's assume the following scenario in one single pass
of process_session: a connection sits in SI_ST_TAR state during
a retry. At TAR expiration, a new connection attempt is made, the
connection is obtained and srv->cur_sess is increased. Then the
buffer timeout is fires and everything is cleared, the new state
becomes SI_ST_CLO. The cleaning code checks that previous state
was either SI_ST_CON or SI_ST_EST to release the connection. But
that's wrong because last state is still SI_ST_TAR. So the
server's connection count does not get decreased.
This means that prev_state must not be used, and must be replaced
by some transition detection instead of level detection.
The following debugging line was useful to track state changes :
fprintf(stderr, "%s:%d: cs=%d ss=%d(%d) rqf=0x%08x rpf=0x%08x\n", __FUNCTION__, __LINE__,
s->si[0].state, s->si[1].state, s->si[1].err_type, s->req->flags, s-> rep->flags);
2008-11-03 00:26:53 -05:00
|
|
|
#include <proto/stream_interface.h>
|
2008-08-27 15:41:35 -04:00
|
|
|
#include <proto/stream_sock.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/task.h>
|
|
|
|
|
|
2006-07-09 02:22:27 -04:00
|
|
|
/* This is used by remote monitoring */
|
2006-12-23 14:51:41 -05:00
|
|
|
const char HTTP_200[] =
|
2006-07-09 02:22:27 -04:00
|
|
|
"HTTP/1.0 200 OK\r\n"
|
|
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
|
"Content-Type: text/html\r\n"
|
|
|
|
|
"\r\n"
|
|
|
|
|
"<html><body><h1>200 OK</h1>\nHAProxy: service ready.\n</body></html>\n";
|
|
|
|
|
|
2006-12-23 14:51:41 -05:00
|
|
|
const struct chunk http_200_chunk = {
|
|
|
|
|
.str = (char *)&HTTP_200,
|
|
|
|
|
.len = sizeof(HTTP_200)-1
|
|
|
|
|
};
|
|
|
|
|
|
2008-06-07 17:08:56 -04:00
|
|
|
const char *HTTP_301 =
|
|
|
|
|
"HTTP/1.0 301 Moved Permantenly\r\n"
|
|
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
|
"Location: "; /* not terminated since it will be concatenated with the URL */
|
|
|
|
|
|
2006-12-23 14:51:41 -05:00
|
|
|
const char *HTTP_302 =
|
|
|
|
|
"HTTP/1.0 302 Found\r\n"
|
|
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
|
"Location: "; /* not terminated since it will be concatenated with the URL */
|
|
|
|
|
|
|
|
|
|
/* same as 302 except that the browser MUST retry with the GET method */
|
|
|
|
|
const char *HTTP_303 =
|
|
|
|
|
"HTTP/1.0 303 See Other\r\n"
|
|
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
|
"Location: "; /* not terminated since it will be concatenated with the URL */
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/* Warning: this one is an sprintf() fmt string, with <realm> as its only argument */
|
|
|
|
|
const char *HTTP_401_fmt =
|
|
|
|
|
"HTTP/1.0 401 Unauthorized\r\n"
|
|
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
2006-07-08 10:53:38 -04:00
|
|
|
"Content-Type: text/html\r\n"
|
2006-06-25 20:48:02 -04:00
|
|
|
"WWW-Authenticate: Basic realm=\"%s\"\r\n"
|
|
|
|
|
"\r\n"
|
|
|
|
|
"<html><body><h1>401 Unauthorized</h1>\nYou need a valid user and password to access this content.\n</body></html>\n";
|
|
|
|
|
|
|
|
|
|
|
2006-12-23 14:51:41 -05:00
|
|
|
const int http_err_codes[HTTP_ERR_SIZE] = {
|
|
|
|
|
[HTTP_ERR_400] = 400,
|
|
|
|
|
[HTTP_ERR_403] = 403,
|
|
|
|
|
[HTTP_ERR_408] = 408,
|
|
|
|
|
[HTTP_ERR_500] = 500,
|
|
|
|
|
[HTTP_ERR_502] = 502,
|
|
|
|
|
[HTTP_ERR_503] = 503,
|
|
|
|
|
[HTTP_ERR_504] = 504,
|
|
|
|
|
};
|
|
|
|
|
|
2006-12-24 11:47:20 -05:00
|
|
|
static const char *http_err_msgs[HTTP_ERR_SIZE] = {
|
2006-12-23 14:51:41 -05:00
|
|
|
[HTTP_ERR_400] =
|
2006-12-24 11:47:20 -05:00
|
|
|
"HTTP/1.0 400 Bad request\r\n"
|
2006-12-23 14:51:41 -05:00
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
|
"Content-Type: text/html\r\n"
|
|
|
|
|
"\r\n"
|
|
|
|
|
"<html><body><h1>400 Bad request</h1>\nYour browser sent an invalid request.\n</body></html>\n",
|
|
|
|
|
|
|
|
|
|
[HTTP_ERR_403] =
|
|
|
|
|
"HTTP/1.0 403 Forbidden\r\n"
|
|
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
|
"Content-Type: text/html\r\n"
|
|
|
|
|
"\r\n"
|
|
|
|
|
"<html><body><h1>403 Forbidden</h1>\nRequest forbidden by administrative rules.\n</body></html>\n",
|
|
|
|
|
|
|
|
|
|
[HTTP_ERR_408] =
|
|
|
|
|
"HTTP/1.0 408 Request Time-out\r\n"
|
|
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
|
"Content-Type: text/html\r\n"
|
|
|
|
|
"\r\n"
|
|
|
|
|
"<html><body><h1>408 Request Time-out</h1>\nYour browser didn't send a complete request in time.\n</body></html>\n",
|
|
|
|
|
|
|
|
|
|
[HTTP_ERR_500] =
|
|
|
|
|
"HTTP/1.0 500 Server Error\r\n"
|
|
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
|
"Content-Type: text/html\r\n"
|
|
|
|
|
"\r\n"
|
|
|
|
|
"<html><body><h1>500 Server Error</h1>\nAn internal server error occured.\n</body></html>\n",
|
|
|
|
|
|
|
|
|
|
[HTTP_ERR_502] =
|
|
|
|
|
"HTTP/1.0 502 Bad Gateway\r\n"
|
|
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
|
"Content-Type: text/html\r\n"
|
|
|
|
|
"\r\n"
|
|
|
|
|
"<html><body><h1>502 Bad Gateway</h1>\nThe server returned an invalid or incomplete response.\n</body></html>\n",
|
|
|
|
|
|
|
|
|
|
[HTTP_ERR_503] =
|
|
|
|
|
"HTTP/1.0 503 Service Unavailable\r\n"
|
|
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
|
"Content-Type: text/html\r\n"
|
|
|
|
|
"\r\n"
|
|
|
|
|
"<html><body><h1>503 Service Unavailable</h1>\nNo server is available to handle this request.\n</body></html>\n",
|
|
|
|
|
|
|
|
|
|
[HTTP_ERR_504] =
|
|
|
|
|
"HTTP/1.0 504 Gateway Time-out\r\n"
|
|
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
|
"Content-Type: text/html\r\n"
|
|
|
|
|
"\r\n"
|
|
|
|
|
"<html><body><h1>504 Gateway Time-out</h1>\nThe server didn't respond in time.\n</body></html>\n",
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
2006-12-24 11:47:20 -05:00
|
|
|
/* We must put the messages here since GCC cannot initialize consts depending
|
|
|
|
|
* on strlen().
|
|
|
|
|
*/
|
|
|
|
|
struct chunk http_err_chunks[HTTP_ERR_SIZE];
|
|
|
|
|
|
2007-03-31 19:30:43 -04:00
|
|
|
#define FD_SETS_ARE_BITFIELDS
|
|
|
|
|
#ifdef FD_SETS_ARE_BITFIELDS
|
|
|
|
|
/*
|
|
|
|
|
* This map is used with all the FD_* macros to check whether a particular bit
|
|
|
|
|
* is set or not. Each bit represents an ACSII code. FD_SET() sets those bytes
|
|
|
|
|
* which should be encoded. When FD_ISSET() returns non-zero, it means that the
|
|
|
|
|
* byte should be encoded. Be careful to always pass bytes from 0 to 255
|
|
|
|
|
* exclusively to the macros.
|
|
|
|
|
*/
|
|
|
|
|
fd_set hdr_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
|
|
|
|
|
fd_set url_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
#error "Check if your OS uses bitfields for fd_sets"
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-12-24 11:47:20 -05:00
|
|
|
void init_proto_http()
|
|
|
|
|
{
|
2007-03-31 19:30:43 -04:00
|
|
|
int i;
|
|
|
|
|
char *tmp;
|
2006-12-24 11:47:20 -05:00
|
|
|
int msg;
|
2007-03-31 19:30:43 -04:00
|
|
|
|
2006-12-24 11:47:20 -05:00
|
|
|
for (msg = 0; msg < HTTP_ERR_SIZE; msg++) {
|
|
|
|
|
if (!http_err_msgs[msg]) {
|
|
|
|
|
Alert("Internal error: no message defined for HTTP return code %d. Aborting.\n", msg);
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
http_err_chunks[msg].str = (char *)http_err_msgs[msg];
|
|
|
|
|
http_err_chunks[msg].len = strlen(http_err_msgs[msg]);
|
|
|
|
|
}
|
2007-03-31 19:30:43 -04:00
|
|
|
|
|
|
|
|
/* initialize the log header encoding map : '{|}"#' should be encoded with
|
|
|
|
|
* '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
|
|
|
|
|
* URL encoding only requires '"', '#' to be encoded as well as non-
|
|
|
|
|
* printable characters above.
|
|
|
|
|
*/
|
|
|
|
|
memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
|
|
|
|
|
memset(url_encode_map, 0, sizeof(url_encode_map));
|
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
|
FD_SET(i, hdr_encode_map);
|
|
|
|
|
FD_SET(i, url_encode_map);
|
|
|
|
|
}
|
|
|
|
|
for (i = 127; i < 256; i++) {
|
|
|
|
|
FD_SET(i, hdr_encode_map);
|
|
|
|
|
FD_SET(i, url_encode_map);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp = "\"#{|}";
|
|
|
|
|
while (*tmp) {
|
|
|
|
|
FD_SET(*tmp, hdr_encode_map);
|
|
|
|
|
tmp++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp = "\"#";
|
|
|
|
|
while (*tmp) {
|
|
|
|
|
FD_SET(*tmp, url_encode_map);
|
|
|
|
|
tmp++;
|
|
|
|
|
}
|
2007-05-13 15:36:56 -04:00
|
|
|
|
|
|
|
|
/* memory allocations */
|
|
|
|
|
pool2_requri = create_pool("requri", REQURI_LEN, MEM_F_SHARED);
|
2007-05-13 15:45:51 -04:00
|
|
|
pool2_capture = create_pool("capture", CAPTURE_LEN, MEM_F_SHARED);
|
2006-12-24 11:47:20 -05:00
|
|
|
}
|
2006-12-23 14:51:41 -05:00
|
|
|
|
2006-12-17 07:37:46 -05:00
|
|
|
/*
|
|
|
|
|
* We have 26 list of methods (1 per first letter), each of which can have
|
|
|
|
|
* up to 3 entries (2 valid, 1 null).
|
|
|
|
|
*/
|
|
|
|
|
struct http_method_desc {
|
|
|
|
|
http_meth_t meth;
|
|
|
|
|
int len;
|
|
|
|
|
const char text[8];
|
|
|
|
|
};
|
|
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
const struct http_method_desc http_methods[26][3] = {
|
2006-12-17 07:37:46 -05:00
|
|
|
['C' - 'A'] = {
|
|
|
|
|
[0] = { .meth = HTTP_METH_CONNECT , .len=7, .text="CONNECT" },
|
|
|
|
|
},
|
|
|
|
|
['D' - 'A'] = {
|
|
|
|
|
[0] = { .meth = HTTP_METH_DELETE , .len=6, .text="DELETE" },
|
|
|
|
|
},
|
|
|
|
|
['G' - 'A'] = {
|
|
|
|
|
[0] = { .meth = HTTP_METH_GET , .len=3, .text="GET" },
|
|
|
|
|
},
|
|
|
|
|
['H' - 'A'] = {
|
|
|
|
|
[0] = { .meth = HTTP_METH_HEAD , .len=4, .text="HEAD" },
|
|
|
|
|
},
|
|
|
|
|
['P' - 'A'] = {
|
|
|
|
|
[0] = { .meth = HTTP_METH_POST , .len=4, .text="POST" },
|
|
|
|
|
[1] = { .meth = HTTP_METH_PUT , .len=3, .text="PUT" },
|
|
|
|
|
},
|
|
|
|
|
['T' - 'A'] = {
|
|
|
|
|
[0] = { .meth = HTTP_METH_TRACE , .len=5, .text="TRACE" },
|
|
|
|
|
},
|
|
|
|
|
/* rest is empty like this :
|
|
|
|
|
* [1] = { .meth = HTTP_METH_NONE , .len=0, .text="" },
|
|
|
|
|
*/
|
|
|
|
|
};
|
|
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
/* It is about twice as fast on recent architectures to lookup a byte in a
|
2008-04-14 14:47:37 -04:00
|
|
|
* table than to perform a boolean AND or OR between two tests. Refer to
|
2007-01-21 13:16:41 -05:00
|
|
|
* RFC2616 for those chars.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
const char http_is_spht[256] = {
|
|
|
|
|
[' '] = 1, ['\t'] = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char http_is_crlf[256] = {
|
|
|
|
|
['\r'] = 1, ['\n'] = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char http_is_lws[256] = {
|
|
|
|
|
[' '] = 1, ['\t'] = 1,
|
|
|
|
|
['\r'] = 1, ['\n'] = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char http_is_sep[256] = {
|
|
|
|
|
['('] = 1, [')'] = 1, ['<'] = 1, ['>'] = 1,
|
|
|
|
|
['@'] = 1, [','] = 1, [';'] = 1, [':'] = 1,
|
|
|
|
|
['"'] = 1, ['/'] = 1, ['['] = 1, [']'] = 1,
|
|
|
|
|
['{'] = 1, ['}'] = 1, ['?'] = 1, ['='] = 1,
|
|
|
|
|
[' '] = 1, ['\t'] = 1, ['\\'] = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char http_is_ctl[256] = {
|
|
|
|
|
[0 ... 31] = 1,
|
|
|
|
|
[127] = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* A token is any ASCII char that is neither a separator nor a CTL char.
|
|
|
|
|
* Do not overwrite values in assignment since gcc-2.95 will not handle
|
|
|
|
|
* them correctly. Instead, define every non-CTL char's status.
|
|
|
|
|
*/
|
|
|
|
|
const char http_is_token[256] = {
|
|
|
|
|
[' '] = 0, ['!'] = 1, ['"'] = 0, ['#'] = 1,
|
|
|
|
|
['$'] = 1, ['%'] = 1, ['&'] = 1, ['\''] = 1,
|
|
|
|
|
['('] = 0, [')'] = 0, ['*'] = 1, ['+'] = 1,
|
|
|
|
|
[','] = 0, ['-'] = 1, ['.'] = 1, ['/'] = 0,
|
|
|
|
|
['0'] = 1, ['1'] = 1, ['2'] = 1, ['3'] = 1,
|
|
|
|
|
['4'] = 1, ['5'] = 1, ['6'] = 1, ['7'] = 1,
|
|
|
|
|
['8'] = 1, ['9'] = 1, [':'] = 0, [';'] = 0,
|
|
|
|
|
['<'] = 0, ['='] = 0, ['>'] = 0, ['?'] = 0,
|
|
|
|
|
['@'] = 0, ['A'] = 1, ['B'] = 1, ['C'] = 1,
|
|
|
|
|
['D'] = 1, ['E'] = 1, ['F'] = 1, ['G'] = 1,
|
|
|
|
|
['H'] = 1, ['I'] = 1, ['J'] = 1, ['K'] = 1,
|
|
|
|
|
['L'] = 1, ['M'] = 1, ['N'] = 1, ['O'] = 1,
|
|
|
|
|
['P'] = 1, ['Q'] = 1, ['R'] = 1, ['S'] = 1,
|
|
|
|
|
['T'] = 1, ['U'] = 1, ['V'] = 1, ['W'] = 1,
|
|
|
|
|
['X'] = 1, ['Y'] = 1, ['Z'] = 1, ['['] = 0,
|
|
|
|
|
['\\'] = 0, [']'] = 0, ['^'] = 1, ['_'] = 1,
|
|
|
|
|
['`'] = 1, ['a'] = 1, ['b'] = 1, ['c'] = 1,
|
|
|
|
|
['d'] = 1, ['e'] = 1, ['f'] = 1, ['g'] = 1,
|
|
|
|
|
['h'] = 1, ['i'] = 1, ['j'] = 1, ['k'] = 1,
|
|
|
|
|
['l'] = 1, ['m'] = 1, ['n'] = 1, ['o'] = 1,
|
|
|
|
|
['p'] = 1, ['q'] = 1, ['r'] = 1, ['s'] = 1,
|
|
|
|
|
['t'] = 1, ['u'] = 1, ['v'] = 1, ['w'] = 1,
|
|
|
|
|
['x'] = 1, ['y'] = 1, ['z'] = 1, ['{'] = 0,
|
|
|
|
|
['|'] = 1, ['}'] = 0, ['~'] = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2007-03-04 12:13:58 -05:00
|
|
|
/*
|
|
|
|
|
* An http ver_token is any ASCII which can be found in an HTTP version,
|
|
|
|
|
* which includes 'H', 'T', 'P', '/', '.' and any digit.
|
|
|
|
|
*/
|
|
|
|
|
const char http_is_ver_token[256] = {
|
|
|
|
|
['.'] = 1, ['/'] = 1,
|
|
|
|
|
['0'] = 1, ['1'] = 1, ['2'] = 1, ['3'] = 1, ['4'] = 1,
|
|
|
|
|
['5'] = 1, ['6'] = 1, ['7'] = 1, ['8'] = 1, ['9'] = 1,
|
|
|
|
|
['H'] = 1, ['P'] = 1, ['T'] = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2007-03-18 17:36:26 -04:00
|
|
|
/*
|
|
|
|
|
* Adds a header and its CRLF at the tail of buffer <b>, just before the last
|
|
|
|
|
* CRLF. Text length is measured first, so it cannot be NULL.
|
|
|
|
|
* The header is also automatically added to the index <hdr_idx>, and the end
|
|
|
|
|
* of headers is automatically adjusted. The number of bytes added is returned
|
|
|
|
|
* on success, otherwise <0 is returned indicating an error.
|
|
|
|
|
*/
|
|
|
|
|
int http_header_add_tail(struct buffer *b, struct http_msg *msg,
|
|
|
|
|
struct hdr_idx *hdr_idx, const char *text)
|
|
|
|
|
{
|
|
|
|
|
int bytes, len;
|
|
|
|
|
|
|
|
|
|
len = strlen(text);
|
|
|
|
|
bytes = buffer_insert_line2(b, b->data + msg->eoh, text, len);
|
|
|
|
|
if (!bytes)
|
|
|
|
|
return -1;
|
|
|
|
|
msg->eoh += bytes;
|
|
|
|
|
return hdr_idx_add(len, 1, hdr_idx, hdr_idx->tail);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Adds a header and its CRLF at the tail of buffer <b>, just before the last
|
|
|
|
|
* CRLF. <len> bytes are copied, not counting the CRLF. If <text> is NULL, then
|
|
|
|
|
* the buffer is only opened and the space reserved, but nothing is copied.
|
|
|
|
|
* The header is also automatically added to the index <hdr_idx>, and the end
|
|
|
|
|
* of headers is automatically adjusted. The number of bytes added is returned
|
|
|
|
|
* on success, otherwise <0 is returned indicating an error.
|
|
|
|
|
*/
|
|
|
|
|
int http_header_add_tail2(struct buffer *b, struct http_msg *msg,
|
|
|
|
|
struct hdr_idx *hdr_idx, const char *text, int len)
|
|
|
|
|
{
|
|
|
|
|
int bytes;
|
|
|
|
|
|
|
|
|
|
bytes = buffer_insert_line2(b, b->data + msg->eoh, text, len);
|
|
|
|
|
if (!bytes)
|
|
|
|
|
return -1;
|
|
|
|
|
msg->eoh += bytes;
|
|
|
|
|
return hdr_idx_add(len, 1, hdr_idx, hdr_idx->tail);
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2007-03-18 18:50:16 -04:00
|
|
|
/*
|
|
|
|
|
* Checks if <hdr> is exactly <name> for <len> chars, and ends with a colon.
|
|
|
|
|
* If so, returns the position of the first non-space character relative to
|
|
|
|
|
* <hdr>, or <end>-<hdr> if not found before. If no value is found, it tries
|
|
|
|
|
* to return a pointer to the place after the first space. Returns 0 if the
|
|
|
|
|
* header name does not match. Checks are case-insensitive.
|
|
|
|
|
*/
|
|
|
|
|
int http_header_match2(const char *hdr, const char *end,
|
|
|
|
|
const char *name, int len)
|
|
|
|
|
{
|
|
|
|
|
const char *val;
|
|
|
|
|
|
|
|
|
|
if (hdr + len >= end)
|
|
|
|
|
return 0;
|
|
|
|
|
if (hdr[len] != ':')
|
|
|
|
|
return 0;
|
|
|
|
|
if (strncasecmp(hdr, name, len) != 0)
|
|
|
|
|
return 0;
|
|
|
|
|
val = hdr + len + 1;
|
|
|
|
|
while (val < end && HTTP_IS_SPHT(*val))
|
|
|
|
|
val++;
|
|
|
|
|
if ((val >= end) && (len + 2 <= end - hdr))
|
|
|
|
|
return len + 2; /* we may replace starting from second space */
|
|
|
|
|
return val - hdr;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-10 13:45:56 -04:00
|
|
|
/* Find the end of the header value contained between <s> and <e>.
|
|
|
|
|
* See RFC2616, par 2.2 for more information. Note that it requires
|
|
|
|
|
* a valid header to return a valid result.
|
|
|
|
|
*/
|
|
|
|
|
const char *find_hdr_value_end(const char *s, const char *e)
|
|
|
|
|
{
|
|
|
|
|
int quoted, qdpair;
|
|
|
|
|
|
|
|
|
|
quoted = qdpair = 0;
|
|
|
|
|
for (; s < e; s++) {
|
|
|
|
|
if (qdpair) qdpair = 0;
|
|
|
|
|
else if (quoted && *s == '\\') qdpair = 1;
|
|
|
|
|
else if (quoted && *s == '"') quoted = 0;
|
|
|
|
|
else if (*s == '"') quoted = 1;
|
|
|
|
|
else if (*s == ',') return s;
|
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find the first or next occurrence of header <name> in message buffer <sol>
|
|
|
|
|
* using headers index <idx>, and return it in the <ctx> structure. This
|
|
|
|
|
* structure holds everything necessary to use the header and find next
|
|
|
|
|
* occurrence. If its <idx> member is 0, the header is searched from the
|
|
|
|
|
* beginning. Otherwise, the next occurrence is returned. The function returns
|
|
|
|
|
* 1 when it finds a value, and 0 when there is no more.
|
|
|
|
|
*/
|
|
|
|
|
int http_find_header2(const char *name, int len,
|
|
|
|
|
const char *sol, struct hdr_idx *idx,
|
|
|
|
|
struct hdr_ctx *ctx)
|
|
|
|
|
{
|
|
|
|
|
const char *eol, *sov;
|
|
|
|
|
int cur_idx;
|
|
|
|
|
|
|
|
|
|
if (ctx->idx) {
|
|
|
|
|
/* We have previously returned a value, let's search
|
|
|
|
|
* another one on the same line.
|
|
|
|
|
*/
|
|
|
|
|
cur_idx = ctx->idx;
|
|
|
|
|
sol = ctx->line;
|
|
|
|
|
sov = sol + ctx->val + ctx->vlen;
|
|
|
|
|
eol = sol + idx->v[cur_idx].len;
|
|
|
|
|
|
|
|
|
|
if (sov >= eol)
|
|
|
|
|
/* no more values in this header */
|
|
|
|
|
goto next_hdr;
|
|
|
|
|
|
|
|
|
|
/* values remaining for this header, skip the comma */
|
|
|
|
|
sov++;
|
|
|
|
|
while (sov < eol && http_is_lws[(unsigned char)*sov])
|
|
|
|
|
sov++;
|
|
|
|
|
|
|
|
|
|
goto return_hdr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* first request for this header */
|
|
|
|
|
sol += hdr_idx_first_pos(idx);
|
|
|
|
|
cur_idx = hdr_idx_first_idx(idx);
|
|
|
|
|
|
|
|
|
|
while (cur_idx) {
|
|
|
|
|
eol = sol + idx->v[cur_idx].len;
|
|
|
|
|
|
2007-06-10 15:42:55 -04:00
|
|
|
if (len == 0) {
|
|
|
|
|
/* No argument was passed, we want any header.
|
|
|
|
|
* To achieve this, we simply build a fake request. */
|
|
|
|
|
while (sol + len < eol && sol[len] != ':')
|
|
|
|
|
len++;
|
|
|
|
|
name = sol;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-10 13:45:56 -04:00
|
|
|
if ((len < eol - sol) &&
|
|
|
|
|
(sol[len] == ':') &&
|
|
|
|
|
(strncasecmp(sol, name, len) == 0)) {
|
|
|
|
|
|
|
|
|
|
sov = sol + len + 1;
|
|
|
|
|
while (sov < eol && http_is_lws[(unsigned char)*sov])
|
|
|
|
|
sov++;
|
|
|
|
|
return_hdr:
|
|
|
|
|
ctx->line = sol;
|
|
|
|
|
ctx->idx = cur_idx;
|
|
|
|
|
ctx->val = sov - sol;
|
|
|
|
|
|
|
|
|
|
eol = find_hdr_value_end(sov, eol);
|
|
|
|
|
ctx->vlen = eol - sov;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
next_hdr:
|
|
|
|
|
sol = eol + idx->v[cur_idx].cr + 1;
|
|
|
|
|
cur_idx = idx->v[cur_idx].next;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int http_find_header(const char *name,
|
|
|
|
|
const char *sol, struct hdr_idx *idx,
|
|
|
|
|
struct hdr_ctx *ctx)
|
|
|
|
|
{
|
|
|
|
|
return http_find_header2(name, strlen(name), sol, idx, ctx);
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-30 14:20:08 -05:00
|
|
|
/* This function handles a server error at the stream interface level. The
|
|
|
|
|
* stream interface is assumed to be already in a closed state. An optional
|
|
|
|
|
* message is copied into the input buffer, and an HTTP status code stored.
|
|
|
|
|
* The error flags are set to the values in arguments. Any pending request
|
2009-03-08 15:33:29 -04:00
|
|
|
* in this buffer will be lost.
|
2006-06-25 20:48:02 -04:00
|
|
|
*/
|
2008-11-30 14:20:08 -05:00
|
|
|
static void http_server_error(struct session *t, struct stream_interface *si,
|
|
|
|
|
int err, int finst, int status, const struct chunk *msg)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
2009-03-08 15:33:29 -04:00
|
|
|
buffer_erase(si->ob);
|
|
|
|
|
buffer_erase(si->ib);
|
2009-09-19 15:04:57 -04:00
|
|
|
buffer_auto_close(si->ib);
|
2006-12-23 14:51:41 -05:00
|
|
|
if (status > 0 && msg) {
|
2007-03-18 12:31:28 -04:00
|
|
|
t->txn.status = status;
|
2008-11-30 14:20:08 -05:00
|
|
|
buffer_write(si->ib, msg->str, msg->len);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
if (!(t->flags & SN_ERR_MASK))
|
|
|
|
|
t->flags |= err;
|
|
|
|
|
if (!(t->flags & SN_FINST_MASK))
|
|
|
|
|
t->flags |= finst;
|
|
|
|
|
}
|
|
|
|
|
|
2006-12-24 11:47:20 -05:00
|
|
|
/* This function returns the appropriate error location for the given session
|
|
|
|
|
* and message.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
struct chunk *error_message(struct session *s, int msgnum)
|
|
|
|
|
{
|
2007-03-31 18:01:37 -04:00
|
|
|
if (s->be->errmsg[msgnum].str)
|
|
|
|
|
return &s->be->errmsg[msgnum];
|
2006-12-24 11:47:20 -05:00
|
|
|
else if (s->fe->errmsg[msgnum].str)
|
|
|
|
|
return &s->fe->errmsg[msgnum];
|
|
|
|
|
else
|
|
|
|
|
return &http_err_chunks[msgnum];
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2006-12-17 07:37:46 -05:00
|
|
|
/*
|
|
|
|
|
* returns HTTP_METH_NONE if there is nothing valid to read (empty or non-text
|
|
|
|
|
* string), HTTP_METH_OTHER for unknown methods, or the identified method.
|
|
|
|
|
*/
|
|
|
|
|
static http_meth_t find_http_meth(const char *str, const int len)
|
|
|
|
|
{
|
|
|
|
|
unsigned char m;
|
2007-01-21 13:16:41 -05:00
|
|
|
const struct http_method_desc *h;
|
2006-12-17 07:37:46 -05:00
|
|
|
|
|
|
|
|
m = ((unsigned)*str - 'A');
|
|
|
|
|
|
|
|
|
|
if (m < 26) {
|
2007-01-21 13:16:41 -05:00
|
|
|
for (h = http_methods[m]; h->len > 0; h++) {
|
|
|
|
|
if (unlikely(h->len != len))
|
2006-12-17 07:37:46 -05:00
|
|
|
continue;
|
2007-01-21 13:16:41 -05:00
|
|
|
if (likely(memcmp(str, h->text, h->len) == 0))
|
2006-12-17 07:37:46 -05:00
|
|
|
return h->meth;
|
|
|
|
|
};
|
|
|
|
|
return HTTP_METH_OTHER;
|
|
|
|
|
}
|
|
|
|
|
return HTTP_METH_NONE;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2008-02-14 14:25:24 -05:00
|
|
|
/* Parse the URI from the given transaction (which is assumed to be in request
|
|
|
|
|
* phase) and look for the "/" beginning the PATH. If not found, return NULL.
|
|
|
|
|
* It is returned otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static char *
|
|
|
|
|
http_get_path(struct http_txn *txn)
|
|
|
|
|
{
|
|
|
|
|
char *ptr, *end;
|
|
|
|
|
|
|
|
|
|
ptr = txn->req.sol + txn->req.sl.rq.u;
|
|
|
|
|
end = ptr + txn->req.sl.rq.u_l;
|
|
|
|
|
|
|
|
|
|
if (ptr >= end)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* RFC2616, par. 5.1.2 :
|
|
|
|
|
* Request-URI = "*" | absuri | abspath | authority
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (*ptr == '*')
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if (isalpha((unsigned char)*ptr)) {
|
|
|
|
|
/* this is a scheme as described by RFC3986, par. 3.1 */
|
|
|
|
|
ptr++;
|
|
|
|
|
while (ptr < end &&
|
|
|
|
|
(isalnum((unsigned char)*ptr) || *ptr == '+' || *ptr == '-' || *ptr == '.'))
|
|
|
|
|
ptr++;
|
|
|
|
|
/* skip '://' */
|
|
|
|
|
if (ptr == end || *ptr++ != ':')
|
|
|
|
|
return NULL;
|
|
|
|
|
if (ptr == end || *ptr++ != '/')
|
|
|
|
|
return NULL;
|
|
|
|
|
if (ptr == end || *ptr++ != '/')
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
/* skip [user[:passwd]@]host[:[port]] */
|
|
|
|
|
|
|
|
|
|
while (ptr < end && *ptr != '/')
|
|
|
|
|
ptr++;
|
|
|
|
|
|
|
|
|
|
if (ptr == end)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* OK, we got the '/' ! */
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-26 15:49:47 -04:00
|
|
|
/* Returns a 302 for a redirectable request. This may only be called just after
|
|
|
|
|
* the stream interface has moved to SI_ST_ASS. Unprocessable requests are
|
|
|
|
|
* left unchanged and will follow normal proxy processing.
|
|
|
|
|
*/
|
2008-11-30 12:47:21 -05:00
|
|
|
void perform_http_redirect(struct session *s, struct stream_interface *si)
|
2008-10-26 15:49:47 -04:00
|
|
|
{
|
|
|
|
|
struct http_txn *txn;
|
|
|
|
|
struct chunk rdr;
|
|
|
|
|
char *path;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
/* 1: create the response header */
|
|
|
|
|
rdr.len = strlen(HTTP_302);
|
|
|
|
|
rdr.str = trash;
|
|
|
|
|
memcpy(rdr.str, HTTP_302, rdr.len);
|
|
|
|
|
|
|
|
|
|
/* 2: add the server's prefix */
|
2009-09-27 07:23:20 -04:00
|
|
|
if (rdr.len + s->srv->rdr_len > rdr.size)
|
2008-10-26 15:49:47 -04:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
memcpy(rdr.str + rdr.len, s->srv->rdr_pfx, s->srv->rdr_len);
|
|
|
|
|
rdr.len += s->srv->rdr_len;
|
|
|
|
|
|
|
|
|
|
/* 3: add the request URI */
|
|
|
|
|
txn = &s->txn;
|
|
|
|
|
path = http_get_path(txn);
|
|
|
|
|
if (!path)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
len = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path;
|
2009-09-27 07:23:20 -04:00
|
|
|
if (rdr.len + len > rdr.size - 4) /* 4 for CRLF-CRLF */
|
2008-10-26 15:49:47 -04:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
memcpy(rdr.str + rdr.len, path, len);
|
|
|
|
|
rdr.len += len;
|
|
|
|
|
memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
|
|
|
|
|
rdr.len += 4;
|
|
|
|
|
|
|
|
|
|
/* prepare to return without error. */
|
2008-11-27 04:30:51 -05:00
|
|
|
si->shutr(si);
|
|
|
|
|
si->shutw(si);
|
2008-10-26 15:49:47 -04:00
|
|
|
si->err_type = SI_ET_NONE;
|
|
|
|
|
si->err_loc = NULL;
|
|
|
|
|
si->state = SI_ST_CLO;
|
|
|
|
|
|
|
|
|
|
/* send the message */
|
2008-11-30 14:20:08 -05:00
|
|
|
http_server_error(s, si, SN_ERR_PRXCOND, SN_FINST_C, 302, &rdr);
|
2008-10-26 15:49:47 -04:00
|
|
|
|
|
|
|
|
/* FIXME: we should increase a counter of redirects per server and per backend. */
|
|
|
|
|
if (s->srv)
|
2009-03-05 12:43:00 -05:00
|
|
|
srv_inc_sess_ctr(s->srv);
|
2008-10-26 15:49:47 -04:00
|
|
|
}
|
|
|
|
|
|
2008-11-30 14:44:17 -05:00
|
|
|
/* Return the error message corresponding to si->err_type. It is assumed
|
2008-10-26 15:49:47 -04:00
|
|
|
* that the server side is closed. Note that err_type is actually a
|
|
|
|
|
* bitmask, where almost only aborts may be cumulated with other
|
|
|
|
|
* values. We consider that aborted operations are more important
|
|
|
|
|
* than timeouts or errors due to the fact that nobody else in the
|
|
|
|
|
* logs might explain incomplete retries. All others should avoid
|
|
|
|
|
* being cumulated. It should normally not be possible to have multiple
|
|
|
|
|
* aborts at once, but just in case, the first one in sequence is reported.
|
|
|
|
|
*/
|
2008-11-30 14:44:17 -05:00
|
|
|
void http_return_srv_error(struct session *s, struct stream_interface *si)
|
2008-10-26 15:49:47 -04:00
|
|
|
{
|
2008-11-30 14:44:17 -05:00
|
|
|
int err_type = si->err_type;
|
2008-10-26 15:49:47 -04:00
|
|
|
|
|
|
|
|
if (err_type & SI_ET_QUEUE_ABRT)
|
2008-11-30 14:20:08 -05:00
|
|
|
http_server_error(s, si, SN_ERR_CLICL, SN_FINST_Q,
|
|
|
|
|
503, error_message(s, HTTP_ERR_503));
|
2008-10-26 15:49:47 -04:00
|
|
|
else if (err_type & SI_ET_CONN_ABRT)
|
2008-11-30 14:20:08 -05:00
|
|
|
http_server_error(s, si, SN_ERR_CLICL, SN_FINST_C,
|
|
|
|
|
503, error_message(s, HTTP_ERR_503));
|
2008-10-26 15:49:47 -04:00
|
|
|
else if (err_type & SI_ET_QUEUE_TO)
|
2008-11-30 14:20:08 -05:00
|
|
|
http_server_error(s, si, SN_ERR_SRVTO, SN_FINST_Q,
|
|
|
|
|
503, error_message(s, HTTP_ERR_503));
|
2008-10-26 15:49:47 -04:00
|
|
|
else if (err_type & SI_ET_QUEUE_ERR)
|
2008-11-30 14:20:08 -05:00
|
|
|
http_server_error(s, si, SN_ERR_SRVCL, SN_FINST_Q,
|
|
|
|
|
503, error_message(s, HTTP_ERR_503));
|
2008-10-26 15:49:47 -04:00
|
|
|
else if (err_type & SI_ET_CONN_TO)
|
2008-11-30 14:20:08 -05:00
|
|
|
http_server_error(s, si, SN_ERR_SRVTO, SN_FINST_C,
|
|
|
|
|
503, error_message(s, HTTP_ERR_503));
|
2008-10-26 15:49:47 -04:00
|
|
|
else if (err_type & SI_ET_CONN_ERR)
|
2008-11-30 14:20:08 -05:00
|
|
|
http_server_error(s, si, SN_ERR_SRVCL, SN_FINST_C,
|
|
|
|
|
503, error_message(s, HTTP_ERR_503));
|
2008-10-26 15:49:47 -04:00
|
|
|
else /* SI_ET_CONN_OTHER and others */
|
2008-11-30 14:20:08 -05:00
|
|
|
http_server_error(s, si, SN_ERR_INTERNAL, SN_FINST_C,
|
|
|
|
|
500, error_message(s, HTTP_ERR_500));
|
2008-10-26 15:49:47 -04:00
|
|
|
}
|
|
|
|
|
|
2007-03-31 19:30:43 -04:00
|
|
|
extern const char sess_term_cond[8];
|
|
|
|
|
extern const char sess_fin_state[8];
|
|
|
|
|
extern const char *monthname[12];
|
|
|
|
|
const char sess_cookie[4] = "NIDV"; /* No cookie, Invalid cookie, cookie for a Down server, Valid cookie */
|
|
|
|
|
const char sess_set_cookie[8] = "N1I3PD5R"; /* No set-cookie, unknown, Set-Cookie Inserted, unknown,
|
|
|
|
|
Set-cookie seen and left unchanged (passive), Set-cookie Deleted,
|
|
|
|
|
unknown, Set-cookie Rewritten */
|
2007-05-13 15:36:56 -04:00
|
|
|
struct pool_head *pool2_requri;
|
2007-05-13 15:45:51 -04:00
|
|
|
struct pool_head *pool2_capture;
|
2007-01-21 13:16:41 -05:00
|
|
|
|
2009-06-30 12:26:00 -04:00
|
|
|
void http_sess_clflog(struct session *s)
|
|
|
|
|
{
|
|
|
|
|
char pn[INET6_ADDRSTRLEN + strlen(":65535")];
|
|
|
|
|
struct proxy *fe = s->fe;
|
|
|
|
|
struct proxy *be = s->be;
|
|
|
|
|
struct proxy *prx_log;
|
|
|
|
|
struct http_txn *txn = &s->txn;
|
|
|
|
|
int tolog, level, err;
|
|
|
|
|
char *uri, *h;
|
|
|
|
|
char *svid;
|
|
|
|
|
struct tm tm;
|
|
|
|
|
static char tmpline[MAX_SYSLOG_LEN];
|
|
|
|
|
int hdr;
|
|
|
|
|
size_t w;
|
|
|
|
|
int t_request;
|
|
|
|
|
|
|
|
|
|
prx_log = fe;
|
|
|
|
|
err = (s->flags & (SN_ERR_MASK | SN_REDISP)) ||
|
|
|
|
|
(s->conn_retries != be->conn_retries) ||
|
|
|
|
|
txn->status >= 500;
|
|
|
|
|
|
|
|
|
|
if (s->cli_addr.ss_family == AF_INET)
|
|
|
|
|
inet_ntop(AF_INET,
|
|
|
|
|
(const void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr,
|
|
|
|
|
pn, sizeof(pn));
|
|
|
|
|
else
|
|
|
|
|
inet_ntop(AF_INET6,
|
|
|
|
|
(const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr,
|
|
|
|
|
pn, sizeof(pn));
|
|
|
|
|
|
|
|
|
|
get_gmtime(s->logs.accept_date.tv_sec, &tm);
|
|
|
|
|
|
|
|
|
|
/* FIXME: let's limit ourselves to frontend logging for now. */
|
|
|
|
|
tolog = fe->to_log;
|
|
|
|
|
|
|
|
|
|
h = tmpline;
|
|
|
|
|
|
|
|
|
|
w = snprintf(h, sizeof(tmpline),
|
|
|
|
|
"%s - - [%02d/%s/%04d:%02d:%02d:%02d +0000]",
|
|
|
|
|
pn,
|
|
|
|
|
tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
|
|
|
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
|
|
|
if (w < 0 || w >= sizeof(tmpline) - (h - tmpline))
|
|
|
|
|
goto trunc;
|
|
|
|
|
h += w;
|
|
|
|
|
|
|
|
|
|
if (h >= tmpline + sizeof(tmpline) - 4)
|
|
|
|
|
goto trunc;
|
|
|
|
|
|
|
|
|
|
*(h++) = ' ';
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
uri = txn->uri ? txn->uri : "<BADREQ>";
|
|
|
|
|
h = encode_string(h, tmpline + sizeof(tmpline) - 1,
|
|
|
|
|
'#', url_encode_map, uri);
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
|
|
|
|
|
w = snprintf(h, sizeof(tmpline) - (h - tmpline), " %d %lld", txn->status, s->logs.bytes_out);
|
|
|
|
|
if (w < 0 || w >= sizeof(tmpline) - (h - tmpline))
|
|
|
|
|
goto trunc;
|
|
|
|
|
h += w;
|
|
|
|
|
|
|
|
|
|
if (h >= tmpline + sizeof(tmpline) - 9)
|
|
|
|
|
goto trunc;
|
|
|
|
|
memcpy(h, " \"-\" \"-\"", 8);
|
|
|
|
|
h += 8;
|
|
|
|
|
|
|
|
|
|
w = snprintf(h, sizeof(tmpline) - (h - tmpline),
|
|
|
|
|
" %d %03d",
|
|
|
|
|
(s->cli_addr.ss_family == AF_INET) ?
|
|
|
|
|
ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port) :
|
|
|
|
|
ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port),
|
|
|
|
|
(int)s->logs.accept_date.tv_usec/1000);
|
|
|
|
|
if (w < 0 || w >= sizeof(tmpline) - (h - tmpline))
|
|
|
|
|
goto trunc;
|
|
|
|
|
h += w;
|
|
|
|
|
|
|
|
|
|
w = strlen(fe->id);
|
|
|
|
|
if (h >= tmpline + sizeof(tmpline) - 4 - w)
|
|
|
|
|
goto trunc;
|
|
|
|
|
*(h++) = ' ';
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
memcpy(h, fe->id, w);
|
|
|
|
|
h += w;
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
|
|
|
|
|
w = strlen(be->id);
|
|
|
|
|
if (h >= tmpline + sizeof(tmpline) - 4 - w)
|
|
|
|
|
goto trunc;
|
|
|
|
|
*(h++) = ' ';
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
memcpy(h, be->id, w);
|
|
|
|
|
h += w;
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
|
|
|
|
|
svid = (tolog & LW_SVID) ?
|
|
|
|
|
(s->data_source != DATA_SRC_STATS) ?
|
|
|
|
|
(s->srv != NULL) ? s->srv->id : "<NOSRV>" : "<STATS>" : "-";
|
|
|
|
|
|
|
|
|
|
w = strlen(svid);
|
|
|
|
|
if (h >= tmpline + sizeof(tmpline) - 4 - w)
|
|
|
|
|
goto trunc;
|
|
|
|
|
*(h++) = ' ';
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
memcpy(h, svid, w);
|
|
|
|
|
h += w;
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
|
|
|
|
|
t_request = -1;
|
|
|
|
|
if (tv_isge(&s->logs.tv_request, &s->logs.tv_accept))
|
|
|
|
|
t_request = tv_ms_elapsed(&s->logs.tv_accept, &s->logs.tv_request);
|
|
|
|
|
w = snprintf(h, sizeof(tmpline) - (h - tmpline),
|
|
|
|
|
" %d %ld %ld %ld %ld",
|
|
|
|
|
t_request,
|
|
|
|
|
(s->logs.t_queue >= 0) ? s->logs.t_queue - t_request : -1,
|
|
|
|
|
(s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_queue : -1,
|
|
|
|
|
(s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
|
|
|
|
|
s->logs.t_close);
|
|
|
|
|
if (w < 0 || w >= sizeof(tmpline) - (h - tmpline))
|
|
|
|
|
goto trunc;
|
|
|
|
|
h += w;
|
|
|
|
|
|
|
|
|
|
if (h >= tmpline + sizeof(tmpline) - 8)
|
|
|
|
|
goto trunc;
|
|
|
|
|
*(h++) = ' ';
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
*(h++) = sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT];
|
|
|
|
|
*(h++) = sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT];
|
|
|
|
|
*(h++) = (be->options & PR_O_COOK_ANY) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-',
|
|
|
|
|
*(h++) = (be->options & PR_O_COOK_ANY) ? sess_set_cookie[(txn->flags & TX_SCK_MASK) >> TX_SCK_SHIFT] : '-';
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
|
|
|
|
|
w = snprintf(h, sizeof(tmpline) - (h - tmpline),
|
|
|
|
|
" %d %d %d %d %d %ld %ld",
|
|
|
|
|
actconn, fe->feconn, be->beconn, s->srv ? s->srv->cur_sess : 0,
|
|
|
|
|
(s->conn_retries > 0) ? (be->conn_retries - s->conn_retries) : be->conn_retries,
|
|
|
|
|
s->logs.srv_queue_size, s->logs.prx_queue_size);
|
|
|
|
|
|
|
|
|
|
if (w < 0 || w >= sizeof(tmpline) - (h - tmpline))
|
|
|
|
|
goto trunc;
|
|
|
|
|
h += w;
|
|
|
|
|
|
|
|
|
|
if (txn->cli_cookie) {
|
|
|
|
|
w = strlen(txn->cli_cookie);
|
|
|
|
|
if (h >= tmpline + sizeof(tmpline) - 4 - w)
|
|
|
|
|
goto trunc;
|
|
|
|
|
*(h++) = ' ';
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
memcpy(h, txn->cli_cookie, w);
|
|
|
|
|
h += w;
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
} else {
|
|
|
|
|
if (h >= tmpline + sizeof(tmpline) - 5)
|
|
|
|
|
goto trunc;
|
|
|
|
|
memcpy(h, " \"-\"", 4);
|
|
|
|
|
h += 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (txn->srv_cookie) {
|
|
|
|
|
w = strlen(txn->srv_cookie);
|
|
|
|
|
if (h >= tmpline + sizeof(tmpline) - 4 - w)
|
|
|
|
|
goto trunc;
|
|
|
|
|
*(h++) = ' ';
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
memcpy(h, txn->srv_cookie, w);
|
|
|
|
|
h += w;
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
} else {
|
|
|
|
|
if (h >= tmpline + sizeof(tmpline) - 5)
|
|
|
|
|
goto trunc;
|
|
|
|
|
memcpy(h, " \"-\"", 4);
|
|
|
|
|
h += 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((fe->to_log & LW_REQHDR) && txn->req.cap) {
|
|
|
|
|
for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
|
|
|
|
|
if (h >= sizeof (tmpline) + tmpline - 4)
|
|
|
|
|
goto trunc;
|
|
|
|
|
*(h++) = ' ';
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
h = encode_string(h, tmpline + sizeof(tmpline) - 2,
|
|
|
|
|
'#', hdr_encode_map, txn->req.cap[hdr]);
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((fe->to_log & LW_RSPHDR) && txn->rsp.cap) {
|
|
|
|
|
for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
|
|
|
|
|
if (h >= sizeof (tmpline) + tmpline - 4)
|
|
|
|
|
goto trunc;
|
|
|
|
|
*(h++) = ' ';
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
h = encode_string(h, tmpline + sizeof(tmpline) - 2,
|
|
|
|
|
'#', hdr_encode_map, txn->rsp.cap[hdr]);
|
|
|
|
|
*(h++) = '\"';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trunc:
|
|
|
|
|
*h = '\0';
|
|
|
|
|
|
|
|
|
|
level = LOG_INFO;
|
|
|
|
|
if (err && (fe->options2 & PR_O2_LOGERRORS))
|
|
|
|
|
level = LOG_ERR;
|
|
|
|
|
|
|
|
|
|
send_log(prx_log, level, "%s\n", tmpline);
|
|
|
|
|
|
|
|
|
|
s->logs.logwait = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-31 19:30:43 -04:00
|
|
|
/*
|
|
|
|
|
* send a log for the session when we have enough info about it.
|
|
|
|
|
* Will not log if the frontend has no log defined.
|
2007-01-21 13:16:41 -05:00
|
|
|
*/
|
2008-11-30 12:47:21 -05:00
|
|
|
void http_sess_log(struct session *s)
|
2007-03-31 19:30:43 -04:00
|
|
|
{
|
|
|
|
|
char pn[INET6_ADDRSTRLEN + strlen(":65535")];
|
|
|
|
|
struct proxy *fe = s->fe;
|
|
|
|
|
struct proxy *be = s->be;
|
|
|
|
|
struct proxy *prx_log;
|
|
|
|
|
struct http_txn *txn = &s->txn;
|
2009-05-10 05:57:02 -04:00
|
|
|
int tolog, level, err;
|
2007-03-31 19:30:43 -04:00
|
|
|
char *uri, *h;
|
|
|
|
|
char *svid;
|
2007-10-25 04:34:16 -04:00
|
|
|
struct tm tm;
|
2007-03-31 19:30:43 -04:00
|
|
|
static char tmpline[MAX_SYSLOG_LEN];
|
2008-06-13 15:12:51 -04:00
|
|
|
int t_request;
|
2007-03-31 19:30:43 -04:00
|
|
|
int hdr;
|
|
|
|
|
|
2009-05-10 05:57:02 -04:00
|
|
|
/* if we don't want to log normal traffic, return now */
|
|
|
|
|
err = (s->flags & (SN_ERR_MASK | SN_REDISP)) ||
|
|
|
|
|
(s->conn_retries != be->conn_retries) ||
|
|
|
|
|
txn->status >= 500;
|
|
|
|
|
if (!err && (fe->options2 & PR_O2_NOLOGNORM))
|
|
|
|
|
return;
|
|
|
|
|
|
2007-03-31 19:30:43 -04:00
|
|
|
if (fe->logfac1 < 0 && fe->logfac2 < 0)
|
|
|
|
|
return;
|
|
|
|
|
prx_log = fe;
|
|
|
|
|
|
2009-06-30 12:26:00 -04:00
|
|
|
if (prx_log->options2 & PR_O2_CLFLOG)
|
|
|
|
|
return http_sess_clflog(s);
|
|
|
|
|
|
2007-03-31 19:30:43 -04:00
|
|
|
if (s->cli_addr.ss_family == AF_INET)
|
|
|
|
|
inet_ntop(AF_INET,
|
|
|
|
|
(const void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr,
|
|
|
|
|
pn, sizeof(pn));
|
|
|
|
|
else
|
|
|
|
|
inet_ntop(AF_INET6,
|
|
|
|
|
(const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr,
|
|
|
|
|
pn, sizeof(pn));
|
|
|
|
|
|
2008-06-22 11:18:02 -04:00
|
|
|
get_localtime(s->logs.accept_date.tv_sec, &tm);
|
2007-03-31 19:30:43 -04:00
|
|
|
|
|
|
|
|
/* FIXME: let's limit ourselves to frontend logging for now. */
|
|
|
|
|
tolog = fe->to_log;
|
|
|
|
|
|
|
|
|
|
h = tmpline;
|
|
|
|
|
if (fe->to_log & LW_REQHDR &&
|
|
|
|
|
txn->req.cap &&
|
|
|
|
|
(h < tmpline + sizeof(tmpline) - 10)) {
|
|
|
|
|
*(h++) = ' ';
|
|
|
|
|
*(h++) = '{';
|
|
|
|
|
for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
|
|
|
|
|
if (hdr)
|
|
|
|
|
*(h++) = '|';
|
|
|
|
|
if (txn->req.cap[hdr] != NULL)
|
|
|
|
|
h = encode_string(h, tmpline + sizeof(tmpline) - 7,
|
|
|
|
|
'#', hdr_encode_map, txn->req.cap[hdr]);
|
|
|
|
|
}
|
|
|
|
|
*(h++) = '}';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fe->to_log & LW_RSPHDR &&
|
|
|
|
|
txn->rsp.cap &&
|
|
|
|
|
(h < tmpline + sizeof(tmpline) - 7)) {
|
|
|
|
|
*(h++) = ' ';
|
|
|
|
|
*(h++) = '{';
|
|
|
|
|
for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
|
|
|
|
|
if (hdr)
|
|
|
|
|
*(h++) = '|';
|
|
|
|
|
if (txn->rsp.cap[hdr] != NULL)
|
|
|
|
|
h = encode_string(h, tmpline + sizeof(tmpline) - 4,
|
|
|
|
|
'#', hdr_encode_map, txn->rsp.cap[hdr]);
|
|
|
|
|
}
|
|
|
|
|
*(h++) = '}';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (h < tmpline + sizeof(tmpline) - 4) {
|
|
|
|
|
*(h++) = ' ';
|
|
|
|
|
*(h++) = '"';
|
|
|
|
|
uri = txn->uri ? txn->uri : "<BADREQ>";
|
|
|
|
|
h = encode_string(h, tmpline + sizeof(tmpline) - 1,
|
|
|
|
|
'#', url_encode_map, uri);
|
|
|
|
|
*(h++) = '"';
|
|
|
|
|
}
|
|
|
|
|
*h = '\0';
|
|
|
|
|
|
|
|
|
|
svid = (tolog & LW_SVID) ?
|
|
|
|
|
(s->data_source != DATA_SRC_STATS) ?
|
|
|
|
|
(s->srv != NULL) ? s->srv->id : "<NOSRV>" : "<STATS>" : "-";
|
|
|
|
|
|
2008-06-13 15:12:51 -04:00
|
|
|
t_request = -1;
|
|
|
|
|
if (tv_isge(&s->logs.tv_request, &s->logs.tv_accept))
|
|
|
|
|
t_request = tv_ms_elapsed(&s->logs.tv_accept, &s->logs.tv_request);
|
|
|
|
|
|
2009-05-10 05:57:02 -04:00
|
|
|
level = LOG_INFO;
|
|
|
|
|
if (err && (fe->options2 & PR_O2_LOGERRORS))
|
|
|
|
|
level = LOG_ERR;
|
|
|
|
|
|
|
|
|
|
send_log(prx_log, level,
|
2007-03-31 19:30:43 -04:00
|
|
|
"%s:%d [%02d/%s/%04d:%02d:%02d:%02d.%03d]"
|
2009-04-03 08:49:12 -04:00
|
|
|
" %s %s/%s %d/%ld/%ld/%ld/%s%ld %d %s%lld"
|
|
|
|
|
" %s %s %c%c%c%c %d/%d/%d/%d/%s%u %ld/%ld%s\n",
|
2007-03-31 19:30:43 -04:00
|
|
|
pn,
|
|
|
|
|
(s->cli_addr.ss_family == AF_INET) ?
|
|
|
|
|
ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port) :
|
|
|
|
|
ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port),
|
2007-10-25 04:34:16 -04:00
|
|
|
tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
|
2009-04-03 08:49:12 -04:00
|
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec, (int)s->logs.accept_date.tv_usec/1000,
|
2007-03-31 19:30:43 -04:00
|
|
|
fe->id, be->id, svid,
|
2008-06-13 15:12:51 -04:00
|
|
|
t_request,
|
|
|
|
|
(s->logs.t_queue >= 0) ? s->logs.t_queue - t_request : -1,
|
2007-03-31 19:30:43 -04:00
|
|
|
(s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_queue : -1,
|
|
|
|
|
(s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
|
|
|
|
|
(tolog & LW_BYTES) ? "" : "+", s->logs.t_close,
|
|
|
|
|
txn->status,
|
2008-01-18 05:16:32 -05:00
|
|
|
(tolog & LW_BYTES) ? "" : "+", s->logs.bytes_out,
|
2007-03-31 19:30:43 -04:00
|
|
|
txn->cli_cookie ? txn->cli_cookie : "-",
|
|
|
|
|
txn->srv_cookie ? txn->srv_cookie : "-",
|
|
|
|
|
sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT],
|
|
|
|
|
sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT],
|
|
|
|
|
(be->options & PR_O_COOK_ANY) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-',
|
|
|
|
|
(be->options & PR_O_COOK_ANY) ? sess_set_cookie[(txn->flags & TX_SCK_MASK) >> TX_SCK_SHIFT] : '-',
|
|
|
|
|
actconn, fe->feconn, be->beconn, s->srv ? s->srv->cur_sess : 0,
|
2008-01-06 10:36:16 -05:00
|
|
|
(s->flags & SN_REDISP)?"+":"",
|
|
|
|
|
(s->conn_retries>0)?(be->conn_retries - s->conn_retries):be->conn_retries,
|
2007-03-31 19:30:43 -04:00
|
|
|
s->logs.srv_queue_size, s->logs.prx_queue_size, tmpline);
|
|
|
|
|
|
|
|
|
|
s->logs.logwait = 0;
|
|
|
|
|
}
|
2007-01-21 13:16:41 -05:00
|
|
|
|
2007-03-04 12:17:17 -05:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Capture headers from message starting at <som> according to header list
|
|
|
|
|
* <cap_hdr>, and fill the <idx> structure appropriately.
|
|
|
|
|
*/
|
|
|
|
|
void capture_headers(char *som, struct hdr_idx *idx,
|
|
|
|
|
char **cap, struct cap_hdr *cap_hdr)
|
|
|
|
|
{
|
|
|
|
|
char *eol, *sol, *col, *sov;
|
|
|
|
|
int cur_idx;
|
|
|
|
|
struct cap_hdr *h;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
sol = som + hdr_idx_first_pos(idx);
|
|
|
|
|
cur_idx = hdr_idx_first_idx(idx);
|
|
|
|
|
|
|
|
|
|
while (cur_idx) {
|
|
|
|
|
eol = sol + idx->v[cur_idx].len;
|
|
|
|
|
|
|
|
|
|
col = sol;
|
|
|
|
|
while (col < eol && *col != ':')
|
|
|
|
|
col++;
|
|
|
|
|
|
|
|
|
|
sov = col + 1;
|
|
|
|
|
while (sov < eol && http_is_lws[(unsigned char)*sov])
|
|
|
|
|
sov++;
|
|
|
|
|
|
|
|
|
|
for (h = cap_hdr; h; h = h->next) {
|
|
|
|
|
if ((h->namelen == col - sol) &&
|
|
|
|
|
(strncasecmp(sol, h->name, h->namelen) == 0)) {
|
|
|
|
|
if (cap[h->index] == NULL)
|
|
|
|
|
cap[h->index] =
|
2007-05-13 16:46:04 -04:00
|
|
|
pool_alloc2(h->pool);
|
2007-03-04 12:17:17 -05:00
|
|
|
|
|
|
|
|
if (cap[h->index] == NULL) {
|
|
|
|
|
Alert("HTTP capture : out of memory.\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = eol - sov;
|
|
|
|
|
if (len > h->len)
|
|
|
|
|
len = h->len;
|
|
|
|
|
|
|
|
|
|
memcpy(cap[h->index], sov, len);
|
|
|
|
|
cap[h->index][len]=0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sol = eol + idx->v[cur_idx].cr + 1;
|
|
|
|
|
cur_idx = idx->v[cur_idx].next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-03-31 19:30:43 -04:00
|
|
|
/* either we find an LF at <ptr> or we jump to <bad>.
|
|
|
|
|
*/
|
|
|
|
|
#define EXPECT_LF_HERE(ptr, bad) do { if (unlikely(*(ptr) != '\n')) goto bad; } while (0)
|
|
|
|
|
|
|
|
|
|
/* plays with variables <ptr>, <end> and <state>. Jumps to <good> if OK,
|
|
|
|
|
* otherwise to <http_msg_ood> with <state> set to <st>.
|
|
|
|
|
*/
|
|
|
|
|
#define EAT_AND_JUMP_OR_RETURN(good, st) do { \
|
|
|
|
|
ptr++; \
|
|
|
|
|
if (likely(ptr < end)) \
|
|
|
|
|
goto good; \
|
|
|
|
|
else { \
|
|
|
|
|
state = (st); \
|
|
|
|
|
goto http_msg_ood; \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
|
2007-01-21 17:58:29 -05:00
|
|
|
/*
|
2007-03-18 11:22:39 -04:00
|
|
|
* This function parses a status line between <ptr> and <end>, starting with
|
2007-01-21 17:58:29 -05:00
|
|
|
* parser state <state>. Only states HTTP_MSG_RPVER, HTTP_MSG_RPVER_SP,
|
|
|
|
|
* HTTP_MSG_RPCODE, HTTP_MSG_RPCODE_SP and HTTP_MSG_RPREASON are handled. Others
|
|
|
|
|
* will give undefined results.
|
|
|
|
|
* Note that it is upon the caller's responsibility to ensure that ptr < end,
|
|
|
|
|
* and that msg->sol points to the beginning of the response.
|
|
|
|
|
* If a complete line is found (which implies that at least one CR or LF is
|
|
|
|
|
* found before <end>, the updated <ptr> is returned, otherwise NULL is
|
|
|
|
|
* returned indicating an incomplete line (which does not mean that parts have
|
|
|
|
|
* not been updated). In the incomplete case, if <ret_ptr> or <ret_state> are
|
|
|
|
|
* non-NULL, they are fed with the new <ptr> and <state> values to be passed
|
|
|
|
|
* upon next call.
|
|
|
|
|
*
|
2007-05-02 14:58:19 -04:00
|
|
|
* This function was intentionally designed to be called from
|
2007-01-21 17:58:29 -05:00
|
|
|
* http_msg_analyzer() with the lowest overhead. It should integrate perfectly
|
|
|
|
|
* within its state machine and use the same macros, hence the need for same
|
2007-05-02 14:58:19 -04:00
|
|
|
* labels and variable names. Note that msg->sol is left unchanged.
|
2007-01-21 17:58:29 -05:00
|
|
|
*/
|
2008-01-26 18:34:10 -05:00
|
|
|
const char *http_parse_stsline(struct http_msg *msg, const char *msg_buf,
|
|
|
|
|
unsigned int state, const char *ptr, const char *end,
|
|
|
|
|
char **ret_ptr, unsigned int *ret_state)
|
2007-01-21 17:58:29 -05:00
|
|
|
{
|
|
|
|
|
switch (state) {
|
|
|
|
|
http_msg_rpver:
|
|
|
|
|
case HTTP_MSG_RPVER:
|
2007-03-04 12:13:58 -05:00
|
|
|
if (likely(HTTP_IS_VER_TOKEN(*ptr)))
|
2007-01-21 17:58:29 -05:00
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rpver, HTTP_MSG_RPVER);
|
|
|
|
|
|
|
|
|
|
if (likely(HTTP_IS_SPHT(*ptr))) {
|
2007-03-03 07:54:32 -05:00
|
|
|
msg->sl.st.v_l = (ptr - msg_buf) - msg->som;
|
2007-01-21 17:58:29 -05:00
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rpver_sp, HTTP_MSG_RPVER_SP);
|
|
|
|
|
}
|
2009-03-01 05:10:40 -05:00
|
|
|
state = HTTP_MSG_ERROR;
|
|
|
|
|
break;
|
|
|
|
|
|
2007-01-21 17:58:29 -05:00
|
|
|
http_msg_rpver_sp:
|
|
|
|
|
case HTTP_MSG_RPVER_SP:
|
|
|
|
|
if (likely(!HTTP_IS_LWS(*ptr))) {
|
|
|
|
|
msg->sl.st.c = ptr - msg_buf;
|
|
|
|
|
goto http_msg_rpcode;
|
|
|
|
|
}
|
|
|
|
|
if (likely(HTTP_IS_SPHT(*ptr)))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rpver_sp, HTTP_MSG_RPVER_SP);
|
|
|
|
|
/* so it's a CR/LF, this is invalid */
|
2009-03-01 05:10:40 -05:00
|
|
|
state = HTTP_MSG_ERROR;
|
|
|
|
|
break;
|
2007-01-21 17:58:29 -05:00
|
|
|
|
|
|
|
|
http_msg_rpcode:
|
|
|
|
|
case HTTP_MSG_RPCODE:
|
|
|
|
|
if (likely(!HTTP_IS_LWS(*ptr)))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rpcode, HTTP_MSG_RPCODE);
|
|
|
|
|
|
|
|
|
|
if (likely(HTTP_IS_SPHT(*ptr))) {
|
|
|
|
|
msg->sl.st.c_l = (ptr - msg_buf) - msg->sl.st.c;
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rpcode_sp, HTTP_MSG_RPCODE_SP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* so it's a CR/LF, so there is no reason phrase */
|
|
|
|
|
msg->sl.st.c_l = (ptr - msg_buf) - msg->sl.st.c;
|
|
|
|
|
http_msg_rsp_reason:
|
|
|
|
|
/* FIXME: should we support HTTP responses without any reason phrase ? */
|
|
|
|
|
msg->sl.st.r = ptr - msg_buf;
|
|
|
|
|
msg->sl.st.r_l = 0;
|
|
|
|
|
goto http_msg_rpline_eol;
|
|
|
|
|
|
|
|
|
|
http_msg_rpcode_sp:
|
|
|
|
|
case HTTP_MSG_RPCODE_SP:
|
|
|
|
|
if (likely(!HTTP_IS_LWS(*ptr))) {
|
|
|
|
|
msg->sl.st.r = ptr - msg_buf;
|
|
|
|
|
goto http_msg_rpreason;
|
|
|
|
|
}
|
|
|
|
|
if (likely(HTTP_IS_SPHT(*ptr)))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rpcode_sp, HTTP_MSG_RPCODE_SP);
|
|
|
|
|
/* so it's a CR/LF, so there is no reason phrase */
|
|
|
|
|
goto http_msg_rsp_reason;
|
|
|
|
|
|
|
|
|
|
http_msg_rpreason:
|
|
|
|
|
case HTTP_MSG_RPREASON:
|
|
|
|
|
if (likely(!HTTP_IS_CRLF(*ptr)))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rpreason, HTTP_MSG_RPREASON);
|
|
|
|
|
msg->sl.st.r_l = (ptr - msg_buf) - msg->sl.st.r;
|
|
|
|
|
http_msg_rpline_eol:
|
|
|
|
|
/* We have seen the end of line. Note that we do not
|
|
|
|
|
* necessarily have the \n yet, but at least we know that we
|
|
|
|
|
* have EITHER \r OR \n, otherwise the response would not be
|
|
|
|
|
* complete. We can then record the response length and return
|
|
|
|
|
* to the caller which will be able to register it.
|
|
|
|
|
*/
|
|
|
|
|
msg->sl.st.l = ptr - msg->sol;
|
|
|
|
|
return ptr;
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG_FULL
|
|
|
|
|
default:
|
|
|
|
|
fprintf(stderr, "FIXME !!!! impossible state at %s:%d = %d\n", __FILE__, __LINE__, state);
|
|
|
|
|
exit(1);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
http_msg_ood:
|
2009-03-01 05:10:40 -05:00
|
|
|
/* out of valid data */
|
2007-01-21 17:58:29 -05:00
|
|
|
if (ret_state)
|
|
|
|
|
*ret_state = state;
|
|
|
|
|
if (ret_ptr)
|
|
|
|
|
*ret_ptr = (char *)ptr;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*
|
2007-01-21 13:16:41 -05:00
|
|
|
* This function parses a request line between <ptr> and <end>, starting with
|
|
|
|
|
* parser state <state>. Only states HTTP_MSG_RQMETH, HTTP_MSG_RQMETH_SP,
|
|
|
|
|
* HTTP_MSG_RQURI, HTTP_MSG_RQURI_SP and HTTP_MSG_RQVER are handled. Others
|
|
|
|
|
* will give undefined results.
|
|
|
|
|
* Note that it is upon the caller's responsibility to ensure that ptr < end,
|
|
|
|
|
* and that msg->sol points to the beginning of the request.
|
|
|
|
|
* If a complete line is found (which implies that at least one CR or LF is
|
|
|
|
|
* found before <end>, the updated <ptr> is returned, otherwise NULL is
|
|
|
|
|
* returned indicating an incomplete line (which does not mean that parts have
|
|
|
|
|
* not been updated). In the incomplete case, if <ret_ptr> or <ret_state> are
|
|
|
|
|
* non-NULL, they are fed with the new <ptr> and <state> values to be passed
|
|
|
|
|
* upon next call.
|
|
|
|
|
*
|
2007-05-02 14:58:19 -04:00
|
|
|
* This function was intentionally designed to be called from
|
2007-01-21 13:16:41 -05:00
|
|
|
* http_msg_analyzer() with the lowest overhead. It should integrate perfectly
|
|
|
|
|
* within its state machine and use the same macros, hence the need for same
|
2007-05-02 14:58:19 -04:00
|
|
|
* labels and variable names. Note that msg->sol is left unchanged.
|
2006-06-25 20:48:02 -04:00
|
|
|
*/
|
2008-01-26 18:34:10 -05:00
|
|
|
const char *http_parse_reqline(struct http_msg *msg, const char *msg_buf,
|
|
|
|
|
unsigned int state, const char *ptr, const char *end,
|
|
|
|
|
char **ret_ptr, unsigned int *ret_state)
|
2007-01-21 13:16:41 -05:00
|
|
|
{
|
|
|
|
|
switch (state) {
|
|
|
|
|
http_msg_rqmeth:
|
|
|
|
|
case HTTP_MSG_RQMETH:
|
|
|
|
|
if (likely(HTTP_IS_TOKEN(*ptr)))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rqmeth, HTTP_MSG_RQMETH);
|
|
|
|
|
|
|
|
|
|
if (likely(HTTP_IS_SPHT(*ptr))) {
|
2007-03-03 07:54:32 -05:00
|
|
|
msg->sl.rq.m_l = (ptr - msg_buf) - msg->som;
|
2007-01-21 13:16:41 -05:00
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rqmeth_sp, HTTP_MSG_RQMETH_SP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (likely(HTTP_IS_CRLF(*ptr))) {
|
|
|
|
|
/* HTTP 0.9 request */
|
2007-03-03 07:54:32 -05:00
|
|
|
msg->sl.rq.m_l = (ptr - msg_buf) - msg->som;
|
2007-01-21 13:16:41 -05:00
|
|
|
http_msg_req09_uri:
|
|
|
|
|
msg->sl.rq.u = ptr - msg_buf;
|
|
|
|
|
http_msg_req09_uri_e:
|
|
|
|
|
msg->sl.rq.u_l = (ptr - msg_buf) - msg->sl.rq.u;
|
|
|
|
|
http_msg_req09_ver:
|
|
|
|
|
msg->sl.rq.v = ptr - msg_buf;
|
|
|
|
|
msg->sl.rq.v_l = 0;
|
|
|
|
|
goto http_msg_rqline_eol;
|
|
|
|
|
}
|
2009-03-01 05:10:40 -05:00
|
|
|
state = HTTP_MSG_ERROR;
|
|
|
|
|
break;
|
|
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
http_msg_rqmeth_sp:
|
|
|
|
|
case HTTP_MSG_RQMETH_SP:
|
|
|
|
|
if (likely(!HTTP_IS_LWS(*ptr))) {
|
|
|
|
|
msg->sl.rq.u = ptr - msg_buf;
|
|
|
|
|
goto http_msg_rquri;
|
|
|
|
|
}
|
|
|
|
|
if (likely(HTTP_IS_SPHT(*ptr)))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rqmeth_sp, HTTP_MSG_RQMETH_SP);
|
|
|
|
|
/* so it's a CR/LF, meaning an HTTP 0.9 request */
|
|
|
|
|
goto http_msg_req09_uri;
|
|
|
|
|
|
|
|
|
|
http_msg_rquri:
|
|
|
|
|
case HTTP_MSG_RQURI:
|
|
|
|
|
if (likely(!HTTP_IS_LWS(*ptr)))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rquri, HTTP_MSG_RQURI);
|
|
|
|
|
|
|
|
|
|
if (likely(HTTP_IS_SPHT(*ptr))) {
|
|
|
|
|
msg->sl.rq.u_l = (ptr - msg_buf) - msg->sl.rq.u;
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rquri_sp, HTTP_MSG_RQURI_SP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* so it's a CR/LF, meaning an HTTP 0.9 request */
|
|
|
|
|
goto http_msg_req09_uri_e;
|
|
|
|
|
|
|
|
|
|
http_msg_rquri_sp:
|
|
|
|
|
case HTTP_MSG_RQURI_SP:
|
|
|
|
|
if (likely(!HTTP_IS_LWS(*ptr))) {
|
|
|
|
|
msg->sl.rq.v = ptr - msg_buf;
|
|
|
|
|
goto http_msg_rqver;
|
|
|
|
|
}
|
|
|
|
|
if (likely(HTTP_IS_SPHT(*ptr)))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rquri_sp, HTTP_MSG_RQURI_SP);
|
|
|
|
|
/* so it's a CR/LF, meaning an HTTP 0.9 request */
|
|
|
|
|
goto http_msg_req09_ver;
|
|
|
|
|
|
|
|
|
|
http_msg_rqver:
|
|
|
|
|
case HTTP_MSG_RQVER:
|
2007-03-04 12:13:58 -05:00
|
|
|
if (likely(HTTP_IS_VER_TOKEN(*ptr)))
|
2007-01-21 13:16:41 -05:00
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rqver, HTTP_MSG_RQVER);
|
2007-03-04 12:13:58 -05:00
|
|
|
|
|
|
|
|
if (likely(HTTP_IS_CRLF(*ptr))) {
|
|
|
|
|
msg->sl.rq.v_l = (ptr - msg_buf) - msg->sl.rq.v;
|
|
|
|
|
http_msg_rqline_eol:
|
|
|
|
|
/* We have seen the end of line. Note that we do not
|
|
|
|
|
* necessarily have the \n yet, but at least we know that we
|
|
|
|
|
* have EITHER \r OR \n, otherwise the request would not be
|
|
|
|
|
* complete. We can then record the request length and return
|
|
|
|
|
* to the caller which will be able to register it.
|
|
|
|
|
*/
|
|
|
|
|
msg->sl.rq.l = ptr - msg->sol;
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* neither an HTTP_VER token nor a CRLF */
|
2009-03-01 05:10:40 -05:00
|
|
|
state = HTTP_MSG_ERROR;
|
|
|
|
|
break;
|
2007-01-21 13:16:41 -05:00
|
|
|
|
|
|
|
|
#ifdef DEBUG_FULL
|
|
|
|
|
default:
|
|
|
|
|
fprintf(stderr, "FIXME !!!! impossible state at %s:%d = %d\n", __FILE__, __LINE__, state);
|
|
|
|
|
exit(1);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
http_msg_ood:
|
2009-03-01 05:10:40 -05:00
|
|
|
/* out of valid data */
|
2007-01-21 13:16:41 -05:00
|
|
|
if (ret_state)
|
|
|
|
|
*ret_state = state;
|
|
|
|
|
if (ret_ptr)
|
|
|
|
|
*ret_ptr = (char *)ptr;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-01-21 17:58:29 -05:00
|
|
|
/*
|
|
|
|
|
* This function parses an HTTP message, either a request or a response,
|
2007-03-03 07:54:32 -05:00
|
|
|
* depending on the initial msg->msg_state. It can be preempted everywhere
|
2007-01-21 17:58:29 -05:00
|
|
|
* when data are missing and recalled at the exact same location with no
|
|
|
|
|
* information loss. The header index is re-initialized when switching from
|
2007-05-02 14:58:19 -04:00
|
|
|
* MSG_R[PQ]BEFORE to MSG_RPVER|MSG_RQMETH. It modifies msg->sol among other
|
|
|
|
|
* fields.
|
2007-01-21 17:58:29 -05:00
|
|
|
*/
|
2007-01-21 13:16:41 -05:00
|
|
|
void http_msg_analyzer(struct buffer *buf, struct http_msg *msg, struct hdr_idx *idx)
|
|
|
|
|
{
|
2008-01-26 18:34:10 -05:00
|
|
|
unsigned int state; /* updated only when leaving the FSM */
|
2007-01-21 13:16:41 -05:00
|
|
|
register char *ptr, *end; /* request pointers, to avoid dereferences */
|
|
|
|
|
|
2007-03-03 07:54:32 -05:00
|
|
|
state = msg->msg_state;
|
2007-01-21 13:16:41 -05:00
|
|
|
ptr = buf->lr;
|
|
|
|
|
end = buf->r;
|
|
|
|
|
|
|
|
|
|
if (unlikely(ptr >= end))
|
|
|
|
|
goto http_msg_ood;
|
|
|
|
|
|
|
|
|
|
switch (state) {
|
2007-01-21 17:58:29 -05:00
|
|
|
/*
|
|
|
|
|
* First, states that are specific to the response only.
|
|
|
|
|
* We check them first so that request and headers are
|
|
|
|
|
* closer to each other (accessed more often).
|
|
|
|
|
*/
|
|
|
|
|
http_msg_rpbefore:
|
|
|
|
|
case HTTP_MSG_RPBEFORE:
|
|
|
|
|
if (likely(HTTP_IS_TOKEN(*ptr))) {
|
2009-09-15 15:25:21 -04:00
|
|
|
#if !defined(PARSE_PRESERVE_EMPTY_LINES)
|
|
|
|
|
if (likely(ptr != buf->data)) {
|
2007-01-21 17:58:29 -05:00
|
|
|
/* Remove empty leading lines, as recommended by
|
|
|
|
|
* RFC2616. This takes a lot of time because we
|
|
|
|
|
* must move all the buffer backwards, but this
|
|
|
|
|
* is rarely needed. The method above will be
|
|
|
|
|
* cleaner when we'll be able to start sending
|
|
|
|
|
* the request from any place in the buffer.
|
|
|
|
|
*/
|
2009-09-15 15:25:21 -04:00
|
|
|
ptr += buffer_replace2(buf, buf->lr, ptr, NULL, 0);
|
2007-01-21 17:58:29 -05:00
|
|
|
end = buf->r;
|
|
|
|
|
}
|
2009-09-15 15:25:21 -04:00
|
|
|
#endif
|
|
|
|
|
msg->sol = ptr;
|
|
|
|
|
msg->som = ptr - buf->data;
|
2007-01-21 17:58:29 -05:00
|
|
|
hdr_idx_init(idx);
|
|
|
|
|
state = HTTP_MSG_RPVER;
|
|
|
|
|
goto http_msg_rpver;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (unlikely(!HTTP_IS_CRLF(*ptr)))
|
|
|
|
|
goto http_msg_invalid;
|
|
|
|
|
|
|
|
|
|
if (unlikely(*ptr == '\n'))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rpbefore, HTTP_MSG_RPBEFORE);
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rpbefore_cr, HTTP_MSG_RPBEFORE_CR);
|
|
|
|
|
/* stop here */
|
|
|
|
|
|
|
|
|
|
http_msg_rpbefore_cr:
|
|
|
|
|
case HTTP_MSG_RPBEFORE_CR:
|
|
|
|
|
EXPECT_LF_HERE(ptr, http_msg_invalid);
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rpbefore, HTTP_MSG_RPBEFORE);
|
|
|
|
|
/* stop here */
|
|
|
|
|
|
|
|
|
|
http_msg_rpver:
|
|
|
|
|
case HTTP_MSG_RPVER:
|
|
|
|
|
case HTTP_MSG_RPVER_SP:
|
|
|
|
|
case HTTP_MSG_RPCODE:
|
|
|
|
|
case HTTP_MSG_RPCODE_SP:
|
|
|
|
|
case HTTP_MSG_RPREASON:
|
2007-03-18 11:22:39 -04:00
|
|
|
ptr = (char *)http_parse_stsline(msg, buf->data, state, ptr, end,
|
2007-03-03 07:54:32 -05:00
|
|
|
&buf->lr, &msg->msg_state);
|
2007-01-21 17:58:29 -05:00
|
|
|
if (unlikely(!ptr))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* we have a full response and we know that we have either a CR
|
|
|
|
|
* or an LF at <ptr>.
|
|
|
|
|
*/
|
2007-03-03 07:54:32 -05:00
|
|
|
//fprintf(stderr,"som=%d rq.l=%d *ptr=0x%02x\n", msg->som, msg->sl.st.l, *ptr);
|
2007-01-21 17:58:29 -05:00
|
|
|
hdr_idx_set_start(idx, msg->sl.st.l, *ptr == '\r');
|
|
|
|
|
|
|
|
|
|
msg->sol = ptr;
|
|
|
|
|
if (likely(*ptr == '\r'))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rpline_end, HTTP_MSG_RPLINE_END);
|
|
|
|
|
goto http_msg_rpline_end;
|
|
|
|
|
|
|
|
|
|
http_msg_rpline_end:
|
|
|
|
|
case HTTP_MSG_RPLINE_END:
|
|
|
|
|
/* msg->sol must point to the first of CR or LF. */
|
|
|
|
|
EXPECT_LF_HERE(ptr, http_msg_invalid);
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_hdr_first, HTTP_MSG_HDR_FIRST);
|
|
|
|
|
/* stop here */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Second, states that are specific to the request only
|
|
|
|
|
*/
|
2007-01-21 13:16:41 -05:00
|
|
|
http_msg_rqbefore:
|
|
|
|
|
case HTTP_MSG_RQBEFORE:
|
|
|
|
|
if (likely(HTTP_IS_TOKEN(*ptr))) {
|
|
|
|
|
if (likely(ptr == buf->data)) {
|
|
|
|
|
msg->sol = ptr;
|
2007-03-03 07:54:32 -05:00
|
|
|
msg->som = 0;
|
2007-01-21 13:16:41 -05:00
|
|
|
} else {
|
|
|
|
|
#if PARSE_PRESERVE_EMPTY_LINES
|
|
|
|
|
/* only skip empty leading lines, don't remove them */
|
|
|
|
|
msg->sol = ptr;
|
2007-03-03 07:54:32 -05:00
|
|
|
msg->som = ptr - buf->data;
|
2007-01-21 13:16:41 -05:00
|
|
|
#else
|
|
|
|
|
/* Remove empty leading lines, as recommended by
|
|
|
|
|
* RFC2616. This takes a lot of time because we
|
|
|
|
|
* must move all the buffer backwards, but this
|
|
|
|
|
* is rarely needed. The method above will be
|
|
|
|
|
* cleaner when we'll be able to start sending
|
|
|
|
|
* the request from any place in the buffer.
|
|
|
|
|
*/
|
|
|
|
|
buf->lr = ptr;
|
|
|
|
|
buffer_replace2(buf, buf->data, buf->lr, NULL, 0);
|
2007-03-03 07:54:32 -05:00
|
|
|
msg->som = 0;
|
2007-01-21 13:16:41 -05:00
|
|
|
msg->sol = buf->data;
|
|
|
|
|
ptr = buf->data;
|
|
|
|
|
end = buf->r;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2007-01-25 06:03:42 -05:00
|
|
|
/* we will need this when keep-alive will be supported
|
|
|
|
|
hdr_idx_init(idx);
|
|
|
|
|
*/
|
2007-01-21 17:58:29 -05:00
|
|
|
state = HTTP_MSG_RQMETH;
|
|
|
|
|
goto http_msg_rqmeth;
|
2007-01-21 13:16:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (unlikely(!HTTP_IS_CRLF(*ptr)))
|
|
|
|
|
goto http_msg_invalid;
|
|
|
|
|
|
|
|
|
|
if (unlikely(*ptr == '\n'))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rqbefore, HTTP_MSG_RQBEFORE);
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rqbefore_cr, HTTP_MSG_RQBEFORE_CR);
|
2007-01-21 17:58:29 -05:00
|
|
|
/* stop here */
|
2007-01-21 13:16:41 -05:00
|
|
|
|
|
|
|
|
http_msg_rqbefore_cr:
|
|
|
|
|
case HTTP_MSG_RQBEFORE_CR:
|
|
|
|
|
EXPECT_LF_HERE(ptr, http_msg_invalid);
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rqbefore, HTTP_MSG_RQBEFORE);
|
2007-01-21 17:58:29 -05:00
|
|
|
/* stop here */
|
2007-01-21 13:16:41 -05:00
|
|
|
|
|
|
|
|
http_msg_rqmeth:
|
|
|
|
|
case HTTP_MSG_RQMETH:
|
|
|
|
|
case HTTP_MSG_RQMETH_SP:
|
|
|
|
|
case HTTP_MSG_RQURI:
|
|
|
|
|
case HTTP_MSG_RQURI_SP:
|
|
|
|
|
case HTTP_MSG_RQVER:
|
|
|
|
|
ptr = (char *)http_parse_reqline(msg, buf->data, state, ptr, end,
|
2007-03-03 07:54:32 -05:00
|
|
|
&buf->lr, &msg->msg_state);
|
2007-01-21 13:16:41 -05:00
|
|
|
if (unlikely(!ptr))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* we have a full request and we know that we have either a CR
|
|
|
|
|
* or an LF at <ptr>.
|
|
|
|
|
*/
|
2007-03-03 07:54:32 -05:00
|
|
|
//fprintf(stderr,"som=%d rq.l=%d *ptr=0x%02x\n", msg->som, msg->sl.rq.l, *ptr);
|
2007-01-21 13:16:41 -05:00
|
|
|
hdr_idx_set_start(idx, msg->sl.rq.l, *ptr == '\r');
|
|
|
|
|
|
|
|
|
|
msg->sol = ptr;
|
|
|
|
|
if (likely(*ptr == '\r'))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_rqline_end, HTTP_MSG_RQLINE_END);
|
|
|
|
|
goto http_msg_rqline_end;
|
|
|
|
|
|
|
|
|
|
http_msg_rqline_end:
|
|
|
|
|
case HTTP_MSG_RQLINE_END:
|
|
|
|
|
/* check for HTTP/0.9 request : no version information available.
|
|
|
|
|
* msg->sol must point to the first of CR or LF.
|
|
|
|
|
*/
|
|
|
|
|
if (unlikely(msg->sl.rq.v_l == 0))
|
|
|
|
|
goto http_msg_last_lf;
|
|
|
|
|
|
|
|
|
|
EXPECT_LF_HERE(ptr, http_msg_invalid);
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_hdr_first, HTTP_MSG_HDR_FIRST);
|
2007-01-21 17:58:29 -05:00
|
|
|
/* stop here */
|
2007-01-21 13:16:41 -05:00
|
|
|
|
2007-01-21 17:58:29 -05:00
|
|
|
/*
|
|
|
|
|
* Common states below
|
|
|
|
|
*/
|
2007-01-21 13:16:41 -05:00
|
|
|
http_msg_hdr_first:
|
|
|
|
|
case HTTP_MSG_HDR_FIRST:
|
|
|
|
|
msg->sol = ptr;
|
|
|
|
|
if (likely(!HTTP_IS_CRLF(*ptr))) {
|
|
|
|
|
goto http_msg_hdr_name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (likely(*ptr == '\r'))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_last_lf, HTTP_MSG_LAST_LF);
|
|
|
|
|
goto http_msg_last_lf;
|
|
|
|
|
|
|
|
|
|
http_msg_hdr_name:
|
|
|
|
|
case HTTP_MSG_HDR_NAME:
|
|
|
|
|
/* assumes msg->sol points to the first char */
|
|
|
|
|
if (likely(HTTP_IS_TOKEN(*ptr)))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_hdr_name, HTTP_MSG_HDR_NAME);
|
|
|
|
|
|
|
|
|
|
if (likely(*ptr == ':')) {
|
|
|
|
|
msg->col = ptr - buf->data;
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_hdr_l1_sp, HTTP_MSG_HDR_L1_SP);
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-02 05:35:18 -04:00
|
|
|
if (likely(msg->err_pos < -1) || *ptr == '\n')
|
|
|
|
|
goto http_msg_invalid;
|
|
|
|
|
|
|
|
|
|
if (msg->err_pos == -1) /* capture error pointer */
|
|
|
|
|
msg->err_pos = ptr - buf->data; /* >= 0 now */
|
|
|
|
|
|
|
|
|
|
/* and we still accept this non-token character */
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_hdr_name, HTTP_MSG_HDR_NAME);
|
2007-01-21 13:16:41 -05:00
|
|
|
|
|
|
|
|
http_msg_hdr_l1_sp:
|
|
|
|
|
case HTTP_MSG_HDR_L1_SP:
|
|
|
|
|
/* assumes msg->sol points to the first char and msg->col to the colon */
|
|
|
|
|
if (likely(HTTP_IS_SPHT(*ptr)))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_hdr_l1_sp, HTTP_MSG_HDR_L1_SP);
|
|
|
|
|
|
|
|
|
|
/* header value can be basically anything except CR/LF */
|
|
|
|
|
msg->sov = ptr - buf->data;
|
|
|
|
|
|
|
|
|
|
if (likely(!HTTP_IS_CRLF(*ptr))) {
|
|
|
|
|
goto http_msg_hdr_val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (likely(*ptr == '\r'))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_hdr_l1_lf, HTTP_MSG_HDR_L1_LF);
|
|
|
|
|
goto http_msg_hdr_l1_lf;
|
|
|
|
|
|
|
|
|
|
http_msg_hdr_l1_lf:
|
|
|
|
|
case HTTP_MSG_HDR_L1_LF:
|
|
|
|
|
EXPECT_LF_HERE(ptr, http_msg_invalid);
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_hdr_l1_lws, HTTP_MSG_HDR_L1_LWS);
|
|
|
|
|
|
|
|
|
|
http_msg_hdr_l1_lws:
|
|
|
|
|
case HTTP_MSG_HDR_L1_LWS:
|
|
|
|
|
if (likely(HTTP_IS_SPHT(*ptr))) {
|
|
|
|
|
/* replace HT,CR,LF with spaces */
|
|
|
|
|
for (; buf->data+msg->sov < ptr; msg->sov++)
|
|
|
|
|
buf->data[msg->sov] = ' ';
|
|
|
|
|
goto http_msg_hdr_l1_sp;
|
|
|
|
|
}
|
2007-03-18 18:50:16 -04:00
|
|
|
/* we had a header consisting only in spaces ! */
|
|
|
|
|
msg->eol = buf->data + msg->sov;
|
2007-01-21 13:16:41 -05:00
|
|
|
goto http_msg_complete_header;
|
|
|
|
|
|
|
|
|
|
http_msg_hdr_val:
|
|
|
|
|
case HTTP_MSG_HDR_VAL:
|
|
|
|
|
/* assumes msg->sol points to the first char, msg->col to the
|
|
|
|
|
* colon, and msg->sov points to the first character of the
|
|
|
|
|
* value.
|
|
|
|
|
*/
|
|
|
|
|
if (likely(!HTTP_IS_CRLF(*ptr)))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_hdr_val, HTTP_MSG_HDR_VAL);
|
|
|
|
|
|
|
|
|
|
msg->eol = ptr;
|
|
|
|
|
/* Note: we could also copy eol into ->eoh so that we have the
|
|
|
|
|
* real header end in case it ends with lots of LWS, but is this
|
|
|
|
|
* really needed ?
|
|
|
|
|
*/
|
|
|
|
|
if (likely(*ptr == '\r'))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_hdr_l2_lf, HTTP_MSG_HDR_L2_LF);
|
|
|
|
|
goto http_msg_hdr_l2_lf;
|
|
|
|
|
|
|
|
|
|
http_msg_hdr_l2_lf:
|
|
|
|
|
case HTTP_MSG_HDR_L2_LF:
|
|
|
|
|
EXPECT_LF_HERE(ptr, http_msg_invalid);
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_hdr_l2_lws, HTTP_MSG_HDR_L2_LWS);
|
|
|
|
|
|
|
|
|
|
http_msg_hdr_l2_lws:
|
|
|
|
|
case HTTP_MSG_HDR_L2_LWS:
|
|
|
|
|
if (unlikely(HTTP_IS_SPHT(*ptr))) {
|
|
|
|
|
/* LWS: replace HT,CR,LF with spaces */
|
|
|
|
|
for (; msg->eol < ptr; msg->eol++)
|
|
|
|
|
*msg->eol = ' ';
|
|
|
|
|
goto http_msg_hdr_val;
|
|
|
|
|
}
|
|
|
|
|
http_msg_complete_header:
|
|
|
|
|
/*
|
|
|
|
|
* It was a new header, so the last one is finished.
|
|
|
|
|
* Assumes msg->sol points to the first char, msg->col to the
|
|
|
|
|
* colon, msg->sov points to the first character of the value
|
|
|
|
|
* and msg->eol to the first CR or LF so we know how the line
|
|
|
|
|
* ends. We insert last header into the index.
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
fprintf(stderr,"registering %-2d bytes : ", msg->eol - msg->sol);
|
|
|
|
|
write(2, msg->sol, msg->eol-msg->sol);
|
|
|
|
|
fprintf(stderr,"\n");
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (unlikely(hdr_idx_add(msg->eol - msg->sol, *msg->eol == '\r',
|
|
|
|
|
idx, idx->tail) < 0))
|
|
|
|
|
goto http_msg_invalid;
|
|
|
|
|
|
|
|
|
|
msg->sol = ptr;
|
|
|
|
|
if (likely(!HTTP_IS_CRLF(*ptr))) {
|
|
|
|
|
goto http_msg_hdr_name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (likely(*ptr == '\r'))
|
|
|
|
|
EAT_AND_JUMP_OR_RETURN(http_msg_last_lf, HTTP_MSG_LAST_LF);
|
|
|
|
|
goto http_msg_last_lf;
|
|
|
|
|
|
|
|
|
|
http_msg_last_lf:
|
|
|
|
|
case HTTP_MSG_LAST_LF:
|
|
|
|
|
/* Assumes msg->sol points to the first of either CR or LF */
|
|
|
|
|
EXPECT_LF_HERE(ptr, http_msg_invalid);
|
|
|
|
|
ptr++;
|
|
|
|
|
buf->lr = ptr;
|
|
|
|
|
msg->eoh = msg->sol - buf->data;
|
2007-03-03 07:54:32 -05:00
|
|
|
msg->msg_state = HTTP_MSG_BODY;
|
2007-01-21 13:16:41 -05:00
|
|
|
return;
|
|
|
|
|
#ifdef DEBUG_FULL
|
|
|
|
|
default:
|
|
|
|
|
fprintf(stderr, "FIXME !!!! impossible state at %s:%d = %d\n", __FILE__, __LINE__, state);
|
|
|
|
|
exit(1);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
http_msg_ood:
|
|
|
|
|
/* out of data */
|
2007-03-03 07:54:32 -05:00
|
|
|
msg->msg_state = state;
|
2007-01-21 13:16:41 -05:00
|
|
|
buf->lr = ptr;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
http_msg_invalid:
|
|
|
|
|
/* invalid message */
|
2007-03-03 07:54:32 -05:00
|
|
|
msg->msg_state = HTTP_MSG_ERROR;
|
2009-03-01 05:10:40 -05:00
|
|
|
buf->lr = ptr;
|
2007-01-21 13:16:41 -05:00
|
|
|
return;
|
|
|
|
|
}
|
2007-11-29 09:43:32 -05:00
|
|
|
|
2009-07-10 18:06:00 -04:00
|
|
|
/* convert an HTTP/0.9 request into an HTTP/1.0 request. Returns 1 if the
|
|
|
|
|
* conversion succeeded, 0 in case of error. If the request was already 1.X,
|
|
|
|
|
* nothing is done and 1 is returned.
|
|
|
|
|
*/
|
|
|
|
|
static int http_upgrade_v09_to_v10(struct buffer *req, struct http_msg *msg, struct http_txn *txn)
|
|
|
|
|
{
|
|
|
|
|
int delta;
|
|
|
|
|
char *cur_end;
|
|
|
|
|
|
|
|
|
|
if (msg->sl.rq.v_l != 0)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
msg->sol = req->data + msg->som;
|
|
|
|
|
cur_end = msg->sol + msg->sl.rq.l;
|
|
|
|
|
delta = 0;
|
|
|
|
|
|
|
|
|
|
if (msg->sl.rq.u_l == 0) {
|
|
|
|
|
/* if no URI was set, add "/" */
|
|
|
|
|
delta = buffer_replace2(req, cur_end, cur_end, " /", 2);
|
|
|
|
|
cur_end += delta;
|
|
|
|
|
msg->eoh += delta;
|
|
|
|
|
}
|
|
|
|
|
/* add HTTP version */
|
|
|
|
|
delta = buffer_replace2(req, cur_end, cur_end, " HTTP/1.0\r\n", 11);
|
|
|
|
|
msg->eoh += delta;
|
|
|
|
|
cur_end += delta;
|
|
|
|
|
cur_end = (char *)http_parse_reqline(msg, req->data,
|
|
|
|
|
HTTP_MSG_RQMETH,
|
|
|
|
|
msg->sol, cur_end + 1,
|
|
|
|
|
NULL, NULL);
|
|
|
|
|
if (unlikely(!cur_end))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* we have a full HTTP/1.0 request now and we know that
|
|
|
|
|
* we have either a CR or an LF at <ptr>.
|
|
|
|
|
*/
|
|
|
|
|
hdr_idx_set_start(&txn->hdr_idx, msg->sl.rq.l, *cur_end == '\r');
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-07 04:14:51 -04:00
|
|
|
/* This stream analyser waits for a complete HTTP request. It returns 1 if the
|
|
|
|
|
* processing can continue on next analysers, or zero if it either needs more
|
|
|
|
|
* data or wants to immediately abort the request (eg: timeout, error, ...). It
|
|
|
|
|
* is tied to AN_REQ_WAIT_HTTP and may may remove itself from s->req->analysers
|
|
|
|
|
* when it has nothing left to do, and may remove any analyser when it wants to
|
|
|
|
|
* abort.
|
2006-06-25 20:48:02 -04:00
|
|
|
*/
|
2009-07-07 04:55:49 -04:00
|
|
|
int http_wait_for_request(struct session *s, struct buffer *req, int an_bit)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
2008-11-30 17:51:27 -05:00
|
|
|
/*
|
|
|
|
|
* We will parse the partial (or complete) lines.
|
|
|
|
|
* We will check the request syntax, and also join multi-line
|
|
|
|
|
* headers. An index of all the lines will be elaborated while
|
|
|
|
|
* parsing.
|
|
|
|
|
*
|
|
|
|
|
* For the parsing, we use a 28 states FSM.
|
|
|
|
|
*
|
|
|
|
|
* Here is the information we currently have :
|
2009-03-01 17:21:47 -05:00
|
|
|
* req->data + msg->som = beginning of request
|
2008-11-30 17:51:27 -05:00
|
|
|
* req->data + req->eoh = end of processed headers / start of current one
|
|
|
|
|
* req->data + req->eol = end of current header or line (LF or CRLF)
|
|
|
|
|
* req->lr = first non-visited byte
|
|
|
|
|
* req->r = end of data
|
2009-07-07 04:14:51 -04:00
|
|
|
*
|
|
|
|
|
* At end of parsing, we may perform a capture of the error (if any), and
|
|
|
|
|
* we will set a few fields (msg->sol, txn->meth, sn->flags/SN_REDIRECTABLE).
|
|
|
|
|
* We also check for monitor-uri, logging, HTTP/0.9 to 1.0 conversion, and
|
|
|
|
|
* finally headers capture.
|
2008-11-30 17:51:27 -05:00
|
|
|
*/
|
2006-12-17 04:06:03 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
int cur_idx;
|
|
|
|
|
struct http_txn *txn = &s->txn;
|
|
|
|
|
struct http_msg *msg = &txn->req;
|
|
|
|
|
|
2009-02-24 04:48:35 -05:00
|
|
|
DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
|
|
|
|
|
now_ms, __FUNCTION__,
|
|
|
|
|
s,
|
|
|
|
|
req,
|
|
|
|
|
req->rex, req->wex,
|
|
|
|
|
req->flags,
|
|
|
|
|
req->l,
|
|
|
|
|
req->analysers);
|
|
|
|
|
|
2009-08-16 16:45:38 -04:00
|
|
|
/* we're speaking HTTP here, so let's speak HTTP to the client */
|
|
|
|
|
s->srv_error = http_return_srv_error;
|
|
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
if (likely(req->lr < req->r))
|
|
|
|
|
http_msg_analyzer(req, msg, &txn->hdr_idx);
|
|
|
|
|
|
|
|
|
|
/* 1: we might have to print this header in debug mode */
|
|
|
|
|
if (unlikely((global.mode & MODE_DEBUG) &&
|
|
|
|
|
(!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) &&
|
|
|
|
|
(msg->msg_state == HTTP_MSG_BODY || msg->msg_state == HTTP_MSG_ERROR))) {
|
|
|
|
|
char *eol, *sol;
|
|
|
|
|
|
|
|
|
|
sol = req->data + msg->som;
|
|
|
|
|
eol = sol + msg->sl.rq.l;
|
|
|
|
|
debug_hdr("clireq", s, sol, eol);
|
|
|
|
|
|
|
|
|
|
sol += hdr_idx_first_pos(&txn->hdr_idx);
|
|
|
|
|
cur_idx = hdr_idx_first_idx(&txn->hdr_idx);
|
|
|
|
|
|
|
|
|
|
while (cur_idx) {
|
|
|
|
|
eol = sol + txn->hdr_idx.v[cur_idx].len;
|
|
|
|
|
debug_hdr("clihdr", s, sol, eol);
|
|
|
|
|
sol = eol + txn->hdr_idx.v[cur_idx].cr + 1;
|
|
|
|
|
cur_idx = txn->hdr_idx.v[cur_idx].next;
|
2007-01-21 13:16:41 -05:00
|
|
|
}
|
2008-11-30 17:51:27 -05:00
|
|
|
}
|
|
|
|
|
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/*
|
|
|
|
|
* Now we quickly check if we have found a full valid request.
|
|
|
|
|
* If not so, we check the FD and buffer states before leaving.
|
|
|
|
|
* A full request is indicated by the fact that we have seen
|
|
|
|
|
* the double LF/CRLF, so the state is HTTP_MSG_BODY. Invalid
|
|
|
|
|
* requests are checked first.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
|
2006-12-03 20:26:12 -05:00
|
|
|
/*
|
2008-11-30 17:51:27 -05:00
|
|
|
* First, let's catch bad requests.
|
2006-12-03 20:26:12 -05:00
|
|
|
*/
|
2008-11-30 17:51:27 -05:00
|
|
|
if (unlikely(msg->msg_state == HTTP_MSG_ERROR))
|
|
|
|
|
goto return_bad_req;
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* 1: Since we are in header mode, if there's no space
|
|
|
|
|
* left for headers, we won't be able to free more
|
|
|
|
|
* later, so the session will never terminate. We
|
|
|
|
|
* must terminate it now.
|
|
|
|
|
*/
|
|
|
|
|
if (unlikely(req->flags & BF_FULL)) {
|
|
|
|
|
/* FIXME: check if URI is set and return Status
|
|
|
|
|
* 414 Request URI too long instead.
|
2006-12-03 20:26:12 -05:00
|
|
|
*/
|
2008-11-30 17:51:27 -05:00
|
|
|
goto return_bad_req;
|
|
|
|
|
}
|
2008-08-27 17:57:16 -04:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* 2: have we encountered a read error ? */
|
|
|
|
|
else if (req->flags & BF_READ_ERROR) {
|
|
|
|
|
/* we cannot return any message on error */
|
2009-04-02 09:18:36 -04:00
|
|
|
if (msg->err_pos >= 0)
|
|
|
|
|
http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe);
|
2008-11-30 17:51:27 -05:00
|
|
|
msg->msg_state = HTTP_MSG_ERROR;
|
|
|
|
|
req->analysers = 0;
|
2009-10-04 08:52:57 -04:00
|
|
|
s->fe->counters.failed_req++;
|
2008-11-30 17:51:27 -05:00
|
|
|
if (!(s->flags & SN_ERR_MASK))
|
|
|
|
|
s->flags |= SN_ERR_CLICL;
|
|
|
|
|
if (!(s->flags & SN_FINST_MASK))
|
|
|
|
|
s->flags |= SN_FINST_R;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2008-08-11 17:42:50 -04:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* 3: has the read timeout expired ? */
|
|
|
|
|
else if (req->flags & BF_READ_TIMEOUT || tick_is_expired(req->analyse_exp, now_ms)) {
|
|
|
|
|
/* read timeout : give up with an error message. */
|
2009-04-02 09:18:36 -04:00
|
|
|
if (msg->err_pos >= 0)
|
|
|
|
|
http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe);
|
2008-11-30 17:51:27 -05:00
|
|
|
txn->status = 408;
|
|
|
|
|
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_408));
|
|
|
|
|
msg->msg_state = HTTP_MSG_ERROR;
|
|
|
|
|
req->analysers = 0;
|
2009-10-04 08:52:57 -04:00
|
|
|
s->fe->counters.failed_req++;
|
2008-11-30 17:51:27 -05:00
|
|
|
if (!(s->flags & SN_ERR_MASK))
|
|
|
|
|
s->flags |= SN_ERR_CLITO;
|
|
|
|
|
if (!(s->flags & SN_FINST_MASK))
|
|
|
|
|
s->flags |= SN_FINST_R;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2008-08-11 17:42:50 -04:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* 4: have we encountered a close ? */
|
|
|
|
|
else if (req->flags & BF_SHUTR) {
|
2009-04-02 09:18:36 -04:00
|
|
|
if (msg->err_pos >= 0)
|
|
|
|
|
http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe);
|
2008-11-30 17:51:27 -05:00
|
|
|
txn->status = 400;
|
|
|
|
|
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_400));
|
|
|
|
|
msg->msg_state = HTTP_MSG_ERROR;
|
|
|
|
|
req->analysers = 0;
|
2009-10-04 08:52:57 -04:00
|
|
|
s->fe->counters.failed_req++;
|
2008-11-30 17:51:27 -05:00
|
|
|
if (!(s->flags & SN_ERR_MASK))
|
|
|
|
|
s->flags |= SN_ERR_CLICL;
|
|
|
|
|
if (!(s->flags & SN_FINST_MASK))
|
|
|
|
|
s->flags |= SN_FINST_R;
|
2008-08-16 19:00:46 -04:00
|
|
|
return 0;
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
|
|
|
|
|
2009-09-19 15:04:57 -04:00
|
|
|
buffer_dont_connect(req);
|
2009-03-21 16:10:04 -04:00
|
|
|
req->flags |= BF_READ_DONTWAIT; /* try to get back here ASAP */
|
|
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* just set the request timeout once at the beginning of the request */
|
|
|
|
|
if (!tick_isset(req->analyse_exp))
|
2009-07-12 04:03:17 -04:00
|
|
|
req->analyse_exp = tick_add_ifset(now_ms, s->be->timeout.httpreq);
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* we're not ready yet */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2009-07-07 04:14:51 -04:00
|
|
|
/* OK now we have a complete HTTP request with indexed headers. Let's
|
|
|
|
|
* complete the request parsing by setting a few fields we will need
|
|
|
|
|
* later.
|
|
|
|
|
*/
|
2008-08-10 16:55:22 -04:00
|
|
|
|
2009-07-07 04:14:51 -04:00
|
|
|
/* Maybe we found in invalid header name while we were configured not
|
|
|
|
|
* to block on that, so we have to capture it now.
|
|
|
|
|
*/
|
|
|
|
|
if (unlikely(msg->err_pos >= 0))
|
2009-04-02 09:18:36 -04:00
|
|
|
http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe);
|
|
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* ensure we keep this pointer to the beginning of the message */
|
|
|
|
|
msg->sol = req->data + msg->som;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 1: identify the method
|
|
|
|
|
*/
|
|
|
|
|
txn->meth = find_http_meth(&req->data[msg->som], msg->sl.rq.m_l);
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* we can make use of server redirect on GET and HEAD */
|
|
|
|
|
if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
|
|
|
|
|
s->flags |= SN_REDIRECTABLE;
|
[MAJOR] rework of the server FSM
srv_state has been removed from HTTP state machines, and states
have been split in either TCP states or analyzers. For instance,
the TARPIT state has just become a simple analyzer.
New flags have been added to the struct buffer to compensate this.
The high-level stream processors sometimes need to force a disconnection
without touching a file-descriptor (eg: report an error). But if
they touched BF_SHUTW or BF_SHUTR, the file descriptor would not
be closed. Thus, the two SHUT?_NOW flags have been added so that
an application can request a forced close which the stream interface
will be forced to obey.
During this change, a new BF_HIJACK flag was added. It will
be used for data generation, eg during a stats dump. It
prevents the producer on a buffer from sending data into it.
BF_SHUTR_NOW /* the producer must shut down for reads ASAP */
BF_SHUTW_NOW /* the consumer must shut down for writes ASAP */
BF_HIJACK /* the producer is temporarily replaced */
BF_SHUTW_NOW has precedence over BF_HIJACK. BF_HIJACK has
precedence over BF_MAY_FORWARD (so that it does not need it).
New functions buffer_shutr_now(), buffer_shutw_now(), buffer_abort()
are provided to manipulate BF_SHUT* flags.
A new type "stream_interface" has been added to describe both
sides of a buffer. A stream interface has states and error
reporting. The session now has two stream interfaces (one per
side). Each buffer has stream_interface pointers to both
consumer and producer sides.
The server-side file descriptor has moved to its stream interface,
so that even the buffer has access to it.
process_srv() has been split into three parts :
- tcp_get_connection() obtains a connection to the server
- tcp_connection_failed() tests if a previously attempted
connection has succeeded or not.
- process_srv_data() only manages the data phase, and in
this sense should be roughly equivalent to process_cli.
Little code has been removed, and a lot of old code has been
left in comments for now.
2008-10-19 01:30:41 -04:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/*
|
|
|
|
|
* 2: check if the URI matches the monitor_uri.
|
|
|
|
|
* We have to do this for every request which gets in, because
|
|
|
|
|
* the monitor-uri is defined by the frontend.
|
|
|
|
|
*/
|
|
|
|
|
if (unlikely((s->fe->monitor_uri_len != 0) &&
|
|
|
|
|
(s->fe->monitor_uri_len == msg->sl.rq.u_l) &&
|
|
|
|
|
!memcmp(&req->data[msg->sl.rq.u],
|
|
|
|
|
s->fe->monitor_uri,
|
|
|
|
|
s->fe->monitor_uri_len))) {
|
2006-12-03 20:26:12 -05:00
|
|
|
/*
|
2008-11-30 17:51:27 -05:00
|
|
|
* We have found the monitor URI
|
2006-12-03 20:26:12 -05:00
|
|
|
*/
|
2008-11-30 17:51:27 -05:00
|
|
|
struct acl_cond *cond;
|
2007-11-30 14:51:32 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
s->flags |= SN_MONITOR;
|
2007-11-30 14:51:32 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* Check if we want to fail this monitor request or not */
|
2009-07-07 04:14:51 -04:00
|
|
|
list_for_each_entry(cond, &s->fe->mon_fail_cond, list) {
|
|
|
|
|
int ret = acl_exec_cond(cond, s->fe, s, txn, ACL_DIR_REQ);
|
2008-07-09 10:18:21 -04:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
ret = acl_pass(ret);
|
|
|
|
|
if (cond->pol == ACL_COND_UNLESS)
|
|
|
|
|
ret = !ret;
|
2007-11-30 14:51:32 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
if (ret) {
|
|
|
|
|
/* we fail this request, let's return 503 service unavail */
|
|
|
|
|
txn->status = 503;
|
|
|
|
|
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_503));
|
|
|
|
|
goto return_prx_cond;
|
2007-11-30 14:51:32 -05:00
|
|
|
}
|
2007-01-21 13:16:41 -05:00
|
|
|
}
|
2008-11-30 13:02:32 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* nothing to fail, let's reply normaly */
|
|
|
|
|
txn->status = 200;
|
|
|
|
|
stream_int_retnclose(req->prod, &http_200_chunk);
|
|
|
|
|
goto return_prx_cond;
|
|
|
|
|
}
|
2006-12-17 02:37:22 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/*
|
|
|
|
|
* 3: Maybe we have to copy the original REQURI for the logs ?
|
|
|
|
|
* Note: we cannot log anymore if the request has been
|
|
|
|
|
* classified as invalid.
|
|
|
|
|
*/
|
|
|
|
|
if (unlikely(s->logs.logwait & LW_REQ)) {
|
|
|
|
|
/* we have a complete HTTP request that we must log */
|
|
|
|
|
if ((txn->uri = pool_alloc2(pool2_requri)) != NULL) {
|
|
|
|
|
int urilen = msg->sl.rq.l;
|
|
|
|
|
|
|
|
|
|
if (urilen >= REQURI_LEN)
|
|
|
|
|
urilen = REQURI_LEN - 1;
|
|
|
|
|
memcpy(txn->uri, &req->data[msg->som], urilen);
|
|
|
|
|
txn->uri[urilen] = 0;
|
|
|
|
|
|
|
|
|
|
if (!(s->logs.logwait &= ~LW_REQ))
|
|
|
|
|
s->do_log(s);
|
|
|
|
|
} else {
|
|
|
|
|
Alert("HTTP logging : out of memory.\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-12-17 02:37:22 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* 4. We may have to convert HTTP/0.9 requests to HTTP/1.0 */
|
2009-07-10 18:06:00 -04:00
|
|
|
if (unlikely(msg->sl.rq.v_l == 0) && !http_upgrade_v09_to_v10(req, msg, txn))
|
|
|
|
|
goto return_bad_req;
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* 5: we may need to capture headers */
|
|
|
|
|
if (unlikely((s->logs.logwait & LW_REQHDR) && s->fe->req_cap))
|
|
|
|
|
capture_headers(req->data + msg->som, &txn->hdr_idx,
|
|
|
|
|
txn->req.cap, s->fe->req_cap);
|
2007-01-21 13:16:41 -05:00
|
|
|
|
2009-07-07 04:14:51 -04:00
|
|
|
/* end of job, return OK */
|
2009-07-07 04:55:49 -04:00
|
|
|
req->analysers &= ~an_bit;
|
2009-07-07 04:14:51 -04:00
|
|
|
req->analyse_exp = TICK_ETERNITY;
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
return_bad_req:
|
|
|
|
|
/* We centralize bad requests processing here */
|
|
|
|
|
if (unlikely(msg->msg_state == HTTP_MSG_ERROR) || msg->err_pos >= 0) {
|
|
|
|
|
/* we detected a parsing error. We want to archive this request
|
|
|
|
|
* in the dedicated proxy area for later troubleshooting.
|
|
|
|
|
*/
|
|
|
|
|
http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
txn->req.msg_state = HTTP_MSG_ERROR;
|
|
|
|
|
txn->status = 400;
|
|
|
|
|
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_400));
|
2009-10-04 08:52:57 -04:00
|
|
|
s->fe->counters.failed_req++;
|
2009-07-07 04:14:51 -04:00
|
|
|
|
|
|
|
|
return_prx_cond:
|
|
|
|
|
if (!(s->flags & SN_ERR_MASK))
|
|
|
|
|
s->flags |= SN_ERR_PRXCOND;
|
|
|
|
|
if (!(s->flags & SN_FINST_MASK))
|
|
|
|
|
s->flags |= SN_FINST_R;
|
|
|
|
|
|
|
|
|
|
req->analysers = 0;
|
|
|
|
|
req->analyse_exp = TICK_ETERNITY;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
/* This stream analyser runs all HTTP request processing which is common to
|
|
|
|
|
* frontends and backends, which means blocking ACLs, filters, connection-close,
|
|
|
|
|
* reqadd, stats and redirects. This is performed for the designated proxy.
|
2009-07-07 04:14:51 -04:00
|
|
|
* It returns 1 if the processing can continue on next analysers, or zero if it
|
2009-07-07 09:10:31 -04:00
|
|
|
* either needs more data or wants to immediately abort the request (eg: deny,
|
|
|
|
|
* error, ...).
|
2009-07-07 04:14:51 -04:00
|
|
|
*/
|
2009-07-07 09:10:31 -04:00
|
|
|
int http_process_req_common(struct session *s, struct buffer *req, int an_bit, struct proxy *px)
|
2009-07-07 04:14:51 -04:00
|
|
|
{
|
|
|
|
|
struct http_txn *txn = &s->txn;
|
|
|
|
|
struct http_msg *msg = &txn->req;
|
2009-07-07 09:10:31 -04:00
|
|
|
struct acl_cond *cond;
|
|
|
|
|
struct redirect_rule *rule;
|
|
|
|
|
int cur_idx;
|
2009-07-07 04:14:51 -04:00
|
|
|
|
2009-07-12 03:47:04 -04:00
|
|
|
if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
|
|
|
|
|
/* we need more data */
|
2009-09-19 15:04:57 -04:00
|
|
|
buffer_dont_connect(req);
|
2009-07-12 03:47:04 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-07 04:55:49 -04:00
|
|
|
req->analysers &= ~an_bit;
|
2009-07-07 04:14:51 -04:00
|
|
|
req->analyse_exp = TICK_ETERNITY;
|
|
|
|
|
|
|
|
|
|
DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
|
|
|
|
|
now_ms, __FUNCTION__,
|
|
|
|
|
s,
|
|
|
|
|
req,
|
|
|
|
|
req->rex, req->wex,
|
|
|
|
|
req->flags,
|
|
|
|
|
req->l,
|
|
|
|
|
req->analysers);
|
|
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
/* first check whether we have some ACLs set to block this request */
|
|
|
|
|
list_for_each_entry(cond, &px->block_cond, list) {
|
|
|
|
|
int ret = acl_exec_cond(cond, px, s, txn, ACL_DIR_REQ);
|
2006-12-17 16:14:12 -05:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
ret = acl_pass(ret);
|
|
|
|
|
if (cond->pol == ACL_COND_UNLESS)
|
|
|
|
|
ret = !ret;
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
if (ret) {
|
|
|
|
|
txn->status = 403;
|
|
|
|
|
/* let's log the request time */
|
|
|
|
|
s->logs.tv_request = now;
|
|
|
|
|
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403));
|
|
|
|
|
goto return_prx_cond;
|
2008-11-30 17:51:27 -05:00
|
|
|
}
|
2009-07-07 09:10:31 -04:00
|
|
|
}
|
2008-11-30 17:51:27 -05:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
/* try headers filters */
|
|
|
|
|
if (px->req_exp != NULL) {
|
|
|
|
|
if (apply_filters_to_request(s, req, px->req_exp) < 0)
|
|
|
|
|
goto return_bad_req;
|
2008-11-30 17:51:27 -05:00
|
|
|
|
|
|
|
|
/* has the request been denied ? */
|
|
|
|
|
if (txn->flags & TX_CLDENY) {
|
|
|
|
|
/* no need to go further */
|
|
|
|
|
txn->status = 403;
|
|
|
|
|
/* let's log the request time */
|
|
|
|
|
s->logs.tv_request = now;
|
|
|
|
|
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403));
|
|
|
|
|
goto return_prx_cond;
|
|
|
|
|
}
|
2009-08-30 18:17:18 -04:00
|
|
|
|
|
|
|
|
/* When a connection is tarpitted, we use the tarpit timeout,
|
|
|
|
|
* which may be the same as the connect timeout if unspecified.
|
|
|
|
|
* If unset, then set it to zero because we really want it to
|
|
|
|
|
* eventually expire. We build the tarpit as an analyser.
|
|
|
|
|
*/
|
|
|
|
|
if (txn->flags & TX_CLTARPIT) {
|
|
|
|
|
buffer_erase(s->req);
|
|
|
|
|
/* wipe the request out so that we can drop the connection early
|
|
|
|
|
* if the client closes first.
|
|
|
|
|
*/
|
2009-09-19 15:04:57 -04:00
|
|
|
buffer_dont_connect(req);
|
2009-08-30 18:17:18 -04:00
|
|
|
req->analysers = 0; /* remove switching rules etc... */
|
|
|
|
|
req->analysers |= AN_REQ_HTTP_TARPIT;
|
|
|
|
|
req->analyse_exp = tick_add_ifset(now_ms, s->be->timeout.tarpit);
|
|
|
|
|
if (!req->analyse_exp)
|
|
|
|
|
req->analyse_exp = tick_add(now_ms, 0);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2009-07-07 09:10:31 -04:00
|
|
|
}
|
2008-11-30 17:51:27 -05:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
/* We might have to check for "Connection:" */
|
|
|
|
|
if (((s->fe->options | s->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO)) &&
|
|
|
|
|
!(s->flags & SN_CONN_CLOSED)) {
|
|
|
|
|
char *cur_ptr, *cur_end, *cur_next;
|
|
|
|
|
int old_idx, delta, val;
|
|
|
|
|
struct hdr_idx_elem *cur_hdr;
|
|
|
|
|
|
|
|
|
|
cur_next = req->data + txn->req.som + hdr_idx_first_pos(&txn->hdr_idx);
|
|
|
|
|
old_idx = 0;
|
|
|
|
|
|
|
|
|
|
while ((cur_idx = txn->hdr_idx.v[old_idx].next)) {
|
|
|
|
|
cur_hdr = &txn->hdr_idx.v[cur_idx];
|
|
|
|
|
cur_ptr = cur_next;
|
|
|
|
|
cur_end = cur_ptr + cur_hdr->len;
|
|
|
|
|
cur_next = cur_end + cur_hdr->cr + 1;
|
|
|
|
|
|
|
|
|
|
val = http_header_match2(cur_ptr, cur_end, "Connection", 10);
|
|
|
|
|
if (val) {
|
|
|
|
|
/* 3 possibilities :
|
|
|
|
|
* - we have already set Connection: close,
|
|
|
|
|
* so we remove this line.
|
|
|
|
|
* - we have not yet set Connection: close,
|
|
|
|
|
* but this line indicates close. We leave
|
|
|
|
|
* it untouched and set the flag.
|
|
|
|
|
* - we have not yet set Connection: close,
|
|
|
|
|
* and this line indicates non-close. We
|
|
|
|
|
* replace it.
|
|
|
|
|
*/
|
|
|
|
|
if (s->flags & SN_CONN_CLOSED) {
|
|
|
|
|
delta = buffer_replace2(req, cur_ptr, cur_next, NULL, 0);
|
|
|
|
|
txn->req.eoh += delta;
|
|
|
|
|
cur_next += delta;
|
|
|
|
|
txn->hdr_idx.v[old_idx].next = cur_hdr->next;
|
|
|
|
|
txn->hdr_idx.used--;
|
|
|
|
|
cur_hdr->len = 0;
|
|
|
|
|
} else {
|
|
|
|
|
if (strncasecmp(cur_ptr + val, "close", 5) != 0) {
|
|
|
|
|
delta = buffer_replace2(req, cur_ptr + val, cur_end,
|
|
|
|
|
"close", 5);
|
2008-11-30 17:51:27 -05:00
|
|
|
cur_next += delta;
|
2009-07-07 09:10:31 -04:00
|
|
|
cur_hdr->len += delta;
|
|
|
|
|
txn->req.eoh += delta;
|
2007-01-21 13:16:41 -05:00
|
|
|
}
|
2009-07-07 09:10:31 -04:00
|
|
|
s->flags |= SN_CONN_CLOSED;
|
2007-01-21 13:16:41 -05:00
|
|
|
}
|
2007-03-30 06:02:43 -04:00
|
|
|
}
|
2009-07-07 09:10:31 -04:00
|
|
|
old_idx = cur_idx;
|
2008-11-30 17:51:27 -05:00
|
|
|
}
|
2009-07-07 09:10:31 -04:00
|
|
|
}
|
|
|
|
|
/* add request headers from the rule sets in the same order */
|
|
|
|
|
for (cur_idx = 0; cur_idx < px->nb_reqadd; cur_idx++) {
|
|
|
|
|
if (unlikely(http_header_add_tail(req,
|
|
|
|
|
&txn->req,
|
|
|
|
|
&txn->hdr_idx,
|
|
|
|
|
px->req_add[cur_idx])) < 0)
|
|
|
|
|
goto return_bad_req;
|
|
|
|
|
}
|
2006-12-17 08:52:38 -05:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
/* check if stats URI was requested, and if an auth is needed */
|
|
|
|
|
if (px->uri_auth != NULL &&
|
|
|
|
|
(txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)) {
|
|
|
|
|
/* we have to check the URI and auth for this request.
|
|
|
|
|
* FIXME!!! that one is rather dangerous, we want to
|
|
|
|
|
* make it follow standard rules (eg: clear req->analysers).
|
|
|
|
|
*/
|
|
|
|
|
if (stats_check_uri_auth(s, px)) {
|
|
|
|
|
req->analysers = 0;
|
|
|
|
|
return 0;
|
2008-11-30 17:51:27 -05:00
|
|
|
}
|
2009-07-07 09:10:31 -04:00
|
|
|
}
|
2006-12-17 08:52:38 -05:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
/* check whether we have some ACLs set to redirect this request */
|
|
|
|
|
list_for_each_entry(rule, &px->redirect_rules, list) {
|
|
|
|
|
int ret = acl_exec_cond(rule->cond, px, s, txn, ACL_DIR_REQ);
|
2009-07-06 10:34:52 -04:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
ret = acl_pass(ret);
|
|
|
|
|
if (rule->cond->pol == ACL_COND_UNLESS)
|
|
|
|
|
ret = !ret;
|
2009-07-06 10:34:52 -04:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
if (ret) {
|
|
|
|
|
struct chunk rdr = { trash, 0 };
|
|
|
|
|
const char *msg_fmt;
|
2009-07-06 10:34:52 -04:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
/* build redirect message */
|
|
|
|
|
switch(rule->code) {
|
|
|
|
|
case 303:
|
|
|
|
|
msg_fmt = HTTP_303;
|
|
|
|
|
break;
|
|
|
|
|
case 301:
|
|
|
|
|
msg_fmt = HTTP_301;
|
|
|
|
|
break;
|
|
|
|
|
case 302:
|
|
|
|
|
default:
|
|
|
|
|
msg_fmt = HTTP_302;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-27 07:23:20 -04:00
|
|
|
if (unlikely(chunk_strcpy(&rdr, msg_fmt)))
|
2009-07-07 09:10:31 -04:00
|
|
|
goto return_bad_req;
|
|
|
|
|
|
|
|
|
|
switch(rule->type) {
|
|
|
|
|
case REDIRECT_TYPE_PREFIX: {
|
|
|
|
|
const char *path;
|
|
|
|
|
int pathlen;
|
|
|
|
|
|
|
|
|
|
path = http_get_path(txn);
|
|
|
|
|
/* build message using path */
|
|
|
|
|
if (path) {
|
|
|
|
|
pathlen = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path;
|
|
|
|
|
if (rule->flags & REDIRECT_FLAG_DROP_QS) {
|
|
|
|
|
int qs = 0;
|
|
|
|
|
while (qs < pathlen) {
|
|
|
|
|
if (path[qs] == '?') {
|
|
|
|
|
pathlen = qs;
|
|
|
|
|
break;
|
2009-07-06 10:34:52 -04:00
|
|
|
}
|
2009-07-07 09:10:31 -04:00
|
|
|
qs++;
|
2009-07-06 10:34:52 -04:00
|
|
|
}
|
|
|
|
|
}
|
2009-07-07 09:10:31 -04:00
|
|
|
} else {
|
|
|
|
|
path = "/";
|
|
|
|
|
pathlen = 1;
|
2009-07-06 10:34:52 -04:00
|
|
|
}
|
|
|
|
|
|
2009-09-27 07:23:20 -04:00
|
|
|
if (rdr.len + rule->rdr_len + pathlen > rdr.size - 4)
|
2009-07-07 09:10:31 -04:00
|
|
|
goto return_bad_req;
|
|
|
|
|
|
|
|
|
|
/* add prefix. Note that if prefix == "/", we don't want to
|
|
|
|
|
* add anything, otherwise it makes it hard for the user to
|
|
|
|
|
* configure a self-redirection.
|
|
|
|
|
*/
|
|
|
|
|
if (rule->rdr_len != 1 || *rule->rdr_str != '/') {
|
2009-07-06 10:34:52 -04:00
|
|
|
memcpy(rdr.str + rdr.len, rule->rdr_str, rule->rdr_len);
|
|
|
|
|
rdr.len += rule->rdr_len;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
/* add path */
|
|
|
|
|
memcpy(rdr.str + rdr.len, path, pathlen);
|
|
|
|
|
rdr.len += pathlen;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case REDIRECT_TYPE_LOCATION:
|
|
|
|
|
default:
|
2009-09-27 07:23:20 -04:00
|
|
|
if (rdr.len + rule->rdr_len > rdr.size - 4)
|
2009-07-07 09:10:31 -04:00
|
|
|
goto return_bad_req;
|
2009-07-06 10:34:52 -04:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
/* add location */
|
|
|
|
|
memcpy(rdr.str + rdr.len, rule->rdr_str, rule->rdr_len);
|
|
|
|
|
rdr.len += rule->rdr_len;
|
|
|
|
|
break;
|
2009-07-06 10:34:52 -04:00
|
|
|
}
|
|
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
if (rule->cookie_len) {
|
|
|
|
|
memcpy(rdr.str + rdr.len, "\r\nSet-Cookie: ", 14);
|
|
|
|
|
rdr.len += 14;
|
|
|
|
|
memcpy(rdr.str + rdr.len, rule->cookie_str, rule->cookie_len);
|
|
|
|
|
rdr.len += rule->cookie_len;
|
|
|
|
|
memcpy(rdr.str + rdr.len, "\r\n", 2);
|
|
|
|
|
rdr.len += 2;
|
2007-06-17 13:56:27 -04:00
|
|
|
}
|
2006-12-17 16:14:12 -05:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
/* add end of headers */
|
|
|
|
|
memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
|
|
|
|
|
rdr.len += 4;
|
|
|
|
|
|
|
|
|
|
txn->status = rule->code;
|
|
|
|
|
/* let's log the request time */
|
|
|
|
|
s->logs.tv_request = now;
|
|
|
|
|
stream_int_retnclose(req->prod, &rdr);
|
|
|
|
|
goto return_prx_cond;
|
2007-11-29 09:43:32 -05:00
|
|
|
}
|
2009-07-07 09:10:31 -04:00
|
|
|
}
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
/* that's OK for us now, let's move on to next analysers */
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
return_bad_req:
|
|
|
|
|
/* We centralize bad requests processing here */
|
|
|
|
|
if (unlikely(msg->msg_state == HTTP_MSG_ERROR) || msg->err_pos >= 0) {
|
|
|
|
|
/* we detected a parsing error. We want to archive this request
|
|
|
|
|
* in the dedicated proxy area for later troubleshooting.
|
2006-12-17 02:37:22 -05:00
|
|
|
*/
|
2009-07-07 09:10:31 -04:00
|
|
|
http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe);
|
2008-11-30 17:51:27 -05:00
|
|
|
}
|
2006-12-17 02:37:22 -05:00
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
txn->req.msg_state = HTTP_MSG_ERROR;
|
|
|
|
|
txn->status = 400;
|
|
|
|
|
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_400));
|
2009-10-04 08:52:57 -04:00
|
|
|
s->fe->counters.failed_req++;
|
2009-07-07 09:10:31 -04:00
|
|
|
|
|
|
|
|
return_prx_cond:
|
|
|
|
|
if (!(s->flags & SN_ERR_MASK))
|
|
|
|
|
s->flags |= SN_ERR_PRXCOND;
|
|
|
|
|
if (!(s->flags & SN_FINST_MASK))
|
|
|
|
|
s->flags |= SN_FINST_R;
|
|
|
|
|
|
|
|
|
|
req->analysers = 0;
|
|
|
|
|
req->analyse_exp = TICK_ETERNITY;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This function performs all the processing enabled for the current request.
|
|
|
|
|
* It returns 1 if the processing can continue on next analysers, or zero if it
|
|
|
|
|
* needs more data, encounters an error, or wants to immediately abort the
|
|
|
|
|
* request. It relies on buffers flags, and updates s->req->analysers.
|
|
|
|
|
*/
|
|
|
|
|
int http_process_request(struct session *s, struct buffer *req, int an_bit)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = &s->txn;
|
|
|
|
|
struct http_msg *msg = &txn->req;
|
|
|
|
|
|
2009-07-12 03:47:04 -04:00
|
|
|
if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
|
|
|
|
|
/* we need more data */
|
2009-09-19 15:04:57 -04:00
|
|
|
buffer_dont_connect(req);
|
2009-07-12 03:47:04 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-07 09:10:31 -04:00
|
|
|
req->analysers &= ~an_bit;
|
|
|
|
|
req->analyse_exp = TICK_ETERNITY;
|
|
|
|
|
|
|
|
|
|
DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
|
|
|
|
|
now_ms, __FUNCTION__,
|
|
|
|
|
s,
|
|
|
|
|
req,
|
|
|
|
|
req->rex, req->wex,
|
|
|
|
|
req->flags,
|
|
|
|
|
req->l,
|
|
|
|
|
req->analysers);
|
|
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/*
|
|
|
|
|
* Right now, we know that we have processed the entire headers
|
|
|
|
|
* and that unwanted requests have been filtered out. We can do
|
|
|
|
|
* whatever we want with the remaining request. Also, now we
|
|
|
|
|
* may have separate values for ->fe, ->be.
|
|
|
|
|
*/
|
2006-12-17 02:37:22 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/*
|
|
|
|
|
* If HTTP PROXY is set we simply get remote server address
|
|
|
|
|
* parsing incoming request.
|
|
|
|
|
*/
|
|
|
|
|
if ((s->be->options & PR_O_HTTP_PROXY) && !(s->flags & SN_ADDR_SET)) {
|
|
|
|
|
url2sa(req->data + msg->sl.rq.u, msg->sl.rq.u_l, &s->srv_addr);
|
|
|
|
|
}
|
2006-12-17 02:37:22 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/*
|
|
|
|
|
* 7: the appsession cookie was looked up very early in 1.2,
|
|
|
|
|
* so let's do the same now.
|
|
|
|
|
*/
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* It needs to look into the URI */
|
|
|
|
|
if (s->be->appsession_name) {
|
|
|
|
|
get_srv_from_appsession(s, &req->data[msg->som], msg->sl.rq.l);
|
|
|
|
|
}
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/*
|
|
|
|
|
* 8: Now we can work with the cookies.
|
|
|
|
|
* Note that doing so might move headers in the request, but
|
|
|
|
|
* the fields will stay coherent and the URI will not move.
|
|
|
|
|
* This should only be performed in the backend.
|
|
|
|
|
*/
|
2008-10-17 06:01:58 -04:00
|
|
|
if ((s->be->cookie_name || s->be->appsession_name || s->fe->capture_name)
|
2008-11-30 17:51:27 -05:00
|
|
|
&& !(txn->flags & (TX_CLDENY|TX_CLTARPIT)))
|
|
|
|
|
manage_client_side_cookies(s, req);
|
2007-03-25 10:00:04 -04:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/*
|
|
|
|
|
* 9: add X-Forwarded-For if either the frontend or the backend
|
|
|
|
|
* asks for it.
|
|
|
|
|
*/
|
|
|
|
|
if ((s->fe->options | s->be->options) & PR_O_FWDFOR) {
|
|
|
|
|
if (s->cli_addr.ss_family == AF_INET) {
|
|
|
|
|
/* Add an X-Forwarded-For header unless the source IP is
|
|
|
|
|
* in the 'except' network range.
|
|
|
|
|
*/
|
|
|
|
|
if ((!s->fe->except_mask.s_addr ||
|
|
|
|
|
(((struct sockaddr_in *)&s->cli_addr)->sin_addr.s_addr & s->fe->except_mask.s_addr)
|
|
|
|
|
!= s->fe->except_net.s_addr) &&
|
|
|
|
|
(!s->be->except_mask.s_addr ||
|
|
|
|
|
(((struct sockaddr_in *)&s->cli_addr)->sin_addr.s_addr & s->be->except_mask.s_addr)
|
|
|
|
|
!= s->be->except_net.s_addr)) {
|
2006-12-04 18:05:46 -05:00
|
|
|
int len;
|
2008-11-30 17:51:27 -05:00
|
|
|
unsigned char *pn;
|
|
|
|
|
pn = (unsigned char *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr;
|
2008-08-03 04:51:45 -04:00
|
|
|
|
|
|
|
|
/* Note: we rely on the backend to get the header name to be used for
|
|
|
|
|
* x-forwarded-for, because the header is really meant for the backends.
|
|
|
|
|
* However, if the backend did not specify any option, we have to rely
|
|
|
|
|
* on the frontend's header name.
|
|
|
|
|
*/
|
2008-11-30 17:51:27 -05:00
|
|
|
if (s->be->fwdfor_hdr_len) {
|
|
|
|
|
len = s->be->fwdfor_hdr_len;
|
|
|
|
|
memcpy(trash, s->be->fwdfor_hdr_name, len);
|
2008-08-03 04:51:45 -04:00
|
|
|
} else {
|
2008-11-30 17:51:27 -05:00
|
|
|
len = s->fe->fwdfor_hdr_len;
|
|
|
|
|
memcpy(trash, s->fe->fwdfor_hdr_name, len);
|
|
|
|
|
}
|
|
|
|
|
len += sprintf(trash + len, ": %d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
|
2008-11-30 17:15:34 -05:00
|
|
|
|
2007-03-18 17:36:26 -04:00
|
|
|
if (unlikely(http_header_add_tail2(req, &txn->req,
|
|
|
|
|
&txn->hdr_idx, trash, len)) < 0)
|
2006-12-17 02:37:22 -05:00
|
|
|
goto return_bad_req;
|
2006-12-04 18:05:46 -05:00
|
|
|
}
|
|
|
|
|
}
|
2008-11-30 17:51:27 -05:00
|
|
|
else if (s->cli_addr.ss_family == AF_INET6) {
|
|
|
|
|
/* FIXME: for the sake of completeness, we should also support
|
|
|
|
|
* 'except' here, although it is mostly useless in this case.
|
|
|
|
|
*/
|
|
|
|
|
int len;
|
|
|
|
|
char pn[INET6_ADDRSTRLEN];
|
|
|
|
|
inet_ntop(AF_INET6,
|
|
|
|
|
(const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr,
|
|
|
|
|
pn, sizeof(pn));
|
|
|
|
|
|
|
|
|
|
/* Note: we rely on the backend to get the header name to be used for
|
|
|
|
|
* x-forwarded-for, because the header is really meant for the backends.
|
|
|
|
|
* However, if the backend did not specify any option, we have to rely
|
|
|
|
|
* on the frontend's header name.
|
|
|
|
|
*/
|
|
|
|
|
if (s->be->fwdfor_hdr_len) {
|
|
|
|
|
len = s->be->fwdfor_hdr_len;
|
|
|
|
|
memcpy(trash, s->be->fwdfor_hdr_name, len);
|
|
|
|
|
} else {
|
|
|
|
|
len = s->fe->fwdfor_hdr_len;
|
|
|
|
|
memcpy(trash, s->fe->fwdfor_hdr_name, len);
|
|
|
|
|
}
|
|
|
|
|
len += sprintf(trash + len, ": %s", pn);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
if (unlikely(http_header_add_tail2(req, &txn->req,
|
|
|
|
|
&txn->hdr_idx, trash, len)) < 0)
|
2006-12-17 02:37:22 -05:00
|
|
|
goto return_bad_req;
|
2006-12-14 16:26:42 -05:00
|
|
|
}
|
2008-11-30 17:51:27 -05:00
|
|
|
}
|
2008-04-14 14:47:37 -04:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/*
|
2009-04-17 12:53:21 -04:00
|
|
|
* 10: add X-Original-To if either the frontend or the backend
|
|
|
|
|
* asks for it.
|
|
|
|
|
*/
|
|
|
|
|
if ((s->fe->options | s->be->options) & PR_O_ORGTO) {
|
|
|
|
|
|
|
|
|
|
/* FIXME: don't know if IPv6 can handle that case too. */
|
|
|
|
|
if (s->cli_addr.ss_family == AF_INET) {
|
|
|
|
|
/* Add an X-Original-To header unless the destination IP is
|
|
|
|
|
* in the 'except' network range.
|
|
|
|
|
*/
|
|
|
|
|
if (!(s->flags & SN_FRT_ADDR_SET))
|
|
|
|
|
get_frt_addr(s);
|
|
|
|
|
|
|
|
|
|
if ((!s->fe->except_mask_to.s_addr ||
|
|
|
|
|
(((struct sockaddr_in *)&s->frt_addr)->sin_addr.s_addr & s->fe->except_mask_to.s_addr)
|
|
|
|
|
!= s->fe->except_to.s_addr) &&
|
|
|
|
|
(!s->be->except_mask_to.s_addr ||
|
|
|
|
|
(((struct sockaddr_in *)&s->frt_addr)->sin_addr.s_addr & s->be->except_mask_to.s_addr)
|
|
|
|
|
!= s->be->except_to.s_addr)) {
|
|
|
|
|
int len;
|
|
|
|
|
unsigned char *pn;
|
|
|
|
|
pn = (unsigned char *)&((struct sockaddr_in *)&s->frt_addr)->sin_addr;
|
|
|
|
|
|
|
|
|
|
/* Note: we rely on the backend to get the header name to be used for
|
|
|
|
|
* x-original-to, because the header is really meant for the backends.
|
|
|
|
|
* However, if the backend did not specify any option, we have to rely
|
|
|
|
|
* on the frontend's header name.
|
|
|
|
|
*/
|
|
|
|
|
if (s->be->orgto_hdr_len) {
|
|
|
|
|
len = s->be->orgto_hdr_len;
|
|
|
|
|
memcpy(trash, s->be->orgto_hdr_name, len);
|
|
|
|
|
} else {
|
|
|
|
|
len = s->fe->orgto_hdr_len;
|
|
|
|
|
memcpy(trash, s->fe->orgto_hdr_name, len);
|
|
|
|
|
}
|
|
|
|
|
len += sprintf(trash + len, ": %d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
|
|
|
|
|
|
|
|
|
|
if (unlikely(http_header_add_tail2(req, &txn->req,
|
|
|
|
|
&txn->hdr_idx, trash, len)) < 0)
|
|
|
|
|
goto return_bad_req;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 11: add "Connection: close" if needed and not yet set.
|
2008-11-30 17:51:27 -05:00
|
|
|
* Note that we do not need to add it in case of HTTP/1.0.
|
|
|
|
|
*/
|
|
|
|
|
if (!(s->flags & SN_CONN_CLOSED) &&
|
|
|
|
|
((s->fe->options | s->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO))) {
|
|
|
|
|
if ((unlikely(msg->sl.rq.v_l != 8) ||
|
|
|
|
|
unlikely(req->data[msg->som + msg->sl.rq.v + 7] != '0')) &&
|
|
|
|
|
unlikely(http_header_add_tail2(req, &txn->req, &txn->hdr_idx,
|
|
|
|
|
"Connection: close", 17)) < 0)
|
|
|
|
|
goto return_bad_req;
|
|
|
|
|
s->flags |= SN_CONN_CLOSED;
|
|
|
|
|
}
|
|
|
|
|
/* Before we switch to data, was assignment set in manage_client_side_cookie?
|
|
|
|
|
* If not assigned, perhaps we are balancing on url_param, but this is a
|
|
|
|
|
* POST; and the parameters are in the body, maybe scan there to find our server.
|
|
|
|
|
* (unless headers overflowed the buffer?)
|
|
|
|
|
*/
|
|
|
|
|
if (!(s->flags & (SN_ASSIGNED|SN_DIRECT)) &&
|
|
|
|
|
s->txn.meth == HTTP_METH_POST && s->be->url_param_name != NULL &&
|
|
|
|
|
s->be->url_param_post_limit != 0 && !(req->flags & BF_FULL) &&
|
|
|
|
|
memchr(msg->sol + msg->sl.rq.u, '?', msg->sl.rq.u_l) == NULL) {
|
|
|
|
|
/* are there enough bytes here? total == l || r || rlim ?
|
|
|
|
|
* len is unsigned, but eoh is int,
|
|
|
|
|
* how many bytes of body have we received?
|
|
|
|
|
* eoh is the first empty line of the header
|
|
|
|
|
*/
|
|
|
|
|
/* already established CRLF or LF at eoh, move to start of message, find message length in buffer */
|
|
|
|
|
unsigned long len = req->l - (msg->sol[msg->eoh] == '\r' ? msg->eoh + 2 : msg->eoh + 1);
|
2008-08-11 09:24:42 -04:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* If we have HTTP/1.1 and Expect: 100-continue, then abort.
|
|
|
|
|
* We can't assume responsibility for the server's decision,
|
|
|
|
|
* on this URI and header set. See rfc2616: 14.20, 8.2.3,
|
|
|
|
|
* We also can't change our mind later, about which server to choose, so round robin.
|
|
|
|
|
*/
|
|
|
|
|
if ((likely(msg->sl.rq.v_l == 8) && req->data[msg->som + msg->sl.rq.v + 7] == '1')) {
|
|
|
|
|
struct hdr_ctx ctx;
|
|
|
|
|
ctx.idx = 0;
|
|
|
|
|
/* Expect is allowed in 1.1, look for it */
|
|
|
|
|
http_find_header2("Expect", 6, msg->sol, &txn->hdr_idx, &ctx);
|
|
|
|
|
if (ctx.idx != 0 &&
|
|
|
|
|
unlikely(ctx.vlen == 12 && strncasecmp(ctx.line+ctx.val, "100-continue", 12) == 0))
|
|
|
|
|
/* We can't reliablly stall and wait for data, because of
|
|
|
|
|
* .NET clients that don't conform to rfc2616; so, no need for
|
|
|
|
|
* the next block to check length expectations.
|
|
|
|
|
* We could send 100 status back to the client, but then we need to
|
|
|
|
|
* re-write headers, and send the message. And this isn't the right
|
|
|
|
|
* place for that action.
|
|
|
|
|
* TODO: support Expect elsewhere and delete this block.
|
2008-04-14 14:47:37 -04:00
|
|
|
*/
|
2008-11-30 17:51:27 -05:00
|
|
|
goto end_check_maybe_wait_for_body;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (likely(len > s->be->url_param_post_limit)) {
|
|
|
|
|
/* nothing to do, we got enough */
|
|
|
|
|
} else {
|
|
|
|
|
/* limit implies we are supposed to need this many bytes
|
|
|
|
|
* to find the parameter. Let's see how many bytes we can wait for.
|
|
|
|
|
*/
|
|
|
|
|
long long hint = len;
|
|
|
|
|
struct hdr_ctx ctx;
|
|
|
|
|
ctx.idx = 0;
|
|
|
|
|
http_find_header2("Transfer-Encoding", 17, msg->sol, &txn->hdr_idx, &ctx);
|
|
|
|
|
if (ctx.idx && ctx.vlen >= 7 && strncasecmp(ctx.line+ctx.val, "chunked", 7) == 0) {
|
2009-09-19 15:04:57 -04:00
|
|
|
buffer_dont_connect(req);
|
2008-11-30 17:51:27 -05:00
|
|
|
req->analysers |= AN_REQ_HTTP_BODY;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2008-04-14 14:47:37 -04:00
|
|
|
ctx.idx = 0;
|
2008-11-30 17:51:27 -05:00
|
|
|
http_find_header2("Content-Length", 14, msg->sol, &txn->hdr_idx, &ctx);
|
|
|
|
|
/* now if we have a length, we'll take the hint */
|
|
|
|
|
if (ctx.idx) {
|
|
|
|
|
/* We have Content-Length */
|
|
|
|
|
if (strl2llrc(ctx.line+ctx.val,ctx.vlen, &hint))
|
|
|
|
|
hint = 0; /* parse failure, untrusted client */
|
|
|
|
|
else {
|
|
|
|
|
if (hint > 0)
|
|
|
|
|
msg->hdr_content_len = hint;
|
|
|
|
|
else
|
|
|
|
|
hint = 0; /* bad client, sent negative length */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* but limited to what we care about, maybe we don't expect any entity data (hint == 0) */
|
|
|
|
|
if (s->be->url_param_post_limit < hint)
|
|
|
|
|
hint = s->be->url_param_post_limit;
|
|
|
|
|
/* now do we really need to buffer more data? */
|
|
|
|
|
if (len < hint) {
|
2009-09-19 15:04:57 -04:00
|
|
|
buffer_dont_connect(req);
|
2008-08-17 09:20:19 -04:00
|
|
|
req->analysers |= AN_REQ_HTTP_BODY;
|
2008-08-21 04:05:00 -04:00
|
|
|
}
|
2008-11-30 17:51:27 -05:00
|
|
|
/* else... There are no body bytes to wait for */
|
2008-04-14 14:47:37 -04:00
|
|
|
}
|
|
|
|
|
}
|
2008-11-30 17:51:27 -05:00
|
|
|
}
|
|
|
|
|
end_check_maybe_wait_for_body:
|
2006-12-14 16:26:42 -05:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/*************************************************************
|
|
|
|
|
* OK, that's finished for the headers. We have done what we *
|
|
|
|
|
* could. Let's switch to the DATA state. *
|
|
|
|
|
************************************************************/
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2009-08-16 17:27:46 -04:00
|
|
|
buffer_set_rlim(req, req->size); /* no more rewrite needed */
|
2008-11-30 17:51:27 -05:00
|
|
|
s->logs.tv_request = now;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
/* OK let's go on with the BODY now */
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
return_bad_req: /* let's centralize all bad requests */
|
2009-04-02 09:18:36 -04:00
|
|
|
if (unlikely(msg->msg_state == HTTP_MSG_ERROR) || msg->err_pos >= 0) {
|
2009-03-01 17:21:47 -05:00
|
|
|
/* we detected a parsing error. We want to archive this request
|
|
|
|
|
* in the dedicated proxy area for later troubleshooting.
|
|
|
|
|
*/
|
2009-04-02 09:18:36 -04:00
|
|
|
http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe);
|
2009-03-01 17:21:47 -05:00
|
|
|
}
|
2009-04-02 09:18:36 -04:00
|
|
|
|
2008-11-30 17:51:27 -05:00
|
|
|
txn->req.msg_state = HTTP_MSG_ERROR;
|
|
|
|
|
txn->status = 400;
|
|
|
|
|
req->analysers = 0;
|
|
|
|
|
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_400));
|
2009-10-04 08:52:57 -04:00
|
|
|
s->fe->counters.failed_req++;
|
2008-11-30 17:51:27 -05:00
|
|
|
|
|
|
|
|
if (!(s->flags & SN_ERR_MASK))
|
|
|
|
|
s->flags |= SN_ERR_PRXCOND;
|
|
|
|
|
if (!(s->flags & SN_FINST_MASK))
|
|
|
|
|
s->flags |= SN_FINST_R;
|
2008-08-16 19:00:46 -04:00
|
|
|
return 0;
|
2008-08-11 17:42:50 -04:00
|
|
|
}
|
2008-08-11 09:24:42 -04:00
|
|
|
|
2008-11-30 17:28:40 -05:00
|
|
|
/* This function is an analyser which processes the HTTP tarpit. It always
|
|
|
|
|
* returns zero, at the beginning because it prevents any other processing
|
|
|
|
|
* from occurring, and at the end because it terminates the request.
|
|
|
|
|
*/
|
2009-07-07 04:55:49 -04:00
|
|
|
int http_process_tarpit(struct session *s, struct buffer *req, int an_bit)
|
2008-11-30 17:28:40 -05:00
|
|
|
{
|
|
|
|
|
struct http_txn *txn = &s->txn;
|
|
|
|
|
|
|
|
|
|
/* This connection is being tarpitted. The CLIENT side has
|
|
|
|
|
* already set the connect expiration date to the right
|
|
|
|
|
* timeout. We just have to check that the client is still
|
|
|
|
|
* there and that the timeout has not expired.
|
|
|
|
|
*/
|
2009-09-19 15:04:57 -04:00
|
|
|
buffer_dont_connect(req);
|
2008-11-30 17:28:40 -05:00
|
|
|
if ((req->flags & (BF_SHUTR|BF_READ_ERROR)) == 0 &&
|
|
|
|
|
!tick_is_expired(req->analyse_exp, now_ms))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* We will set the queue timer to the time spent, just for
|
|
|
|
|
* logging purposes. We fake a 500 server error, so that the
|
|
|
|
|
* attacker will not suspect his connection has been tarpitted.
|
|
|
|
|
* It will not cause trouble to the logs because we can exclude
|
|
|
|
|
* the tarpitted connections by filtering on the 'PT' status flags.
|
|
|
|
|
*/
|
|
|
|
|
s->logs.t_queue = tv_ms_elapsed(&s->logs.tv_accept, &now);
|
|
|
|
|
|
|
|
|
|
txn->status = 500;
|
|
|
|
|
if (req->flags != BF_READ_ERROR)
|
|
|
|
|
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_500));
|
|
|
|
|
|
|
|
|
|
req->analysers = 0;
|
|
|
|
|
req->analyse_exp = TICK_ETERNITY;
|
2009-10-04 08:52:57 -04:00
|
|
|
s->fe->counters.failed_req++;
|
2008-11-30 17:28:40 -05:00
|
|
|
|
|
|
|
|
if (!(s->flags & SN_ERR_MASK))
|
|
|
|
|
s->flags |= SN_ERR_PRXCOND;
|
|
|
|
|
if (!(s->flags & SN_FINST_MASK))
|
|
|
|
|
s->flags |= SN_FINST_T;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-30 17:36:37 -05:00
|
|
|
/* This function is an analyser which processes the HTTP request body. It looks
|
|
|
|
|
* for parameters to be used for the load balancing algorithm (url_param). It
|
|
|
|
|
* must only be called after the standard HTTP request processing has occurred,
|
|
|
|
|
* because it expects the request to be parsed. It returns zero if it needs to
|
|
|
|
|
* read more data, or 1 once it has completed its analysis.
|
|
|
|
|
*/
|
2009-07-07 04:55:49 -04:00
|
|
|
int http_process_request_body(struct session *s, struct buffer *req, int an_bit)
|
2008-11-30 17:36:37 -05:00
|
|
|
{
|
|
|
|
|
struct http_msg *msg = &s->txn.req;
|
|
|
|
|
unsigned long body = msg->sol[msg->eoh] == '\r' ? msg->eoh + 2 : msg->eoh + 1;
|
|
|
|
|
long long limit = s->be->url_param_post_limit;
|
|
|
|
|
struct hdr_ctx ctx;
|
|
|
|
|
|
2009-07-12 03:47:04 -04:00
|
|
|
if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
|
|
|
|
|
/* we need more data */
|
2009-09-19 15:04:57 -04:00
|
|
|
buffer_dont_connect(req);
|
2009-07-12 03:47:04 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-30 17:36:37 -05:00
|
|
|
/* We have to parse the HTTP request body to find any required data.
|
|
|
|
|
* "balance url_param check_post" should have been the only way to get
|
|
|
|
|
* into this. We were brought here after HTTP header analysis, so all
|
|
|
|
|
* related structures are ready.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
ctx.idx = 0;
|
|
|
|
|
|
|
|
|
|
/* now if we have a length, we'll take the hint */
|
|
|
|
|
http_find_header2("Transfer-Encoding", 17, msg->sol, &s->txn.hdr_idx, &ctx);
|
|
|
|
|
if (ctx.idx && ctx.vlen >= 7 && strncasecmp(ctx.line+ctx.val, "chunked", 7) == 0) {
|
|
|
|
|
unsigned int chunk = 0;
|
|
|
|
|
while (body < req->l && !HTTP_IS_CRLF(msg->sol[body])) {
|
|
|
|
|
char c = msg->sol[body];
|
|
|
|
|
if (ishex(c)) {
|
|
|
|
|
unsigned int hex = toupper(c) - '0';
|
|
|
|
|
if (hex > 9)
|
|
|
|
|
hex -= 'A' - '9' - 1;
|
|
|
|
|
chunk = (chunk << 4) | hex;
|
|
|
|
|
} else
|
|
|
|
|
break;
|
|
|
|
|
body++;
|
|
|
|
|
}
|
|
|
|
|
if (body + 2 >= req->l) /* we want CRLF too */
|
|
|
|
|
goto http_body_end; /* end of buffer? data missing! */
|
|
|
|
|
|
|
|
|
|
if (memcmp(msg->sol+body, "\r\n", 2) != 0)
|
|
|
|
|
goto http_body_end; /* chunked encoding len ends with CRLF, and we don't have it yet */
|
|
|
|
|
|
|
|
|
|
body += 2; // skip CRLF
|
|
|
|
|
|
|
|
|
|
/* if we support more then one chunk here, we have to do it again when assigning server
|
|
|
|
|
* 1. how much entity data do we have? new var
|
|
|
|
|
* 2. should save entity_start, entity_cursor, elen & rlen in req; so we don't repeat scanning here
|
|
|
|
|
* 3. test if elen > limit, or set new limit to elen if 0 (end of entity found)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (chunk < limit)
|
|
|
|
|
limit = chunk; /* only reading one chunk */
|
|
|
|
|
} else {
|
|
|
|
|
if (msg->hdr_content_len < limit)
|
|
|
|
|
limit = msg->hdr_content_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
http_body_end:
|
|
|
|
|
/* we leave once we know we have nothing left to do. This means that we have
|
|
|
|
|
* enough bytes, or that we know we'll not get any more (buffer full, read
|
|
|
|
|
* buffer closed).
|
|
|
|
|
*/
|
|
|
|
|
if (req->l - body >= limit || /* enough bytes! */
|
|
|
|
|
req->flags & (BF_FULL | BF_READ_ERROR | BF_SHUTR | BF_READ_TIMEOUT) ||
|
|
|
|
|
tick_is_expired(req->analyse_exp, now_ms)) {
|
|
|
|
|
/* The situation will not evolve, so let's give up on the analysis. */
|
|
|
|
|
s->logs.tv_request = now; /* update the request timer to reflect full request */
|
2009-07-07 04:55:49 -04:00
|
|
|
req->analysers &= ~an_bit;
|
2008-11-30 17:36:37 -05:00
|
|
|
req->analyse_exp = TICK_ETERNITY;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Not enough data. We'll re-use the http-request
|
|
|
|
|
* timeout here. Ideally, we should set the timeout
|
|
|
|
|
* relative to the accept() date. We just set the
|
|
|
|
|
* request timeout once at the beginning of the
|
|
|
|
|
* request.
|
|
|
|
|
*/
|
2009-09-19 15:04:57 -04:00
|
|
|
buffer_dont_connect(req);
|
2008-11-30 17:36:37 -05:00
|
|
|
if (!tick_isset(req->analyse_exp))
|
2009-07-12 04:03:17 -04:00
|
|
|
req->analyse_exp = tick_add_ifset(now_ms, s->be->timeout.httpreq);
|
2008-11-30 17:36:37 -05:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/* This function performs all the processing enabled for the current response.
|
2008-08-16 19:00:46 -04:00
|
|
|
* It normally returns zero, but may return 1 if it absolutely needs to be
|
|
|
|
|
* called again after other functions. It relies on buffers flags, and updates
|
2008-08-17 09:20:19 -04:00
|
|
|
* t->rep->analysers. It might make sense to explode it into several other
|
2008-08-21 04:05:00 -04:00
|
|
|
* functions. It works like process_request (see indications above).
|
2008-08-11 17:42:50 -04:00
|
|
|
*/
|
2008-08-14 12:35:40 -04:00
|
|
|
int process_response(struct session *t)
|
2008-08-11 17:42:50 -04:00
|
|
|
{
|
2008-08-14 12:35:40 -04:00
|
|
|
struct http_txn *txn = &t->txn;
|
2008-08-11 17:42:50 -04:00
|
|
|
struct buffer *req = t->req;
|
|
|
|
|
struct buffer *rep = t->rep;
|
2008-08-11 09:24:42 -04:00
|
|
|
|
2009-09-15 15:25:21 -04:00
|
|
|
next_response:
|
2008-08-28 02:54:27 -04:00
|
|
|
DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
|
[MAJOR] rework of the server FSM
srv_state has been removed from HTTP state machines, and states
have been split in either TCP states or analyzers. For instance,
the TARPIT state has just become a simple analyzer.
New flags have been added to the struct buffer to compensate this.
The high-level stream processors sometimes need to force a disconnection
without touching a file-descriptor (eg: report an error). But if
they touched BF_SHUTW or BF_SHUTR, the file descriptor would not
be closed. Thus, the two SHUT?_NOW flags have been added so that
an application can request a forced close which the stream interface
will be forced to obey.
During this change, a new BF_HIJACK flag was added. It will
be used for data generation, eg during a stats dump. It
prevents the producer on a buffer from sending data into it.
BF_SHUTR_NOW /* the producer must shut down for reads ASAP */
BF_SHUTW_NOW /* the consumer must shut down for writes ASAP */
BF_HIJACK /* the producer is temporarily replaced */
BF_SHUTW_NOW has precedence over BF_HIJACK. BF_HIJACK has
precedence over BF_MAY_FORWARD (so that it does not need it).
New functions buffer_shutr_now(), buffer_shutw_now(), buffer_abort()
are provided to manipulate BF_SHUT* flags.
A new type "stream_interface" has been added to describe both
sides of a buffer. A stream interface has states and error
reporting. The session now has two stream interfaces (one per
side). Each buffer has stream_interface pointers to both
consumer and producer sides.
The server-side file descriptor has moved to its stream interface,
so that even the buffer has access to it.
process_srv() has been split into three parts :
- tcp_get_connection() obtains a connection to the server
- tcp_connection_failed() tests if a previously attempted
connection has succeeded or not.
- process_srv_data() only manages the data phase, and in
this sense should be roughly equivalent to process_cli.
Little code has been removed, and a lot of old code has been
left in comments for now.
2008-10-19 01:30:41 -04:00
|
|
|
now_ms, __FUNCTION__,
|
2008-08-28 02:54:27 -04:00
|
|
|
t,
|
|
|
|
|
rep,
|
|
|
|
|
rep->rex, rep->wex,
|
|
|
|
|
rep->flags,
|
|
|
|
|
rep->l,
|
|
|
|
|
rep->analysers);
|
2008-08-10 16:55:22 -04:00
|
|
|
|
2008-08-17 09:20:19 -04:00
|
|
|
if (rep->analysers & AN_RTR_HTTP_HDR) { /* receiving server headers */
|
2008-08-14 12:35:40 -04:00
|
|
|
/*
|
|
|
|
|
* Now parse the partial (or complete) lines.
|
|
|
|
|
* We will check the response syntax, and also join multi-line
|
|
|
|
|
* headers. An index of all the lines will be elaborated while
|
|
|
|
|
* parsing.
|
|
|
|
|
*
|
|
|
|
|
* For the parsing, we use a 28 states FSM.
|
|
|
|
|
*
|
|
|
|
|
* Here is the information we currently have :
|
2008-08-16 16:18:07 -04:00
|
|
|
* rep->data + rep->som = beginning of response
|
|
|
|
|
* rep->data + rep->eoh = end of processed headers / start of current one
|
|
|
|
|
* rep->data + rep->eol = end of current header or line (LF or CRLF)
|
2008-08-14 12:35:40 -04:00
|
|
|
* rep->lr = first non-visited byte
|
|
|
|
|
* rep->r = end of data
|
|
|
|
|
*/
|
2008-08-11 11:35:01 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
int cur_idx;
|
|
|
|
|
struct http_msg *msg = &txn->rsp;
|
|
|
|
|
struct proxy *cur_proxy;
|
|
|
|
|
|
|
|
|
|
if (likely(rep->lr < rep->r))
|
|
|
|
|
http_msg_analyzer(rep, msg, &txn->hdr_idx);
|
|
|
|
|
|
|
|
|
|
/* 1: we might have to print this header in debug mode */
|
|
|
|
|
if (unlikely((global.mode & MODE_DEBUG) &&
|
|
|
|
|
(!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) &&
|
|
|
|
|
(msg->msg_state == HTTP_MSG_BODY || msg->msg_state == HTTP_MSG_ERROR))) {
|
|
|
|
|
char *eol, *sol;
|
|
|
|
|
|
|
|
|
|
sol = rep->data + msg->som;
|
|
|
|
|
eol = sol + msg->sl.rq.l;
|
|
|
|
|
debug_hdr("srvrep", t, sol, eol);
|
|
|
|
|
|
|
|
|
|
sol += hdr_idx_first_pos(&txn->hdr_idx);
|
|
|
|
|
cur_idx = hdr_idx_first_idx(&txn->hdr_idx);
|
|
|
|
|
|
|
|
|
|
while (cur_idx) {
|
|
|
|
|
eol = sol + txn->hdr_idx.v[cur_idx].len;
|
|
|
|
|
debug_hdr("srvhdr", t, sol, eol);
|
|
|
|
|
sol = eol + txn->hdr_idx.v[cur_idx].cr + 1;
|
|
|
|
|
cur_idx = txn->hdr_idx.v[cur_idx].next;
|
2008-08-11 17:42:50 -04:00
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/*
|
|
|
|
|
* Now we quickly check if we have found a full valid response.
|
|
|
|
|
* If not so, we check the FD and buffer states before leaving.
|
|
|
|
|
* A full response is indicated by the fact that we have seen
|
|
|
|
|
* the double LF/CRLF, so the state is HTTP_MSG_BODY. Invalid
|
|
|
|
|
* responses are checked first.
|
|
|
|
|
*
|
|
|
|
|
* Depending on whether the client is still there or not, we
|
|
|
|
|
* may send an error response back or not. Note that normally
|
|
|
|
|
* we should only check for HTTP status there, and check I/O
|
|
|
|
|
* errors somewhere else.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
|
2008-08-15 12:16:37 -04:00
|
|
|
/* Invalid response */
|
|
|
|
|
if (unlikely(msg->msg_state == HTTP_MSG_ERROR)) {
|
2009-03-01 17:21:47 -05:00
|
|
|
/* we detected a parsing error. We want to archive this response
|
|
|
|
|
* in the dedicated proxy area for later troubleshooting.
|
|
|
|
|
*/
|
2008-08-15 12:16:37 -04:00
|
|
|
hdr_response_bad:
|
2009-04-02 09:18:36 -04:00
|
|
|
if (msg->msg_state == HTTP_MSG_ERROR || msg->err_pos >= 0)
|
|
|
|
|
http_capture_bad_message(&t->be->invalid_rep, t, rep, msg, t->fe);
|
|
|
|
|
|
[MAJOR] rework of the server FSM
srv_state has been removed from HTTP state machines, and states
have been split in either TCP states or analyzers. For instance,
the TARPIT state has just become a simple analyzer.
New flags have been added to the struct buffer to compensate this.
The high-level stream processors sometimes need to force a disconnection
without touching a file-descriptor (eg: report an error). But if
they touched BF_SHUTW or BF_SHUTR, the file descriptor would not
be closed. Thus, the two SHUT?_NOW flags have been added so that
an application can request a forced close which the stream interface
will be forced to obey.
During this change, a new BF_HIJACK flag was added. It will
be used for data generation, eg during a stats dump. It
prevents the producer on a buffer from sending data into it.
BF_SHUTR_NOW /* the producer must shut down for reads ASAP */
BF_SHUTW_NOW /* the consumer must shut down for writes ASAP */
BF_HIJACK /* the producer is temporarily replaced */
BF_SHUTW_NOW has precedence over BF_HIJACK. BF_HIJACK has
precedence over BF_MAY_FORWARD (so that it does not need it).
New functions buffer_shutr_now(), buffer_shutw_now(), buffer_abort()
are provided to manipulate BF_SHUT* flags.
A new type "stream_interface" has been added to describe both
sides of a buffer. A stream interface has states and error
reporting. The session now has two stream interfaces (one per
side). Each buffer has stream_interface pointers to both
consumer and producer sides.
The server-side file descriptor has moved to its stream interface,
so that even the buffer has access to it.
process_srv() has been split into three parts :
- tcp_get_connection() obtains a connection to the server
- tcp_connection_failed() tests if a previously attempted
connection has succeeded or not.
- process_srv_data() only manages the data phase, and in
this sense should be roughly equivalent to process_cli.
Little code has been removed, and a lot of old code has been
left in comments for now.
2008-10-19 01:30:41 -04:00
|
|
|
buffer_shutr_now(rep);
|
|
|
|
|
buffer_shutw_now(req);
|
2009-03-15 18:11:49 -04:00
|
|
|
if (t->srv)
|
2009-10-04 08:52:57 -04:00
|
|
|
t->srv->counters.failed_resp++;
|
|
|
|
|
t->be->counters.failed_resp++;
|
2008-08-17 09:20:19 -04:00
|
|
|
rep->analysers = 0;
|
2008-08-14 12:35:40 -04:00
|
|
|
txn->status = 502;
|
2008-11-30 13:22:53 -05:00
|
|
|
stream_int_return(rep->cons, error_message(t, HTTP_ERR_502));
|
2008-08-14 12:35:40 -04:00
|
|
|
if (!(t->flags & SN_ERR_MASK))
|
2008-08-15 12:16:37 -04:00
|
|
|
t->flags |= SN_ERR_PRXCOND;
|
2008-08-14 12:35:40 -04:00
|
|
|
if (!(t->flags & SN_FINST_MASK))
|
|
|
|
|
t->flags |= SN_FINST_H;
|
2008-08-15 17:43:19 -04:00
|
|
|
|
2008-08-16 19:00:46 -04:00
|
|
|
return 0;
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2008-08-27 17:57:16 -04:00
|
|
|
/* too large response does not fit in buffer. */
|
|
|
|
|
else if (rep->flags & BF_FULL) {
|
|
|
|
|
goto hdr_response_bad;
|
|
|
|
|
}
|
|
|
|
|
/* read error */
|
|
|
|
|
else if (rep->flags & BF_READ_ERROR) {
|
2009-04-02 09:18:36 -04:00
|
|
|
if (msg->err_pos >= 0)
|
|
|
|
|
http_capture_bad_message(&t->be->invalid_rep, t, rep, msg, t->fe);
|
[MAJOR] rework of the server FSM
srv_state has been removed from HTTP state machines, and states
have been split in either TCP states or analyzers. For instance,
the TARPIT state has just become a simple analyzer.
New flags have been added to the struct buffer to compensate this.
The high-level stream processors sometimes need to force a disconnection
without touching a file-descriptor (eg: report an error). But if
they touched BF_SHUTW or BF_SHUTR, the file descriptor would not
be closed. Thus, the two SHUT?_NOW flags have been added so that
an application can request a forced close which the stream interface
will be forced to obey.
During this change, a new BF_HIJACK flag was added. It will
be used for data generation, eg during a stats dump. It
prevents the producer on a buffer from sending data into it.
BF_SHUTR_NOW /* the producer must shut down for reads ASAP */
BF_SHUTW_NOW /* the consumer must shut down for writes ASAP */
BF_HIJACK /* the producer is temporarily replaced */
BF_SHUTW_NOW has precedence over BF_HIJACK. BF_HIJACK has
precedence over BF_MAY_FORWARD (so that it does not need it).
New functions buffer_shutr_now(), buffer_shutw_now(), buffer_abort()
are provided to manipulate BF_SHUT* flags.
A new type "stream_interface" has been added to describe both
sides of a buffer. A stream interface has states and error
reporting. The session now has two stream interfaces (one per
side). Each buffer has stream_interface pointers to both
consumer and producer sides.
The server-side file descriptor has moved to its stream interface,
so that even the buffer has access to it.
process_srv() has been split into three parts :
- tcp_get_connection() obtains a connection to the server
- tcp_connection_failed() tests if a previously attempted
connection has succeeded or not.
- process_srv_data() only manages the data phase, and in
this sense should be roughly equivalent to process_cli.
Little code has been removed, and a lot of old code has been
left in comments for now.
2008-10-19 01:30:41 -04:00
|
|
|
buffer_shutr_now(rep);
|
|
|
|
|
buffer_shutw_now(req);
|
2009-03-15 18:11:49 -04:00
|
|
|
if (t->srv)
|
2009-10-04 08:52:57 -04:00
|
|
|
t->srv->counters.failed_resp++;
|
|
|
|
|
t->be->counters.failed_resp++;
|
2008-08-17 09:20:19 -04:00
|
|
|
rep->analysers = 0;
|
2008-08-15 12:16:37 -04:00
|
|
|
txn->status = 502;
|
2008-11-30 13:22:53 -05:00
|
|
|
stream_int_return(rep->cons, error_message(t, HTTP_ERR_502));
|
2008-08-15 12:16:37 -04:00
|
|
|
if (!(t->flags & SN_ERR_MASK))
|
|
|
|
|
t->flags |= SN_ERR_SRVCL;
|
|
|
|
|
if (!(t->flags & SN_FINST_MASK))
|
|
|
|
|
t->flags |= SN_FINST_H;
|
2008-08-16 19:00:46 -04:00
|
|
|
return 0;
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2008-08-14 12:35:40 -04:00
|
|
|
/* read timeout : return a 504 to the client. */
|
2008-08-15 12:16:37 -04:00
|
|
|
else if (rep->flags & BF_READ_TIMEOUT) {
|
2009-04-02 09:18:36 -04:00
|
|
|
if (msg->err_pos >= 0)
|
|
|
|
|
http_capture_bad_message(&t->be->invalid_rep, t, rep, msg, t->fe);
|
[MAJOR] rework of the server FSM
srv_state has been removed from HTTP state machines, and states
have been split in either TCP states or analyzers. For instance,
the TARPIT state has just become a simple analyzer.
New flags have been added to the struct buffer to compensate this.
The high-level stream processors sometimes need to force a disconnection
without touching a file-descriptor (eg: report an error). But if
they touched BF_SHUTW or BF_SHUTR, the file descriptor would not
be closed. Thus, the two SHUT?_NOW flags have been added so that
an application can request a forced close which the stream interface
will be forced to obey.
During this change, a new BF_HIJACK flag was added. It will
be used for data generation, eg during a stats dump. It
prevents the producer on a buffer from sending data into it.
BF_SHUTR_NOW /* the producer must shut down for reads ASAP */
BF_SHUTW_NOW /* the consumer must shut down for writes ASAP */
BF_HIJACK /* the producer is temporarily replaced */
BF_SHUTW_NOW has precedence over BF_HIJACK. BF_HIJACK has
precedence over BF_MAY_FORWARD (so that it does not need it).
New functions buffer_shutr_now(), buffer_shutw_now(), buffer_abort()
are provided to manipulate BF_SHUT* flags.
A new type "stream_interface" has been added to describe both
sides of a buffer. A stream interface has states and error
reporting. The session now has two stream interfaces (one per
side). Each buffer has stream_interface pointers to both
consumer and producer sides.
The server-side file descriptor has moved to its stream interface,
so that even the buffer has access to it.
process_srv() has been split into three parts :
- tcp_get_connection() obtains a connection to the server
- tcp_connection_failed() tests if a previously attempted
connection has succeeded or not.
- process_srv_data() only manages the data phase, and in
this sense should be roughly equivalent to process_cli.
Little code has been removed, and a lot of old code has been
left in comments for now.
2008-10-19 01:30:41 -04:00
|
|
|
buffer_shutr_now(rep);
|
|
|
|
|
buffer_shutw_now(req);
|
2009-03-15 18:11:49 -04:00
|
|
|
if (t->srv)
|
2009-10-04 08:52:57 -04:00
|
|
|
t->srv->counters.failed_resp++;
|
|
|
|
|
t->be->counters.failed_resp++;
|
2008-08-17 09:20:19 -04:00
|
|
|
rep->analysers = 0;
|
2008-08-14 12:35:40 -04:00
|
|
|
txn->status = 504;
|
2008-11-30 13:22:53 -05:00
|
|
|
stream_int_return(rep->cons, error_message(t, HTTP_ERR_504));
|
2008-08-14 12:35:40 -04:00
|
|
|
if (!(t->flags & SN_ERR_MASK))
|
|
|
|
|
t->flags |= SN_ERR_SRVTO;
|
|
|
|
|
if (!(t->flags & SN_FINST_MASK))
|
|
|
|
|
t->flags |= SN_FINST_H;
|
2008-08-16 19:00:46 -04:00
|
|
|
return 0;
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2009-03-15 18:11:49 -04:00
|
|
|
/* close from server */
|
|
|
|
|
else if (rep->flags & BF_SHUTR) {
|
2009-04-02 09:18:36 -04:00
|
|
|
if (msg->err_pos >= 0)
|
|
|
|
|
http_capture_bad_message(&t->be->invalid_rep, t, rep, msg, t->fe);
|
2008-08-27 17:57:16 -04:00
|
|
|
buffer_shutw_now(req);
|
2009-03-15 18:11:49 -04:00
|
|
|
if (t->srv)
|
2009-10-04 08:52:57 -04:00
|
|
|
t->srv->counters.failed_resp++;
|
|
|
|
|
t->be->counters.failed_resp++;
|
2008-08-27 17:57:16 -04:00
|
|
|
rep->analysers = 0;
|
|
|
|
|
txn->status = 502;
|
2008-11-30 13:22:53 -05:00
|
|
|
stream_int_return(rep->cons, error_message(t, HTTP_ERR_502));
|
2008-08-27 17:57:16 -04:00
|
|
|
if (!(t->flags & SN_ERR_MASK))
|
|
|
|
|
t->flags |= SN_ERR_SRVCL;
|
|
|
|
|
if (!(t->flags & SN_FINST_MASK))
|
|
|
|
|
t->flags |= SN_FINST_H;
|
2009-03-15 18:11:49 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/* write error to client (we don't send any message then) */
|
|
|
|
|
else if (rep->flags & BF_WRITE_ERROR) {
|
2009-04-02 09:18:36 -04:00
|
|
|
if (msg->err_pos >= 0)
|
|
|
|
|
http_capture_bad_message(&t->be->invalid_rep, t, rep, msg, t->fe);
|
2009-03-15 18:11:49 -04:00
|
|
|
buffer_shutr_now(rep);
|
2009-10-04 08:52:57 -04:00
|
|
|
t->be->counters.failed_resp++;
|
2009-03-15 18:11:49 -04:00
|
|
|
rep->analysers = 0;
|
|
|
|
|
if (!(t->flags & SN_ERR_MASK))
|
|
|
|
|
t->flags |= SN_ERR_CLICL;
|
|
|
|
|
if (!(t->flags & SN_FINST_MASK))
|
|
|
|
|
t->flags |= SN_FINST_H;
|
2008-08-27 17:57:16 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
2009-09-15 15:25:21 -04:00
|
|
|
|
2009-09-19 15:04:57 -04:00
|
|
|
buffer_dont_close(rep);
|
2008-08-15 12:16:37 -04:00
|
|
|
return 0;
|
2008-08-14 12:35:40 -04:00
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-02-14 14:25:24 -05:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/*****************************************************************
|
|
|
|
|
* More interesting part now : we know that we have a complete *
|
|
|
|
|
* response which at least looks like HTTP. We have an indicator *
|
|
|
|
|
* of each header's length, so we can parse them quickly. *
|
|
|
|
|
****************************************************************/
|
2008-02-14 14:25:24 -05:00
|
|
|
|
2009-04-02 09:18:36 -04:00
|
|
|
if (msg->err_pos >= 0)
|
|
|
|
|
http_capture_bad_message(&t->be->invalid_rep, t, rep, msg, t->fe);
|
|
|
|
|
|
2008-08-17 09:20:19 -04:00
|
|
|
rep->analysers &= ~AN_RTR_HTTP_HDR;
|
2008-08-16 12:40:18 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/* ensure we keep this pointer to the beginning of the message */
|
|
|
|
|
msg->sol = rep->data + msg->som;
|
2008-02-14 14:25:24 -05:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/*
|
|
|
|
|
* 1: get the status code and check for cacheability.
|
|
|
|
|
*/
|
2008-02-14 14:25:24 -05:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
t->logs.logwait &= ~LW_RESP;
|
|
|
|
|
txn->status = strl2ui(rep->data + msg->sl.st.c, msg->sl.st.c_l);
|
2008-02-14 14:25:24 -05:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
switch (txn->status) {
|
|
|
|
|
case 200:
|
|
|
|
|
case 203:
|
|
|
|
|
case 206:
|
|
|
|
|
case 300:
|
|
|
|
|
case 301:
|
|
|
|
|
case 410:
|
|
|
|
|
/* RFC2616 @13.4:
|
|
|
|
|
* "A response received with a status code of
|
|
|
|
|
* 200, 203, 206, 300, 301 or 410 MAY be stored
|
|
|
|
|
* by a cache (...) unless a cache-control
|
|
|
|
|
* directive prohibits caching."
|
|
|
|
|
*
|
|
|
|
|
* RFC2616 @9.5: POST method :
|
|
|
|
|
* "Responses to this method are not cacheable,
|
|
|
|
|
* unless the response includes appropriate
|
|
|
|
|
* Cache-Control or Expires header fields."
|
|
|
|
|
*/
|
|
|
|
|
if (likely(txn->meth != HTTP_METH_POST) &&
|
|
|
|
|
(t->be->options & (PR_O_CHK_CACHE|PR_O_COOK_NOC)))
|
|
|
|
|
txn->flags |= TX_CACHEABLE | TX_CACHE_COOK;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2008-02-14 14:25:24 -05:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/*
|
|
|
|
|
* 2: we may need to capture headers
|
|
|
|
|
*/
|
|
|
|
|
if (unlikely((t->logs.logwait & LW_RSPHDR) && t->fe->rsp_cap))
|
|
|
|
|
capture_headers(rep->data + msg->som, &txn->hdr_idx,
|
|
|
|
|
txn->rsp.cap, t->fe->rsp_cap);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 3: we will have to evaluate the filters.
|
|
|
|
|
* As opposed to version 1.2, now they will be evaluated in the
|
|
|
|
|
* filters order and not in the header order. This means that
|
|
|
|
|
* each filter has to be validated among all headers.
|
|
|
|
|
*
|
|
|
|
|
* Filters are tried with ->be first, then with ->fe if it is
|
|
|
|
|
* different from ->be.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
t->flags &= ~SN_CONN_CLOSED; /* prepare for inspection */
|
|
|
|
|
|
|
|
|
|
cur_proxy = t->be;
|
|
|
|
|
while (1) {
|
|
|
|
|
struct proxy *rule_set = cur_proxy;
|
|
|
|
|
|
|
|
|
|
/* try headers filters */
|
|
|
|
|
if (rule_set->rsp_exp != NULL) {
|
|
|
|
|
if (apply_filters_to_response(t, rep, rule_set->rsp_exp) < 0) {
|
|
|
|
|
return_bad_resp:
|
2009-03-15 18:11:49 -04:00
|
|
|
if (t->srv)
|
2009-10-04 08:52:57 -04:00
|
|
|
t->srv->counters.failed_resp++;
|
|
|
|
|
cur_proxy->counters.failed_resp++;
|
2008-08-14 12:35:40 -04:00
|
|
|
return_srv_prx_502:
|
[MAJOR] rework of the server FSM
srv_state has been removed from HTTP state machines, and states
have been split in either TCP states or analyzers. For instance,
the TARPIT state has just become a simple analyzer.
New flags have been added to the struct buffer to compensate this.
The high-level stream processors sometimes need to force a disconnection
without touching a file-descriptor (eg: report an error). But if
they touched BF_SHUTW or BF_SHUTR, the file descriptor would not
be closed. Thus, the two SHUT?_NOW flags have been added so that
an application can request a forced close which the stream interface
will be forced to obey.
During this change, a new BF_HIJACK flag was added. It will
be used for data generation, eg during a stats dump. It
prevents the producer on a buffer from sending data into it.
BF_SHUTR_NOW /* the producer must shut down for reads ASAP */
BF_SHUTW_NOW /* the consumer must shut down for writes ASAP */
BF_HIJACK /* the producer is temporarily replaced */
BF_SHUTW_NOW has precedence over BF_HIJACK. BF_HIJACK has
precedence over BF_MAY_FORWARD (so that it does not need it).
New functions buffer_shutr_now(), buffer_shutw_now(), buffer_abort()
are provided to manipulate BF_SHUT* flags.
A new type "stream_interface" has been added to describe both
sides of a buffer. A stream interface has states and error
reporting. The session now has two stream interfaces (one per
side). Each buffer has stream_interface pointers to both
consumer and producer sides.
The server-side file descriptor has moved to its stream interface,
so that even the buffer has access to it.
process_srv() has been split into three parts :
- tcp_get_connection() obtains a connection to the server
- tcp_connection_failed() tests if a previously attempted
connection has succeeded or not.
- process_srv_data() only manages the data phase, and in
this sense should be roughly equivalent to process_cli.
Little code has been removed, and a lot of old code has been
left in comments for now.
2008-10-19 01:30:41 -04:00
|
|
|
buffer_shutr_now(rep);
|
|
|
|
|
buffer_shutw_now(req);
|
2008-08-17 09:20:19 -04:00
|
|
|
rep->analysers = 0;
|
2008-08-14 12:35:40 -04:00
|
|
|
txn->status = 502;
|
2008-11-30 13:22:53 -05:00
|
|
|
stream_int_return(rep->cons, error_message(t, HTTP_ERR_502));
|
2008-08-14 12:35:40 -04:00
|
|
|
if (!(t->flags & SN_ERR_MASK))
|
|
|
|
|
t->flags |= SN_ERR_PRXCOND;
|
|
|
|
|
if (!(t->flags & SN_FINST_MASK))
|
|
|
|
|
t->flags |= SN_FINST_H;
|
2008-08-16 19:00:46 -04:00
|
|
|
return 0;
|
2008-02-14 14:25:24 -05:00
|
|
|
}
|
2008-08-14 12:35:40 -04:00
|
|
|
}
|
2008-02-14 14:25:24 -05:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/* has the response been denied ? */
|
|
|
|
|
if (txn->flags & TX_SVDENY) {
|
2009-03-15 18:11:49 -04:00
|
|
|
if (t->srv)
|
2009-10-04 08:52:57 -04:00
|
|
|
t->srv->counters.failed_secu++;
|
|
|
|
|
cur_proxy->counters.denied_resp++;
|
2008-08-14 12:35:40 -04:00
|
|
|
goto return_srv_prx_502;
|
2008-03-10 17:04:20 -04:00
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/* We might have to check for "Connection:" */
|
|
|
|
|
if (((t->fe->options | t->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO)) &&
|
2009-09-15 15:25:21 -04:00
|
|
|
!(t->flags & SN_CONN_CLOSED) &&
|
|
|
|
|
txn->status >= 200) {
|
2008-08-14 12:35:40 -04:00
|
|
|
char *cur_ptr, *cur_end, *cur_next;
|
|
|
|
|
int cur_idx, old_idx, delta, val;
|
|
|
|
|
struct hdr_idx_elem *cur_hdr;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
cur_next = rep->data + txn->rsp.som + hdr_idx_first_pos(&txn->hdr_idx);
|
|
|
|
|
old_idx = 0;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
while ((cur_idx = txn->hdr_idx.v[old_idx].next)) {
|
|
|
|
|
cur_hdr = &txn->hdr_idx.v[cur_idx];
|
|
|
|
|
cur_ptr = cur_next;
|
|
|
|
|
cur_end = cur_ptr + cur_hdr->len;
|
|
|
|
|
cur_next = cur_end + cur_hdr->cr + 1;
|
2008-01-06 17:34:21 -05:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
val = http_header_match2(cur_ptr, cur_end, "Connection", 10);
|
|
|
|
|
if (val) {
|
|
|
|
|
/* 3 possibilities :
|
|
|
|
|
* - we have already set Connection: close,
|
|
|
|
|
* so we remove this line.
|
|
|
|
|
* - we have not yet set Connection: close,
|
|
|
|
|
* but this line indicates close. We leave
|
|
|
|
|
* it untouched and set the flag.
|
|
|
|
|
* - we have not yet set Connection: close,
|
|
|
|
|
* and this line indicates non-close. We
|
|
|
|
|
* replace it.
|
|
|
|
|
*/
|
|
|
|
|
if (t->flags & SN_CONN_CLOSED) {
|
|
|
|
|
delta = buffer_replace2(rep, cur_ptr, cur_next, NULL, 0);
|
|
|
|
|
txn->rsp.eoh += delta;
|
|
|
|
|
cur_next += delta;
|
|
|
|
|
txn->hdr_idx.v[old_idx].next = cur_hdr->next;
|
|
|
|
|
txn->hdr_idx.used--;
|
|
|
|
|
cur_hdr->len = 0;
|
|
|
|
|
} else {
|
|
|
|
|
if (strncasecmp(cur_ptr + val, "close", 5) != 0) {
|
|
|
|
|
delta = buffer_replace2(rep, cur_ptr + val, cur_end,
|
|
|
|
|
"close", 5);
|
|
|
|
|
cur_next += delta;
|
|
|
|
|
cur_hdr->len += delta;
|
|
|
|
|
txn->rsp.eoh += delta;
|
|
|
|
|
}
|
|
|
|
|
t->flags |= SN_CONN_CLOSED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
old_idx = cur_idx;
|
2008-01-06 17:34:21 -05:00
|
|
|
}
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/* add response headers from the rule sets in the same order */
|
|
|
|
|
for (cur_idx = 0; cur_idx < rule_set->nb_rspadd; cur_idx++) {
|
2009-09-15 15:25:21 -04:00
|
|
|
if (txn->status < 200)
|
|
|
|
|
break;
|
2008-08-14 12:35:40 -04:00
|
|
|
if (unlikely(http_header_add_tail(rep, &txn->rsp, &txn->hdr_idx,
|
|
|
|
|
rule_set->rsp_add[cur_idx])) < 0)
|
|
|
|
|
goto return_bad_resp;
|
2006-10-15 08:26:02 -04:00
|
|
|
}
|
|
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/* check whether we're already working on the frontend */
|
|
|
|
|
if (cur_proxy == t->fe)
|
|
|
|
|
break;
|
|
|
|
|
cur_proxy = t->fe;
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/*
|
|
|
|
|
* 4: check for server cookie.
|
|
|
|
|
*/
|
2009-09-15 15:25:21 -04:00
|
|
|
if ((t->be->cookie_name || t->be->appsession_name || t->fe->capture_name
|
|
|
|
|
|| (t->be->options & PR_O_CHK_CACHE)) && txn->status >= 200)
|
2008-08-14 12:35:40 -04:00
|
|
|
manage_server_side_cookies(t, rep);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
|
2007-03-18 11:22:39 -04:00
|
|
|
/*
|
2008-08-14 12:35:40 -04:00
|
|
|
* 5: check for cache-control or pragma headers if required.
|
2007-03-18 11:22:39 -04:00
|
|
|
*/
|
2009-09-15 15:25:21 -04:00
|
|
|
if ((t->be->options & (PR_O_COOK_NOC | PR_O_CHK_CACHE)) != 0 && txn->status >= 200)
|
2008-08-14 12:35:40 -04:00
|
|
|
check_response_for_cacheability(t, rep);
|
2007-03-18 11:22:39 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/*
|
|
|
|
|
* 6: add server cookie in the response if needed
|
|
|
|
|
*/
|
|
|
|
|
if ((t->srv) && !(t->flags & SN_DIRECT) && (t->be->options & PR_O_COOK_INS) &&
|
2009-09-15 15:25:21 -04:00
|
|
|
(!(t->be->options & PR_O_COOK_POST) || (txn->meth == HTTP_METH_POST)) &&
|
|
|
|
|
txn->status >= 200) {
|
2008-08-14 12:35:40 -04:00
|
|
|
int len;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/* the server is known, it's not the one the client requested, we have to
|
|
|
|
|
* insert a set-cookie here, except if we want to insert only on POST
|
|
|
|
|
* requests and this one isn't. Note that servers which don't have cookies
|
|
|
|
|
* (eg: some backup servers) will return a full cookie removal request.
|
|
|
|
|
*/
|
|
|
|
|
len = sprintf(trash, "Set-Cookie: %s=%s; path=/",
|
|
|
|
|
t->be->cookie_name,
|
|
|
|
|
t->srv->cookie ? t->srv->cookie : "; Expires=Thu, 01-Jan-1970 00:00:01 GMT");
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
if (t->be->cookie_domain)
|
|
|
|
|
len += sprintf(trash+len, "; domain=%s", t->be->cookie_domain);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
if (unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
|
|
|
|
|
trash, len)) < 0)
|
|
|
|
|
goto return_bad_resp;
|
|
|
|
|
txn->flags |= TX_SCK_INSERTED;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/* Here, we will tell an eventual cache on the client side that we don't
|
|
|
|
|
* want it to cache this reply because HTTP/1.0 caches also cache cookies !
|
|
|
|
|
* Some caches understand the correct form: 'no-cache="set-cookie"', but
|
|
|
|
|
* others don't (eg: apache <= 1.3.26). So we use 'private' instead.
|
|
|
|
|
*/
|
|
|
|
|
if ((t->be->options & PR_O_COOK_NOC) && (txn->flags & TX_CACHEABLE)) {
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
|
|
|
|
|
|
|
|
|
|
if (unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
|
|
|
|
|
"Cache-control: private", 22)) < 0)
|
|
|
|
|
goto return_bad_resp;
|
2007-03-18 11:22:39 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/*
|
|
|
|
|
* 7: check if result will be cacheable with a cookie.
|
|
|
|
|
* We'll block the response if security checks have caught
|
|
|
|
|
* nasty things such as a cacheable cookie.
|
|
|
|
|
*/
|
|
|
|
|
if (((txn->flags & (TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_ANY)) ==
|
|
|
|
|
(TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_ANY)) &&
|
2009-09-15 15:25:21 -04:00
|
|
|
(t->be->options & PR_O_CHK_CACHE) &&
|
|
|
|
|
txn->status >= 200) {
|
2008-08-14 12:35:40 -04:00
|
|
|
|
|
|
|
|
/* we're in presence of a cacheable response containing
|
|
|
|
|
* a set-cookie header. We'll block it as requested by
|
|
|
|
|
* the 'checkcache' option, and send an alert.
|
2007-03-18 11:22:39 -04:00
|
|
|
*/
|
2009-03-15 18:11:49 -04:00
|
|
|
if (t->srv)
|
2009-10-04 08:52:57 -04:00
|
|
|
t->srv->counters.failed_secu++;
|
|
|
|
|
cur_proxy->counters.denied_resp++;
|
2008-08-14 12:35:40 -04:00
|
|
|
Alert("Blocking cacheable cookie in response from instance %s, server %s.\n",
|
|
|
|
|
t->be->id, t->srv?t->srv->id:"<dispatch>");
|
|
|
|
|
send_log(t->be, LOG_ALERT,
|
|
|
|
|
"Blocking cacheable cookie in response from instance %s, server %s.\n",
|
|
|
|
|
t->be->id, t->srv?t->srv->id:"<dispatch>");
|
|
|
|
|
goto return_srv_prx_502;
|
|
|
|
|
}
|
2007-03-18 11:22:39 -04:00
|
|
|
|
|
|
|
|
/*
|
2008-08-14 12:35:40 -04:00
|
|
|
* 8: add "Connection: close" if needed and not yet set.
|
|
|
|
|
* Note that we do not need to add it in case of HTTP/1.0.
|
2007-03-18 11:22:39 -04:00
|
|
|
*/
|
2008-08-14 12:35:40 -04:00
|
|
|
if (!(t->flags & SN_CONN_CLOSED) &&
|
2009-09-15 15:25:21 -04:00
|
|
|
((t->fe->options | t->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO)) &&
|
|
|
|
|
txn->status >= 200) {
|
2008-08-14 12:35:40 -04:00
|
|
|
if ((unlikely(msg->sl.st.v_l != 8) ||
|
|
|
|
|
unlikely(req->data[msg->som + 7] != '0')) &&
|
|
|
|
|
unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
|
|
|
|
|
"Connection: close", 17)) < 0)
|
|
|
|
|
goto return_bad_resp;
|
|
|
|
|
t->flags |= SN_CONN_CLOSED;
|
|
|
|
|
}
|
2007-03-18 11:22:39 -04:00
|
|
|
|
2009-09-15 15:25:21 -04:00
|
|
|
/*
|
|
|
|
|
* 9: we may be facing a 1xx response (100 continue, 101 switching protocols),
|
|
|
|
|
* in which case this is not the right response, and we're waiting for the
|
|
|
|
|
* next one. Let's allow this response to go to the client and wait for the
|
|
|
|
|
* next one.
|
|
|
|
|
*/
|
|
|
|
|
if (txn->status < 200) {
|
|
|
|
|
hdr_idx_init(&txn->hdr_idx);
|
|
|
|
|
buffer_forward(rep, rep->lr - (rep->data + msg->som));
|
|
|
|
|
msg->msg_state = HTTP_MSG_RPBEFORE;
|
|
|
|
|
txn->status = 0;
|
|
|
|
|
rep->analysers |= AN_RTR_HTTP_HDR;
|
|
|
|
|
goto next_response;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/*************************************************************
|
|
|
|
|
* OK, that's finished for the headers. We have done what we *
|
|
|
|
|
* could. Let's switch to the DATA state. *
|
|
|
|
|
************************************************************/
|
2007-03-18 11:22:39 -04:00
|
|
|
|
2009-08-16 17:27:46 -04:00
|
|
|
buffer_set_rlim(rep, rep->size); /* no more rewrite needed */
|
2008-08-14 12:35:40 -04:00
|
|
|
t->logs.t_data = tv_ms_elapsed(&t->logs.tv_accept, &now);
|
2007-03-18 11:22:39 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/* if the user wants to log as soon as possible, without counting
|
|
|
|
|
* bytes from the server, then this is the right moment. We have
|
|
|
|
|
* to temporarily assign bytes_out to log what we currently have.
|
|
|
|
|
*/
|
|
|
|
|
if (t->fe->to_log && !(t->logs.logwait & LW_BYTES)) {
|
|
|
|
|
t->logs.t_close = t->logs.t_data; /* to get a valid end date */
|
|
|
|
|
t->logs.bytes_out = txn->rsp.eoh;
|
2008-11-30 13:02:32 -05:00
|
|
|
t->do_log(t);
|
2008-08-14 12:35:40 -04:00
|
|
|
t->logs.bytes_out = 0;
|
|
|
|
|
}
|
2007-03-18 11:22:39 -04:00
|
|
|
|
2008-08-14 12:35:40 -04:00
|
|
|
/* Note: we must not try to cheat by jumping directly to DATA,
|
|
|
|
|
* otherwise we would not let the client side wake up.
|
|
|
|
|
*/
|
2007-03-18 11:22:39 -04:00
|
|
|
|
2008-08-16 19:00:46 -04:00
|
|
|
return 0;
|
2008-08-14 12:35:40 -04:00
|
|
|
}
|
2008-08-16 19:00:46 -04:00
|
|
|
|
2008-08-17 09:20:19 -04:00
|
|
|
/* Note: eventhough nobody should set an unknown flag, clearing them right now will
|
|
|
|
|
* probably reduce one day's debugging session.
|
|
|
|
|
*/
|
|
|
|
|
#ifdef DEBUG_DEV
|
|
|
|
|
if (rep->analysers & ~(AN_RTR_HTTP_HDR)) {
|
|
|
|
|
fprintf(stderr, "FIXME !!!! unknown analysers flags %s:%d = 0x%08X\n",
|
|
|
|
|
__FILE__, __LINE__, rep->analysers);
|
|
|
|
|
ABORT_NOW();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
rep->analysers &= AN_RTR_HTTP_HDR;
|
2008-08-14 12:35:40 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
2007-03-18 11:22:39 -04:00
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
/* Iterate the same filter through all request headers.
|
|
|
|
|
* Returns 1 if this filter can be stopped upon return, otherwise 0.
|
2007-03-18 11:22:39 -04:00
|
|
|
* Since it can manage the switch to another backend, it updates the per-proxy
|
|
|
|
|
* DENY stats.
|
2006-12-03 20:26:12 -05:00
|
|
|
*/
|
2007-01-21 13:16:41 -05:00
|
|
|
int apply_filter_to_req_headers(struct session *t, struct buffer *req, struct hdr_exp *exp)
|
2006-12-03 20:26:12 -05:00
|
|
|
{
|
2007-01-21 13:16:41 -05:00
|
|
|
char term;
|
|
|
|
|
char *cur_ptr, *cur_end, *cur_next;
|
|
|
|
|
int cur_idx, old_idx, last_hdr;
|
2007-03-03 10:23:22 -05:00
|
|
|
struct http_txn *txn = &t->txn;
|
2007-01-21 13:16:41 -05:00
|
|
|
struct hdr_idx_elem *cur_hdr;
|
|
|
|
|
int len, delta;
|
2007-01-07 09:46:13 -05:00
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
last_hdr = 0;
|
|
|
|
|
|
2007-03-03 10:23:22 -05:00
|
|
|
cur_next = req->data + txn->req.som + hdr_idx_first_pos(&txn->hdr_idx);
|
2007-01-21 13:16:41 -05:00
|
|
|
old_idx = 0;
|
|
|
|
|
|
|
|
|
|
while (!last_hdr) {
|
2007-03-18 13:34:41 -04:00
|
|
|
if (unlikely(txn->flags & (TX_CLDENY | TX_CLTARPIT)))
|
2007-01-21 13:16:41 -05:00
|
|
|
return 1;
|
2007-03-18 13:34:41 -04:00
|
|
|
else if (unlikely(txn->flags & TX_CLALLOW) &&
|
2007-01-21 13:16:41 -05:00
|
|
|
(exp->action == ACT_ALLOW ||
|
|
|
|
|
exp->action == ACT_DENY ||
|
|
|
|
|
exp->action == ACT_TARPIT))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-03-03 10:23:22 -05:00
|
|
|
cur_idx = txn->hdr_idx.v[old_idx].next;
|
2007-01-21 13:16:41 -05:00
|
|
|
if (!cur_idx)
|
|
|
|
|
break;
|
|
|
|
|
|
2007-03-03 10:23:22 -05:00
|
|
|
cur_hdr = &txn->hdr_idx.v[cur_idx];
|
2007-01-21 13:16:41 -05:00
|
|
|
cur_ptr = cur_next;
|
|
|
|
|
cur_end = cur_ptr + cur_hdr->len;
|
|
|
|
|
cur_next = cur_end + cur_hdr->cr + 1;
|
|
|
|
|
|
|
|
|
|
/* Now we have one header between cur_ptr and cur_end,
|
|
|
|
|
* and the next header starts at cur_next.
|
2006-12-03 20:26:12 -05:00
|
|
|
*/
|
|
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
/* The annoying part is that pattern matching needs
|
|
|
|
|
* that we modify the contents to null-terminate all
|
|
|
|
|
* strings before testing them.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
term = *cur_end;
|
|
|
|
|
*cur_end = '\0';
|
|
|
|
|
|
|
|
|
|
if (regexec(exp->preg, cur_ptr, MAX_MATCH, pmatch, 0) == 0) {
|
|
|
|
|
switch (exp->action) {
|
|
|
|
|
case ACT_SETBE:
|
|
|
|
|
/* It is not possible to jump a second time.
|
|
|
|
|
* FIXME: should we return an HTTP/500 here so that
|
|
|
|
|
* the admin knows there's a problem ?
|
|
|
|
|
*/
|
|
|
|
|
if (t->be != t->fe)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Swithing Proxy */
|
2009-07-07 09:10:31 -04:00
|
|
|
session_set_backend(t, (struct proxy *)exp->replace);
|
2007-01-21 13:16:41 -05:00
|
|
|
last_hdr = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_ALLOW:
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_CLALLOW;
|
2007-01-21 13:16:41 -05:00
|
|
|
last_hdr = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_DENY:
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_CLDENY;
|
2007-01-21 13:16:41 -05:00
|
|
|
last_hdr = 1;
|
2009-10-04 08:52:57 -04:00
|
|
|
t->be->counters.denied_req++;
|
2007-01-21 13:16:41 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_TARPIT:
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_CLTARPIT;
|
2007-01-21 13:16:41 -05:00
|
|
|
last_hdr = 1;
|
2009-10-04 08:52:57 -04:00
|
|
|
t->be->counters.denied_req++;
|
2007-01-21 13:16:41 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_REPLACE:
|
|
|
|
|
len = exp_replace(trash, cur_ptr, exp->replace, pmatch);
|
|
|
|
|
delta = buffer_replace2(req, cur_ptr, cur_end, trash, len);
|
|
|
|
|
/* FIXME: if the user adds a newline in the replacement, the
|
|
|
|
|
* index will not be recalculated for now, and the new line
|
|
|
|
|
* will not be counted as a new header.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
cur_end += delta;
|
|
|
|
|
cur_next += delta;
|
|
|
|
|
cur_hdr->len += delta;
|
2007-03-03 10:23:22 -05:00
|
|
|
txn->req.eoh += delta;
|
2007-01-21 13:16:41 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_REMOVE:
|
|
|
|
|
delta = buffer_replace2(req, cur_ptr, cur_next, NULL, 0);
|
|
|
|
|
cur_next += delta;
|
|
|
|
|
|
|
|
|
|
/* FIXME: this should be a separate function */
|
2007-03-03 10:23:22 -05:00
|
|
|
txn->req.eoh += delta;
|
|
|
|
|
txn->hdr_idx.v[old_idx].next = cur_hdr->next;
|
|
|
|
|
txn->hdr_idx.used--;
|
2007-01-21 13:16:41 -05:00
|
|
|
cur_hdr->len = 0;
|
|
|
|
|
cur_end = NULL; /* null-term has been rewritten */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
2007-01-21 13:16:41 -05:00
|
|
|
if (cur_end)
|
|
|
|
|
*cur_end = term; /* restore the string terminator */
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
/* keep the link from this header to next one in case of later
|
|
|
|
|
* removal of next header.
|
2006-12-03 20:26:12 -05:00
|
|
|
*/
|
2007-01-21 13:16:41 -05:00
|
|
|
old_idx = cur_idx;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Apply the filter to the request line.
|
|
|
|
|
* Returns 0 if nothing has been done, 1 if the filter has been applied,
|
|
|
|
|
* or -1 if a replacement resulted in an invalid request line.
|
2007-03-18 11:22:39 -04:00
|
|
|
* Since it can manage the switch to another backend, it updates the per-proxy
|
|
|
|
|
* DENY stats.
|
2007-01-21 13:16:41 -05:00
|
|
|
*/
|
|
|
|
|
int apply_filter_to_req_line(struct session *t, struct buffer *req, struct hdr_exp *exp)
|
|
|
|
|
{
|
|
|
|
|
char term;
|
|
|
|
|
char *cur_ptr, *cur_end;
|
|
|
|
|
int done;
|
2007-03-03 10:23:22 -05:00
|
|
|
struct http_txn *txn = &t->txn;
|
2007-01-21 13:16:41 -05:00
|
|
|
int len, delta;
|
|
|
|
|
|
|
|
|
|
|
2007-03-18 13:34:41 -04:00
|
|
|
if (unlikely(txn->flags & (TX_CLDENY | TX_CLTARPIT)))
|
2007-01-21 13:16:41 -05:00
|
|
|
return 1;
|
2007-03-18 13:34:41 -04:00
|
|
|
else if (unlikely(txn->flags & TX_CLALLOW) &&
|
2007-01-21 13:16:41 -05:00
|
|
|
(exp->action == ACT_ALLOW ||
|
|
|
|
|
exp->action == ACT_DENY ||
|
|
|
|
|
exp->action == ACT_TARPIT))
|
|
|
|
|
return 0;
|
|
|
|
|
else if (exp->action == ACT_REMOVE)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
done = 0;
|
|
|
|
|
|
2007-05-02 14:58:19 -04:00
|
|
|
cur_ptr = req->data + txn->req.som; /* should be equal to txn->sol */
|
2007-03-03 10:23:22 -05:00
|
|
|
cur_end = cur_ptr + txn->req.sl.rq.l;
|
2007-01-21 13:16:41 -05:00
|
|
|
|
|
|
|
|
/* Now we have the request line between cur_ptr and cur_end */
|
|
|
|
|
|
|
|
|
|
/* The annoying part is that pattern matching needs
|
|
|
|
|
* that we modify the contents to null-terminate all
|
|
|
|
|
* strings before testing them.
|
|
|
|
|
*/
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
term = *cur_end;
|
|
|
|
|
*cur_end = '\0';
|
|
|
|
|
|
|
|
|
|
if (regexec(exp->preg, cur_ptr, MAX_MATCH, pmatch, 0) == 0) {
|
|
|
|
|
switch (exp->action) {
|
|
|
|
|
case ACT_SETBE:
|
|
|
|
|
/* It is not possible to jump a second time.
|
|
|
|
|
* FIXME: should we return an HTTP/500 here so that
|
|
|
|
|
* the admin knows there's a problem ?
|
2006-12-03 20:26:12 -05:00
|
|
|
*/
|
2007-01-21 13:16:41 -05:00
|
|
|
if (t->be != t->fe)
|
|
|
|
|
break;
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
/* Swithing Proxy */
|
2009-07-07 09:10:31 -04:00
|
|
|
session_set_backend(t, (struct proxy *)exp->replace);
|
2007-01-21 13:16:41 -05:00
|
|
|
done = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_ALLOW:
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_CLALLOW;
|
2007-01-21 13:16:41 -05:00
|
|
|
done = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_DENY:
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_CLDENY;
|
2009-10-04 08:52:57 -04:00
|
|
|
t->be->counters.denied_req++;
|
2007-01-21 13:16:41 -05:00
|
|
|
done = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_TARPIT:
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_CLTARPIT;
|
2009-10-04 08:52:57 -04:00
|
|
|
t->be->counters.denied_req++;
|
2007-01-21 13:16:41 -05:00
|
|
|
done = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_REPLACE:
|
|
|
|
|
*cur_end = term; /* restore the string terminator */
|
|
|
|
|
len = exp_replace(trash, cur_ptr, exp->replace, pmatch);
|
|
|
|
|
delta = buffer_replace2(req, cur_ptr, cur_end, trash, len);
|
|
|
|
|
/* FIXME: if the user adds a newline in the replacement, the
|
|
|
|
|
* index will not be recalculated for now, and the new line
|
|
|
|
|
* will not be counted as a new header.
|
|
|
|
|
*/
|
2006-12-17 17:15:24 -05:00
|
|
|
|
2007-03-03 10:23:22 -05:00
|
|
|
txn->req.eoh += delta;
|
2007-01-21 13:16:41 -05:00
|
|
|
cur_end += delta;
|
2006-12-17 17:15:24 -05:00
|
|
|
|
2007-05-02 14:58:19 -04:00
|
|
|
txn->req.sol = req->data + txn->req.som; /* should be equal to txn->sol */
|
2007-03-03 10:23:22 -05:00
|
|
|
cur_end = (char *)http_parse_reqline(&txn->req, req->data,
|
2007-01-21 13:16:41 -05:00
|
|
|
HTTP_MSG_RQMETH,
|
|
|
|
|
cur_ptr, cur_end + 1,
|
|
|
|
|
NULL, NULL);
|
|
|
|
|
if (unlikely(!cur_end))
|
|
|
|
|
return -1;
|
2006-12-17 17:15:24 -05:00
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
/* we have a full request and we know that we have either a CR
|
|
|
|
|
* or an LF at <ptr>.
|
|
|
|
|
*/
|
2007-03-03 10:23:22 -05:00
|
|
|
txn->meth = find_http_meth(cur_ptr, txn->req.sl.rq.m_l);
|
|
|
|
|
hdr_idx_set_start(&txn->hdr_idx, txn->req.sl.rq.l, *cur_end == '\r');
|
2007-01-21 13:16:41 -05:00
|
|
|
/* there is no point trying this regex on headers */
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*cur_end = term; /* restore the string terminator */
|
|
|
|
|
return done;
|
|
|
|
|
}
|
2006-12-17 17:15:24 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
/*
|
|
|
|
|
* Apply all the req filters <exp> to all headers in buffer <req> of session <t>.
|
|
|
|
|
* Returns 0 if everything is alright, or -1 in case a replacement lead to an
|
2007-03-18 11:22:39 -04:00
|
|
|
* unparsable request. Since it can manage the switch to another backend, it
|
|
|
|
|
* updates the per-proxy DENY stats.
|
2007-01-21 13:16:41 -05:00
|
|
|
*/
|
|
|
|
|
int apply_filters_to_request(struct session *t, struct buffer *req, struct hdr_exp *exp)
|
|
|
|
|
{
|
2007-03-18 13:34:41 -04:00
|
|
|
struct http_txn *txn = &t->txn;
|
2007-01-21 13:16:41 -05:00
|
|
|
/* iterate through the filters in the outer loop */
|
2007-03-18 13:34:41 -04:00
|
|
|
while (exp && !(txn->flags & (TX_CLDENY|TX_CLTARPIT))) {
|
2007-01-21 13:16:41 -05:00
|
|
|
int ret;
|
2006-12-27 11:18:38 -05:00
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
/*
|
|
|
|
|
* The interleaving of transformations and verdicts
|
|
|
|
|
* makes it difficult to decide to continue or stop
|
|
|
|
|
* the evaluation.
|
|
|
|
|
*/
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2007-03-18 13:34:41 -04:00
|
|
|
if ((txn->flags & TX_CLALLOW) &&
|
2007-01-21 13:16:41 -05:00
|
|
|
(exp->action == ACT_ALLOW || exp->action == ACT_DENY ||
|
|
|
|
|
exp->action == ACT_TARPIT || exp->action == ACT_PASS)) {
|
|
|
|
|
exp = exp->next;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
/* Apply the filter to the request line. */
|
|
|
|
|
ret = apply_filter_to_req_line(t, req, exp);
|
|
|
|
|
if (unlikely(ret < 0))
|
|
|
|
|
return -1;
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
if (likely(ret == 0)) {
|
|
|
|
|
/* The filter did not match the request, it can be
|
|
|
|
|
* iterated through all headers.
|
|
|
|
|
*/
|
|
|
|
|
apply_filter_to_req_headers(t, req, exp);
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
|
|
|
|
exp = exp->next;
|
|
|
|
|
}
|
2007-01-21 13:16:41 -05:00
|
|
|
return 0;
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-03-18 11:22:39 -04:00
|
|
|
|
2006-12-03 20:26:12 -05:00
|
|
|
/*
|
2007-11-04 13:30:00 -05:00
|
|
|
* Manage client-side cookie. It can impact performance by about 2% so it is
|
|
|
|
|
* desirable to call it only when needed.
|
2006-12-03 20:26:12 -05:00
|
|
|
*/
|
|
|
|
|
void manage_client_side_cookies(struct session *t, struct buffer *req)
|
|
|
|
|
{
|
2007-03-03 10:23:22 -05:00
|
|
|
struct http_txn *txn = &t->txn;
|
2006-12-03 20:26:12 -05:00
|
|
|
char *p1, *p2, *p3, *p4;
|
|
|
|
|
char *del_colon, *del_cookie, *colon;
|
|
|
|
|
int app_cookies;
|
|
|
|
|
|
|
|
|
|
appsess *asession_temp = NULL;
|
|
|
|
|
appsess local_asession;
|
|
|
|
|
|
|
|
|
|
char *cur_ptr, *cur_end, *cur_next;
|
2007-01-21 13:16:41 -05:00
|
|
|
int cur_idx, old_idx;
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2006-12-04 18:05:46 -05:00
|
|
|
/* Iterate through the headers.
|
2006-12-03 20:26:12 -05:00
|
|
|
* we start with the start line.
|
|
|
|
|
*/
|
2007-01-22 02:55:47 -05:00
|
|
|
old_idx = 0;
|
2007-03-03 10:23:22 -05:00
|
|
|
cur_next = req->data + txn->req.som + hdr_idx_first_pos(&txn->hdr_idx);
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2007-03-03 10:23:22 -05:00
|
|
|
while ((cur_idx = txn->hdr_idx.v[old_idx].next)) {
|
2006-12-03 20:26:12 -05:00
|
|
|
struct hdr_idx_elem *cur_hdr;
|
2007-03-18 18:50:16 -04:00
|
|
|
int val;
|
2006-12-03 20:26:12 -05:00
|
|
|
|
2007-03-03 10:23:22 -05:00
|
|
|
cur_hdr = &txn->hdr_idx.v[cur_idx];
|
2006-12-03 20:26:12 -05:00
|
|
|
cur_ptr = cur_next;
|
|
|
|
|
cur_end = cur_ptr + cur_hdr->len;
|
|
|
|
|
cur_next = cur_end + cur_hdr->cr + 1;
|
|
|
|
|
|
|
|
|
|
/* We have one full header between cur_ptr and cur_end, and the
|
|
|
|
|
* next header starts at cur_next. We're only interested in
|
|
|
|
|
* "Cookie:" headers.
|
|
|
|
|
*/
|
|
|
|
|
|
2007-03-18 18:50:16 -04:00
|
|
|
val = http_header_match2(cur_ptr, cur_end, "Cookie", 6);
|
|
|
|
|
if (!val) {
|
2006-12-03 20:26:12 -05:00
|
|
|
old_idx = cur_idx;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now look for cookies. Conforming to RFC2109, we have to support
|
|
|
|
|
* attributes whose name begin with a '$', and associate them with
|
|
|
|
|
* the right cookie, if we want to delete this cookie.
|
|
|
|
|
* So there are 3 cases for each cookie read :
|
|
|
|
|
* 1) it's a special attribute, beginning with a '$' : ignore it.
|
|
|
|
|
* 2) it's a server id cookie that we *MAY* want to delete : save
|
|
|
|
|
* some pointers on it (last semi-colon, beginning of cookie...)
|
|
|
|
|
* 3) it's an application cookie : we *MAY* have to delete a previous
|
|
|
|
|
* "special" cookie.
|
|
|
|
|
* At the end of loop, if a "special" cookie remains, we may have to
|
|
|
|
|
* remove it. If no application cookie persists in the header, we
|
|
|
|
|
* *MUST* delete it
|
|
|
|
|
*/
|
|
|
|
|
|
2007-03-18 18:50:16 -04:00
|
|
|
colon = p1 = cur_ptr + val; /* first non-space char after 'Cookie:' */
|
2006-12-03 20:26:12 -05:00
|
|
|
|
|
|
|
|
/* del_cookie == NULL => nothing to be deleted */
|
|
|
|
|
del_colon = del_cookie = NULL;
|
|
|
|
|
app_cookies = 0;
|
|
|
|
|
|
|
|
|
|
while (p1 < cur_end) {
|
|
|
|
|
/* skip spaces and colons, but keep an eye on these ones */
|
|
|
|
|
while (p1 < cur_end) {
|
|
|
|
|
if (*p1 == ';' || *p1 == ',')
|
|
|
|
|
colon = p1;
|
2007-06-17 15:51:38 -04:00
|
|
|
else if (!isspace((unsigned char)*p1))
|
2006-12-03 20:26:12 -05:00
|
|
|
break;
|
|
|
|
|
p1++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (p1 == cur_end)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* p1 is at the beginning of the cookie name */
|
|
|
|
|
p2 = p1;
|
|
|
|
|
while (p2 < cur_end && *p2 != '=')
|
|
|
|
|
p2++;
|
|
|
|
|
|
|
|
|
|
if (p2 == cur_end)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
p3 = p2 + 1; /* skips the '=' sign */
|
|
|
|
|
if (p3 == cur_end)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
p4 = p3;
|
2007-06-17 15:51:38 -04:00
|
|
|
while (p4 < cur_end && !isspace((unsigned char)*p4) && *p4 != ';' && *p4 != ',')
|
2006-12-03 20:26:12 -05:00
|
|
|
p4++;
|
|
|
|
|
|
|
|
|
|
/* here, we have the cookie name between p1 and p2,
|
|
|
|
|
* and its value between p3 and p4.
|
|
|
|
|
* we can process it :
|
|
|
|
|
*
|
|
|
|
|
* Cookie: NAME=VALUE;
|
|
|
|
|
* | || || |
|
|
|
|
|
* | || || +--> p4
|
|
|
|
|
* | || |+-------> p3
|
|
|
|
|
* | || +--------> p2
|
|
|
|
|
* | |+------------> p1
|
|
|
|
|
* | +-------------> colon
|
|
|
|
|
* +--------------------> cur_ptr
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (*p1 == '$') {
|
|
|
|
|
/* skip this one */
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* first, let's see if we want to capture it */
|
2007-03-31 18:01:37 -04:00
|
|
|
if (t->fe->capture_name != NULL &&
|
2007-03-18 12:31:28 -04:00
|
|
|
txn->cli_cookie == NULL &&
|
2007-03-31 18:01:37 -04:00
|
|
|
(p4 - p1 >= t->fe->capture_namelen) &&
|
|
|
|
|
memcmp(p1, t->fe->capture_name, t->fe->capture_namelen) == 0) {
|
2006-12-03 20:26:12 -05:00
|
|
|
int log_len = p4 - p1;
|
|
|
|
|
|
2007-05-13 15:45:51 -04:00
|
|
|
if ((txn->cli_cookie = pool_alloc2(pool2_capture)) == NULL) {
|
2006-12-03 20:26:12 -05:00
|
|
|
Alert("HTTP logging : out of memory.\n");
|
|
|
|
|
} else {
|
2007-03-31 18:01:37 -04:00
|
|
|
if (log_len > t->fe->capture_len)
|
|
|
|
|
log_len = t->fe->capture_len;
|
2007-03-18 12:31:28 -04:00
|
|
|
memcpy(txn->cli_cookie, p1, log_len);
|
|
|
|
|
txn->cli_cookie[log_len] = 0;
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-31 18:01:37 -04:00
|
|
|
if ((p2 - p1 == t->be->cookie_len) && (t->be->cookie_name != NULL) &&
|
|
|
|
|
(memcmp(p1, t->be->cookie_name, p2 - p1) == 0)) {
|
2006-12-03 20:26:12 -05:00
|
|
|
/* Cool... it's the right one */
|
2007-03-31 18:01:37 -04:00
|
|
|
struct server *srv = t->be->srv;
|
2006-12-03 20:26:12 -05:00
|
|
|
char *delim;
|
|
|
|
|
|
|
|
|
|
/* if we're in cookie prefix mode, we'll search the delimitor so that we
|
|
|
|
|
* have the server ID betweek p3 and delim, and the original cookie between
|
|
|
|
|
* delim+1 and p4. Otherwise, delim==p4 :
|
|
|
|
|
*
|
|
|
|
|
* Cookie: NAME=SRV~VALUE;
|
|
|
|
|
* | || || | |
|
|
|
|
|
* | || || | +--> p4
|
|
|
|
|
* | || || +--------> delim
|
|
|
|
|
* | || |+-----------> p3
|
|
|
|
|
* | || +------------> p2
|
|
|
|
|
* | |+----------------> p1
|
|
|
|
|
* | +-----------------> colon
|
|
|
|
|
* +------------------------> cur_ptr
|
|
|
|
|
*/
|
|
|
|
|
|
2007-03-31 18:01:37 -04:00
|
|
|
if (t->be->options & PR_O_COOK_PFX) {
|
2006-12-03 20:26:12 -05:00
|
|
|
for (delim = p3; delim < p4; delim++)
|
|
|
|
|
if (*delim == COOKIE_DELIM)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
delim = p4;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Here, we'll look for the first running server which supports the cookie.
|
|
|
|
|
* This allows to share a same cookie between several servers, for example
|
|
|
|
|
* to dedicate backup servers to specific servers only.
|
|
|
|
|
* However, to prevent clients from sticking to cookie-less backup server
|
|
|
|
|
* when they have incidentely learned an empty cookie, we simply ignore
|
|
|
|
|
* empty cookies and mark them as invalid.
|
|
|
|
|
*/
|
|
|
|
|
if (delim == p3)
|
|
|
|
|
srv = NULL;
|
|
|
|
|
|
|
|
|
|
while (srv) {
|
2007-02-02 16:14:47 -05:00
|
|
|
if (srv->cookie && (srv->cklen == delim - p3) &&
|
|
|
|
|
!memcmp(p3, srv->cookie, delim - p3)) {
|
2007-03-31 18:01:37 -04:00
|
|
|
if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
|
2006-12-03 20:26:12 -05:00
|
|
|
/* we found the server and it's usable */
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags &= ~TX_CK_MASK;
|
|
|
|
|
txn->flags |= TX_CK_VALID;
|
|
|
|
|
t->flags |= SN_DIRECT | SN_ASSIGNED;
|
2006-12-03 20:26:12 -05:00
|
|
|
t->srv = srv;
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
/* we found a server, but it's down */
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags &= ~TX_CK_MASK;
|
|
|
|
|
txn->flags |= TX_CK_DOWN;
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
srv = srv->next;
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-18 13:34:41 -04:00
|
|
|
if (!srv && !(txn->flags & TX_CK_DOWN)) {
|
2006-12-03 20:26:12 -05:00
|
|
|
/* no server matched this cookie */
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags &= ~TX_CK_MASK;
|
|
|
|
|
txn->flags |= TX_CK_INVALID;
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* depending on the cookie mode, we may have to either :
|
|
|
|
|
* - delete the complete cookie if we're in insert+indirect mode, so that
|
|
|
|
|
* the server never sees it ;
|
|
|
|
|
* - remove the server id from the cookie value, and tag the cookie as an
|
|
|
|
|
* application cookie so that it does not get accidentely removed later,
|
|
|
|
|
* if we're in cookie prefix mode
|
|
|
|
|
*/
|
2007-03-31 18:01:37 -04:00
|
|
|
if ((t->be->options & PR_O_COOK_PFX) && (delim != p4)) {
|
2006-12-03 20:26:12 -05:00
|
|
|
int delta; /* negative */
|
|
|
|
|
|
|
|
|
|
delta = buffer_replace2(req, p3, delim + 1, NULL, 0);
|
|
|
|
|
p4 += delta;
|
|
|
|
|
cur_end += delta;
|
|
|
|
|
cur_next += delta;
|
|
|
|
|
cur_hdr->len += delta;
|
2007-03-03 10:23:22 -05:00
|
|
|
txn->req.eoh += delta;
|
2006-12-03 20:26:12 -05:00
|
|
|
|
|
|
|
|
del_cookie = del_colon = NULL;
|
|
|
|
|
app_cookies++; /* protect the header from deletion */
|
|
|
|
|
}
|
|
|
|
|
else if (del_cookie == NULL &&
|
2007-03-31 18:01:37 -04:00
|
|
|
(t->be->options & (PR_O_COOK_INS | PR_O_COOK_IND)) == (PR_O_COOK_INS | PR_O_COOK_IND)) {
|
2006-12-03 20:26:12 -05:00
|
|
|
del_cookie = p1;
|
|
|
|
|
del_colon = colon;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* now we know that we must keep this cookie since it's
|
|
|
|
|
* not ours. But if we wanted to delete our cookie
|
|
|
|
|
* earlier, we cannot remove the complete header, but we
|
|
|
|
|
* can remove the previous block itself.
|
|
|
|
|
*/
|
|
|
|
|
app_cookies++;
|
|
|
|
|
|
|
|
|
|
if (del_cookie != NULL) {
|
|
|
|
|
int delta; /* negative */
|
|
|
|
|
|
|
|
|
|
delta = buffer_replace2(req, del_cookie, p1, NULL, 0);
|
|
|
|
|
p4 += delta;
|
|
|
|
|
cur_end += delta;
|
|
|
|
|
cur_next += delta;
|
|
|
|
|
cur_hdr->len += delta;
|
2007-03-03 10:23:22 -05:00
|
|
|
txn->req.eoh += delta;
|
2006-12-03 20:26:12 -05:00
|
|
|
del_cookie = del_colon = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-31 18:01:37 -04:00
|
|
|
if ((t->be->appsession_name != NULL) &&
|
|
|
|
|
(memcmp(p1, t->be->appsession_name, p2 - p1) == 0)) {
|
2006-12-03 20:26:12 -05:00
|
|
|
/* first, let's see if the cookie is our appcookie*/
|
2008-08-13 13:57:02 -04:00
|
|
|
|
2006-12-03 20:26:12 -05:00
|
|
|
/* Cool... it's the right one */
|
|
|
|
|
|
|
|
|
|
asession_temp = &local_asession;
|
|
|
|
|
|
2007-05-13 15:29:55 -04:00
|
|
|
if ((asession_temp->sessid = pool_alloc2(apools.sessid)) == NULL) {
|
2006-12-03 20:26:12 -05:00
|
|
|
Alert("Not enough memory process_cli():asession->sessid:malloc().\n");
|
|
|
|
|
send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession->sessid:malloc().\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-31 18:01:37 -04:00
|
|
|
memcpy(asession_temp->sessid, p3, t->be->appsession_len);
|
|
|
|
|
asession_temp->sessid[t->be->appsession_len] = 0;
|
2006-12-03 20:26:12 -05:00
|
|
|
asession_temp->serverid = NULL;
|
2007-09-09 15:56:53 -04:00
|
|
|
|
2006-12-03 20:26:12 -05:00
|
|
|
/* only do insert, if lookup fails */
|
2007-09-09 15:56:53 -04:00
|
|
|
asession_temp = appsession_hash_lookup(&(t->be->htbl_proxy), asession_temp->sessid);
|
|
|
|
|
if (asession_temp == NULL) {
|
2007-05-13 15:29:55 -04:00
|
|
|
if ((asession_temp = pool_alloc2(pool2_appsess)) == NULL) {
|
2006-12-03 20:26:12 -05:00
|
|
|
/* free previously allocated memory */
|
2007-05-13 15:29:55 -04:00
|
|
|
pool_free2(apools.sessid, local_asession.sessid);
|
2006-12-03 20:26:12 -05:00
|
|
|
Alert("Not enough memory process_cli():asession:calloc().\n");
|
|
|
|
|
send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
asession_temp->sessid = local_asession.sessid;
|
|
|
|
|
asession_temp->serverid = local_asession.serverid;
|
2008-08-13 13:57:02 -04:00
|
|
|
asession_temp->request_count = 0;
|
2007-09-09 15:56:53 -04:00
|
|
|
appsession_hash_insert(&(t->be->htbl_proxy), asession_temp);
|
2006-12-03 20:26:12 -05:00
|
|
|
} else {
|
|
|
|
|
/* free previously allocated memory */
|
2007-05-13 15:29:55 -04:00
|
|
|
pool_free2(apools.sessid, local_asession.sessid);
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
|
|
|
|
if (asession_temp->serverid == NULL) {
|
2008-08-13 13:57:02 -04:00
|
|
|
/* TODO redispatch request */
|
2006-12-03 20:26:12 -05:00
|
|
|
Alert("Found Application Session without matching server.\n");
|
|
|
|
|
} else {
|
2007-03-31 18:01:37 -04:00
|
|
|
struct server *srv = t->be->srv;
|
2006-12-03 20:26:12 -05:00
|
|
|
while (srv) {
|
|
|
|
|
if (strcmp(srv->id, asession_temp->serverid) == 0) {
|
2007-03-31 18:01:37 -04:00
|
|
|
if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
|
2006-12-03 20:26:12 -05:00
|
|
|
/* we found the server and it's usable */
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags &= ~TX_CK_MASK;
|
|
|
|
|
txn->flags |= TX_CK_VALID;
|
|
|
|
|
t->flags |= SN_DIRECT | SN_ASSIGNED;
|
2006-12-03 20:26:12 -05:00
|
|
|
t->srv = srv;
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags &= ~TX_CK_MASK;
|
|
|
|
|
txn->flags |= TX_CK_DOWN;
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
srv = srv->next;
|
|
|
|
|
}/* end while(srv) */
|
|
|
|
|
}/* end else if server == NULL */
|
|
|
|
|
|
2008-07-06 18:09:58 -04:00
|
|
|
asession_temp->expire = tick_add_ifset(now_ms, t->be->timeout.appsession);
|
2008-08-13 13:57:02 -04:00
|
|
|
asession_temp->request_count++;
|
|
|
|
|
#if defined(DEBUG_HASH)
|
|
|
|
|
Alert("manage_client_side_cookies\n");
|
|
|
|
|
appsession_hash_dump(&(t->be->htbl_proxy));
|
|
|
|
|
#endif
|
2006-12-03 20:26:12 -05:00
|
|
|
}/* end if ((t->proxy->appsession_name != NULL) ... */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* we'll have to look for another cookie ... */
|
|
|
|
|
p1 = p4;
|
|
|
|
|
} /* while (p1 < cur_end) */
|
|
|
|
|
|
|
|
|
|
/* There's no more cookie on this line.
|
|
|
|
|
* We may have marked the last one(s) for deletion.
|
|
|
|
|
* We must do this now in two ways :
|
|
|
|
|
* - if there is no app cookie, we simply delete the header ;
|
|
|
|
|
* - if there are app cookies, we must delete the end of the
|
|
|
|
|
* string properly, including the colon/semi-colon before
|
|
|
|
|
* the cookie name.
|
|
|
|
|
*/
|
|
|
|
|
if (del_cookie != NULL) {
|
|
|
|
|
int delta;
|
|
|
|
|
if (app_cookies) {
|
|
|
|
|
delta = buffer_replace2(req, del_colon, cur_end, NULL, 0);
|
|
|
|
|
cur_end = del_colon;
|
|
|
|
|
cur_hdr->len += delta;
|
|
|
|
|
} else {
|
|
|
|
|
delta = buffer_replace2(req, cur_ptr, cur_next, NULL, 0);
|
|
|
|
|
|
|
|
|
|
/* FIXME: this should be a separate function */
|
2007-03-03 10:23:22 -05:00
|
|
|
txn->hdr_idx.v[old_idx].next = cur_hdr->next;
|
|
|
|
|
txn->hdr_idx.used--;
|
2006-12-03 20:26:12 -05:00
|
|
|
cur_hdr->len = 0;
|
|
|
|
|
}
|
2006-12-16 18:05:15 -05:00
|
|
|
cur_next += delta;
|
2007-03-03 10:23:22 -05:00
|
|
|
txn->req.eoh += delta;
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* keep the link from this header to next one */
|
|
|
|
|
old_idx = cur_idx;
|
|
|
|
|
} /* end of cookie processing on this header */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-03-18 11:22:39 -04:00
|
|
|
/* Iterate the same filter through all response headers contained in <rtr>.
|
|
|
|
|
* Returns 1 if this filter can be stopped upon return, otherwise 0.
|
|
|
|
|
*/
|
|
|
|
|
int apply_filter_to_resp_headers(struct session *t, struct buffer *rtr, struct hdr_exp *exp)
|
|
|
|
|
{
|
|
|
|
|
char term;
|
|
|
|
|
char *cur_ptr, *cur_end, *cur_next;
|
|
|
|
|
int cur_idx, old_idx, last_hdr;
|
|
|
|
|
struct http_txn *txn = &t->txn;
|
|
|
|
|
struct hdr_idx_elem *cur_hdr;
|
|
|
|
|
int len, delta;
|
|
|
|
|
|
|
|
|
|
last_hdr = 0;
|
|
|
|
|
|
|
|
|
|
cur_next = rtr->data + txn->rsp.som + hdr_idx_first_pos(&txn->hdr_idx);
|
|
|
|
|
old_idx = 0;
|
|
|
|
|
|
|
|
|
|
while (!last_hdr) {
|
2007-03-18 13:34:41 -04:00
|
|
|
if (unlikely(txn->flags & TX_SVDENY))
|
2007-03-18 11:22:39 -04:00
|
|
|
return 1;
|
2007-03-18 13:34:41 -04:00
|
|
|
else if (unlikely(txn->flags & TX_SVALLOW) &&
|
2007-03-18 11:22:39 -04:00
|
|
|
(exp->action == ACT_ALLOW ||
|
|
|
|
|
exp->action == ACT_DENY))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
cur_idx = txn->hdr_idx.v[old_idx].next;
|
|
|
|
|
if (!cur_idx)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
cur_hdr = &txn->hdr_idx.v[cur_idx];
|
|
|
|
|
cur_ptr = cur_next;
|
|
|
|
|
cur_end = cur_ptr + cur_hdr->len;
|
|
|
|
|
cur_next = cur_end + cur_hdr->cr + 1;
|
|
|
|
|
|
|
|
|
|
/* Now we have one header between cur_ptr and cur_end,
|
|
|
|
|
* and the next header starts at cur_next.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* The annoying part is that pattern matching needs
|
|
|
|
|
* that we modify the contents to null-terminate all
|
|
|
|
|
* strings before testing them.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
term = *cur_end;
|
|
|
|
|
*cur_end = '\0';
|
|
|
|
|
|
|
|
|
|
if (regexec(exp->preg, cur_ptr, MAX_MATCH, pmatch, 0) == 0) {
|
|
|
|
|
switch (exp->action) {
|
|
|
|
|
case ACT_ALLOW:
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_SVALLOW;
|
2007-03-18 11:22:39 -04:00
|
|
|
last_hdr = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_DENY:
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_SVDENY;
|
2007-03-18 11:22:39 -04:00
|
|
|
last_hdr = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_REPLACE:
|
|
|
|
|
len = exp_replace(trash, cur_ptr, exp->replace, pmatch);
|
|
|
|
|
delta = buffer_replace2(rtr, cur_ptr, cur_end, trash, len);
|
|
|
|
|
/* FIXME: if the user adds a newline in the replacement, the
|
|
|
|
|
* index will not be recalculated for now, and the new line
|
|
|
|
|
* will not be counted as a new header.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
cur_end += delta;
|
|
|
|
|
cur_next += delta;
|
|
|
|
|
cur_hdr->len += delta;
|
|
|
|
|
txn->rsp.eoh += delta;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_REMOVE:
|
|
|
|
|
delta = buffer_replace2(rtr, cur_ptr, cur_next, NULL, 0);
|
|
|
|
|
cur_next += delta;
|
|
|
|
|
|
|
|
|
|
/* FIXME: this should be a separate function */
|
|
|
|
|
txn->rsp.eoh += delta;
|
|
|
|
|
txn->hdr_idx.v[old_idx].next = cur_hdr->next;
|
|
|
|
|
txn->hdr_idx.used--;
|
|
|
|
|
cur_hdr->len = 0;
|
|
|
|
|
cur_end = NULL; /* null-term has been rewritten */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (cur_end)
|
|
|
|
|
*cur_end = term; /* restore the string terminator */
|
|
|
|
|
|
|
|
|
|
/* keep the link from this header to next one in case of later
|
|
|
|
|
* removal of next header.
|
|
|
|
|
*/
|
|
|
|
|
old_idx = cur_idx;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Apply the filter to the status line in the response buffer <rtr>.
|
|
|
|
|
* Returns 0 if nothing has been done, 1 if the filter has been applied,
|
|
|
|
|
* or -1 if a replacement resulted in an invalid status line.
|
|
|
|
|
*/
|
|
|
|
|
int apply_filter_to_sts_line(struct session *t, struct buffer *rtr, struct hdr_exp *exp)
|
|
|
|
|
{
|
|
|
|
|
char term;
|
|
|
|
|
char *cur_ptr, *cur_end;
|
|
|
|
|
int done;
|
|
|
|
|
struct http_txn *txn = &t->txn;
|
|
|
|
|
int len, delta;
|
|
|
|
|
|
|
|
|
|
|
2007-03-18 13:34:41 -04:00
|
|
|
if (unlikely(txn->flags & TX_SVDENY))
|
2007-03-18 11:22:39 -04:00
|
|
|
return 1;
|
2007-03-18 13:34:41 -04:00
|
|
|
else if (unlikely(txn->flags & TX_SVALLOW) &&
|
2007-03-18 11:22:39 -04:00
|
|
|
(exp->action == ACT_ALLOW ||
|
|
|
|
|
exp->action == ACT_DENY))
|
|
|
|
|
return 0;
|
|
|
|
|
else if (exp->action == ACT_REMOVE)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
done = 0;
|
|
|
|
|
|
2007-05-02 14:58:19 -04:00
|
|
|
cur_ptr = rtr->data + txn->rsp.som; /* should be equal to txn->sol */
|
2007-03-18 11:22:39 -04:00
|
|
|
cur_end = cur_ptr + txn->rsp.sl.rq.l;
|
|
|
|
|
|
|
|
|
|
/* Now we have the status line between cur_ptr and cur_end */
|
|
|
|
|
|
|
|
|
|
/* The annoying part is that pattern matching needs
|
|
|
|
|
* that we modify the contents to null-terminate all
|
|
|
|
|
* strings before testing them.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
term = *cur_end;
|
|
|
|
|
*cur_end = '\0';
|
|
|
|
|
|
|
|
|
|
if (regexec(exp->preg, cur_ptr, MAX_MATCH, pmatch, 0) == 0) {
|
|
|
|
|
switch (exp->action) {
|
|
|
|
|
case ACT_ALLOW:
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_SVALLOW;
|
2007-03-18 11:22:39 -04:00
|
|
|
done = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_DENY:
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_SVDENY;
|
2007-03-18 11:22:39 -04:00
|
|
|
done = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ACT_REPLACE:
|
|
|
|
|
*cur_end = term; /* restore the string terminator */
|
|
|
|
|
len = exp_replace(trash, cur_ptr, exp->replace, pmatch);
|
|
|
|
|
delta = buffer_replace2(rtr, cur_ptr, cur_end, trash, len);
|
|
|
|
|
/* FIXME: if the user adds a newline in the replacement, the
|
|
|
|
|
* index will not be recalculated for now, and the new line
|
|
|
|
|
* will not be counted as a new header.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
txn->rsp.eoh += delta;
|
|
|
|
|
cur_end += delta;
|
|
|
|
|
|
2007-05-02 14:58:19 -04:00
|
|
|
txn->rsp.sol = rtr->data + txn->rsp.som; /* should be equal to txn->sol */
|
2007-03-18 11:22:39 -04:00
|
|
|
cur_end = (char *)http_parse_stsline(&txn->rsp, rtr->data,
|
2007-04-03 08:45:44 -04:00
|
|
|
HTTP_MSG_RPVER,
|
2007-03-18 11:22:39 -04:00
|
|
|
cur_ptr, cur_end + 1,
|
|
|
|
|
NULL, NULL);
|
|
|
|
|
if (unlikely(!cur_end))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* we have a full respnse and we know that we have either a CR
|
|
|
|
|
* or an LF at <ptr>.
|
|
|
|
|
*/
|
2007-03-18 12:31:28 -04:00
|
|
|
txn->status = strl2ui(rtr->data + txn->rsp.sl.st.c, txn->rsp.sl.st.c_l);
|
2007-03-18 11:22:39 -04:00
|
|
|
hdr_idx_set_start(&txn->hdr_idx, txn->rsp.sl.rq.l, *cur_end == '\r');
|
|
|
|
|
/* there is no point trying this regex on headers */
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*cur_end = term; /* restore the string terminator */
|
|
|
|
|
return done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Apply all the resp filters <exp> to all headers in buffer <rtr> of session <t>.
|
|
|
|
|
* Returns 0 if everything is alright, or -1 in case a replacement lead to an
|
|
|
|
|
* unparsable response.
|
|
|
|
|
*/
|
|
|
|
|
int apply_filters_to_response(struct session *t, struct buffer *rtr, struct hdr_exp *exp)
|
|
|
|
|
{
|
2007-03-18 13:34:41 -04:00
|
|
|
struct http_txn *txn = &t->txn;
|
2007-03-18 11:22:39 -04:00
|
|
|
/* iterate through the filters in the outer loop */
|
2007-03-18 13:34:41 -04:00
|
|
|
while (exp && !(txn->flags & TX_SVDENY)) {
|
2007-03-18 11:22:39 -04:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The interleaving of transformations and verdicts
|
|
|
|
|
* makes it difficult to decide to continue or stop
|
|
|
|
|
* the evaluation.
|
|
|
|
|
*/
|
|
|
|
|
|
2007-03-18 13:34:41 -04:00
|
|
|
if ((txn->flags & TX_SVALLOW) &&
|
2007-03-18 11:22:39 -04:00
|
|
|
(exp->action == ACT_ALLOW || exp->action == ACT_DENY ||
|
|
|
|
|
exp->action == ACT_PASS)) {
|
|
|
|
|
exp = exp->next;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Apply the filter to the status line. */
|
|
|
|
|
ret = apply_filter_to_sts_line(t, rtr, exp);
|
|
|
|
|
if (unlikely(ret < 0))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (likely(ret == 0)) {
|
|
|
|
|
/* The filter did not match the response, it can be
|
|
|
|
|
* iterated through all headers.
|
|
|
|
|
*/
|
|
|
|
|
apply_filter_to_resp_headers(t, rtr, exp);
|
|
|
|
|
}
|
|
|
|
|
exp = exp->next;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2007-11-04 13:30:00 -05:00
|
|
|
* Manage server-side cookies. It can impact performance by about 2% so it is
|
|
|
|
|
* desirable to call it only when needed.
|
2007-03-18 11:22:39 -04:00
|
|
|
*/
|
|
|
|
|
void manage_server_side_cookies(struct session *t, struct buffer *rtr)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = &t->txn;
|
|
|
|
|
char *p1, *p2, *p3, *p4;
|
|
|
|
|
|
|
|
|
|
appsess *asession_temp = NULL;
|
|
|
|
|
appsess local_asession;
|
|
|
|
|
|
|
|
|
|
char *cur_ptr, *cur_end, *cur_next;
|
|
|
|
|
int cur_idx, old_idx, delta;
|
|
|
|
|
|
|
|
|
|
/* Iterate through the headers.
|
|
|
|
|
* we start with the start line.
|
|
|
|
|
*/
|
|
|
|
|
old_idx = 0;
|
|
|
|
|
cur_next = rtr->data + txn->rsp.som + hdr_idx_first_pos(&txn->hdr_idx);
|
|
|
|
|
|
|
|
|
|
while ((cur_idx = txn->hdr_idx.v[old_idx].next)) {
|
|
|
|
|
struct hdr_idx_elem *cur_hdr;
|
2007-03-18 18:50:16 -04:00
|
|
|
int val;
|
2007-03-18 11:22:39 -04:00
|
|
|
|
|
|
|
|
cur_hdr = &txn->hdr_idx.v[cur_idx];
|
|
|
|
|
cur_ptr = cur_next;
|
|
|
|
|
cur_end = cur_ptr + cur_hdr->len;
|
|
|
|
|
cur_next = cur_end + cur_hdr->cr + 1;
|
|
|
|
|
|
|
|
|
|
/* We have one full header between cur_ptr and cur_end, and the
|
|
|
|
|
* next header starts at cur_next. We're only interested in
|
|
|
|
|
* "Cookie:" headers.
|
|
|
|
|
*/
|
|
|
|
|
|
2007-03-18 18:50:16 -04:00
|
|
|
val = http_header_match2(cur_ptr, cur_end, "Set-Cookie", 10);
|
|
|
|
|
if (!val) {
|
2007-03-18 11:22:39 -04:00
|
|
|
old_idx = cur_idx;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* OK, right now we know we have a set-cookie at cur_ptr */
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_SCK_ANY;
|
2007-03-18 11:22:39 -04:00
|
|
|
|
|
|
|
|
|
2008-10-17 06:01:58 -04:00
|
|
|
/* maybe we only wanted to see if there was a set-cookie. Note that
|
|
|
|
|
* the cookie capture is declared in the fronend.
|
|
|
|
|
*/
|
2007-03-31 18:01:37 -04:00
|
|
|
if (t->be->cookie_name == NULL &&
|
|
|
|
|
t->be->appsession_name == NULL &&
|
2008-10-17 06:01:58 -04:00
|
|
|
t->fe->capture_name == NULL)
|
2007-03-18 11:22:39 -04:00
|
|
|
return;
|
|
|
|
|
|
2007-03-18 18:50:16 -04:00
|
|
|
p1 = cur_ptr + val; /* first non-space char after 'Set-Cookie:' */
|
2007-03-18 11:22:39 -04:00
|
|
|
|
|
|
|
|
while (p1 < cur_end) { /* in fact, we'll break after the first cookie */
|
|
|
|
|
if (p1 == cur_end || *p1 == ';') /* end of cookie */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* p1 is at the beginning of the cookie name */
|
|
|
|
|
p2 = p1;
|
|
|
|
|
|
|
|
|
|
while (p2 < cur_end && *p2 != '=' && *p2 != ';')
|
|
|
|
|
p2++;
|
|
|
|
|
|
|
|
|
|
if (p2 == cur_end || *p2 == ';') /* next cookie */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
p3 = p2 + 1; /* skip the '=' sign */
|
|
|
|
|
if (p3 == cur_end)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
p4 = p3;
|
2007-06-17 15:51:38 -04:00
|
|
|
while (p4 < cur_end && !isspace((unsigned char)*p4) && *p4 != ';')
|
2007-03-18 11:22:39 -04:00
|
|
|
p4++;
|
|
|
|
|
|
|
|
|
|
/* here, we have the cookie name between p1 and p2,
|
|
|
|
|
* and its value between p3 and p4.
|
|
|
|
|
* we can process it.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* first, let's see if we want to capture it */
|
2008-10-17 06:01:58 -04:00
|
|
|
if (t->fe->capture_name != NULL &&
|
2007-03-18 12:31:28 -04:00
|
|
|
txn->srv_cookie == NULL &&
|
2008-10-17 06:01:58 -04:00
|
|
|
(p4 - p1 >= t->fe->capture_namelen) &&
|
|
|
|
|
memcmp(p1, t->fe->capture_name, t->fe->capture_namelen) == 0) {
|
2007-03-18 11:22:39 -04:00
|
|
|
int log_len = p4 - p1;
|
|
|
|
|
|
2007-05-13 15:45:51 -04:00
|
|
|
if ((txn->srv_cookie = pool_alloc2(pool2_capture)) == NULL) {
|
2007-03-18 11:22:39 -04:00
|
|
|
Alert("HTTP logging : out of memory.\n");
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-17 06:01:58 -04:00
|
|
|
if (log_len > t->fe->capture_len)
|
|
|
|
|
log_len = t->fe->capture_len;
|
2007-03-18 12:31:28 -04:00
|
|
|
memcpy(txn->srv_cookie, p1, log_len);
|
|
|
|
|
txn->srv_cookie[log_len] = 0;
|
2007-03-18 11:22:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* now check if we need to process it for persistence */
|
2007-03-31 18:01:37 -04:00
|
|
|
if ((p2 - p1 == t->be->cookie_len) && (t->be->cookie_name != NULL) &&
|
|
|
|
|
(memcmp(p1, t->be->cookie_name, p2 - p1) == 0)) {
|
2007-03-18 11:22:39 -04:00
|
|
|
/* Cool... it's the right one */
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_SCK_SEEN;
|
2007-03-18 11:22:39 -04:00
|
|
|
|
|
|
|
|
/* If the cookie is in insert mode on a known server, we'll delete
|
|
|
|
|
* this occurrence because we'll insert another one later.
|
|
|
|
|
* We'll delete it too if the "indirect" option is set and we're in
|
|
|
|
|
* a direct access. */
|
2007-03-31 18:01:37 -04:00
|
|
|
if (((t->srv) && (t->be->options & PR_O_COOK_INS)) ||
|
|
|
|
|
((t->flags & SN_DIRECT) && (t->be->options & PR_O_COOK_IND))) {
|
2007-03-18 11:22:39 -04:00
|
|
|
/* this header must be deleted */
|
|
|
|
|
delta = buffer_replace2(rtr, cur_ptr, cur_next, NULL, 0);
|
|
|
|
|
txn->hdr_idx.v[old_idx].next = cur_hdr->next;
|
|
|
|
|
txn->hdr_idx.used--;
|
|
|
|
|
cur_hdr->len = 0;
|
|
|
|
|
cur_next += delta;
|
|
|
|
|
txn->rsp.eoh += delta;
|
|
|
|
|
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_SCK_DELETED;
|
2007-03-18 11:22:39 -04:00
|
|
|
}
|
|
|
|
|
else if ((t->srv) && (t->srv->cookie) &&
|
2007-03-31 18:01:37 -04:00
|
|
|
(t->be->options & PR_O_COOK_RW)) {
|
2007-03-18 11:22:39 -04:00
|
|
|
/* replace bytes p3->p4 with the cookie name associated
|
|
|
|
|
* with this server since we know it.
|
|
|
|
|
*/
|
|
|
|
|
delta = buffer_replace2(rtr, p3, p4, t->srv->cookie, t->srv->cklen);
|
|
|
|
|
cur_hdr->len += delta;
|
|
|
|
|
cur_next += delta;
|
|
|
|
|
txn->rsp.eoh += delta;
|
|
|
|
|
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_SCK_INSERTED | TX_SCK_DELETED;
|
2007-03-18 11:22:39 -04:00
|
|
|
}
|
|
|
|
|
else if ((t->srv) && (t->srv->cookie) &&
|
2007-03-31 18:01:37 -04:00
|
|
|
(t->be->options & PR_O_COOK_PFX)) {
|
2007-03-18 11:22:39 -04:00
|
|
|
/* insert the cookie name associated with this server
|
|
|
|
|
* before existing cookie, and insert a delimitor between them..
|
|
|
|
|
*/
|
|
|
|
|
delta = buffer_replace2(rtr, p3, p3, t->srv->cookie, t->srv->cklen + 1);
|
|
|
|
|
cur_hdr->len += delta;
|
|
|
|
|
cur_next += delta;
|
|
|
|
|
txn->rsp.eoh += delta;
|
|
|
|
|
|
|
|
|
|
p3[t->srv->cklen] = COOKIE_DELIM;
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_SCK_INSERTED | TX_SCK_DELETED;
|
2007-03-18 11:22:39 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* next, let's see if the cookie is our appcookie */
|
2007-03-31 18:01:37 -04:00
|
|
|
else if ((t->be->appsession_name != NULL) &&
|
|
|
|
|
(memcmp(p1, t->be->appsession_name, p2 - p1) == 0)) {
|
2007-03-18 11:22:39 -04:00
|
|
|
|
|
|
|
|
/* Cool... it's the right one */
|
|
|
|
|
|
|
|
|
|
size_t server_id_len = strlen(t->srv->id) + 1;
|
|
|
|
|
asession_temp = &local_asession;
|
|
|
|
|
|
2007-05-13 15:29:55 -04:00
|
|
|
if ((asession_temp->sessid = pool_alloc2(apools.sessid)) == NULL) {
|
2007-03-18 11:22:39 -04:00
|
|
|
Alert("Not enough Memory process_srv():asession->sessid:malloc().\n");
|
|
|
|
|
send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-03-31 18:01:37 -04:00
|
|
|
memcpy(asession_temp->sessid, p3, t->be->appsession_len);
|
|
|
|
|
asession_temp->sessid[t->be->appsession_len] = 0;
|
2007-03-18 11:22:39 -04:00
|
|
|
asession_temp->serverid = NULL;
|
|
|
|
|
|
|
|
|
|
/* only do insert, if lookup fails */
|
2008-02-17 05:24:35 -05:00
|
|
|
asession_temp = appsession_hash_lookup(&(t->be->htbl_proxy), asession_temp->sessid);
|
|
|
|
|
if (asession_temp == NULL) {
|
2007-05-13 15:29:55 -04:00
|
|
|
if ((asession_temp = pool_alloc2(pool2_appsess)) == NULL) {
|
2007-03-18 11:22:39 -04:00
|
|
|
Alert("Not enough Memory process_srv():asession:calloc().\n");
|
|
|
|
|
send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession:calloc().\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
asession_temp->sessid = local_asession.sessid;
|
|
|
|
|
asession_temp->serverid = local_asession.serverid;
|
2008-08-13 13:57:02 -04:00
|
|
|
asession_temp->request_count = 0;
|
2007-09-09 15:56:53 -04:00
|
|
|
appsession_hash_insert(&(t->be->htbl_proxy), asession_temp);
|
|
|
|
|
} else {
|
2007-03-18 11:22:39 -04:00
|
|
|
/* free wasted memory */
|
2007-05-13 15:29:55 -04:00
|
|
|
pool_free2(apools.sessid, local_asession.sessid);
|
2007-09-09 15:56:53 -04:00
|
|
|
}
|
|
|
|
|
|
2007-03-18 11:22:39 -04:00
|
|
|
if (asession_temp->serverid == NULL) {
|
2007-05-13 15:29:55 -04:00
|
|
|
if ((asession_temp->serverid = pool_alloc2(apools.serverid)) == NULL) {
|
2007-03-18 11:22:39 -04:00
|
|
|
Alert("Not enough Memory process_srv():asession->sessid:malloc().\n");
|
|
|
|
|
send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
asession_temp->serverid[0] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (asession_temp->serverid[0] == '\0')
|
|
|
|
|
memcpy(asession_temp->serverid, t->srv->id, server_id_len);
|
|
|
|
|
|
2008-07-06 18:09:58 -04:00
|
|
|
asession_temp->expire = tick_add_ifset(now_ms, t->be->timeout.appsession);
|
2008-08-13 13:57:02 -04:00
|
|
|
asession_temp->request_count++;
|
2007-03-18 11:22:39 -04:00
|
|
|
#if defined(DEBUG_HASH)
|
2008-08-13 13:57:02 -04:00
|
|
|
Alert("manage_server_side_cookies\n");
|
2007-09-09 15:56:53 -04:00
|
|
|
appsession_hash_dump(&(t->be->htbl_proxy));
|
2007-03-18 11:22:39 -04:00
|
|
|
#endif
|
|
|
|
|
}/* end if ((t->proxy->appsession_name != NULL) ... */
|
|
|
|
|
break; /* we don't want to loop again since there cannot be another cookie on the same line */
|
|
|
|
|
} /* we're now at the end of the cookie value */
|
|
|
|
|
|
|
|
|
|
/* keep the link from this header to next one */
|
|
|
|
|
old_idx = cur_idx;
|
|
|
|
|
} /* end of cookie processing on this header */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check if response is cacheable or not. Updates t->flags.
|
|
|
|
|
*/
|
|
|
|
|
void check_response_for_cacheability(struct session *t, struct buffer *rtr)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = &t->txn;
|
|
|
|
|
char *p1, *p2;
|
|
|
|
|
|
|
|
|
|
char *cur_ptr, *cur_end, *cur_next;
|
|
|
|
|
int cur_idx;
|
|
|
|
|
|
2007-11-25 10:20:08 -05:00
|
|
|
if (!(txn->flags & TX_CACHEABLE))
|
2007-03-18 11:22:39 -04:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Iterate through the headers.
|
|
|
|
|
* we start with the start line.
|
|
|
|
|
*/
|
|
|
|
|
cur_idx = 0;
|
|
|
|
|
cur_next = rtr->data + txn->rsp.som + hdr_idx_first_pos(&txn->hdr_idx);
|
|
|
|
|
|
|
|
|
|
while ((cur_idx = txn->hdr_idx.v[cur_idx].next)) {
|
|
|
|
|
struct hdr_idx_elem *cur_hdr;
|
2007-03-18 18:50:16 -04:00
|
|
|
int val;
|
2007-03-18 11:22:39 -04:00
|
|
|
|
|
|
|
|
cur_hdr = &txn->hdr_idx.v[cur_idx];
|
|
|
|
|
cur_ptr = cur_next;
|
|
|
|
|
cur_end = cur_ptr + cur_hdr->len;
|
|
|
|
|
cur_next = cur_end + cur_hdr->cr + 1;
|
|
|
|
|
|
|
|
|
|
/* We have one full header between cur_ptr and cur_end, and the
|
|
|
|
|
* next header starts at cur_next. We're only interested in
|
|
|
|
|
* "Cookie:" headers.
|
|
|
|
|
*/
|
|
|
|
|
|
2007-03-18 18:50:16 -04:00
|
|
|
val = http_header_match2(cur_ptr, cur_end, "Pragma", 6);
|
|
|
|
|
if (val) {
|
|
|
|
|
if ((cur_end - (cur_ptr + val) >= 8) &&
|
|
|
|
|
strncasecmp(cur_ptr + val, "no-cache", 8) == 0) {
|
|
|
|
|
txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-03-18 11:22:39 -04:00
|
|
|
}
|
|
|
|
|
|
2007-03-18 18:50:16 -04:00
|
|
|
val = http_header_match2(cur_ptr, cur_end, "Cache-control", 13);
|
|
|
|
|
if (!val)
|
2007-03-18 11:22:39 -04:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* OK, right now we know we have a cache-control header at cur_ptr */
|
|
|
|
|
|
2007-03-18 18:50:16 -04:00
|
|
|
p1 = cur_ptr + val; /* first non-space char after 'cache-control:' */
|
2007-03-18 11:22:39 -04:00
|
|
|
|
|
|
|
|
if (p1 >= cur_end) /* no more info */
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* p1 is at the beginning of the value */
|
|
|
|
|
p2 = p1;
|
|
|
|
|
|
2007-06-17 15:51:38 -04:00
|
|
|
while (p2 < cur_end && *p2 != '=' && *p2 != ',' && !isspace((unsigned char)*p2))
|
2007-03-18 11:22:39 -04:00
|
|
|
p2++;
|
|
|
|
|
|
|
|
|
|
/* we have a complete value between p1 and p2 */
|
|
|
|
|
if (p2 < cur_end && *p2 == '=') {
|
|
|
|
|
/* we have something of the form no-cache="set-cookie" */
|
|
|
|
|
if ((cur_end - p1 >= 21) &&
|
|
|
|
|
strncasecmp(p1, "no-cache=\"set-cookie", 20) == 0
|
|
|
|
|
&& (p1[20] == '"' || p1[20] == ','))
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags &= ~TX_CACHE_COOK;
|
2007-03-18 11:22:39 -04:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* OK, so we know that either p2 points to the end of string or to a comma */
|
|
|
|
|
if (((p2 - p1 == 7) && strncasecmp(p1, "private", 7) == 0) ||
|
|
|
|
|
((p2 - p1 == 8) && strncasecmp(p1, "no-store", 8) == 0) ||
|
|
|
|
|
((p2 - p1 == 9) && strncasecmp(p1, "max-age=0", 9) == 0) ||
|
|
|
|
|
((p2 - p1 == 10) && strncasecmp(p1, "s-maxage=0", 10) == 0)) {
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
|
2007-03-18 11:22:39 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((p2 - p1 == 6) && strncasecmp(p1, "public", 6) == 0) {
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags |= TX_CACHEABLE | TX_CACHE_COOK;
|
2007-03-18 11:22:39 -04:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-12-03 20:26:12 -05:00
|
|
|
/*
|
|
|
|
|
* Try to retrieve a known appsession in the URI, then the associated server.
|
|
|
|
|
* If the server is found, it's assigned to the session.
|
|
|
|
|
*/
|
2007-01-21 13:16:41 -05:00
|
|
|
void get_srv_from_appsession(struct session *t, const char *begin, int len)
|
2006-12-03 20:26:12 -05:00
|
|
|
{
|
2007-03-18 13:34:41 -04:00
|
|
|
struct http_txn *txn = &t->txn;
|
2006-12-03 20:26:12 -05:00
|
|
|
appsess *asession_temp = NULL;
|
|
|
|
|
appsess local_asession;
|
|
|
|
|
char *request_line;
|
|
|
|
|
|
2007-03-31 18:01:37 -04:00
|
|
|
if (t->be->appsession_name == NULL ||
|
2007-03-03 07:54:32 -05:00
|
|
|
(t->txn.meth != HTTP_METH_GET && t->txn.meth != HTTP_METH_POST) ||
|
2007-01-21 13:16:41 -05:00
|
|
|
(request_line = memchr(begin, ';', len)) == NULL ||
|
2007-03-31 18:01:37 -04:00
|
|
|
((1 + t->be->appsession_name_len + 1 + t->be->appsession_len) > (begin + len - request_line)))
|
2006-12-03 20:26:12 -05:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* skip ';' */
|
|
|
|
|
request_line++;
|
|
|
|
|
|
|
|
|
|
/* look if we have a jsessionid */
|
2007-03-31 18:01:37 -04:00
|
|
|
if (strncasecmp(request_line, t->be->appsession_name, t->be->appsession_name_len) != 0)
|
2006-12-03 20:26:12 -05:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* skip jsessionid= */
|
2007-03-31 18:01:37 -04:00
|
|
|
request_line += t->be->appsession_name_len + 1;
|
2006-12-03 20:26:12 -05:00
|
|
|
|
|
|
|
|
/* First try if we already have an appsession */
|
|
|
|
|
asession_temp = &local_asession;
|
|
|
|
|
|
2007-05-13 15:29:55 -04:00
|
|
|
if ((asession_temp->sessid = pool_alloc2(apools.sessid)) == NULL) {
|
2006-12-03 20:26:12 -05:00
|
|
|
Alert("Not enough memory process_cli():asession_temp->sessid:calloc().\n");
|
|
|
|
|
send_log(t->be, LOG_ALERT, "Not enough Memory process_cli():asession_temp->sessid:calloc().\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy the sessionid */
|
2007-03-31 18:01:37 -04:00
|
|
|
memcpy(asession_temp->sessid, request_line, t->be->appsession_len);
|
|
|
|
|
asession_temp->sessid[t->be->appsession_len] = 0;
|
2006-12-03 20:26:12 -05:00
|
|
|
asession_temp->serverid = NULL;
|
|
|
|
|
|
|
|
|
|
/* only do insert, if lookup fails */
|
2008-02-17 05:24:35 -05:00
|
|
|
asession_temp = appsession_hash_lookup(&(t->be->htbl_proxy), asession_temp->sessid);
|
|
|
|
|
if (asession_temp == NULL) {
|
2007-05-13 15:29:55 -04:00
|
|
|
if ((asession_temp = pool_alloc2(pool2_appsess)) == NULL) {
|
2006-12-03 20:26:12 -05:00
|
|
|
/* free previously allocated memory */
|
2007-05-13 15:29:55 -04:00
|
|
|
pool_free2(apools.sessid, local_asession.sessid);
|
2006-12-03 20:26:12 -05:00
|
|
|
Alert("Not enough memory process_cli():asession:calloc().\n");
|
|
|
|
|
send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
asession_temp->sessid = local_asession.sessid;
|
|
|
|
|
asession_temp->serverid = local_asession.serverid;
|
2008-08-13 13:57:02 -04:00
|
|
|
asession_temp->request_count=0;
|
2007-09-09 15:56:53 -04:00
|
|
|
appsession_hash_insert(&(t->be->htbl_proxy), asession_temp);
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* free previously allocated memory */
|
2007-05-13 15:29:55 -04:00
|
|
|
pool_free2(apools.sessid, local_asession.sessid);
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
2007-09-09 15:56:53 -04:00
|
|
|
|
2008-07-06 18:09:58 -04:00
|
|
|
asession_temp->expire = tick_add_ifset(now_ms, t->be->timeout.appsession);
|
2006-12-03 20:26:12 -05:00
|
|
|
asession_temp->request_count++;
|
2007-09-09 15:56:53 -04:00
|
|
|
|
2006-12-03 20:26:12 -05:00
|
|
|
#if defined(DEBUG_HASH)
|
2008-08-13 13:57:02 -04:00
|
|
|
Alert("get_srv_from_appsession\n");
|
2007-09-09 15:56:53 -04:00
|
|
|
appsession_hash_dump(&(t->be->htbl_proxy));
|
2006-12-03 20:26:12 -05:00
|
|
|
#endif
|
|
|
|
|
if (asession_temp->serverid == NULL) {
|
2008-08-13 13:57:02 -04:00
|
|
|
/* TODO redispatch request */
|
2006-12-03 20:26:12 -05:00
|
|
|
Alert("Found Application Session without matching server.\n");
|
|
|
|
|
} else {
|
2007-03-31 18:01:37 -04:00
|
|
|
struct server *srv = t->be->srv;
|
2006-12-03 20:26:12 -05:00
|
|
|
while (srv) {
|
|
|
|
|
if (strcmp(srv->id, asession_temp->serverid) == 0) {
|
2007-03-31 18:01:37 -04:00
|
|
|
if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
|
2006-12-03 20:26:12 -05:00
|
|
|
/* we found the server and it's usable */
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags &= ~TX_CK_MASK;
|
|
|
|
|
txn->flags |= TX_CK_VALID;
|
|
|
|
|
t->flags |= SN_DIRECT | SN_ASSIGNED;
|
2006-12-03 20:26:12 -05:00
|
|
|
t->srv = srv;
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
2007-03-18 13:34:41 -04:00
|
|
|
txn->flags &= ~TX_CK_MASK;
|
|
|
|
|
txn->flags |= TX_CK_DOWN;
|
2006-12-03 20:26:12 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
srv = srv->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-12-17 08:52:38 -05:00
|
|
|
/*
|
2007-01-07 07:47:30 -05:00
|
|
|
* In a GET or HEAD request, check if the requested URI matches the stats uri
|
|
|
|
|
* for the current backend, and if an authorization has been passed and is valid.
|
2006-12-17 08:52:38 -05:00
|
|
|
*
|
2007-01-07 07:47:30 -05:00
|
|
|
* It is assumed that the request is either a HEAD or GET and that the
|
2007-03-31 18:01:37 -04:00
|
|
|
* t->be->uri_auth field is valid. An HTTP/401 response may be sent, or
|
2009-10-04 09:56:38 -04:00
|
|
|
* the stats I/O handler will be registered to start sending data.
|
2006-12-17 08:52:38 -05:00
|
|
|
*
|
|
|
|
|
* Returns 1 if the session's state changes, otherwise 0.
|
|
|
|
|
*/
|
|
|
|
|
int stats_check_uri_auth(struct session *t, struct proxy *backend)
|
|
|
|
|
{
|
2007-03-03 10:23:22 -05:00
|
|
|
struct http_txn *txn = &t->txn;
|
2006-12-17 08:52:38 -05:00
|
|
|
struct uri_auth *uri_auth = backend->uri_auth;
|
|
|
|
|
struct user_auth *user;
|
|
|
|
|
int authenticated, cur_idx;
|
2007-01-21 13:16:41 -05:00
|
|
|
char *h;
|
2007-01-07 07:47:30 -05:00
|
|
|
|
[MEDIUM] fix stats socket limitation to 16 kB
Due to the way the stats socket work, it was not possible to
maintain the information related to the command entered, so
after filling a whole buffer, the request was lost and it was
considered that there was nothing to write anymore.
The major reason was that some flags were passed directly
during the first call to stats_dump_raw() instead of being
stored persistently in the session.
To definitely fix this problem, flags were added to the stats
member of the session structure.
A second problem appeared. When the stats were produced, a first
call to client_retnclose() was performed, then one or multiple
subsequent calls to buffer_write_chunks() were done. But once the
stats buffer was full and a reschedule operated, the buffer was
flushed, the write flag cleared from the buffer and nothing was
done to re-arm it.
For this reason, a check was added in the proto_uxst_stats()
function in order to re-call the client FSM when data were added
by stats_dump_raw(). Finally, the whole unix stats dump FSM was
rewritten to avoid all the magics it depended on. It is now
simpler and looks more like the HTTP one.
2008-03-17 16:38:24 -04:00
|
|
|
memset(&t->data_ctx.stats, 0, sizeof(t->data_ctx.stats));
|
|
|
|
|
|
2007-01-21 13:16:41 -05:00
|
|
|
/* check URI size */
|
2007-03-03 10:23:22 -05:00
|
|
|
if (uri_auth->uri_len > txn->req.sl.rq.u_l)
|
2006-12-17 08:52:38 -05:00
|
|
|
return 0;
|
|
|
|
|
|
2007-03-03 10:23:22 -05:00
|
|
|
h = t->req->data + txn->req.sl.rq.u;
|
2007-01-21 13:16:41 -05:00
|
|
|
|
2007-01-07 07:47:30 -05:00
|
|
|
/* the URI is in h */
|
|
|
|
|
if (memcmp(h, uri_auth->uri_prefix, uri_auth->uri_len) != 0)
|
2006-12-17 08:52:38 -05:00
|
|
|
return 0;
|
|
|
|
|
|
2007-07-25 08:43:32 -04:00
|
|
|
h += uri_auth->uri_len;
|
|
|
|
|
while (h <= t->req->data + txn->req.sl.rq.u + txn->req.sl.rq.u_l - 3) {
|
|
|
|
|
if (memcmp(h, ";up", 3) == 0) {
|
[MEDIUM] fix stats socket limitation to 16 kB
Due to the way the stats socket work, it was not possible to
maintain the information related to the command entered, so
after filling a whole buffer, the request was lost and it was
considered that there was nothing to write anymore.
The major reason was that some flags were passed directly
during the first call to stats_dump_raw() instead of being
stored persistently in the session.
To definitely fix this problem, flags were added to the stats
member of the session structure.
A second problem appeared. When the stats were produced, a first
call to client_retnclose() was performed, then one or multiple
subsequent calls to buffer_write_chunks() were done. But once the
stats buffer was full and a reschedule operated, the buffer was
flushed, the write flag cleared from the buffer and nothing was
done to re-arm it.
For this reason, a check was added in the proto_uxst_stats()
function in order to re-call the client FSM when data were added
by stats_dump_raw(). Finally, the whole unix stats dump FSM was
rewritten to avoid all the magics it depended on. It is now
simpler and looks more like the HTTP one.
2008-03-17 16:38:24 -04:00
|
|
|
t->data_ctx.stats.flags |= STAT_HIDE_DOWN;
|
2007-07-25 08:43:32 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
h++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (uri_auth->refresh) {
|
|
|
|
|
h = t->req->data + txn->req.sl.rq.u + uri_auth->uri_len;
|
|
|
|
|
while (h <= t->req->data + txn->req.sl.rq.u + txn->req.sl.rq.u_l - 10) {
|
|
|
|
|
if (memcmp(h, ";norefresh", 10) == 0) {
|
[MEDIUM] fix stats socket limitation to 16 kB
Due to the way the stats socket work, it was not possible to
maintain the information related to the command entered, so
after filling a whole buffer, the request was lost and it was
considered that there was nothing to write anymore.
The major reason was that some flags were passed directly
during the first call to stats_dump_raw() instead of being
stored persistently in the session.
To definitely fix this problem, flags were added to the stats
member of the session structure.
A second problem appeared. When the stats were produced, a first
call to client_retnclose() was performed, then one or multiple
subsequent calls to buffer_write_chunks() were done. But once the
stats buffer was full and a reschedule operated, the buffer was
flushed, the write flag cleared from the buffer and nothing was
done to re-arm it.
For this reason, a check was added in the proto_uxst_stats()
function in order to re-call the client FSM when data were added
by stats_dump_raw(). Finally, the whole unix stats dump FSM was
rewritten to avoid all the magics it depended on. It is now
simpler and looks more like the HTTP one.
2008-03-17 16:38:24 -04:00
|
|
|
t->data_ctx.stats.flags |= STAT_NO_REFRESH;
|
2007-07-25 08:43:32 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
h++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-17 12:44:57 -04:00
|
|
|
h = t->req->data + txn->req.sl.rq.u + uri_auth->uri_len;
|
|
|
|
|
while (h <= t->req->data + txn->req.sl.rq.u + txn->req.sl.rq.u_l - 4) {
|
|
|
|
|
if (memcmp(h, ";csv", 4) == 0) {
|
[MEDIUM] fix stats socket limitation to 16 kB
Due to the way the stats socket work, it was not possible to
maintain the information related to the command entered, so
after filling a whole buffer, the request was lost and it was
considered that there was nothing to write anymore.
The major reason was that some flags were passed directly
during the first call to stats_dump_raw() instead of being
stored persistently in the session.
To definitely fix this problem, flags were added to the stats
member of the session structure.
A second problem appeared. When the stats were produced, a first
call to client_retnclose() was performed, then one or multiple
subsequent calls to buffer_write_chunks() were done. But once the
stats buffer was full and a reschedule operated, the buffer was
flushed, the write flag cleared from the buffer and nothing was
done to re-arm it.
For this reason, a check was added in the proto_uxst_stats()
function in order to re-call the client FSM when data were added
by stats_dump_raw(). Finally, the whole unix stats dump FSM was
rewritten to avoid all the magics it depended on. It is now
simpler and looks more like the HTTP one.
2008-03-17 16:38:24 -04:00
|
|
|
t->data_ctx.stats.flags |= STAT_FMT_CSV;
|
2007-10-17 12:44:57 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
h++;
|
|
|
|
|
}
|
|
|
|
|
|
[MEDIUM] fix stats socket limitation to 16 kB
Due to the way the stats socket work, it was not possible to
maintain the information related to the command entered, so
after filling a whole buffer, the request was lost and it was
considered that there was nothing to write anymore.
The major reason was that some flags were passed directly
during the first call to stats_dump_raw() instead of being
stored persistently in the session.
To definitely fix this problem, flags were added to the stats
member of the session structure.
A second problem appeared. When the stats were produced, a first
call to client_retnclose() was performed, then one or multiple
subsequent calls to buffer_write_chunks() were done. But once the
stats buffer was full and a reschedule operated, the buffer was
flushed, the write flag cleared from the buffer and nothing was
done to re-arm it.
For this reason, a check was added in the proto_uxst_stats()
function in order to re-call the client FSM when data were added
by stats_dump_raw(). Finally, the whole unix stats dump FSM was
rewritten to avoid all the magics it depended on. It is now
simpler and looks more like the HTTP one.
2008-03-17 16:38:24 -04:00
|
|
|
t->data_ctx.stats.flags |= STAT_SHOW_STAT | STAT_SHOW_INFO;
|
|
|
|
|
|
2006-12-17 08:52:38 -05:00
|
|
|
/* we are in front of a interceptable URI. Let's check
|
|
|
|
|
* if there's an authentication and if it's valid.
|
|
|
|
|
*/
|
|
|
|
|
user = uri_auth->users;
|
|
|
|
|
if (!user) {
|
|
|
|
|
/* no user auth required, it's OK */
|
|
|
|
|
authenticated = 1;
|
|
|
|
|
} else {
|
|
|
|
|
authenticated = 0;
|
|
|
|
|
|
|
|
|
|
/* a user list is defined, we have to check.
|
|
|
|
|
* skip 21 chars for "Authorization: Basic ".
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* FIXME: this should move to an earlier place */
|
|
|
|
|
cur_idx = 0;
|
2007-03-03 10:23:22 -05:00
|
|
|
h = t->req->data + txn->req.som + hdr_idx_first_pos(&txn->hdr_idx);
|
|
|
|
|
while ((cur_idx = txn->hdr_idx.v[cur_idx].next)) {
|
|
|
|
|
int len = txn->hdr_idx.v[cur_idx].len;
|
2006-12-17 08:52:38 -05:00
|
|
|
if (len > 14 &&
|
|
|
|
|
!strncasecmp("Authorization:", h, 14)) {
|
2007-03-03 10:23:22 -05:00
|
|
|
txn->auth_hdr.str = h;
|
|
|
|
|
txn->auth_hdr.len = len;
|
2006-12-17 08:52:38 -05:00
|
|
|
break;
|
|
|
|
|
}
|
2007-03-03 10:23:22 -05:00
|
|
|
h += len + txn->hdr_idx.v[cur_idx].cr + 1;
|
2006-12-17 08:52:38 -05:00
|
|
|
}
|
|
|
|
|
|
2007-03-03 10:23:22 -05:00
|
|
|
if (txn->auth_hdr.len < 21 ||
|
|
|
|
|
memcmp(txn->auth_hdr.str + 14, " Basic ", 7))
|
2006-12-17 08:52:38 -05:00
|
|
|
user = NULL;
|
|
|
|
|
|
|
|
|
|
while (user) {
|
2007-03-03 10:23:22 -05:00
|
|
|
if ((txn->auth_hdr.len == user->user_len + 14 + 7)
|
|
|
|
|
&& !memcmp(txn->auth_hdr.str + 14 + 7,
|
2006-12-17 08:52:38 -05:00
|
|
|
user->user_pwd, user->user_len)) {
|
|
|
|
|
authenticated = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
user = user->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!authenticated) {
|
2006-12-23 14:51:41 -05:00
|
|
|
struct chunk msg;
|
2006-12-17 08:52:38 -05:00
|
|
|
|
|
|
|
|
/* no need to go further */
|
2009-09-27 07:23:20 -04:00
|
|
|
sprintf(trash, HTTP_401_fmt, uri_auth->auth_realm);
|
|
|
|
|
chunk_initlen(&msg, trash, sizeof(trash), strlen(trash));
|
2007-03-18 12:31:28 -04:00
|
|
|
txn->status = 401;
|
2008-11-30 13:48:07 -05:00
|
|
|
stream_int_retnclose(t->req->prod, &msg);
|
2008-08-17 09:20:19 -04:00
|
|
|
t->req->analysers = 0;
|
2006-12-17 08:52:38 -05:00
|
|
|
if (!(t->flags & SN_ERR_MASK))
|
|
|
|
|
t->flags |= SN_ERR_PRXCOND;
|
|
|
|
|
if (!(t->flags & SN_FINST_MASK))
|
|
|
|
|
t->flags |= SN_FINST_R;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
[MEDIUM] fix stats socket limitation to 16 kB
Due to the way the stats socket work, it was not possible to
maintain the information related to the command entered, so
after filling a whole buffer, the request was lost and it was
considered that there was nothing to write anymore.
The major reason was that some flags were passed directly
during the first call to stats_dump_raw() instead of being
stored persistently in the session.
To definitely fix this problem, flags were added to the stats
member of the session structure.
A second problem appeared. When the stats were produced, a first
call to client_retnclose() was performed, then one or multiple
subsequent calls to buffer_write_chunks() were done. But once the
stats buffer was full and a reschedule operated, the buffer was
flushed, the write flag cleared from the buffer and nothing was
done to re-arm it.
For this reason, a check was added in the proto_uxst_stats()
function in order to re-call the client FSM when data were added
by stats_dump_raw(). Finally, the whole unix stats dump FSM was
rewritten to avoid all the magics it depended on. It is now
simpler and looks more like the HTTP one.
2008-03-17 16:38:24 -04:00
|
|
|
/* The request is valid, the user is authenticated. Let's start sending
|
2006-12-17 08:52:38 -05:00
|
|
|
* data.
|
|
|
|
|
*/
|
2008-06-13 15:12:51 -04:00
|
|
|
t->logs.tv_request = now;
|
2006-12-17 08:52:38 -05:00
|
|
|
t->data_source = DATA_SRC_STATS;
|
|
|
|
|
t->data_state = DATA_ST_INIT;
|
2008-06-30 01:51:00 -04:00
|
|
|
t->task->nice = -32; /* small boost for HTTP statistics */
|
2009-10-04 09:56:38 -04:00
|
|
|
stream_int_register_handler(t->rep->prod, http_stats_io_handler);
|
|
|
|
|
t->rep->prod->private = t;
|
|
|
|
|
t->rep->prod->st0 = t->rep->prod->st1 = 0;
|
2006-12-17 08:52:38 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-02 09:18:36 -04:00
|
|
|
/*
|
|
|
|
|
* Capture a bad request or response and archive it in the proxy's structure.
|
|
|
|
|
*/
|
|
|
|
|
void http_capture_bad_message(struct error_snapshot *es, struct session *s,
|
|
|
|
|
struct buffer *buf, struct http_msg *msg,
|
|
|
|
|
struct proxy *other_end)
|
|
|
|
|
{
|
2009-05-01 05:33:17 -04:00
|
|
|
es->len = buf->r - (buf->data + msg->som);
|
|
|
|
|
memcpy(es->buf, buf->data + msg->som, MIN(es->len, sizeof(es->buf)));
|
2009-04-02 09:18:36 -04:00
|
|
|
if (msg->err_pos >= 0)
|
2009-05-01 05:33:17 -04:00
|
|
|
es->pos = msg->err_pos - msg->som;
|
2009-04-02 09:18:36 -04:00
|
|
|
else
|
2009-05-01 05:33:17 -04:00
|
|
|
es->pos = buf->lr - (buf->data + msg->som);
|
2009-04-02 09:18:36 -04:00
|
|
|
es->when = date; // user-visible date
|
|
|
|
|
es->sid = s->uniq_id;
|
|
|
|
|
es->srv = s->srv;
|
|
|
|
|
es->oe = other_end;
|
|
|
|
|
es->src = s->cli_addr;
|
|
|
|
|
}
|
2006-12-17 08:52:38 -05:00
|
|
|
|
2006-12-03 20:26:12 -05:00
|
|
|
/*
|
|
|
|
|
* Print a debug line with a header
|
|
|
|
|
*/
|
|
|
|
|
void debug_hdr(const char *dir, struct session *t, const char *start, const char *end)
|
|
|
|
|
{
|
|
|
|
|
int len, max;
|
|
|
|
|
len = sprintf(trash, "%08x:%s.%s[%04x:%04x]: ", t->uniq_id, t->be->id,
|
2008-08-28 02:54:27 -04:00
|
|
|
dir, (unsigned short)t->req->prod->fd, (unsigned short)t->req->cons->fd);
|
2006-12-03 20:26:12 -05:00
|
|
|
max = end - start;
|
|
|
|
|
UBOUND(max, sizeof(trash) - len - 1);
|
|
|
|
|
len += strlcpy2(trash + len, start, max + 1);
|
|
|
|
|
trash[len++] = '\n';
|
|
|
|
|
write(1, trash, len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-05-06 18:55:35 -04:00
|
|
|
/************************************************************************/
|
|
|
|
|
/* The code below is dedicated to ACL parsing and matching */
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 1. Check on METHOD
|
|
|
|
|
* We use the pre-parsed method if it is known, and store its number as an
|
|
|
|
|
* integer. If it is unknown, we use the pointer and the length.
|
|
|
|
|
*/
|
2007-06-09 17:10:04 -04:00
|
|
|
static int acl_parse_meth(const char **text, struct acl_pattern *pattern, int *opaque)
|
2007-05-06 18:55:35 -04:00
|
|
|
{
|
|
|
|
|
int len, meth;
|
|
|
|
|
|
2007-06-09 17:10:04 -04:00
|
|
|
len = strlen(*text);
|
|
|
|
|
meth = find_http_meth(*text, len);
|
2007-05-06 18:55:35 -04:00
|
|
|
|
|
|
|
|
pattern->val.i = meth;
|
|
|
|
|
if (meth == HTTP_METH_OTHER) {
|
2007-06-09 17:10:04 -04:00
|
|
|
pattern->ptr.str = strdup(*text);
|
2007-05-06 18:55:35 -04:00
|
|
|
if (!pattern->ptr.str)
|
|
|
|
|
return 0;
|
|
|
|
|
pattern->len = len;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-10 04:06:18 -04:00
|
|
|
static int
|
2007-06-10 05:47:14 -04:00
|
|
|
acl_fetch_meth(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
2007-05-06 18:55:35 -04:00
|
|
|
{
|
|
|
|
|
int meth;
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->req.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-05-06 18:55:35 -04:00
|
|
|
meth = txn->meth;
|
|
|
|
|
test->i = meth;
|
|
|
|
|
if (meth == HTTP_METH_OTHER) {
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
|
|
|
|
|
/* ensure the indexes are not affected */
|
|
|
|
|
return 0;
|
2007-05-06 18:55:35 -04:00
|
|
|
test->len = txn->req.sl.rq.m_l;
|
|
|
|
|
test->ptr = txn->req.sol;
|
|
|
|
|
}
|
|
|
|
|
test->flags = ACL_TEST_F_READ_ONLY | ACL_TEST_F_VOL_1ST;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int acl_match_meth(struct acl_test *test, struct acl_pattern *pattern)
|
|
|
|
|
{
|
2007-06-17 02:20:33 -04:00
|
|
|
int icase;
|
|
|
|
|
|
2007-05-06 18:55:35 -04:00
|
|
|
if (test->i != pattern->val.i)
|
2008-07-09 10:18:21 -04:00
|
|
|
return ACL_PAT_FAIL;
|
2007-05-06 18:55:35 -04:00
|
|
|
|
|
|
|
|
if (test->i != HTTP_METH_OTHER)
|
2008-07-09 10:18:21 -04:00
|
|
|
return ACL_PAT_PASS;
|
2007-05-06 18:55:35 -04:00
|
|
|
|
|
|
|
|
/* Other method, we must compare the strings */
|
|
|
|
|
if (pattern->len != test->len)
|
2008-07-09 10:18:21 -04:00
|
|
|
return ACL_PAT_FAIL;
|
2007-06-17 02:20:33 -04:00
|
|
|
|
|
|
|
|
icase = pattern->flags & ACL_PAT_F_IGNORE_CASE;
|
|
|
|
|
if ((icase && strncasecmp(pattern->ptr.str, test->ptr, test->len) != 0) ||
|
|
|
|
|
(!icase && strncmp(pattern->ptr.str, test->ptr, test->len) != 0))
|
2008-07-09 10:18:21 -04:00
|
|
|
return ACL_PAT_FAIL;
|
|
|
|
|
return ACL_PAT_PASS;
|
2007-05-06 18:55:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 2. Check on Request/Status Version
|
|
|
|
|
* We simply compare strings here.
|
|
|
|
|
*/
|
2007-06-09 17:10:04 -04:00
|
|
|
static int acl_parse_ver(const char **text, struct acl_pattern *pattern, int *opaque)
|
2007-05-06 18:55:35 -04:00
|
|
|
{
|
2007-06-09 17:10:04 -04:00
|
|
|
pattern->ptr.str = strdup(*text);
|
2007-05-06 18:55:35 -04:00
|
|
|
if (!pattern->ptr.str)
|
|
|
|
|
return 0;
|
2007-06-09 17:10:04 -04:00
|
|
|
pattern->len = strlen(*text);
|
2007-05-06 18:55:35 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-10 04:06:18 -04:00
|
|
|
static int
|
2007-06-10 05:47:14 -04:00
|
|
|
acl_fetch_rqver(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
2007-05-06 18:55:35 -04:00
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
char *ptr;
|
|
|
|
|
int len;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->req.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-05-06 18:55:35 -04:00
|
|
|
len = txn->req.sl.rq.v_l;
|
|
|
|
|
ptr = txn->req.sol + txn->req.sl.rq.v - txn->req.som;
|
|
|
|
|
|
|
|
|
|
while ((len-- > 0) && (*ptr++ != '/'));
|
|
|
|
|
if (len <= 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
test->ptr = ptr;
|
|
|
|
|
test->len = len;
|
|
|
|
|
|
|
|
|
|
test->flags = ACL_TEST_F_READ_ONLY | ACL_TEST_F_VOL_1ST;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-10 04:06:18 -04:00
|
|
|
static int
|
2007-06-10 05:47:14 -04:00
|
|
|
acl_fetch_stver(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
2007-05-06 18:55:35 -04:00
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
char *ptr;
|
|
|
|
|
int len;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-05-06 18:55:35 -04:00
|
|
|
len = txn->rsp.sl.st.v_l;
|
|
|
|
|
ptr = txn->rsp.sol;
|
|
|
|
|
|
|
|
|
|
while ((len-- > 0) && (*ptr++ != '/'));
|
|
|
|
|
if (len <= 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
test->ptr = ptr;
|
|
|
|
|
test->len = len;
|
|
|
|
|
|
|
|
|
|
test->flags = ACL_TEST_F_READ_ONLY | ACL_TEST_F_VOL_1ST;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 3. Check on Status Code. We manipulate integers here. */
|
2007-06-10 04:06:18 -04:00
|
|
|
static int
|
2007-06-10 05:47:14 -04:00
|
|
|
acl_fetch_stcode(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
2007-05-06 18:55:35 -04:00
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
char *ptr;
|
|
|
|
|
int len;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-05-06 18:55:35 -04:00
|
|
|
len = txn->rsp.sl.st.c_l;
|
|
|
|
|
ptr = txn->rsp.sol + txn->rsp.sl.st.c - txn->rsp.som;
|
|
|
|
|
|
|
|
|
|
test->i = __strl2ui(ptr, len);
|
|
|
|
|
test->flags = ACL_TEST_F_VOL_1ST;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 4. Check on URL/URI. A pointer to the URI is stored. */
|
2007-06-10 04:06:18 -04:00
|
|
|
static int
|
2007-06-10 05:47:14 -04:00
|
|
|
acl_fetch_url(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
2007-05-06 18:55:35 -04:00
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->req.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
|
|
|
|
|
/* ensure the indexes are not affected */
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-05-06 18:55:35 -04:00
|
|
|
test->len = txn->req.sl.rq.u_l;
|
|
|
|
|
test->ptr = txn->req.sol + txn->req.sl.rq.u;
|
|
|
|
|
|
2007-05-08 16:45:09 -04:00
|
|
|
/* we do not need to set READ_ONLY because the data is in a buffer */
|
|
|
|
|
test->flags = ACL_TEST_F_VOL_1ST;
|
2007-05-06 18:55:35 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-29 09:43:32 -05:00
|
|
|
static int
|
|
|
|
|
acl_fetch_url_ip(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-11-29 09:43:32 -05:00
|
|
|
if (txn->req.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
|
2007-11-29 09:43:32 -05:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
|
|
|
|
|
/* ensure the indexes are not affected */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Parse HTTP request */
|
|
|
|
|
url2sa(txn->req.sol + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &l4->srv_addr);
|
|
|
|
|
test->ptr = (void *)&((struct sockaddr_in *)&l4->srv_addr)->sin_addr;
|
|
|
|
|
test->i = AF_INET;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If we are parsing url in frontend space, we prepare backend stage
|
|
|
|
|
* to not parse again the same url ! optimization lazyness...
|
|
|
|
|
*/
|
|
|
|
|
if (px->options & PR_O_HTTP_PROXY)
|
|
|
|
|
l4->flags |= SN_ADDR_SET;
|
|
|
|
|
|
|
|
|
|
test->flags = ACL_TEST_F_READ_ONLY;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
acl_fetch_url_port(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-11-29 09:43:32 -05:00
|
|
|
if (txn->req.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
|
2007-11-29 09:43:32 -05:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
|
|
|
|
|
/* ensure the indexes are not affected */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Same optimization as url_ip */
|
|
|
|
|
url2sa(txn->req.sol + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &l4->srv_addr);
|
|
|
|
|
test->i = ntohs(((struct sockaddr_in *)&l4->srv_addr)->sin_port);
|
|
|
|
|
|
|
|
|
|
if (px->options & PR_O_HTTP_PROXY)
|
|
|
|
|
l4->flags |= SN_ADDR_SET;
|
|
|
|
|
|
|
|
|
|
test->flags = ACL_TEST_F_READ_ONLY;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
/* 5. Check on HTTP header. A pointer to the beginning of the value is returned.
|
|
|
|
|
* This generic function is used by both acl_fetch_chdr() and acl_fetch_shdr().
|
|
|
|
|
*/
|
2007-06-10 13:45:56 -04:00
|
|
|
static int
|
2007-06-17 10:58:38 -04:00
|
|
|
acl_fetch_hdr(struct proxy *px, struct session *l4, void *l7, char *sol,
|
2007-06-10 13:45:56 -04:00
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
struct hdr_idx *idx = &txn->hdr_idx;
|
|
|
|
|
struct hdr_ctx *ctx = (struct hdr_ctx *)test->ctx.a;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-10 13:45:56 -04:00
|
|
|
if (!(test->flags & ACL_TEST_F_FETCH_MORE))
|
|
|
|
|
/* search for header from the beginning */
|
|
|
|
|
ctx->idx = 0;
|
|
|
|
|
|
|
|
|
|
if (http_find_header2(expr->arg.str, expr->arg_len, sol, idx, ctx)) {
|
|
|
|
|
test->flags |= ACL_TEST_F_FETCH_MORE;
|
|
|
|
|
test->flags |= ACL_TEST_F_VOL_HDR;
|
|
|
|
|
test->len = ctx->vlen;
|
|
|
|
|
test->ptr = (char *)ctx->line + ctx->val;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test->flags &= ~ACL_TEST_F_FETCH_MORE;
|
|
|
|
|
test->flags |= ACL_TEST_F_VOL_HDR;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2007-06-17 10:58:38 -04:00
|
|
|
acl_fetch_chdr(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->req.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
|
|
|
|
|
/* ensure the indexes are not affected */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return acl_fetch_hdr(px, l4, txn, txn->req.sol, expr, test);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
acl_fetch_shdr(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return acl_fetch_hdr(px, l4, txn, txn->rsp.sol, expr, test);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 6. Check on HTTP header count. The number of occurrences is returned.
|
|
|
|
|
* This generic function is used by both acl_fetch_chdr* and acl_fetch_shdr*.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
acl_fetch_hdr_cnt(struct proxy *px, struct session *l4, void *l7, char *sol,
|
2007-06-10 13:45:56 -04:00
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
struct hdr_idx *idx = &txn->hdr_idx;
|
|
|
|
|
struct hdr_ctx ctx;
|
|
|
|
|
int cnt;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-10 13:45:56 -04:00
|
|
|
ctx.idx = 0;
|
|
|
|
|
cnt = 0;
|
|
|
|
|
while (http_find_header2(expr->arg.str, expr->arg_len, sol, idx, &ctx))
|
|
|
|
|
cnt++;
|
|
|
|
|
|
|
|
|
|
test->i = cnt;
|
|
|
|
|
test->flags = ACL_TEST_F_VOL_HDR;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
static int
|
|
|
|
|
acl_fetch_chdr_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->req.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
|
|
|
|
|
/* ensure the indexes are not affected */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return acl_fetch_hdr_cnt(px, l4, txn, txn->req.sol, expr, test);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
acl_fetch_shdr_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return acl_fetch_hdr_cnt(px, l4, txn, txn->rsp.sol, expr, test);
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-10 13:45:56 -04:00
|
|
|
/* 7. Check on HTTP header's integer value. The integer value is returned.
|
|
|
|
|
* FIXME: the type is 'int', it may not be appropriate for everything.
|
2007-06-17 10:58:38 -04:00
|
|
|
* This generic function is used by both acl_fetch_chdr* and acl_fetch_shdr*.
|
2007-06-10 13:45:56 -04:00
|
|
|
*/
|
|
|
|
|
static int
|
2007-06-17 10:58:38 -04:00
|
|
|
acl_fetch_hdr_val(struct proxy *px, struct session *l4, void *l7, char *sol,
|
2007-06-10 13:45:56 -04:00
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
struct hdr_idx *idx = &txn->hdr_idx;
|
|
|
|
|
struct hdr_ctx *ctx = (struct hdr_ctx *)test->ctx.a;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-10 13:45:56 -04:00
|
|
|
if (!(test->flags & ACL_TEST_F_FETCH_MORE))
|
|
|
|
|
/* search for header from the beginning */
|
|
|
|
|
ctx->idx = 0;
|
|
|
|
|
|
|
|
|
|
if (http_find_header2(expr->arg.str, expr->arg_len, sol, idx, ctx)) {
|
|
|
|
|
test->flags |= ACL_TEST_F_FETCH_MORE;
|
|
|
|
|
test->flags |= ACL_TEST_F_VOL_HDR;
|
|
|
|
|
test->i = strl2ic((char *)ctx->line + ctx->val, ctx->vlen);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test->flags &= ~ACL_TEST_F_FETCH_MORE;
|
|
|
|
|
test->flags |= ACL_TEST_F_VOL_HDR;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
static int
|
|
|
|
|
acl_fetch_chdr_val(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->req.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
|
|
|
|
|
/* ensure the indexes are not affected */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return acl_fetch_hdr_val(px, l4, txn, txn->req.sol, expr, test);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
acl_fetch_shdr_val(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return acl_fetch_hdr_val(px, l4, txn, txn->rsp.sol, expr, test);
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-19 01:54:16 -04:00
|
|
|
/* 7. Check on HTTP header's IPv4 address value. The IPv4 address is returned.
|
|
|
|
|
* This generic function is used by both acl_fetch_chdr* and acl_fetch_shdr*.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
acl_fetch_hdr_ip(struct proxy *px, struct session *l4, void *l7, char *sol,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
struct hdr_idx *idx = &txn->hdr_idx;
|
|
|
|
|
struct hdr_ctx *ctx = (struct hdr_ctx *)test->ctx.a;
|
|
|
|
|
|
|
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (!(test->flags & ACL_TEST_F_FETCH_MORE))
|
|
|
|
|
/* search for header from the beginning */
|
|
|
|
|
ctx->idx = 0;
|
|
|
|
|
|
|
|
|
|
if (http_find_header2(expr->arg.str, expr->arg_len, sol, idx, ctx)) {
|
|
|
|
|
test->flags |= ACL_TEST_F_FETCH_MORE;
|
|
|
|
|
test->flags |= ACL_TEST_F_VOL_HDR;
|
|
|
|
|
/* Same optimization as url_ip */
|
|
|
|
|
memset(&l4->srv_addr.sin_addr, 0, sizeof(l4->srv_addr.sin_addr));
|
|
|
|
|
url2ip((char *)ctx->line + ctx->val, &l4->srv_addr.sin_addr);
|
|
|
|
|
test->ptr = (void *)&l4->srv_addr.sin_addr;
|
|
|
|
|
test->i = AF_INET;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test->flags &= ~ACL_TEST_F_FETCH_MORE;
|
|
|
|
|
test->flags |= ACL_TEST_F_VOL_HDR;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
acl_fetch_chdr_ip(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
|
|
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (txn->req.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
|
|
|
|
|
/* ensure the indexes are not affected */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return acl_fetch_hdr_ip(px, l4, txn, txn->req.sol, expr, test);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
acl_fetch_shdr_ip(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
|
|
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return acl_fetch_hdr_ip(px, l4, txn, txn->rsp.sol, expr, test);
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-10 15:28:46 -04:00
|
|
|
/* 8. Check on URI PATH. A pointer to the PATH is stored. The path starts at
|
|
|
|
|
* the first '/' after the possible hostname, and ends before the possible '?'.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
acl_fetch_path(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct http_txn *txn = l7;
|
|
|
|
|
char *ptr, *end;
|
|
|
|
|
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
if (!txn)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->req.msg_state != HTTP_MSG_BODY)
|
|
|
|
|
return 0;
|
[MAJOR] implement tcp request content inspection
Some people need to inspect contents of TCP requests before
deciding to forward a connection or not. A future extension
of this demand might consist in selecting a server farm
depending on the protocol detected in the request.
For this reason, a new state CL_STINSPECT has been added on
the client side. It is immediately entered upon accept() if
the statement "tcp-request inspect-delay <xxx>" is found in
the frontend configuration. Haproxy will then wait up to
this amount of time trying to find a matching ACL, and will
either accept or reject the connection depending on the
"tcp-request content <action> {if|unless}" rules, where
<action> is either "accept" or "reject".
Note that it only waits that long if no definitive verdict
can be found earlier. That generally implies calling a fetch()
function which does not have enough information to decode
some contents, or a match() function which only finds the
beginning of what it's looking for.
It is only at the ACL level that partial data may be processed
as such, because we need to distinguish between MISS and FAIL
*before* applying the term negation.
Thus it is enough to add "| ACL_PARTIAL" to the last argument
when calling acl_exec_cond() to indicate that we expect
ACL_PAT_MISS to be returned if some data is missing (for
fetch() or match()). This is the only case we may return
this value. For this reason, the ACL check in process_cli()
has become a lot simpler.
A new ACL "req_len" of type "int" has been added. Right now
it is already possible to drop requests which talk too early
(eg: for SMTP) or which don't talk at all (eg: HTTP/SSL).
Also, the acl fetch() functions have been extended in order
to permit reporting of missing data in case of fetch failure,
using the ACL_TEST_F_MAY_CHANGE flag.
The default behaviour is unchanged, and if no rule matches,
the request is accepted.
As a side effect, all layer 7 fetching functions have been
cleaned up so that they now check for the validity of the
layer 7 pointer before dereferencing it.
2008-07-14 17:54:42 -04:00
|
|
|
|
2007-06-17 10:58:38 -04:00
|
|
|
if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
|
|
|
|
|
/* ensure the indexes are not affected */
|
|
|
|
|
return 0;
|
|
|
|
|
|
2008-02-14 14:25:24 -05:00
|
|
|
end = txn->req.sol + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
|
|
|
|
|
ptr = http_get_path(txn);
|
|
|
|
|
if (!ptr)
|
2007-06-10 15:28:46 -04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* OK, we got the '/' ! */
|
|
|
|
|
test->ptr = ptr;
|
|
|
|
|
|
|
|
|
|
while (ptr < end && *ptr != '?')
|
|
|
|
|
ptr++;
|
|
|
|
|
|
|
|
|
|
test->len = ptr - test->ptr;
|
|
|
|
|
|
|
|
|
|
/* we do not need to set READ_ONLY because the data is in a buffer */
|
|
|
|
|
test->flags = ACL_TEST_F_VOL_1ST;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-10 18:06:00 -04:00
|
|
|
static int
|
|
|
|
|
acl_fetch_proto_http(struct proxy *px, struct session *s, void *l7, int dir,
|
|
|
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
|
|
|
{
|
|
|
|
|
struct buffer *req = s->req;
|
|
|
|
|
struct http_txn *txn = &s->txn;
|
|
|
|
|
struct http_msg *msg = &txn->req;
|
|
|
|
|
|
|
|
|
|
/* Note: hdr_idx.v cannot be NULL in this ACL because the ACL is tagged
|
|
|
|
|
* as a layer7 ACL, which involves automatic allocation of hdr_idx.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!s || !req)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (unlikely(msg->msg_state == HTTP_MSG_BODY)) {
|
|
|
|
|
/* Already decoded as OK */
|
|
|
|
|
test->flags |= ACL_TEST_F_SET_RES_PASS;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Try to decode HTTP request */
|
|
|
|
|
if (likely(req->lr < req->r))
|
|
|
|
|
http_msg_analyzer(req, msg, &txn->hdr_idx);
|
|
|
|
|
|
|
|
|
|
if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
|
|
|
|
|
if ((msg->msg_state == HTTP_MSG_ERROR) || (req->flags & BF_FULL)) {
|
|
|
|
|
test->flags |= ACL_TEST_F_SET_RES_FAIL;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
/* wait for final state */
|
|
|
|
|
test->flags |= ACL_TEST_F_MAY_CHANGE;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* OK we got a valid HTTP request. We have some minor preparation to
|
|
|
|
|
* perform so that further checks can rely on HTTP tests.
|
|
|
|
|
*/
|
|
|
|
|
msg->sol = req->data + msg->som;
|
|
|
|
|
txn->meth = find_http_meth(&req->data[msg->som], msg->sl.rq.m_l);
|
|
|
|
|
if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
|
|
|
|
|
s->flags |= SN_REDIRECTABLE;
|
|
|
|
|
|
|
|
|
|
if (unlikely(msg->sl.rq.v_l == 0) && !http_upgrade_v09_to_v10(req, msg, txn)) {
|
|
|
|
|
test->flags |= ACL_TEST_F_SET_RES_FAIL;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test->flags |= ACL_TEST_F_SET_RES_PASS;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2007-05-06 18:55:35 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
/* All supported keywords must be declared here. */
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
|
|
|
|
|
/* Note: must not be declared <const> as its list will be overwritten */
|
|
|
|
|
static struct acl_kw_list acl_kws = {{ },{
|
2009-07-10 18:06:00 -04:00
|
|
|
{ "req_proto_http", acl_parse_nothing, acl_fetch_proto_http, acl_match_nothing, ACL_USE_L7REQ_PERMANENT },
|
|
|
|
|
|
2008-07-25 13:31:03 -04:00
|
|
|
{ "method", acl_parse_meth, acl_fetch_meth, acl_match_meth, ACL_USE_L7REQ_PERMANENT },
|
|
|
|
|
{ "req_ver", acl_parse_ver, acl_fetch_rqver, acl_match_str, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "resp_ver", acl_parse_ver, acl_fetch_stver, acl_match_str, ACL_USE_L7RTR_VOLATILE },
|
|
|
|
|
{ "status", acl_parse_int, acl_fetch_stcode, acl_match_int, ACL_USE_L7RTR_PERMANENT },
|
|
|
|
|
|
|
|
|
|
{ "url", acl_parse_str, acl_fetch_url, acl_match_str, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "url_beg", acl_parse_str, acl_fetch_url, acl_match_beg, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "url_end", acl_parse_str, acl_fetch_url, acl_match_end, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "url_sub", acl_parse_str, acl_fetch_url, acl_match_sub, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "url_dir", acl_parse_str, acl_fetch_url, acl_match_dir, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "url_dom", acl_parse_str, acl_fetch_url, acl_match_dom, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "url_reg", acl_parse_reg, acl_fetch_url, acl_match_reg, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "url_ip", acl_parse_ip, acl_fetch_url_ip, acl_match_ip, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "url_port", acl_parse_int, acl_fetch_url_port, acl_match_int, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
|
|
|
|
|
/* note: we should set hdr* to use ACL_USE_HDR_VOLATILE, and chdr* to use L7REQ_VOLATILE */
|
|
|
|
|
{ "hdr", acl_parse_str, acl_fetch_chdr, acl_match_str, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "hdr_reg", acl_parse_reg, acl_fetch_chdr, acl_match_reg, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "hdr_beg", acl_parse_str, acl_fetch_chdr, acl_match_beg, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "hdr_end", acl_parse_str, acl_fetch_chdr, acl_match_end, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "hdr_sub", acl_parse_str, acl_fetch_chdr, acl_match_sub, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "hdr_dir", acl_parse_str, acl_fetch_chdr, acl_match_dir, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "hdr_dom", acl_parse_str, acl_fetch_chdr, acl_match_dom, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "hdr_cnt", acl_parse_int, acl_fetch_chdr_cnt,acl_match_int, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "hdr_val", acl_parse_int, acl_fetch_chdr_val,acl_match_int, ACL_USE_L7REQ_VOLATILE },
|
2009-09-19 01:54:16 -04:00
|
|
|
{ "hdr_ip", acl_parse_ip, acl_fetch_chdr_ip, acl_match_ip, ACL_USE_L7REQ_VOLATILE },
|
2008-07-25 13:31:03 -04:00
|
|
|
|
|
|
|
|
{ "shdr", acl_parse_str, acl_fetch_shdr, acl_match_str, ACL_USE_L7RTR_VOLATILE },
|
|
|
|
|
{ "shdr_reg", acl_parse_reg, acl_fetch_shdr, acl_match_reg, ACL_USE_L7RTR_VOLATILE },
|
|
|
|
|
{ "shdr_beg", acl_parse_str, acl_fetch_shdr, acl_match_beg, ACL_USE_L7RTR_VOLATILE },
|
|
|
|
|
{ "shdr_end", acl_parse_str, acl_fetch_shdr, acl_match_end, ACL_USE_L7RTR_VOLATILE },
|
|
|
|
|
{ "shdr_sub", acl_parse_str, acl_fetch_shdr, acl_match_sub, ACL_USE_L7RTR_VOLATILE },
|
|
|
|
|
{ "shdr_dir", acl_parse_str, acl_fetch_shdr, acl_match_dir, ACL_USE_L7RTR_VOLATILE },
|
|
|
|
|
{ "shdr_dom", acl_parse_str, acl_fetch_shdr, acl_match_dom, ACL_USE_L7RTR_VOLATILE },
|
|
|
|
|
{ "shdr_cnt", acl_parse_int, acl_fetch_shdr_cnt,acl_match_int, ACL_USE_L7RTR_VOLATILE },
|
|
|
|
|
{ "shdr_val", acl_parse_int, acl_fetch_shdr_val,acl_match_int, ACL_USE_L7RTR_VOLATILE },
|
2009-09-19 01:54:16 -04:00
|
|
|
{ "shdr_ip", acl_parse_ip, acl_fetch_shdr_ip, acl_match_ip, ACL_USE_L7RTR_VOLATILE },
|
2008-07-25 13:31:03 -04:00
|
|
|
|
|
|
|
|
{ "path", acl_parse_str, acl_fetch_path, acl_match_str, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "path_reg", acl_parse_reg, acl_fetch_path, acl_match_reg, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "path_beg", acl_parse_str, acl_fetch_path, acl_match_beg, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "path_end", acl_parse_str, acl_fetch_path, acl_match_end, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "path_sub", acl_parse_str, acl_fetch_path, acl_match_sub, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "path_dir", acl_parse_str, acl_fetch_path, acl_match_dir, ACL_USE_L7REQ_VOLATILE },
|
|
|
|
|
{ "path_dom", acl_parse_str, acl_fetch_path, acl_match_dom, ACL_USE_L7REQ_VOLATILE },
|
2007-06-10 15:28:46 -04:00
|
|
|
|
2007-05-08 16:45:09 -04:00
|
|
|
{ NULL, NULL, NULL, NULL },
|
|
|
|
|
|
|
|
|
|
#if 0
|
2007-05-06 18:55:35 -04:00
|
|
|
{ "line", acl_parse_str, acl_fetch_line, acl_match_str },
|
|
|
|
|
{ "line_reg", acl_parse_reg, acl_fetch_line, acl_match_reg },
|
|
|
|
|
{ "line_beg", acl_parse_str, acl_fetch_line, acl_match_beg },
|
|
|
|
|
{ "line_end", acl_parse_str, acl_fetch_line, acl_match_end },
|
|
|
|
|
{ "line_sub", acl_parse_str, acl_fetch_line, acl_match_sub },
|
|
|
|
|
{ "line_dir", acl_parse_str, acl_fetch_line, acl_match_dir },
|
|
|
|
|
{ "line_dom", acl_parse_str, acl_fetch_line, acl_match_dom },
|
|
|
|
|
|
|
|
|
|
{ "cook", acl_parse_str, acl_fetch_cook, acl_match_str },
|
|
|
|
|
{ "cook_reg", acl_parse_reg, acl_fetch_cook, acl_match_reg },
|
|
|
|
|
{ "cook_beg", acl_parse_str, acl_fetch_cook, acl_match_beg },
|
|
|
|
|
{ "cook_end", acl_parse_str, acl_fetch_cook, acl_match_end },
|
|
|
|
|
{ "cook_sub", acl_parse_str, acl_fetch_cook, acl_match_sub },
|
|
|
|
|
{ "cook_dir", acl_parse_str, acl_fetch_cook, acl_match_dir },
|
|
|
|
|
{ "cook_dom", acl_parse_str, acl_fetch_cook, acl_match_dom },
|
|
|
|
|
{ "cook_pst", acl_parse_none, acl_fetch_cook, acl_match_pst },
|
|
|
|
|
|
|
|
|
|
{ "auth_user", acl_parse_str, acl_fetch_user, acl_match_str },
|
|
|
|
|
{ "auth_regex", acl_parse_reg, acl_fetch_user, acl_match_reg },
|
|
|
|
|
{ "auth_clear", acl_parse_str, acl_fetch_auth, acl_match_str },
|
|
|
|
|
{ "auth_md5", acl_parse_str, acl_fetch_auth, acl_match_md5 },
|
|
|
|
|
{ NULL, NULL, NULL, NULL },
|
|
|
|
|
#endif
|
|
|
|
|
}};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
__attribute__((constructor))
|
|
|
|
|
static void __http_protocol_init(void)
|
|
|
|
|
{
|
|
|
|
|
acl_register_keywords(&acl_kws);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*
|
|
|
|
|
* Local variables:
|
|
|
|
|
* c-indent-level: 8
|
|
|
|
|
* c-basic-offset: 8
|
|
|
|
|
* End:
|
|
|
|
|
*/
|