diff options
author | Omar Polo <op@omarpolo.com> | 2024-08-03 14:40:17 +0000 |
---|---|---|
committer | Omar Polo <op@omarpolo.com> | 2024-08-03 14:40:17 +0000 |
commit | 905a32998fc46eea222fff851aab69eea4ac8e8a (patch) | |
tree | c84ef2222e5ab9df8a5accc87462fd3638130fae /proxy-proto.c | |
parent | a6b469c41b427f70e285c9e89dcfede523c81a51 (diff) |
proxy-protocol: accept cross-family proxying
Due to a strict interpretation of the spec if "TCP4" is used we
expect two ipv4 addresses (and similar for "TCP6" and ipv6 addresses).
However, the family specified in the proxy header matters only for
the first address (the source), not the destination! After all,
it's not strange to proxy from/to ipv4 and ipv6.
Use getaddrinfo(NI_NUMERICHOST) to parse the IP addresses since
inet_pton() is too strict.
Diffstat (limited to 'proxy-proto.c')
-rw-r--r-- | proxy-proto.c | 97 |
1 files changed, 48 insertions, 49 deletions
diff --git a/proxy-proto.c b/proxy-proto.c index ecf885f..f7c00d1 100644 --- a/proxy-proto.c +++ b/proxy-proto.c @@ -19,6 +19,8 @@ #include <stdint.h> #include <string.h> +#include "log.h" + #define MIN(a, b) (a) < (b) ? (a) : (b) static int @@ -88,17 +90,29 @@ check_crlf_v1(char *const *buf, size_t buflen) } static int -check_ip_v1(int af, void *addr, char **buf) +check_ip_v1(int af, char **buf, struct sockaddr_storage *addr, socklen_t *len) { - char *spc; + struct addrinfo hints, *res; + char *spc; + int err; if ((spc = strchr(*buf, ' ')) == NULL) return (-1); *spc++ = '\0'; - if (inet_pton(af, *buf, addr) != 1) + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_flags = AI_NUMERICHOST; + err = getaddrinfo(*buf, NULL, &hints, &res); + if (err) { + log_warnx("getaddrinfo(%s): %s", *buf, gai_strerror(err)); return (-1); + } + + memcpy(addr, res->ai_addr, res->ai_addrlen); + *len = res->ai_addrlen; + freeaddrinfo(res); *buf = spc; @@ -132,6 +146,7 @@ proxy_proto_v1_parse(struct proxy_protocol_v1 *s, char *buf, size_t buflen, size_t *consumed) { const char *begin = buf; + int af; if (check_crlf_v1(&buf, buflen) == -1 || check_prefix_v1(&buf) == -1) @@ -149,24 +164,13 @@ proxy_proto_v1_parse(struct proxy_protocol_v1 *s, char *buf, size_t buflen, return (-1); } - switch (s->proto) { - case PROTO_V4: - if (check_ip_v1(AF_INET, &s->srcaddr.v4, &buf) == -1 || - check_ip_v1(AF_INET, &s->dstaddr.v4, &buf) == -1) - return (-1); - break; + af = AF_INET; + if (s->proto == PROTO_V6) + af = AF_INET6; - case PROTO_V6: - if (check_ip_v1(AF_INET6, &s->srcaddr.v6, &buf) == -1 || - check_ip_v1(AF_INET6, &s->dstaddr.v6, &buf) == -1) - return (-1); - break; - - default: - return (-1); - } - - if (check_port_v1(&s->srcport, &buf) == -1 || + if (check_ip_v1(af, &buf, &s->srcaddr, &s->srclen) == -1 || + check_ip_v1(AF_UNSPEC, &buf, &s->dstaddr, &s->dstlen) == -1 || + check_port_v1(&s->srcport, &buf) == -1 || check_port_v1(&s->dstport, &buf) == -1) return (-1); @@ -182,36 +186,31 @@ int proxy_proto_v1_string(const struct proxy_protocol_v1 *s, char *buf, size_t buflen) { - // "0000:0000:0000:0000:0000:0000:0000:0000\0" - char srcaddrbuf[40], dstaddrbuf[40]; + char srcaddr[NI_MAXHOST], dstaddr[NI_MAXHOST]; int ret; - switch (s->proto) { - case PROTO_UNKNOWN: - ret = snprintf(buf, buflen, "unknown"); - goto fin; - case PROTO_V4: - inet_ntop(AF_INET, &s->srcaddr.v4, srcaddrbuf, - sizeof(srcaddrbuf)); - inet_ntop(AF_INET, &s->dstaddr.v4, dstaddrbuf, - sizeof(dstaddrbuf)); - break; - case PROTO_V6: - inet_ntop(AF_INET6, &s->srcaddr.v6, srcaddrbuf, - sizeof(srcaddrbuf)); - inet_ntop(AF_INET6, &s->dstaddr.v6, dstaddrbuf, - sizeof(dstaddrbuf)); - break; + + if (s->proto == PROTO_UNKNOWN) + return strlcpy(buf, "unknown", buflen); + + ret = getnameinfo((struct sockaddr *)&s->srcaddr, s->srclen, + srcaddr, sizeof(srcaddr), NULL, 0, + NI_NUMERICHOST); + if (ret) { + log_warnx("getnameinfo: %s", gai_strerror(ret)); + return (-1); } - ret = snprintf( - buf, - buflen, - "from %s port %u via %s port %u", - srcaddrbuf, - s->srcport, - dstaddrbuf, - s->dstport); - -fin: - return ret; + ret = getnameinfo((struct sockaddr *)&s->dstaddr, s->dstlen, + dstaddr, sizeof(dstaddr), NULL, 0, + NI_NUMERICHOST); + if (ret) { + log_warnx("getnameinfo: %s", gai_strerror(ret)); + return (-1); + } + + ret = snprintf(buf, buflen, "from %s port %u via %s port %u", + srcaddr, s->srcport, dstaddr, s->dstport); + if (ret < 0 || (size_t)ret >= buflen) + return (-1); + return (ret); } |