dns.c 34.4 KB
Newer Older
Roger Dingledine's avatar
Roger Dingledine committed
1
/* Copyright 2003-2004 Roger Dingledine.
Roger Dingledine's avatar
Roger Dingledine committed
2
 * Copyright 2004-2006 Roger Dingledine, Nick Mathewson. */
3
4
/* See LICENSE for licensing information */
/* $Id$ */
5
6
const char dns_c_id[] =
  "$Id$";
7

Roger Dingledine's avatar
Roger Dingledine committed
8
9
/**
 * \file dns.c
10
11
12
13
 * \brief Implements a farm of 'DNS worker' threads or processes to
 * perform DNS lookups for onion routers and cache the results.
 * [This needs to be done in the background because of the lack of a
 * good, ubiquitous asynchronous DNS implementation.]
Roger Dingledine's avatar
Roger Dingledine committed
14
 **/
15

16
17
/* See
 * http://elvin.dstc.com/ListArchive/elvin-dev/archive/2001/09/msg00027.html
18
19
20
21
 * for some approaches to asynchronous dns. We will want to switch once one of
 * them becomes more commonly available.
 */

22
#include "or.h"
23
#include "../common/ht.h"
24
25
26
#ifdef USE_EVENTDNS
#include "eventdns.h"
#endif
27

Roger Dingledine's avatar
Roger Dingledine committed
28
/** Longest hostname we're willing to resolve. */
29
30
#define MAX_ADDRESSLEN 256

Roger Dingledine's avatar
Roger Dingledine committed
31
/** Maximum DNS processes to spawn. */
32
#define MAX_DNSWORKERS 100
Roger Dingledine's avatar
Roger Dingledine committed
33
/** Minimum DNS processes to spawn. */
34
#define MIN_DNSWORKERS 3
35

Roger Dingledine's avatar
Roger Dingledine committed
36
/** If more than this many processes are idle, shut down the extras. */
37
#define MAX_IDLE_DNSWORKERS 10
38

Roger Dingledine's avatar
Roger Dingledine committed
39
/** Possible outcomes from hostname lookup: permanent failure,
40
 * transient (retryable) failure, and success. */
41
42
43
44
#define DNS_RESOLVE_FAILED_TRANSIENT 1
#define DNS_RESOLVE_FAILED_PERMANENT 2
#define DNS_RESOLVE_SUCCEEDED 3

45
#ifndef USE_EVENTDNS
46
/** How many dnsworkers we have running right now. */
47
static int num_dnsworkers=0;
48
/** How many of the running dnsworkers have an assigned task right now. */
49
50
51
static int num_dnsworkers_busy=0;
/** When did we last rotate the dnsworkers? */
static time_t last_rotation_time=0;
52
#endif
53

Roger Dingledine's avatar
Roger Dingledine committed
54
/** Linked list of connections waiting for a DNS answer. */
55
56
typedef struct pending_connection_t {
  connection_t *conn;
57
  struct pending_connection_t *next;
58
} pending_connection_t;
59

Roger Dingledine's avatar
Roger Dingledine committed
60
/** A DNS request: possibly completed, possibly pending; cached_resolve
61
 * structs are stored at the OR side in a hash table, and as a linked
62
63
 * list from oldest to newest.
 */
64
typedef struct cached_resolve_t {
65
  HT_ENTRY(cached_resolve_t) node;
66
  char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */
67
  uint32_t addr; /**< IPv4 addr for <b>address</b>. */
68
69
  char state; /**< 0 is pending; 1 means answer is valid; 2 means resolve
               * failed. */
70
71
72
#define CACHE_STATE_PENDING 0
#define CACHE_STATE_VALID 1
#define CACHE_STATE_FAILED 2
73
  uint32_t expire; /**< Remove items from cache after this time. */
74
75
76
  pending_connection_t *pending_connections;
  struct cached_resolve_t *next;
} cached_resolve_t;
77

78
static void purge_expired_resolves(uint32_t now);
79
static void dns_purge_resolve(cached_resolve_t *resolve);
80
81
82
83
84
85
static void dns_found_answer(char *address, uint32_t addr, char outcome,
                             uint32_t ttl);
static void send_resolved_cell(connection_t *conn, uint8_t answer_type,
                               uint32_t ttl);
static int assign_to_dnsworker(connection_t *exitconn);
#ifndef USE_EVENTDNS
Nick Mathewson's avatar
Nick Mathewson committed
86
static int dnsworker_main(void *data);
87
static int spawn_dnsworker(void);
88
static int spawn_enough_dnsworkers(void);
89
#endif
90

91
92
/** Hash table of cached_resolve objects. */
static HT_HEAD(cache_map, cached_resolve_t) cache_root;
93

