connection.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
10
extern or_options_t options; /* command-line and config-file options */

11
12
extern int global_read_bucket;

Roger Dingledine's avatar
Roger Dingledine committed
13
char *conn_type_to_string[] = {
14
15
16
17
18
19
20
  "",            /* 0 */
  "OP listener", /* 1 */
  "OP",          /* 2 */
  "OR listener", /* 3 */
  "OR",          /* 4 */
  "Exit",        /* 5 */
  "App listener",/* 6 */
21
22
23
  "App",         /* 7 */
  "Dir listener",/* 8 */
  "Dir",         /* 9 */
Roger Dingledine's avatar
Roger Dingledine committed
24
25
  "DNS worker",  /* 10 */
  "CPU worker",  /* 11 */
Roger Dingledine's avatar
Roger Dingledine committed
26
27
};

28
char *conn_state_to_string[][15] = {
29
	{ NULL },         /* no type associated with 0 */
Roger Dingledine's avatar
Roger Dingledine committed
30
31
32
33
34
35
  { "ready" }, /* op listener, 0 */
  { "awaiting keys", /* op, 0 */
    "open",              /* 1 */
    "close",             /* 2 */
    "close_wait" },      /* 3 */
  { "ready" }, /* or listener, 0 */
36
37
38
  { "connect()ing",                 /* 0 */
    "handshaking",                  /* 1 */
    "open" },                       /* 2 */
39
40
41
42
  { "waiting for dest info",     /* exit, 0 */
    "connecting",                      /* 1 */
    "open" },                          /* 2 */
  { "ready" }, /* app listener, 0 */
43
44
45
46
47
48
  { "", /* 0 */
    "", /* 1 */
    "", /* 2 */
    "awaiting dest info",         /* app, 3 */
    "waiting for OR connection",       /* 4 */
    "open" },                          /* 5 */
49
  { "ready" }, /* dir listener, 0 */
50
51
52
53
54
55
56
57
  { "connecting (fetch)",              /* 0 */
    "connecting (upload)",             /* 1 */
    "client sending fetch",            /* 2 */
    "client sending upload",           /* 3 */
    "client reading fetch",            /* 4 */
    "client reading upload",           /* 5 */
    "awaiting command",                /* 6 */
    "writing" },                       /* 7 */
Roger Dingledine's avatar
Roger Dingledine committed
58
59
60
61
62
  { "idle",                /* dns worker, 0 */
    "busy" },                          /* 1 */
  { "idle",                /* cpu worker, 0 */
    "busy with onion",                 /* 1 */
    "busy with handshake" },           /* 2 */
Roger Dingledine's avatar
Roger Dingledine committed
63
64
65
66
};

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

67
static int connection_init_accepted_conn(connection_t *conn);
68
69
static int connection_tls_continue_handshake(connection_t *conn);
static int connection_tls_finish_handshake(connection_t *conn);
70
71
72

/**************************************************************/

Roger Dingledine's avatar
Roger Dingledine committed
73
74
connection_t *connection_new(int type) {
  connection_t *conn;
75
76
  struct timeval now;

77
  my_gettimeofday(&now);
Roger Dingledine's avatar
Roger Dingledine committed
78

79
  conn = (connection_t *)tor_malloc(sizeof(connection_t));
Roger Dingledine's avatar
Roger Dingledine committed
80
81
82
  memset(conn,0,sizeof(connection_t)); /* zero it out to start */

  conn->type = type;
83
84
85
  if(buf_new(&conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen) < 0 ||
     buf_new(&conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen) < 0)
    return NULL;
Roger Dingledine's avatar
Roger Dingledine committed
86

87
  conn->receiver_bucket = 50000; /* should be enough to do the handshake */
88
  conn->bandwidth = conn->receiver_bucket / 10; /* give it a default */
89

90
  conn->timestamp_created = now.tv_sec;
91
92
93
  conn->timestamp_lastread = now.tv_sec;
  conn->timestamp_lastwritten = now.tv_sec;

Roger Dingledine's avatar
Roger Dingledine committed
94
95
96
97
98
99
100
101
102
103
104
  return conn;
}

void connection_free(connection_t *conn) {
  assert(conn);

  buf_free(conn->inbuf);
  buf_free(conn->outbuf);
  if(conn->address)
    free(conn->address);

105
  if(connection_speaks_cells(conn)) {
106
    directory_set_dirty();
Roger Dingledine's avatar
Roger Dingledine committed
107
108
    if (conn->tls)
      tor_tls_free(conn->tls);
Roger Dingledine's avatar
Roger Dingledine committed
109
110
  }

111
112
113
  if (conn->pkey)
    crypto_free_pk_env(conn->pkey);

114
  if(conn->s > 0) {
115
    log_fn(LOG_INFO,"closing fd %d.",conn->s);
Roger Dingledine's avatar
Roger Dingledine committed
116
    close(conn->s);
117
  }
Roger Dingledine's avatar
Roger Dingledine committed
118
119
120
  free(conn);
}

