Skip to content

Commit b0a448a

Browse files
committed
fix: properly handle IPv6 addresses in HTTP Host headers
This commit fixes IPv6 address handling in HTTP client Host headers by adding bracket notation when required and improving URL parsing validation. Changes: - Add automatic bracket wrapping for unbracketed IPv6 addresses in Host headers for both standard and non-standard ports - Add IPv6 bracketing for HTTPS default port (443) to ensure RFC compliance even when port is omitted (e.g., Host: [::1]) - Fix off-by-one error in IPv6 bracket stripping (was removing one extra character) - Fix incorrect length calculation in flb_utils_copy_host_sds for bracketed IPv6 extraction (changed from absolute position to relative length to properly account for pos_init offset) - Constrain IPv6 bracket validation to host portion only, preventing false negatives when brackets appear in URL paths or query strings - Use memchr with length limit for consistent and safe bracket detection in both IPv6 and non-IPv6 cases - Improve error handling in URL parsing with proper cleanup on failure - Update TLS flag checking to use flb_stream_get_flag_status() for more reliable detection Tests: - Add test for IPv6 with HTTPS on default port 443 - Add test cases for brackets in URL paths and query strings - Add test cases for malformed bracket scenarios Signed-off-by: Shelby Hagman <shelbyzh@amazon.com>
1 parent 4f8c50b commit b0a448a

File tree

5 files changed

+346
-9
lines changed

5 files changed

+346
-9
lines changed

src/flb_http_client.c

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@
3333
#define _GNU_SOURCE
3434
#include <string.h>
3535

36+
#ifdef FLB_SYSTEM_WINDOWS
37+
#include <winsock2.h>
38+
#include <ws2tcpip.h>
39+
#endif
40+
3641
#include <fluent-bit/flb_info.h>
42+
#include <fluent-bit/flb_compat.h>
3743
#include <fluent-bit/flb_kv.h>
3844
#include <fluent-bit/flb_log.h>
3945
#include <fluent-bit/flb_mem.h>
@@ -617,11 +623,32 @@ static int add_host_and_content_length(struct flb_http_client *c)
617623
out_port = c->port;
618624
}
619625

