summaryrefslogtreecommitdiff
path: root/src/app/config/resolve_addr.c
blob: c8b44de84537f592fd196464b5d2ac3bf360bb0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
/* Copyright (c) 2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */

/**
 * \file resolve_addr.c
 * \brief Implement resolving address functions
 **/

#define RESOLVE_ADDR_PRIVATE

#include "app/config/config.h"
#include "app/config/resolve_addr.h"

#include "core/mainloop/mainloop.h"

#include "feature/control/control_events.h"

#include "lib/encoding/confline.h"
#include "lib/net/gethostname.h"
#include "lib/net/resolve.h"

/** Maximum "Address" statement allowed in our configuration. */
#define MAX_CONFIG_ADDRESS 2

/** Ease our life. Arrays containing state per address family. These are to
 * add semantic to the code so we know what is accessed. */
#define IDX_NULL 0 /* Index to zeroed address object. */
#define IDX_IPV4 1 /* Index to AF_INET. */
#define IDX_IPV6 2 /* Index to AF_INET6. */
#define IDX_SIZE 3 /* How many indexes do we have. */

/** Function in our address function table return one of these code. */
typedef enum {
  /* The address has been found. */
  FN_RET_OK   = 0,
  /* The failure requirements were not met and thus it is recommended that the
   * caller stops the search. */
  FN_RET_BAIL = 1,
  /* The address was not found or failure is transient so the caller should go
   * to the next method. */
  FN_RET_NEXT = 2,
} fn_address_ret_t;

/** Last resolved addresses. */
static tor_addr_t last_resolved_addrs[IDX_SIZE];

static inline int
af_to_idx(const int family)
{
  switch (family) {
  case AF_INET:
    return IDX_IPV4;
  case AF_INET6:
    return IDX_IPV6;
  default:
    /* It wouldn't be safe to just die here with an assert but we can heavily
     * scream with a bug. Return the index of the NULL address. */
    tor_assert_nonfatal_unreached();
    return IDX_NULL;
  }
}

/** Copy the last resolved address of family into addr_out.
 *
 * If not last resolved address existed, the addr_out is a null address (use
 * tor_addr_is_null()). */
void
resolved_addr_get_last(int family, tor_addr_t *addr_out)
{
  tor_addr_copy(addr_out, &last_resolved_addrs[af_to_idx(family)]);
}

/** Reset the last resolved address of family.
 *
 * This makes it null address. */
void
resolved_addr_reset_last(int family)
{
  tor_addr_make_null(&last_resolved_addrs[af_to_idx(family)], family);
}

/** Errors returned by address_can_be_used() in order for the caller to know
 * why the address is denied or not. */
#define ERR_DEFAULT_DIRAUTH     -1 /* Using default authorities. */
#define ERR_ADDRESS_IS_INTERNAL -2 /* IP is internal. */

/** @brief Return true iff the given IP address can be used as a valid
 *         external resolved address.
 *
 * Two tests are done in this function:
 *    1) If the address if NOT internal, it can be used.
 *    2) If the address is internal and we have custom directory authorities
 *       configured then it can they be used. Important for testing networks.
 *
 * @param addr The IP address to validate.
 * @param options Global configuration options.
 * @param warn_severity Log level that should be used on error.
 * @param explicit_ip Was the IP address explicitly given.
 *
 * @return Return 0 if it can be used. Return error code ERR_* found at the
 *         top of the file.
 */
static int
address_can_be_used(const tor_addr_t *addr, const or_options_t *options,
                    int warn_severity, const bool explicit_ip)
{
  tor_assert(addr);

  /* Public address, this is fine. */
  if (!tor_addr_is_internal(addr, 0)) {
    goto allow;
  }

  /* We have a private IP address. It is allowed only if we set custom
   * directory authorities. */
  if (using_default_dir_authorities(options)) {
    log_fn(warn_severity, LD_CONFIG,
           "Address '%s' is a private IP address. Tor relays that use "
           "the default DirAuthorities must have public IP addresses.",
           fmt_addr(addr));
    return ERR_DEFAULT_DIRAUTH;
  }

  if (!explicit_ip) {
    /* Even with custom directory authorities, only an explicit internal
     * address is accepted. */
    log_fn(warn_severity, LD_CONFIG,
           "Address %s was resolved and thus not explicitly "
           "set. Even if DirAuthorities are custom, this is "
           "not allowed.", fmt_addr(addr));
    return ERR_ADDRESS_IS_INTERNAL;
  }

 allow:
  return 0;
}

