main.c 14.9 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
16
17
static struct pollfd poll_array[MAXCONNECTIONS];
/*  =       { [0 ... MAXCONNECTIONS-1] = { -1, 0, 0 } };
 */
Roger Dingledine's avatar
Roger Dingledine committed
18

Roger Dingledine's avatar
Roger Dingledine committed
19
static int nfds=0; /* number of connections currently active */
Roger Dingledine's avatar
Roger Dingledine committed
20
21

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

/* router array */
Roger Dingledine's avatar
Roger Dingledine committed
25
26
static routerinfo_t **router_array = NULL;
static int rarray_len = 0;
Roger Dingledine's avatar
Roger Dingledine committed
27
28
29

/********* END VARIABLES ************/

Roger Dingledine's avatar
Roger Dingledine committed
30
31
32
33
34
35
36
37
/****************************************************************************
*
* 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
38
39
int connection_add(connection_t *conn) {

40
  if(nfds >= options.MaxConn-1) {
41
    log(LOG_INFO,"connection_add(): failing because nfds is too high.");
Roger Dingledine's avatar
Roger Dingledine committed
42
43
44
45
46
47
48
49
50
51
52
53
54
    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++;

55
  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
56
57
58
59
60
61
62
63
64
65
66
67
68
69

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

70
  log(LOG_INFO,"connection_remove(): removing socket %d, nfds now %d",conn->s, nfds-1);
Roger Dingledine's avatar
Roger Dingledine committed
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  circuit_about_to_close_connection(conn); /* flush and send destroys for all circuits on this conn */

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

90
int pkey_cmp(crypto_pk_env_t *a, crypto_pk_env_t *b) {
Roger Dingledine's avatar
Roger Dingledine committed
91
92
  /* return 0 if a and b are "the same key". Return non-0 otherwise. */

93
  return crypto_pk_cmp_keys(a, b);
Roger Dingledine's avatar
Roger Dingledine committed
94
95
}

96
connection_t *connection_twin_get_by_addr_port(uint32_t addr, uint16_t port) {
97
98
99
100
101
  /* 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.
   */
102
103
  int i;
  connection_t *conn;
Roger Dingledine's avatar
Roger Dingledine committed
104
  routerinfo_t *router;
105
106
107

  /* first check if it's there exactly */
  conn = connection_exact_get_by_addr_port(addr,port);
108
  if(conn && connection_state_is_open(conn)) {
Roger Dingledine's avatar
Roger Dingledine committed
109
    log(LOG_INFO,"connection_twin_get_by_addr_port(): Found exact match.");
110
    return conn;
111
  }
112
113
114

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

Roger Dingledine's avatar
Roger Dingledine committed
115
116
117
118
119
120
121
122
  router = router_get_by_addr_port(addr,port);
  if(!router)
    return NULL;

  for(i=0;i<nfds;i++) {
    conn = connection_array[i];
    assert(conn);
    if(connection_state_is_open(conn) && !pkey_cmp(conn->pkey, router->pkey)) {
Roger Dingledine's avatar
Roger Dingledine committed
123
      log(LOG_INFO,"connection_twin_get_by_addr_port(): Found twin (%s).",conn->address);
Roger Dingledine's avatar
Roger Dingledine committed
124
125
126
      return conn;
    }
  }
127
128
129
130
131
132
133

  /* 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
  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;
  }

  return NULL;
}

Roger Dingledine's avatar
Roger Dingledine committed
160
161
162
163
164
165
166
167



/* the next 4 functions should move to routers.c once we get it
 * cleaned up more. The router_array and rarray_len variables should
 * move there too.
 */

Roger Dingledine's avatar
Roger Dingledine committed
168
169
170
171
routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) {
  int i;
  routerinfo_t *router;

Roger Dingledine's avatar
Roger Dingledine committed
172
  assert(router_array);
Roger Dingledine's avatar
Roger Dingledine committed
173

Roger Dingledine's avatar
Roger Dingledine committed
174
  for(i=0;i<rarray_len;i++) {
Roger Dingledine's avatar
Roger Dingledine committed
175
    router = router_array[i];
176
    if ((router->addr == addr) && (router->or_port == port))
Roger Dingledine's avatar
Roger Dingledine committed
177
178
179
180
181
182
      return router;
  }

  return NULL;
}

