main.c 24.4 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 */
Roger Dingledine's avatar
Roger Dingledine committed
10

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

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

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

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

Roger Dingledine's avatar
Roger Dingledine committed
21
/* private key */
22
23
static crypto_pk_env_t *privatekey=NULL;
static crypto_pk_env_t *signing_privatekey=NULL;
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 ************/

29
void set_privatekey(crypto_pk_env_t *k) {
Roger Dingledine's avatar
cleanup    
Roger Dingledine committed
30
31
32
  privatekey = k;
}

33
crypto_pk_env_t *get_privatekey(void) {
Roger Dingledine's avatar
cleanup    
Roger Dingledine committed
34
35
36
37
  assert(privatekey);
  return privatekey;
}

38
39
40
41
42
43
44
45
46
void set_signing_privatekey(crypto_pk_env_t *k) {
  signing_privatekey = k;
}

crypto_pk_env_t *get_signing_privatekey(void) {
  assert(signing_privatekey);
  return signing_privatekey;
}

Roger Dingledine's avatar
Roger Dingledine committed
47
48
49
50
51
52
53
54
/****************************************************************************
*
* 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
55
56
int connection_add(connection_t *conn) {

57
  if(nfds >= options.MaxConn-1) {
58
    log(LOG_INFO,"connection_add(): failing because nfds is too high.");
Roger Dingledine's avatar
Roger Dingledine committed
59
60
61
62
63
64
65
66
67
68
69
70
71
    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++;

72
  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
73
74
75
76
77
78
79
80
81
82
83
84
85
86

  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);

87
  log(LOG_INFO,"connection_remove(): removing socket %d, nfds now %d",conn->s, nfds-1);
88
89
90
91
  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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

  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;  
}

110
connection_t *connection_twin_get_by_addr_port(uint32_t addr, uint16_t port) {
111
112
113
114
115
  /* 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.
   */
116
117
  int i;
  connection_t *conn;
Roger Dingledine's avatar
Roger Dingledine committed
118
  routerinfo_t *router;
119
120
121

  /* first check if it's there exactly */
  conn = connection_exact_get_by_addr_port(addr,port);
122
  if(conn && connection_state_is_open(conn)) {
Roger Dingledine's avatar
Roger Dingledine committed
123
    log(LOG_INFO,"connection_twin_get_by_addr_port(): Found exact match.");
124
    return conn;
125
  }
126
127
128

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

Roger Dingledine's avatar
Roger Dingledine committed
129
130
131
132
133
134
135
  router = router_get_by_addr_port(addr,port);
  if(!router)
    return NULL;

  for(i=0;i<nfds;i++) {
    conn = connection_array[i];
    assert(conn);
136
    if(connection_state_is_open(conn) && !crypto_pk_cmp_keys(conn->pkey, router->pkey)) {
Roger Dingledine's avatar
Roger Dingledine committed
137
      log(LOG_INFO,"connection_twin_get_by_addr_port(): Found twin (%s).",conn->address);
Roger Dingledine's avatar
Roger Dingledine committed
138
139
140
      return conn;
    }
  }
141
142
143
144
145
146
  /* 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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  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;
  }
168
169
170
  return NULL;
}

171
172
173
174
175
176
177
178
179
180
181
182
connection_t *connection_get_by_type_state(int type, int state) {
  int i;
  connection_t *conn;

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

Roger Dingledine's avatar
Roger Dingledine committed
183
184
185
186
187
188
189
void connection_watch_events(connection_t *conn, short events) {

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

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

190
191
192
193
void connection_stop_reading(connection_t *conn) {

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

194
  log(LOG_DEBUG,"connection_stop_reading() called.");
195
196
197
198
199
200
201
202
203
204
205
  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;
}

206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
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
222
223
224
225
226
227
228
229
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);
230
//    log(LOG_DEBUG,"check_conn_read(): socket %d has something to read.",conn->s);
Roger Dingledine's avatar
Roger Dingledine committed
231

232
    if (conn->type == CONN_TYPE_OR_LISTENER) {
Roger Dingledine's avatar
Roger Dingledine committed
233
      retval = connection_or_handle_listener_read(conn);
234
235
    } else if (conn->type == CONN_TYPE_AP_LISTENER) {
      retval = connection_ap_handle_listener_read(conn);
236
237
    } else if (conn->type == CONN_TYPE_DIR_LISTENER) {
      retval = connection_dir_handle_listener_read(conn);
Roger Dingledine's avatar
Roger Dingledine committed
238
239
    } else {
      retval = connection_read_to_buf(conn);
Roger Dingledine's avatar
Roger Dingledine committed
240
241
      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 */
