From 8e3bf699db197019f6a2840923f6b33cc13bb184 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 3 Dec 2012 15:41:18 +0100 Subject: [PATCH] MEDIUM: connection: add error reporting for the PROXY protocol header When the PROXY protocol header is expected and fails, leading to an abort of the incoming connection, we now emit a log message. If option dontlognull is set and it was just a port probe, then nothing is logged. --- include/proto/connection.h | 6 +++++ include/types/connection.h | 7 ++++++ src/connection.c | 50 +++++++++++++++++++++++++++----------- src/session.c | 3 ++- 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/include/proto/connection.h b/include/proto/connection.h index 741e88ec6..8a40575a7 100644 --- a/include/proto/connection.h +++ b/include/proto/connection.h @@ -469,7 +469,13 @@ static inline const char *conn_err_code_str(struct connection *c) { switch (c->err_code) { case CO_ER_NONE: return "Success"; + case CO_ER_PRX_EMPTY: return "Connection closed while waiting for PROXY protocol header"; + case CO_ER_PRX_ABORT: return "Connection error while waiting for PROXY protocol header"; case CO_ER_PRX_TIMEOUT: return "Timeout while waiting for PROXY protocol header"; + case CO_ER_PRX_TRUNCATED: return "Truncated PROXY protocol header received"; + case CO_ER_PRX_NOT_HDR: return "Received something which does not look like a PROXY protocol header"; + case CO_ER_PRX_BAD_HDR: return "Received an invalid PROXY protocol header"; + case CO_ER_PRX_BAD_PROTO: return "Received an unhandled protocol in the PROXY protocol header"; case CO_ER_SSL_TIMEOUT: return "Timeout during SSL handshake"; } return NULL; diff --git a/include/types/connection.h b/include/types/connection.h index 6b6ddab24..f9bedcdcc 100644 --- a/include/types/connection.h +++ b/include/types/connection.h @@ -146,7 +146,14 @@ enum { /* possible connection error codes */ enum { CO_ER_NONE, /* no error */ + CO_ER_PRX_EMPTY, /* nothing received in PROXY protocol header */ + CO_ER_PRX_ABORT, /* client abort during PROXY protocol header */ CO_ER_PRX_TIMEOUT, /* timeout while waiting for a PROXY header */ + CO_ER_PRX_TRUNCATED, /* truncated PROXY protocol header */ + CO_ER_PRX_NOT_HDR, /* not a PROXY protocol header */ + CO_ER_PRX_BAD_HDR, /* bad PROXY protocol header */ + CO_ER_PRX_BAD_PROTO, /* unsupported protocol in PROXY header */ + CO_ER_SSL_TIMEOUT, /* timeout during SSL handshake */ }; diff --git a/src/connection.c b/src/connection.c index a527bd774..e2d75ba7a 100644 --- a/src/connection.c +++ b/src/connection.c @@ -280,10 +280,16 @@ int conn_recv_proxy(struct connection *conn, int flag) conn_sock_poll_recv(conn); return 0; } - goto fail; + goto recv_abort; } } while (0); + if (!trash.len) { + /* client shutdown */ + conn->err_code = CO_ER_PRX_EMPTY; + goto fail; + } + if (trash.len < 6) goto missing; @@ -291,8 +297,10 @@ int conn_recv_proxy(struct connection *conn, int flag) end = trash.str + trash.len; /* Decode a possible proxy request, fail early if it does not match */ - if (strncmp(line, "PROXY ", 6) != 0) + if (strncmp(line, "PROXY ", 6) != 0) { + conn->err_code = CO_ER_PRX_NOT_HDR; goto fail; + } line += 6; if (trash.len < 18) /* shortest possible line */ @@ -307,27 +315,27 @@ int conn_recv_proxy(struct connection *conn, int flag) if (line == end) goto missing; if (*line++ != ' ') - goto fail; + goto bad_header; dst3 = inetaddr_host_lim_ret(line, end, &line); if (line == end) goto missing; if (*line++ != ' ') - goto fail; + goto bad_header; sport = read_uint((const char **)&line, end); if (line == end) goto missing; if (*line++ != ' ') - goto fail; + goto bad_header; dport = read_uint((const char **)&line, end); if (line > end - 2) goto missing; if (*line++ != '\r') - goto fail; + goto bad_header; if (*line++ != '\n') - goto fail; + goto bad_header; /* update the session's addresses and mark them set */ ((struct sockaddr_in *)&conn->addr.from)->sin_family = AF_INET; @@ -357,7 +365,7 @@ int conn_recv_proxy(struct connection *conn, int flag) *line = 0; line++; if (*line++ != '\n') - goto fail; + goto bad_header; break; } @@ -374,21 +382,21 @@ int conn_recv_proxy(struct connection *conn, int flag) } if (!dst_s || !sport_s || !dport_s) - goto fail; + goto bad_header; sport = read_uint((const char **)&sport_s,dport_s - 1); if (*sport_s != 0) - goto fail; + goto bad_header; dport = read_uint((const char **)&dport_s,line - 2); if (*dport_s != 0) - goto fail; + goto bad_header; if (inet_pton(AF_INET6, src_s, (void *)&src3) != 1) - goto fail; + goto bad_header; if (inet_pton(AF_INET6, dst_s, (void *)&dst3) != 1) - goto fail; + goto bad_header; /* update the session's addresses and mark them set */ ((struct sockaddr_in6 *)&conn->addr.from)->sin6_family = AF_INET6; @@ -401,6 +409,8 @@ int conn_recv_proxy(struct connection *conn, int flag) conn->flags |= CO_FL_ADDR_FROM_SET | CO_FL_ADDR_TO_SET; } else { + /* The protocol does not match something known (TCP4/TCP6) */ + conn->err_code = CO_ER_PRX_BAD_PROTO; goto fail; } @@ -414,7 +424,7 @@ int conn_recv_proxy(struct connection *conn, int flag) if (len2 < 0 && errno == EINTR) continue; if (len2 != trash.len) - goto fail; + goto recv_abort; } while (0); conn->flags &= ~flag; @@ -425,6 +435,18 @@ int conn_recv_proxy(struct connection *conn, int flag) * we have not read anything. Otherwise we need to fail because we won't * be able to poll anymore. */ + conn->err_code = CO_ER_PRX_TRUNCATED; + goto fail; + + bad_header: + /* This is not a valid proxy protocol header */ + conn->err_code = CO_ER_PRX_BAD_HDR; + goto fail; + + recv_abort: + conn->err_code = CO_ER_PRX_ABORT; + goto fail; + fail: conn_sock_stop_both(conn); conn->flags |= CO_FL_ERROR; diff --git a/src/session.c b/src/session.c index 12f7f581e..c22459ba7 100644 --- a/src/session.c +++ b/src/session.c @@ -285,7 +285,8 @@ static void kill_mini_session(struct session *s) if (log && (s->fe->options & PR_O_NULLNOLOG)) { /* with "option dontlognull", we don't log connections with no transfer */ - if (!conn->err_code) + if (!conn->err_code || + conn->err_code == CO_ER_PRX_EMPTY || conn->err_code == CO_ER_PRX_ABORT) log = 0; }