/** @brief Get IP address from the given config line and for a specific address
 *         family.
 *
 * This can fail is more than two Address statement are found for the same
 * address family. It also fails if no statement is found.
 *
 * @param options Global configuration options.
 * @param warn_severity Log level that should be used on error.
 * @param family IP address family. Only AF_INET and AF_INET6 are supported.
 * @param method_out OUT: String denoting by which method the address was
 *                   found. This is described in the control-spec.txt as
 *                   actions for "STATUS_SERVER".
 * @param hostname_out OUT: String containing the hostname gotten from the
 *                     Address value if any.
 * @param addr_out OUT: Tor address of the address found in the cline or
 *                 resolved from the cline.
 *
 * @return Return 0 on success that is an address has been found or resolved
 *         successfully. Return error code ERR_* found at the top of the file.
 */
static fn_address_ret_t
get_address_from_config(const or_options_t *options, int warn_severity,
                        int family, const char **method_out,
                        char **hostname_out, tor_addr_t *addr_out)
{
  int ret;
  bool explicit_ip = false;
  int num_valid_addr = 0;

  tor_assert(options);
  tor_assert(addr_out);
  tor_assert(method_out);
  tor_assert(hostname_out);

  /* Set them to NULL for safety reasons. */
  *hostname_out = NULL;
  *method_out = NULL;

  log_debug(LD_CONFIG, "Attempting to get address from configuration");

  if (!options->Address) {
    log_info(LD_CONFIG, "No Address option found in configuration.");
    /* No Address statement, inform caller to try next method. */
    return FN_RET_NEXT;
  }

  for (const config_line_t *cfg = options->Address; cfg != NULL;
       cfg = cfg->next) {
    int af;
    tor_addr_t addr;

    af = tor_addr_parse(&addr, cfg->value);
    if (af == family) {
      tor_addr_copy(addr_out, &addr);
      *method_out = "CONFIGURED";
      explicit_ip = true;
      num_valid_addr++;
      continue;
    }

    /* Not an IP address. Considering this value a hostname and attempting to
     * do a DNS lookup. */
    if (!tor_addr_lookup(cfg->value, family, &addr)) {
      tor_addr_copy(addr_out, &addr);
      *method_out = "RESOLVED";
      *hostname_out = tor_strdup(cfg->value);
      explicit_ip = false;
      num_valid_addr++;
      continue;
    } else {
      /* Hostname that can't be resolved, this is a fatal error. */
      log_fn(warn_severity, LD_CONFIG,
             "Could not resolve local Address '%s'. Failing.", cfg->value);
      return FN_RET_BAIL;
    }
  }

  if (!num_valid_addr) {
    log_fn(warn_severity, LD_CONFIG,
           "No Address option found for family %s in configuration.",
           fmt_af_family(family));
    /* No Address statement for family, inform caller to try next method. */
    return FN_RET_NEXT;
  }

  if (num_valid_addr >= MAX_CONFIG_ADDRESS) {
    /* Too many Address for same family. This is a fatal error. */
    log_fn(warn_severity, LD_CONFIG,
           "Found %d Address statement of address family %s. "
           "Only one is allowed.", num_valid_addr, fmt_af_family(family));
    return FN_RET_BAIL;
  }

  /* Great, we found an address. */
  ret = address_can_be_used(addr_out, options, warn_severity, explicit_ip);
  if (ret != 0) {
    /* One of the requirement of this interface is if an internal Address is
     * used, custom authorities must be defined else it is a fatal error.
     * Furthermore, if the Address was resolved to an internal interface, we
     * stop immediately. */
    return FN_RET_BAIL;
  }

  /* Address can be used. We are done. */
  log_fn(warn_severity, LD_CONFIG, "Address found in configuration: %s",
         fmt_addr(addr_out));
  return FN_RET_OK;
}

