dns.c 26.1 KB
Newer Older
1
2
3
4
/* Copyright 2003 Roger Dingledine. */
/* See LICENSE for licensing information */
/* $Id$ */

Roger Dingledine's avatar
Roger Dingledine committed
5
6
7
8
/**
 * \file dns.c
 * \brief Resolve hostnames in separate processes.
 **/
9

10
11
12
13
14
/* See http://elvin.dstc.com/ListArchive/elvin-dev/archive/2001/09/msg00027.html
 * for some approaches to asynchronous dns. We will want to switch once one of
 * them becomes more commonly available.
 */

15
#include "or.h"
16
#include "tree.h"
17

18
extern or_options_t options; /* command-line and config-file options */
Roger Dingledine's avatar
Roger Dingledine committed
19

Roger Dingledine's avatar
Roger Dingledine committed
20
/** Longest hostname we're willing to resolve. */
21
22
#define MAX_ADDRESSLEN 256

Roger Dingledine's avatar
Roger Dingledine committed
23
/** Maximum DNS processes to spawn. */
24
#define MAX_DNSWORKERS 50
Roger Dingledine's avatar
Roger Dingledine committed
25
/** Minimum DNS processes to spawn. */
26
#define MIN_DNSWORKERS 3
27

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

Roger Dingledine's avatar
Roger Dingledine committed
31
/** Possible outcomes from hostname lookup: permanent failure,
32
 * transient (retryable) failure, and success. */
33
34
35
36
#define DNS_RESOLVE_FAILED_TRANSIENT 1
#define DNS_RESOLVE_FAILED_PERMANENT 2
#define DNS_RESOLVE_SUCCEEDED 3

37
/** How many dnsworkers we have running right now. */
38
static int num_dnsworkers=0;
39
/** How many of the running dnsworkers have an assigned task right now. */
40
41
42
static int num_dnsworkers_busy=0;
/** When did we last rotate the dnsworkers? */
static time_t last_rotation_time=0;
43

Roger Dingledine's avatar
Roger Dingledine committed
44
/** Linked list of connections waiting for a DNS answer. */
45
46
47
48
49
struct pending_connection_t {
  struct connection_t *conn;
  struct pending_connection_t *next;
};

Roger Dingledine's avatar
Roger Dingledine committed
50
/** A DNS request: possibly completed, possibly pending; cached_resolve
51
52
53
 * structs are stored at the OR side in a splay tree, and as a linked
 * list from oldest to newest.
 */
54
55
struct cached_resolve {
  SPLAY_ENTRY(cached_resolve) node;
56
  char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */
57
  uint32_t addr; /**< IPv4 addr for <b>address</b>. */
58
  char state; /**< 0 is pending; 1 means answer is valid; 2 means resolve failed. */
59
60
61
#define CACHE_STATE_PENDING 0
#define CACHE_STATE_VALID 1
#define CACHE_STATE_FAILED 2
62
  uint32_t expire; /**< Remove items from cache after this time. */
63
64
65
66
  struct pending_connection_t *pending_connections;
  struct cached_resolve *next;
};

67
68
69
70
static void purge_expired_resolves(uint32_t now);
static int assign_to_dnsworker(connection_t *exitconn);
static void dns_purge_resolve(struct cached_resolve *resolve);
static void dns_found_answer(char *address, uint32_t addr, char outcome);
Nick Mathewson's avatar
Nick Mathewson committed
71
static int dnsworker_main(void *data);
72
73
static int spawn_dnsworker(void);
static void spawn_enough_dnsworkers(void);
74
static void send_resolved_cell(connection_t *conn, uint8_t answer_type);
75

76
/** Splay tree of cached_resolve objects. */
77
static SPLAY_HEAD(cache_tree, cached_resolve) cache_root;
78

Roger Dingledine's avatar
Roger Dingledine committed
79
/** Function to compare hashed resolves on their addresses; used to
80
 * implement splay trees. */
81
82
static int compare_cached_resolves(struct cached_resolve *a,
                                   struct cached_resolve *b) {
83
  /* make this smarter one day? */
84
  return strncasecmp(a->address, b->address, MAX_ADDRESSLEN);
85
86
87
88
89
}

SPLAY_PROTOTYPE(cache_tree, cached_resolve, node, compare_cached_resolves);
SPLAY_GENERATE(cache_tree, cached_resolve, node, compare_cached_resolves);

90
/** Initialize the DNS cache. */
91
static void init_cache_tree(void) {
92
93
94
  SPLAY_INIT(&cache_root);
}