121
int connection_create_listener(struct sockaddr_in *bindaddr, int type) {
Roger Dingledine's avatar
Roger Dingledine committed
122
123
124
125
126
  connection_t *conn;
  int s;
  int one=1;

  s = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
127
  if (s < 0) { 
128
    log_fn(LOG_ERR,"Socket creation failed.");
Roger Dingledine's avatar
Roger Dingledine committed
129
130
131
    return -1;
  }

132
  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one));
Roger Dingledine's avatar
Roger Dingledine committed
133

134
  if(bind(s,(struct sockaddr *)bindaddr,sizeof(*bindaddr)) < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
135
    perror("bind ");
136
    log(LOG_ERR,"Could not bind to port %u.",ntohs(bindaddr->sin_port));
Roger Dingledine's avatar
Roger Dingledine committed
137
138
139
140
    return -1;
  }

  if(listen(s,SOMAXCONN) < 0) {
141
    log(LOG_ERR,"Could not listen on port %u.",ntohs(bindaddr->sin_port));
Roger Dingledine's avatar
Roger Dingledine committed
142
143
144
    return -1;
  }

145
  set_socket_nonblocking(s);
Roger Dingledine's avatar
Roger Dingledine committed
146
147

  conn = connection_new(type);
148
  if(!conn) {
149
    log_fn(LOG_DEBUG,"connection_new failed. Giving up.");
150
    return -1;
151
  }
Roger Dingledine's avatar
Roger Dingledine committed
152
153
154
  conn->s = s;

  if(connection_add(conn) < 0) { /* no space, forget it */
155
    log_fn(LOG_DEBUG,"connection_add failed. Giving up.");
Roger Dingledine's avatar
Roger Dingledine committed
156
157
158
159
    connection_free(conn);
    return -1;
  }

160
  log_fn(LOG_DEBUG,"%s listening on port %u.",conn_type_to_string[type], ntohs(bindaddr->sin_port));
Roger Dingledine's avatar
Roger Dingledine committed
161
162

  conn->state = LISTENER_STATE_READY;
163
  connection_start_reading(conn);
Roger Dingledine's avatar
Roger Dingledine committed
164
165
166
167

  return 0;
}

168
int connection_handle_listener_read(connection_t *conn, int new_type) {
Roger Dingledine's avatar
Roger Dingledine committed
169
170
171
172
173

  int news; /* the new socket */
  connection_t *newconn;
  struct sockaddr_in remote; /* information about the remote peer when connecting to other routers */
  int remotelen = sizeof(struct sockaddr_in); /* length of the remote address */
174
175
176
#ifdef MS_WINDOWS
  int e;
#endif
Roger Dingledine's avatar
Roger Dingledine committed
177
178
179

  news = accept(conn->s,(struct sockaddr *)&remote,&remotelen);
  if (news == -1) { /* accept() error */
180
181
182
183
184
185
    if(ERRNO_EAGAIN(errno)) {
#ifdef MS_WINDOWS
      e = correct_socket_errno(conn->s);
      if (ERRNO_EAGAIN(e))
        return 0;
#else
Roger Dingledine's avatar
Roger Dingledine committed
186
      return 0; /* he hung up before we could accept(). that's fine. */
187
188
#endif
    }
Roger Dingledine's avatar
Roger Dingledine committed
189
    /* else there was a real error. */
190
    log_fn(LOG_ERR,"accept() failed. Closing.");
Roger Dingledine's avatar
Roger Dingledine committed
191
192
    return -1;
  }
193
  log(LOG_INFO,"Connection accepted on socket %d (child of fd %d).",news, conn->s);
Roger Dingledine's avatar
Roger Dingledine committed
194

195
  set_socket_nonblocking(news);
196

Roger Dingledine's avatar
Roger Dingledine committed
197
198
199
  newconn = connection_new(new_type);
  newconn->s = news;

200
201
202
203
204
  if(!connection_speaks_cells(newconn)) {
    newconn->receiver_bucket = -1;
    newconn->bandwidth = -1;
  }

205
  newconn->address = strdup(inet_ntoa(remote.sin_addr)); /* remember the remote address */
206
207
  newconn->addr = ntohl(remote.sin_addr.s_addr);
  newconn->port = ntohs(remote.sin_port);
Roger Dingledine's avatar
Roger Dingledine committed
208
209
210

  if(connection_add(newconn) < 0) { /* no space, forget it */
    connection_free(newconn);
211
    return 0; /* no need to tear down the parent */
Roger Dingledine's avatar
Roger Dingledine committed
212
213
  }

214
  if(connection_init_accepted_conn(newconn) < 0) {
215
    newconn->marked_for_close = 1;
216
217
218
219
220
221
222
223
224
225
226
    return 0;
  }
  return 0;
}

