Skip to content

Commit

Permalink
link-local scope
Browse files Browse the repository at this point in the history
  • Loading branch information
bradh352 committed Sep 13, 2024
1 parent 87bf957 commit defa847
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 11 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,5 @@ See [Features](FEATURES.md)
IPv6 address sorting as used by `ares_getaddrinfo()`.
- [RFC7413](https://datatracker.ietf.org/doc/html/rfc7413).
TCP FastOpen (TFO) for 0-RTT TCP Connection Resumption.
- [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986).
Uniform Resource Identifier (URI). Used for server configuration.
1 change: 1 addition & 0 deletions src/lib/include/ares_str.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ CARES_EXTERN size_t ares_strlen(const char *str);
CARES_EXTERN size_t ares_strcpy(char *dest, const char *src, size_t dest_size);

CARES_EXTERN ares_bool_t ares_str_isnum(const char *str);
CARES_EXTERN ares_bool_t ares_str_isalnum(const char *str);

CARES_EXTERN void ares_str_ltrim(char *str);
CARES_EXTERN void ares_str_rtrim(char *str);
Expand Down
18 changes: 17 additions & 1 deletion src/lib/str/ares_str.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,23 @@ ares_bool_t ares_str_isnum(const char *str)
}

for (i = 0; str[i] != 0; i++) {
if (str[i] < '0' || str[i] > '9') {
if (!ares_isdigit(str[i])) {
return ARES_FALSE;
}
}
return ARES_TRUE;
}

