connection_edge.c 32 KB
Newer Older
1
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
2
3
4
5
/* See LICENSE for licensing information */
/* $Id$ */

#include "or.h"
6
#include "tree.h"
7
8
9

extern or_options_t options; /* command-line and config-file options */

10
static int connection_ap_handshake_process_socks(connection_t *conn);
11
static int connection_ap_handshake_attach_circuit(connection_t *conn);
12
static void connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ);
Roger Dingledine's avatar
Roger Dingledine committed
13
14
static int connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
                                               int replylen, char success);
15
16

static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
17
static void connection_edge_consider_sending_sendme(connection_t *conn);
18

19
20
21
static uint32_t client_dns_lookup_entry(const char *address);
static void client_dns_set_entry(const char *address, uint32_t val);

22
23
24
25
26
27
28
29
30
31
int connection_edge_process_inbuf(connection_t *conn) {

  assert(conn);
  assert(conn->type == CONN_TYPE_AP || conn->type == CONN_TYPE_EXIT);

  if(conn->inbuf_reached_eof) {
#ifdef HALF_OPEN
    /* eof reached; we're done reading, but we might want to write more. */ 
    conn->done_receiving = 1;
    shutdown(conn->s, 0); /* XXX check return, refactor NM */
32
    if (conn->done_sending) {
33
      connection_edge_end(conn, END_STREAM_REASON_DONE, conn->cpath_layer);
34
35
36
37
    } else {
      connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_END,
                                   NULL, 0, conn->cpath_layer);
    }
38
39
40
    return 0;
#else 
    /* eof reached, kill it. */
41
    log_fn(LOG_INFO,"conn (fd %d) reached eof. Closing.", conn->s);
42
    connection_edge_end(conn, END_STREAM_REASON_DONE, conn->cpath_layer);
43
    return -1;
44
45
46
47
48
#endif
  }

  switch(conn->state) {
    case AP_CONN_STATE_SOCKS_WAIT:
49
      if(connection_ap_handshake_process_socks(conn) < 0) {
50
        connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
51
52
53
        return -1;
      }
      return 0;
54
55
    case AP_CONN_STATE_OPEN:
    case EXIT_CONN_STATE_OPEN:
56
57
58
59
      if(conn->package_window <= 0) {
        log_fn(LOG_WARN,"called with package_window %d. Tell Roger.", conn->package_window);
        return 0;
      }
60
      if(connection_edge_package_raw_inbuf(conn) < 0) {
61
        connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
62
63
        return -1;
      }
64
65
      return 0;
    case EXIT_CONN_STATE_CONNECTING:
66
      log_fn(LOG_INFO,"text from server while in 'connecting' state at exit. Leaving it on buffer.");
67
68
69
70
71
72
      return 0;
  }

  return 0;
}

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
char *connection_edge_end_reason(char *payload, unsigned char length) {
  if(length < 1) {
    log_fn(LOG_WARN,"End cell arrived with length 0. Should be at least 1.");
    return "MALFORMED";
  }
  if(*payload < END_STREAM_REASON_MISC || *payload > END_STREAM_REASON_DONE) {
    log_fn(LOG_WARN,"Reason for ending (%d) not recognized.",*payload);
    return "MALFORMED";
  }
  switch(*payload) {
    case END_STREAM_REASON_MISC:           return "misc error";
    case END_STREAM_REASON_RESOLVEFAILED:  return "resolve failed";
    case END_STREAM_REASON_CONNECTFAILED:  return "connect failed";
    case END_STREAM_REASON_EXITPOLICY:     return "exit policy failed";
    case END_STREAM_REASON_DESTROY:        return "destroyed";
    case END_STREAM_REASON_DONE:           return "closed normally";
  }
  assert(0);
  return "";
}

void connection_edge_end(connection_t *conn, char reason, crypt_path_t *cpath_layer) {
  char payload[5];
  int payload_len=1;
  circuit_t *circ;
98
99
100
101
102
103

  if(conn->has_sent_end) {
    log_fn(LOG_WARN,"It appears I've already sent the end. Are you calling me twice?");
    return;
  }

104
105
106
  payload[0] = reason;
  if(reason == END_STREAM_REASON_EXITPOLICY) {
    *(uint32_t *)(payload+1) = htonl(conn->addr);
Roger Dingledine's avatar
Roger Dingledine committed
107
    payload_len += 4;
108
109
110
  }

  circ = circuit_get_by_conn(conn);
111
112
113
114
115
116
117
118
119
120
  if(circ) {
    log_fn(LOG_DEBUG,"Marking conn (fd %d) and sending end.",conn->s);
    connection_edge_send_command(conn, circ, RELAY_COMMAND_END,
                                 payload, payload_len, cpath_layer);
  }

  conn->marked_for_close = 1;
  conn->has_sent_end = 1;
}