Roger Dingledine's avatar
Roger Dingledine committed
94
/** Function to compare hashed resolves on their addresses; used to
95
 * implement hash tables. */
96
97
static INLINE int
cached_resolves_eq(cached_resolve_t *a, cached_resolve_t *b)
98
{
99
  /* make this smarter one day? */
100
101
102
103
104
105
106
  return !strncmp(a->address, b->address, MAX_ADDRESSLEN);
}

static INLINE unsigned int
cached_resolve_hash(cached_resolve_t *a)
{
  return ht_string_hash(a->address);
107
108
}

109
HT_PROTOTYPE(cache_map, cached_resolve_t, node, cached_resolve_hash,
110
             cached_resolves_eq);
111
HT_GENERATE(cache_map, cached_resolve_t, node, cached_resolve_hash,
112
            cached_resolves_eq, 0.6, malloc, realloc, free);
113

114
/** Initialize the DNS cache. */
115
static void
116
init_cache_map(void)
117
{
118
  HT_INIT(&cache_root);
119
120
}

Roger Dingledine's avatar
Roger Dingledine committed
121
/** Initialize the DNS subsystem; called by the OR process. */
122
123
124
void
dns_init(void)
{
125
  init_cache_map();
126
  dnsworkers_rotate();
127
128
129
130
#ifdef USE_EVENTDNS
  eventdns_resolv_conf_parse(DNS_OPTION_NAMESERVERS|DNS_OPTION_MISC,
                             "/etc/resolv.conf");
#endif
131
}
132

133
/** Helper: free storage held by an entry in the DNS cache. */
134
static void
135
_free_cached_resolve(cached_resolve_t *r)
136
{
Nick Mathewson's avatar
Nick Mathewson committed
137
  while (r->pending_connections) {
138
    pending_connection_t *victim = r->pending_connections;
139
140
141
142
143
144
    r->pending_connections = victim->next;
    tor_free(victim);
  }
  tor_free(r);
}

145
/** Free all storage held in the DNS cache */
146
147
148
void
dns_free_all(void)
{
149
  cached_resolve_t **ptr, **next, *item;
150
  for (ptr = HT_START(cache_map, &cache_root); ptr != NULL; ptr = next) {
151
    item = *ptr;
152
    next = HT_NEXT_RMV(cache_map, &cache_root, ptr);
153
    _free_cached_resolve(item);
154
  }
155
  HT_CLEAR(cache_map, &cache_root);
156
157
}

158
/** Linked list of resolved addresses, oldest to newest. */
159
160
static cached_resolve_t *oldest_cached_resolve = NULL;
static cached_resolve_t *newest_cached_resolve = NULL;
161

Nick Mathewson's avatar
Nick Mathewson committed
162
/** Remove every cached_resolve whose <b>expire</b> time is before <b>now</b>
163
 * from the cache. */
164
165
166
static void
purge_expired_resolves(uint32_t now)
{
167
168
  cached_resolve_t *resolve;
  pending_connection_t *pend;
169
  connection_t *pendconn;
170
171
172
173

  /* this is fast because the linked list
   * oldest_cached_resolve is ordered by when they came in.
   */
174
  while (oldest_cached_resolve && (oldest_cached_resolve->expire < now)) {
175
    resolve = oldest_cached_resolve;
176
177
    log_debug(LD_EXIT,
              "Forgetting old cached resolve (address %s, expires %lu)",
178
179
              escaped_safe_str(resolve->address),
              (unsigned long)resolve->expire);
180
    if (resolve->state == CACHE_STATE_PENDING) {
181
      log_debug(LD_EXIT,
182
183
                "Bug: Expiring a dns resolve %s that's still pending."
                " Forgot to cull it?", escaped_safe_str(resolve->address));
184
      tor_fragile_assert();
185
186
    }
    if (resolve->pending_connections) {
187
188
      log_debug(LD_EXIT,
                "Closing pending connections on expiring DNS resolve!");
189
      tor_fragile_assert();
190
191
192
193
194
195
      while (resolve->pending_connections) {
        pend = resolve->pending_connections;
        resolve->pending_connections = pend->next;
        /* Connections should only be pending if they have no socket. */
        tor_assert(pend->conn->s == -1);
        pendconn = pend->conn;
196
        connection_edge_end(pendconn, END_STREAM_REASON_TIMEOUT,
197
                            pendconn->cpath_layer);
198
        circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
199
200
201
        connection_free(pendconn);
        tor_free(pend);
      }
202
    }
203
    oldest_cached_resolve = resolve->next;
204
    if (!oldest_cached_resolve) /* if there are no more, */
205
206
      newest_cached_resolve = NULL; /* then make sure the list's tail knows
                                     * that too */
207
    HT_REMOVE(cache_map, &cache_root, resolve);
208
    tor_free(resolve);
209
210
211
  }
}