183
routerinfo_t *router_get_first_in_route(unsigned int *route, int routelen) {
184
185
186
187
  return router_array[route[routelen-1]];
}

/* a wrapper around new_route. put all these in routers.c perhaps? */
188
189
unsigned int *router_new_route(int *routelen) {
  return new_route(options.CoinWeight, router_array, rarray_len, routelen);
190
191
192
}

/* a wrapper around create_onion */
193
194
unsigned char *router_create_onion(unsigned int *route, int routelen, int *len, crypt_path_t **cpath) {
  return create_onion(router_array,rarray_len,route,routelen,len,cpath);
195
196
}

Roger Dingledine's avatar
Roger Dingledine committed
197
198
199
200




201
connection_t *connect_to_router_as_op(routerinfo_t *router) {
202
  return connection_connect_to_router_as_op(router, prkey, options.ORPort);
203
204
}

Roger Dingledine's avatar
Roger Dingledine committed
205
206
207
208
209
210
211
void connection_watch_events(connection_t *conn, short events) {

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

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

212
213
214
215
void connection_stop_reading(connection_t *conn) {

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

216
  log(LOG_DEBUG,"connection_stop_reading() called.");
217
218
219
220
221
222
223
224
225
226
227
  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;
}

228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
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
244
245
246
247
248
249
250
251
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);
252
//    log(LOG_DEBUG,"check_conn_read(): socket %d has something to read.",conn->s);
Roger Dingledine's avatar
Roger Dingledine committed
253
254
255
256
257

    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);
258
259
    } else if (conn->type == CONN_TYPE_AP_LISTENER) {
      retval = connection_ap_handle_listener_read(conn);
Roger Dingledine's avatar
Roger Dingledine committed
260
    } else {
261
      /* else it's an OP, OR, or exit */
Roger Dingledine's avatar
Roger Dingledine committed
262
263
264
      retval = connection_read_to_buf(conn);
      if (retval >= 0) { /* all still well */
        retval = connection_process_inbuf(conn);
265
266
267
268
269
//	log(LOG_DEBUG,"check_conn_read(): connection_process_inbuf returned %d.",retval);
        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
270
271
      }
    }
272

Roger Dingledine's avatar
Roger Dingledine committed
273
    if(retval < 0) { /* this connection is broken. remove it */
274
      log(LOG_INFO,"check_conn_read(): Connection broken, removing."); 
Roger Dingledine's avatar
Roger Dingledine committed
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
      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];
292
//    log(LOG_DEBUG,"check_conn_write(): socket %d wants to write.",conn->s);
Roger Dingledine's avatar
Roger Dingledine committed
293
294
295
296
297
298

    if(conn->type == CONN_TYPE_OP_LISTENER ||
       conn->type == CONN_TYPE_OR_LISTENER) {
      log(LOG_DEBUG,"check_conn_write(): Got a listener socket. Can't happen!");
      retval = -1;
    } else {
299
      /* else it's an OP, OR, or exit */
Roger Dingledine's avatar
Roger Dingledine committed
300
301
302
303
304
305
306
307
308
309
310
311
      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. */
312
        check_conn_write(i);
Roger Dingledine's avatar
Roger Dingledine committed
313
314
315
316
317
318
319
320
321
322
323
324
      }
    }
  }
}

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.");
325
326
327
328
    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
329
330
331
332
    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
333
      check_conn_marked(i);