620-
if (c->flags & FLB_IO_TLS && out_port == 443) {
621-
tmp = flb_sds_copy(host, out_host, strlen(out_host));
626+
/* Check if connection uses TLS and port is 443 (HTTPS default) */
627+
if (flb_stream_get_flag_status(&u->base, FLB_IO_TLS) && out_port == 443) {
628+
struct in6_addr addr;
629+
630+
/* Check if out_host is an unbracketed IPv6 address */
631+
if (out_host && out_host[0] != '[' && inet_pton(AF_INET6, out_host, &addr) == 1) {
632+
/* IPv6 address needs brackets for RFC compliance */
633+
tmp = flb_sds_printf(&host, "[%s]", out_host);
634+
}
635+
else {
636+
/* HTTPS on default port 443 - omit port from Host header */
637+
tmp = flb_sds_copy(host, out_host, strlen(out_host));
638+
}
622639
}
623640
else {
624-
tmp = flb_sds_printf(&host, "%s:%i", out_host, out_port);
641+
struct in6_addr addr;
642+
643+
/* Check if out_host is an unbracketed IPv6 address */
644+
if (out_host && out_host[0] != '[' && inet_pton(AF_INET6, out_host, &addr) == 1) {
645+
/* IPv6 address needs brackets when combined with port */
646+
tmp = flb_sds_printf(&host, "[%s]:%i", out_host, out_port);
647+
}
648+
else {
649+
/* IPv4 address, domain name, or already bracketed IPv6 */
650+
tmp = flb_sds_printf(&host, "%s:%i", out_host, out_port);
651+
}
625652
}
626653

627654
if (!tmp) {

src/flb_network.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#ifdef FLB_SYSTEM_WINDOWS
3131
#define poll WSAPoll
3232
#include <winsock2.h>
33+
#include <ws2tcpip.h>
3334
#else
3435
#include <sys/poll.h>
3536
#endif

src/flb_utils.c

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,13 +1431,48 @@ static char *flb_utils_copy_host_sds(const char *string, int pos_init, int pos_e
14311431
if (string[pos_end-1] != ']') {
14321432
return NULL;
14331433
}
1434-
return flb_sds_create_len(string + pos_init + 1, pos_end - 1);
1434+
return flb_sds_create_len(string + pos_init + 1, pos_end - pos_init - 2);
14351435
}
14361436
else {
1437-
return flb_sds_create_len(string + pos_init, pos_end);
1437+
return flb_sds_create_len(string + pos_init, pos_end - pos_init);
14381438
}
14391439
}
14401440

1441+
/* Validate IPv6 bracket syntax in URL host part */
1442+
static int validate_ipv6_brackets(const char *p, char **out_bracket)
1443+
{
1444+
const char *host_end;
1445+
char *bracket = NULL;
1446+
char *closing;
1447+
1448+
/* Only inspect the host portion (up to the first '/') */
1449+
host_end = strchr(p, '/');
1450+
if (!host_end) {
1451+
host_end = p + strlen(p);
1452+
}
1453+
1454+
if (p[0] == '[') {
1455+
closing = memchr(p, ']', host_end - p);
1456+
if (!closing || closing == p + 1) {
1457+
/* Missing closing bracket or empty brackets [] */
1458+
return -1;
1459+
}
1460+
bracket = closing;
1461+
}
1462+
else {
1463+
/* Non-bracketed hosts must not contain ']' before the first '/' */
1464+
closing = memchr(p, ']', host_end - p);
1465+
if (closing) {
1466+
return -1;
1467+
}
1468+
}
1469+
1470+
if (out_bracket) {
1471+
*out_bracket = bracket;
1472+
}
1473+
return 0;
1474+
}
1475+
14411476
int flb_utils_url_split(const char *in_url, char **out_protocol,
14421477
char **out_host, char **out_port, char **out_uri)
14431478
{
@@ -1448,6 +1483,7 @@ int flb_utils_url_split(const char *in_url, char **out_protocol,
14481483
char *p;
14491484
char *tmp;
14501485
char *sep;
1486+
char *bracket = NULL;
14511487

14521488
/* Protocol */
14531489
p = strstr(in_url, "://");
@@ -1467,9 +1503,17 @@ int flb_utils_url_split(const char *in_url, char **out_protocol,
14671503
/* Advance position after protocol */
14681504
p += 3;
14691505

1470-
/* Check for first '/' */
1506+
/* Validate IPv6 brackets */
14711507
sep = strchr(p, '/');
1472-
tmp = strchr(p, ':');
1508+
if (validate_ipv6_brackets(p, &bracket) < 0) {
1509+
flb_errno();
1510+
goto error;
1511+
}
1512+
if (bracket) {
1513+
tmp = strchr(bracket, ':');
1514+
} else {
1515+
tmp = strchr(p, ':');
1516+
}
14731517

14741518
/* Validate port separator is found before the first slash */
14751519
if (sep && tmp) {
@@ -1501,10 +1545,18 @@ int flb_utils_url_split(const char *in_url, char **out_protocol,
15011545
tmp = strchr(p, '/');
15021546
if (tmp) {
15031547
host = flb_copy_host(p, 0, tmp - p);
1548+
if (!host) {
1549+
flb_errno();
1550+
goto error;
1551+
}
15041552
uri = flb_strdup(tmp);
15051553
}
15061554
else {
15071555
host = flb_copy_host(p, 0, strlen(p));
1556+
if (!host) {
1557+
flb_errno();
1558+
goto error;
1559+
}
15081560
uri = flb_strdup("/");
15091561
}
15101562
}
@@ -1529,6 +1581,15 @@ int flb_utils_url_split(const char *in_url, char **out_protocol,
15291581
if (protocol) {
15301582
flb_free(protocol);
15311583
}
1584+
if (host) {
1585+
flb_free(host);
1586+
}
1587+
if (port) {
1588+
flb_free(port);
1589+
}
1590+
if (uri) {
1591+
flb_free(uri);
1592+
}
15321593

15331594
return -1;
15341595
}
@@ -1544,6 +1605,7 @@ int flb_utils_url_split_sds(const flb_sds_t in_url, flb_sds_t *out_protocol,
15441605
char *p = NULL;
15451606
char *tmp = NULL;
15461607
char *sep = NULL;
1608+
char *bracket = NULL;
15471609

15481610
/* Protocol */
15491611
p = strstr(in_url, "://");
@@ -1563,9 +1625,17 @@ int flb_utils_url_split_sds(const flb_sds_t in_url, flb_sds_t *out_protocol,
15631625
/* Advance position after protocol */
15641626
p += 3;
15651627

1566-
/* Check for first '/' */
1628+
/* Validate IPv6 brackets */
15671629
sep = strchr(p, '/');
1568-
tmp = strchr(p, ':');
1630+
if (validate_ipv6_brackets(p, &bracket) < 0) {
1631+
flb_errno();
1632+
goto error;
1633+
}
1634+
if (bracket) {
1635+
tmp = strchr(bracket, ':');
1636+
} else {
1637+
tmp = strchr(p, ':');
1638+
}
15691639

15701640
/* Validate port separator is found before the first slash */
15711641
if (sep && tmp) {
@@ -1597,10 +1667,18 @@ int flb_utils_url_split_sds(const flb_sds_t in_url, flb_sds_t *out_protocol,
15971667
tmp = strchr(p, '/');
15981668
if (tmp) {
15991669
host = flb_utils_copy_host_sds(p, 0, tmp - p);
1670+
if (!host) {
1671+
flb_errno();
1672+
goto error;
1673+
}
16001674
uri = flb_sds_create(tmp);
16011675
}
16021676
else {
16031677
host = flb_utils_copy_host_sds(p, 0, strlen(p));
1678+
if (!host) {
1679+
flb_errno();
1680+
goto error;
1681+
}
16041682
uri = flb_sds_create("/");
16051683
}
16061684
}

0 commit comments

Comments
 (0)