242
         router_forget_router(conn->addr,conn->port); /* FIXME i don't think this function works. */
243
      }
Roger Dingledine's avatar
Roger Dingledine committed
244
245
      if (retval >= 0) { /* all still well */
        retval = connection_process_inbuf(conn);
246
//      log(LOG_DEBUG,"check_conn_read(): connection_process_inbuf returned %d.",retval);
247
248
249
250
        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
251
252
      }
    }
253

Roger Dingledine's avatar
Roger Dingledine committed
254
    if(retval < 0) { /* this connection is broken. remove it */
255
      log(LOG_INFO,"check_conn_read(): Connection broken, removing."); 
Roger Dingledine's avatar
Roger Dingledine committed
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
      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];
273
//    log(LOG_DEBUG,"check_conn_write(): socket %d wants to write.",conn->s);
Roger Dingledine's avatar
Roger Dingledine committed
274

275
    if(connection_is_listener(conn)) {
Roger Dingledine's avatar
Roger Dingledine committed
276
277
278
      log(LOG_DEBUG,"check_conn_write(): Got a listener socket. Can't happen!");
      retval = -1;
    } else {
279
      /* else it's an OP, OR, or exit */
Roger Dingledine's avatar
Roger Dingledine committed
280
281
282
283
284
285
286
287
288
289
290
291
      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. */
292
        check_conn_write(i);
Roger Dingledine's avatar
Roger Dingledine committed
293
294
295
296
297
298
299
300
301
302
303
304
      }
    }
  }
}

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.");
305
306
307
308
    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
309
310
311
312
    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
313
      check_conn_marked(i);
Roger Dingledine's avatar
Roger Dingledine committed
314
315
316
317
    }
  }
}

318
319
int prepare_for_poll(int *timeout) {
  int i;
320
//  connection_t *conn = NULL;
321
  connection_t *tmpconn;
322
  struct timeval now; //soonest;
323
324
  static long current_second = 0; /* from previous calls to gettimeofday */
  static long time_to_fetch_directory = 0;
325
  static long time_to_new_circuit = 0;
326
//  int ms_until_conn;
327
  cell_t cell;
328
  circuit_t *circ;
329

330
  my_gettimeofday(&now);
331

332
333
  if(now.tv_sec > current_second) { /* the second has rolled over. check more stuff. */

334
    if(!options.DirPort) {
335
336
337
338
339
340
341
      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;
342
343
344
      }
    }

345
346
347
    if(options.APPort && time_to_new_circuit < now.tv_sec) {
      circuit_expire_unused_circuits();
      circuit_launch_new(-1); /* tell it to forget about previous failures */
348
      circ = circuit_get_newest_ap();
349
      if(!circ || circ->dirty) {
350
        log(LOG_INFO,"prepare_for_poll(): Youngest circuit %s; launching replacement.", circ ? "dirty" : "missing");
351
352
353
354
355
        circuit_launch_new(0); /* make an onion and lay the circuit */
      }
      time_to_new_circuit = now.tv_sec + options.NewCircuitPeriod;
    }

356
357
358
359
    /* do housekeeping for each connection */
    for(i=0;i<nfds;i++) {
      tmpconn = connection_array[i];
      connection_increment_receiver_bucket(tmpconn);
360

361
362
363
364
      /* check connections to see whether we should send a keepalive, expire, or wait */
      if(!connection_speaks_cells(tmpconn))
        continue; /* this conn type doesn't send cells */
      if(now.tv_sec >= tmpconn->timestamp_lastwritten + options.KeepalivePeriod) {
365
        if((!options.OnionRouter && !circuit_get_by_conn(tmpconn)) ||
366
367
368
369
           (!connection_state_is_open(tmpconn))) {
          /* we're an onion proxy, with no circuits; or our handshake has expired. kill it. */
          log(LOG_DEBUG,"prepare_for_poll(): Expiring connection to %d (%s:%d).",
              i,tmpconn->address, tmpconn->port);
370
          tmpconn->marked_for_close = 1;
371
372
373
374
        } 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);
375
          memset(&cell,0,sizeof(cell_t));
376
377
378
379
          cell.command = CELL_PADDING;
          if(connection_write_cell_to_buf(&cell, tmpconn) < 0)
            tmpconn->marked_for_close = 1;
        }
380
381
      }
    }
