@@ -1431,13 +1431,103 @@ 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 , const char * * out_bracket )
1443+ {
1444+ const char * host_end ;
1445+ const char * bracket = NULL ;
1446+ const char * closing ;
1447+ const char * query_or_fragment ;
1448+
1449+ /* Only inspect the host portion (up to the first '/', '?', or '#') */
1450+ host_end = strchr (p , '/' );
1451+ query_or_fragment = strpbrk (p , "?#" );
1452+
1453+ /* Use the earliest delimiter found */
1454+ if (query_or_fragment && (!host_end || query_or_fragment < host_end )) {
1455+ host_end = query_or_fragment ;
1456+ }
1457+
1458+ if (!host_end ) {
1459+ host_end = p + strlen (p );
1460+ }
1461+
1462+ if (p [0 ] == '[' ) {
1463+ closing = memchr (p , ']' , host_end - p );
1464+ if (!closing || closing == p + 1 ) {
1465+ /* Missing closing bracket or empty brackets [] */
1466+ return -1 ;
1467+ }
1468+ bracket = closing ;
1469+ }
1470+ else {
1471+ /* Non-bracketed hosts must not contain ']' before the first '/' */
1472+ closing = memchr (p , ']' , host_end - p );
1473+ if (closing ) {
1474+ return -1 ;
1475+ }
1476+ }
1477+
1478+ if (out_bracket ) {
1479+ * out_bracket = bracket ;
1480+ }
1481+ return 0 ;
1482+ }
1483+
1484+ /* Helper to create URI with prepended '/' if it starts with '?' or '#' */
1485+ static char * create_uri_with_slash (const char * uri_part )
1486+ {
1487+ char * uri ;
1488+ size_t uri_part_len ;
1489+
1490+ if (!uri_part || * uri_part == '\0' ) {
1491+ return flb_strdup ("/" );
1492+ }
1493+
1494+ /* If URI starts with '?' or '#', prepend '/' */
1495+ if (* uri_part == '?' || * uri_part == '#' ) {
1496+ uri_part_len = strlen (uri_part );
1497+ /* Allocate space for '/' + uri_part + '\0' */
1498+ uri = flb_malloc (uri_part_len + 2 );
1499+ if (!uri ) {
1500+ return NULL ;
1501+ }
1502+ uri [0 ] = '/' ;
1503+ /* +1 to include '\0' */
1504+ memcpy (uri + 1 , uri_part , uri_part_len + 1 );
1505+ return uri ;
1506+ }
1507+
1508+ /* URI already starts with '/' or is a normal path */
1509+ return flb_strdup (uri_part );
1510+ }
1511+
1512+ /* SDS version: Helper to create URI with prepended '/' if it starts with '?' or '#' */
1513+ static flb_sds_t create_uri_with_slash_sds (const char * uri_part )
1514+ {
1515+ char * result ;
1516+ flb_sds_t uri ;
1517+
1518+ /* Use the regular version to create the string */
1519+ result = create_uri_with_slash (uri_part );
1520+ if (!result ) {
1521+ return NULL ;
1522+ }
1523+
1524+ /* Convert to SDS */
1525+ uri = flb_sds_create (result );
1526+ flb_free (result );
1527+
1528+ return uri ;
1529+ }
1530+
14411531int flb_utils_url_split (const char * in_url , char * * out_protocol ,
14421532 char * * out_host , char * * out_port , char * * out_uri )
14431533{
@@ -1448,6 +1538,7 @@ int flb_utils_url_split(const char *in_url, char **out_protocol,
14481538 char * p ;
14491539 char * tmp ;
14501540 char * sep ;
1541+ const char * bracket = NULL ;
14511542
14521543 /* Protocol */
14531544 p = strstr (in_url , "://" );
@@ -1467,48 +1558,81 @@ int flb_utils_url_split(const char *in_url, char **out_protocol,
14671558 /* Advance position after protocol */
14681559 p += 3 ;
14691560
1470- /* Check for first '/' */
1561+ /* Validate IPv6 brackets */
14711562 sep = strchr (p , '/' );
1472- tmp = strchr (p , ':' );
1563+ if (validate_ipv6_brackets (p , & bracket ) < 0 ) {
1564+ flb_errno ();
1565+ goto error ;
1566+ }
14731567
1474- /* Validate port separator is found before the first slash */
1475- if (sep && tmp ) {
1476- if (tmp > sep ) {
1477- tmp = NULL ;
1478- }
1568+ /* Compute end of host segment (before '/', '?', or '#') */
1569+ const char * host_end = sep ;
1570+ const char * qf = strpbrk (p , "?#" );
1571+
1572+ if (!host_end || (qf && qf < host_end )) {
1573+ host_end = qf ;
1574+ }
1575+ if (!host_end ) {
1576+ host_end = p + strlen (p );
1577+ }
1578+
1579+ if (bracket ) {
1580+ /* For bracketed IPv6, only ports after ']' and before URI delimiters are valid */
1581+ tmp = memchr (bracket , ':' , host_end - bracket );
1582+ }
1583+ else {
1584+ /* Non-IPv6: limit ':' search to the host portion */
1585+ tmp = memchr (p , ':' , host_end - p );
14791586 }
14801587
1588+ /* Extract host if port separator was found */
14811589 if (tmp ) {
14821590 host = flb_copy_host (p , 0 , tmp - p );
14831591 if (!host ) {
14841592 flb_errno ();
14851593 goto error ;
14861594 }
14871595 p = tmp + 1 ;
1596+ }
14881597
1489- /* Look for an optional URI */
1490- tmp = strchr (p , '/' );
1598+ /* Find URI delimiter (/, ?, or #) */
1599+ tmp = strpbrk (p , "/?#" );
1600+
1601+ if (!host ) {
1602+ /* No port: extract host */
14911603 if (tmp ) {
1492- port = mk_string_copy_substr (p , 0 , tmp - p );
1493- uri = flb_strdup (tmp );
1604+ host = flb_copy_host (p , 0 , tmp - p );
14941605 }
14951606 else {
1496- port = flb_strdup (p );
1497- uri = flb_strdup ("/" );
1607+ host = flb_copy_host (p , 0 , strlen (p ));
1608+ }
1609+ if (!host ) {
1610+ flb_errno ();
1611+ goto error ;
14981612 }
14991613 }
15001614 else {
1501- tmp = strchr ( p , '/' );
1615+ /* Port exists: extract port */
15021616 if (tmp ) {
1503- host = flb_copy_host (p , 0 , tmp - p );
1504- uri = flb_strdup (tmp );
1617+ port = mk_string_copy_substr (p , 0 , tmp - p );
15051618 }
15061619 else {
1507- host = flb_copy_host (p , 0 , strlen (p ));
1508- uri = flb_strdup ("/" );
1620+ port = flb_strdup (p );
15091621 }
15101622 }
15111623
1624+ /* Extract URI */
1625+ if (tmp ) {
1626+ uri = create_uri_with_slash (tmp );
1627+ if (!uri ) {
1628+ flb_errno ();
1629+ goto error ;
1630+ }
1631+ }
1632+ else {
1633+ uri = flb_strdup ("/" );
1634+ }
1635+
15121636 if (!port ) {
15131637 if (strcmp (protocol , "http" ) == 0 ) {
15141638 port = flb_strdup ("80" );
@@ -1529,6 +1653,15 @@ int flb_utils_url_split(const char *in_url, char **out_protocol,
15291653 if (protocol ) {
15301654 flb_free (protocol );
15311655 }
1656+ if (host ) {
1657+ flb_free (host );
1658+ }
1659+ if (port ) {
1660+ flb_free (port );
1661+ }
1662+ if (uri ) {
1663+ flb_free (uri );
1664+ }
15321665
15331666 return -1 ;
15341667}
@@ -1544,6 +1677,7 @@ int flb_utils_url_split_sds(const flb_sds_t in_url, flb_sds_t *out_protocol,
15441677 char * p = NULL ;
15451678 char * tmp = NULL ;
15461679 char * sep = NULL ;
1680+ const char * bracket = NULL ;
15471681
15481682 /* Protocol */
15491683 p = strstr (in_url , "://" );
@@ -1563,47 +1697,80 @@ int flb_utils_url_split_sds(const flb_sds_t in_url, flb_sds_t *out_protocol,
15631697 /* Advance position after protocol */
15641698 p += 3 ;
15651699
1566- /* Check for first '/' */
1700+ /* Validate IPv6 brackets */
15671701 sep = strchr (p , '/' );
1568- tmp = strchr (p , ':' );
1702+ if (validate_ipv6_brackets (p , & bracket ) < 0 ) {
1703+ flb_errno ();
1704+ goto error ;
1705+ }
15691706
1570- /* Validate port separator is found before the first slash */
1571- if (sep && tmp ) {
1572- if (tmp > sep ) {
1573- tmp = NULL ;
1574- }
1707+ /* Compute end of host segment (before '/', '?', or '#') */
1708+ const char * host_end = sep ;
1709+ const char * qf = strpbrk (p , "?#" );
1710+
1711+ if (!host_end || (qf && qf < host_end )) {
1712+ host_end = qf ;
1713+ }
1714+ if (!host_end ) {
1715+ host_end = p + strlen (p );
15751716 }
15761717
1718+ if (bracket ) {
1719+ /* For bracketed IPv6, only ports after ']' and before URI delimiters are valid */
1720+ tmp = memchr (bracket , ':' , host_end - bracket );
1721+ }
1722+ else {
1723+ /* Non-IPv6: limit ':' search to the host portion */
1724+ tmp = memchr (p , ':' , host_end - p );
1725+ }
1726+
1727+ /* Extract host if port separator was found */
15771728 if (tmp ) {
15781729 host = flb_utils_copy_host_sds (p , 0 , tmp - p );
15791730 if (!host ) {
15801731 flb_errno ();
15811732 goto error ;
15821733 }
15831734 p = tmp + 1 ;
1735+ }
15841736
1585- /* Look for an optional URI */
1586- tmp = strchr (p , '/' );
1737+ /* Find URI delimiter (/, ?, or #) */
1738+ tmp = strpbrk (p , "/?#" );
1739+
1740+ if (!host ) {
1741+ /* No port: extract host */
15871742 if (tmp ) {
1588- port = flb_sds_create_len (p , tmp - p );
1589- uri = flb_sds_create (tmp );
1743+ host = flb_utils_copy_host_sds (p , 0 , tmp - p );
15901744 }
15911745 else {
1592- port = flb_sds_create_len (p , strlen (p ));
1593- uri = flb_sds_create ("/" );
1746+ host = flb_utils_copy_host_sds (p , 0 , strlen (p ));
1747+ }
1748+ if (!host ) {
1749+ flb_errno ();
1750+ goto error ;
15941751 }
15951752 }
15961753 else {
1597- tmp = strchr ( p , '/' );
1754+ /* Port exists: extract port */
15981755 if (tmp ) {
1599- host = flb_utils_copy_host_sds (p , 0 , tmp - p );
1600- uri = flb_sds_create (tmp );
1756+ port = flb_sds_create_len (p , tmp - p );
16011757 }
16021758 else {
1603- host = flb_utils_copy_host_sds (p , 0 , strlen (p ));
1604- uri = flb_sds_create ("/" );
1759+ port = flb_sds_create_len (p , strlen (p ));
1760+ }
1761+ }
1762+
1763+ /* Extract URI */
1764+ if (tmp ) {
1765+ uri = create_uri_with_slash_sds (tmp );
1766+ if (!uri ) {
1767+ flb_errno ();
1768+ goto error ;
16051769 }
16061770 }
1771+ else {
1772+ uri = flb_sds_create ("/" );
1773+ }
16071774
16081775 if (!port ) {
16091776 if (strcmp (protocol , "http" ) == 0 ) {
0 commit comments