• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* MIT License
2  *
3  * Copyright (c) 1998, 2011, 2013 Massachusetts Institute of Technology
4  * Copyright (c) The c-ares project and its contributors
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  *
25  * SPDX-License-Identifier: MIT
26  */
27 
28 #include "ares_setup.h"
29 
30 #ifdef HAVE_NETINET_IN_H
31 #  include <netinet/in.h>
32 #endif
33 #ifdef HAVE_NETDB_H
34 #  include <netdb.h>
35 #endif
36 #ifdef HAVE_ARPA_INET_H
37 #  include <arpa/inet.h>
38 #endif
39 
40 #include "ares_nameser.h"
41 
42 #ifdef HAVE_STRINGS_H
43 #  include <strings.h>
44 #endif
45 
46 #include "ares.h"
47 #include "ares_inet_net_pton.h"
48 #include "ares_platform.h"
49 #include "ares_private.h"
50 
51 static void   sort_addresses(const struct hostent  *host,
52                              const struct apattern *sortlist, size_t nsort);
53 static void   sort6_addresses(const struct hostent  *host,
54                               const struct apattern *sortlist, size_t nsort);
55 static size_t get_address_index(const struct in_addr  *addr,
56                                 const struct apattern *sortlist, size_t nsort);
57 static size_t get6_address_index(const struct ares_in6_addr *addr,
58                                  const struct apattern *sortlist, size_t nsort);
59 
60 struct host_query {
61   ares_host_callback callback;
62   void              *arg;
63   ares_channel_t    *channel;
64 };
65 
ares_gethostbyname_callback(void * arg,int status,int timeouts,struct ares_addrinfo * result)66 static void ares_gethostbyname_callback(void *arg, int status, int timeouts,
67                                         struct ares_addrinfo *result)
68 {
69   struct hostent    *hostent  = NULL;
70   struct host_query *ghbn_arg = arg;
71 
72   if (status == ARES_SUCCESS) {
73     status = (int)ares__addrinfo2hostent(result, AF_UNSPEC, &hostent);
74   }
75 
76   /* addrinfo2hostent will only return ENODATA if there are no addresses _and_
77    * no cname/aliases.  However, gethostbyname will return ENODATA even if there
78    * is cname/alias data */
79   if (status == ARES_SUCCESS && hostent &&
80       (!hostent->h_addr_list || !hostent->h_addr_list[0])) {
81     status = ARES_ENODATA;
82   }
83 
84   if (status == ARES_SUCCESS && ghbn_arg->channel->nsort && hostent) {
85     if (hostent->h_addrtype == AF_INET6) {
86       sort6_addresses(hostent, ghbn_arg->channel->sortlist,
87                       ghbn_arg->channel->nsort);
88     }
89     if (hostent->h_addrtype == AF_INET) {
90       sort_addresses(hostent, ghbn_arg->channel->sortlist,
91                      ghbn_arg->channel->nsort);
92     }
93   }
94 
95   ghbn_arg->callback(ghbn_arg->arg, status, timeouts, hostent);
96 
97   ares_freeaddrinfo(result);
98   ares_free(ghbn_arg);
99   ares_free_hostent(hostent);
100 }
101 
ares_gethostbyname(ares_channel_t * channel,const char * name,int family,ares_host_callback callback,void * arg)102 void ares_gethostbyname(ares_channel_t *channel, const char *name, int family,
103                         ares_host_callback callback, void *arg)
104 {
105   const struct ares_addrinfo_hints hints = { ARES_AI_CANONNAME, family, 0, 0 };
106   struct host_query               *ghbn_arg;
107 
108   if (!callback) {
109     return;
110   }
111 
112   ghbn_arg = ares_malloc(sizeof(*ghbn_arg));
113   if (!ghbn_arg) {
114     callback(arg, ARES_ENOMEM, 0, NULL);
115     return;
116   }
117 
118   ghbn_arg->callback = callback;
119   ghbn_arg->arg      = arg;
120   ghbn_arg->channel  = channel;
121 
122   /* NOTE: ares_getaddrinfo() locks the channel, we don't use the channel
123    *       outside so no need to lock */
124   ares_getaddrinfo(channel, name, NULL, &hints, ares_gethostbyname_callback,
125                    ghbn_arg);
126 }
127 
sort_addresses(const struct hostent * host,const struct apattern * sortlist,size_t nsort)128 static void sort_addresses(const struct hostent  *host,
129                            const struct apattern *sortlist, size_t nsort)
130 {
131   struct in_addr a1;
132   struct in_addr a2;
133   int            i1;
134   int            i2;
135   size_t         ind1;
136   size_t         ind2;
137 
138   /* This is a simple insertion sort, not optimized at all.  i1 walks
139    * through the address list, with the loop invariant that everything
140    * to the left of i1 is sorted.  In the loop body, the value at i1 is moved
141    * back through the list (via i2) until it is in sorted order.
142    */
143   for (i1 = 0; host->h_addr_list[i1]; i1++) {
144     memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr));
145     ind1 = get_address_index(&a1, sortlist, nsort);
146     for (i2 = i1 - 1; i2 >= 0; i2--) {
147       memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr));
148       ind2 = get_address_index(&a2, sortlist, nsort);
149       if (ind2 <= ind1) {
150         break;
151       }
152       memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr));
153     }
154     memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr));
155   }
156 }
157 
158 /* Find the first entry in sortlist which matches addr.  Return nsort
159  * if none of them match.
160  */
get_address_index(const struct in_addr * addr,const struct apattern * sortlist,size_t nsort)161 static size_t get_address_index(const struct in_addr  *addr,
162                                 const struct apattern *sortlist, size_t nsort)
163 {
164   size_t           i;
165   struct ares_addr aaddr;
166 
167   memset(&aaddr, 0, sizeof(aaddr));
168   aaddr.family = AF_INET;
169   memcpy(&aaddr.addr.addr4, addr, 4);
170 
171   for (i = 0; i < nsort; i++) {
172     if (sortlist[i].addr.family != AF_INET) {
173       continue;
174     }
175 
176     if (ares__subnet_match(&aaddr, &sortlist[i].addr, sortlist[i].mask)) {
177       break;
178     }
179   }
180 
181   return i;
182 }
183 
sort6_addresses(const struct hostent * host,const struct apattern * sortlist,size_t nsort)184 static void sort6_addresses(const struct hostent  *host,
185                             const struct apattern *sortlist, size_t nsort)
186 {
187   struct ares_in6_addr a1;
188   struct ares_in6_addr a2;
189   int                  i1;
190   int                  i2;
191   size_t               ind1;
192   size_t               ind2;
193 
194   /* This is a simple insertion sort, not optimized at all.  i1 walks
195    * through the address list, with the loop invariant that everything
196    * to the left of i1 is sorted.  In the loop body, the value at i1 is moved
197    * back through the list (via i2) until it is in sorted order.
198    */
199   for (i1 = 0; host->h_addr_list[i1]; i1++) {
200     memcpy(&a1, host->h_addr_list[i1], sizeof(struct ares_in6_addr));
201     ind1 = get6_address_index(&a1, sortlist, nsort);
202     for (i2 = i1 - 1; i2 >= 0; i2--) {
203       memcpy(&a2, host->h_addr_list[i2], sizeof(struct ares_in6_addr));
204       ind2 = get6_address_index(&a2, sortlist, nsort);
205       if (ind2 <= ind1) {
206         break;
207       }
208       memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct ares_in6_addr));
209     }
210     memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr));
211   }
212 }
213 
214 /* Find the first entry in sortlist which matches addr.  Return nsort
215  * if none of them match.
216  */
get6_address_index(const struct ares_in6_addr * addr,const struct apattern * sortlist,size_t nsort)217 static size_t get6_address_index(const struct ares_in6_addr *addr,
218                                  const struct apattern *sortlist, size_t nsort)
219 {
220   size_t           i;
221   struct ares_addr aaddr;
222 
223   memset(&aaddr, 0, sizeof(aaddr));
224   aaddr.family = AF_INET6;
225   memcpy(&aaddr.addr.addr6, addr, 16);
226 
227   for (i = 0; i < nsort; i++) {
228     if (sortlist[i].addr.family != AF_INET6) {
229       continue;
230     }
231 
232     if (ares__subnet_match(&aaddr, &sortlist[i].addr, sortlist[i].mask)) {
233       break;
234     }
235   }
236   return i;
237 }
238 
ares__hostent_localhost(const char * name,int family,struct hostent ** host_out)239 static ares_status_t ares__hostent_localhost(const char *name, int family,
240                                              struct hostent **host_out)
241 {
242   ares_status_t              status;
243   struct ares_addrinfo      *ai = NULL;
244   struct ares_addrinfo_hints hints;
245 
246   memset(&hints, 0, sizeof(hints));
247   hints.ai_family = family;
248 
249   ai = ares_malloc_zero(sizeof(*ai));
250   if (ai == NULL) {
251     status = ARES_ENOMEM;
252     goto done;
253   }
254 
255   status = ares__addrinfo_localhost(name, 0, &hints, ai);
256   if (status != ARES_SUCCESS) {
257     goto done;
258   }
259 
260   status = ares__addrinfo2hostent(ai, family, host_out);
261   if (status != ARES_SUCCESS) {
262     goto done;
263   }
264 
265 done:
266   ares_freeaddrinfo(ai);
267   return status;
268 }
269 
270 /* I really have no idea why this is exposed as a public function, but since
271  * it is, we can't kill this legacy function. */
ares_gethostbyname_file_int(ares_channel_t * channel,const char * name,int family,struct hostent ** host)272 static ares_status_t ares_gethostbyname_file_int(ares_channel_t *channel,
273                                                  const char *name, int family,
274                                                  struct hostent **host)
275 {
276   const ares_hosts_entry_t *entry;
277   ares_status_t             status;
278 
279   /* We only take the channel to ensure that ares_init() been called. */
280   if (channel == NULL || name == NULL || host == NULL) {
281     /* Anything will do, really.  This seems fine, and is consistent with
282        other error cases. */
283     if (host != NULL) {
284       *host = NULL;
285     }
286     return ARES_ENOTFOUND;
287   }
288 
289   /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
290   if (ares__is_onion_domain(name)) {
291     return ARES_ENOTFOUND;
292   }
293 
294   status = ares__hosts_search_host(channel, ARES_FALSE, name, &entry);
295   if (status != ARES_SUCCESS) {
296     goto done;
297   }
298 
299   status = ares__hosts_entry_to_hostent(entry, family, host);
300   if (status != ARES_SUCCESS) {
301     goto done;
302   }
303 
304 done:
305   /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries
306    * SHOULD recognize localhost names as special and SHOULD always return the
307    * IP loopback address for address queries".
308    * We will also ignore ALL errors when trying to resolve localhost, such
309    * as permissions errors reading /etc/hosts or a malformed /etc/hosts */
310   if (status != ARES_SUCCESS && status != ARES_ENOMEM &&
311       ares__is_localhost(name)) {
312     return ares__hostent_localhost(name, family, host);
313   }
314 
315   return status;
316 }
317 
ares_gethostbyname_file(ares_channel_t * channel,const char * name,int family,struct hostent ** host)318 int ares_gethostbyname_file(ares_channel_t *channel, const char *name,
319                             int family, struct hostent **host)
320 {
321   ares_status_t status;
322   if (channel == NULL) {
323     return ARES_ENOTFOUND;
324   }
325 
326   ares__channel_lock(channel);
327   status = ares_gethostbyname_file_int(channel, name, family, host);
328   ares__channel_unlock(channel);
329   return (int)status;
330 }
331