121
122
int connection_edge_send_command(connection_t *fromconn, circuit_t *circ, int relay_command,
                                 void *payload, int payload_len, crypt_path_t *cpath_layer) {
123
  cell_t cell;
124
  int cell_direction;
125
  int is_control_cell=0;
126
127

  if(!circ) {
Roger Dingledine's avatar
Roger Dingledine committed
128
    log_fn(LOG_WARN,"no circ. Closing.");
129
    return 0;
130
131
  }

132
133
134
  if(!fromconn || relay_command == RELAY_COMMAND_BEGIN) /* XXX more */
    is_control_cell = 1;

135
  memset(&cell, 0, sizeof(cell_t));
136
  if(fromconn && fromconn->type == CONN_TYPE_AP) {
137
    cell.circ_id = circ->n_circ_id;
138
139
140
    cell_direction = CELL_DIRECTION_OUT;
  } else {
    /* NOTE: if !fromconn, we assume that it's heading towards the OP */
141
    cell.circ_id = circ->p_circ_id;
142
143
144
    cell_direction = CELL_DIRECTION_IN;
  }

145
146
  cell.command = CELL_RELAY;
  SET_CELL_RELAY_COMMAND(cell, relay_command);
147
  if(is_control_cell)
148
    SET_CELL_STREAM_ID(cell, ZERO_STREAM);
149
150
  else
    SET_CELL_STREAM_ID(cell, fromconn->stream_id);
151

152
153
154
155
  cell.length = RELAY_HEADER_SIZE + payload_len;
  if(payload_len) {
    memcpy(cell.payload+RELAY_HEADER_SIZE,payload,payload_len);
  }
Roger Dingledine's avatar
Roger Dingledine committed
156
  log_fn(LOG_DEBUG,"delivering %d cell %s.", relay_command,
157
         cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward");
158

159
  if(circuit_deliver_relay_cell(&cell, circ, cell_direction, cpath_layer) < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
160
    log_fn(LOG_WARN,"circuit_deliver_relay_cell failed. Closing.");
161
    circuit_close(circ);
162
    return -1;
163
  }
164
  return 0;
165
166
}

167
168
/* an incoming relay cell has arrived. return -1 if you want to tear down the
 * circuit, else 0. */
169
170
int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection_t *conn,
                                       int edge_type, crypt_path_t *layer_hint) {
171
  int relay_command;
172
  static int num_seen=0;
173
  uint32_t addr;
174
175
176

  assert(cell && circ);

177
  relay_command = CELL_RELAY_COMMAND(*cell);
178
//  log_fn(LOG_DEBUG,"command %d stream %d", relay_command, stream_id);
179
  num_seen++;
180
  log_fn(LOG_DEBUG,"Now seen %d relay cells here.", num_seen);
181

182
183
  /* either conn is NULL, in which case we've got a control cell, or else
   * conn points to the recognized stream. */
184
185

  if(conn && conn->state != AP_CONN_STATE_OPEN && conn->state != EXIT_CONN_STATE_OPEN) {
186
    if(conn->type == CONN_TYPE_EXIT && relay_command == RELAY_COMMAND_END) {
187
188
      log_fn(LOG_INFO,"Exit got end (%s) before we're connected. Marking for close.",
        connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, cell->length));
189
190
191
192
      if(conn->state == EXIT_CONN_STATE_RESOLVING) {
        log_fn(LOG_INFO,"...and informing resolver we don't want the answer anymore.");
        dns_cancel_pending_resolve(conn->address, conn);
      }
193
194
      conn->marked_for_close = 1;
      conn->has_sent_end = 1;
195
      return 0;
196
    } else {
Roger Dingledine's avatar
Roger Dingledine committed
197
      log_fn(LOG_WARN,"Got an unexpected relay cell, not in 'open' state. Closing.");
198
      connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
199
      return -1;
200
201
202
    }
  }

203
204
  switch(relay_command) {
    case RELAY_COMMAND_BEGIN:
205
      if(edge_type == EDGE_AP) {
Roger Dingledine's avatar
Roger Dingledine committed
206
        log_fn(LOG_WARN,"relay begin request unsupported at AP. Dropping.");
207
208
        return 0;
      }
209
      if(conn) {
Roger Dingledine's avatar
Roger Dingledine committed
210
        log_fn(LOG_WARN,"begin cell for known stream. Dropping.");
211
212
        return 0;
      }
213
214
      connection_exit_begin_conn(cell, circ);
      return 0;
215
    case RELAY_COMMAND_DATA:
216
      ++stats_n_data_cells_received;
217
218
      if((edge_type == EDGE_AP && --layer_hint->deliver_window < 0) ||
         (edge_type == EDGE_EXIT && --circ->deliver_window < 0)) {
Roger Dingledine's avatar
Roger Dingledine committed
219
        log_fn(LOG_WARN,"(relay data) circ deliver_window below 0. Killing.");
220
        connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
221
        return -1;
222
      }
223
      log_fn(LOG_DEBUG,"circ deliver_window now %d.", edge_type == EDGE_AP ? layer_hint->deliver_window : circ->deliver_window);
224

225
      if(circuit_consider_sending_sendme(circ, edge_type, layer_hint) < 0) {
226
        log_fn(LOG_WARN,"circuit_consider_sending_sendme() failed.");
227
        conn->has_sent_end = 1; /* we failed because conn is broken. can't send end. */
228
        return -1;
229
      }
230

231
      if(!conn) {
232
        log_fn(LOG_INFO,"relay cell dropped, unknown stream %d.",*(int*)conn->stream_id);
233
234
        return 0;
      }
235
236

      if(--conn->deliver_window < 0) { /* is it below 0 after decrement? */
Roger Dingledine's avatar
Roger Dingledine committed
237
        log_fn(LOG_WARN,"(relay data) conn deliver_window below 0. Killing.");
238
239
240
        return -1; /* somebody's breaking protocol. kill the whole circuit. */
      }

241
//      printf("New text for buf (%d bytes): '%s'", cell->length - RELAY_HEADER_SIZE, cell->payload + RELAY_HEADER_SIZE);
242
      stats_n_data_bytes_received += (cell->length - RELAY_HEADER_SIZE);
243
      connection_write_to_buf(cell->payload + RELAY_HEADER_SIZE,
244
                              cell->length - RELAY_HEADER_SIZE, conn);
245
      connection_edge_consider_sending_sendme(conn);
246
      return 0;
247
    case RELAY_COMMAND_END:
248
      if(!conn) {
249
250
251
        log_fn(LOG_INFO,"end cell (%s) dropped, unknown stream %d.",
          connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, cell->length),
          *(int*)conn->stream_id);
252
253
        return 0;
      }