Roger Dingledine's avatar
Roger Dingledine committed
334
335
336
337
    }
  }
}

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
int prepare_for_poll(int *timeout) {
  int i;
  int need_to_refill_buckets = 0;
  connection_t *conn = NULL;
  connection_t *tmpconn;
  struct timeval now, soonest;
  static int current_second = 0; /* from previous calls to gettimeofday */
  int ms_until_conn;

  *timeout = -1; /* set it to never timeout, possibly overridden below */
  
  /* first check if we need to refill buckets */
  for(i=0;i<nfds;i++) {
    if(connection_receiver_bucket_should_increase(connection_array[i])) {
      need_to_refill_buckets = 1;
      break;
    }
  }

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

  if(need_to_refill_buckets) {
    if(now.tv_sec > current_second) { /* the second has already rolled over! */
362
363
364
//      log(LOG_DEBUG,"prepare_for_poll(): The second has rolled over, immediately refilling.");
      for(i=0;i<nfds;i++)
        connection_increment_receiver_bucket(connection_array[i]);
365
366
367
368
369
370
371
372
373
374
      current_second = now.tv_sec; /* remember which second it is, for next time */
    }
    *timeout = 1000 - (now.tv_usec / 1000); /* how many milliseconds til the next second? */
//    log(LOG_DEBUG,"prepare_for_poll(): %d milliseconds til next second.",*timeout);
  }

  if(options.LinkPadding) {
    /* now check which conn wants to speak soonest */
    for(i=0;i<nfds;i++) {
      tmpconn = connection_array[i];
375
      if(!connection_speaks_cells(tmpconn))
376
377
378
379
        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
380
381
382
383
//        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
//        );
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
        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);
      if(*timeout == -1 || ms_until_conn < *timeout) { /* use the new one */
//        log(LOG_DEBUG,"prepare_for_poll(): conn %d soonest, in %d ms.",conn->s,ms_until_conn);
        *timeout = ms_until_conn;
      }
    }
  }

  return 0;
}

Roger Dingledine's avatar
Roger Dingledine committed
408
409
int do_main_loop(void) {
  int i;
410
411
  int timeout;
  int poll_result;
Roger Dingledine's avatar
Roger Dingledine committed
412
413

  /* load the routers file */
414
  router_array = getrouters(options.RouterFile,&rarray_len, options.ORPort);
Roger Dingledine's avatar
Roger Dingledine committed
415
416
417
  if (!router_array)
  {
    log(LOG_ERR,"Error loading router list.");
418
    return -1;
Roger Dingledine's avatar
Roger Dingledine committed
419
420
421
  }

  /* load the private key */
422
423
424
425
426
427
  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_filename(prkey, options.PrivateKeyFile))
Roger Dingledine's avatar
Roger Dingledine committed
428
429
  {
    log(LOG_ERR,"Error loading private key.");
430
    return -1;
Roger Dingledine's avatar
Roger Dingledine committed
431
432
  }

433
434
  /* 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 */
435
  retry_all_connections(options.Role, router_array, rarray_len, prkey, 
436
		        options.ORPort, options.OPPort, options.APPort);
Roger Dingledine's avatar
Roger Dingledine committed
437
438

  for(;;) {
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
    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 */
    if(timeout >= 0 && timeout < 10)
      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
456
#if 0 /* let catch() handle things like ^c, and otherwise don't worry about it */
457
458
459
460
461
    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
462
#endif
Roger Dingledine's avatar
Roger Dingledine committed
463

464
465
466
467
    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
468

469
470
471
      /* then do the writes */
      for(i=0;i<nfds;i++)
        check_conn_write(i);
Roger Dingledine's avatar
Roger Dingledine committed
472

473
474
475
476
477
478
479
      /* 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
480
481
482
  }
}

483
void catch () {
Roger Dingledine's avatar
Roger Dingledine committed
484
485
486
487
488
489
490
  errno = 0; /* netcat does this. it looks fun. */

  log(LOG_DEBUG,"Catching ^c, exiting cleanly.");
   
  exit(0);
}

491
int main(int argc, char *argv[]) {
Roger Dingledine's avatar
Roger Dingledine committed
492
493
494
495
  int retval = 0;

  signal (SIGINT, catch); /* to catch ^c so we can exit cleanly */

496
  if ( getoptions(argc,argv,&options) ) exit(1);
497
  log(options.loglevel,NULL);         /* assign logging severity level from options */
498
  global_role = options.Role;   /* assign global_role from options. FIX: remove from global namespace later. */
Roger Dingledine's avatar
Roger Dingledine committed
499

500
  crypto_global_init();
Roger Dingledine's avatar
Roger Dingledine committed
501
  retval = do_main_loop();
502
  crypto_global_cleanup();
Roger Dingledine's avatar
Roger Dingledine committed
503
504
505
506

  return retval;
}