212
213
214
/** Send a response to the RESOVLE request of a connection. answer_type must
 *  be one of RESOLVED_TYPE_(IPV4|ERROR|ERROR_TRANSIENT) */
static void
215
send_resolved_cell(connection_t *conn, uint8_t answer_type, uint32_t ttl)
216
217
{
  char buf[RELAY_PAYLOAD_SIZE];
218
  size_t buflen;
219
220
221
222
223
224
225
226

  buf[0] = answer_type;

  switch (answer_type)
    {
    case RESOLVED_TYPE_IPV4:
      buf[1] = 4;
      set_uint32(buf+2, htonl(conn->addr));
227
      set_uint32(buf+6, htonl(ttl));
228
      buflen = 10;
229
230
231
      break;
    case RESOLVED_TYPE_ERROR_TRANSIENT:
    case RESOLVED_TYPE_ERROR:
232
233
234
      {
        const char *errmsg = "Error resolving hostname";
        int msglen = strlen(errmsg);
235

236
237
        buf[1] = msglen;
        strlcpy(buf+2, errmsg, sizeof(buf)-2);
238
        set_uint32(buf+2+msglen, htonl(ttl));
239
240
241
        buflen = 6+msglen;
        break;
      }
242
243
244
    default:
      tor_assert(0);
    }
245
  connection_edge_send_command(conn, circuit_get_by_edge_conn(conn),
246
247
248
249
                               RELAY_COMMAND_RESOLVED, buf, buflen,
                               conn->cpath_layer);
}

250
251
/** Link <b>r</b> into the hash table of address-to-result mappings, and add it
 * to the linked list of resolves-by-age. */
252
static void
253
insert_resolve(cached_resolve_t *r)
254
255
256
257
258
259
260
261
262
{
  /* add us to the linked list of resolves */
  if (!oldest_cached_resolve) {
    oldest_cached_resolve = r;
  } else {
    newest_cached_resolve->next = r;
  }
  newest_cached_resolve = r;

263
  HT_INSERT(cache_map, &cache_root, r);
264
265
}

Nick Mathewson's avatar
Nick Mathewson committed
266
267
/** See if we have a cache entry for <b>exitconn</b>-\>address. if so,
 * if resolve valid, put it into <b>exitconn</b>-\>addr and return 1.
268
 * If resolve failed, unlink exitconn if needed, free it, and return -1.
269
270
271
272
273
274
275
 *
 * Else, if seen before and pending, add conn to the pending list,
 * and return 0.
 *
 * Else, if not seen before, add conn to pending list, hand to
 * dns farm, and return 0.
 */
276
277
278
int
dns_resolve(connection_t *exitconn)
{
279
280
281
  cached_resolve_t *resolve;
  cached_resolve_t search;
  pending_connection_t *pending_connection;
282
  struct in_addr in;
283
  circuit_t *circ;
284
  uint32_t now = time(NULL);
285
  assert_connection_ok(exitconn, 0);
286
  tor_assert(exitconn->s == -1);
287

288
289
290
291
  /* first check if exitconn->address is an IP. If so, we already
   * know the answer. */
  if (tor_inet_aton(exitconn->address, &in) != 0) {
    exitconn->addr = ntohl(in.s_addr);
292
    if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
293
      send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4, MAX_DNS_ENTRY_AGE);
294
    return 1;
295
296
297
  }

  /* then take this opportunity to see if there are any expired
298
   * resolves in the hash table. */
299
  purge_expired_resolves(now);
300

301
302
303
  /* lower-case exitconn->address, so it's in canonical form */
  tor_strlower(exitconn->address);

304
  /* now check the hash table to see if 'address' is already there. */
Nick Mathewson's avatar
Nick Mathewson committed
305
  strlcpy(search.address, exitconn->address, sizeof(search.address));
306
  resolve = HT_FIND(cache_map, &cache_root, &search);