static int connection_init_accepted_conn(connection_t *conn) {

  connection_start_reading(conn);

  switch(conn->type) {
    case CONN_TYPE_OR:
227
      if(connection_tls_start_handshake(conn, 1) < 0)
228
229
230
231
232
233
        return -1;
      break;
    case CONN_TYPE_AP:
      conn->state = AP_CONN_STATE_SOCKS_WAIT;
      break;
    case CONN_TYPE_DIR:
234
      conn->state = DIR_CONN_STATE_SERVER_COMMAND_WAIT;
235
236
237
238
239
      break;
  }
  return 0;
}

240
int connection_tls_start_handshake(connection_t *conn, int receiving) {
241
  conn->state = OR_CONN_STATE_HANDSHAKING;
242
  conn->tls = tor_tls_new(conn->s, receiving);
243
244
245
246
247
  if(!conn->tls) {
    log_fn(LOG_ERR,"tor_tls_new failed. Closing.");
    return -1;
  }
  connection_start_reading(conn);
248
  log_fn(LOG_DEBUG,"starting the handshake");
249
250
251
252
  if(connection_tls_continue_handshake(conn) < 0)
    return -1;
  return 0;
}
Roger Dingledine's avatar
Roger Dingledine committed
253

254
static int connection_tls_continue_handshake(connection_t *conn) {
255
256
257
258
259
260
  switch(tor_tls_handshake(conn->tls)) {
    case TOR_TLS_ERROR:
    case TOR_TLS_CLOSE:
      log_fn(LOG_DEBUG,"tls error. breaking.");
      return -1;
    case TOR_TLS_DONE:
261
     return connection_tls_finish_handshake(conn);
262
263
    case TOR_TLS_WANTWRITE:
      connection_start_writing(conn);
264
      log_fn(LOG_DEBUG,"wanted write");
265
266
      return 0;
    case TOR_TLS_WANTREAD: /* handshaking conns are *always* reading */
267
      log_fn(LOG_DEBUG,"wanted read");
268
269
      return 0;
  }
Roger Dingledine's avatar
Roger Dingledine committed
270
271
  return 0;
}
272
273
274
275
276
277
278
279

static int connection_tls_finish_handshake(connection_t *conn) {
  crypto_pk_env_t *pk;
  routerinfo_t *router;

  conn->state = OR_CONN_STATE_OPEN;
  directory_set_dirty();
  connection_watch_events(conn, POLLIN);
280
  log_fn(LOG_DEBUG,"tls handshake done. verifying.");
281
282
283
284
285
286
287
  if(options.OnionRouter) { /* I'm an OR */
    if(tor_tls_peer_has_cert(conn->tls)) { /* it's another OR */
      pk = tor_tls_verify(conn->tls);
      if(!pk) {
        log_fn(LOG_INFO,"Other side has a cert but it's bad. Closing.");
        return -1;
      }
288
289
290
291
      router = router_get_by_pk(pk);
      if (!router) {
        log_fn(LOG_INFO,"Unrecognized public key from peer. Closing.");
        crypto_free_pk_env(pk);
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
        return -1;
      }
      if(conn->pkey) { /* I initiated this connection. */
        if(crypto_pk_cmp_keys(conn->pkey, pk)) {
          log_fn(LOG_INFO,"We connected to '%s' but he gave us a different key. Closing.", router->nickname);
          crypto_free_pk_env(pk);
          return -1;
        }
        log_fn(LOG_DEBUG,"The router's pk matches the one we meant to connect to. Good.");
        crypto_free_pk_env(pk);
      } else {
        if(connection_exact_get_by_addr_port(router->addr,router->or_port)) {
          log_fn(LOG_INFO,"That router is already connected. Dropping.");
          return -1;
        }
        conn->pkey = pk;
        conn->bandwidth = router->bandwidth;
        conn->addr = router->addr, conn->port = router->or_port;
310
311
        if(conn->address)
          free(conn->address);
312
        conn->address = strdup(router->address);
313
      }
314
315
316
317
    } else { /* it's an OP */
      conn->bandwidth = DEFAULT_BANDWIDTH_OP;
    }
  } else { /* I'm a client */
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
    if(!tor_tls_peer_has_cert(conn->tls)) { /* it's a client too?! */
      log_fn(LOG_INFO,"Neither peer sent a cert! Closing.");
      return -1;
    }
    pk = tor_tls_verify(conn->tls);
    if(!pk) {
      log_fn(LOG_INFO,"Other side has a cert but it's bad. Closing.");
      return -1;
    }
    router = router_get_by_pk(pk);
    if (!router) {
      log_fn(LOG_INFO,"Unrecognized public key from peer. Closing.");
      crypto_free_pk_env(pk);
      return -1;
    }
    if(crypto_pk_cmp_keys(conn->pkey, pk)) {
      log_fn(LOG_INFO,"We connected to '%s' but he gave us a different key. Closing.", router->nickname);
      crypto_free_pk_env(pk);
      return -1;
    }
    log_fn(LOG_DEBUG,"The router's pk matches the one we meant to connect to. Good.");
    crypto_free_pk_env(pk);
340
341
342
343
344
    conn->bandwidth = DEFAULT_BANDWIDTH_OP;
    circuit_n_conn_open(conn); /* send the pending create */
  }
  return 0;
}
Roger Dingledine's avatar
Roger Dingledine committed
345

