main.c 21.1 KB
Newer Older
1
2
3
/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */
/* See LICENSE for licensing information */
/* $Id$ */
Roger Dingledine's avatar
Roger Dingledine committed
4
5
6
7
8

#include "or.h"

/********* START VARIABLES **********/

9
or_options_t options; /* command-line and config-file options */
10
int global_role;
Roger Dingledine's avatar
Roger Dingledine committed
11

Roger Dingledine's avatar
Roger Dingledine committed
12
static connection_t *connection_array[MAXCONNECTIONS] =
Roger Dingledine's avatar
Roger Dingledine committed
13
14
        { NULL };

Nick Mathewson's avatar
Nick Mathewson committed
15
static struct pollfd poll_array[MAXCONNECTIONS];
Roger Dingledine's avatar
Roger Dingledine committed
16

Roger Dingledine's avatar
Roger Dingledine committed
17
static int nfds=0; /* number of connections currently active */
Roger Dingledine's avatar
Roger Dingledine committed
18

19
static int please_dumpstats=0; /* whether we should dump stats during the loop */
20
static int please_fetch_directory=0; /* whether we should fetch a new directory */
21

Roger Dingledine's avatar
Roger Dingledine committed
22
/* private key */
Roger Dingledine's avatar
cleanup    
Roger Dingledine committed
23
static crypto_pk_env_t *privatekey;
Roger Dingledine's avatar
Roger Dingledine committed
24

25
26
routerinfo_t *my_routerinfo=NULL;

Roger Dingledine's avatar
Roger Dingledine committed
27
28
/********* END VARIABLES ************/

Roger Dingledine's avatar
cleanup    
Roger Dingledine committed
29
30
31
32
33
34
35
36
37
void setprivatekey(crypto_pk_env_t *k) {
  privatekey = k;
}

crypto_pk_env_t *getprivatekey(void) {
  assert(privatekey);
  return privatekey;
}

Roger Dingledine's avatar
Roger Dingledine committed
38
39
40
41
42
43
44
45
/****************************************************************************
*
* This section contains accessors and other methods on the connection_array
* and poll_array variables (which are global within this file and unavailable
* outside it).
*
****************************************************************************/

Roger Dingledine's avatar
Roger Dingledine committed
46
47
int connection_add(connection_t *conn) {

48
  if(nfds >= options.MaxConn-1) {
49
    log(LOG_INFO,"connection_add(): failing because nfds is too high.");
Roger Dingledine's avatar
Roger Dingledine committed
50
51
52
53
54
55
56
57
58
59
60
61
62
    return -1;
  }

  conn->poll_index = nfds;
  connection_set_poll_socket(conn);
  connection_array[nfds] = conn;

  /* zero these out here, because otherwise we'll inherit values from the previously freed one */
  poll_array[nfds].events = 0;
  poll_array[nfds].revents = 0;

  nfds++;

63
  log(LOG_INFO,"connection_add(): new conn type %d, socket %d, nfds %d.",conn->type, conn->s, nfds);
Roger Dingledine's avatar
Roger Dingledine committed
64
65
66
67
68
69
70
71
72
73
74
75
76
77

  return 0;
}

void connection_set_poll_socket(connection_t *conn) {
  poll_array[conn->poll_index].fd = conn->s;
}

int connection_remove(connection_t *conn) {
  int current_index;

  assert(conn);
  assert(nfds>0);

78
  log(LOG_INFO,"connection_remove(): removing socket %d, nfds now %d",conn->s, nfds-1);
79
80
81
82
  circuit_about_to_close_connection(conn); /* if it's an edge conn, remove it from the list
                                            * of conn's on this circuit. If it's not on an edge,
                                            * flush and send destroys for all circuits on this conn
                                            */
Roger Dingledine's avatar
Roger Dingledine committed
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

  current_index = conn->poll_index;
  if(current_index == nfds-1) { /* this is the end */
    nfds--;
    return 0;
  } 

  /* we replace this one with the one at the end, then free it */
  nfds--;
  poll_array[current_index].fd = poll_array[nfds].fd; 
  poll_array[current_index].events = poll_array[nfds].events;
  poll_array[current_index].revents = poll_array[nfds].revents;
  connection_array[current_index] = connection_array[nfds];
  connection_array[current_index]->poll_index = current_index;

  return 0;  
}