307
308
309
310
  if (resolve && resolve->expire > now) { /* already there */
    // XXXX Security problem: this leaks a time at which somebody asked for
    // XXXX the address.  That's a problem.
    unsigned int ttl = (unsigned int) resolve->expire - now;
311
    switch (resolve->state) {
312
313
      case CACHE_STATE_PENDING:
        /* add us to the pending list */
314
        pending_connection = tor_malloc_zero(
315
                                      sizeof(pending_connection_t));
316
        pending_connection->conn = exitconn;
Roger Dingledine's avatar
   
Roger Dingledine committed
317
318
        pending_connection->next = resolve->pending_connections;
        resolve->pending_connections = pending_connection;
319
        log_debug(LD_EXIT,"Connection (fd %d) waiting for pending DNS "
320
321
                  "resolve of %s",
                  exitconn->s, escaped_safe_str(exitconn->address));
322
        exitconn->state = EXIT_CONN_STATE_RESOLVING;
Roger Dingledine's avatar
   
Roger Dingledine committed
323
        return 0;
324
      case CACHE_STATE_VALID:
325
        exitconn->addr = resolve->addr;
326
327
        log_debug(LD_EXIT,"Connection (fd %d) found cached answer for %s",
                  exitconn->s, escaped_safe_str(exitconn->address));
328
        if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
329
          send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4, ttl);
330
        return 1;
331
      case CACHE_STATE_FAILED:
332
333
        log_debug(LD_EXIT,"Connection (fd %d) found cached error for %s",
                  exitconn->s, escaped_safe_str(exitconn->address));
334
        if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
335
          send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR, ttl);
336
        circ = circuit_get_by_edge_conn(exitconn);
337
338
        if (circ)
          circuit_detach_stream(circ, exitconn);
339
340
        if (!exitconn->marked_for_close)
          connection_free(exitconn);
341
342
        return -1;
    }
Roger Dingledine's avatar
Roger Dingledine committed
343
    tor_assert(0);
Roger Dingledine's avatar
Roger Dingledine committed
344
345
  }
  /* not there, need to add it */
346
  resolve = tor_malloc_zero(sizeof(cached_resolve_t));
Roger Dingledine's avatar
Roger Dingledine committed
347
  resolve->state = CACHE_STATE_PENDING;
348
  resolve->expire = now + MAX_DNS_ENTRY_AGE;
Nick Mathewson's avatar
Nick Mathewson committed
349
  strlcpy(resolve->address, exitconn->address, sizeof(resolve->address));
Roger Dingledine's avatar
Roger Dingledine committed
350
351

  /* add us to the pending list */
352
  pending_connection = tor_malloc_zero(sizeof(pending_connection_t));
Roger Dingledine's avatar
Roger Dingledine committed
353
354
  pending_connection->conn = exitconn;
  resolve->pending_connections = pending_connection;
355
  exitconn->state = EXIT_CONN_STATE_RESOLVING;
Roger Dingledine's avatar
Roger Dingledine committed
356

357
  insert_resolve(resolve);
358
359
  log_debug(LD_EXIT,"Assigning question %s to dnsworker.",
            escaped_safe_str(exitconn->address));
Roger Dingledine's avatar
Roger Dingledine committed
360
  return assign_to_dnsworker(exitconn);
361
362
}

363
/** Log an error and abort if conn is waiting for a DNS resolve.
364
 */
365
366
void
assert_connection_edge_not_dns_pending(connection_t *conn)
367
{
368
369
  pending_connection_t *pend;
  cached_resolve_t **resolve;
370

371
372
373
374
375
376
  HT_FOREACH(resolve, cache_map, &cache_root) {
    for (pend = (*resolve)->pending_connections;
         pend;
         pend = pend->next) {
      tor_assert(pend->conn != conn);
    }
377
  }
378
}
379

380
381
382
383
384
385
386
/** Log an error and abort if any connection waiting for a DNS resolve is
 * corrupted. */
void
assert_all_pending_dns_resolves_ok(void)
{
  pending_connection_t *pend;
  cached_resolve_t **resolve;
387

388
389
390
391
392
393
394
395
  HT_FOREACH(resolve, cache_map, &cache_root) {
    for (pend = (*resolve)->pending_connections;
         pend;
         pend = pend->next) {
      assert_connection_ok(pend->conn, 0);
      tor_assert(pend->conn->s == -1);
      tor_assert(!connection_in_array(pend->conn));
    }
396
397
398
  }
}

Nick Mathewson's avatar
Nick Mathewson committed
399
/** Remove <b>conn</b> from the list of connections waiting for conn-\>address.
400
 */
