• 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_private.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_inet_net_pton.h"
47 
48 static void   sort_addresses(const struct hostent  *host,
49                              const struct apattern *sortlist, size_t nsort);
50 static void   sort6_addresses(const struct hostent  *host,
51                               const struct apattern *sortlist, size_t nsort);
52 static size_t get_address_index(const struct in_addr  *addr,
53                                 const struct apattern *sortlist, size_t nsort);
54 static size_t get6_address_index(const struct ares_in6_addr *addr,
55                                  const struct apattern *sortlist, size_t nsort);
56 
57 struct host_query {
58   ares_host_callback callback;
59   void              *arg;
60   ares_channel_t    *channel;
61 };
62 
ares_gethostbyname_callback(void * arg,int status,int timeouts,struct ares_addrinfo * result)63 static void ares_gethostbyname_callback(void *arg, int status, int timeouts,
64                                         struct ares_addrinfo *result)
65 {
66   struct hostent    *hostent  = NULL;
67   struct host_query *ghbn_arg = arg;
68 
69   if (status == ARES_SUCCESS) {
70     status = (int)ares_addrinfo2hostent(result, AF_UNSPEC, &hostent);
71   }
72 
73   /* addrinfo2hostent will only return ENODATA if there are no addresses _and_
74    * no cname/aliases.  However, gethostbyname will return ENODATA even if there
75    * is cname/alias data */
76   if (status == ARES_SUCCESS && hostent &&
77       (!hostent->h_addr_list || !hostent->h_addr_list[0])) {
78     status = ARES_ENODATA;
79   }
80 
81   if (status == ARES_SUCCESS && ghbn_arg->channel->nsort && hostent) {
82     if (hostent->h_addrtype == AF_INET6) {
83       sort6_addresses(hostent, ghbn_arg->channel->sortlist,
84                       ghbn_arg->channel->nsort);
85     }
86     if (hostent->h_addrtype == AF_INET) {
87       sort_addresses(hostent, ghbn_arg->channel->sortlist,
88                      ghbn_arg->channel->nsort);
89     }
90   }
91 
92   ghbn_arg->callback(ghbn_arg->arg, status, timeouts, hostent);
93 
94   ares_freeaddrinfo(result);
95   ares_free(ghbn_arg);
96   ares_free_hostent(hostent);
97 }
98 
ares_gethostbyname(ares_channel_t * channel,const char * name,int family,ares_host_callback callback,void * arg)99 void ares_gethostbyname(ares_channel_t *channel, const char *name, int family,
100                         ares_host_callback callback, void *arg)
101 {
102   struct ares_addrinfo_hints hints;
103   struct host_query         *ghbn_arg;
104 
105   if (!callback) {
106     return;
107   }
108 
109   memset(&hints, 0, sizeof(hints));
110   hints.ai_flags  = ARES_AI_CANONNAME;
111   hints.ai_family = family;
112 
113   ghbn_arg = ares_malloc(sizeof(*ghbn_arg));
114   if (!ghbn_arg) {
115     callback(arg, ARES_ENOMEM, 0, NULL);
116     return;
117   }
118 
119   ghbn_arg->callback = callback;
120   ghbn_arg->arg      = arg;
121   ghbn_arg->channel  = channel;
122 
123   /* NOTE: ares_getaddrinfo() locks the channel, we don't use the channel
124    *       outside so no need to lock */
125   ares_getaddrinfo(channel, name, NULL, &hints, ares_gethostbyname_callback,
126                    ghbn_arg);
127 }
128 
sort_addresses(const struct hostent * host,const struct apattern * sortlist,size_t nsort)129 static void sort_addresses(const struct hostent  *host,
130                            const struct apattern *sortlist, size_t nsort)
131 {
132   struct in_addr a1;
133   struct in_addr a2;
134   int            i1;
135   int            i2;
136   size_t         ind1;
137   size_t         ind2;
138 
139   /* This is a simple insertion sort, not optimized at all.  i1 walks
140    * through the address list, with the loop invariant that everything
141    * to the left of i1 is sorted.  In the loop body, the value at i1 is moved
142    * back through the list (via i2) until it is in sorted order.
143    */
144   for (i1 = 0; host->h_addr_list[i1]; i1++) {
145     memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr));
146     ind1 = get_address_index(&a1, sortlist, nsort);
147     for (i2 = i1 - 1; i2 >= 0; i2--) {
148       memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr));
149       ind2 = get_address_index(&a2, sortlist, nsort);
150       if (ind2 <= ind1) {
151         break;
152       }
153       memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr));
154     }
155     memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr));
156   }
157 }
158 
159 /* Find the first entry in sortlist which matches addr.  Return nsort
160  * if none of them match.
161  */
get_address_index(const struct in_addr * addr,const struct apattern * sortlist,size_t nsort)162 static size_t get_address_index(const struct in_addr  *addr,
163                                 const struct apattern *sortlist, size_t nsort)
164 {
165   size_t           i;
166   struct ares_addr aaddr;
167 
168   memset(&aaddr, 0, sizeof(aaddr));
169   aaddr.family = AF_INET;
170   memcpy(&aaddr.addr.addr4, addr, 4);
171 
172   for (i = 0; i < nsort; i++) {
173     if (sortlist[i].addr.family != AF_INET) {
174       continue;
175     }
176 
177     if (ares_subnet_match(&aaddr, &sortlist[i].addr, sortlist[i].mask)) {
178       break;
179     }
180   }
181 
182   return i;
183 }
184 
sort6_addresses(const struct hostent * host,const struct apattern * sortlist,size_t nsort)185 static void sort6_addresses(const struct hostent  *host,
186                             const struct apattern *sortlist, size_t nsort)
187 {
188   struct ares_in6_addr a1;
189   struct ares_in6_addr a2;
190   int                  i1;
191   int                  i2;
192   size_t               ind1;
193   size_t               ind2;
194 
195   /* This is a simple insertion sort, not optimized at all.  i1 walks
196    * through the address list, with the loop invariant that everything
197    * to the left of i1 is sorted.  In the loop body, the value at i1 is moved
198    * back through the list (via i2) until it is in sorted order.
199    */
200   for (i1 = 0; host->h_addr_list[i1]; i1++) {
201     memcpy(&a1, host->h_addr_list[i1], sizeof(struct ares_in6_addr));
202     ind1 = get6_address_index(&a1, sortlist, nsort);
203     for (i2 = i1 - 1; i2 >= 0; i2--) {
204       memcpy(&a2, host->h_addr_list[i2], sizeof(struct ares_in6_addr));
205       ind2 = get6_address_index(&a2, sortlist, nsort);
206       if (ind2 <= ind1) {
207         break;
208       }
209       memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct ares_in6_addr));
210     }
211     memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr));
212   }
213 }
214 
215 /* Find the first entry in sortlist which matches addr.  Return nsort
216  * if none of them match.
217  */
get6_address_index(const struct ares_in6_addr * addr,const struct apattern * sortlist,size_t nsort)218 static size_t get6_address_index(const struct ares_in6_addr *addr,
219                                  const struct apattern *sortlist, size_t nsort)
220 {
221   size_t           i;
222   struct ares_addr aaddr;
223 
224   memset(&aaddr, 0, sizeof(aaddr));
225   aaddr.family = AF_INET6;
226   memcpy(&aaddr.addr.addr6, addr, 16);
227 
228   for (i = 0; i < nsort; i++) {
229     if (sortlist[i].addr.family != AF_INET6) {
230       continue;
231     }
232 
233     if (ares_subnet_match(&aaddr, &sortlist[i].addr, sortlist[i].mask)) {
234       break;
235     }
236   }
237   return i;
238 }
239 
ares_hostent_localhost(const char * name,int family,struct hostent ** host_out)240 static ares_status_t ares_hostent_localhost(const char *name, int family,
241                                             struct hostent **host_out)
242 {
243   ares_status_t              status;
244   struct ares_addrinfo      *ai = NULL;
245   struct ares_addrinfo_hints hints;
246 
247   memset(&hints, 0, sizeof(hints));
248   hints.ai_family = family;
249 
250   ai = ares_malloc_zero(sizeof(*ai));
251   if (ai == NULL) {
252     status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
253     goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
254   }
255 
256   status = ares_addrinfo_localhost(name, 0, &hints, ai);
257   if (status != ARES_SUCCESS) {
258     goto done; /* LCOV_EXCL_LINE: OutOfMemory */
259   }
260 
261   status = ares_addrinfo2hostent(ai, family, host_out);
262   if (status != ARES_SUCCESS) {
263     goto done; /* LCOV_EXCL_LINE: OutOfMemory */
264   }
265 
266 done:
267   ares_freeaddrinfo(ai);
268   return status;
269 }
270 
271 /* I really have no idea why this is exposed as a public function, but since
272  * 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)273 static ares_status_t ares_gethostbyname_file_int(ares_channel_t *channel,
274                                                  const char *name, int family,
275                                                  struct hostent **host)
276 {
277   const ares_hosts_entry_t *entry;
278   ares_status_t             status;
279 
280   /* We only take the channel to ensure that ares_init() been called. */
281   if (channel == NULL || name == NULL || host == NULL) {
282     /* Anything will do, really.  This seems fine, and is consistent with
283        other error cases. */
284     if (host != NULL) {
285       *host = NULL;
286     }
287     return ARES_ENOTFOUND;
288   }
289 
290   *host  = NULL;
291 
292   /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
293   if (ares_is_onion_domain(name)) {
294     return ARES_ENOTFOUND;
295   }
296 
297   status = ares_hosts_search_host(channel, ARES_FALSE, name, &entry);
298   if (status != ARES_SUCCESS) {
299     goto done;
300   }
301 
302   status = ares_hosts_entry_to_hostent(entry, family, host);
303   if (status != ARES_SUCCESS) {
304     goto done; /* LCOV_EXCL_LINE: OutOfMemory */
305   }
306 
307 done:
308   /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries
309    * SHOULD recognize localhost names as special and SHOULD always return the
310    * IP loopback address for address queries".
311    * We will also ignore ALL errors when trying to resolve localhost, such
312    * as permissions errors reading /etc/hosts or a malformed /etc/hosts.
313    *
314    * Also, just because the query itself returned success from /etc/hosts
315    * lookup doesn't mean it returned everything it needed to for all requested
316    * address families. As long as we're not on a critical out of memory
317    * condition pass it through to fill in any other address classes. */
318   if (status != ARES_ENOMEM && ares_is_localhost(name)) {
319     return ares_hostent_localhost(name, family, host);
320   }
321 
322   return status;
323 }
324 
ares_gethostbyname_file(ares_channel_t * channel,const char * name,int family,struct hostent ** host)325 int ares_gethostbyname_file(ares_channel_t *channel, const char *name,
326                             int family, struct hostent **host)
327 {
328   ares_status_t status;
329   if (channel == NULL) {
330     return ARES_ENOTFOUND;
331   }
332 
333   ares_channel_lock(channel);
334   status = ares_gethostbyname_file_int(channel, name, family, host);
335   ares_channel_unlock(channel);
336   return (int)status;
337 }
338