Roger Dingledine's avatar
Roger Dingledine committed
95
/** Initialize the DNS subsystem; called by the OR process. */
96
97
void dns_init(void) {
  init_cache_tree();
98
  last_rotation_time=time(NULL);
Roger Dingledine's avatar
Roger Dingledine committed
99
  spawn_enough_dnsworkers();
100
}
101

102
/** Linked list of resolved addresses, oldest to newest. */
Roger Dingledine's avatar
Roger Dingledine committed
103
104
static struct cached_resolve *oldest_cached_resolve = NULL;
static struct cached_resolve *newest_cached_resolve = NULL;
105

Nick Mathewson's avatar
Nick Mathewson committed
106
/** Remove every cached_resolve whose <b>expire</b> time is before <b>now</b>
107
 * from the cache. */
108
109
static void purge_expired_resolves(uint32_t now) {
  struct cached_resolve *resolve;
110
111
  struct pending_connection_t *pend;
  connection_t *pendconn;
112
113
114
115
116
117

  /* this is fast because the linked list
   * oldest_cached_resolve is ordered by when they came in.
   */
  while(oldest_cached_resolve && (oldest_cached_resolve->expire < now)) {
    resolve = oldest_cached_resolve;
118
    log(LOG_DEBUG,"Forgetting old cached resolve (expires %lu)", (unsigned long)resolve->expire);
119
120
    if(resolve->state == CACHE_STATE_PENDING) {
      log_fn(LOG_WARN,"Expiring a dns resolve that's still pending. Forgot to cull it?");
121
122
123
124
125
126
127
128
129
130
131
    }
    if (resolve->pending_connections) {
      log_fn(LOG_WARN, "Closing pending connections on expiring DNS resolve!");
      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;
        connection_edge_end(pendconn, END_STREAM_REASON_MISC,
                            pendconn->cpath_layer);
132
        circuit_detach_stream(circuit_get_by_conn(pendconn), pendconn);
133
134
135
        connection_free(pendconn);
        tor_free(pend);
      }
136
    }
137
138
139
140
    oldest_cached_resolve = resolve->next;
    if(!oldest_cached_resolve) /* if there are no more, */
      newest_cached_resolve = NULL; /* then make sure the list's tail knows that too */
    SPLAY_REMOVE(cache_tree, &cache_root, resolve);
141
    tor_free(resolve);
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
static void send_resolved_cell(connection_t *conn, uint8_t answer_type)
{
  char buf[RELAY_PAYLOAD_SIZE];
  int buflen;

  buf[0] = answer_type;

  switch (answer_type)
    {
    case RESOLVED_TYPE_IPV4:
      buf[1] = 4;
      set_uint32(buf+2, htonl(conn->addr));
      buflen = 6;
      break;
    case RESOLVED_TYPE_ERROR_TRANSIENT:
    case RESOLVED_TYPE_ERROR:
      buf[1] = 24; /* length of "error resolving hostname" */
      strcpy(buf+2, "error resolving hostname");
      buflen = 26;
      break;
    default:
      tor_assert(0);
    }
  connection_edge_send_command(conn, circuit_get_by_conn(conn),
                               RELAY_COMMAND_RESOLVED, buf, buflen,
                               conn->cpath_layer);
}

Nick Mathewson's avatar
Nick Mathewson committed
173
174
/** 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.
175
 * If resolve failed, return -1.
176
177
178
179
180
181
182
183
184
 *
 * 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.
 */
int dns_resolve(connection_t *exitconn) {
  struct cached_resolve *resolve;
Roger Dingledine's avatar
   
Roger Dingledine committed
185
  struct cached_resolve search;
186
  struct pending_connection_t *pending_connection;
187
  struct in_addr in;
188
  uint32_t now = time(NULL);
189
  assert_connection_ok(exitconn, 0);
190
  tor_assert(exitconn->s == -1);
191

192
193
194
195
196
  /* 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);
    return 1;
197
198
199
200
  }

  /* then take this opportunity to see if there are any expired
   * resolves in the tree. */
201
  purge_expired_resolves(now);
202

203
204
  /* now check the tree to see if 'address' is already there. */
  strncpy(search.address, exitconn->address, MAX_ADDRESSLEN);
205
  search.address[MAX_ADDRESSLEN-1] = 0;
Roger Dingledine's avatar
   
Roger Dingledine committed
206
  resolve = SPLAY_FIND(cache_tree, &cache_root, &search);
207
  if(resolve) { /* already there */
208
209
210
    switch(resolve->state) {
      case CACHE_STATE_PENDING:
        /* add us to the pending list */
211
212
        pending_connection = tor_malloc_zero(
                                      sizeof(struct pending_connection_t));
213
        pending_connection->conn = exitconn;
Roger Dingledine's avatar
   
Roger Dingledine committed
214
215
        pending_connection->next = resolve->pending_connections;
        resolve->pending_connections = pending_connection;
216
217
        log_fn(LOG_DEBUG,"Connection (fd %d) waiting for pending DNS resolve of '%s'",
               exitconn->s, exitconn->address);
218
        exitconn->state = EXIT_CONN_STATE_RESOLVING;
Roger Dingledine's avatar
   
Roger Dingledine committed
219
        return 0;
220
      case CACHE_STATE_VALID:
221
        exitconn->addr = resolve->addr;
222
223
        log_fn(LOG_DEBUG,"Connection (fd %d) found cached answer for '%s'",
               exitconn->s, exitconn->address);
224
225
        if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
          send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4);
226
        return 1;
227
      case CACHE_STATE_FAILED:
228
229
        if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
          send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR);
230
231
        return -1;
    }
Roger Dingledine's avatar
Roger Dingledine committed
232
    tor_assert(0);
Roger Dingledine's avatar
Roger Dingledine committed
233
234
235
236
  }
  /* not there, need to add it */
  resolve = tor_malloc_zero(sizeof(struct cached_resolve));
  resolve->state = CACHE_STATE_PENDING;