401
402
void
connection_dns_remove(connection_t *conn)
403
{
404
405
406
  pending_connection_t *pend, *victim;
  cached_resolve_t search;
  cached_resolve_t *resolve;
407

408
409
410
  tor_assert(conn->type == CONN_TYPE_EXIT);
  tor_assert(conn->state == EXIT_CONN_STATE_RESOLVING);

Nick Mathewson's avatar
Nick Mathewson committed
411
  strlcpy(search.address, conn->address, sizeof(search.address));
412

413
  resolve = HT_FIND(cache_map, &cache_root, &search);
414
  if (!resolve) {
415
416
    log_notice(LD_BUG, "Address %s is not pending. Dropping.",
               escaped_safe_str(conn->address));
417
418
419
    return;
  }

Roger Dingledine's avatar
Roger Dingledine committed
420
  tor_assert(resolve->pending_connections);
421
422
423
424
  assert_connection_ok(conn,0);

  pend = resolve->pending_connections;

425
  if (pend->conn == conn) {
426
    resolve->pending_connections = pend->next;
427
    tor_free(pend);
428
    log_debug(LD_EXIT, "First connection (fd %d) no longer waiting "
429
430
              "for resolve of %s",
              conn->s, escaped_safe_str(conn->address));
431
432
    return;
  } else {
433
434
    for ( ; pend->next; pend = pend->next) {
      if (pend->next->conn == conn) {
435
436
        victim = pend->next;
        pend->next = victim->next;
437
        tor_free(victim);
438
        log_debug(LD_EXIT,
439
440
                  "Connection (fd %d) no longer waiting for resolve of %s",
                  conn->s, escaped_safe_str(conn->address));
441
442
443
        return; /* more are pending */
      }
    }
Roger Dingledine's avatar
Roger Dingledine committed
444
    tor_assert(0); /* not reachable unless onlyconn not in pending list */
445
446
447
  }
}

Nick Mathewson's avatar
Nick Mathewson committed
448
449
450
/** Mark all connections waiting for <b>address</b> for close.  Then cancel
 * the resolve for <b>address</b> itself, and remove any cached results for
 * <b>address</b> from the cache.
451
 */
452
453
454
void
dns_cancel_pending_resolve(char *address)
{
455
456
457
  pending_connection_t *pend;
  cached_resolve_t search;
  cached_resolve_t *resolve;
458
  connection_t *pendconn;
459
  circuit_t *circ;
460

Nick Mathewson's avatar
Nick Mathewson committed
461
  strlcpy(search.address, address, sizeof(search.address));
462

463
  resolve = HT_FIND(cache_map, &cache_root, &search);
464
  if (!resolve) {
465
466
    log_notice(LD_BUG,"Address %s is not pending. Dropping.",
               escaped_safe_str(address));
467
468
469
    return;
  }

470
471
  if (!resolve->pending_connections) {
    /* XXX this should never trigger, but sometimes it does */
472
    log_warn(LD_BUG,
473
474
             "Bug: Address %s is pending but has no pending connections!",
             escaped_safe_str(address));
475
    tor_fragile_assert();
476
477
    return;
  }
Roger Dingledine's avatar
Roger Dingledine committed
478
  tor_assert(resolve->pending_connections);
479

480
  /* mark all pending connections to fail */
481
  log_debug(LD_EXIT,
482
483
             "Failing all connections waiting on DNS resolve of %s",
             escaped_safe_str(address));
484
  while (resolve->pending_connections) {
485
    pend = resolve->pending_connections;
486
    pend->conn->state = EXIT_CONN_STATE_RESOLVEFAILED;
487
    pendconn = pend->conn;
488
    tor_assert(pendconn->s == -1);
489
    if (!pendconn->marked_for_close) {
490
491
      connection_edge_end(pendconn, END_STREAM_REASON_RESOURCELIMIT,
                          pendconn->cpath_layer);
492
    }
493
    circ = circuit_get_by_edge_conn(pendconn);
494
495
    if (circ)
      circuit_detach_stream(circ, pendconn);
496
    connection_free(pendconn);
497
498
    resolve->pending_connections = pend->next;
    tor_free(pend);
499
500
  }

501
502
503
  dns_purge_resolve(resolve);
}

Nick Mathewson's avatar
Nick Mathewson committed
504
/** Remove <b>resolve</b> from the cache.
505
 */
506
static void
507
dns_purge_resolve(cached_resolve_t *resolve)
508
{
509
  cached_resolve_t *tmp;
510

511
  /* remove resolve from the linked list */
512
  if (resolve == oldest_cached_resolve) {
513
    oldest_cached_resolve = resolve->next;
514
    if (oldest_cached_resolve == NULL)
515
      newest_cached_resolve = NULL;
516
  } else {
517
    /* FFFF make it a doubly linked list if this becomes too slow */
518
519
520
521
    for (tmp=oldest_cached_resolve; tmp && tmp->next != resolve; tmp=tmp->next)
      ;
    tor_assert(tmp); /* it's got to be in the list, or we screwed up somewhere
                      * else */
522
523
    tmp->next = resolve->next; /* unlink it */

524
    if (newest_cached_resolve == resolve)
525
      newest_cached_resolve = tmp;
526
  }
527

528
  /* remove resolve from the map */
529
  HT_REMOVE(cache_map, &cache_root, resolve);
530

531
  tor_free(resolve);
532
533
}

