From 2a938b3eaadcc6edeee612c2c0d8b0cbd7cd91df Mon Sep 17 00:00:00 2001 From: Paul Kulchenko Date: Mon, 14 Mar 2022 17:21:15 -0700 Subject: [PATCH] Use last X-Forwarded-For header (#367) This header is non-standard but AWS seems to need this. --- net/http/khttprepeatable.c | 1 + net/http/parseforwarded.c | 9 +++++++-- tool/net/help.txt | 3 ++- tool/net/redbean.c | 8 ++++++-- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/net/http/khttprepeatable.c b/net/http/khttprepeatable.c index 11360b35f..184823e70 100644 --- a/net/http/khttprepeatable.c +++ b/net/http/khttprepeatable.c @@ -77,6 +77,7 @@ const bool kHttpRepeatable[kHttpHeadersMax] = { [kHttpVia] = true, [kHttpWarning] = true, [kHttpWwwAuthenticate] = true, + [kHttpXForwardedFor] = true, [kHttpAccessControlAllowHeaders] = true, [kHttpAccessControlAllowMethods] = true, [kHttpAccessControlRequestHeaders] = true, diff --git a/net/http/parseforwarded.c b/net/http/parseforwarded.c index 4b25697eb..d5aa2b052 100644 --- a/net/http/parseforwarded.c +++ b/net/http/parseforwarded.c @@ -24,13 +24,13 @@ * * This header is used by reverse proxies. For example: * - * X-Forwarded-For: 203.0.113.42:31337 + * X-Forwarded-For: 203.0.110.2, 203.0.113.42:31337 * * The port is optional and will be set to zero if absent. * * @param s is input data * @param n if -1 implies strlen - * @param ip receives ip on success if not NULL + * @param ip receives last/right ip on success if not NULL * @param port receives port on success if not NULL * @return 0 on success or -1 on failure * @see RFC7239's poorly designed Forwarded header @@ -38,10 +38,15 @@ int ParseForwarded(const char *s, size_t n, uint32_t *ip, uint16_t *port) { int c, t; size_t i; + char *r; uint32_t x; if (n == -1) n = s ? strlen(s) : 0; if (n) { t = x = i = 0; + if ((r = strrchr(s, ','))) { + i = r - s; + if ((s[++i] & 255) == ' ') ++i; // skip optional space + } do { c = s[i++] & 255; if (isdigit(c)) { diff --git a/tool/net/help.txt b/tool/net/help.txt index 7323e05c4..e7d09842f 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -574,7 +574,8 @@ FUNCTIONS Returns client ip4 address and port, e.g. 0x01020304,31337 would represent 1.2.3.4:31337. This is the same as GetClientAddr except it will use the ip:port from the X-Forwarded-For header, only if - IsPrivateIp or IsLoopbackIp return true. + IsPrivateIp or IsLoopbackIp return true. When multiple addresses + are present in the header, the last/right-most address is used. GetClientAddr() → ip:uint32,port:uint16 Returns client socket ip4 address and port, e.g. 0x01020304,31337 diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 371268a06..aa4392ea9 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -822,8 +822,12 @@ static inline void GetRemoteAddr(uint32_t *ip, uint16_t *port) { GetClientAddr(ip, port); if (HasHeader(kHttpXForwardedFor) && (IsPrivateIp(*ip) || IsLoopbackIp(*ip))) { - ParseForwarded(HeaderData(kHttpXForwardedFor), - HeaderLength(kHttpXForwardedFor), ip, port); + if (ParseForwarded(HeaderData(kHttpXForwardedFor), + HeaderLength(kHttpXForwardedFor), + ip, port) == -1) + WARNF("invalid X-Forwarded-For value: %`'.*s", + HeaderLength(kHttpXForwardedFor), + HeaderData(kHttpXForwardedFor)); } }