237
238
  resolve->expire = now + MAX_DNS_ENTRY_AGE;
  strncpy(resolve->address, exitconn->address, MAX_ADDRESSLEN);
239
  resolve->address[MAX_ADDRESSLEN-1] = 0;
Roger Dingledine's avatar
Roger Dingledine committed
240
241

  /* add us to the pending list */
242
  pending_connection = tor_malloc_zero(sizeof(struct pending_connection_t));
Roger Dingledine's avatar
Roger Dingledine committed
243
  pending_connection->conn = exitconn;
244
  pending_connection->next = NULL;
Roger Dingledine's avatar
Roger Dingledine committed
245
  resolve->pending_connections = pending_connection;
246
  exitconn->state = EXIT_CONN_STATE_RESOLVING;
Roger Dingledine's avatar
Roger Dingledine committed
247
248
249
250
251
252

  /* add us to the linked list of resolves */
  if (!oldest_cached_resolve) {
    oldest_cached_resolve = resolve;
  } else {
    newest_cached_resolve->next = resolve;
253
  }
Roger Dingledine's avatar
Roger Dingledine committed
254
  newest_cached_resolve = resolve;
255

Roger Dingledine's avatar
Roger Dingledine committed
256
257
  SPLAY_INSERT(cache_tree, &cache_root, resolve);
  return assign_to_dnsworker(exitconn);
258
259
}

Roger Dingledine's avatar
Roger Dingledine committed
260
/** Find or spawn a dns worker process to handle resolving
Nick Mathewson's avatar
Nick Mathewson committed
261
 * <b>exitconn</b>-\>address; tell that dns worker to begin resolving.
262
 */
Roger Dingledine's avatar
Roger Dingledine committed
263
static int assign_to_dnsworker(connection_t *exitconn) {
264
265
  connection_t *dnsconn;
  unsigned char len;
266

Roger Dingledine's avatar
Roger Dingledine committed
267
  tor_assert(exitconn->state == EXIT_CONN_STATE_RESOLVING);
268
  tor_assert(exitconn->s == -1);
269

Roger Dingledine's avatar
Roger Dingledine committed
270
  spawn_enough_dnsworkers(); /* respawn here, to be sure there are enough */
271

272
  dnsconn = connection_get_by_type_state(CONN_TYPE_DNSWORKER, DNSWORKER_STATE_IDLE);
273
274

  if(!dnsconn) {
Roger Dingledine's avatar
Roger Dingledine committed
275
    log_fn(LOG_WARN,"no idle dns workers. Failing.");
276
    dns_cancel_pending_resolve(exitconn->address);
277
    send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR_TRANSIENT);
278
    return -1;
279
280
  }

281
282
283
  log_fn(LOG_DEBUG, "Connection (fd %d) needs to resolve '%s'; assigning to DNSWorker (fd %d)",
         exitconn->s, exitconn->address, dnsconn->s);

284
  tor_free(dnsconn->address);