346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
/* take conn, make a nonblocking socket; try to connect to 
 * addr:port (they arrive in *host order*). If fail, return -1. Else
 * assign s to conn->s: if connected return 1, if eagain return 0.
 * address is used to make the logs useful.
 */
int connection_connect(connection_t *conn, char *address, uint32_t addr, uint16_t port) {
  int s;
  struct sockaddr_in dest_addr;

  s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
  if (s < 0) {
    log_fn(LOG_ERR,"Error creating network socket.");
    return -1;
  }
  set_socket_nonblocking(s);

  memset((void *)&dest_addr,0,sizeof(dest_addr));
  dest_addr.sin_family = AF_INET;
  dest_addr.sin_port = htons(port);
  dest_addr.sin_addr.s_addr = htonl(addr);

  log_fn(LOG_DEBUG,"Connecting to %s:%u.",address,port);

  if(connect(s,(struct sockaddr *)&dest_addr,sizeof(dest_addr)) < 0) {
    if(!ERRNO_CONN_EINPROGRESS(errno)) {
      /* yuck. kill it. */
      perror("connect");
      log_fn(LOG_DEBUG,"Connect failed.");
      return -1;
    } else {
      /* it's in progress. set state appropriately and return. */
      conn->s = s;
      log_fn(LOG_DEBUG,"connect in progress, socket %d.",s);
      return 0;
    }
  }

  /* it succeeded. we're connected. */
  log_fn(LOG_DEBUG,"Connection to %s:%u established.",address,port);
  conn->s = s;
  return 1;
}

Roger Dingledine's avatar
Roger Dingledine committed
389
/* start all connections that should be up but aren't */
390
int retry_all_connections(uint16_t or_listenport, uint16_t ap_listenport, uint16_t dir_listenport) {
391
  struct sockaddr_in bindaddr; /* where to bind */
392

393
  if(or_listenport) {
394
    router_retry_connections();
Roger Dingledine's avatar
Roger Dingledine committed
395
396
  }

397
398
399
  memset(&bindaddr,0,sizeof(struct sockaddr_in));
  bindaddr.sin_family = AF_INET;
  bindaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* anyone can connect */
400

401
  if(or_listenport) {
402
    bindaddr.sin_port = htons(or_listenport);
403
    if(!connection_get_by_type(CONN_TYPE_OR_LISTENER)) {
404
      connection_create_listener(&bindaddr, CONN_TYPE_OR_LISTENER);
405
    }
Roger Dingledine's avatar
Roger Dingledine committed
406
  }
407

408
  if(dir_listenport) {
409
    bindaddr.sin_port = htons(dir_listenport);
410
    if(!connection_get_by_type(CONN_TYPE_DIR_LISTENER)) {
411
      connection_create_listener(&bindaddr, CONN_TYPE_DIR_LISTENER);
412
413
    }
  }
Roger Dingledine's avatar
Roger Dingledine committed
414
 
415
  if(ap_listenport) {
416
    bindaddr.sin_port = htons(ap_listenport);
417
    bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* the AP listens only on localhost! */
418
    if(!connection_get_by_type(CONN_TYPE_AP_LISTENER)) {
419
      connection_create_listener(&bindaddr, CONN_TYPE_AP_LISTENER);
420
421
422
    }
  }

Roger Dingledine's avatar
Roger Dingledine committed
423
424
425
  return 0;
}

