aboutsummaryrefslogtreecommitdiff
path: root/src/app/config/resolve_addr.c
blob: b811a68b8cffff4fa51b4a916885f717c4a7a0d1 (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
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
/* 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 "feature/dirauth/authmode.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[] =
  { TOR_ADDR_NULL, TOR_ADDR_NULL, TOR_ADDR_NULL };
CTASSERT(ARRAY_LENGTH(last_resolved_addrs) == IDX_SIZE);

/** Last suggested addresses.
 *
 * These addresses come from a NETINFO cell from a trusted relay (currently
 * only authorities). We only use those in last resort. */
static tor_addr_t last_suggested_addrs[] =
  { TOR_ADDR_NULL, TOR_ADDR_NULL, TOR_ADDR_NULL };
CTASSERT(ARRAY_LENGTH(last_suggested_addrs) == IDX_SIZE);

/** True iff the address was found to be configured that is from the
 * configuration file either using Address or ORPort. */
static bool last_addrs_configured[] = { false, false, false };
CTASSERT(ARRAY_LENGTH(last_addrs_configured) == 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;
  }
}

/** Return string representation of the given method. */
const char *
resolved_addr_method_to_str(const resolved_addr_method_t method)
{
  switch (method) {
  case RESOLVED_ADDR_NONE:
    return "NONE";
  case RESOLVED_ADDR_CONFIGURED:
    return "CONFIGURED";
  case RESOLVED_ADDR_CONFIGURED_ORPORT:
    return "CONFIGURED_ORPORT";
  case RESOLVED_ADDR_GETHOSTNAME:
    return "GETHOSTNAME";
  case RESOLVED_ADDR_INTERFACE:
    return "INTERFACE";
  case RESOLVED_ADDR_RESOLVED:
    return "RESOLVED";
  default:
    tor_assert_nonfatal_unreached();
    return "???";
  }
}

/** Return true if the last address of family was configured or not. An
 * address is considered configured if it was found in the Address or ORPort
 * statement.
 *
 * This applies to the address returned by the function
 * resolved_addr_get_last() which is the cache of discovered addresses. */
bool
resolved_addr_is_configured(int family)
{
  return last_addrs_configured[af_to_idx(family)];
}

/** Copy the last suggested address of family into addr_out.
 *
 * If no last suggested address exists, the addr_out is a null address (use
 * tor_addr_is_null() to confirm). */
void
resolved_addr_get_suggested(int family, tor_addr_t *addr_out)
{
  tor_addr_copy(addr_out, &last_suggested_addrs[af_to_idx(family)]);
}

/** Set the last suggested address into our cache. This is called when we get
 * a new NETINFO cell from a trusted source. */
void
resolved_addr_set_suggested(const tor_addr_t *addr)
{
  if (BUG(tor_addr_family(addr) != AF_INET &&
          tor_addr_family(addr) != AF_INET6)) {
    return;
  }
  tor_addr_copy(&last_suggested_addrs[af_to_idx(tor_addr_family(addr))],
                addr);
}

