Merge branch 'tmp-711' into 'master'

Set `sa` before attempting to set `sa->sin6_family`

Closes #711

See merge request openconnect/ocserv!554
This commit is contained in:
Nikos Mavrogiannopoulos
2026-05-24 09:22:08 +00:00
3 changed files with 125 additions and 2 deletions
+1
View File
@@ -26,6 +26,7 @@
* /cert.cer * /cert.cer
* /ca.pem * /ca.pem
* /ca.cer * /ca.cer
- Fix incorrect local address when using PROXY protocol with IPv6 (#711).
* Version 1.4.2 (released 2026-04-16) * Version 1.4.2 (released 2026-04-16)
+2 -2
View File
@@ -443,8 +443,8 @@ int parse_proxy_proto_header(struct worker_st *ws, int fd)
memset(&ws->remote_addr, 0, sizeof(ws->remote_addr)); memset(&ws->remote_addr, 0, sizeof(ws->remote_addr));
sa->sin_family = AF_INET; sa->sin_family = AF_INET;
memcpy(&sa->sin_port, p + 8, 2);
memcpy(&sa->sin_addr, p, 4); memcpy(&sa->sin_addr, p, 4);
memcpy(&sa->sin_port, p + 8, 2);
ws->remote_addr_len = sizeof(struct sockaddr_in); ws->remote_addr_len = sizeof(struct sockaddr_in);
memset(&ws->our_addr, 0, sizeof(ws->our_addr)); memset(&ws->our_addr, 0, sizeof(ws->our_addr));
@@ -473,8 +473,8 @@ int parse_proxy_proto_header(struct worker_st *ws, int fd)
ws->remote_addr_len = sizeof(struct sockaddr_in6); ws->remote_addr_len = sizeof(struct sockaddr_in6);
memset(&ws->our_addr, 0, sizeof(ws->our_addr)); memset(&ws->our_addr, 0, sizeof(ws->our_addr));
sa->sin6_family = AF_INET6;
sa = (void *)&ws->our_addr; sa = (void *)&ws->our_addr;
sa->sin6_family = AF_INET6;
memcpy(&sa->sin6_addr, p + 16, 16); memcpy(&sa->sin6_addr, p + 16, 16);
memcpy(&sa->sin6_port, p + 34, 2); memcpy(&sa->sin6_port, p + 34, 2);
ws->our_addr_len = sizeof(struct sockaddr_in6); ws->our_addr_len = sizeof(struct sockaddr_in6);
+122
View File
@@ -130,6 +130,55 @@ static size_t build_v2_packet(uint8_t *buf, size_t bufsz,
return pos; return pos;
} }
/*
* Build a proxy protocol v2 packet with an IPv6 address block into buf[].
* Returns the total number of bytes written.
*/
static size_t build_v2_ipv6_packet(uint8_t *buf, size_t bufsz,
const struct in6_addr *src_addr,
uint16_t src_port,
const struct in6_addr *dst_addr,
uint16_t dst_port)
{
size_t pos = 0;
uint16_t sp, dp, plen;
memcpy(buf + pos, PP2_SIG, 12);
pos += 12;
buf[pos++] = 0x21; /* ver=2, PROXY command */
buf[pos++] = 0x21; /* AF_INET6 (0x2) | TCP (0x1) */
size_t len_offset = pos;
buf[pos++] = 0;
buf[pos++] = 0;
size_t payload_start = pos;
/* IPv6 address block (36 bytes):
* src IP (16 bytes)
* dst IP (16 bytes)
* src port (2 bytes, network order)
* dst port (2 bytes, network order)
*/
memcpy(buf + pos, src_addr, 16);
pos += 16;
memcpy(buf + pos, dst_addr, 16);
pos += 16;
sp = htons(src_port);
dp = htons(dst_port);
memcpy(buf + pos, &sp, 2);
pos += 2;
memcpy(buf + pos, &dp, 2);
pos += 2;
plen = (uint16_t)(pos - payload_start);
buf[len_offset] = (plen >> 8) & 0xff;
buf[len_offset + 1] = plen & 0xff;
assert(pos <= bufsz);
return pos;
}
/* /*
* Feed pkt into a pipe and call parse_proxy_proto_header. * Feed pkt into a pipe and call parse_proxy_proto_header.
* ws->conn_type must be set by the caller. * ws->conn_type must be set by the caller.
@@ -275,5 +324,78 @@ int main(void)
return 1; return 1;
} }
/* ------------------------------------------------------------------ */
/* Test 6: IPv6 proxy protocol v2 — both remote and local addr correct */
/* */
/* Reproducer for the bug where sa->sin6_family was assigned before sa */
/* was redirected to &ws->our_addr, leaving our_addr.sin6_family == 0. */
/* ------------------------------------------------------------------ */
{
struct in6_addr src6, dst6;
struct sockaddr_in6 *rem6, *loc6;
memset(&ws, 0, sizeof(ws));
ws.conn_type = SOCK_TYPE_TCP;
inet_pton(AF_INET6, "2001:db8::1", &src6);
inet_pton(AF_INET6, "2001:db8::2", &dst6);
pkt_len = build_v2_ipv6_packet(pkt, sizeof(pkt), &src6, 1234,
&dst6, 443);
ret = run_parse(&ws, pkt, pkt_len);
if (ret != 0) {
fprintf(stderr, "Test 6: parse failed (%d)\n", ret);
return 1;
}
if (ws.remote_addr_len != sizeof(struct sockaddr_in6)) {
fprintf(stderr, "Test 6: remote_addr_len wrong (%u)\n",
ws.remote_addr_len);
return 1;
}
if (ws.our_addr_len != sizeof(struct sockaddr_in6)) {
fprintf(stderr, "Test 6: our_addr_len wrong (%u)\n",
ws.our_addr_len);
return 1;
}
rem6 = (void *)&ws.remote_addr;
loc6 = (void *)&ws.our_addr;
if (rem6->sin6_family != AF_INET6) {
fprintf(stderr,
"Test 6: remote sin6_family wrong: %d\n",
rem6->sin6_family);
return 1;
}
/* This check catches the bug: sin6_family was written to
* remote_addr a second time instead of to our_addr. */
if (loc6->sin6_family != AF_INET6) {
fprintf(stderr,
"Test 6: local sin6_family wrong: %d (expected %d)\n",
loc6->sin6_family, AF_INET6);
return 1;
}
if (memcmp(&rem6->sin6_addr, &src6, 16) != 0) {
fprintf(stderr, "Test 6: remote address mismatch\n");
return 1;
}
if (memcmp(&loc6->sin6_addr, &dst6, 16) != 0) {
fprintf(stderr, "Test 6: local address mismatch\n");
return 1;
}
if (ntohs(rem6->sin6_port) != 1234) {
fprintf(stderr, "Test 6: remote port wrong: %u\n",
ntohs(rem6->sin6_port));
return 1;
}
if (ntohs(loc6->sin6_port) != 443) {
fprintf(stderr, "Test 6: local port wrong: %u\n",
ntohs(loc6->sin6_port));
return 1;
}
}
return 0; return 0;
} }