382
383
384
385
386
    /* blow away any connections that need to die. can't do this later
     * because we might open up a circuit and not realize it we're about to cull it.
     */
    for(i=0;i<nfds;i++)
      check_conn_marked(i); 
387

388
    current_second = now.tv_sec; /* remember which second it is, for next time */
389
390
  }

391
392
393
394
395
396
397
398
399
400
  if(onion_pending_check()) {
    /* there's an onion pending. check for new things to do, but don't wait any time */
    *timeout = 0;
  } else {
    *timeout = 1000 - (now.tv_usec / 1000); /* how many milliseconds til the next second? */
  }

  return 0;
}

401

402
403
404

/* Link padding stuff left here for fun. Not used now. */
#if 0
405
406
407
408
  if(options.LinkPadding) {
    /* now check which conn wants to speak soonest */
    for(i=0;i<nfds;i++) {
      tmpconn = connection_array[i];
409
      if(!connection_speaks_cells(tmpconn))
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
        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 */
        connection_send_cell(tmpconn);
      }
      if(!conn || tv_cmp(&tmpconn->send_timeval, &soonest) < 0) { /* this is the best choice so far */
        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);
427
      if(ms_until_conn < *timeout) { /* use the new one */
428
429
430
431
432
//        log(LOG_DEBUG,"prepare_for_poll(): conn %d soonest, in %d ms.",conn->s,ms_until_conn);
        *timeout = ms_until_conn;
      }
    }
  }
433
#endif
434

