/src/tor/src/lib/net/resolve.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2003-2004, Roger Dingledine |
2 | | * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. |
3 | | * Copyright (c) 2007-2021, The Tor Project, Inc. */ |
4 | | /* See LICENSE for licensing information */ |
5 | | |
6 | | /** |
7 | | * \file resolve.c |
8 | | * \brief Use the libc DNS resolver to convert hostnames into addresses. |
9 | | **/ |
10 | | |
11 | | #define RESOLVE_PRIVATE |
12 | | #include "lib/net/resolve.h" |
13 | | |
14 | | #include "lib/net/address.h" |
15 | | #include "lib/net/inaddr.h" |
16 | | #include "lib/malloc/malloc.h" |
17 | | #include "lib/string/parse_int.h" |
18 | | #include "lib/string/util_string.h" |
19 | | |
20 | | #include "ext/siphash.h" |
21 | | #include "ext/ht.h" |
22 | | |
23 | | #ifdef HAVE_SYS_TYPES_H |
24 | | #include <sys/types.h> |
25 | | #endif |
26 | | #ifdef HAVE_SYS_SOCKET_H |
27 | | #include <sys/socket.h> |
28 | | #endif |
29 | | #ifdef HAVE_NETDB_H |
30 | | #include <netdb.h> |
31 | | #endif |
32 | | |
33 | | #include <string.h> |
34 | | |
35 | | /** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set |
36 | | * *<b>addr</b> to the proper IP address, in host byte order. Returns 0 |
37 | | * on success, -1 on failure; 1 on transient failure. |
38 | | * |
39 | | * This function only accepts IPv4 addresses. |
40 | | * |
41 | | * (This function exists because standard windows gethostbyname |
42 | | * doesn't treat raw IP addresses properly.) |
43 | | */ |
44 | | |
45 | | MOCK_IMPL(int, |
46 | | tor_lookup_hostname,(const char *name, uint32_t *addr)) |
47 | 0 | { |
48 | 0 | tor_addr_t myaddr; |
49 | 0 | int ret; |
50 | |
|
51 | 0 | if (BUG(!addr)) |
52 | 0 | return -1; |
53 | | |
54 | 0 | *addr = 0; |
55 | |
|
56 | 0 | if ((ret = tor_addr_lookup(name, AF_INET, &myaddr))) |
57 | 0 | return ret; |
58 | | |
59 | 0 | if (tor_addr_family(&myaddr) == AF_INET) { |
60 | 0 | *addr = tor_addr_to_ipv4h(&myaddr); |
61 | 0 | return ret; |
62 | 0 | } |
63 | | |
64 | 0 | return -1; |
65 | 0 | } |
66 | | |
67 | | #ifdef HAVE_GETADDRINFO |
68 | | |
69 | | /* Host lookup helper for tor_addr_lookup(), when getaddrinfo() is |
70 | | * available on this system. |
71 | | * |
72 | | * See tor_addr_lookup() for details. |
73 | | */ |
74 | | MOCK_IMPL(STATIC int, |
75 | | tor_addr_lookup_host_impl,(const char *name, |
76 | | uint16_t family, |
77 | | tor_addr_t *addr)) |
78 | 0 | { |
79 | 0 | int err; |
80 | 0 | struct addrinfo *res=NULL, *res_p; |
81 | 0 | struct addrinfo *best=NULL; |
82 | 0 | struct addrinfo hints; |
83 | 0 | int result = -1; |
84 | 0 | memset(&hints, 0, sizeof(hints)); |
85 | 0 | hints.ai_family = family; |
86 | 0 | hints.ai_socktype = SOCK_STREAM; |
87 | 0 | err = tor_getaddrinfo(name, NULL, &hints, &res); |
88 | | /* The check for 'res' here shouldn't be necessary, but it makes static |
89 | | * analysis tools happy. */ |
90 | 0 | if (!err && res) { |
91 | 0 | best = NULL; |
92 | 0 | for (res_p = res; res_p; res_p = res_p->ai_next) { |
93 | 0 | if (family == AF_UNSPEC) { |
94 | 0 | if (res_p->ai_family == AF_INET) { |
95 | 0 | best = res_p; |
96 | 0 | break; |
97 | 0 | } else if (res_p->ai_family == AF_INET6 && !best) { |
98 | 0 | best = res_p; |
99 | 0 | } |
100 | 0 | } else if (family == res_p->ai_family) { |
101 | 0 | best = res_p; |
102 | 0 | break; |
103 | 0 | } |
104 | 0 | } |
105 | 0 | if (!best) |
106 | 0 | best = res; |
107 | 0 | if (best->ai_family == AF_INET) { |
108 | 0 | tor_addr_from_in(addr, |
109 | 0 | &((struct sockaddr_in*)best->ai_addr)->sin_addr); |
110 | 0 | result = 0; |
111 | 0 | } else if (best->ai_family == AF_INET6) { |
112 | 0 | tor_addr_from_in6(addr, |
113 | 0 | &((struct sockaddr_in6*)best->ai_addr)->sin6_addr); |
114 | 0 | result = 0; |
115 | 0 | } |
116 | 0 | tor_freeaddrinfo(res); |
117 | 0 | return result; |
118 | 0 | } |
119 | 0 | return (err == EAI_AGAIN) ? 1 : -1; |
120 | 0 | } |
121 | | |
122 | | #else /* !defined(HAVE_GETADDRINFO) */ |
123 | | |
124 | | /* Host lookup helper for tor_addr_lookup(), which calls gethostbyname(). |
125 | | * Used when getaddrinfo() is not available on this system. |
126 | | * |
127 | | * See tor_addr_lookup() for details. |
128 | | */ |
129 | | MOCK_IMPL(STATIC int, |
130 | | tor_addr_lookup_host_impl,(const char *name, |
131 | | uint16_t family, |
132 | | tor_addr_t *addr)) |
133 | | { |
134 | | (void) family; |
135 | | struct hostent *ent; |
136 | | int err; |
137 | | #ifdef HAVE_GETHOSTBYNAME_R_6_ARG |
138 | | char buf[2048]; |
139 | | struct hostent hostent; |
140 | | int r; |
141 | | r = gethostbyname_r(name, &hostent, buf, sizeof(buf), &ent, &err); |
142 | | #elif defined(HAVE_GETHOSTBYNAME_R_5_ARG) |
143 | | char buf[2048]; |
144 | | struct hostent hostent; |
145 | | ent = gethostbyname_r(name, &hostent, buf, sizeof(buf), &err); |
146 | | #elif defined(HAVE_GETHOSTBYNAME_R_3_ARG) |
147 | | struct hostent_data data; |
148 | | struct hostent hent; |
149 | | memset(&data, 0, sizeof(data)); |
150 | | err = gethostbyname_r(name, &hent, &data); |
151 | | ent = err ? NULL : &hent; |
152 | | #else |
153 | | ent = gethostbyname(name); |
154 | | #ifdef _WIN32 |
155 | | err = WSAGetLastError(); |
156 | | #else |
157 | | err = h_errno; |
158 | | #endif /* defined(_WIN32) */ |
159 | | #endif /* defined(HAVE_GETHOSTBYNAME_R_6_ARG) || ... */ |
160 | | if (ent) { |
161 | | if (ent->h_addrtype == AF_INET) { |
162 | | tor_addr_from_in(addr, (struct in_addr*) ent->h_addr); |
163 | | } else if (ent->h_addrtype == AF_INET6) { |
164 | | tor_addr_from_in6(addr, (struct in6_addr*) ent->h_addr); |
165 | | } else { |
166 | | tor_assert(0); // LCOV_EXCL_LINE: gethostbyname() returned bizarre type |
167 | | } |
168 | | return 0; |
169 | | } |
170 | | #ifdef _WIN32 |
171 | | return (err == WSATRY_AGAIN) ? 1 : -1; |
172 | | #else |
173 | | return (err == TRY_AGAIN) ? 1 : -1; |
174 | | #endif |
175 | | } |
176 | | #endif /* defined(HAVE_GETADDRINFO) */ |
177 | | |
178 | | /** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set |
179 | | * *<b>addr</b> to the proper IP address and family. The <b>family</b> |
180 | | * argument (which must be AF_INET, AF_INET6, or AF_UNSPEC) declares a |
181 | | * <i>preferred</i> family, though another one may be returned if only one |
182 | | * family is implemented for this address. |
183 | | * |
184 | | * Like tor_addr_parse(), this function accepts IPv6 addresses with or without |
185 | | * square brackets. |
186 | | * |
187 | | * Return 0 on success, -1 on failure; 1 on transient failure. |
188 | | */ |
189 | | MOCK_IMPL(int, |
190 | | tor_addr_lookup,(const char *name, uint16_t family, tor_addr_t *addr)) |
191 | 0 | { |
192 | | /* Perhaps eventually this should be replaced by a tor_getaddrinfo or |
193 | | * something. |
194 | | */ |
195 | 0 | int parsed_family = 0; |
196 | 0 | int result = -1; |
197 | |
|
198 | 0 | tor_assert(name); |
199 | 0 | tor_assert(addr); |
200 | 0 | tor_assert(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC); |
201 | | |
202 | 0 | if (!*name) { |
203 | | /* Empty address is an error. */ |
204 | 0 | goto permfail; |
205 | 0 | } |
206 | | |
207 | | /* Is it an IP address? */ |
208 | 0 | parsed_family = tor_addr_parse(addr, name); |
209 | |
|
210 | 0 | if (parsed_family >= 0) { |
211 | | /* If the IP address family matches, or was unspecified */ |
212 | 0 | if (parsed_family == family || family == AF_UNSPEC) { |
213 | 0 | goto success; |
214 | 0 | } else { |
215 | 0 | goto permfail; |
216 | 0 | } |
217 | 0 | } else { |
218 | | /* Clear the address after a failed tor_addr_parse(). */ |
219 | 0 | memset(addr, 0, sizeof(tor_addr_t)); |
220 | 0 | result = tor_addr_lookup_host_impl(name, family, addr); |
221 | 0 | goto done; |
222 | 0 | } |
223 | | |
224 | | /* If we weren't successful, and haven't already set the result, |
225 | | * assume it's a permanent failure */ |
226 | 0 | permfail: |
227 | 0 | result = -1; |
228 | 0 | goto done; |
229 | 0 | success: |
230 | 0 | result = 0; |
231 | | |
232 | | /* We have set the result, now it's time to clean up */ |
233 | 0 | done: |
234 | 0 | if (result) { |
235 | | /* Clear the address on error */ |
236 | 0 | memset(addr, 0, sizeof(tor_addr_t)); |
237 | 0 | } |
238 | 0 | return result; |
239 | 0 | } |
240 | | |
241 | | /** Parse an address or address-port combination from <b>s</b>, resolve the |
242 | | * address as needed, and put the result in <b>addr_out</b> and (optionally) |
243 | | * <b>port_out</b>. |
244 | | * |
245 | | * Like tor_addr_port_parse(), this function accepts: |
246 | | * - IPv6 address and port, when the IPv6 address is in square brackets, |
247 | | * - IPv6 address with square brackets, |
248 | | * - IPv6 address without square brackets. |
249 | | * |
250 | | * Return 0 on success, negative on failure. */ |
251 | | int |
252 | | tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out) |
253 | 0 | { |
254 | 0 | tor_addr_t addr; |
255 | 0 | uint16_t portval = 0; |
256 | 0 | char *tmp = NULL; |
257 | 0 | int rv = 0; |
258 | 0 | int result; |
259 | |
|
260 | 0 | tor_assert(s); |
261 | 0 | tor_assert(addr_out); |
262 | | |
263 | 0 | s = eat_whitespace(s); |
264 | | |
265 | | /* Try parsing s as an address:port first, so we don't have to duplicate |
266 | | * the logic that rejects IPv6:Port with no square brackets. */ |
267 | 0 | rv = tor_addr_port_parse(LOG_WARN, s, &addr, &portval, 0); |
268 | | /* That was easy, no DNS required. */ |
269 | 0 | if (rv == 0) |
270 | 0 | goto success; |
271 | | |
272 | | /* Now let's check for malformed IPv6 addresses and ports: |
273 | | * tor_addr_port_parse() requires squared brackes if there is a port, |
274 | | * and we want tor_addr_port_lookup() to have the same requirement. |
275 | | * But we strip the port using tor_addr_port_split(), so tor_addr_lookup() |
276 | | * only sees the address, and will accept it without square brackets. */ |
277 | 0 | int family = tor_addr_parse(&addr, s); |
278 | | /* If tor_addr_parse() succeeds where tor_addr_port_parse() failed, we need |
279 | | * to reject this address as malformed. */ |
280 | 0 | if (family >= 0) { |
281 | | /* Double-check it's an IPv6 address. If not, we have a parsing bug. |
282 | | */ |
283 | 0 | tor_assertf_nonfatal(family == AF_INET6, |
284 | 0 | "Wrong family: %d (should be IPv6: %d) which " |
285 | 0 | "failed IP:port parsing, but passed IP parsing. " |
286 | 0 | "input string: '%s'; parsed address: '%s'.", |
287 | 0 | family, AF_INET6, s, fmt_addr(&addr)); |
288 | 0 | goto err; |
289 | 0 | } |
290 | | |
291 | | /* Now we have a hostname. Let's split off the port, if any. */ |
292 | 0 | rv = tor_addr_port_split(LOG_WARN, s, &tmp, &portval); |
293 | 0 | if (rv < 0) |
294 | 0 | goto err; |
295 | | |
296 | | /* And feed the hostname to the lookup function. */ |
297 | 0 | if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) != 0) |
298 | 0 | goto err; |
299 | | |
300 | 0 | success: |
301 | 0 | if (port_out) |
302 | 0 | *port_out = portval; |
303 | 0 | tor_addr_copy(addr_out, &addr); |
304 | 0 | result = 0; |
305 | 0 | goto done; |
306 | | |
307 | 0 | err: |
308 | | /* Clear the address and port on error */ |
309 | 0 | memset(addr_out, 0, sizeof(tor_addr_t)); |
310 | 0 | if (port_out) |
311 | 0 | *port_out = 0; |
312 | 0 | result = -1; |
313 | | |
314 | | /* We have set the result, now it's time to clean up */ |
315 | 0 | done: |
316 | 0 | tor_free(tmp); |
317 | 0 | return result; |
318 | 0 | } |
319 | | |
320 | | #ifdef USE_SANDBOX_GETADDRINFO |
321 | | /** True if we should only return cached values */ |
322 | | static int sandbox_getaddrinfo_is_active = 0; |
323 | | |
324 | | /** Cache entry for getaddrinfo results; used when sandboxing is implemented |
325 | | * so that we can consult the cache when the sandbox prevents us from doing |
326 | | * getaddrinfo. |
327 | | * |
328 | | * We support only a limited range of getaddrinfo calls, where servname is null |
329 | | * and hints contains only socktype=SOCK_STREAM, family in INET,INET6,UNSPEC. |
330 | | */ |
331 | | typedef struct cached_getaddrinfo_item_t { |
332 | | HT_ENTRY(cached_getaddrinfo_item_t) node; |
333 | | char *name; |
334 | | int family; |
335 | | /** set if no error; otherwise NULL */ |
336 | | struct addrinfo *res; |
337 | | /** 0 for no error; otherwise an EAI_* value */ |
338 | | int err; |
339 | | } cached_getaddrinfo_item_t; |
340 | | |
341 | | static unsigned |
342 | | cached_getaddrinfo_item_hash(const cached_getaddrinfo_item_t *item) |
343 | | { |
344 | | return (unsigned)siphash24g(item->name, strlen(item->name)) + item->family; |
345 | | } |
346 | | |
347 | | static unsigned |
348 | | cached_getaddrinfo_items_eq(const cached_getaddrinfo_item_t *a, |
349 | | const cached_getaddrinfo_item_t *b) |
350 | | { |
351 | | return (a->family == b->family) && 0 == strcmp(a->name, b->name); |
352 | | } |
353 | | |
354 | | #define cached_getaddrinfo_item_free(item) \ |
355 | | FREE_AND_NULL(cached_getaddrinfo_item_t, \ |
356 | | cached_getaddrinfo_item_free_, (item)) |
357 | | |
358 | | static void |
359 | | cached_getaddrinfo_item_free_(cached_getaddrinfo_item_t *item) |
360 | | { |
361 | | if (item == NULL) |
362 | | return; |
363 | | |
364 | | tor_free(item->name); |
365 | | if (item->res) |
366 | | freeaddrinfo(item->res); |
367 | | tor_free(item); |
368 | | } |
369 | | |
370 | | static HT_HEAD(getaddrinfo_cache, cached_getaddrinfo_item_t) |
371 | | getaddrinfo_cache = HT_INITIALIZER(); |
372 | | |
373 | | HT_PROTOTYPE(getaddrinfo_cache, cached_getaddrinfo_item_t, node, |
374 | | cached_getaddrinfo_item_hash, |
375 | | cached_getaddrinfo_items_eq); |
376 | | HT_GENERATE2(getaddrinfo_cache, cached_getaddrinfo_item_t, node, |
377 | | cached_getaddrinfo_item_hash, |
378 | | cached_getaddrinfo_items_eq, |
379 | | 0.6, tor_reallocarray_, tor_free_); |
380 | | |
381 | | /** If true, don't try to cache getaddrinfo results. */ |
382 | | static int sandbox_getaddrinfo_cache_disabled = 0; |
383 | | |
384 | | /** Tell the sandbox layer not to try to cache getaddrinfo results. Used as in |
385 | | * tor-resolve, when we have no intention of initializing crypto or of |
386 | | * installing the sandbox.*/ |
387 | | void |
388 | | sandbox_disable_getaddrinfo_cache(void) |
389 | | { |
390 | | sandbox_getaddrinfo_cache_disabled = 1; |
391 | | } |
392 | | |
393 | | void |
394 | | tor_freeaddrinfo(struct addrinfo *ai) |
395 | | { |
396 | | if (sandbox_getaddrinfo_cache_disabled) |
397 | | freeaddrinfo(ai); |
398 | | } |
399 | | |
400 | | int |
401 | | tor_getaddrinfo(const char *name, const char *servname, |
402 | | const struct addrinfo *hints, |
403 | | struct addrinfo **res) |
404 | | { |
405 | | int err; |
406 | | struct cached_getaddrinfo_item_t search, *item; |
407 | | |
408 | | if (sandbox_getaddrinfo_cache_disabled) { |
409 | | return getaddrinfo(name, NULL, hints, res); |
410 | | } |
411 | | |
412 | | if (servname != NULL) { |
413 | | log_warn(LD_BUG, "called with non-NULL servname"); |
414 | | return EAI_NONAME; |
415 | | } |
416 | | if (name == NULL) { |
417 | | log_warn(LD_BUG, "called with NULL name"); |
418 | | return EAI_NONAME; |
419 | | } |
420 | | |
421 | | *res = NULL; |
422 | | |
423 | | memset(&search, 0, sizeof(search)); |
424 | | search.name = (char *) name; |
425 | | search.family = hints ? hints->ai_family : AF_UNSPEC; |
426 | | item = HT_FIND(getaddrinfo_cache, &getaddrinfo_cache, &search); |
427 | | |
428 | | if (! sandbox_getaddrinfo_is_active) { |
429 | | /* If the sandbox is not turned on yet, then getaddrinfo and store the |
430 | | result. */ |
431 | | |
432 | | err = getaddrinfo(name, NULL, hints, res); |
433 | | log_info(LD_NET,"(Sandbox) getaddrinfo %s.", err ? "failed" : "succeeded"); |
434 | | |
435 | | if (! item) { |
436 | | item = tor_malloc_zero(sizeof(*item)); |
437 | | item->name = tor_strdup(name); |
438 | | item->family = hints ? hints->ai_family : AF_UNSPEC; |
439 | | HT_INSERT(getaddrinfo_cache, &getaddrinfo_cache, item); |
440 | | } |
441 | | |
442 | | if (item->res) { |
443 | | freeaddrinfo(item->res); |
444 | | item->res = NULL; |
445 | | } |
446 | | item->res = *res; |
447 | | item->err = err; |
448 | | return err; |
449 | | } |
450 | | |
451 | | /* Otherwise, the sandbox is on. If we have an item, yield its cached |
452 | | result. */ |
453 | | if (item) { |
454 | | *res = item->res; |
455 | | return item->err; |
456 | | } |
457 | | |
458 | | /* getting here means something went wrong */ |
459 | | log_err(LD_BUG,"(Sandbox) failed to get address %s!", name); |
460 | | return EAI_NONAME; |
461 | | } |
462 | | |
463 | | int |
464 | | tor_add_addrinfo(const char *name) |
465 | | { |
466 | | struct addrinfo *res; |
467 | | struct addrinfo hints; |
468 | | int i; |
469 | | static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC }; |
470 | | |
471 | | memset(&hints, 0, sizeof(hints)); |
472 | | hints.ai_socktype = SOCK_STREAM; |
473 | | for (i = 0; i < 3; ++i) { |
474 | | hints.ai_family = families[i]; |
475 | | |
476 | | res = NULL; |
477 | | (void) tor_getaddrinfo(name, NULL, &hints, &res); |
478 | | if (res) |
479 | | tor_freeaddrinfo(res); |
480 | | } |
481 | | |
482 | | return 0; |
483 | | } |
484 | | |
485 | | void |
486 | | tor_free_getaddrinfo_cache(void) |
487 | | { |
488 | | cached_getaddrinfo_item_t **next, **item, *this; |
489 | | |
490 | | for (item = HT_START(getaddrinfo_cache, &getaddrinfo_cache); |
491 | | item; |
492 | | item = next) { |
493 | | this = *item; |
494 | | next = HT_NEXT_RMV(getaddrinfo_cache, &getaddrinfo_cache, item); |
495 | | cached_getaddrinfo_item_free(this); |
496 | | } |
497 | | |
498 | | HT_CLEAR(getaddrinfo_cache, &getaddrinfo_cache); |
499 | | } |
500 | | |
501 | | void |
502 | | tor_make_getaddrinfo_cache_active(void) |
503 | | { |
504 | | sandbox_getaddrinfo_is_active = 1; |
505 | | } |
506 | | #else /* !defined(USE_SANDBOX_GETADDRINFO) */ |
507 | | void |
508 | | sandbox_disable_getaddrinfo_cache(void) |
509 | 0 | { |
510 | 0 | } |
511 | | void |
512 | | tor_make_getaddrinfo_cache_active(void) |
513 | 0 | { |
514 | 0 | } |
515 | | #endif /* defined(USE_SANDBOX_GETADDRINFO) */ |