285
  dnsconn->address = tor_strdup(exitconn->address);
286
  dnsconn->state = DNSWORKER_STATE_BUSY;
Roger Dingledine's avatar
Roger Dingledine committed
287
  num_dnsworkers_busy++;
288

289
  len = strlen(dnsconn->address);
290
291
  connection_write_to_buf(&len, 1, dnsconn);
  connection_write_to_buf(dnsconn->address, len, dnsconn);
292

Roger Dingledine's avatar
Roger Dingledine committed
293
//  log_fn(LOG_DEBUG,"submitted '%s'", exitconn->address);
294
295
296
  return 0;
}

Nick Mathewson's avatar
Nick Mathewson committed
297
/** Remove <b>conn</b> from the list of connections waiting for conn-\>address.
298
 */
299
300
301
302
303
304
void connection_dns_remove(connection_t *conn)
{
  struct pending_connection_t *pend, *victim;
  struct cached_resolve search;
  struct cached_resolve *resolve;

305
306
307
  tor_assert(conn->type == CONN_TYPE_EXIT);
  tor_assert(conn->state == EXIT_CONN_STATE_RESOLVING);

308
309
310
311
312
313
314
315
316
  strncpy(search.address, conn->address, MAX_ADDRESSLEN);
  search.address[MAX_ADDRESSLEN-1] = 0;

  resolve = SPLAY_FIND(cache_tree, &cache_root, &search);
  if(!resolve) {
    log_fn(LOG_WARN,"Address '%s' is not pending. Dropping.", conn->address);
    return;
  }

Roger Dingledine's avatar
Roger Dingledine committed
317
  tor_assert(resolve->pending_connections);
318
319
320
321
322
323
  assert_connection_ok(conn,0);

  pend = resolve->pending_connections;

  if(pend->conn == conn) {
    resolve->pending_connections = pend->next;
324
    tor_free(pend);
325
    log_fn(LOG_DEBUG, "First connection (fd %d) no longer waiting for resolve of '%s'",
326
327
328
329
330
331
332
           conn->s, conn->address);
    return;
  } else {
    for( ; pend->next; pend = pend->next) {
      if(pend->next->conn == conn) {
        victim = pend->next;
        pend->next = victim->next;
333
        tor_free(victim);
334
335
336
337
338
        log_fn(LOG_DEBUG, "Connection (fd %d) no longer waiting for resolve of '%s'",
               conn->s, conn->address);
        return; /* more are pending */
      }
    }
Roger Dingledine's avatar
Roger Dingledine committed
339
    tor_assert(0); /* not reachable unless onlyconn not in pending list */
340
341
342
  }
}

Roger Dingledine's avatar
Roger Dingledine committed
343
/** Log an error and abort if conn is waiting for a DNS resolve.
344
 */
345
346
347
348
349
350
351
352
void assert_connection_edge_not_dns_pending(connection_t *conn) {
  struct pending_connection_t *pend;
  struct cached_resolve *resolve;

  SPLAY_FOREACH(resolve, cache_tree, &cache_root) {
    for(pend = resolve->pending_connections;
        pend;
        pend = pend->next) {
Roger Dingledine's avatar
Roger Dingledine committed
353
      tor_assert(pend->conn != conn);
354
355
356
357
    }
  }
}

Roger Dingledine's avatar
Roger Dingledine committed
358
/** Log an error and abort if any connection waiting for a DNS resolve is
359
 * corrupted. */
360
361
362
363
364
365
366
367
368
void assert_all_pending_dns_resolves_ok(void) {
  struct pending_connection_t *pend;
  struct cached_resolve *resolve;

  SPLAY_FOREACH(resolve, cache_tree, &cache_root) {
    for(pend = resolve->pending_connections;
        pend;
        pend = pend->next) {
      assert_connection_ok(pend->conn, 0);
369
370
      tor_assert(pend->conn->s == -1);
      tor_assert(!connection_in_array(pend->conn));
371
372
373
374
    }
  }
}

Nick Mathewson's avatar
Nick Mathewson committed
375
376
377
/** 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.
378
 */