426
427
428
429
430
431
432
433
int connection_handle_read(connection_t *conn) {
  struct timeval now;

  my_gettimeofday(&now);
  conn->timestamp_lastread = now.tv_sec;

  switch(conn->type) {
    case CONN_TYPE_OR_LISTENER:
434
      return connection_handle_listener_read(conn, CONN_TYPE_OR);
435
    case CONN_TYPE_AP_LISTENER:
436
      return connection_handle_listener_read(conn, CONN_TYPE_AP);
437
    case CONN_TYPE_DIR_LISTENER:
438
439
      return connection_handle_listener_read(conn, CONN_TYPE_DIR);
  }
440

441
  if(connection_read_to_buf(conn) < 0) {
442
    if(conn->type == CONN_TYPE_DIR && 
443
444
      (conn->state == DIR_CONN_STATE_CONNECTING_FETCH ||
       conn->state == DIR_CONN_STATE_CONNECTING_UPLOAD)) {
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
       /* it's a directory server and connecting failed: forget about this router */
       /* XXX I suspect pollerr may make Windows not get to this point. :( */
       router_forget_router(conn->addr,conn->port); 
         /* FIXME i don't think router_forget_router works. */
    }
    return -1;
  }
  if(connection_process_inbuf(conn) < 0) {
    //log_fn(LOG_DEBUG,"connection_process_inbuf returned %d.",retval);
    return -1;
  }
  if(!connection_state_is_open(conn) && conn->receiver_bucket == 0) {
    log_fn(LOG_DEBUG,"receiver bucket reached 0 before handshake finished. Closing.");
    return -1;
  }
Roger Dingledine's avatar
Roger Dingledine committed
460
  return 0;
461
462
}

Roger Dingledine's avatar
Roger Dingledine committed
463
/* return -1 if we want to break conn, else return 0 */
Roger Dingledine's avatar
Roger Dingledine committed
464
int connection_read_to_buf(connection_t *conn) {
Roger Dingledine's avatar
Roger Dingledine committed
465
  int result;
Roger Dingledine's avatar
Roger Dingledine committed
466
  int at_most;
467

Roger Dingledine's avatar
Roger Dingledine committed
468
469
470
471
472
  assert((connection_speaks_cells(conn) && conn->receiver_bucket >= 0) ||
         (!connection_speaks_cells(conn) && conn->receiver_bucket < 0));

  if(options.LinkPadding) {
    at_most = global_read_bucket;
473
  } else {
Roger Dingledine's avatar
Roger Dingledine committed
474
475
476
477
478
479
480
481
482
    /* do a rudimentary round-robin so one connection can't hog a thickpipe */
    if(connection_speaks_cells(conn)) {
      at_most = 10*(CELL_NETWORK_SIZE);
    } else {
      at_most = 10*(CELL_PAYLOAD_SIZE - RELAY_HEADER_SIZE);
    }

    if(at_most > global_read_bucket)
      at_most = global_read_bucket;
483
  }
484

485
486
487
  if(conn->receiver_bucket >= 0 && at_most > conn->receiver_bucket)
    at_most = conn->receiver_bucket;

Roger Dingledine's avatar
Roger Dingledine committed
488
  if(connection_speaks_cells(conn) && conn->state != OR_CONN_STATE_CONNECTING) {
489
490
491
492
493
494
    if(conn->state == OR_CONN_STATE_HANDSHAKING)
      return connection_tls_continue_handshake(conn);

    /* else open, or closing */
    result = read_to_buf_tls(conn->tls, at_most, &conn->inbuf,
                             &conn->inbuflen, &conn->inbuf_datalen);
Roger Dingledine's avatar
Roger Dingledine committed
495
496
497
498
499
500

    switch(result) {
      case TOR_TLS_ERROR:
      case TOR_TLS_CLOSE:
        log_fn(LOG_DEBUG,"tls error. breaking.");
        return -1; /* XXX deal with close better */
501
      case TOR_TLS_WANTWRITE:
Roger Dingledine's avatar
Roger Dingledine committed
502
503
        connection_start_writing(conn);
        return 0;
504
      case TOR_TLS_WANTREAD: /* we're already reading */
Roger Dingledine's avatar
Roger Dingledine committed
505
506
      case TOR_TLS_DONE: /* no data read, so nothing to process */
        return 0;
507
    }
508
  } else {
Roger Dingledine's avatar
Roger Dingledine committed
509
510
511
512
513
514
    result = read_to_buf(conn->s, at_most, &conn->inbuf, &conn->inbuflen,
                         &conn->inbuf_datalen, &conn->inbuf_reached_eof);
//  log(LOG_DEBUG,"connection_read_to_buf(): read_to_buf returned %d.",read_result);

    if(result < 0)
      return -1;
515
516
  }

Roger Dingledine's avatar
Roger Dingledine committed
517
518
519
520
521
522
523
524
525
  global_read_bucket -= result; assert(global_read_bucket >= 0);
  if(connection_speaks_cells(conn))
    conn->receiver_bucket -= result;
  if(conn->receiver_bucket == 0 || global_read_bucket == 0) {
    log_fn(LOG_DEBUG,"buckets (%d, %d) exhausted. Pausing.", global_read_bucket, conn->receiver_bucket);
    conn->wants_to_read = 1;
    connection_stop_reading(conn);
  }
  return 0;
Roger Dingledine's avatar
Roger Dingledine committed
526
527
528
529
530
531
}