101
connection_t *connection_twin_get_by_addr_port(uint32_t addr, uint16_t port) {
102
103
104
105
106
  /* Find a connection to the router described by addr and port,
   *   or alternately any router which knows its key.
   * This connection *must* be in 'open' state.
   * If not, return NULL.
   */
107
108
  int i;
  connection_t *conn;
Roger Dingledine's avatar
Roger Dingledine committed
109
  routerinfo_t *router;
110
111
112

  /* first check if it's there exactly */
  conn = connection_exact_get_by_addr_port(addr,port);
113
  if(conn && connection_state_is_open(conn)) {
Roger Dingledine's avatar
Roger Dingledine committed
114
    log(LOG_INFO,"connection_twin_get_by_addr_port(): Found exact match.");
115
    return conn;
116
  }
117
118
119

  /* now check if any of the other open connections are a twin for this one */

Roger Dingledine's avatar
Roger Dingledine committed
120
121
122
123
124
125
126
  router = router_get_by_addr_port(addr,port);
  if(!router)
    return NULL;

  for(i=0;i<nfds;i++) {
    conn = connection_array[i];
    assert(conn);
127
    if(connection_state_is_open(conn) && !crypto_pk_cmp_keys(conn->pkey, router->pkey)) {
Roger Dingledine's avatar
Roger Dingledine committed
128
      log(LOG_INFO,"connection_twin_get_by_addr_port(): Found twin (%s).",conn->address);
Roger Dingledine's avatar
Roger Dingledine committed
129
130
131
      return conn;
    }
  }
132
133
134
135
136
137
  /* guess not */
  return NULL;

}

connection_t *connection_exact_get_by_addr_port(uint32_t addr, uint16_t port) {
Roger Dingledine's avatar
Roger Dingledine committed
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  int i;
  connection_t *conn;

  for(i=0;i<nfds;i++) {
    conn = connection_array[i];
    assert(conn);
    if(conn->addr == addr && conn->port == port)
       return conn;
  }
  return NULL;
}

connection_t *connection_get_by_type(int type) {
  int i;
  connection_t *conn;

  for(i=0;i<nfds;i++) {
    conn = connection_array[i];
    if(conn->type == type)
       return conn;
  }
159
160
161
  return NULL;
}

Roger Dingledine's avatar
Roger Dingledine committed
162
163
164
165
166
167
168
void connection_watch_events(connection_t *conn, short events) {

  assert(conn && conn->poll_index < nfds);

  poll_array[conn->poll_index].events = events;
}

169
170
171
172
void connection_stop_reading(connection_t *conn) {

  assert(conn && conn->poll_index < nfds);

173
  log(LOG_DEBUG,"connection_stop_reading() called.");
174
175
176
177
178
179
180
181
182
183
184
  if(poll_array[conn->poll_index].events & POLLIN)
    poll_array[conn->poll_index].events -= POLLIN;
}

void connection_start_reading(connection_t *conn) {

  assert(conn && conn->poll_index < nfds);

  poll_array[conn->poll_index].events |= POLLIN;
}

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
void connection_stop_writing(connection_t *conn) {

  assert(conn && conn->poll_index < nfds);

  if(poll_array[conn->poll_index].events & POLLOUT)
    poll_array[conn->poll_index].events -= POLLOUT;
}

void connection_start_writing(connection_t *conn) {

  assert(conn && conn->poll_index < nfds);

  poll_array[conn->poll_index].events |= POLLOUT;
}