379
380
void dns_cancel_pending_resolve(char *address) {
  struct pending_connection_t *pend;
381
  struct cached_resolve search;
382
  struct cached_resolve *resolve;
383
  connection_t *pendconn;
384

385
  strncpy(search.address, address, MAX_ADDRESSLEN);
386
  search.address[MAX_ADDRESSLEN-1] = 0;
387
388
389

  resolve = SPLAY_FIND(cache_tree, &cache_root, &search);
  if(!resolve) {
390
    log_fn(LOG_WARN,"Address '%s' is not pending. Dropping.", address);
391
392
393
    return;
  }

Roger Dingledine's avatar
Roger Dingledine committed
394
  tor_assert(resolve->pending_connections);
395

396
397
398
399
  /* mark all pending connections to fail */
  log_fn(LOG_DEBUG, "Failing all connections waiting on DNS resolve of '%s'",
         address);
  while(resolve->pending_connections) {
400
    pend = resolve->pending_connections;
401
    pend->conn->state = EXIT_CONN_STATE_RESOLVEFAILED;
402
    pendconn = pend->conn;
403
    tor_assert(pendconn->s == -1);
404
405
406
    if(!pendconn->marked_for_close) {
      connection_edge_end(pendconn, END_STREAM_REASON_MISC, pendconn->cpath_layer);
    }
407
    circuit_detach_stream(circuit_get_by_conn(pendconn), pendconn);
408
    connection_free(pendconn);
409
410
    resolve->pending_connections = pend->next;
    tor_free(pend);
411
412
  }

413
414
415
  dns_purge_resolve(resolve);
}

Nick Mathewson's avatar
Nick Mathewson committed
416
/** Remove <b>resolve</b> from the cache.
417
 */
418
419
420
static void dns_purge_resolve(struct cached_resolve *resolve) {
  struct cached_resolve *tmp;

421
422
423
424
425
  /* remove resolve from the linked list */
  if(resolve == oldest_cached_resolve) {
    oldest_cached_resolve = resolve->next;
    if(oldest_cached_resolve == NULL)
      newest_cached_resolve = NULL;
426
  } else {
427
428
    /* FFFF make it a doubly linked list if this becomes too slow */
    for(tmp=oldest_cached_resolve; tmp && tmp->next != resolve; tmp=tmp->next) ;
Roger Dingledine's avatar
Roger Dingledine committed
429
    tor_assert(tmp); /* it's got to be in the list, or we screwed up somewhere else */
430
431
432
433
    tmp->next = resolve->next; /* unlink it */

    if(newest_cached_resolve == resolve)
      newest_cached_resolve = tmp;
434
  }
435
436
437
438

  /* remove resolve from the tree */
  SPLAY_REMOVE(cache_tree, &cache_root, resolve);

439
  tor_free(resolve);
440
441
}

Roger Dingledine's avatar
Roger Dingledine committed
442
/** Called on the OR side when a DNS worker tells us the outcome of a DNS
443
 * resolve: tell all pending connections about the result of the lookup, and
Nick Mathewson's avatar
Nick Mathewson committed
444
445
446
447
 * 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}.
448
 */