int connection_fetch_from_buf(char *string, int len, connection_t *conn) {
  return fetch_from_buf(string, len, &conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen);
}

532
533
534
535
int connection_find_on_inbuf(char *string, int len, connection_t *conn) {
  return find_on_inbuf(string, len, conn->inbuf, conn->inbuf_datalen);
}

536
537
538
539
540
541
542
543
int connection_wants_to_flush(connection_t *conn) {
  return conn->outbuf_flushlen;
}

int connection_outbuf_too_full(connection_t *conn) {
  return (conn->outbuf_flushlen > 10*CELL_PAYLOAD_SIZE);
}

Roger Dingledine's avatar
Roger Dingledine committed
544
int connection_flush_buf(connection_t *conn) {
545
546
  return flush_buf(conn->s, &conn->outbuf, &conn->outbuflen,
                   &conn->outbuf_flushlen, &conn->outbuf_datalen);
Roger Dingledine's avatar
Roger Dingledine committed
547
548
}

Roger Dingledine's avatar
Roger Dingledine committed
549
/* return -1 if you want to break the conn, else return 0 */
550
int connection_handle_write(connection_t *conn) {
551
  struct timeval now;
552
553
554
555
556

  if(connection_is_listener(conn)) {
    log_fn(LOG_DEBUG,"Got a listener socket. Can't happen!");
    return -1;
  }
557

558
  my_gettimeofday(&now);
559
560
  conn->timestamp_lastwritten = now.tv_sec;

Roger Dingledine's avatar
Roger Dingledine committed
561
  if(connection_speaks_cells(conn) && conn->state != OR_CONN_STATE_CONNECTING) {
562
563
    if(conn->state == OR_CONN_STATE_HANDSHAKING) {
      connection_stop_writing(conn);
564
      return connection_tls_continue_handshake(conn);
565
    }
566

567
568
569
    /* else open, or closing */
    switch(flush_buf_tls(conn->tls, &conn->outbuf, &conn->outbuflen,
                         &conn->outbuf_flushlen, &conn->outbuf_datalen)) {
Roger Dingledine's avatar
Roger Dingledine committed
570
571
572
573
      case TOR_TLS_ERROR:
      case TOR_TLS_CLOSE:
        log_fn(LOG_DEBUG,"tls error. breaking.");
        return -1; /* XXX deal with close better */
574
      case TOR_TLS_WANTWRITE:
Roger Dingledine's avatar
Roger Dingledine committed
575
576
        /* we're already writing */
        return 0;
577
      case TOR_TLS_WANTREAD:
Roger Dingledine's avatar
Roger Dingledine committed
578
579
580
581
582
583
584
585
586
587
        /* Make sure to avoid a loop if the receive buckets are empty. */
        if(!connection_is_reading(conn)) {
          connection_stop_writing(conn);
          conn->wants_to_write = 1;
          /* we'll start reading again when the next second arrives,
           * and then also start writing again.
           */
        }
        /* else no problem, we're already reading */
        return 0;
588
589
      /* case TOR_TLS_DONE:
       * for TOR_TLS_DONE, fall through to check if the flushlen
Roger Dingledine's avatar
Roger Dingledine committed
590
591
592
       * is empty, so we can stop writing.
       */  
    }
593
  } else {
Roger Dingledine's avatar
Roger Dingledine committed
594
595
596
597
    if(flush_buf(conn->s, &conn->outbuf, &conn->outbuflen,
                 &conn->outbuf_flushlen, &conn->outbuf_datalen) < 0)
      return -1;
      /* conns in CONNECTING state will fall through... */
598
599
  }

Roger Dingledine's avatar
Roger Dingledine committed
600
601
602
603
604
  if(!connection_wants_to_flush(conn)) /* it's done flushing */
    if(connection_finished_flushing(conn) < 0) /* ...and get handled here. */
      return -1;

  return 0;
605
606
607
}