ares_bool_t ares_str_isalnum(const char *str)
{
size_t i;

if (str == NULL || *str == 0) {
return ARES_FALSE;
}

for (i = 0; str[i] != 0; i++) {
if (!ares_isdigit(str[i]) && !ares_isalpha(str[i])) {
return ARES_FALSE;
}
}
Expand Down
50 changes: 42 additions & 8 deletions src/lib/util/ares_uri.c
Original file line number Diff line number Diff line change
Expand Up @@ -416,16 +416,43 @@ ares_status_t ares_uri_set_host(ares_uri_t *uri, const char *host)
{
struct ares_addr addr;
size_t addrlen;
char hoststr[256];
char *ll_scope;

if (uri == NULL || ares_strlen(host) == 0) {
if (uri == NULL || ares_strlen(host) == 0 ||
ares_strlen(host) >= sizeof(hoststr)) {
return ARES_EFORMERR;
}

ares_strcpy(hoststr, host, sizeof(hoststr));

/* Look for '%' which could be a link-local scope for ipv6 addresses and
* parse it off */
ll_scope = strchr(hoststr, '%');
if (ll_scope != NULL) {
*ll_scope = 0;
ll_scope++;
if (!ares_str_isalnum(ll_scope)) {
return ARES_EBADNAME;
}
}

/* If its an IP address, normalize it */
memset(&addr, 0, sizeof(addr));
addr.family = AF_UNSPEC;
if (ares_dns_pton(host, &addr, &addrlen) != NULL) {
ares_inet_ntop(addr.family, &addr.addr, uri->host, sizeof(uri->host));
if (ares_dns_pton(hoststr, &addr, &addrlen) != NULL) {
char ipaddr[256];
ares_inet_ntop(addr.family, &addr.addr, ipaddr, sizeof(ipaddr));
/* Only IPv6 is allowed to have a scope */
if (ll_scope != NULL && addr.family != AF_INET6) {
return ARES_EBADNAME;
}

if (ll_scope != NULL) {
snprintf(uri->host, sizeof(uri->host), "%s%%%s", ipaddr, ll_scope);
} else {
ares_strcpy(uri->host, ipaddr, sizeof(uri->host));
}
return ARES_SUCCESS;
}

Expand Down Expand Up @@ -647,8 +674,6 @@ static ares_status_t ares_uri_write_scheme(ares_uri_t *uri, ares_buf_t *buf)
static ares_status_t ares_uri_write_authority(ares_uri_t *uri, ares_buf_t *buf)
{
ares_status_t status;
struct ares_addr addr;
size_t addrlen;
ares_bool_t is_ipv6 = ARES_FALSE;

if (ares_strlen(uri->username)) {
Expand Down Expand Up @@ -678,10 +703,19 @@ static ares_status_t ares_uri_write_authority(ares_uri_t *uri, ares_buf_t *buf)
}

/* We need to write ipv6 addresses with [ ] */
memset(&addr, 0, sizeof(addr));
addr.family = AF_INET6;
if (ares_dns_pton(uri->host, &addr, &addrlen) != NULL) {
if (strchr(uri->host, '%') != NULL) {
/* If we have a % in the name, it must be ipv6 link local scope, so we
* don't need to check anything else */
is_ipv6 = ARES_TRUE;
} else {
/* Parse the host to see if it is an ipv6 address */
struct ares_addr addr;
size_t addrlen;
memset(&addr, 0, sizeof(addr));
addr.family = AF_INET6;
if (ares_dns_pton(uri->host, &addr, &addrlen) != NULL) {
is_ipv6 = ARES_TRUE;
}
}

if (is_ipv6) {
Expand Down
3 changes: 2 additions & 1 deletion src/lib/util/ares_uri.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ const char *ares_uri_get_password(ares_uri_t *uri);
*/
ares_status_t ares_uri_set_host(ares_uri_t *uri, const char *host);

/*! Retrieve the currently configured host (or ip address).
/*! Retrieve the currently configured host (or ip address). IPv6 addresses
* May include a link-local scope (e.g. fe80::b542:84df:1719:65e3%en0).
*
* \param[in] uri Initialized URI object
* \return string containing host, maybe NULL if not set.
Expand Down
29 changes: 29 additions & 0 deletions test/ares-test-fuzz-name.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@

int LLVMFuzzerTestOneInput(const unsigned char *data, unsigned long size);

/* Fuzzing on a query name isn't very useful as its already fuzzed as part
* of the normal fuzzing operations. So we'll disable this by default and
* instead use this same fuzzer to validate our URI scheme parsers accessed
* via ares_set_servers_csv() */
#ifdef USE_LEGACY_FUZZERS
/* Entrypoint for Clang's libfuzzer, exercising query creation. */
int LLVMFuzzerTestOneInput(const unsigned char *data, unsigned long size)
{
Expand All @@ -48,3 +53,27 @@ int LLVMFuzzerTestOneInput(const unsigned char *data, unsigned long size)
free(name);
return 0;
}

#else

int LLVMFuzzerTestOneInput(const unsigned char *data, unsigned long size)
{
ares_channel_t *channel = NULL;
char *csv;

ares_library_init(ARES_LIB_INIT_ALL);
ares_init(&channel);

/* Need to null-term data */
csv = malloc(size + 1);
memcpy(csv, data, size);
csv[size] = '\0';
ares_set_servers_csv(channel, csv);
free(csv);

ares_destroy(channel);
ares_library_cleanup();

return 0;
}
#endif
2 changes: 1 addition & 1 deletion test/ares-test-fuzz.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

int LLVMFuzzerTestOneInput(const unsigned char *data, unsigned long size);

#ifdef USE_LEGACY_PARSERS
#ifdef USE_LEGACY_FUZZERS

/* This implementation calls the legacy c-ares parsers, which historically
* all used different logic and parsing. As of c-ares 1.21.0 these are
Expand Down
1 change: 1 addition & 0 deletions test/ares-test-internal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ TEST_F(LibraryTest, URI) {
{ ARES_TRUE, "https://www.example.com?key=%41%61%32%2D%2E%5f%7e%2F%3F%21%24%27%28%29%2a%2C%3b%3a%40", "https://www.example.com?key=Aa2-._~/?!$'()*,;:@" },
{ ARES_TRUE, "dns+tls://192.168.1.1:53", NULL },
{ ARES_TRUE, "dns+tls://[fe80::1]:53", NULL },
{ ARES_TRUE, "dns://[fe80::b542:84df:1719:65e3%en0]", NULL },
{ ARES_TRUE, "dns+tls://[fe80:00::00:1]:53", "dns+tls://[fe80::1]:53" },
{ ARES_TRUE, "d.n+s-tls://www.example.com", NULL },
{ ARES_FALSE, "dns*tls://www.example.com", NULL },
Expand Down

0 comments on commit defa847

Please sign in to comment.