254
255
      if(cell->length-RELAY_HEADER_SIZE >= 5 && 
         *(cell->payload+RELAY_HEADER_SIZE) == END_STREAM_REASON_EXITPOLICY) {
256
257
        /* No need to close the connection. We'll hold it open while
         * we try a new exit node.
Roger Dingledine's avatar
Roger Dingledine committed
258
         * cell->payload+RELAY_HEADER_SIZE+1 holds the destination addr.
259
         */
260
        addr = ntohl(*(uint32_t*)(cell->payload+RELAY_HEADER_SIZE+1));
Roger Dingledine's avatar
Roger Dingledine committed
261
        client_dns_set_entry(conn->socks_request->address, addr);
262
        conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
263
        if(connection_ap_handshake_attach_circuit(conn) < 0)
264
          circuit_launch_new(); /* Build another circuit to handle this stream */
265
        return 0;
266
      }
267
268
269
      log_fn(LOG_INFO,"end cell (%s) for stream %d. Removing stream.",
        connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, cell->length),
        *(int*)conn->stream_id);
270
271
272
273

#ifdef HALF_OPEN
      conn->done_sending = 1;
      shutdown(conn->s, 1); /* XXX check return; refactor NM */
274
275
276
277
278
279
280
      if (conn->done_receiving) {
        conn->marked_for_close = 1;
        conn->has_sent_end = 1; /* no need to send end, we just got one! */
      }
#else
      conn->marked_for_close = 1;
      conn->has_sent_end = 1; /* no need to send end, we just got one! */
281
#endif
282
      return 0;
283
284
    case RELAY_COMMAND_EXTEND:
      if(conn) {
Roger Dingledine's avatar
Roger Dingledine committed
285
        log_fn(LOG_WARN,"'extend' for non-zero stream. Dropping.");
286
287
288
289
290
        return 0;
      }
      return circuit_extend(cell, circ);
    case RELAY_COMMAND_EXTENDED:
      if(edge_type == EDGE_EXIT) {
Roger Dingledine's avatar
Roger Dingledine committed
291
        log_fn(LOG_WARN,"'extended' unsupported at exit. Dropping.");
292
293
        return 0;
      }
294
      log_fn(LOG_DEBUG,"Got an extended cell! Yay.");
295
      if(circuit_finish_handshake(circ, cell->payload+RELAY_HEADER_SIZE) < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
296
        log_fn(LOG_WARN,"circuit_finish_handshake failed.");
297
298
        return -1;
      }
299
300
301
302
303
      if (circuit_send_next_onion_skin(circ)<0) {
        log_fn(LOG_WARN,"circuit_send_next_onion_skin() failed.");
        return -1;
      }
      return 0;
304
305
    case RELAY_COMMAND_TRUNCATE:
      if(edge_type == EDGE_AP) {
Roger Dingledine's avatar
Roger Dingledine committed
306
        log_fn(LOG_WARN,"'truncate' unsupported at AP. Dropping.");
307
308
309
        return 0;
      }
      if(circ->n_conn) {
310
        connection_send_destroy(circ->n_circ_id, circ->n_conn);
311
312
        circ->n_conn = NULL;
      }
313
      log_fn(LOG_DEBUG, "Processed 'truncate', replying.");
314
315
316
      connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED,
                                   NULL, 0, NULL);
      return 0;
317
318
    case RELAY_COMMAND_TRUNCATED:
      if(edge_type == EDGE_EXIT) {
Roger Dingledine's avatar
Roger Dingledine committed
319
        log_fn(LOG_WARN,"'truncated' unsupported at exit. Dropping.");
320
321
        return 0;
      }
322
323
      circuit_truncated(circ, layer_hint);
      return 0;
324
    case RELAY_COMMAND_CONNECTED:
325
      if(edge_type == EDGE_EXIT) {
Roger Dingledine's avatar
Roger Dingledine committed
326
        log_fn(LOG_WARN,"'connected' unsupported at exit. Dropping.");
327
328
329
        return 0;
      }
      if(!conn) {
330
        log_fn(LOG_INFO,"connected cell dropped, unknown stream %d.",*(int*)conn->stream_id);
331
        return 0;
332
      }
333
      log_fn(LOG_INFO,"Connected! Notifying application.");
334
      if (cell->length-RELAY_HEADER_SIZE == 4) {
335
        addr = ntohl(*(uint32_t*)(cell->payload + RELAY_HEADER_SIZE));
Roger Dingledine's avatar
Roger Dingledine committed
336
        client_dns_set_entry(conn->socks_request->address, addr);
337
      }
Roger Dingledine's avatar
Roger Dingledine committed
338
      if(connection_ap_handshake_socks_reply(conn, NULL, 0, 1) < 0) {
339
        log_fn(LOG_INFO,"Writing to socks-speaking application failed. Closing.");
340
        connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
341
      }