Roger Dingledine's avatar
Roger Dingledine committed
534
/** Called on the OR side when a DNS worker tells us the outcome of a DNS
535
 * resolve: tell all pending connections about the result of the lookup, and
Nick Mathewson's avatar
Nick Mathewson committed
536
537
538
539
 * cache the value.  (<b>address</b> is a NUL-terminated string containing the
 * address to look up; <b>addr</b> is an IPv4 address in host order;
 * <b>outcome</b> is one of
 * DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}.
540
 */
541
static void
542
dns_found_answer(char *address, uint32_t addr, char outcome, uint32_t ttl)
543
{
544
545
546
  pending_connection_t *pend;
  cached_resolve_t search;
  cached_resolve_t *resolve;
547
  connection_t *pendconn;
548
  circuit_t *circ;
549

Nick Mathewson's avatar
Nick Mathewson committed
550
  strlcpy(search.address, address, sizeof(search.address));
551

552
  resolve = HT_FIND(cache_map, &cache_root, &search);
553
  if (!resolve) {
554
555
    log_info(LD_EXIT,"Resolved unasked address %s; caching anyway.",
             escaped_safe_str(address));
556
    resolve = tor_malloc_zero(sizeof(cached_resolve_t));
557
558
559
    resolve->state = (outcome == DNS_RESOLVE_SUCCEEDED) ?
      CACHE_STATE_VALID : CACHE_STATE_FAILED;
    resolve->addr = addr;
560
    resolve->expire = time(NULL) + ttl;
561
    insert_resolve(resolve);
562
    return;
563
564
  }

565
  if (resolve->state != CACHE_STATE_PENDING) {
566
567
    /* XXXX Maybe update addr? or check addr for consistency? Or let
     * VALID replace FAILED? */
568
569
    log_notice(LD_EXIT, "Resolved %s which was already resolved; ignoring",
               escaped_safe_str(address));
Roger Dingledine's avatar
Roger Dingledine committed
570
    tor_assert(resolve->pending_connections == NULL);
571
572
573
574
575
576
    return;
  }
  /* Removed this assertion: in fact, we'll sometimes get a double answer
   * to the same question.  This can happen when we ask one worker to resolve
   * X.Y.Z., then we cancel the request, and then we ask another worker to
   * resolve X.Y.Z. */
Roger Dingledine's avatar
Roger Dingledine committed
577
  /* tor_assert(resolve->state == CACHE_STATE_PENDING); */
578

579
  resolve->addr = addr;
580
  resolve->expire = time(NULL) + ttl;
581
  if (outcome == DNS_RESOLVE_SUCCEEDED)
582
583
584
585
    resolve->state = CACHE_STATE_VALID;
  else
    resolve->state = CACHE_STATE_FAILED;

586
  while (resolve->pending_connections) {
587
    pend = resolve->pending_connections;
588
    assert_connection_ok(pend->conn,time(NULL));
589
    pend->conn->addr = resolve->addr;
590
591
    pendconn = pend->conn; /* don't pass complex things to the
                              connection_mark_for_close macro */
592

593
    if (resolve->state == CACHE_STATE_FAILED) {
594
      /* prevent double-remove. */
595
      pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED;
596
      if (pendconn->purpose == EXIT_PURPOSE_CONNECT) {
597
598
        connection_edge_end(pendconn, END_STREAM_REASON_RESOLVEFAILED,
                            pendconn->cpath_layer);
599
        /* This detach must happen after we send the end cell. */
600
        circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
601
      } else {
602
        send_resolved_cell(pendconn, RESOLVED_TYPE_ERROR, ttl);
603
        /* This detach must happen after we send the resolved cell. */
604
        circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
605
      }
606
      connection_free(pendconn);
Roger Dingledine's avatar
Roger Dingledine committed
607
    } else {
608
609
610
611
      if (pendconn->purpose == EXIT_PURPOSE_CONNECT) {
        /* prevent double-remove. */
        pend->conn->state = EXIT_CONN_STATE_CONNECTING;

612
        circ = circuit_get_by_edge_conn(pend->conn);
613
614
615
616
617
        tor_assert(circ);
        /* unlink pend->conn from resolving_streams, */
        circuit_detach_stream(circ, pend->conn);
        /* and link it to n_streams */
        pend->conn->next_stream = circ->n_streams;
618
        pend->conn->on_circuit = circ;
619
620
621
622
623
624
625
        circ->n_streams = pend->conn;

        connection_exit_connect(pend->conn);
      } else {
        /* prevent double-remove.  This isn't really an accurate state,
         * but it does the right thing. */
        pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED;
626
        send_resolved_cell(pendconn, RESOLVED_TYPE_IPV4, ttl);
627
        circ = circuit_get_by_edge_conn(pendconn);
628
629
630
631
        tor_assert(circ);
        circuit_detach_stream(circ, pendconn);
        connection_free(pendconn);
      }
Roger Dingledine's avatar
Roger Dingledine committed
632
    }
633
634
    resolve->pending_connections = pend->next;
    tor_free(pend);
635
  }
636

637
  if (outcome == DNS_RESOLVE_FAILED_TRANSIENT) { /* remove from cache */
638
639
    dns_purge_resolve(resolve);
  }
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
#ifndef USE_EVENTDNS
/** Find or spawn a dns worker process to handle resolving
 * <b>exitconn</b>-\>address; tell that dns worker to begin resolving.
 */
static int
assign_to_dnsworker(connection_t *exitconn)
{
  connection_t *dnsconn;
  unsigned char len;

  tor_assert(exitconn->state == EXIT_CONN_STATE_RESOLVING);
  tor_assert(exitconn->s == -1);

  /* respawn here, to be sure there are enough */
  if (spawn_enough_dnsworkers() < 0) {
    goto err;
  }

  dnsconn = connection_get_by_type_state(CONN_TYPE_DNSWORKER,
                                         DNSWORKER_STATE_IDLE);

  if (!dnsconn) {
    log_warn(LD_EXIT,"no idle dns workers. Failing.");
    if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
      send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR_TRANSIENT, 0);
    goto err;
  }

  log_debug(LD_EXIT,
            "Connection (fd %d) needs to resolve %s; assigning "
            "to DNSWorker (fd %d)", exitconn->s,
            escaped_safe_str(exitconn->address), dnsconn->s);

  tor_free(dnsconn->address);
  dnsconn->address = tor_strdup(exitconn->address);
  dnsconn->state = DNSWORKER_STATE_BUSY;
  num_dnsworkers_busy++;

  len = strlen(dnsconn->address);
  connection_write_to_buf((char*)&len, 1, dnsconn);
  connection_write_to_buf(dnsconn->address, len, dnsconn);

  return 0;
err:
  dns_cancel_pending_resolve(exitconn->address); /* also sends end and frees */
  return -1;
}