/** @brief Get IP address from the local hostname by calling gethostbyname()
 *         and doing a DNS resolution on the hostname.
 *
 * @param options Global configuration options.
 * @param warn_severity Log level that should be used on error.
 * @param family IP address family. Only AF_INET and AF_INET6 are supported.
 * @param method_out OUT: String denoting by which method the address was
 *                   found. This is described in the control-spec.txt as
 *                   actions for "STATUS_SERVER".
 * @param hostname_out OUT: String containing the local hostname.
 * @param addr_out OUT: Tor address resolved from the local hostname.
 *
 * @return Return 0 on success that is an address has been found and resolved
 *         successfully. Return error code ERR_* found at the top of the file.
 */
static fn_address_ret_t
get_address_from_hostname(const or_options_t *options, int warn_severity,
                          int family, const char **method_out,
                          char **hostname_out, tor_addr_t *addr_out)
{
  int ret;
  char hostname[256];

  tor_assert(addr_out);
  tor_assert(method_out);

  /* Set them to NULL for safety reasons. */
  *hostname_out = NULL;
  *method_out = NULL;

  log_debug(LD_CONFIG, "Attempting to get address from local hostname");

  if (tor_gethostname(hostname, sizeof(hostname)) < 0) {
    log_fn(warn_severity, LD_NET, "Error obtaining local hostname");
    /* Unable to obtain the local hostname is a fatal error. */
    return FN_RET_BAIL;
  }
  if (tor_addr_lookup(hostname, family, addr_out)) {
    log_fn(warn_severity, LD_NET,
           "Could not resolve local hostname '%s'. Failing.", hostname);
    /* Unable to resolve, inform caller to try next method. */
    return FN_RET_NEXT;
  }

  ret = address_can_be_used(addr_out, options, warn_severity, false);
  if (ret == ERR_DEFAULT_DIRAUTH) {
    /* Non custom authorities, inform caller to try next method. */
    return FN_RET_NEXT;
  } else if (ret == ERR_ADDRESS_IS_INTERNAL) {
    /* Internal address is a fatal error. */
    return FN_RET_BAIL;
  }

  /* addr_out contains the address of the local hostname. */
  *method_out = "GETHOSTNAME";
  *hostname_out = tor_strdup(hostname);

  /* Found it! */
  log_fn(warn_severity, LD_CONFIG, "Address found from local hostname: %s",
         fmt_addr(addr_out));
  return FN_RET_OK;
}

/** @brief Get IP address from a network interface.
 *
 * @param options Global configuration options.
 * @param warn_severity Log level that should be used on error.
 * @param family IP address family. Only AF_INET and AF_INET6 are supported.
 * @param method_out OUT: Always "INTERFACE" on success which is detailed in
 *                   the control-spec.txt as actions for "STATUS_SERVER".
 * @param hostname_out OUT: String containing the local hostname. For this
 *                     function, it is always set to NULL.
 * @param addr_out OUT: Tor address found attached to the interface.
 *
 * @return Return 0 on success that is an address has been found. Return
 *         error code ERR_* found at the top of the file.
 */
static fn_address_ret_t
get_address_from_interface(const or_options_t *options, int warn_severity,
                           int family, const char **method_out,
                           char **hostname_out, tor_addr_t *addr_out)
{
  int ret;

  tor_assert(method_out);
  tor_assert(hostname_out);
  tor_assert(addr_out);

  /* Set them to NULL for safety reasons. */
  *method_out = NULL;
  *hostname_out = NULL;

  log_debug(LD_CONFIG, "Attempting to get address from network interface");

  if (get_interface_address6(warn_severity, family, addr_out) < 0) {
    log_fn(warn_severity, LD_CONFIG,
           "Could not get local interface IP address.");
    /* Unable to get IP from interface. Inform caller to try next method. */
    return FN_RET_NEXT;
  }

  ret = address_can_be_used(addr_out, options, warn_severity, false);
  if (ret < 0) {
    /* Unable to use address. Inform caller to try next method. */
    return FN_RET_NEXT;
  }

  *method_out = "INTERFACE";

  /* Found it! */
  log_fn(warn_severity, LD_CONFIG, "Address found from interface: %s",
         fmt_addr(addr_out));
  return FN_RET_OK;
}