342
      return 0;
343
    case RELAY_COMMAND_SENDME:
344
      if(!conn) {
345
346
347
        if(edge_type == EDGE_AP) {
          assert(layer_hint);
          layer_hint->package_window += CIRCWINDOW_INCREMENT;
348
          log_fn(LOG_DEBUG,"circ-level sendme at AP, packagewindow %d.", layer_hint->package_window);
349
350
351
352
          circuit_resume_edge_reading(circ, EDGE_AP, layer_hint);
        } else {
          assert(!layer_hint);
          circ->package_window += CIRCWINDOW_INCREMENT;
353
          log_fn(LOG_DEBUG,"circ-level sendme at exit, packagewindow %d.", circ->package_window);
354
355
          circuit_resume_edge_reading(circ, EDGE_EXIT, layer_hint);
        }
356
357
        return 0;
      }
358
      conn->package_window += STREAMWINDOW_INCREMENT;
359
      log_fn(LOG_DEBUG,"stream-level sendme, packagewindow now %d.", conn->package_window);
360
      connection_start_reading(conn);
Nick Mathewson's avatar
Nick Mathewson committed
361
      connection_edge_package_raw_inbuf(conn); /* handle whatever might still be on the inbuf */
362
      return 0;
363
    default:
Roger Dingledine's avatar
Roger Dingledine committed
364
      log_fn(LOG_WARN,"unknown relay command %d.",relay_command);
365
      return -1;
366
  }
367
368
  assert(0);
  return -1;
369
370
371
}

int connection_edge_finished_flushing(connection_t *conn) {
372
  unsigned char connected_payload[4];
373
374
375
376
377
378
379
  int e, len=sizeof(e);

  assert(conn);
  assert(conn->type == CONN_TYPE_AP || conn->type == CONN_TYPE_EXIT);

  switch(conn->state) {
    case EXIT_CONN_STATE_CONNECTING:
380
      if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0)  { /* not yet */
381
        if(!ERRNO_CONN_EINPROGRESS(errno)) {
382
          /* yuck. kill it. */
383
          log_fn(LOG_DEBUG,"in-progress exit connect failed. Removing.");
384
385
          return -1;
        } else {
386
          log_fn(LOG_DEBUG,"in-progress exit connect still waiting.");
387
388
389
390
391
          return 0; /* no change, see if next time is better */
        }
      }
      /* the connect has finished. */

392
      log_fn(LOG_INFO,"Exit connection to %s:%u established.",
393
394
395
396
          conn->address,conn->port);

      conn->state = EXIT_CONN_STATE_OPEN;
      connection_watch_events(conn, POLLIN); /* stop writing, continue reading */
397
      if(connection_wants_to_flush(conn)) /* in case there are any queued relay cells */
398
        connection_start_writing(conn);
399
      /* deliver a 'connected' relay cell back through the circuit. */
400
      *(uint32_t*)connected_payload = htonl(conn->addr);
401
402
403
      if(connection_edge_send_command(conn, circuit_get_by_conn(conn),
         RELAY_COMMAND_CONNECTED, NULL, 0, conn->cpath_layer) < 0)
        return 0; /* circuit is closed, don't continue */
404
      return connection_process_inbuf(conn); /* in case the server has written anything */
405
406
407
    case AP_CONN_STATE_OPEN:
    case EXIT_CONN_STATE_OPEN:
      connection_stop_writing(conn);
408
      connection_edge_consider_sending_sendme(conn);
409
      return 0;
Roger Dingledine's avatar
Roger Dingledine committed
410
411
412
    case AP_CONN_STATE_SOCKS_WAIT:
      connection_stop_writing(conn);
      return 0;
413
    default:
414
      log_fn(LOG_WARN,"BUG: called in unexpected state: %d", conn->state);
415
      return -1;
416
417
418
419
  }
  return 0;
}

420
421
422
423
424
uint64_t stats_n_data_cells_packaged = 0;
uint64_t stats_n_data_bytes_packaged = 0;
uint64_t stats_n_data_cells_received = 0;
uint64_t stats_n_data_bytes_received = 0;