Roger Dingledine's avatar
Roger Dingledine committed
201
202
203
204
205
206
207
208
void check_conn_read(int i) {
  int retval;
  connection_t *conn;

  if(poll_array[i].revents & POLLIN) { /* something to read */

    conn = connection_array[i];
    assert(conn);
209
//    log(LOG_DEBUG,"check_conn_read(): socket %d has something to read.",conn->s);
Roger Dingledine's avatar
Roger Dingledine committed
210
211
212
213
214

    if (conn->type == CONN_TYPE_OP_LISTENER) {
      retval = connection_op_handle_listener_read(conn);
    } else if (conn->type == CONN_TYPE_OR_LISTENER) {
      retval = connection_or_handle_listener_read(conn);
215
216
    } else if (conn->type == CONN_TYPE_AP_LISTENER) {
      retval = connection_ap_handle_listener_read(conn);
217
218
    } else if (conn->type == CONN_TYPE_DIR_LISTENER) {
      retval = connection_dir_handle_listener_read(conn);
Roger Dingledine's avatar
Roger Dingledine committed
219
220
    } else {
      retval = connection_read_to_buf(conn);
Roger Dingledine's avatar
Roger Dingledine committed
221
222
      if (retval < 0 && conn->type == CONN_TYPE_DIR && conn->state == DIR_CONN_STATE_CONNECTING) {
         /* it's a directory server and connecting failed: forget about this router */
223
         router_forget_router(conn->addr,conn->port); /* FIXME i don't think this function works. */
224
      }
Roger Dingledine's avatar
Roger Dingledine committed
225
226
      if (retval >= 0) { /* all still well */
        retval = connection_process_inbuf(conn);
227
//      log(LOG_DEBUG,"check_conn_read(): connection_process_inbuf returned %d.",retval);
228
229
230
231
        if(retval >= 0 && !connection_state_is_open(conn) && conn->receiver_bucket == 0) {
          log(LOG_DEBUG,"check_conn_read(): receiver bucket reached 0 before handshake finished. Closing.");
          retval = -1;
        }
Roger Dingledine's avatar
Roger Dingledine committed
232
233
      }
    }
234

Roger Dingledine's avatar
Roger Dingledine committed
235
    if(retval < 0) { /* this connection is broken. remove it */
236
      log(LOG_INFO,"check_conn_read(): Connection broken, removing."); 
Roger Dingledine's avatar
Roger Dingledine committed
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
      connection_remove(conn);
      connection_free(conn);
      if(i<nfds) { /* we just replaced the one at i with a new one.
                      process it too. */
        check_conn_read(i);
      }
    }
  }
}

void check_conn_write(int i) {
  int retval;
  connection_t *conn;

  if(poll_array[i].revents & POLLOUT) { /* something to write */

    conn = connection_array[i];
254
//    log(LOG_DEBUG,"check_conn_write(): socket %d wants to write.",conn->s);
Roger Dingledine's avatar
Roger Dingledine committed
255

256
    if(connection_is_listener(conn)) {
Roger Dingledine's avatar
Roger Dingledine committed
257
258
259
      log(LOG_DEBUG,"check_conn_write(): Got a listener socket. Can't happen!");
      retval = -1;
    } else {
260
      /* else it's an OP, OR, or exit */
Roger Dingledine's avatar
Roger Dingledine committed
261
262
263
264
265
266
267
268
269
270
271
272
      retval = connection_flush_buf(conn); /* conns in CONNECTING state will fall through... */
      if(retval == 0) { /* it's done flushing */
        retval = connection_finished_flushing(conn); /* ...and get handled here. */
      }
    }

    if(retval < 0) { /* this connection is broken. remove it. */
      log(LOG_DEBUG,"check_conn_write(): Connection broken, removing.");
      connection_remove(conn);
      connection_free(conn);
      if(i<nfds) { /* we just replaced the one at i with a new one.
                      process it too. */
273
        check_conn_write(i);
Roger Dingledine's avatar
Roger Dingledine committed
274
275
276
277
278
279
280
281
282
283
284
285
      }
    }
  }
}

void check_conn_marked(int i) {
  connection_t *conn;

  conn = connection_array[i];
  assert(conn);
  if(conn->marked_for_close) {
    log(LOG_DEBUG,"check_conn_marked(): Cleaning up connection.");
286
287
288
289
    if(conn->s >= 0) { /* might be an incomplete exit connection */
      /* FIXME there's got to be a better way to check for this -- and make other checks? */
      connection_flush_buf(conn); /* flush it first */
    }
Roger Dingledine's avatar
Roger Dingledine committed
290
291
292
293
    connection_remove(conn);
    connection_free(conn);
    if(i<nfds) { /* we just replaced the one at i with a new one.
                    process it too. */
Roger Dingledine's avatar
Roger Dingledine committed
294
      check_conn_marked(i);
Roger Dingledine's avatar
Roger Dingledine committed
295
296
297
298
    }
  }
}

