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