449
static void dns_found_answer(char *address, uint32_t addr, char outcome) {
450
451
452
  struct pending_connection_t *pend;
  struct cached_resolve search;
  struct cached_resolve *resolve;
453
  connection_t *pendconn;
454
  circuit_t *circ;
455

456
  strncpy(search.address, address, MAX_ADDRESSLEN);
457
  search.address[MAX_ADDRESSLEN-1] = 0;
458
459
460

  resolve = SPLAY_FIND(cache_tree, &cache_root, &search);
  if(!resolve) {
461
    log_fn(LOG_INFO,"Resolved unasked address '%s'? Dropping.", address);
462
463
    /* XXX Why drop?  Just because we don't care now doesn't mean we shouldn't
     * XXX cache the result for later. */
464
    return;
465
466
  }

467
  if (resolve->state != CACHE_STATE_PENDING) {
468
469
    /* XXXX Maybe update addr? or check addr for consistency? Or let
     * VALID replace FAILED? */
470
471
    log_fn(LOG_WARN, "Resolved '%s' which was already resolved; ignoring",
           address);
Roger Dingledine's avatar
Roger Dingledine committed
472
    tor_assert(resolve->pending_connections == NULL);
473
474
475
476
477
478
    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
479
  /* tor_assert(resolve->state == CACHE_STATE_PENDING); */
480

481
  resolve->addr = ntohl(addr);
482
  if(outcome == DNS_RESOLVE_SUCCEEDED)
483
484
485
486
487
488
    resolve->state = CACHE_STATE_VALID;
  else
    resolve->state = CACHE_STATE_FAILED;

  while(resolve->pending_connections) {
    pend = resolve->pending_connections;
489
    assert_connection_ok(pend->conn,time(NULL));
490
    pend->conn->addr = resolve->addr;
491
492
    pendconn = pend->conn; /* don't pass complex things to the
                              connection_mark_for_close macro */
493

494
    if(resolve->state == CACHE_STATE_FAILED) {
495
      /* prevent double-remove. */
496
      pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED;
497
      circuit_detach_stream(circuit_get_by_conn(pendconn), pendconn);
498
499
500
501
      if (pendconn->purpose == EXIT_PURPOSE_CONNECT)
        connection_edge_end(pendconn, END_STREAM_REASON_MISC, pendconn->cpath_layer);
      else
        send_resolved_cell(pendconn, RESOLVED_TYPE_ERROR);
502
      connection_free(pendconn);
Roger Dingledine's avatar
Roger Dingledine committed
503
    } else {
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
      if (pendconn->purpose == EXIT_PURPOSE_CONNECT) {
        /* prevent double-remove. */
        pend->conn->state = EXIT_CONN_STATE_CONNECTING;

        circ = circuit_get_by_conn(pend->conn);
        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;
        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;
        send_resolved_cell(pendconn, RESOLVED_TYPE_IPV4);
        circ = circuit_get_by_conn(pendconn);
        tor_assert(circ);
        circuit_detach_stream(circ, pendconn);
        connection_free(pendconn);
      }
Roger Dingledine's avatar
Roger Dingledine committed
527
    }
528
529
    resolve->pending_connections = pend->next;
    tor_free(pend);
530
  }
531
532
533
534

  if(outcome == DNS_RESOLVE_FAILED_TRANSIENT) { /* remove from cache */
    dns_purge_resolve(resolve);
  }
535
536
}

537
538
/******************************************************************/

Nick Mathewson's avatar
Nick Mathewson committed
539
/*
540
 * Connection between OR and dnsworker
Nick Mathewson's avatar
Nick Mathewson committed
541
 */
542

Roger Dingledine's avatar
Roger Dingledine committed
543
/** Write handler: called when we've pushed a request to a dnsworker. */
544
int connection_dns_finished_flushing(connection_t *conn) {
Roger Dingledine's avatar
Roger Dingledine committed
545
  tor_assert(conn && conn->type == CONN_TYPE_DNSWORKER);
546
547
548
549
  connection_stop_writing(conn);
  return 0;
}

Roger Dingledine's avatar
Roger Dingledine committed
550
/** Read handler: called when we get data from a dnsworker.  If the
551
552
553
 * connection is closed, mark the dnsworker as dead.  Otherwise, see
 * if we have a complete answer.  If so, call dns_found_answer on the
 * result.  If not, wait.  Returns 0. */
554
int connection_dns_process_inbuf(connection_t *conn) {
555
  char success;
556
  uint32_t addr;
557

Roger Dingledine's avatar
Roger Dingledine committed
558
  tor_assert(conn && conn->type == CONN_TYPE_DNSWORKER);
559
560

  if(conn->inbuf_reached_eof) {
561
    log_fn(LOG_WARN,"Read eof. Worker died unexpectedly.");
562
    if(conn->state == DNSWORKER_STATE_BUSY) {
563
      dns_cancel_pending_resolve(conn->address);
Roger Dingledine's avatar
Roger Dingledine committed
564
      num_dnsworkers_busy--;
565
    }
Roger Dingledine's avatar
Roger Dingledine committed
566
    num_dnsworkers--;
567
    connection_mark_for_close(conn);
568
    return 0;
569
570
  }

571
572
573
574
  if(conn->state != DNSWORKER_STATE_BUSY) {
    log_fn(LOG_WARN,"Bug: poll() indicated than an idle dns worker was readable. Please report.");
    return 0;
  }
575
  if(buf_datalen(conn->inbuf) < 5) /* entire answer available? */
576
    return 0; /* not yet */
577
  tor_assert(conn->state == DNSWORKER_STATE_BUSY);
Roger Dingledine's avatar
Roger Dingledine committed
578
  tor_assert(buf_datalen(conn->inbuf) == 5);
579

580
581
  connection_fetch_from_buf(&success,1,conn);
  connection_fetch_from_buf((char *)&addr,sizeof(uint32_t),conn);
582

583
584
585
  log_fn(LOG_DEBUG, "DNSWorker (fd %d) returned answer for '%s'",
         conn->s, conn->address);

Roger Dingledine's avatar
Roger Dingledine committed
586
587
  tor_assert(success >= DNS_RESOLVE_FAILED_TRANSIENT);
  tor_assert(success <= DNS_RESOLVE_SUCCEEDED);
588
  dns_found_answer(conn->address, addr, success);
589

590
  tor_free(conn->address);
591
  conn->address = tor_strdup("<idle>");
592
  conn->state = DNSWORKER_STATE_IDLE;
Roger Dingledine's avatar
Roger Dingledine committed
593
  num_dnsworkers_busy--;
594
595
596
597
598
  if (conn->timestamp_created < last_rotation_time) {
    connection_mark_for_close(conn);
    num_dnsworkers--;
    spawn_enough_dnsworkers();
  }
599
600
601
  return 0;
}

602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
/** Close and re-open all idle dnsworkers; schedule busy ones to be closed
 * and re-opened once they're no longer busy.
 **/
void dnsworkers_rotate(void)
{
  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);
  spawn_enough_dnsworkers();
}