/** 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: Method denoting how 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, resolved_addr_method_t *method_out,
                        char **hostname_out, tor_addr_t *addr_out)
{
  int ret;
  bool explicit_ip = false, resolve_failure = 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 = RESOLVED_ADDR_NONE;

  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 = RESOLVED_ADDR_CONFIGURED;
      explicit_ip = true;
      num_valid_addr++;
      continue;
    } else if (af != -1) {
      /* Parsable address but just not the one from the family we want. Skip
       * it so we don't attempt a resolve. */
      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_ADDR_RESOLVED;
      if (*hostname_out) {
        tor_free(*hostname_out);
      }
      *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. */
      resolve_failure = true;
      log_fn(warn_severity, LD_CONFIG,
             "Could not resolve local Address '%s'. Failing.", cfg->value);
      continue;
    }
  }

  if (!num_valid_addr) {
    if (resolve_failure) {
      /* We found no address but we got a resolution failure. This means we
       * can know if the hostname given was v4 or v6 so we can't continue. */
      return FN_RET_BAIL;
    }
    log_info(LD_CONFIG,
             "No Address option found for family %s in configuration.",
             fmt_af_family(family));
    /* No Address statement for family so move on 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));
    tor_free(*hostname_out);
    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. */
    tor_free(*hostname_out);
    return FN_RET_BAIL;
  }

  /* Address can be used. We are done. */
  log_info(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: Method denoting how 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, resolved_addr_method_t *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 = RESOLVED_ADDR_NONE;

  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 = RESOLVED_ADDR_GETHOSTNAME;
  *hostname_out = tor_strdup(hostname);

  /* Found it! */
  log_info(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 RESOLVED_ADDR_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, resolved_addr_method_t *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 = RESOLVED_ADDR_NONE;
  *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 = RESOLVED_ADDR_INTERFACE;

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

/** @brief Get IP address from the ORPort (if any).
 *
 * @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 RESOLVED_ADDR_CONFIGURED_ORPORT on success
 *                   which is detailed in the control-spec.txt as actions
 *                   for "STATUS_SERVER".
 * @param hostname_out OUT: String containing the ORPort hostname if any.
 * @param addr_out OUT: Tor address found if any.
 *
 * @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_orport(const or_options_t *options, int warn_severity,
                        int family, resolved_addr_method_t *method_out,
                        char **hostname_out, tor_addr_t *addr_out)
{
  int ret;
  const tor_addr_t *addr;

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

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

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

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

  /* Get ORPort for requested family. */
  addr = get_orport_addr(family);
  if (!addr) {
    /* No address configured for the ORPort. Ignore. */
    return FN_RET_NEXT;
  }

  /* We found the ORPort address. Just make sure it can be used. */
  ret = address_can_be_used(addr, options, warn_severity, true);
  if (ret < 0) {
    /* Unable to use address. Inform caller to try next method. */
    return FN_RET_NEXT;
  }

  /* Found it! */
  *method_out = RESOLVED_ADDR_CONFIGURED_ORPORT;
  tor_addr_copy(addr_out, addr);

  log_fn(warn_severity, LD_CONFIG, "Address found from ORPort: %s",
         fmt_addr(addr_out));
  return FN_RET_OK;
}

/** @brief Set 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).
 */
void
resolved_addr_set_last(const tor_addr_t *addr,
                       const resolved_addr_method_t method_used,
                       const char *hostname_used)
{
  /** Have we done a first resolve. This is used to control logging. */
  static bool have_resolved_once[] = { false, false, false };
  CTASSERT(ARRAY_LENGTH(have_resolved_once) == IDX_SIZE);

  bool *done_one_resolve;
  bool have_hostname = false;
  tor_addr_t *last_resolved;

  tor_assert(addr);

  /* 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),
               resolved_addr_method_to_str(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),
                              resolved_addr_method_to_str(method_used),
                              have_hostname ? " HOSTNAME=" : "",
                              have_hostname ? hostname_used : "");
  /* Copy address to cache. */
  tor_addr_copy(last_resolved, addr);
  *done_one_resolve = true;

  /* Flag true if the address was configured. Else, indicate it was not. */
  last_addrs_configured[idx] = false;
  if (method_used == RESOLVED_ADDR_CONFIGURED ||
      method_used == RESOLVED_ADDR_CONFIGURED_ORPORT) {
    last_addrs_configured[idx] = true;
  }
}

/** Ease our lives. Typedef to the address discovery function signature. */
typedef fn_address_ret_t
  (*fn_address_t)(
     const or_options_t *options, int warn_severity, int family,
     resolved_addr_method_t *method_out, char **hostname_out,
     tor_addr_t *addr_out);

/** Address discovery function table. The order matters as in the first one is
 * executed first and so on. */
static const fn_address_t fn_address_table[] =
{
  /* These functions are in order for our find address algorithm. */
  get_address_from_config,
  get_address_from_orport,
  get_address_from_interface,
  get_address_from_hostname,
};
/** Length of address table as in how many functions. */
static const size_t fn_address_table_len =
  ARRAY_LENGTH(fn_address_table);

/* Address discover function table for authorities (bridge or directory).
 *
 * They only discover their address from either the configuration file or the
 * ORPort. They do not query the interface nor do any DNS resolution for
 * security reasons. */
static const fn_address_t fn_address_table_auth[] =
{
  /* These functions are in order for our find address algorithm. */
  get_address_from_config,
  get_address_from_orport,
};
/** Length of address table as in how many functions. */
static const size_t fn_address_table_auth_len =
  ARRAY_LENGTH(fn_address_table_auth);

/** @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 RESOLVED_ADDR_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_ADDR_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 network interface (see section 2).
 *
 *  2. Look at the network interface.
 *
 *     Attempt to find the first public usable address from the list of
 *     network interfaces returned by the OS.
 *
 *     On failure, we attempt to look at the local hostname (3).
 *
 *     On success, addr_out is set with it, method_out is set to
 *     RESOLVED_ADDR_INTERFACE and hostname_out is set to NULL.
 *
 *  3. 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 RESOLVED_ADDR_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, an error is returned.
 *
 * @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) Method denoting how the address wa
 *                   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, resolved_addr_method_t *method_out,
                char **hostname_out)
{
  resolved_addr_method_t method_used = RESOLVED_ADDR_NONE;
  char *hostname_used = NULL;
  tor_addr_t my_addr;
  const fn_address_t *table = fn_address_table;
  size_t table_len = fn_address_table_len;

  tor_assert(options);
  tor_assert(addr_out);

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

  /* If an IPv6 is requested, check if IPv6 address discovery is disabled and
   * if so we always return a failure. It is done here so we don't populate
   * the resolve cache or do any DNS resolution. */
  if (family == AF_INET6 && options->AddressDisableIPv6) {
    return false;
  }

  /* For authorities (bridge and directory), we use a different table. */
  if (authdir_mode(options)) {
    table = fn_address_table_auth;
    table_len = fn_address_table_auth_len;
  }

  /*
   * Step 1: Discover address by calling methods from the function table.
   */

  /* Go over the function table. They are in order. */
  for (size_t idx = 0; idx < table_len; idx++) {
    fn_address_ret_t ret = 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.
   */
  resolved_addr_set_last(&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;
}

/** @brief: Return true iff the given addr is judged to be local to our
 * resolved address.
 *
 * This function is used to tell whether another address is 'remote' enough
 * that we can trust it when it tells us that we are reachable, or that we
 * have a certain address.
 *
 * The criterion to learn if the address is local are the following:
 *
 *    1. Internal address.
 *    2. If EnforceDistinctSubnets is set then it is never local.
 *    3. Network mask is compared. IPv4: /24 and IPv6 /48. This is different
 *       from the path selection that looks at /16 and /32 because we only
 *       want to learn here if the address is considered to come from the
 *       Internet basically.
 *
 * @param addr The address to test if local and also test against our resovled
 *             address.
 *
 * @return True iff address is considered local or else False.
 */
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[af_to_idx(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:
    /* 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;
  }
}

#ifdef TOR_UNIT_TESTS

void
resolve_addr_reset_suggested(int family)
{
  tor_addr_make_unspec(&last_suggested_addrs[af_to_idx(family)]);
}

#endif /* TOR_UNIT_TESTS */