summaryrefslogtreecommitdiff
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
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.
-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;
}
}