Nick Mathewson's avatar
Nick Mathewson committed
425
int connection_edge_package_raw_inbuf(connection_t *conn) {
426
427
  int amount_to_process, length;
  char payload[CELL_PAYLOAD_SIZE];
428
429
430
431
432
  circuit_t *circ;
 
  assert(conn);
  assert(!connection_speaks_cells(conn));
 
Nick Mathewson's avatar
Nick Mathewson committed
433
repeat_connection_edge_package_raw_inbuf:
434
435
436
 
  circ = circuit_get_by_conn(conn);
  if(!circ) {
437
    log_fn(LOG_INFO,"conn has no circuits! Closing.");
438
439
440
441
442
443
444
    return -1;
  }
 
  if(circuit_consider_stop_edge_reading(circ, conn->type, conn->cpath_layer))
    return 0;
 
  if(conn->package_window <= 0) {
Roger Dingledine's avatar
Roger Dingledine committed
445
    log_fn(LOG_WARN,"called with package_window %d. Tell Roger.", conn->package_window);
446
447
448
449
    connection_stop_reading(conn);
    return 0;
  }
 
450
  amount_to_process = buf_datalen(conn->inbuf);
451
452
453
454
455
 
  if(!amount_to_process)
    return 0;
 
  if(amount_to_process > CELL_PAYLOAD_SIZE - RELAY_HEADER_SIZE) {
456
    length = CELL_PAYLOAD_SIZE - RELAY_HEADER_SIZE;
457
  } else {
458
    length = amount_to_process;
459
  }
460
  stats_n_data_bytes_packaged += length;
461
  stats_n_data_cells_packaged += 1;
462
 
463
464
465
  connection_fetch_from_buf(payload, length, conn);

  log_fn(LOG_DEBUG,"(%d) Packaging %d bytes (%d waiting).",conn->s,length,
466
         (int)buf_datalen(conn->inbuf));
467
 
468
469
470
  if(connection_edge_send_command(conn, circ, RELAY_COMMAND_DATA,
                               payload, length, conn->cpath_layer) < 0)
    return 0; /* circuit is closed, don't continue */
471

472
473
474
  if(conn->type == CONN_TYPE_EXIT) {
    assert(circ->package_window > 0);
    circ->package_window--;
475
  } else { /* we're an AP */
476
477
478
479
480
481
482
483
484
485
486
487
488
489
    assert(conn->type == CONN_TYPE_AP);
    assert(conn->cpath_layer->package_window > 0);
    conn->cpath_layer->package_window--;
  }
 
  if(--conn->package_window <= 0) { /* is it 0 after decrement? */
    connection_stop_reading(conn);
    log_fn(LOG_DEBUG,"conn->package_window reached 0.");
    circuit_consider_stop_edge_reading(circ, conn->type, conn->cpath_layer);
    return 0; /* don't process the inbuf any more */
  }
  log_fn(LOG_DEBUG,"conn->package_window is now %d",conn->package_window);
 
  /* handle more if there's more, or return 0 if there isn't */
Nick Mathewson's avatar
Nick Mathewson committed
490
  goto repeat_connection_edge_package_raw_inbuf;
491
492
}

493
494
495
/* Tell any APs that are waiting for a new circuit that one is available */
void connection_ap_attach_pending(void)
{
496
497
498
499
500
501
502
503
504
  connection_t **carray;
  int n, i;

  get_connection_array(&carray, &n);

  for (i = 0; i < n; ++i) {
    if (carray[i]->type != CONN_TYPE_AP || 
        carray[i]->type != AP_CONN_STATE_CIRCUIT_WAIT)
      continue;
505
    if (connection_ap_handshake_attach_circuit(carray[i])<0) {
506
507
      if(!circuit_get_newest(carray[i], 0)) {
        /* if there are no acceptable clean or not-very-dirty circs on the way */
508
        circuit_launch_new();
509
      }
510
    }
511
512
513
  }
}

514
static void connection_edge_consider_sending_sendme(connection_t *conn) {
515
516
517
  circuit_t *circ;
 
  if(connection_outbuf_too_full(conn))
518
    return;
519
520
521
 
  circ = circuit_get_by_conn(conn);
  if(!circ) {
522
523
    /* this can legitimately happen if the destroy has already
     * arrived and torn down the circuit */
524
    log_fn(LOG_INFO,"No circuit associated with conn. Skipping.");
525
    return;
526
527
528
529
530
  }
 
  while(conn->deliver_window < STREAMWINDOW_START - STREAMWINDOW_INCREMENT) {
    log_fn(LOG_DEBUG,"Outbuf %d, Queueing stream sendme.", conn->outbuf_flushlen);
    conn->deliver_window += STREAMWINDOW_INCREMENT;
531
532
533
534
    if(connection_edge_send_command(conn, circ, RELAY_COMMAND_SENDME,
                                    NULL, 0, conn->cpath_layer) < 0) {
      log_fn(LOG_WARN,"connection_edge_send_command failed. Returning.");
      return; /* the circuit's closed, don't continue */
535
536
537
538
539
    }
  }
}

static int connection_ap_handshake_process_socks(connection_t *conn) {
540
  socks_request_t *socks;
Roger Dingledine's avatar
Roger Dingledine committed
541
  int sockshere;
542
543

  assert(conn);
544
545
546
547
  assert(conn->type == CONN_TYPE_AP);
  assert(conn->state == AP_CONN_STATE_SOCKS_WAIT);
  assert(conn->socks_request);
  socks = conn->socks_request;
548
549
550

  log_fn(LOG_DEBUG,"entered.");

551
  sockshere = fetch_from_buf_socks(conn->inbuf, socks);
Roger Dingledine's avatar
Roger Dingledine committed
552
  if(sockshere == -1 || sockshere == 0) {
553
    if(socks->replylen) { /* we should send reply back */
Roger Dingledine's avatar
Roger Dingledine committed
554
      log_fn(LOG_DEBUG,"reply is already set for us. Using it.");
555
      connection_ap_handshake_socks_reply(conn, socks->reply, socks->replylen, 0);
Roger Dingledine's avatar
Roger Dingledine committed
556
    } else if(sockshere == -1) { /* send normal reject */
Roger Dingledine's avatar
Roger Dingledine committed
557
      log_fn(LOG_WARN,"Fetching socks handshake failed. Closing.");
Roger Dingledine's avatar
Roger Dingledine committed
558
559
560
561
562
563
      connection_ap_handshake_socks_reply(conn, NULL, 0, 0);
    } else {
      log_fn(LOG_DEBUG,"socks handshake not all here yet.");
    }
    return sockshere;
  } /* else socks handshake is done, continue processing */
564

565
  conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
566
  if(connection_ap_handshake_attach_circuit(conn) < 0)
567
    circuit_launch_new(); /* Build another circuit to handle this stream */
568
569
570
571
  return 0;
}

