diff --git a/include/proto/frontend.h b/include/proto/frontend.h index 2f0930f13..6a686229f 100644 --- a/include/proto/frontend.h +++ b/include/proto/frontend.h @@ -2,7 +2,7 @@ * include/proto/frontend.h * This file declares frontend-specific functions. * - * Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu + * Copyright (C) 2000-2011 Willy Tarreau - w@1wt.eu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,6 +28,7 @@ void get_frt_addr(struct session *s); int frontend_accept(struct session *s); int frontend_decode_proxy_request(struct session *s, struct buffer *req, int an_bit); +int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst); #endif /* _PROTO_FRONTEND_H */ diff --git a/src/frontend.c b/src/frontend.c index 75218df53..558bb9ec1 100644 --- a/src/frontend.c +++ b/src/frontend.c @@ -1,7 +1,7 @@ /* * Frontend variables and functions. * - * Copyright 2000-2010 Willy Tarreau + * Copyright 2000-2011 Willy Tarreau * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -460,6 +460,85 @@ int frontend_decode_proxy_request(struct session *s, struct buffer *req, int an_ return 0; } +/* Makes a PROXY protocol line from the two addresses. The output is sent to + * buffer for a maximum size of (including the trailing zero). + * It returns the number of bytes composing this line (including the trailing + * LF), or zero in case of failure (eg: not enough space). It supports TCP4, + * TCP6 and "UNKNOWN" formats. + */ +int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst) +{ + int ret = 0; + + if (src->ss_family == dst->ss_family && src->ss_family == AF_INET) { + ret = snprintf(buf + ret, buf_len - ret, "PROXY TCP4 "); + if (ret >= buf_len) + return 0; + + /* IPv4 src */ + if (!inet_ntop(src->ss_family, &((struct sockaddr_in *)src)->sin_addr, buf + ret, buf_len - ret)) + return 0; + + ret += strlen(buf + ret); + if (ret >= buf_len) + return 0; + + buf[ret++] = ' '; + + /* IPv4 dst */ + if (!inet_ntop(dst->ss_family, &((struct sockaddr_in *)dst)->sin_addr, buf + ret, buf_len - ret)) + return 0; + + ret += strlen(buf + ret); + if (ret >= buf_len) + return 0; + + /* source and destination ports */ + ret += snprintf(buf + ret, buf_len - ret, " %u %u\r\n", + ntohs(((struct sockaddr_in *)src)->sin_port), + ntohs(((struct sockaddr_in *)dst)->sin_port)); + if (ret >= buf_len) + return 0; + } + else if (src->ss_family == dst->ss_family && src->ss_family == AF_INET6) { + ret = snprintf(buf + ret, buf_len - ret, "PROXY TCP6 "); + if (ret >= buf_len) + return 0; + + /* IPv6 src */ + if (!inet_ntop(src->ss_family, &((struct sockaddr_in6 *)src)->sin6_addr, buf + ret, buf_len - ret)) + return 0; + + ret += strlen(buf + ret); + if (ret >= buf_len) + return 0; + + buf[ret++] = ' '; + + /* IPv6 dst */ + if (!inet_ntop(dst->ss_family, &((struct sockaddr_in6 *)dst)->sin6_addr, buf + ret, buf_len - ret)) + return 0; + + ret += strlen(buf + ret); + if (ret >= buf_len) + return 0; + + /* source and destination ports */ + ret += snprintf(buf + ret, buf_len - ret, " %u %u\r\n", + ntohs(((struct sockaddr_in6 *)src)->sin6_port), + ntohs(((struct sockaddr_in6 *)dst)->sin6_port)); + if (ret >= buf_len) + return 0; + } + else { + /* unknown family combination */ + ret = snprintf(buf, buf_len, "PROXY UNKNOWN\r\n"); + if (ret >= buf_len) + return 0; + } + return ret; +} + /* set test->i to the id of the frontend */ static int acl_fetch_fe_id(struct proxy *px, struct session *l4, void *l7, int dir,