299
300
int prepare_for_poll(int *timeout) {
  int i;
301
//  int need_to_wake_soon = 0;
302
303
304
  connection_t *conn = NULL;
  connection_t *tmpconn;
  struct timeval now, soonest;
305
306
307
  static long current_second = 0; /* from previous calls to gettimeofday */
  static long time_to_rebuild_directory = 0;
  static long time_to_fetch_directory = 0;
308
  int ms_until_conn;
309
  cell_t cell;
310
311
312
313

  if(gettimeofday(&now,NULL) < 0)
    return -1;

314
315
316
317
318
  if(options.Role & ROLE_DIR_SERVER) {
    if(time_to_rebuild_directory < now.tv_sec) {
      /* it's time to rebuild our directory */
      if(time_to_rebuild_directory == 0) { 
        /* we just started up. if we build a directory now it will be meaningless. */
319
320
        log(LOG_DEBUG,"prepare_for_poll(): Delaying initial dir build for 10 seconds.");
        time_to_rebuild_directory = now.tv_sec + 10; /* try in 10 seconds */
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
      } else {
        directory_rebuild();
        time_to_rebuild_directory = now.tv_sec + options.DirRebuildPeriod;
      }
    }
    *timeout = 1000*(time_to_rebuild_directory - now.tv_sec) + (1000 - (now.tv_usec / 1000));
//    log(LOG_DEBUG,"prepare_for_poll(): DirBuild timeout is %d",*timeout);
  }

  if(!(options.Role & ROLE_DIR_SERVER)) {
    if(time_to_fetch_directory < now.tv_sec) {
      /* it's time to fetch a new directory */
      /* NOTE directory servers do not currently fetch directories.
       * Hope this doesn't bite us later.
       */
      directory_initiate_fetch(router_pick_directory_server());
      time_to_fetch_directory = now.tv_sec + options.DirFetchPeriod;
    }
    *timeout = 1000*(time_to_fetch_directory - now.tv_sec) + (1000 - (now.tv_usec / 1000));
  }

342
343
344
345
346
347
  /* check connections to see whether we should send a keepalive, expire, or wait */
  for(i=0;i<nfds;i++) {
    tmpconn = connection_array[i];
    if(!connection_speaks_cells(tmpconn))
      continue; /* this conn type doesn't send cells */
    if(now.tv_sec >= tmpconn->timestamp_lastwritten + options.KeepalivePeriod) {
348
349
350
      if((!(options.Role & ROLE_OR_CONNECT_ALL) && !circuit_get_by_conn(tmpconn)) ||
         (!connection_state_is_open(tmpconn))) {
        /* we're an onion proxy, with no circuits; or our handshake has expired. kill it. */
351
352
353
354
355
356
357
358
359
        log(LOG_DEBUG,"prepare_for_poll(): Expiring connection to %d (%s:%d).",
            i,tmpconn->address, tmpconn->port);
        tmpconn->marked_for_close = 1;
      } else {
        /* either a full router, or we've got a circuit. send a padding cell. */
//        log(LOG_DEBUG,"prepare_for_poll(): Sending keepalive to (%s:%d)",
//            tmpconn->address, tmpconn->port);
        memset(&cell,0,sizeof(cell_t));
        cell.command = CELL_PADDING;
360
361
        if(connection_write_cell_to_buf(&cell, tmpconn) < 0)
          tmpconn->marked_for_close = 1;
362
363
364
365
366
367
368
369
370
      }
    }
    if(!tmpconn->marked_for_close &&
       *timeout > 1000*(tmpconn->timestamp_lastwritten + options.KeepalivePeriod - now.tv_sec)) {
      *timeout = 1000*(tmpconn->timestamp_lastwritten + options.KeepalivePeriod - now.tv_sec);
    }
  }
  assert(*timeout >= 0);
  /* blow away any connections that need to die. can't do this later
371
   * because we might open up a circuit and not realize it we're about to cull it.
372
373
374
375
   */
  for(i=0;i<nfds;i++)
    check_conn_marked(i); 

376
377
378
379
  if(now.tv_sec > current_second) { /* the second has already rolled over! */
    for(i=0;i<nfds;i++) {
      connection_increment_receiver_bucket(connection_array[i]);
      connection_array[i]->onions_handled_this_second = 0;
380
    }
381
    current_second = now.tv_sec; /* remember which second it is, for next time */
382
383
  }

384
385
  /* this timeout is definitely sooner than any of the above ones */
  *timeout = 1000 - (now.tv_usec / 1000); /* how many milliseconds til the next second? */
386
387
388
389
390

  if(options.LinkPadding) {
    /* now check which conn wants to speak soonest */
    for(i=0;i<nfds;i++) {
      tmpconn = connection_array[i];
391
      if(!connection_speaks_cells(tmpconn))
392
393
394
395
        continue; /* this conn type doesn't send cells */
      if(!connection_state_is_open(tmpconn))
        continue; /* only conns in state 'open' have a valid send_timeval */ 
      while(tv_cmp(&tmpconn->send_timeval,&now) <= 0) { /* send_timeval has already passed, let it send a cell */
Roger Dingledine's avatar
Roger Dingledine committed
396
397
398
399
//        log(LOG_DEBUG,"prepare_for_poll(): doing backlogged connection_send_cell on socket %d (%d ms old)",tmpconn->s,
//         (now.tv_sec - tmpconn->send_timeval.tv_sec)*1000 +
//         (now.tv_usec - tmpconn->send_timeval.tv_usec)/1000
//        );
400
401
402
403
404
405
406
407
408
409
410
411
412
413
        connection_send_cell(tmpconn);
      }
      if(!conn || tv_cmp(&tmpconn->send_timeval, &soonest) < 0) { /* this is the best choice so far */
//        log(LOG_DEBUG,"prepare_for_poll(): chose socket %d as best connection so far",tmpconn->s);
        conn = tmpconn;
        soonest.tv_sec = conn->send_timeval.tv_sec;
        soonest.tv_usec = conn->send_timeval.tv_usec;
      }
    }

    if(conn) { /* we might want to set *timeout sooner */
      ms_until_conn = (soonest.tv_sec - now.tv_sec)*1000 +
                    (soonest.tv_usec - now.tv_usec)/1000;
//      log(LOG_DEBUG,"prepare_for_poll(): conn %d times out in %d ms.",conn->s, ms_until_conn);
414
      if(ms_until_conn < *timeout) { /* use the new one */
415
416
417
418
419
420
//        log(LOG_DEBUG,"prepare_for_poll(): conn %d soonest, in %d ms.",conn->s,ms_until_conn);
        *timeout = ms_until_conn;
      }
    }
  }

421
422
423
424
425
  if(onion_pending_check()) {
    /* there's an onion pending. check for new things to do, but don't wait any time */
    *timeout = 0;
  }

426
427
428
  return 0;
}