Roger Dingledine's avatar
Roger Dingledine committed
435
436
int do_main_loop(void) {
  int i;
437
438
  int timeout;
  int poll_result;
Roger Dingledine's avatar
cleanup    
Roger Dingledine committed
439
  crypto_pk_env_t *prkey;
Roger Dingledine's avatar
Roger Dingledine committed
440
441

  /* load the routers file */
442
  if(router_get_list_from_file(options.RouterFile) < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
443
    log(LOG_ERR,"Error loading router list.");
444
    return -1;
Roger Dingledine's avatar
Roger Dingledine committed
445
446
  }

447
  /* load the private key, if we're supposed to have one */
448
  if(options.OnionRouter) {
449
450
451
452
453
    prkey = crypto_new_pk_env(CRYPTO_PK_RSA);
    if (!prkey) {
      log(LOG_ERR,"Error creating a crypto environment.");
      return -1;
    }
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
    if (crypto_pk_read_private_key_from_filename(prkey, options.PrivateKeyFile)) {
      log(LOG_ERR,"Error loading private key.");
      return -1;
    }
    set_privatekey(prkey);
  }

  /* load the private key, if we're supposed to have one */
  if(options.DirPort) {
    prkey = crypto_new_pk_env(CRYPTO_PK_RSA);
    if (!prkey) {
      log(LOG_ERR,"Error creating a crypto environment.");
      return -1;
    }
    if (crypto_pk_read_private_key_from_filename(prkey, options.SigningPrivateKeyFile)) {
469
470
471
      log(LOG_ERR,"Error loading private key.");
      return -1;
    }
472
    set_signing_privatekey(prkey);
Roger Dingledine's avatar
Roger Dingledine committed
473
474
  }

475
476
477
478
  /* start up the necessary connections based on which ports are
   * non-zero. This is where we try to connect to all the other ORs,
   * and start the listeners
   */
479
  retry_all_connections(options.ORPort, options.APPort, options.DirPort);
Roger Dingledine's avatar
Roger Dingledine committed
480
481

  for(;;) {
482
483
    if(please_dumpstats) {
      dumpstats();
484
485
486
      please_dumpstats = 0;
    }
    if(please_fetch_directory) {
487
      if(options.DirPort) {
488
        if(router_get_list_from_file(options.RouterFile) < 0) {
489
490
491
492
493
494
          log(LOG_ERR,"Error reloading router list. Continuing with old list.");
        }
      } else {
        directory_initiate_fetch(router_pick_directory_server());
      }
      please_fetch_directory = 0;
495
    }
496
497
498
499
500
501
502
503
504
505
506
507
508
    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)
     */

    /* 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
509
#if 0 /* let catch() handle things like ^c, and otherwise don't worry about it */
510
511
512
513
514
    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
515
#endif
Roger Dingledine's avatar
Roger Dingledine committed
516

517
518
519
520
521
    if(poll_result == 0) { 
      /* poll timed out without anything to do. process a pending onion, if any. */
      onion_pending_process_one();
    }

522
523
524
525
    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
526

527
528
529
      /* then do the writes */
      for(i=0;i<nfds;i++)
        check_conn_write(i);
Roger Dingledine's avatar
Roger Dingledine committed
530

531
532
533
534
535
536
537
      /* 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
538
539
540
  }
}

541
static void catch(int the_signal) {
Roger Dingledine's avatar
Roger Dingledine committed
542

543
  switch(the_signal) {
544
//    case SIGABRT:
545
546
547
548
549
550
551
552
553
554
555
556
557
    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??");
  }
558
559
}

560
void dumpstats(void) { /* dump stats to stdout */
561
562
  int i;
  connection_t *conn;
563
  struct timeval now;
564
  extern char *conn_type_to_string[];
565
  extern char *conn_state_to_string[][15];
566
567

  printf("Dumping stats:\n");
568
  my_gettimeofday(&now);
569
570
571

  for(i=0;i<nfds;i++) {
    conn = connection_array[i];
572
    printf("Conn %d (socket %d) type %d (%s), state %d (%s), created %ld secs ago\n",
573
      i, conn->s, conn->type, conn_type_to_string[conn->type],
574
      conn->state, conn_state_to_string[conn->type][conn->state], now.tv_sec - conn->timestamp_created);
575
576
    if(!connection_is_listener(conn)) {
      printf("Conn %d is to '%s:%d'.\n",i,conn->address, conn->port);
577
578
579
580
      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);
581
582
583
584
585
586
587
    }
    circuit_dump_by_conn(conn); /* dump info about all the circuits using this conn */
    printf("\n");
  }

}