int connection_write_to_buf(char *string, int len, connection_t *conn) {
608

Roger Dingledine's avatar
Roger Dingledine committed
609
610
  if(!len)
    return 0;
611

612
613
614
  if(conn->marked_for_close)
    return 0;

615
  if( (!connection_speaks_cells(conn)) ||
616
617
      (!connection_state_is_open(conn)) ||
      (options.LinkPadding == 0) ) {
618
    /* connection types other than or, or or not in 'open' state, should flush immediately */
619
    /* also flush immediately if we're not doing LinkPadding, since otherwise it will never flush */
620
    connection_start_writing(conn);
621
622
623
    conn->outbuf_flushlen += len;
  }

Roger Dingledine's avatar
Roger Dingledine committed
624
625
626
  return write_to_buf(string, len, &conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen);
}

627
628
629
int connection_receiver_bucket_should_increase(connection_t *conn) {
  assert(conn);

630
631
632
  if(!connection_speaks_cells(conn))
    return 0; /* edge connections don't use receiver_buckets */

633
  if(conn->receiver_bucket > 9*conn->bandwidth)
634
635
636
637
638
    return 0;

  return 1;
}

639
int connection_is_listener(connection_t *conn) {
640
  if(conn->type == CONN_TYPE_OR_LISTENER ||
641
642
     conn->type == CONN_TYPE_AP_LISTENER ||
     conn->type == CONN_TYPE_DIR_LISTENER)
643
644
645
646
    return 1;
  return 0;
}

647
648
649
650
651
652
653
654
655
656
657
int connection_state_is_open(connection_t *conn) {
  assert(conn);

  if((conn->type == CONN_TYPE_OR && conn->state == OR_CONN_STATE_OPEN) ||
     (conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN) ||
     (conn->type == CONN_TYPE_EXIT && conn->state == EXIT_CONN_STATE_OPEN))
    return 1;

  return 0;
}

Roger Dingledine's avatar
Roger Dingledine committed
658
659
660
int connection_send_destroy(aci_t aci, connection_t *conn) {
  cell_t cell;

661
  assert(conn);
Roger Dingledine's avatar
Roger Dingledine committed
662

663
  if(!connection_speaks_cells(conn)) {
664
     log_fn(LOG_INFO,"Aci %d: At an edge. Marking connection for close.", aci);
Roger Dingledine's avatar
Roger Dingledine committed
665
666
667
668
     conn->marked_for_close = 1;
     return 0;
  }

669
  memset(&cell, 0, sizeof(cell_t));
Roger Dingledine's avatar
Roger Dingledine committed
670
671
  cell.aci = aci;
  cell.command = CELL_DESTROY;
672
  log_fn(LOG_INFO,"Sending destroy (aci %d).",aci);
Roger Dingledine's avatar
Roger Dingledine committed
673
  return connection_write_cell_to_buf(&cell, conn);
Roger Dingledine's avatar
Roger Dingledine committed
674
675
}

Roger Dingledine's avatar
Roger Dingledine committed
676
677
678
679
680
681
682
int connection_process_inbuf(connection_t *conn) {

  assert(conn);

  switch(conn->type) {
    case CONN_TYPE_OR:
      return connection_or_process_inbuf(conn);
683
    case CONN_TYPE_EXIT:
684
    case CONN_TYPE_AP:
685
      return connection_edge_process_inbuf(conn);
686
687
    case CONN_TYPE_DIR:
      return connection_dir_process_inbuf(conn);
688
    case CONN_TYPE_DNSWORKER:
689
      return connection_dns_process_inbuf(conn); 
Roger Dingledine's avatar
Roger Dingledine committed
690
691
    case CONN_TYPE_CPUWORKER:
      return connection_cpu_process_inbuf(conn); 
Roger Dingledine's avatar
Roger Dingledine committed
692
    default:
693
      log_fn(LOG_DEBUG,"got unexpected conn->type.");
Roger Dingledine's avatar
Roger Dingledine committed
694
695
696
697
698
699
700
701
      return -1;
  }
}

