ares_set_servers_csv: fixed IPv6 address parsing
[platform/upstream/c-ares.git] / ares_options.c
1
2 /* Copyright 1998 by the Massachusetts Institute of Technology.
3  * Copyright (C) 2008-2013 by Daniel Stenberg
4  *
5  * Permission to use, copy, modify, and distribute this
6  * software and its documentation for any purpose and without
7  * fee is hereby granted, provided that the above copyright
8  * notice appear in all copies and that both that copyright
9  * notice and this permission notice appear in supporting
10  * documentation, and that the name of M.I.T. not be used in
11  * advertising or publicity pertaining to distribution of the
12  * software without specific, written prior permission.
13  * M.I.T. makes no representations about the suitability of
14  * this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  */
17
18
19 #include "ares_setup.h"
20
21 #ifdef HAVE_ARPA_INET_H
22 #  include <arpa/inet.h>
23 #endif
24
25 #include "ares.h"
26 #include "ares_data.h"
27 #include "ares_inet_net_pton.h"
28 #include "ares_private.h"
29
30
31 int ares_get_servers(ares_channel channel,
32                      struct ares_addr_node **servers)
33 {
34   struct ares_addr_node *srvr_head = NULL;
35   struct ares_addr_node *srvr_last = NULL;
36   struct ares_addr_node *srvr_curr;
37   int status = ARES_SUCCESS;
38   int i;
39
40   if (!channel)
41     return ARES_ENODATA;
42
43   for (i = 0; i < channel->nservers; i++)
44     {
45       /* Allocate storage for this server node appending it to the list */
46       srvr_curr = ares_malloc_data(ARES_DATATYPE_ADDR_NODE);
47       if (!srvr_curr)
48         {
49           status = ARES_ENOMEM;
50           break;
51         }
52       if (srvr_last)
53         {
54           srvr_last->next = srvr_curr;
55         }
56       else
57         {
58           srvr_head = srvr_curr;
59         }
60       srvr_last = srvr_curr;
61
62       /* Fill this server node data */
63       srvr_curr->family = channel->servers[i].addr.family;
64       if (srvr_curr->family == AF_INET)
65         memcpy(&srvr_curr->addrV4, &channel->servers[i].addr.addrV4,
66                sizeof(srvr_curr->addrV4));
67       else
68         memcpy(&srvr_curr->addrV6, &channel->servers[i].addr.addrV6,
69                sizeof(srvr_curr->addrV6));
70     }
71
72   if (status != ARES_SUCCESS)
73     {
74       if (srvr_head)
75         {
76           ares_free_data(srvr_head);
77           srvr_head = NULL;
78         }
79     }
80
81   *servers = srvr_head;
82
83   return status;
84 }
85
86
87 int ares_set_servers(ares_channel channel,
88                      struct ares_addr_node *servers)
89 {
90   struct ares_addr_node *srvr;
91   int num_srvrs = 0;
92   int i;
93
94   if (ares_library_initialized() != ARES_SUCCESS)
95     return ARES_ENOTINITIALIZED;
96
97   if (!channel)
98     return ARES_ENODATA;
99
100   ares__destroy_servers_state(channel);
101
102   for (srvr = servers; srvr; srvr = srvr->next)
103     {
104       num_srvrs++;
105     }
106
107   if (num_srvrs > 0)
108     {
109       /* Allocate storage for servers state */
110       channel->servers = malloc(num_srvrs * sizeof(struct server_state));
111       if (!channel->servers)
112         {
113           return ARES_ENOMEM;
114         }
115       channel->nservers = num_srvrs;
116       /* Fill servers state address data */
117       for (i = 0, srvr = servers; srvr; i++, srvr = srvr->next)
118         {
119           channel->servers[i].addr.family = srvr->family;
120           if (srvr->family == AF_INET)
121             memcpy(&channel->servers[i].addr.addrV4, &srvr->addrV4,
122                    sizeof(srvr->addrV4));
123           else
124             memcpy(&channel->servers[i].addr.addrV6, &srvr->addrV6,
125                    sizeof(srvr->addrV6));
126         }
127       /* Initialize servers state remaining data */
128       ares__init_servers_state(channel);
129     }
130
131   return ARES_SUCCESS;
132 }
133
134 /* Incomming string format: host[:port][,host[:port]]... */
135 /* IPv6 addresses with ports require square brackets [fe80::1%lo0]:53 */
136 int ares_set_servers_csv(ares_channel channel,
137                          const char* _csv)
138 {
139   size_t i;
140   char* csv = NULL;
141   char* ptr;
142   char* start_host;
143   int cc = 0;
144   int rv = ARES_SUCCESS;
145   struct ares_addr_node *servers = NULL;
146   struct ares_addr_node *last = NULL;
147
148   if (ares_library_initialized() != ARES_SUCCESS)
149     return ARES_ENOTINITIALIZED;
150
151   if (!channel)
152     return ARES_ENODATA;
153
154   ares__destroy_servers_state(channel);
155
156   i = strlen(_csv);
157   if (i == 0)
158      return ARES_SUCCESS; /* blank all servers */
159
160   csv = malloc(i + 2);
161   strcpy(csv, _csv);
162   if (csv[i-1] != ',') { /* make parsing easier by ensuring ending ',' */
163     csv[i] = ',';
164     csv[i+1] = 0;
165   }
166
167   start_host = csv;
168   for (ptr = csv; *ptr; ptr++) {
169     if (*ptr == ':') {
170       /* count colons to determine if we have an IPv6 number or IPv4 with
171          port */
172       cc++;
173     }
174     else if (*ptr == '[') {
175       /* move start_host if an open square bracket is found wrapping an IPv6
176          address */
177       start_host = ptr + 1;
178     }
179     else if (*ptr == ',') {
180       char* pp = ptr - 1;
181       char* p = ptr;
182       struct in_addr in4;
183       struct ares_in6_addr in6;
184       struct ares_addr_node *s = NULL;
185
186       *ptr = 0; /* null terminate host:port string */
187       /* Got an entry..see if the port was specified. */
188       if (cc > 0) {
189         while (pp > start_host) {
190           /* a single close square bracket followed by a colon, ']:' indicates
191              an IPv6 address with port */
192           if ((*pp == ']') && (*p == ':'))
193             break; /* found port */
194           /* a single colon, ':' indicates an IPv4 address with port */
195           if ((*pp == ':') && (cc == 1))
196             break; /* found port */
197           if (!(ISDIGIT(*pp) || (*pp == ':'))) {
198             /* Found end of digits before we found :, so wasn't a port */
199             /* must allow ':' for IPv6 case of ']:' indicates we found a port */
200             pp = p = ptr;
201             break;
202           }
203           pp--;
204           p--;
205         }
206         if ((pp != start_host) && ((pp + 1) < ptr)) {
207           /* Found it. Parse over the port number */
208           /* when an IPv6 address is wrapped with square brackets the port
209              starts at pp + 2 */
210           if (*pp == ']')
211             p++; /* move p before ':' */
212           /* p will point to the start of the port */
213           (void)strtol(p, NULL, 10);
214           *pp = 0; /* null terminate host */
215         }
216       }
217       /* resolve host, try ipv4 first, rslt is in network byte order */
218       rv = ares_inet_pton(AF_INET, start_host, &in4);
219       if (!rv) {
220         /* Ok, try IPv6 then */
221         rv = ares_inet_pton(AF_INET6, start_host, &in6);
222         if (!rv) {
223           rv = ARES_EBADSTR;
224           goto out;
225         }
226         /* was ipv6, add new server */
227         s = malloc(sizeof(*s));
228         if (!s) {
229           rv = ARES_ENOMEM;
230           goto out;
231         }
232         s->family = AF_INET6;
233         memcpy(&s->addr, &in6, sizeof(struct ares_in6_addr));
234       }
235       else {
236         /* was ipv4, add new server */
237         s = malloc(sizeof(*s));
238         if (!s) {
239           rv = ARES_ENOMEM;
240           goto out;
241         }
242         s->family = AF_INET;
243         memcpy(&s->addr, &in4, sizeof(struct in_addr));
244       }
245       if (s) {
246         /* TODO:  Add port to ares_addr_node and assign it here. */
247
248         s->next = NULL;
249         if (last) {
250           last->next = s;
251           /* need to move last to maintain the linked list */
252           last = last->next;
253         }
254         else {
255           servers = s;
256           last = s;
257         }
258       }
259
260       /* Set up for next one */
261       start_host = ptr + 1;
262       cc = 0;
263     }
264   }
265
266   rv = ares_set_servers(channel, servers);
267
268   out:
269   if (csv)
270     free(csv);
271   while (servers) {
272     struct ares_addr_node *s = servers;
273     servers = servers->next;
274     free(s);
275   }
276
277   return rv;
278 }