dns.c 26.4 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 100
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
        log_fn(LOG_DEBUG,"Connection (fd %d) found cached error for '%s'",
               exitconn->s, exitconn->address);
230
231
        if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
          send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR);
232
233
        return -1;
    }
Roger Dingledine's avatar
Roger Dingledine committed
234
    tor_assert(0);
Roger Dingledine's avatar
Roger Dingledine committed
235
236
237
238
  }
  /* not there, need to add it */
  resolve = tor_malloc_zero(sizeof(struct cached_resolve));
  resolve->state = CACHE_STATE_PENDING;
239
240
  resolve->expire = now + MAX_DNS_ENTRY_AGE;
  strncpy(resolve->address, exitconn->address, MAX_ADDRESSLEN);
241
  resolve->address[MAX_ADDRESSLEN-1] = 0;
Roger Dingledine's avatar
Roger Dingledine committed
242
243

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

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

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

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

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

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

274
  dnsconn = connection_get_by_type_state(CONN_TYPE_DNSWORKER, DNSWORKER_STATE_IDLE);
275
276

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

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

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

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

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

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

307
308
309
  tor_assert(conn->type == CONN_TYPE_EXIT);
  tor_assert(conn->state == EXIT_CONN_STATE_RESOLVING);

310
311
312
313
314
315
316
317
318
  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
319
  tor_assert(resolve->pending_connections);
320
321
322
323
324
325
  assert_connection_ok(conn,0);

  pend = resolve->pending_connections;

  if(pend->conn == conn) {
    resolve->pending_connections = pend->next;
326
    tor_free(pend);
327
    log_fn(LOG_DEBUG, "First connection (fd %d) no longer waiting for resolve of '%s'",
328
329
330
331
332
333
334
           conn->s, conn->address);
    return;
  } else {
    for( ; pend->next; pend = pend->next) {
      if(pend->next->conn == conn) {
        victim = pend->next;
        pend->next = victim->next;
335
        tor_free(victim);
336
337
338
339
340
        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
341
    tor_assert(0); /* not reachable unless onlyconn not in pending list */
342
343
344
  }
}

Roger Dingledine's avatar
Roger Dingledine committed
345
/** Log an error and abort if conn is waiting for a DNS resolve.
346
 */
347
348
349
350
351
352
353
354
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
355
      tor_assert(pend->conn != conn);
356
357
358
359
    }
  }
}

Roger Dingledine's avatar
Roger Dingledine committed
360
/** Log an error and abort if any connection waiting for a DNS resolve is
361
 * corrupted. */
362
363
364
365
366
367
368
369
370
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);
371
372
      tor_assert(pend->conn->s == -1);
      tor_assert(!connection_in_array(pend->conn));
373
374
375
376
    }
  }
}

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

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

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

Roger Dingledine's avatar
Roger Dingledine committed
396
  tor_assert(resolve->pending_connections);
397

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

415
416
417
  dns_purge_resolve(resolve);
}

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

423
424
425
426
427
  /* 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;
428
  } else {
429
430
    /* 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
431
    tor_assert(tmp); /* it's got to be in the list, or we screwed up somewhere else */
432
433
434
435
    tmp->next = resolve->next; /* unlink it */

    if(newest_cached_resolve == resolve)
      newest_cached_resolve = tmp;
436
  }
437
438
439
440

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

441
  tor_free(resolve);
442
443
}

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

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

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

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

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

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

496
    if(resolve->state == CACHE_STATE_FAILED) {
497
      /* prevent double-remove. */
498
      pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED;
499
      if (pendconn->purpose == EXIT_PURPOSE_CONNECT) {
500
501
        connection_edge_end(pendconn, END_STREAM_REASON_RESOLVEFAILED, pendconn->cpath_layer);
        /* This detach must happen after we send the end cell. */
502
503
        circuit_detach_stream(circuit_get_by_conn(pendconn), pendconn);
      } else {
504
        send_resolved_cell(pendconn, RESOLVED_TYPE_ERROR);
505
506
507
        /* This detach must happen after we send the resolved cell. */
        circuit_detach_stream(circuit_get_by_conn(pendconn), pendconn);
      }
508
      connection_free(pendconn);
Roger Dingledine's avatar
Roger Dingledine committed
509
    } else {
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
      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
533
    }
534
535
    resolve->pending_connections = pend->next;
    tor_free(pend);
536
  }
537
538
539
540

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

543
544
/******************************************************************/

Nick Mathewson's avatar
Nick Mathewson committed
545
/*
546
 * Connection between OR and dnsworker
Nick Mathewson's avatar
Nick Mathewson committed
547
 */
548

Roger Dingledine's avatar
Roger Dingledine committed
549
/** Write handler: called when we've pushed a request to a dnsworker. */
550
int connection_dns_finished_flushing(connection_t *conn) {
Roger Dingledine's avatar
Roger Dingledine committed
551
  tor_assert(conn && conn->type == CONN_TYPE_DNSWORKER);
552
553
554
555
  connection_stop_writing(conn);
  return 0;
}

Roger Dingledine's avatar
Roger Dingledine committed
556
/** Read handler: called when we get data from a dnsworker.  If the
557
558
559
 * 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. */
