Coverage Report

Created: 2025-07-11 06:57

/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) */