Roger Dingledine's avatar
Roger Dingledine committed
429
430
int do_main_loop(void) {
  int i;
431
432
  int timeout;
  int poll_result;
Roger Dingledine's avatar
cleanup    
Roger Dingledine committed
433
  crypto_pk_env_t *prkey;
Roger Dingledine's avatar
Roger Dingledine committed
434
435

  /* load the routers file */
436
  if(router_get_list_from_file(options.RouterFile) < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
437
    log(LOG_ERR,"Error loading router list.");
438
    return -1;
Roger Dingledine's avatar
Roger Dingledine committed
439
440
  }

441
442
443
444
445
446
447
  /* load the private key, if we're supposed to have one */
  if(ROLE_IS_OR(global_role)) {
    prkey = crypto_new_pk_env(CRYPTO_PK_RSA);
    if (!prkey) {
      log(LOG_ERR,"Error creating a crypto environment.");
      return -1;
    }
448
    if (crypto_pk_read_private_key_from_filename(prkey, options.PrivateKeyFile))
449
450
451
452
    {
      log(LOG_ERR,"Error loading private key.");
      return -1;
    }
Roger Dingledine's avatar
cleanup    
Roger Dingledine committed
453
    setprivatekey(prkey);
Roger Dingledine's avatar
Roger Dingledine committed
454
455
  }

456
457
  /* start-up the necessary connections based on global_role. This is where we
   * try to connect to all the other ORs, and start the listeners */
Roger Dingledine's avatar
cleanup    
Roger Dingledine committed
458
  retry_all_connections(options.Role, options.ORPort,
459
                        options.OPPort, options.APPort, options.DirPort);
Roger Dingledine's avatar
Roger Dingledine committed
460
461

  for(;;) {
462
463
    if(please_dumpstats) {
      dumpstats();
464
465
466
467
      please_dumpstats = 0;
    }
    if(please_fetch_directory) {
      if(options.Role & ROLE_DIR_SERVER) {
468
        if(router_get_list_from_file(options.RouterFile) < 0) {
469
470
471
472
473
474
          log(LOG_ERR,"Error reloading router list. Continuing with old list.");
        }
      } else {
        directory_initiate_fetch(router_pick_directory_server());
      }
      please_fetch_directory = 0;
475
    }
476
477
478
479
480
481
482
483
484
485
486
    if(prepare_for_poll(&timeout) < 0) {
      log(LOG_DEBUG,"do_main_loop(): prepare_for_poll failed, exiting.");
      return -1;
    }
    /* now timeout is the value we'll hand to poll. It's either -1, meaning
     * don't timeout, else it indicates the soonest event (either the
     * one-second rollover for refilling receiver buckets, or the soonest
     * conn that needs to send a cell)
     */

    /* if the timeout is less than 10, set it to 10 */
487
    if(timeout > 0 && timeout < 10)
488
489
490
491
492
      timeout = 10;

    /* poll until we have an event, or it's time to do something */
    poll_result = poll(poll_array, nfds, timeout);

Roger Dingledine's avatar
Roger Dingledine committed
493
#if 0 /* let catch() handle things like ^c, and otherwise don't worry about it */
494
495
496
497
498
    if(poll_result < 0) {
      log(LOG_ERR,"do_main_loop(): poll failed.");
      if(errno != EINTR) /* let the program survive things like ^z */
        return -1;
    }
Roger Dingledine's avatar
Roger Dingledine committed
499
#endif
Roger Dingledine's avatar
Roger Dingledine committed
500

501
502
503
504
505
    if(poll_result == 0) { 
      /* poll timed out without anything to do. process a pending onion, if any. */
      onion_pending_process_one();
    }

506
507
508
509
    if(poll_result > 0) { /* we have at least one connection to deal with */
      /* do all the reads first, so we can detect closed sockets */
      for(i=0;i<nfds;i++)
        check_conn_read(i); /* this also blows away broken connections */
Roger Dingledine's avatar
Roger Dingledine committed
510

511
512
513
      /* then do the writes */
      for(i=0;i<nfds;i++)
        check_conn_write(i);
Roger Dingledine's avatar
Roger Dingledine committed
514

515
516
517
518
519
520
521
      /* any of the conns need to be closed now? */
      for(i=0;i<nfds;i++)
        check_conn_marked(i); 
    }
    /* refilling buckets and sending cells happens at the beginning of the
     * next iteration of the loop, inside prepare_for_poll()
     */
Roger Dingledine's avatar
Roger Dingledine committed
522
523
524
  }
}