560
int connection_dns_process_inbuf(connection_t *conn) {
561
  char success;
562
  uint32_t addr;
563

Roger Dingledine's avatar
Roger Dingledine committed
564
  tor_assert(conn && conn->type == CONN_TYPE_DNSWORKER);
565
566

  if(conn->inbuf_reached_eof) {
567
    log_fn(LOG_WARN,"Read eof. Worker died unexpectedly.");
568
    if(conn->state == DNSWORKER_STATE_BUSY) {
569
      dns_cancel_pending_resolve(conn->address);
Roger Dingledine's avatar
Roger Dingledine committed
570
      num_dnsworkers_busy--;
571
    }
Roger Dingledine's avatar
Roger Dingledine committed
572
    num_dnsworkers--;
573
    connection_mark_for_close(conn);
574
    return 0;
575
576
  }

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

586
587
  connection_fetch_from_buf(&success,1,conn);
  connection_fetch_from_buf((char *)&addr,sizeof(uint32_t),conn);
588

589
590
591
  log_fn(LOG_DEBUG, "DNSWorker (fd %d) returned answer for '%s'",
         conn->s, conn->address);

Roger Dingledine's avatar
Roger Dingledine committed
592
593
  tor_assert(success >= DNS_RESOLVE_FAILED_TRANSIENT);
  tor_assert(success <= DNS_RESOLVE_SUCCEEDED);
594
  dns_found_answer(conn->address, addr, success);
595

596
  tor_free(conn->address);
597
  conn->address = tor_strdup("<idle>");
598
  conn->state = DNSWORKER_STATE_IDLE;
Roger Dingledine's avatar
Roger Dingledine committed
599
  num_dnsworkers_busy--;
600
601
602
603
604
  if (conn->timestamp_created < last_rotation_time) {
    connection_mark_for_close(conn);
    num_dnsworkers--;
    spawn_enough_dnsworkers();
  }
605
606
607
  return 0;
}

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

649
  tor_close_socket(fdarray[0]); /* this is the side of the socketpair the parent uses */
650
  fd = fdarray[1]; /* this side is ours */
651
#ifndef MS_WINDOWS
652
  connection_free_all(); /* so the child doesn't hold the parent's fd's open */
653
#endif
654
  handle_signals(0); /* ignore interrupts from the keyboard, etc */
655
656
657

  for(;;) {

658
    if(recv(fd, &address_len, 1, 0) != 1) {
659
      log_fn(LOG_INFO,"dnsworker exiting because tor process closed connection (either pruned idle dnsworker or died).");
660
      spawn_exit();
661
662
    }

663
    if(address_len && read_all(fd, address, address_len, 1) != address_len) {
664
      log_fn(LOG_ERR,"read hostname failed. Child exiting.");
665
      spawn_exit();
666
    }
667
    address[address_len] = 0; /* null terminate it */
668

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

Roger Dingledine's avatar
Roger Dingledine committed
697
/** Launch a new DNS worker; return 0 on success, -1 on failure.
698
 */
Roger Dingledine's avatar
Roger Dingledine committed
699
static int spawn_dnsworker(void) {
700
701
702
  int fd[2];
  connection_t *conn;

703
  if(tor_socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
704
705
    log(LOG_ERR, "Couldn't construct socketpair: %s",
        tor_socket_strerror(tor_socket_errno(-1)));
706
    tor_cleanup();
707
708
709
    exit(1);
  }

710
  spawn_func(dnsworker_main, (void*)fd);
Roger Dingledine's avatar
Roger Dingledine committed
711
  log_fn(LOG_DEBUG,"just spawned a worker.");
712
  tor_close_socket(fd[1]); /* we don't need the worker's side of the pipe */
713
714
715

  conn = connection_new(CONN_TYPE_DNSWORKER);

716
  set_socket_nonblocking(fd[0]);
717
718
719

  /* 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
720
  conn->address = tor_strdup("<unused>");
721
722

  if(connection_add(conn) < 0) { /* no space, forget it */
Roger Dingledine's avatar
Roger Dingledine committed
723
    log_fn(LOG_WARN,"connection_add failed. Giving up.");
724
725
726
727
728
729
730
731
732
733
    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
734
/** If we have too many or too few DNS workers, spawn or kill some.
735
 */
Roger Dingledine's avatar
Roger Dingledine committed
736
737
static void spawn_enough_dnsworkers(void) {
  int num_dnsworkers_needed; /* aim to have 1 more than needed,
738
                           * but no less than min and no more than max */
739
740
  connection_t *dnsconn;

741
  /* XXX This may not be the best strategy. Maybe we should queue pending
742
743
744
745
746
747
   *     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
748
   */
Roger Dingledine's avatar
Roger Dingledine committed
749
  if(num_dnsworkers_busy == MAX_DNSWORKERS) {
750
751
752
    /* We always want at least one worker idle.
     * So find the oldest busy worker and kill it.
     */
753
754
    dnsconn = connection_get_by_type_state_lastwritten(CONN_TYPE_DNSWORKER,
                                                       DNSWORKER_STATE_BUSY);
Roger Dingledine's avatar
Roger Dingledine committed
755
    tor_assert(dnsconn);
756

757
758
    log_fn(LOG_WARN, "%d DNS workers are spawned; all are busy. Killing one.",
           MAX_DNSWORKERS);
759

760
    connection_mark_for_close(dnsconn);
Roger Dingledine's avatar
Roger Dingledine committed
761
    num_dnsworkers_busy--;
762
    num_dnsworkers--;
763
  }
764

Roger Dingledine's avatar
Roger Dingledine committed
765
766
  if(num_dnsworkers_busy >= MIN_DNSWORKERS)
    num_dnsworkers_needed = num_dnsworkers_busy+1;
767
  else
Roger Dingledine's avatar
Roger Dingledine committed
768
    num_dnsworkers_needed = MIN_DNSWORKERS;
769

Roger Dingledine's avatar
Roger Dingledine committed
770
771
  while(num_dnsworkers < num_dnsworkers_needed) {
    if(spawn_dnsworker() < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
772
      log(LOG_WARN,"spawn_enough_dnsworkers(): spawn failed!");
773
774
      return;
    }
Roger Dingledine's avatar
Roger Dingledine committed
775
    num_dnsworkers++;
776
777
  }

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

789
790
791
792
793
794
795
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/