/** @brief Update the last resolved address cache using the given address.
 *
 * A log notice is emitted if the given address has changed from before. Not
 * emitted on first resolve.
 *
 * Control port event "STATUS_SERVER" is emitted with the new information if
 * it has changed.
 *
 * Finally, tor is notified that the IP address has changed.
 *
 * @param addr IP address to update the cache with.
 * @param method_used By which method did we resolved it (for logging and
 *                    control port).
 * @param hostname_used Which hostname was used. If none were used, it is
 *                      NULL. (for logging and control port).
 */
static void
update_resolved_cache(const tor_addr_t *addr, const char *method_used,
                      const char *hostname_used)
{
  /** Have we done a first resolve. This is used to control logging. */
  static bool have_resolved_once[IDX_SIZE] = { false, false, false };
  bool *done_one_resolve;
  bool have_hostname = false;
  tor_addr_t *last_resolved;

  tor_assert(addr);
  tor_assert(method_used);

  /* Do we have an hostname. */
  have_hostname = (hostname_used != NULL);

  int idx = af_to_idx(tor_addr_family(addr));
  if (idx == IDX_NULL) {
    /* Not suppose to happen and if it does, af_to_idx() screams loudly. */
    return;
  }

  /* Get values from cache. */
  done_one_resolve = &have_resolved_once[idx];
  last_resolved = &last_resolved_addrs[idx];

  /* Same address last resolved. Ignore. */
  if (tor_addr_eq(last_resolved, addr)) {
    return;
  }

  /* Don't log notice if this is the first resolve we do. */
  if (*done_one_resolve) {
    /* Leave this as a notice, regardless of the requested severity,
     * at least until dynamic IP address support becomes bulletproof. */
    log_notice(LD_NET,
               "Your IP address seems to have changed to %s "
               "(METHOD=%s%s%s). Updating.",
               fmt_addr(addr), method_used,
               have_hostname ? " HOSTNAME=" : "",
               have_hostname ? hostname_used : "");
    ip_address_changed(0);
  }

  /* Notify control port. */
  control_event_server_status(LOG_NOTICE,
                              "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s%s%s",
                              fmt_addr(addr), method_used,
                              have_hostname ? " HOSTNAME=" : "",
                              have_hostname ? hostname_used : "");
  /* Copy address to cache. */
  tor_addr_copy(last_resolved, addr);
  *done_one_resolve = true;
}

/** Address discovery function table. The order matters as in the first one is
 * executed first and so on. */
static fn_address_ret_t
  (*fn_address_table[])(
    const or_options_t *options, int warn_severity, int family,
    const char **method_out, char **hostname_out, tor_addr_t *addr_out) =
{
  /* These functions are in order for our find address algorithm. */
  get_address_from_config,
  get_address_from_hostname,
  get_address_from_interface,
};
/** Length of address table as in how many functions. */
static const size_t fn_address_table_len = ARRAY_LENGTH(fn_address_table);

/** @brief Attempt to find our IP address that can be used as our external
 *         reachable address.
 *
 *  The following describe the algorithm to find an address. Each have
 *  specific conditions so read carefully.
 *
 *  On success, true is returned and depending on how the address was found,
 *  the out parameters can have different values.
 *
 *  On error, false is returned and out parameters are set to NULL.
 *
 *  1. Look at the configuration Address option.

 *     If Address is a public address, True is returned and addr_out is set
 *     with it, the method_out is set to "CONFIGURED" and hostname_out is set
 *     to NULL.
 *
 *     If Address is an internal address but NO custom authorities are used,
 *     an error is returned.
 *
 *     If Address is a hostname, that is it can't be converted to an address,
 *     it is resolved. On success, addr_out is set with the address,
 *     method_out is set to "RESOLVED" and hostname_out is set to the resolved
 *     hostname. On failure to resolve, an error is returned.
 *
 *     If no given Address, fallback to the local hostname (see section 2).
 *
 *  2. Look at the local hostname.
 *
 *     If the local hostname resolves to a non internal address, addr_out is
 *     set with it, method_out is set to "GETHOSTNAME" and hostname_out is set
 *     to the resolved hostname.
 *
 *     If a local hostname can NOT be found, an error is returned.
 *
 *     If the local hostname resolves to an internal address, an error is
 *     returned.
 *
 *     If the local hostname can NOT be resolved, fallback to the network
 *     interface (see section 3).
 *
 *  3. Look at the network interface.
 *
 *     Attempt to find the first public usable address from the list of
 *     network interface returned by the OS.
 *
 *     On failure, an error is returned. This error indicates that all
 *     attempts have failed and thus the address for the given family can not
 *     be found.
 *
 *     On success, addr_out is set with it, method_out is set to "INTERFACE"
 *     and hostname_out is set to NULL.
 *
 * @param options Global configuration options.
 * @param family IP address family. Only AF_INET and AF_INET6 are supported.
 * @param warn_severity Logging level.
 * @param addr_out OUT: Set with the IP address found if any.
 * @param method_out OUT: (optional) String denoting by which method the
 *                   address was found. This is described in the
 *                   control-spec.txt as actions for "STATUS_SERVER".
 * @param hostname_out OUT: String containing the hostname if any was used.
 *                     Only be set for "RESOLVED" and "GETHOSTNAME" methods.
 *                     Else it is set to NULL.
 *
 * @return True if the address was found for the given family. False if not or
 *         on errors.
 */
