mirror of https://github.com/c-ares/c-ares.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
264 lines
7.1 KiB
264 lines
7.1 KiB
/* Copyright (C) 2019 by Andrew Selivanov |
|
* |
|
* Permission to use, copy, modify, and distribute this |
|
* software and its documentation for any purpose and without |
|
* fee is hereby granted, provided that the above copyright |
|
* notice appear in all copies and that both that copyright |
|
* notice and this permission notice appear in supporting |
|
* documentation, and that the name of M.I.T. not be used in |
|
* advertising or publicity pertaining to distribution of the |
|
* software without specific, written prior permission. |
|
* M.I.T. makes no representations about the suitability of |
|
* this software for any purpose. It is provided "as is" |
|
* without express or implied warranty. |
|
*/ |
|
|
|
#include "ares_setup.h" |
|
|
|
#ifdef HAVE_NETINET_IN_H |
|
# include <netinet/in.h> |
|
#endif |
|
#ifdef HAVE_NETDB_H |
|
# include <netdb.h> |
|
#endif |
|
#ifdef HAVE_ARPA_INET_H |
|
# include <arpa/inet.h> |
|
#endif |
|
|
|
#include "ares.h" |
|
#include "ares_inet_net_pton.h" |
|
#include "ares_nowarn.h" |
|
#include "ares_private.h" |
|
|
|
#define MAX_ALIASES 40 |
|
|
|
int ares__readaddrinfo(FILE *fp, |
|
const char *name, |
|
unsigned short port, |
|
const struct ares_addrinfo_hints *hints, |
|
struct ares_addrinfo *ai) |
|
{ |
|
char *line = NULL, *p, *q; |
|
char *txtaddr, *txthost, *txtalias; |
|
char *aliases[MAX_ALIASES]; |
|
unsigned int i, alias_count; |
|
int status; |
|
size_t linesize; |
|
ares_sockaddr addr; |
|
struct ares_addrinfo_cname *cname = NULL, *cnames = NULL; |
|
struct ares_addrinfo_node *node = NULL, *nodes = NULL; |
|
int match_with_alias, match_with_canonical; |
|
int want_cname = hints->ai_flags & ARES_AI_CANONNAME; |
|
|
|
/* Validate family */ |
|
switch (hints->ai_family) { |
|
case AF_INET: |
|
case AF_INET6: |
|
case AF_UNSPEC: |
|
break; |
|
default: |
|
return ARES_EBADFAMILY; |
|
} |
|
|
|
|
|
while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) |
|
{ |
|
match_with_alias = 0; |
|
match_with_canonical = 0; |
|
alias_count = 0; |
|
/* Trim line comment. */ |
|
p = line; |
|
while (*p && (*p != '#')) |
|
p++; |
|
*p = '\0'; |
|
|
|
/* Trim trailing whitespace. */ |
|
q = p - 1; |
|
while ((q >= line) && ISSPACE(*q)) |
|
q--; |
|
*++q = '\0'; |
|
|
|
/* Skip leading whitespace. */ |
|
p = line; |
|
while (*p && ISSPACE(*p)) |
|
p++; |
|
if (!*p) |
|
/* Ignore line if empty. */ |
|
continue; |
|
|
|
/* Pointer to start of IPv4 or IPv6 address part. */ |
|
txtaddr = p; |
|
|
|
/* Advance past address part. */ |
|
while (*p && !ISSPACE(*p)) |
|
p++; |
|
if (!*p) |
|
/* Ignore line if reached end of line. */ |
|
continue; |
|
|
|
/* Null terminate address part. */ |
|
*p = '\0'; |
|
|
|
/* Advance to host name */ |
|
p++; |
|
while (*p && ISSPACE(*p)) |
|
p++; |
|
if (!*p) |
|
/* Ignore line if reached end of line. */ |
|
continue; /* LCOV_EXCL_LINE: trailing whitespace already stripped */ |
|
|
|
/* Pointer to start of host name. */ |
|
txthost = p; |
|
|
|
/* Advance past host name. */ |
|
while (*p && !ISSPACE(*p)) |
|
p++; |
|
|
|
/* Pointer to start of first alias. */ |
|
txtalias = NULL; |
|
if (*p) |
|
{ |
|
q = p + 1; |
|
while (*q && ISSPACE(*q)) |
|
q++; |
|
if (*q) |
|
txtalias = q; |
|
} |
|
|
|
/* Null terminate host name. */ |
|
*p = '\0'; |
|
|
|
/* Find out if host name matches with canonical host name. */ |
|
if (strcasecmp(txthost, name) == 0) |
|
{ |
|
match_with_canonical = 1; |
|
} |
|
|
|
/* Find out if host name matches with one of the aliases. */ |
|
while (txtalias) |
|
{ |
|
p = txtalias; |
|
while (*p && !ISSPACE(*p)) |
|
p++; |
|
q = p; |
|
while (*q && ISSPACE(*q)) |
|
q++; |
|
*p = '\0'; |
|
if (strcasecmp(txtalias, name) == 0) |
|
{ |
|
match_with_alias = 1; |
|
if (!want_cname) |
|
break; |
|
} |
|
if (alias_count < MAX_ALIASES) |
|
{ |
|
aliases[alias_count++] = txtalias; |
|
} |
|
txtalias = *q ? q : NULL; |
|
} |
|
|
|
/* Try next line if host does not match. */ |
|
if (!match_with_alias && !match_with_canonical) |
|
{ |
|
continue; |
|
} |
|
|
|
/* Zero-out 'addr' struct, as there are members that we may not set, especially |
|
* for ipv6. We don't want garbage data */ |
|
memset(&addr, 0, sizeof(addr)); |
|
|
|
/* |
|
* Convert address string to network address for the requested families. |
|
* Actual address family possible values are AF_INET and AF_INET6 only. |
|
*/ |
|
if ((hints->ai_family == AF_INET) || (hints->ai_family == AF_UNSPEC)) |
|
{ |
|
addr.sa4.sin_port = htons(port); |
|
if (ares_inet_pton(AF_INET, txtaddr, &addr.sa4.sin_addr) > 0) |
|
{ |
|
node = ares__append_addrinfo_node(&nodes); |
|
if(!node) |
|
{ |
|
goto enomem; |
|
} |
|
|
|
node->ai_family = addr.sa.sa_family = AF_INET; |
|
node->ai_addrlen = sizeof(addr.sa4); |
|
node->ai_addr = ares_malloc(sizeof(addr.sa4)); |
|
if (!node->ai_addr) |
|
{ |
|
goto enomem; |
|
} |
|
memcpy(node->ai_addr, &addr.sa4, sizeof(addr.sa4)); |
|
} |
|
} |
|
if ((hints->ai_family == AF_INET6) || (hints->ai_family == AF_UNSPEC)) |
|
{ |
|
addr.sa6.sin6_port = htons(port); |
|
if (ares_inet_pton(AF_INET6, txtaddr, &addr.sa6.sin6_addr) > 0) |
|
{ |
|
node = ares__append_addrinfo_node(&nodes); |
|
if (!node) |
|
{ |
|
goto enomem; |
|
} |
|
|
|
node->ai_family = addr.sa.sa_family = AF_INET6; |
|
node->ai_addrlen = sizeof(addr.sa6); |
|
node->ai_addr = ares_malloc(sizeof(addr.sa6)); |
|
if (!node->ai_addr) |
|
{ |
|
goto enomem; |
|
} |
|
memcpy(node->ai_addr, &addr.sa6, sizeof(addr.sa6)); |
|
} |
|
} |
|
if (!node) |
|
/* Ignore line if invalid address string for the requested family. */ |
|
continue; |
|
|
|
if (want_cname) |
|
{ |
|
for (i = 0; i < alias_count; ++i) |
|
{ |
|
cname = ares__append_addrinfo_cname(&cnames); |
|
if (!cname) |
|
{ |
|
goto enomem; |
|
} |
|
cname->alias = ares_strdup(aliases[i]); |
|
cname->name = ares_strdup(txthost); |
|
} |
|
/* No aliases, cname only. */ |
|
if(!alias_count) |
|
{ |
|
cname = ares__append_addrinfo_cname(&cnames); |
|
if (!cname) |
|
{ |
|
goto enomem; |
|
} |
|
cname->name = ares_strdup(txthost); |
|
} |
|
} |
|
} |
|
|
|
/* Last read failed. */ |
|
if (status == ARES_ENOMEM) |
|
{ |
|
goto enomem; |
|
} |
|
|
|
/* Free line buffer. */ |
|
ares_free(line); |
|
|
|
ares__addrinfo_cat_cnames(&ai->cnames, cnames); |
|
ares__addrinfo_cat_nodes(&ai->nodes, nodes); |
|
|
|
return node ? ARES_SUCCESS : ARES_ENOTFOUND; |
|
|
|
enomem: |
|
ares_free(line); |
|
ares__freeaddrinfo_cnames(cnames); |
|
ares__freeaddrinfo_nodes(nodes); |
|
return ARES_ENOMEM; |
|
}
|
|
|