588
int dump_router_to_string(char *s, int maxlen, routerinfo_t *router) {
589
  char *pkey;
590
591
  char *signing_pkey, *signing_pkey_tag;
  int pkeylen, signing_pkeylen;
592
  int written;
593
594
  int result=0;
  struct exit_policy_t *tmpe;
595
596

  if(crypto_pk_write_public_key_to_string(router->pkey,&pkey,&pkeylen)<0) {
597
    log(LOG_ERR,"dump_router_to_string(): write pkey to string failed!");
598
599
    return 0;
  }
600

601
602
603
604
605
606
607
608
609
610
611
612
  signing_pkey = "";
  signing_pkey_tag = "";
  if (router->signing_pkey) {
    if(crypto_pk_write_public_key_to_string(router->signing_pkey,
                                         &signing_pkey,&signing_pkeylen)<0) {
      log(LOG_ERR,"dump_router_to_string(): write signing_pkey to string failed!");
      return 0;
    }
    signing_pkey_tag = "signing-key\n";
  }
  
  result = snprintf(s, maxlen, "router %s %d %d %d %d %d\n%s%s%s",
613
614
615
616
617
618
    router->address,
    router->or_port,
    router->op_port,
    router->ap_port,
    router->dir_port,
    router->bandwidth,
619
620
    pkey,
    signing_pkey_tag, signing_pkey);
621
622

  free(pkey);
623
624
  if (*signing_pkey)
    free(signing_pkey);
625

626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
  if(result < 0 || result > maxlen) {
    /* apparently different glibcs do different things on snprintf error.. so check both */
    return -1;
  }
  written = result;

  for(tmpe=router->exit_policy; tmpe; tmpe=tmpe->next) {
    result = snprintf(s+written, maxlen-written, "%s %s:%s\n", 
      tmpe->policy_type == EXIT_POLICY_ACCEPT ? "accept" : "reject",
      tmpe->address, tmpe->port);
    if(result < 0 || result+written > maxlen) {
      /* apparently different glibcs do different things on snprintf error.. so check both */
      return -1;
    }
    written += result;
  }

  if(written > maxlen-2) {
    return -1; /* not enough space for \n\0 */
  }
  /* XXX count fenceposts here. They're probably wrong. In general,
   * we need a better way to handle overruns in building the directory
   * string, and a better way to handle directory string size in general. */

  /* include a last '\n' */
  s[written] = '\n';
  s[written+1] = 0;
  return written+1;

655
656
}

657
658
static int 
build_directory(directory_t *dir) {
659
  routerinfo_t **routers = NULL;
660
  connection_t *conn;
661
  routerinfo_t *router;
662
  int i, n = 0;
663

664
  routers = (routerinfo_t **)tor_malloc(sizeof(routerinfo_t*) * (nfds+1));
665
  if (my_routerinfo) {
666
    log(LOG_INFO, "build_directory(): adding self (%s:%d)", 
667
        my_routerinfo->address, my_routerinfo->or_port);
668
669
670
    routers[n++] = my_routerinfo;
  }
  for(i = 0; i<nfds; ++i) {
671
672
673
674
    conn = connection_array[i];

    if(conn->type != CONN_TYPE_OR)
      continue; /* we only want to list ORs */
675
676
    if(conn->state != OR_CONN_STATE_OPEN)
      continue; /* we only want to list ones that successfully handshaked */
677
678
    router = router_get_by_addr_port(conn->addr,conn->port);
    if(!router) {
679
      log(LOG_ERR,"build_directory(): couldn't find router %d:%d!",
Nick Mathewson's avatar
Nick Mathewson committed
680
          conn->addr,conn->port);
681
      continue;
682
    }
683
    log(LOG_INFO, "build_directory(): adding router (%s:%d)",
684
        router->address, router->or_port);
685
686
    routers[n++] = router;
  }
687
688
689
690
  dir->routers = routers;
  dir->n_routers = n;
  return 0;
}
691

692
693
694
695
696
int
dump_signed_directory_to_string(char *s, int maxlen,
                                crypto_pk_env_t *private_key)
{
  directory_t dir;
Nick Mathewson's avatar
Nick Mathewson committed
697
698
  if (build_directory(&dir)) {
    log(LOG_ERR,"dump_signed_directory_to_string(): build_directory failed.");
699
    return -1;
Nick Mathewson's avatar
Nick Mathewson committed
700
701
  }
  return dump_signed_directory_to_string_impl(s, maxlen, &dir, private_key);
702
703
704
705
706
707
}

