ares_build.h.dist: enhance non-configure GCC ABI detection logic
[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 int ares_set_servers_csv(ares_channel channel,
136                          const char* _csv)
137 {
138   size_t i;
139   char* csv = NULL;
140   char* ptr;
141   char* start_host;
142   int rv = ARES_SUCCESS;
143   struct ares_addr_node *servers = NULL;
144   struct ares_addr_node *last = NULL;
145
146   if (ares_library_initialized() != ARES_SUCCESS)
147     return ARES_ENOTINITIALIZED;
148
149   if (!channel)
150     return ARES_ENODATA;
151
152   ares__destroy_servers_state(channel);
153
154   i = strlen(_csv);
155   if (i == 0)
156      return ARES_SUCCESS; /* blank all servers */
157
158   csv = malloc(i + 2);
159   strcpy(csv, _csv);
160   if (csv[i-1] != ',') { /* make parsing easier by ensuring ending ',' */
161     csv[i] = ',';
162     csv[i+1] = 0;
163   }
164
165   start_host = csv;
166   for (ptr = csv; *ptr; ptr++) {
167     if (*ptr == ',') {
168       char* pp = ptr - 1;
169       struct in_addr in4;
170       struct ares_in6_addr in6;
171       struct ares_addr_node *s = NULL;
172
173       *ptr = 0; /* null terminate host:port string */
174       /* Got an entry..see if port was specified. */
175       while (pp > start_host) {
176         if (*pp == ':')
177           break; /* yes */
178         if (!ISDIGIT(*pp)) {
179           /* Found end of digits before we found :, so wasn't a port */
180           pp = ptr;
181           break;
182         }
183         pp--;
184       }
185       if ((pp != start_host) && ((pp + 1) < ptr)) {
186         /* Found it. Parse over the port number */
187         (void)strtol(pp + 1, NULL, 10);
188         *pp = 0; /* null terminate host */
189       }
190       /* resolve host, try ipv4 first, rslt is in network byte order */
191       rv = ares_inet_pton(AF_INET, start_host, &in4);
192       if (!rv) {
193         /* Ok, try IPv6 then */
194         rv = ares_inet_pton(AF_INET6, start_host, &in6);
195         if (!rv) {
196           rv = ARES_EBADSTR;
197           goto out;
198         }
199         /* was ipv6, add new server */
200         s = malloc(sizeof(*s));
201         if (!s) {
202           rv = ARES_ENOMEM;
203           goto out;
204         }
205         s->family = AF_INET6;
206         memcpy(&s->addr, &in6, sizeof(struct ares_in6_addr));
207       }
208       else {
209         /* was ipv4, add new server */
210         s = malloc(sizeof(*s));
211         if (!s) {
212           rv = ARES_ENOMEM;
213           goto out;
214         }
215         s->family = AF_INET;
216         memcpy(&s->addr, &in4, sizeof(struct in_addr));
217       }
218       if (s) {
219         /* TODO:  Add port to ares_addr_node and assign it here. */
220
221         s->next = NULL;
222         if (last) {
223           last->next = s;
224         }
225         else {
226           servers = s;
227           last = s;
228         }
229       }
230
231       /* Set up for next one */
232       start_host = ptr + 1;
233     }
234   }
235
236   rv = ares_set_servers(channel, servers);
237
238   out:
239   if (csv)
240     free(csv);
241   while (servers) {
242     struct ares_addr_node *s = servers;
243     servers = servers->next;
244     free(s);
245   }
246
247   return rv;
248 }