525
static void catch(int the_signal) {
Roger Dingledine's avatar
Roger Dingledine committed
526

527
  switch(the_signal) {
528
//    case SIGABRT:
529
530
531
532
533
534
535
536
537
538
539
540
541
    case SIGTERM:
    case SIGINT:
      log(LOG_NOTICE,"Catching signal %d, exiting cleanly.", the_signal);
      exit(0);
    case SIGHUP:
      please_fetch_directory = 1;
      break;
    case SIGUSR1:
      please_dumpstats = 1;
      break;
    default:
      log(LOG_ERR,"Caught signal that we can't handle??");
  }
542
543
544
545
546
}

void dumpstats (void) { /* dump stats to stdout */
  int i;
  connection_t *conn;
547
  struct timeval now;
548
  extern char *conn_type_to_string[];
549
  extern char *conn_state_to_string[][15];
550
551

  printf("Dumping stats:\n");
552
553
  if(gettimeofday(&now,NULL) < 0)
    return ;
554
555
556

  for(i=0;i<nfds;i++) {
    conn = connection_array[i];
557
    printf("Conn %d (socket %d) type %d (%s), state %d (%s), created %ld secs ago\n",
558
      i, conn->s, conn->type, conn_type_to_string[conn->type],
559
      conn->state, conn_state_to_string[conn->type][conn->state], now.tv_sec - conn->timestamp_created);
560
561
    if(!connection_is_listener(conn)) {
      printf("Conn %d is to '%s:%d'.\n",i,conn->address, conn->port);
562
563
564
565
      printf("Conn %d: %d bytes waiting on inbuf (last read %ld secs ago)\n",i,conn->inbuf_datalen,
        now.tv_sec - conn->timestamp_lastread);
      printf("Conn %d: %d bytes waiting on outbuf (last written %ld secs ago)\n",i,conn->outbuf_datalen, 
        now.tv_sec - conn->timestamp_lastwritten);
566
567
568
569
570
571
572
    }
    circuit_dump_by_conn(conn); /* dump info about all the circuits using this conn */
    printf("\n");
  }

}

