mirror of
https://github.com/haproxy/haproxy.git
synced 2026-05-25 10:42:14 -04:00
This patch has 2 goals :
1. I wanted to test the appsession feature with a small PHP code,
using PHPSESSID. The problem is that when PHP gets an unknown session
id, it creates a new one with this ID. So, when sending an unknown
session to PHP, persistance is broken : haproxy won't see any new
cookie in the response and will never attach this session to a
specific server.
This also happens when you restart haproxy : the internal hash becomes
empty and all sessions loose their persistance (load balancing the
requests on all backend servers, creating a new session on each one).
For a user, it's like the service is unusable.
The patch modifies the code to make haproxy also learn the persistance
from the client : if no session is sent from the server, then the
session id found in the client part (using the URI or the client cookie)
is used to associated the server that gave the response.
As it's probably not a feature usable in all cases, I added an option
to enable it (by default it's disabled). The syntax of appsession becomes :
appsession <cookie> len <length> timeout <holdtime> [request-learn]
This helps haproxy repair the persistance (with the risk of losing its
session at the next request, as the user will probably not be load
balanced to the same server the first time).
2. This patch also tries to reduce the memory usage.
Here is a little example to explain the current behaviour :
- Take a Tomcat server where /session.jsp is valid.
- Send a request using a cookie with an unknown value AND a path
parameter with another unknown value :
curl -b "JSESSIONID=12345678901234567890123456789012" http://<haproxy>/session.jsp;jsessionid=00000000000000000000000000000001
(I know, it's unexpected to have a request like that on a live service)
Here, haproxy finds the URI session ID and stores it in its internal
hash (with no server associated). But it also finds the cookie session
ID and stores it again.
- As a result, session.jsp sends a new session ID also stored in the
internal hash, with a server associated.
=> For 1 request, haproxy has stored 3 entries, with only 1 which will be usable
The patch modifies the behaviour to store only 1 entry (maximum).
Similar patch was merged in 1.4 with commit ID bf47aeb946.
602 lines
18 KiB
C
602 lines
18 KiB
C
/*
|
|
* Client-side variables and functions.
|
|
*
|
|
* Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
|
|
*
|
|
* 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 <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <common/compat.h>
|
|
#include <common/config.h>
|
|
#include <common/time.h>
|
|
|
|
#include <types/global.h>
|
|
|
|
#include <proto/acl.h>
|
|
#include <proto/buffers.h>
|
|
#include <proto/client.h>
|
|
#include <proto/fd.h>
|
|
#include <proto/log.h>
|
|
#include <proto/hdr_idx.h>
|
|
#include <proto/proto_http.h>
|
|
#include <proto/proxy.h>
|
|
#include <proto/session.h>
|
|
#include <proto/stream_interface.h>
|
|
#include <proto/stream_sock.h>
|
|
#include <proto/task.h>
|
|
|
|
|
|
/* Retrieves the original destination address used by the client, and sets the
|
|
* SN_FRT_ADDR_SET flag.
|
|
*/
|
|
void get_frt_addr(struct session *s)
|
|
{
|
|
socklen_t namelen = sizeof(s->frt_addr);
|
|
|
|
if (get_original_dst(s->si[0].fd, (struct sockaddr_in *)&s->frt_addr, &namelen) == -1)
|
|
getsockname(s->si[0].fd, (struct sockaddr *)&s->frt_addr, &namelen);
|
|
s->flags |= SN_FRT_ADDR_SET;
|
|
}
|
|
|
|
/*
|
|
* FIXME: This should move to the STREAM_SOCK code then split into TCP and HTTP.
|
|
*/
|
|
|
|
/*
|
|
* this function is called on a read event from a listen socket, corresponding
|
|
* to an accept. It tries to accept as many connections as possible.
|
|
* It returns 0.
|
|
*/
|
|
int event_accept(int fd) {
|
|
struct listener *l = fdtab[fd].owner;
|
|
struct proxy *p = (struct proxy *)l->private; /* attached frontend */
|
|
struct session *s;
|
|
struct http_txn *txn;
|
|
struct task *t;
|
|
int cfd;
|
|
int max_accept = global.tune.maxaccept;
|
|
|
|
if (p->fe_sps_lim) {
|
|
int max = freq_ctr_remain(&p->fe_sess_per_sec, p->fe_sps_lim, 0);
|
|
if (max_accept > max)
|
|
max_accept = max;
|
|
}
|
|
|
|
while (p->feconn < p->maxconn && actconn < global.maxconn && max_accept--) {
|
|
struct sockaddr_storage addr;
|
|
socklen_t laddr = sizeof(addr);
|
|
|
|
if ((cfd = accept(fd, (struct sockaddr *)&addr, &laddr)) == -1) {
|
|
switch (errno) {
|
|
case EAGAIN:
|
|
case EINTR:
|
|
case ECONNABORTED:
|
|
return 0; /* nothing more to accept */
|
|
case ENFILE:
|
|
send_log(p, LOG_EMERG,
|
|
"Proxy %s reached system FD limit at %d. Please check system tunables.\n",
|
|
p->id, maxfd);
|
|
return 0;
|
|
case EMFILE:
|
|
send_log(p, LOG_EMERG,
|
|
"Proxy %s reached process FD limit at %d. Please check 'ulimit-n' and restart.\n",
|
|
p->id, maxfd);
|
|
return 0;
|
|
case ENOBUFS:
|
|
case ENOMEM:
|
|
send_log(p, LOG_EMERG,
|
|
"Proxy %s reached system memory limit at %d sockets. Please check system tunables.\n",
|
|
p->id, maxfd);
|
|
return 0;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if ((s = pool_alloc2(pool2_session)) == NULL) { /* disable this proxy for a while */
|
|
Alert("out of memory in event_accept().\n");
|
|
EV_FD_CLR(fd, DIR_RD);
|
|
p->state = PR_STIDLE;
|
|
goto out_close;
|
|
}
|
|
|
|
LIST_ADDQ(&sessions, &s->list);
|
|
LIST_INIT(&s->back_refs);
|
|
|
|
s->flags = 0;
|
|
s->term_trace = 0;
|
|
|
|
/* if this session comes from a known monitoring system, we want to ignore
|
|
* it as soon as possible, which means closing it immediately for TCP.
|
|
*/
|
|
if (addr.ss_family == AF_INET &&
|
|
p->mon_mask.s_addr &&
|
|
(((struct sockaddr_in *)&addr)->sin_addr.s_addr & p->mon_mask.s_addr) == p->mon_net.s_addr) {
|
|
if (p->mode == PR_MODE_TCP) {
|
|
close(cfd);
|
|
pool_free2(pool2_session, s);
|
|
continue;
|
|
}
|
|
s->flags |= SN_MONITOR;
|
|
}
|
|
|
|
if ((t = task_new()) == NULL) { /* disable this proxy for a while */
|
|
Alert("out of memory in event_accept().\n");
|
|
EV_FD_CLR(fd, DIR_RD);
|
|
p->state = PR_STIDLE;
|
|
goto out_free_session;
|
|
}
|
|
|
|
s->cli_addr = addr;
|
|
if (cfd >= global.maxsock) {
|
|
Alert("accept(): not enough free sockets. Raise -n argument. Giving up.\n");
|
|
goto out_free_task;
|
|
}
|
|
|
|
if ((fcntl(cfd, F_SETFL, O_NONBLOCK) == -1) ||
|
|
(setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY,
|
|
(char *) &one, sizeof(one)) == -1)) {
|
|
Alert("accept(): cannot set the socket in non blocking mode. Giving up\n");
|
|
goto out_free_task;
|
|
}
|
|
|
|
if (p->options & PR_O_TCP_CLI_KA)
|
|
setsockopt(cfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one));
|
|
|
|
if (p->options & PR_O_TCP_NOLING)
|
|
setsockopt(cfd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger));
|
|
|
|
t->process = l->handler;
|
|
t->context = s;
|
|
|
|
s->task = t;
|
|
s->listener = l;
|
|
s->be = s->fe = p;
|
|
|
|
/* in HTTP mode, content switching requires that the backend
|
|
* first points to the same proxy as the frontend. However, in
|
|
* TCP mode there will be no header processing so any default
|
|
* backend must be assigned if set.
|
|
*/
|
|
if (p->mode == PR_MODE_TCP) {
|
|
if (p->defbe.be)
|
|
s->be = p->defbe.be;
|
|
s->flags |= SN_BE_ASSIGNED;
|
|
}
|
|
|
|
s->ana_state = 0; /* analysers may change it but must reset it upon exit */
|
|
s->req = s->rep = NULL; /* will be allocated later */
|
|
s->sessid = NULL;
|
|
|
|
s->si[0].state = s->si[0].prev_state = SI_ST_EST;
|
|
s->si[0].err_type = SI_ET_NONE;
|
|
s->si[0].err_loc = NULL;
|
|
s->si[0].owner = t;
|
|
s->si[0].shutr = stream_sock_shutr;
|
|
s->si[0].shutw = stream_sock_shutw;
|
|
s->si[0].chk_rcv = stream_sock_chk_rcv;
|
|
s->si[0].chk_snd = stream_sock_chk_snd;
|
|
s->si[0].fd = cfd;
|
|
s->si[0].flags = SI_FL_NONE;
|
|
if (s->fe->options2 & PR_O2_INDEPSTR)
|
|
s->si[0].flags |= SI_FL_INDEP_STR;
|
|
s->si[0].exp = TICK_ETERNITY;
|
|
|
|
s->si[1].state = s->si[1].prev_state = SI_ST_INI;
|
|
s->si[1].err_type = SI_ET_NONE;
|
|
s->si[1].err_loc = NULL;
|
|
s->si[1].owner = t;
|
|
s->si[1].shutr = stream_sock_shutr;
|
|
s->si[1].shutw = stream_sock_shutw;
|
|
s->si[1].chk_rcv = stream_sock_chk_rcv;
|
|
s->si[1].chk_snd = stream_sock_chk_snd;
|
|
s->si[1].exp = TICK_ETERNITY;
|
|
s->si[1].fd = -1; /* just to help with debugging */
|
|
s->si[1].flags = SI_FL_NONE;
|
|
if (s->be->options2 & PR_O2_INDEPSTR)
|
|
s->si[1].flags |= SI_FL_INDEP_STR;
|
|
|
|
s->srv = s->prev_srv = s->srv_conn = NULL;
|
|
s->pend_pos = NULL;
|
|
s->conn_retries = s->be->conn_retries;
|
|
|
|
/* FIXME: the logs are horribly complicated now, because they are
|
|
* defined in <p>, <p>, and later <be> and <be>.
|
|
*/
|
|
|
|
if (s->flags & SN_MONITOR)
|
|
s->logs.logwait = 0;
|
|
else
|
|
s->logs.logwait = p->to_log;
|
|
|
|
if (s->logs.logwait & LW_REQ)
|
|
s->do_log = http_sess_log;
|
|
else
|
|
s->do_log = tcp_sess_log;
|
|
|
|
if (p->mode == PR_MODE_HTTP)
|
|
s->srv_error = http_return_srv_error;
|
|
else
|
|
s->srv_error = default_srv_error;
|
|
|
|
s->logs.accept_date = date; /* user-visible date for logging */
|
|
s->logs.tv_accept = now; /* corrected date for internal use */
|
|
tv_zero(&s->logs.tv_request);
|
|
s->logs.t_queue = -1;
|
|
s->logs.t_connect = -1;
|
|
s->logs.t_data = -1;
|
|
s->logs.t_close = 0;
|
|
s->logs.bytes_in = s->logs.bytes_out = 0;
|
|
s->logs.prx_queue_size = 0; /* we get the number of pending conns before us */
|
|
s->logs.srv_queue_size = 0; /* we will get this number soon */
|
|
|
|
s->data_source = DATA_SRC_NONE;
|
|
|
|
s->uniq_id = totalconn;
|
|
proxy_inc_fe_ctr(p); /* note: cum_beconn will be increased once assigned */
|
|
|
|
txn = &s->txn;
|
|
txn->flags = 0;
|
|
/* Those variables will be checked and freed if non-NULL in
|
|
* session.c:session_free(). It is important that they are
|
|
* properly initialized.
|
|
*/
|
|
txn->srv_cookie = NULL;
|
|
txn->cli_cookie = NULL;
|
|
txn->uri = NULL;
|
|
txn->req.cap = NULL;
|
|
txn->rsp.cap = NULL;
|
|
txn->hdr_idx.v = NULL;
|
|
txn->hdr_idx.size = txn->hdr_idx.used = 0;
|
|
|
|
if (p->mode == PR_MODE_HTTP) {
|
|
txn->status = -1;
|
|
txn->req.hdr_content_len = 0LL;
|
|
txn->rsp.hdr_content_len = 0LL;
|
|
txn->req.msg_state = HTTP_MSG_RQBEFORE; /* at the very beginning of the request */
|
|
txn->rsp.msg_state = HTTP_MSG_RPBEFORE; /* at the very beginning of the response */
|
|
txn->req.sol = txn->req.eol = NULL;
|
|
txn->req.som = txn->req.eoh = 0; /* relative to the buffer */
|
|
txn->rsp.sol = txn->rsp.eol = NULL;
|
|
txn->rsp.som = txn->rsp.eoh = 0; /* relative to the buffer */
|
|
txn->req.err_pos = txn->rsp.err_pos = -2; /* block buggy requests/responses */
|
|
if (p->options2 & PR_O2_REQBUG_OK)
|
|
txn->req.err_pos = -1; /* let buggy requests pass */
|
|
txn->auth_hdr.len = -1;
|
|
|
|
if (p->nb_req_cap > 0) {
|
|
if ((txn->req.cap = pool_alloc2(p->req_cap_pool)) == NULL)
|
|
goto out_fail_reqcap; /* no memory */
|
|
|
|
memset(txn->req.cap, 0, p->nb_req_cap*sizeof(char *));
|
|
}
|
|
|
|
|
|
if (p->nb_rsp_cap > 0) {
|
|
if ((txn->rsp.cap = pool_alloc2(p->rsp_cap_pool)) == NULL)
|
|
goto out_fail_rspcap; /* no memory */
|
|
|
|
memset(txn->rsp.cap, 0, p->nb_rsp_cap*sizeof(char *));
|
|
}
|
|
|
|
|
|
txn->hdr_idx.size = MAX_HTTP_HDR;
|
|
|
|
if ((txn->hdr_idx.v = pool_alloc2(p->hdr_idx_pool)) == NULL)
|
|
goto out_fail_idx; /* no memory */
|
|
|
|
hdr_idx_init(&txn->hdr_idx);
|
|
}
|
|
|
|
if ((p->mode == PR_MODE_TCP || p->mode == PR_MODE_HTTP)
|
|
&& (p->logfac1 >= 0 || p->logfac2 >= 0)) {
|
|
if (p->to_log) {
|
|
/* we have the client ip */
|
|
if (s->logs.logwait & LW_CLIP)
|
|
if (!(s->logs.logwait &= ~LW_CLIP))
|
|
s->do_log(s);
|
|
}
|
|
else if (s->cli_addr.ss_family == AF_INET) {
|
|
char pn[INET_ADDRSTRLEN], sn[INET_ADDRSTRLEN];
|
|
|
|
if (!(s->flags & SN_FRT_ADDR_SET))
|
|
get_frt_addr(s);
|
|
|
|
if (inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)&s->frt_addr)->sin_addr,
|
|
sn, sizeof(sn)) &&
|
|
inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr,
|
|
pn, sizeof(pn))) {
|
|
send_log(p, LOG_INFO, "Connect from %s:%d to %s:%d (%s/%s)\n",
|
|
pn, ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port),
|
|
sn, ntohs(((struct sockaddr_in *)&s->frt_addr)->sin_port),
|
|
p->id, (p->mode == PR_MODE_HTTP) ? "HTTP" : "TCP");
|
|
}
|
|
}
|
|
else {
|
|
char pn[INET6_ADDRSTRLEN], sn[INET6_ADDRSTRLEN];
|
|
|
|
if (!(s->flags & SN_FRT_ADDR_SET))
|
|
get_frt_addr(s);
|
|
|
|
if (inet_ntop(AF_INET6, (const void *)&((struct sockaddr_in6 *)&s->frt_addr)->sin6_addr,
|
|
sn, sizeof(sn)) &&
|
|
inet_ntop(AF_INET6, (const void *)&((struct sockaddr_in6 *)&s->cli_addr)->sin6_addr,
|
|
pn, sizeof(pn))) {
|
|
send_log(p, LOG_INFO, "Connect from %s:%d to %s:%d (%s/%s)\n",
|
|
pn, ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port),
|
|
sn, ntohs(((struct sockaddr_in6 *)&s->frt_addr)->sin6_port),
|
|
p->id, (p->mode == PR_MODE_HTTP) ? "HTTP" : "TCP");
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
|
|
int len;
|
|
|
|
if (!(s->flags & SN_FRT_ADDR_SET))
|
|
get_frt_addr(s);
|
|
|
|
if (s->cli_addr.ss_family == AF_INET) {
|
|
char pn[INET_ADDRSTRLEN];
|
|
inet_ntop(AF_INET,
|
|
(const void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr,
|
|
pn, sizeof(pn));
|
|
|
|
len = sprintf(trash, "%08x:%s.accept(%04x)=%04x from [%s:%d]\n",
|
|
s->uniq_id, p->id, (unsigned short)fd, (unsigned short)cfd,
|
|
pn, ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port));
|
|
}
|
|
else {
|
|
char pn[INET6_ADDRSTRLEN];
|
|
inet_ntop(AF_INET6,
|
|
(const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr,
|
|
pn, sizeof(pn));
|
|
|
|
len = sprintf(trash, "%08x:%s.accept(%04x)=%04x from [%s:%d]\n",
|
|
s->uniq_id, p->id, (unsigned short)fd, (unsigned short)cfd,
|
|
pn, ntohs(((struct sockaddr_in6 *)(&s->cli_addr))->sin6_port));
|
|
}
|
|
|
|
write(1, trash, len);
|
|
}
|
|
|
|
if ((s->req = pool_alloc2(pool2_buffer)) == NULL)
|
|
goto out_fail_req; /* no memory */
|
|
|
|
buffer_init(s->req);
|
|
s->req->prod = &s->si[0];
|
|
s->req->cons = &s->si[1];
|
|
s->si[0].ib = s->si[1].ob = s->req;
|
|
|
|
s->req->flags |= BF_READ_ATTACHED; /* the producer is already connected */
|
|
|
|
if (p->mode == PR_MODE_HTTP) { /* reserve some space for header rewriting */
|
|
s->req->max_len -= MAXREWRITE;
|
|
s->req->flags |= BF_READ_DONTWAIT; /* one read is usually enough */
|
|
}
|
|
|
|
/* activate default analysers enabled for this listener */
|
|
s->req->analysers = l->analysers;
|
|
|
|
if (!s->req->analysers)
|
|
buffer_write_ena(s->req); /* don't wait to establish connection */
|
|
|
|
s->req->rto = s->fe->timeout.client;
|
|
s->req->wto = s->be->timeout.server;
|
|
s->req->cto = s->be->timeout.connect;
|
|
|
|
if ((s->rep = pool_alloc2(pool2_buffer)) == NULL)
|
|
goto out_fail_rep; /* no memory */
|
|
|
|
buffer_init(s->rep);
|
|
s->rep->prod = &s->si[1];
|
|
s->rep->cons = &s->si[0];
|
|
s->si[0].ob = s->si[1].ib = s->rep;
|
|
|
|
s->rep->rto = s->be->timeout.server;
|
|
s->rep->wto = s->fe->timeout.client;
|
|
s->rep->cto = TICK_ETERNITY;
|
|
|
|
s->req->rex = TICK_ETERNITY;
|
|
s->req->wex = TICK_ETERNITY;
|
|
s->req->analyse_exp = TICK_ETERNITY;
|
|
s->rep->rex = TICK_ETERNITY;
|
|
s->rep->wex = TICK_ETERNITY;
|
|
s->rep->analyse_exp = TICK_ETERNITY;
|
|
t->expire = TICK_ETERNITY;
|
|
|
|
fd_insert(cfd);
|
|
fdtab[cfd].owner = &s->si[0];
|
|
fdtab[cfd].state = FD_STREADY;
|
|
fdtab[cfd].cb[DIR_RD].f = l->proto->read;
|
|
fdtab[cfd].cb[DIR_RD].b = s->req;
|
|
fdtab[cfd].cb[DIR_WR].f = l->proto->write;
|
|
fdtab[cfd].cb[DIR_WR].b = s->rep;
|
|
fdtab[cfd].peeraddr = (struct sockaddr *)&s->cli_addr;
|
|
fdtab[cfd].peerlen = sizeof(s->cli_addr);
|
|
|
|
if ((p->mode == PR_MODE_HTTP && (s->flags & SN_MONITOR)) ||
|
|
(p->mode == PR_MODE_HEALTH && (p->options & PR_O_HTTP_CHK))) {
|
|
/* Either we got a request from a monitoring system on an HTTP instance,
|
|
* or we're in health check mode with the 'httpchk' option enabled. In
|
|
* both cases, we return a fake "HTTP/1.0 200 OK" response and we exit.
|
|
*/
|
|
struct chunk msg = { .str = "HTTP/1.0 200 OK\r\n\r\n", .len = 19 };
|
|
stream_int_retnclose(&s->si[0], &msg); /* forge a 200 response */
|
|
s->req->analysers = 0;
|
|
t->expire = s->rep->wex;
|
|
}
|
|
else if (p->mode == PR_MODE_HEALTH) { /* health check mode, no client reading */
|
|
struct chunk msg = { .str = "OK\n", .len = 3 };
|
|
stream_int_retnclose(&s->si[0], &msg); /* forge an "OK" response */
|
|
s->req->analysers = 0;
|
|
t->expire = s->rep->wex;
|
|
}
|
|
else {
|
|
EV_FD_SET(cfd, DIR_RD);
|
|
}
|
|
|
|
/* it is important not to call the wakeup function directly but to
|
|
* pass through task_wakeup(), because this one knows how to apply
|
|
* priorities to tasks.
|
|
*/
|
|
task_wakeup(t, TASK_WOKEN_INIT);
|
|
|
|
p->feconn++; /* beconn will be increased later */
|
|
if (p->feconn > p->feconn_max)
|
|
p->feconn_max = p->feconn;
|
|
|
|
if (s->flags & SN_BE_ASSIGNED) {
|
|
proxy_inc_be_ctr(s->be);
|
|
s->be->beconn++;
|
|
if (s->be->beconn > s->be->beconn_max)
|
|
s->be->beconn_max = s->be->beconn;
|
|
}
|
|
actconn++;
|
|
totalconn++;
|
|
|
|
// fprintf(stderr, "accepting from %p => %d conn, %d total, task=%p\n", p, actconn, totalconn, t);
|
|
} /* end of while (p->feconn < p->maxconn) */
|
|
return 0;
|
|
|
|
/* Error unrolling */
|
|
out_fail_rep:
|
|
pool_free2(pool2_buffer, s->req);
|
|
out_fail_req:
|
|
pool_free2(p->hdr_idx_pool, txn->hdr_idx.v);
|
|
out_fail_idx:
|
|
pool_free2(p->rsp_cap_pool, txn->rsp.cap);
|
|
out_fail_rspcap:
|
|
pool_free2(p->req_cap_pool, txn->req.cap);
|
|
out_fail_reqcap:
|
|
out_free_task:
|
|
task_free(t);
|
|
out_free_session:
|
|
LIST_DEL(&s->list);
|
|
pool_free2(pool2_session, s);
|
|
out_close:
|
|
close(cfd);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* All supported keywords must be declared here. */
|
|
/************************************************************************/
|
|
|
|
/* set test->ptr to point to the source IPv4/IPv6 address and test->i to the family */
|
|
static int
|
|
acl_fetch_src(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
{
|
|
test->i = l4->cli_addr.ss_family;
|
|
if (test->i == AF_INET)
|
|
test->ptr = (void *)&((struct sockaddr_in *)&l4->cli_addr)->sin_addr;
|
|
else
|
|
test->ptr = (void *)&((struct sockaddr_in6 *)(&l4->cli_addr))->sin6_addr;
|
|
test->flags = ACL_TEST_F_READ_ONLY;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* set test->i to the connexion's source port */
|
|
static int
|
|
acl_fetch_sport(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
{
|
|
if (l4->cli_addr.ss_family == AF_INET)
|
|
test->i = ntohs(((struct sockaddr_in *)&l4->cli_addr)->sin_port);
|
|
else
|
|
test->i = ntohs(((struct sockaddr_in6 *)(&l4->cli_addr))->sin6_port);
|
|
test->flags = 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* set test->ptr to point to the frontend's IPv4/IPv6 address and test->i to the family */
|
|
static int
|
|
acl_fetch_dst(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
{
|
|
if (!(l4->flags & SN_FRT_ADDR_SET))
|
|
get_frt_addr(l4);
|
|
|
|
test->i = l4->frt_addr.ss_family;
|
|
if (test->i == AF_INET)
|
|
test->ptr = (void *)&((struct sockaddr_in *)&l4->frt_addr)->sin_addr;
|
|
else
|
|
test->ptr = (void *)&((struct sockaddr_in6 *)(&l4->frt_addr))->sin6_addr;
|
|
test->flags = ACL_TEST_F_READ_ONLY;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* set test->i to the frontend connexion's destination port */
|
|
static int
|
|
acl_fetch_dport(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
{
|
|
if (!(l4->flags & SN_FRT_ADDR_SET))
|
|
get_frt_addr(l4);
|
|
|
|
if (l4->frt_addr.ss_family == AF_INET)
|
|
test->i = ntohs(((struct sockaddr_in *)&l4->frt_addr)->sin_port);
|
|
else
|
|
test->i = ntohs(((struct sockaddr_in6 *)(&l4->frt_addr))->sin6_port);
|
|
test->flags = 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* set test->i to the number of connexions to the same listening socket */
|
|
static int
|
|
acl_fetch_dconn(struct proxy *px, struct session *l4, void *l7, int dir,
|
|
struct acl_expr *expr, struct acl_test *test)
|
|
{
|
|
test->i = l4->listener->nbconn;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* Note: must not be declared <const> as its list will be overwritten */
|
|
static struct acl_kw_list acl_kws = {{ },{
|
|
{ "src_port", acl_parse_int, acl_fetch_sport, acl_match_int, ACL_USE_TCP_PERMANENT },
|
|
{ "src", acl_parse_ip, acl_fetch_src, acl_match_ip, ACL_USE_TCP4_PERMANENT },
|
|
{ "dst", acl_parse_ip, acl_fetch_dst, acl_match_ip, ACL_USE_TCP4_PERMANENT },
|
|
{ "dst_port", acl_parse_int, acl_fetch_dport, acl_match_int, ACL_USE_TCP_PERMANENT },
|
|
#if 0
|
|
{ "src_limit", acl_parse_int, acl_fetch_sconn, acl_match_int },
|
|
#endif
|
|
{ "dst_conn", acl_parse_int, acl_fetch_dconn, acl_match_int, ACL_USE_NOTHING },
|
|
{ NULL, NULL, NULL, NULL },
|
|
}};
|
|
|
|
|
|
__attribute__((constructor))
|
|
static void __client_init(void)
|
|
{
|
|
acl_register_keywords(&acl_kws);
|
|
}
|
|
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-indent-level: 8
|
|
* c-basic-offset: 8
|
|
* End:
|
|
*/
|