Roger Dingledine's avatar
Roger Dingledine committed
617
/** Implementation for DNS workers; this code runs in a separate
618
619
620
 * 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
621
622
623
624
625
626
 *    - The OR says:
 *         - ADDRESSLEN [1 byte]
 *         - ADDRESS    [ADDRESSLEN bytes]
 *    - The DNS worker does the lookup, and replies:
 *         - OUTCOME    [1 byte]
 *         - IP         [4 bytes]
627
628
629
630
631
632
633
 *
 * 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.
 */
Nick Mathewson's avatar
Nick Mathewson committed
634
static int dnsworker_main(void *data) {
635
636
  char address[MAX_ADDRESSLEN];
  unsigned char address_len;
637
  char answer[5];
Roger Dingledine's avatar
Roger Dingledine committed
638
  uint32_t ip;
639
  int *fdarray = data;
Roger Dingledine's avatar
Roger Dingledine committed
640
  int fd;
641
  int result;
642

643
  tor_close_socket(fdarray[0]); /* this is the side of the socketpair the parent uses */
644
  fd = fdarray[1]; /* this side is ours */
645
#ifndef MS_WINDOWS
646
  connection_free_all(); /* so the child doesn't hold the parent's fd's open */
647
#endif
648
  handle_signals(0); /* ignore interrupts from the keyboard, etc */
649
650
651

  for(;;) {

652
    if(recv(fd, &address_len, 1, 0) != 1) {
653
      log_fn(LOG_INFO,"dnsworker exiting because tor process closed connection (either pruned idle dnsworker or died).");
654
      spawn_exit();
655
656
    }

657
    if(address_len && read_all(fd, address, address_len, 1) != address_len) {
658
      log_fn(LOG_ERR,"read hostname failed. Child exiting.");
659
      spawn_exit();
660
    }
661
    address[address_len] = 0; /* null terminate it */
662

663
664
665
666
667
    result = tor_lookup_hostname(address, &ip);
    /* Make 0.0.0.0 an error, so that we can use "0" to mean "no addr") */
    if (!ip)
      result = -1;
    switch (result) {
668
      case 1:
669
/* XXX008 result can never be 1, because we set it to -1 above on error */
670
671
        log_fn(LOG_INFO,"Could not resolve dest addr %s (transient).",address);
        answer[0] = DNS_RESOLVE_FAILED_TRANSIENT;
672
673
        break;
      case -1:
674
675
        log_fn(LOG_INFO,"Could not resolve dest addr %s (permanent).",address);
        answer[0] = DNS_RESOLVE_FAILED_PERMANENT;
676
        break;
677
678
679
      case 0:
        log_fn(LOG_INFO,"Resolved address '%s'.",address);
        answer[0] = DNS_RESOLVE_SUCCEEDED;
680
        break;
681
    }
Roger Dingledine's avatar
Roger Dingledine committed
682
    set_uint32(answer+1, ip);
683
684
685
686
    if(write_all(fd, answer, 5, 1) != 5) {
      log_fn(LOG_ERR,"writing answer failed. Child exiting.");
      spawn_exit();
    }
687
  }
688
  return 0; /* windows wants this function to return an int */
689
690
}

Roger Dingledine's avatar
Roger Dingledine committed
691
/** Launch a new DNS worker; return 0 on success, -1 on failure.
692
 */