/* Try to find a live circuit.  If we don't find one, tell 'conn' to
572
 * stop reading and return 0.  Otherwise, associate the CONN_TYPE_AP
573
 * connection 'conn' with a safe live circuit, start sending a
574
 * BEGIN cell down the circuit, and return 1.
575
576
577
 */
static int connection_ap_handshake_attach_circuit(connection_t *conn) {
  circuit_t *circ;
578

579
580
581
582
  assert(conn);
  assert(conn->type == CONN_TYPE_AP);
  assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
  assert(conn->socks_request);
583

584
  /* find the circuit that we should use, if there is one. */
585
  circ = circuit_get_newest(conn, 1);
586
587

  if(!circ) {
588
589
    log_fn(LOG_INFO,"No safe circuit ready for edge connection; delaying.");
    connection_stop_reading(conn); /* don't read until the connected cell arrives */
590
    return -1;
591
  }
592

Roger Dingledine's avatar
Roger Dingledine committed
593
  connection_start_reading(conn);
594

Roger Dingledine's avatar
Roger Dingledine committed
595
596
  if(!circ->timestamp_dirty)
    circ->timestamp_dirty = time(NULL);
597
598

  /* add it into the linked list of streams on this circuit */
599
  log_fn(LOG_DEBUG,"attaching new conn to circ. n_circ_id %d.", circ->n_circ_id);
600
  conn->next_stream = circ->p_streams;
601
  /* assert_connection_ok(conn, time(NULL)); */
602
603
604
605
606
607
  circ->p_streams = conn;

  assert(circ->cpath && circ->cpath->prev);
  assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
  conn->cpath_layer = circ->cpath->prev;

608
  connection_ap_handshake_send_begin(conn, circ);
609

610
  return 0;
611
612
}

613
/* deliver the destaddr:destport in a relay cell */
614
static void connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ)
615
{
616
617
  char payload[CELL_PAYLOAD_SIZE];
  int payload_len;
618
619
  struct in_addr in;
  const char *string_addr;
620

621
622
623
624
  assert(ap_conn->type == CONN_TYPE_AP);
  assert(ap_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
  assert(ap_conn->socks_request);

625
626
  crypto_pseudo_rand(STREAM_ID_SIZE, ap_conn->stream_id);
  /* FIXME check for collisions */
627

628
  in.s_addr = htonl(client_dns_lookup_entry(ap_conn->socks_request->address));
629
630
  string_addr = in.s_addr ? inet_ntoa(in) : NULL;

631
632
633
  memcpy(payload, ap_conn->stream_id, STREAM_ID_SIZE);
  payload_len = STREAM_ID_SIZE + 1 +
    snprintf(payload+STREAM_ID_SIZE,CELL_PAYLOAD_SIZE-RELAY_HEADER_SIZE-STREAM_ID_SIZE,
634
             "%s:%d", 
Roger Dingledine's avatar
Roger Dingledine committed
635
             string_addr ? string_addr : ap_conn->socks_request->address,
636
             ap_conn->socks_request->port);
637
638
639

  log_fn(LOG_DEBUG,"Sending relay cell to begin stream %d.",*(int *)ap_conn->stream_id);

640
641
  if(connection_edge_send_command(ap_conn, circ, RELAY_COMMAND_BEGIN,
                               payload, payload_len, ap_conn->cpath_layer) < 0)
642
    return; /* circuit is closed, don't continue */
643

644
645
646
  ap_conn->package_window = STREAMWINDOW_START;
  ap_conn->deliver_window = STREAMWINDOW_START;
  ap_conn->state = AP_CONN_STATE_OPEN;
647
648
649
650
651
652
  /* XXX Right now, we rely on the socks client not to send us any data
   * XXX until we've sent back a socks reply.  (If it does, we could wind
   * XXX up packaging that data and sending it to the exit, then later having
   * XXX the exit refuse us.)  
   * XXX Perhaps we should grow an AP_CONN_STATE_CONNECTING state.
   */
653
  log_fn(LOG_INFO,"Address/port sent, ap socket %d, n_circ_id %d",ap_conn->s,circ->n_circ_id);
654
  return;
655
656
}

Roger Dingledine's avatar
Roger Dingledine committed
657
658
659
static int connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
                                               int replylen, char success) {
  char buf[256];
660

Roger Dingledine's avatar
Roger Dingledine committed
661
662
  if(replylen) { /* we already have a reply in mind */
    connection_write_to_buf(reply, replylen, conn);
663
    return flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen); /* try to flush it */
Roger Dingledine's avatar
Roger Dingledine committed
664
  }
665
666
  assert(conn->socks_request);
  if(conn->socks_request->socks_version == 4) {
Roger Dingledine's avatar
Roger Dingledine committed
667
668
669
670
671
672
    memset(buf,0,SOCKS4_NETWORK_LEN);
#define SOCKS4_GRANTED          90
#define SOCKS4_REJECT           91
    buf[1] = (success ? SOCKS4_GRANTED : SOCKS4_REJECT);
    /* leave version, destport, destip zero */
    connection_write_to_buf(buf, SOCKS4_NETWORK_LEN, conn);
673
    return flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen); /* try to flush it */
Roger Dingledine's avatar
Roger Dingledine committed
674
  }