bool
find_my_address(const or_options_t *options, int family, int warn_severity,
                tor_addr_t *addr_out, const char **method_out,
                char **hostname_out)
{
  const char *method_used = NULL;
  char *hostname_used = NULL;
  tor_addr_t my_addr;

  tor_assert(options);
  tor_assert(addr_out);

  /* Set them to NULL for safety reasons. */
  if (method_out) *method_out = NULL;
  if (hostname_out) *hostname_out = NULL;

  /*
   * Step 1: Discover address by attempting 3 different methods consecutively.
   */

  /* Go over the function table. They are in order. */
  for (size_t idx = 0; idx < fn_address_table_len; idx++) {
    fn_address_ret_t ret = fn_address_table[idx](options, warn_severity,
                                                 family, &method_used,
                                                 &hostname_used, &my_addr);
    if (ret == FN_RET_BAIL) {
      return false;
    } else if (ret == FN_RET_OK) {
      goto found;
    }
    tor_assert(ret == FN_RET_NEXT);
  }

  /* We've exhausted our attempts. Failure. */
  log_fn(warn_severity, LD_CONFIG, "Unable to find our IP address.");
  return false;

 found:
  /*
   * Step 2: Update last resolved address cache and inform the control port.
   */
  update_resolved_cache(&my_addr, method_used, hostname_used);

  if (method_out) {
    *method_out = method_used;
  }
  if (hostname_out) {
    *hostname_out = hostname_used;
  } else {
    tor_free(hostname_used);
  }

  tor_addr_copy(addr_out, &my_addr);
  return true;
}

/** Return true iff <b>addr</b> is judged to be on the same network as us, or
 * on a private network.
 */
MOCK_IMPL(bool,
is_local_to_resolve_addr, (const tor_addr_t *addr))
{
  const int family = tor_addr_family(addr);
  const tor_addr_t *last_resolved_addr = &last_resolved_addrs[family];

  /* Internal address is always local. */
  if (tor_addr_is_internal(addr, 0)) {
    return true;
  }

  /* Address is not local if we don't enforce subnet distinction. */
  if (get_options()->EnforceDistinctSubnets == 0) {
    return false;
  }

  switch (family) {
  case AF_INET:
    /* XXX: Why is this /24 and not /16 which the rest of tor does? Unknown
     * reasons at the moment highlighted in ticket #40009. Because of that, we
     * can't use addrs_in_same_network_family(). */

    /* It's possible that this next check will hit before the first time
     * find_my_address actually succeeds. For clients, it is likely that
     * find_my_address will never be called at all. In those cases,
     * last_resolved_addr_v4 will be 0, and so checking to see whether ip is
     * on the same /24 as last_resolved_addrs[AF_INET] will be the same as
     * checking whether it was on net 0, which is already done by
     * tor_addr_is_internal. */
    return tor_addr_compare_masked(addr, last_resolved_addr, 24,
                                   CMP_SEMANTIC) == 0;
  case AF_INET6:
    /* Look at /48 because it is typically the smallest network in the global
     * IPv6 routing tables, and it was previously the recommended per-customer
     * network block. (See [RFC 6177: IPv6 End Site Address Assignment].) */
    return tor_addr_compare_masked(addr, last_resolved_addr, 48,
                                   CMP_SEMANTIC) == 0;
    break;
  default:
    /* Unknown address type so not local. */
    return false;
  }
}