Roger Dingledine's avatar
Roger Dingledine committed
693
static int spawn_dnsworker(void) {
694
695
696
  int fd[2];
  connection_t *conn;

697
  if(tor_socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
698
699
    log(LOG_ERR, "Couldn't construct socketpair: %s",
        tor_socket_strerror(tor_socket_errno(-1)));
700
    tor_cleanup();
701
702
703
    exit(1);
  }

704
  spawn_func(dnsworker_main, (void*)fd);
Roger Dingledine's avatar
Roger Dingledine committed
705
  log_fn(LOG_DEBUG,"just spawned a worker.");
706
  tor_close_socket(fd[1]); /* we don't need the worker's side of the pipe */
707
708
709

  conn = connection_new(CONN_TYPE_DNSWORKER);

710
  set_socket_nonblocking(fd[0]);
711
712
713

  /* set up conn so it's got all the data we need to remember */
  conn->s = fd[0];
Roger Dingledine's avatar
Roger Dingledine committed
714
  conn->address = tor_strdup("<unused>");
715
716

  if(connection_add(conn) < 0) { /* no space, forget it */
Roger Dingledine's avatar
Roger Dingledine committed
717
    log_fn(LOG_WARN,"connection_add failed. Giving up.");
718
719
720
721
722
723
724
725
726
727
    connection_free(conn); /* this closes fd[0] */
    return -1;
  }

  conn->state = DNSWORKER_STATE_IDLE;
  connection_start_reading(conn);

  return 0; /* success */
}

Roger Dingledine's avatar
Roger Dingledine committed
728
/** If we have too many or too few DNS workers, spawn or kill some.
729
 */
Roger Dingledine's avatar
Roger Dingledine committed
730
731
static void spawn_enough_dnsworkers(void) {
  int num_dnsworkers_needed; /* aim to have 1 more than needed,
732
                           * but no less than min and no more than max */
733
734
  connection_t *dnsconn;

735
  /* XXX This may not be the best strategy. Maybe we should queue pending
736
737
738
739
740
741
   *     requests until the old ones finish or time out: otherwise, if
   *     the connection requests come fast enough, we never get any DNS done. -NM
   * XXX But if we queue them, then the adversary can pile even more
   *     queries onto us, blocking legitimate requests for even longer.
   *     Maybe we should compromise and only kill if it's been at it for
   *     more than, e.g., 2 seconds. -RD
742
   */
Roger Dingledine's avatar
Roger Dingledine committed
743
  if(num_dnsworkers_busy == MAX_DNSWORKERS) {
744
745
746
    /* We always want at least one worker idle.
     * So find the oldest busy worker and kill it.
     */
747
748
    dnsconn = connection_get_by_type_state_lastwritten(CONN_TYPE_DNSWORKER,
                                                       DNSWORKER_STATE_BUSY);
Roger Dingledine's avatar
Roger Dingledine committed
749
    tor_assert(dnsconn);
750

751
752
    log_fn(LOG_WARN, "%d DNS workers are spawned; all are busy. Killing one.",
           MAX_DNSWORKERS);
753

754
    connection_mark_for_close(dnsconn);
Roger Dingledine's avatar
Roger Dingledine committed
755
    num_dnsworkers_busy--;
756
    num_dnsworkers--;
757
  }
758

Roger Dingledine's avatar
Roger Dingledine committed
759
760
  if(num_dnsworkers_busy >= MIN_DNSWORKERS)
    num_dnsworkers_needed = num_dnsworkers_busy+1;
761
  else
Roger Dingledine's avatar
Roger Dingledine committed
762
    num_dnsworkers_needed = MIN_DNSWORKERS;
763

Roger Dingledine's avatar
Roger Dingledine committed
764
765
  while(num_dnsworkers < num_dnsworkers_needed) {
    if(spawn_dnsworker() < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
766
      log(LOG_WARN,"spawn_enough_dnsworkers(): spawn failed!");
767
768
      return;
    }
Roger Dingledine's avatar
Roger Dingledine committed
769
    num_dnsworkers++;
770
771
  }

772
  while(num_dnsworkers > num_dnsworkers_busy+MAX_IDLE_DNSWORKERS) { /* too many idle? */
773
    /* cull excess workers */
774
775
    log_fn(LOG_WARN,"%d of %d dnsworkers are idle. Killing one.",
           num_dnsworkers-num_dnsworkers_needed, num_dnsworkers);
776
    dnsconn = connection_get_by_type_state(CONN_TYPE_DNSWORKER, DNSWORKER_STATE_IDLE);
Roger Dingledine's avatar
Roger Dingledine committed
777
    tor_assert(dnsconn);
778
    connection_mark_for_close(dnsconn);
Roger Dingledine's avatar
Roger Dingledine committed
779
    num_dnsworkers--;
780
  }
781
782
}

783
784
785
786
787
788
789
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/