675
  if(conn->socks_request->socks_version == 5) {
Roger Dingledine's avatar
Roger Dingledine committed
676
677
678
679
680
681
    buf[0] = 5; /* version 5 */
#define SOCKS5_SUCCESS          0
#define SOCKS5_GENERIC_ERROR    1
    buf[1] = success ? SOCKS5_SUCCESS : SOCKS5_GENERIC_ERROR;
    buf[2] = 0;
    buf[3] = 1; /* ipv4 addr */
682
683
    memset(buf+4,0,6); /* Set external addr/port to 0.
                          The spec doesn't seem to say what to do here. -RD */
Roger Dingledine's avatar
Roger Dingledine committed
684
    connection_write_to_buf(buf,10,conn);
685
    return flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen); /* try to flush it */
Roger Dingledine's avatar
Roger Dingledine committed
686
  }
687
  return 0; /* if socks_version isn't 4 or 5, don't send anything */
688
689
}

690
static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ) {
691
692
693
  connection_t *n_stream;
  char *colon;

694
695
696
697
  /* XXX currently we don't send an end cell back if we drop the
   * begin because it's malformed.
   */

Roger Dingledine's avatar
Roger Dingledine committed
698
699
  if(!memchr(cell->payload+RELAY_HEADER_SIZE+STREAM_ID_SIZE,0,
             cell->length-RELAY_HEADER_SIZE-STREAM_ID_SIZE)) {
Roger Dingledine's avatar
Roger Dingledine committed
700
    log_fn(LOG_WARN,"relay begin cell has no \\0. Dropping.");
701
702
703
704
    return 0;
  }
  colon = strchr(cell->payload+RELAY_HEADER_SIZE+STREAM_ID_SIZE, ':');
  if(!colon) {
Roger Dingledine's avatar
Roger Dingledine committed
705
    log_fn(LOG_WARN,"relay begin cell has no colon. Dropping.");
706
707
708
709
710
    return 0;
  }
  *colon = 0;

  if(!atoi(colon+1)) { /* bad port */
Roger Dingledine's avatar
Roger Dingledine committed
711
    log_fn(LOG_WARN,"relay begin cell has invalid port. Dropping.");
712
713
714
715
716
717
718
    return 0;
  }

  log_fn(LOG_DEBUG,"Creating new exit connection.");
  n_stream = connection_new(CONN_TYPE_EXIT);

  memcpy(n_stream->stream_id, cell->payload + RELAY_HEADER_SIZE, STREAM_ID_SIZE);
719
  n_stream->address = tor_strdup(cell->payload + RELAY_HEADER_SIZE + STREAM_ID_SIZE);
720
721
  n_stream->port = atoi(colon+1);
  n_stream->state = EXIT_CONN_STATE_RESOLVING;
722
  /* leave n_stream->s at -1, because it's not yet valid */
723
724
725
  n_stream->package_window = STREAMWINDOW_START;
  n_stream->deliver_window = STREAMWINDOW_START;
  if(connection_add(n_stream) < 0) { /* no space, forget it */
Roger Dingledine's avatar
Roger Dingledine committed
726
    log_fn(LOG_WARN,"connection_add failed. Dropping.");
727
728
729
730
731
732
733
734
735
736
737
    connection_free(n_stream);
    return 0;
  }

  /* add it into the linked list of streams on this circuit */
  n_stream->next_stream = circ->n_streams;
  circ->n_streams = n_stream;

  /* send it off to the gethostbyname farm */
  switch(dns_resolve(n_stream)) {
    case 1: /* resolve worked */
738
739
      connection_exit_connect(n_stream);
      return 0;
740
    case -1: /* resolve failed */
741
      log_fn(LOG_INFO,"Resolve failed (%s).", n_stream->address);
742
743
      connection_edge_end(n_stream, END_STREAM_REASON_RESOLVEFAILED, NULL);
    /* case 0, resolve added to pending list */
744
745
746
747
  }
  return 0;
}

748
void connection_exit_connect(connection_t *conn) {
749
  unsigned char connected_payload[4];
750
751
752

  if(router_compare_to_exit_policy(conn) < 0) {
    log_fn(LOG_INFO,"%s:%d failed exit policy. Closing.", conn->address, conn->port);
753
754
    connection_edge_end(conn, END_STREAM_REASON_EXITPOLICY, NULL);
    return;
755
756
  }

757
758
  switch(connection_connect(conn, conn->address, conn->addr, conn->port)) {
    case -1:
759
760
      connection_edge_end(conn, END_STREAM_REASON_CONNECTFAILED, NULL);
      return;
761
    case 0:
762
763
764
765
766
767
      connection_set_poll_socket(conn);
      conn->state = EXIT_CONN_STATE_CONNECTING;

      connection_watch_events(conn, POLLOUT | POLLIN | POLLERR);
      /* writable indicates finish, readable indicates broken link,
         error indicates broken link in windowsland. */
768
      return;
769
    /* case 1: fall through */
770
771
772
773
774
  }

  connection_set_poll_socket(conn);
  conn->state = EXIT_CONN_STATE_OPEN;
  if(connection_wants_to_flush(conn)) { /* in case there are any queued data cells */
Roger Dingledine's avatar
Roger Dingledine committed
775
    log_fn(LOG_WARN,"tell roger: newly connected conn had data waiting!");
776
777
778
779
780
781
//    connection_start_writing(conn);
  }
//   connection_process_inbuf(conn);
  connection_watch_events(conn, POLLIN);

  /* also, deliver a 'connected' cell back through the circuit. */
782
  *((uint32_t*) connected_payload) = htonl(conn->addr);
783
  connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_CONNECTED,
784
785
786
                               connected_payload, 4, conn->cpath_layer);
}