690
691
/******************************************************************/

Nick Mathewson's avatar
Nick Mathewson committed
692
/*
693
 * Connection between OR and dnsworker
Nick Mathewson's avatar
Nick Mathewson committed
694
 */
695

Roger Dingledine's avatar
Roger Dingledine committed
696
/** Write handler: called when we've pushed a request to a dnsworker. */
697
698
699
int
connection_dns_finished_flushing(connection_t *conn)
{
700
701
  tor_assert(conn);
  tor_assert(conn->type == CONN_TYPE_DNSWORKER);
702
703
704
705
  connection_stop_writing(conn);
  return 0;
}

706
707
708
int
connection_dns_reached_eof(connection_t *conn)
{
709
  log_warn(LD_EXIT,"Read eof. Worker died unexpectedly.");
710
  if (conn->state == DNSWORKER_STATE_BUSY) {
711
712
713
714
    /* don't cancel the resolve here -- it would be cancelled in
     * connection_about_to_close_connection(), since conn is still
     * in state BUSY
     */
715
716
717
718
719
720
721
722
    num_dnsworkers_busy--;
  }
  num_dnsworkers--;
  connection_mark_for_close(conn);
  return 0;
}

/** Read handler: called when we get data from a dnsworker. See
723
724
 * if we have a complete answer.  If so, call dns_found_answer on the
 * result.  If not, wait.  Returns 0. */
