From 60b85b06947af7e99fdd95c694dc518253cb9cf1 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 30 Nov 2008 23:28:40 +0100 Subject: [PATCH] [MEDIUM] extract the HTTP tarpit code from process_request(). The tarpit is now an autonomous independant analyser. --- include/proto/proto_http.h | 1 + src/proto_http.c | 77 ++++++++++++++++++++------------------ src/session.c | 4 ++ 3 files changed, 46 insertions(+), 36 deletions(-) diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index 72a5e3cc0..535611d47 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -63,6 +63,7 @@ int process_cli(struct session *t); int process_srv_data(struct session *t); int process_srv_conn(struct session *t); int process_request(struct session *t); +int http_process_tarpit(struct session *s, struct buffer *req); int process_response(struct session *t); int produce_content(struct session *s); diff --git a/src/proto_http.c b/src/proto_http.c index a3750bd18..ab8c255d3 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -2359,42 +2359,6 @@ int process_request(struct session *t) ; // to keep gcc happy } - if (req->analysers & AN_REQ_HTTP_TARPIT) { - struct http_txn *txn = &t->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. - */ - 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. - */ - trace_term(t, TT_HTTP_SRV_2); - t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now); - - txn->status = 500; - if (req->flags != BF_READ_ERROR) - stream_int_retnclose(req->prod, error_message(t, HTTP_ERR_500)); - - req->analysers = 0; - req->analyse_exp = TICK_ETERNITY; - - t->fe->failed_req++; - if (!(t->flags & SN_ERR_MASK)) - t->flags |= SN_ERR_PRXCOND; - if (!(t->flags & SN_FINST_MASK)) - t->flags |= SN_FINST_T; - return 0; - } - if (req->analysers & AN_REQ_HTTP_BODY) { /* 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 @@ -2484,6 +2448,47 @@ int process_request(struct session *t) return 0; } +/* 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. + */ +int http_process_tarpit(struct session *s, struct buffer *req) +{ + 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. + */ + 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. + */ + trace_term(s, TT_HTTP_SRV_2); + 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; + + s->fe->failed_req++; + if (!(s->flags & SN_ERR_MASK)) + s->flags |= SN_ERR_PRXCOND; + if (!(s->flags & SN_FINST_MASK)) + s->flags |= SN_FINST_T; + return 0; +} + /* This function performs all the processing enabled for the current response. * 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 diff --git a/src/session.c b/src/session.c index e13bca648..34d3d0acd 100644 --- a/src/session.c +++ b/src/session.c @@ -709,6 +709,10 @@ resync_stream_interface: if (!process_request(s)) break; + if (s->req->analysers & AN_REQ_HTTP_TARPIT) + if (!http_process_tarpit(s, s->req)) + break; + /* Just make sure that nobody set a wrong flag causing an endless loop */ s->req->analysers &= AN_REQ_INSPECT | AN_REQ_HTTP_HDR | AN_REQ_HTTP_TARPIT | AN_REQ_HTTP_BODY;