int
dump_signed_directory_to_string_impl(char *s, int maxlen, directory_t *dir,
                                     crypto_pk_env_t *private_key)
{
708
  char *cp, *eos;
709
710
  char digest[20];
  char signature[128];
711
712
  int i, written;
  routerinfo_t *router;
713
  eos = s+maxlen;
714
715
  strncpy(s, 
          "signed-directory\n"
716
          "recommended-software 0.0.2pre4,0.0.2pre5,0.0.2pre6\n" /* XXX make this real */
717
          , maxlen);
Nick Mathewson's avatar
Nick Mathewson committed
718
719
720
  
  i = strlen(s);
  cp = s+i;
721
722
  for (i = 0; i < dir->n_routers; ++i) {
    router = dir->routers[i];
723
    written = dump_router_to_string(cp, eos-cp, router);
724
725

    if(written < 0) { 
Nick Mathewson's avatar
Nick Mathewson committed
726
727
      log(LOG_ERR,"dump_signed_directory_to_string(): tried to exceed string length.");
      cp[maxlen-1] = 0; /* make sure it's null terminated */
728
729
      return -1;
    }
Nick Mathewson's avatar
Nick Mathewson committed
730
    cp += written;
731
732
  }

733
734
735
736
  /* These multiple strlen calls are inefficient, but dwarfed by the RSA
     signature.
  */
  i = strlen(s);
737
738
  strncat(s, "directory-signature\n", maxlen-i);
  i = strlen(s);
739
740
  cp = s + i;
  
Nick Mathewson's avatar
Nick Mathewson committed
741
  if (crypto_SHA_digest(s, i, digest)) {
742
    log(LOG_ERR,"dump_signed_directory_to_string(): couldn't compute digest");
743
    return -1;
Nick Mathewson's avatar
Nick Mathewson committed
744
745
  }
  if (crypto_pk_private_sign(private_key, digest, 20, signature) < 0) {
746
    log(LOG_ERR,"dump_signed_directory_to_string(): couldn't sign digest");
747
    return -1;
Nick Mathewson's avatar
Nick Mathewson committed
748
  }
749
750
  
  strncpy(cp, 
751
          "-----BEGIN SIGNATURE-----\n", maxlen-i);
752
753
754
          
  i = strlen(s);
  cp = s+i;
Nick Mathewson's avatar
Nick Mathewson committed
755
  if (base64_encode(cp, maxlen-i, signature, 128) < 0) {
756
    log(LOG_ERR,"dump_signed_directory_to_string(): couldn't base64-encode signature %d/%d");
757
    return -1;
Nick Mathewson's avatar
Nick Mathewson committed
758
  }
759

760
761
  i = strlen(s);
  cp = s+i;
Nick Mathewson's avatar
Nick Mathewson committed
762
763
764
765
766
767
  strncat(cp, "-----END SIGNATURE-----\n", maxlen-i);
  i = strlen(s);
  if (i == maxlen) {
    log(LOG_ERR,"dump_signed_directory_to_string(): tried to exceed string length.");
    return -1;
  }
768
769
770
771

  return 0;
}

772
void daemonize(void) {
Nick Mathewson's avatar
Nick Mathewson committed
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
  /* Fork; parent exits. */
  if (fork())
    exit(0);

  /* Create new session; make sure we never get a terminal */
  setsid();
  if (fork())
    exit(0);

  chdir("/");
  umask(000);

  fclose(stdin);
  fclose(stdout);
  fclose(stderr);
}

790
int tor_main(int argc, char *argv[]) {
Roger Dingledine's avatar
Roger Dingledine committed
791
792
  int retval = 0;

793
794
  if(getconfig(argc,argv,&options))
    exit(1);
795
  log(options.loglevel,NULL);         /* assign logging severity level from options */
Roger Dingledine's avatar
Roger Dingledine committed
796

797
  if(options.Daemon)
Nick Mathewson's avatar
Nick Mathewson committed
798
799
    daemonize();

800
  if(options.OnionRouter) { /* only spawn dns handlers if we're a router */
801
    dns_init(); /* initialize the dns resolve tree, and spawn workers */
802
803
804
805
806
807
808
809
  }

  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();
Nick Mathewson's avatar
Nick Mathewson committed
810
  crypto_seed_rng();
Roger Dingledine's avatar
Roger Dingledine committed
811
  retval = do_main_loop();
812
  crypto_global_cleanup();
Roger Dingledine's avatar
Roger Dingledine committed
813
814
815
816

  return retval;
}

817
818
819
820
821
822
823
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/