int connection_finished_flushing(connection_t *conn) {

  assert(conn);

702
//  log_fn(LOG_DEBUG,"entered. Socket %u.", conn->s);
Roger Dingledine's avatar
Roger Dingledine committed
703
704
705
706

  switch(conn->type) {
    case CONN_TYPE_OR:
      return connection_or_finished_flushing(conn);
707
    case CONN_TYPE_AP:
708
    case CONN_TYPE_EXIT:
709
      return connection_edge_finished_flushing(conn);
710
711
    case CONN_TYPE_DIR:
      return connection_dir_finished_flushing(conn);
712
    case CONN_TYPE_DNSWORKER:
713
      return connection_dns_finished_flushing(conn);
Roger Dingledine's avatar
Roger Dingledine committed
714
715
    case CONN_TYPE_CPUWORKER:
      return connection_cpu_finished_flushing(conn);
Roger Dingledine's avatar
Roger Dingledine committed
716
    default:
717
      log_fn(LOG_DEBUG,"got unexpected conn->type.");
Roger Dingledine's avatar
Roger Dingledine committed
718
719
720
721
      return -1;
  }
}

722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
void assert_connection_ok(connection_t *conn, time_t now)
{
  assert(conn);
  assert(conn->type >= _CONN_TYPE_MIN);
  assert(conn->type <= _CONN_TYPE_MAX);

  /* XXX check: wants_to_read, wants_to_write, s, poll_index,
   * marked_for_close. */
  
  /* buffers */
  assert(conn->inbuf);
  assert(conn->inbuflen <= conn->inbuf_datalen);
  assert(conn->inbuflen >= 0);
  assert(conn->inbuf_datalen > 0);
  assert(conn->outbuf);
  assert(conn->outbuflen <= conn->outbuf_datalen);
  assert(conn->outbuflen >= 0);
  assert(conn->outbuf_datalen > 0);

  assert(!now || conn->timestamp_lastread <= now);
  assert(!now || conn->timestamp_lastwritten <= now);
  assert(conn->timestamp_created <= conn->timestamp_lastread);
  assert(conn->timestamp_created <= conn->timestamp_lastwritten);
  
  if (conn->type != CONN_TYPE_OR) {
    assert(conn->bandwidth == -1);
    assert(conn->receiver_bucket == -1);
    /* Addr, port, address XXX */
    assert(!conn->pkey);
    assert(!conn->tls);
  } else {
    assert(conn->bandwidth);
    assert(conn->receiver_bucket >= 0);
    assert(conn->receiver_bucket <= 10*conn->bandwidth);
    assert(conn->addr && conn->port);
    assert(conn->address);
    assert(conn->pkey);
    if (conn->state != OR_CONN_STATE_CONNECTING)
      assert(conn->tls);
  }
  
763
  if (conn->type != CONN_TYPE_EXIT && conn->type != CONN_TYPE_AP) {
764
765
766
767
768
769
770
771
772
    assert(!conn->stream_id[0]);
    assert(!conn->next_stream);
    assert(!conn->cpath_layer);
    assert(!conn->package_window);
    assert(!conn->deliver_window);
    assert(!conn->done_sending);
    assert(!conn->done_receiving);
  } else {
    assert(!conn->next_stream || 
773
774
           conn->next_stream->type == CONN_TYPE_EXIT ||
           conn->next_stream->type == CONN_TYPE_AP);
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
    assert(conn->cpath_layer);
    assert_cpath_layer_ok(conn->cpath_layer);
    /* XXX unchecked, package window, deliver window. */
  }

  switch(conn->type) 
    {
    case CONN_TYPE_OR_LISTENER:
    case CONN_TYPE_AP_LISTENER:
    case CONN_TYPE_DIR_LISTENER:
      assert(conn->state == LISTENER_STATE_READY);
      break;
    case CONN_TYPE_OR:
      assert(conn->state >= _OR_CONN_STATE_MIN &&
             conn->state <= _OR_CONN_STATE_MAX);
      break;
    case CONN_TYPE_EXIT:
      assert(conn->state >= _EXIT_CONN_STATE_MIN &&
             conn->state <= _EXIT_CONN_STATE_MAX);
      break;
    case CONN_TYPE_AP:
Roger Dingledine's avatar
Roger Dingledine committed
796
      assert(conn->state >= _AP_CONN_STATE_MIN &&
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
             conn->state <= _AP_CONN_STATE_MAX);
      break;
    case CONN_TYPE_DIR:
      assert(conn->state >= _DIR_CONN_STATE_MIN &&
             conn->state <= _DIR_CONN_STATE_MAX);
      break;
    case CONN_TYPE_DNSWORKER:
      assert(conn->state == DNSWORKER_STATE_IDLE ||
             conn->state == DNSWORKER_STATE_BUSY);
    case CONN_TYPE_CPUWORKER:
      assert(conn->state >= _CPUWORKER_STATE_MIN &&
             conn->state <= _CPUWORKER_STATE_MAX);
      break;
    default:
      assert(0);
  }
}

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