787
788
789
int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit)
{
  uint32_t addr;
Roger Dingledine's avatar
Roger Dingledine committed
790

791
792
793
794
  assert(conn);
  assert(conn->type == CONN_TYPE_AP);
  assert(conn->socks_request);

Roger Dingledine's avatar
Roger Dingledine committed
795
  addr = client_dns_lookup_entry(conn->socks_request->address);
796
797
798
  return router_supports_exit_address(addr, conn->port, exit);
}

799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
/* ***** Client DNS code ***** */
#define MAX_DNS_ENTRY_AGE 30*60

/* XXX Perhaps this should get merged with the dns.c code somehow. */
struct client_dns_entry {
  SPLAY_ENTRY(client_dns_entry) node;
  char *address;
  uint32_t addr;
  time_t expires;
};      
static int client_dns_size = 0;
static SPLAY_HEAD(client_dns_tree, client_dns_entry) client_dns_root;

static int compare_client_dns_entries(struct client_dns_entry *a,
                                      struct client_dns_entry *b) 
{
  return strcasecmp(a->address, b->address);
}

static void client_dns_entry_free(struct client_dns_entry *ent)
{
  tor_free(ent->address);
  tor_free(ent);
}

SPLAY_PROTOTYPE(client_dns_tree, client_dns_entry, node, compare_client_dns_entries);
SPLAY_GENERATE(client_dns_tree, client_dns_entry, node, compare_client_dns_entries);

void client_dns_init(void) {
  SPLAY_INIT(&client_dns_root);
  client_dns_size = 0;
}

static uint32_t client_dns_lookup_entry(const char *address)
{
  struct client_dns_entry *ent;
  struct client_dns_entry search;
  struct in_addr in;
  time_t now;

  assert(address);

  if (inet_aton(address, &in)) {
842
843
844
    log_fn(LOG_DEBUG, "Using static address %s (%08X)", address, 
           ntohl(in.s_addr));
    return ntohl(in.s_addr);
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
  }
  search.address = (char*)address;
  ent = SPLAY_FIND(client_dns_tree, &client_dns_root, &search);
  if (!ent) {
    log_fn(LOG_DEBUG, "No entry found for address %s", address);
    return 0;
  } else {
    now = time(NULL);
    if (ent->expires < now) {
      log_fn(LOG_DEBUG, "Expired entry found for address %s", address);
      SPLAY_REMOVE(client_dns_tree, &client_dns_root, ent);
      client_dns_entry_free(ent);
      --client_dns_size;
      return 0;
    }
860
    in.s_addr = htonl(ent->addr);
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
    log_fn(LOG_DEBUG, "Found cached entry for address %s: %s", address,
           inet_ntoa(in));
    return ent->addr;
  }
}
static void client_dns_set_entry(const char *address, uint32_t val)
{
  struct client_dns_entry *ent;
  struct client_dns_entry search;
  struct in_addr in;
  time_t now;

  assert(address);
  assert(val);

  if (inet_aton(address, &in)) {
877
    if (ntohl(in.s_addr) == val)
878
      return;
879
    in.s_addr = htonl(val);
880
881
882
883
884
885
886
887
888
    log_fn(LOG_WARN, 
        "Trying to store incompatible cached value %s for static address %s",
        inet_ntoa(in), address);
    return;
  }
  search.address = (char*) address;
  now = time(NULL);
  ent = SPLAY_FIND(client_dns_tree, &client_dns_root, &search);
  if (ent) {
889
890
891
    in.s_addr = htonl(val);
    log_fn(LOG_DEBUG, "Updating entry for address %s: %s", address,
           inet_ntoa(in));
892
893
894
    ent->addr = val;
    ent->expires = now+MAX_DNS_ENTRY_AGE;
  } else {
895
    in.s_addr = htonl(val);
896
897
898
899
900
901
902
903
904
905
    log_fn(LOG_DEBUG, "Caching result for address %s: %s", address,
           inet_ntoa(in));
    ent = tor_malloc(sizeof(struct client_dns_entry));
    ent->address = tor_strdup(address);
    ent->addr = val;
    ent->expires = now+MAX_DNS_ENTRY_AGE;
    SPLAY_INSERT(client_dns_tree, &client_dns_root, ent);
    ++client_dns_size;
  }
}
906

907
void client_dns_clean(void)
908
909
910
911
912
913
914
{
  struct client_dns_entry **expired_entries;
  int n_expired_entries = 0;
  struct client_dns_entry *ent;
  time_t now;
  int i;

915
916
  if(!client_dns_size)
    return;
917
  expired_entries = tor_malloc(client_dns_size * 
Roger Dingledine's avatar
Roger Dingledine committed
918
919
                               sizeof(struct client_dns_entry *));

920
921
922
923
924
925
926
927
928
929
930
  now = time(NULL);
  SPLAY_FOREACH(ent, client_dns_tree, &client_dns_root) {
    if (ent->expires < now) {
      expired_entries[n_expired_entries++] = ent;
    }
  }
  for (i = 0; i < n_expired_entries; ++i) {
    SPLAY_REMOVE(client_dns_tree, &client_dns_root, expired_entries[i]);
    client_dns_entry_free(expired_entries[i]);
  }
  tor_free(expired_entries);
931
932
}

933
934
935
936
937
938
939
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/