573
int dump_router_to_string(char *s, int maxlen, routerinfo_t *router) {
574
575
576
  char *pkey;
  int pkeylen;
  int written;
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598

  if(crypto_pk_write_public_key_to_string(router->pkey,&pkey,&pkeylen)<0) {
    log(LOG_ERR,"dump_directory_to_string(): write pkey to string failed!");
    return 0;
  }
  written = snprintf(s, maxlen, "%s %d %d %d %d %d\n%s\n",
    router->address,
    router->or_port,
    router->op_port,
    router->ap_port,
    router->dir_port,
    router->bandwidth,
    pkey);

  free(pkey);

  return written;
}

void dump_directory_to_string(char *s, int maxlen) {
  int i;
  connection_t *conn;
599
  routerinfo_t *router;
600
  int written;
601

602
  /* first write my own info */
603
604
605
606
607
  if(my_routerinfo) {
    written = dump_router_to_string(s, maxlen, my_routerinfo);
    maxlen -= written;
    s += written;
  }
608
609

  /* now write info for other routers */
610
611
612
613
614
  for(i=0;i<nfds;i++) {
    conn = connection_array[i];

    if(conn->type != CONN_TYPE_OR)
      continue; /* we only want to list ORs */
615
616
    if(conn->state != OR_CONN_STATE_OPEN)
      continue; /* we only want to list ones that successfully handshaked */
617
618
619
    router = router_get_by_addr_port(conn->addr,conn->port);
    if(!router) {
      log(LOG_ERR,"dump_directory_to_string(): couldn't find router %d:%d!",conn->addr,conn->port);
620
      continue;
621
622
    }

623
    written = dump_router_to_string(s, maxlen, router);
624
625
626
627
628
629
630
631
632
633
634
635
636
637

    if(written < 0 || written > maxlen) { 
      /* apparently different glibcs do different things on error.. so check both */
      log(LOG_ERR,"dump_directory_to_string(): tried to exceed string length.");
      s[maxlen-1] = 0; /* make sure it's null terminated */
      return;
    }
  
    maxlen -= written;
    s += written;
  }

}

638
int main(int argc, char *argv[]) {
Roger Dingledine's avatar
Roger Dingledine committed
639
640
  int retval = 0;

641
642
  if(getconfig(argc,argv,&options))
    exit(1);
643
  log(options.loglevel,NULL);         /* assign logging severity level from options */
644
  global_role = options.Role;   /* assign global_role from options. FIXME: remove from global namespace later. */
Roger Dingledine's avatar
Roger Dingledine committed
645

646
647
648
649
650
651
  if(options.Role & ROLE_OR_LISTEN) { /* only spawn dns handlers if we're a router */
    if(dns_master_start() < 0) {
      log(LOG_ERR,"main(): We're running without a dns handler. Bad news.");
    }
  }

652
  init_tracked_tree(); /* initialize the replay detection tree */
653
654
655
656
657
658
659

  signal (SIGINT,  catch); /* catch kills so we can exit cleanly */
  signal (SIGTERM, catch);
  signal (SIGUSR1, catch); /* to dump stats to stdout */
  signal (SIGHUP,  catch); /* to reload directory */

  crypto_global_init();
Roger Dingledine's avatar
Roger Dingledine committed
660
  retval = do_main_loop();
661
  crypto_global_cleanup();
Roger Dingledine's avatar
Roger Dingledine committed
662
663
664
665

  return retval;
}