dns.c 25.9 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
  }

Roger Dingledine's avatar
Roger Dingledine committed
571
  tor_assert(conn->state == DNSWORKER_STATE_BUSY);
572
  if(buf_datalen(conn->inbuf) < 5) /* entire answer available? */
573
    return 0; /* not yet */
Roger Dingledine's avatar
Roger Dingledine committed
574
  tor_assert(buf_datalen(conn->inbuf) == 5);
575

576
577
  connection_fetch_from_buf(&success,1,conn);
  connection_fetch_from_buf((char *)&addr,sizeof(uint32_t),conn);
578

579
580
581
  log_fn(LOG_DEBUG, "DNSWorker (fd %d) returned answer for '%s'",
         conn->s, conn->address);

Roger Dingledine's avatar
Roger Dingledine committed
582
583
  tor_assert(success >= DNS_RESOLVE_FAILED_TRANSIENT);
  tor_assert(success <= DNS_RESOLVE_SUCCEEDED);
584
  dns_found_answer(conn->address, addr, success);
585

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

598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
/** 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
613
/** Implementation for DNS workers; this code runs in a separate
614
615
616
 * 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
617
618
619
620
621
622
 *    - The OR says:
 *         - ADDRESSLEN [1 byte]
 *         - ADDRESS    [ADDRESSLEN bytes]
 *    - The DNS worker does the lookup, and replies:
 *         - OUTCOME    [1 byte]
 *         - IP         [4 bytes]
623
624
625
626
627
628
629
 *
 * 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
630
static int dnsworker_main(void *data) {
631
632
  char address[MAX_ADDRESSLEN];
  unsigned char address_len;
633
  char answer[5];
Roger Dingledine's avatar
Roger Dingledine committed
634
  uint32_t ip;
635
  int *fdarray = data;
Roger Dingledine's avatar
Roger Dingledine committed
636
  int fd;
637
  int result;
638

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

  for(;;) {

648
    if(recv(fd, &address_len, 1, 0) != 1) {
649
      log_fn(LOG_INFO,"dnsworker exiting because tor process died.");
650
      spawn_exit();
651
652
    }

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

659
660
661
662
663
    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) {
664
      case 1:
665
/* XXX008 result can never be 1, because we set it to -1 above on error */
666
667
        log_fn(LOG_INFO,"Could not resolve dest addr %s (transient).",address);
        answer[0] = DNS_RESOLVE_FAILED_TRANSIENT;
668
669
        break;
      case -1:
670
671
        log_fn(LOG_INFO,"Could not resolve dest addr %s (permanent).",address);
        answer[0] = DNS_RESOLVE_FAILED_PERMANENT;
672
        break;
673
674
675
      case 0:
        log_fn(LOG_INFO,"Resolved address '%s'.",address);
        answer[0] = DNS_RESOLVE_SUCCEEDED;
676
        break;
677
    }
Roger Dingledine's avatar
Roger Dingledine committed
678
    set_uint32(answer+1, ip);
679
680
681
682
    if(write_all(fd, answer, 5, 1) != 5) {
      log_fn(LOG_ERR,"writing answer failed. Child exiting.");
      spawn_exit();
    }
683
  }
684
  return 0; /* windows wants this function to return an int */
685
686
}

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

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

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

  conn = connection_new(CONN_TYPE_DNSWORKER);

706
  set_socket_nonblocking(fd[0]);
707
708
709

  /* 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
710
  conn->address = tor_strdup("<unused>");
711
712

  if(connection_add(conn) < 0) { /* no space, forget it */
Roger Dingledine's avatar
Roger Dingledine committed
713
    log_fn(LOG_WARN,"connection_add failed. Giving up.");
714
715
716
717
718
719
720
721
722
723
    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
724
/** If we have too many or too few DNS workers, spawn or kill some.
725
 */
Roger Dingledine's avatar
Roger Dingledine committed
726
727
static void spawn_enough_dnsworkers(void) {
  int num_dnsworkers_needed; /* aim to have 1 more than needed,
728
                           * but no less than min and no more than max */
729
730
  connection_t *dnsconn;

731
  /* XXX This may not be the best strategy. Maybe we should queue pending
732
733
734
735
736
737
   *     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
738
   */
Roger Dingledine's avatar
Roger Dingledine committed
739
  if(num_dnsworkers_busy == MAX_DNSWORKERS) {
740
741
742
    /* We always want at least one worker idle.
     * So find the oldest busy worker and kill it.
     */
743
744
    dnsconn = connection_get_by_type_state_lastwritten(CONN_TYPE_DNSWORKER,
                                                       DNSWORKER_STATE_BUSY);
Roger Dingledine's avatar
Roger Dingledine committed
745
    tor_assert(dnsconn);
746

747
748
    log_fn(LOG_WARN, "%d DNS workers are spawned; all are busy. Killing one.",
           MAX_DNSWORKERS);
749

750
    connection_mark_for_close(dnsconn);
Roger Dingledine's avatar
Roger Dingledine committed
751
    num_dnsworkers_busy--;
752
    num_dnsworkers--;
753
  }
754

Roger Dingledine's avatar
Roger Dingledine committed
755
756
  if(num_dnsworkers_busy >= MIN_DNSWORKERS)
    num_dnsworkers_needed = num_dnsworkers_busy+1;
757
  else
Roger Dingledine's avatar
Roger Dingledine committed
758
    num_dnsworkers_needed = MIN_DNSWORKERS;
759

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

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

779
780
781
782
783
784
785
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/