725
726
727
int
connection_dns_process_inbuf(connection_t *conn)
{
728
  char success;
729
  uint32_t addr;
730
  int ttl;
731

732
733
  tor_assert(conn);
  tor_assert(conn->type == CONN_TYPE_DNSWORKER);
734

735
  if (conn->state != DNSWORKER_STATE_BUSY && buf_datalen(conn->inbuf)) {
736
737
    log_warn(LD_BUG,
             "Bug: read data (%d bytes) from an idle dns worker (fd %d, "
738
739
             "address %s). Please report.", (int)buf_datalen(conn->inbuf),
             conn->s, escaped_safe_str(conn->address));
740
    tor_fragile_assert();
741
742
743
744
745
746
747

    /* Pull it off the buffer anyway, or it will just stay there.
     * Keep pulling things off because sometimes we get several
     * answers at once (!). */
    while (buf_datalen(conn->inbuf)) {
      connection_fetch_from_buf(&success,1,conn);
      connection_fetch_from_buf((char *)&addr,sizeof(uint32_t),conn);
748
      log_warn(LD_EXIT,"Discarding idle dns answer (success %d, addr %d.)",
749
               success, addr);
750
    }
751
752
    return 0;
  }
753
  if (buf_datalen(conn->inbuf) < 5) /* entire answer available? */
754
    return 0; /* not yet */
755
  tor_assert(conn->state == DNSWORKER_STATE_BUSY);
Roger Dingledine's avatar
Roger Dingledine committed
756
  tor_assert(buf_datalen(conn->inbuf) == 5);
757

758
759
  connection_fetch_from_buf(&success,1,conn);
  connection_fetch_from_buf((char *)&addr,sizeof(uint32_t),conn);
760

761
762
  log_debug(LD_EXIT, "DNSWorker (fd %d) returned answer for %s",
            conn->s, escaped_safe_str(conn->address));
763

Roger Dingledine's avatar
Roger Dingledine committed
764
765
  tor_assert(success >= DNS_RESOLVE_FAILED_TRANSIENT);
  tor_assert(success <= DNS_RESOLVE_SUCCEEDED);
766
767
768

  ttl = (success == DNS_RESOLVE_FAILED_TRANSIENT) ? 0 : MAX_DNS_ENTRY_AGE;
  dns_found_answer(conn->address, ntohl(addr), success, ttl);
769

770
  tor_free(conn->address);
771
  conn->address = tor_strdup("<idle>");
772
  conn->state = DNSWORKER_STATE_IDLE;
Roger Dingledine's avatar
Roger Dingledine committed
773
  num_dnsworkers_busy--;
774
775
776
777
778
  if (conn->timestamp_created < last_rotation_time) {
    connection_mark_for_close(conn);
    num_dnsworkers--;
    spawn_enough_dnsworkers();
  }
779
780
781
  return 0;
}

782
783
784
/** Close and re-open all idle dnsworkers; schedule busy ones to be closed
 * and re-opened once they're no longer busy.
 **/
785
786
void
dnsworkers_rotate(void)
787
788
789
790
791
792
793
794
{
  connection_t *dnsconn;
  while ((dnsconn = connection_get_by_type_state(CONN_TYPE_DNSWORKER,
                                                 DNSWORKER_STATE_IDLE))) {
    connection_mark_for_close(dnsconn);
    num_dnsworkers--;
  }
  last_rotation_time = time(NULL);
795
796
  if (server_mode(get_options()))
    spawn_enough_dnsworkers();
797
798
}

Roger Dingledine's avatar
Roger Dingledine committed
799
/** Implementation for DNS workers; this code runs in a separate
800
801
802
 * execution context.  It takes as its argument an fdarray as returned
 * by socketpair(), and communicates via fdarray[1].  The protocol is
 * as follows:
Nick Mathewson's avatar
Nick Mathewson committed
803
804
805
806
807
808
 *    - The OR says:
 *         - ADDRESSLEN [1 byte]
 *         - ADDRESS    [ADDRESSLEN bytes]
 *    - The DNS worker does the lookup, and replies:
 *         - OUTCOME    [1 byte]
 *         - IP         [4 bytes]
809
810
811
812
813
814
815
 *
 * OUTCOME is one of DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}.
 * IP is in host order.
 *
 * The dnsworker runs indefinitely, until its connection is closed or an error
 * occurs.
 */
816
817
818
static int
dnsworker_main(void *data)
{
819
820
  char address[MAX_ADDRESSLEN];
  unsigned char address_len;
821
  char *log_address;
822
  char answer[5];
Roger Dingledine's avatar
Roger Dingledine committed
823
  uint32_t ip;
824
  int *fdarray = data;
Roger Dingledine's avatar
Roger Dingledine committed
825
  int fd;
826
  int result;
827

828
829
  /* log_fn(LOG_NOTICE,"After spawn: fdarray @%d has %d:%d", (int)fdarray,
   * fdarray[0],fdarray[1]); */
830

831
  fd = fdarray[1]; /* this side is ours */
832
#ifndef TOR_IS_MULTITHREADED
833
834
  tor_close_socket(fdarray[0]); /* this is the side of the socketpair the
                                 * parent uses */
835
  tor_free_all(1); /* so the child doesn't hold the parent's fd's open */
836
  handle_signals(0); /* ignore interrupts from the keyboard, etc */
837
838
#endif
  tor_free(data);
839

840
  for (;;) {
841
    int r;
842

843
844
    if ((r = recv(fd, &address_len, 1, 0)) != 1) {
      if (r == 0) {
845
846
        log_info(LD_EXIT,"DNS worker exiting because Tor process closed "
                 "connection (either pruned idle dnsworker or died).");
847
      } else {
848
849
850
851
        log_info(LD_EXIT,"DNS worker exiting because of error on connection "
                 "to Tor process.");
        log_info(LD_EXIT,"(Error on %d was %s)", fd,
                 tor_socket_strerror(tor_socket_errno(fd)));
852
      }
853
      tor_close_socket(fd);
854
      crypto_thread_cleanup();
855
      spawn_exit();