summaryrefslogtreecommitdiff
path: root/ares_options.c
diff options
context:
space:
mode:
authorKeith Shaw <github@keithws.net>2013-05-09 17:45:37 -0700
committerDaniel Stenberg <daniel@haxx.se>2013-05-12 15:23:43 +0200
commit09be3edf3a0649435c5222c14a987f2486ade6fe (patch)
tree7a719959988037d130c19d1bdef6205f03e51183 /ares_options.c
parent03e2fd085c28a087870543b447b5cc45efb280a9 (diff)
downloadc-ares-09be3edf3a0649435c5222c14a987f2486ade6fe.tar.gz
c-ares-09be3edf3a0649435c5222c14a987f2486ade6fe.tar.bz2
c-ares-09be3edf3a0649435c5222c14a987f2486ade6fe.zip
ares_set_servers_csv: fixed IPv6 address parsing
Fixed bug that caused the last part of an IPv6 address to be parsed as the port number when the last part is all numeric.
Diffstat (limited to 'ares_options.c')
-rw-r--r--ares_options.c60
1 files changed, 45 insertions, 15 deletions
diff --git a/ares_options.c b/ares_options.c
index 5466cb2..76d82df 100644
--- a/ares_options.c
+++ b/ares_options.c
@@ -132,6 +132,7 @@ int ares_set_servers(ares_channel channel,
}
/* Incomming string format: host[:port][,host[:port]]... */
+/* IPv6 addresses with ports require square brackets [fe80::1%lo0]:53 */
int ares_set_servers_csv(ares_channel channel,
const char* _csv)
{
@@ -139,6 +140,7 @@ int ares_set_servers_csv(ares_channel channel,
char* csv = NULL;
char* ptr;
char* start_host;
+ int cc = 0;
int rv = ARES_SUCCESS;
struct ares_addr_node *servers = NULL;
struct ares_addr_node *last = NULL;
@@ -164,28 +166,53 @@ int ares_set_servers_csv(ares_channel channel,
start_host = csv;
for (ptr = csv; *ptr; ptr++) {
- if (*ptr == ',') {
+ if (*ptr == ':') {
+ /* count colons to determine if we have an IPv6 number or IPv4 with
+ port */
+ cc++;
+ }
+ else if (*ptr == '[') {
+ /* move start_host if an open square bracket is found wrapping an IPv6
+ address */
+ start_host = ptr + 1;
+ }
+ else if (*ptr == ',') {
char* pp = ptr - 1;
+ char* p = ptr;
struct in_addr in4;
struct ares_in6_addr in6;
struct ares_addr_node *s = NULL;
*ptr = 0; /* null terminate host:port string */
- /* Got an entry..see if port was specified. */
- while (pp > start_host) {
- if (*pp == ':')
- break; /* yes */
- if (!ISDIGIT(*pp)) {
- /* Found end of digits before we found :, so wasn't a port */
- pp = ptr;
- break;
+ /* Got an entry..see if the port was specified. */
+ if (cc > 0) {
+ while (pp > start_host) {
+ /* a single close square bracket followed by a colon, ']:' indicates
+ an IPv6 address with port */
+ if ((*pp == ']') && (*p == ':'))
+ break; /* found port */
+ /* a single colon, ':' indicates an IPv4 address with port */
+ if ((*pp == ':') && (cc == 1))
+ break; /* found port */
+ if (!(ISDIGIT(*pp) || (*pp == ':'))) {
+ /* Found end of digits before we found :, so wasn't a port */
+ /* must allow ':' for IPv6 case of ']:' indicates we found a port */
+ pp = p = ptr;
+ break;
+ }
+ pp--;
+ p--;
+ }
+ if ((pp != start_host) && ((pp + 1) < ptr)) {
+ /* Found it. Parse over the port number */
+ /* when an IPv6 address is wrapped with square brackets the port
+ starts at pp + 2 */
+ if (*pp == ']')
+ p++; /* move p before ':' */
+ /* p will point to the start of the port */
+ (void)strtol(p, NULL, 10);
+ *pp = 0; /* null terminate host */
}
- pp--;
- }
- if ((pp != start_host) && ((pp + 1) < ptr)) {
- /* Found it. Parse over the port number */
- (void)strtol(pp + 1, NULL, 10);
- *pp = 0; /* null terminate host */
}
/* resolve host, try ipv4 first, rslt is in network byte order */
rv = ares_inet_pton(AF_INET, start_host, &in4);
@@ -221,6 +248,8 @@ int ares_set_servers_csv(ares_channel channel,
s->next = NULL;
if (last) {
last->next = s;
+ /* need to move last to maintain the linked list */
+ last = last->next;
}
else {
servers = s;
@@ -230,6 +259,7 @@ int ares_set_servers_csv(ares_channel channel,
/* Set up for next one */
start_host = ptr + 1;
+ cc = 0;
}
}