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

#include "or.h"

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

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

static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);

17
18
19
20
21
22
23
24
25
26
27
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 */
    if (conn->done_sending)
28
/*ENDCLOSE*/  conn->marked_for_close = 1;
29
30
31
32
33

    /* XXX Factor out common logic here and in circuit_about_to_close NM */
    circ = circuit_get_by_conn(conn);
    if (!circ)
      return -1;
34

35
    memset(&cell, 0, sizeof(cell_t));
36
37
38
39
    cell.command = CELL_RELAY;
    cell.length = RELAY_HEADER_SIZE;
    SET_CELL_RELAY_COMMAND(cell, RELAY_COMMAND_END);
    SET_CELL_STREAM_ID(cell, conn->stream_id);
40
    cell.aci = circ->n_aci;
41

42
    if (circuit_deliver_relay_cell(&cell, circ, CELL_DIRECTION(conn->type), conn->cpath_layer) < 0) {
43
      log_fn(LOG_WARNING,"(fd %d) circuit_deliver_relay_cell failed. Closing.", conn->s);
44
45
46
47
48
      circuit_close(circ);
    }
    return 0;
#else 
    /* eof reached, kill it. */
49
    log_fn(LOG_INFO,"conn (fd %d) reached eof. Closing.", conn->s);
50
/*ENDCLOSE*/ return -1;
51
52
53
54
55
#endif
  }

  switch(conn->state) {
    case AP_CONN_STATE_SOCKS_WAIT:
56
/*ENDCLOSE*/  return connection_ap_handshake_process_socks(conn);
57
58
59
    case AP_CONN_STATE_OPEN:
    case EXIT_CONN_STATE_OPEN:
      if(connection_package_raw_inbuf(conn) < 0)
60
/*ENDCLOSE*/  return -1;
61
62
      return 0;
    case EXIT_CONN_STATE_CONNECTING:
63
      log_fn(LOG_INFO,"text from server while in 'connecting' state at exit. Leaving it on buffer.");
64
65
66
67
68
69
      return 0;
  }

  return 0;
}

70
int connection_edge_send_command(connection_t *fromconn, circuit_t *circ, int relay_command) {
71
  cell_t cell;
72
  int cell_direction;
73
74

  if(!circ) {
75
    log_fn(LOG_WARNING,"no circ. Closing.");
76
77
78
79
    return -1;
  }

  memset(&cell, 0, sizeof(cell_t));
80
  if(fromconn && fromconn->type == CONN_TYPE_AP) {
81
    cell.aci = circ->n_aci;
82
83
84
    cell_direction = CELL_DIRECTION_OUT;
  } else {
    /* NOTE: if !fromconn, we assume that it's heading towards the OP */
85
    cell.aci = circ->p_aci;
86
87
88
    cell_direction = CELL_DIRECTION_IN;
  }

89
90
  cell.command = CELL_RELAY;
  SET_CELL_RELAY_COMMAND(cell, relay_command);
91
92
93
94
  if(fromconn)
    SET_CELL_STREAM_ID(cell, fromconn->stream_id);
  else
    SET_CELL_STREAM_ID(cell, ZERO_STREAM);
95

96
  cell.length = RELAY_HEADER_SIZE;
97
  log_fn(LOG_INFO,"delivering %d cell %s.", relay_command, cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward");
98

99
  if(circuit_deliver_relay_cell(&cell, circ, cell_direction, fromconn ? fromconn->cpath_layer : NULL) < 0) {
100
    log_fn(LOG_WARNING,"circuit_deliver_relay_cell failed. Closing.");
101
102
103
104
105
106
    circuit_close(circ);
    return 0;
  }
  return 0;
}

107
108
/* an incoming relay cell has arrived. return -1 if you want to tear down the
 * circuit, else 0. */
109
110
int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection_t *conn,
                                       int edge_type, crypt_path_t *layer_hint) {
111
  int relay_command;
112
113
114
115
  static int num_seen=0;

  assert(cell && circ);

116
  relay_command = CELL_RELAY_COMMAND(*cell);
117
//  log_fn(LOG_DEBUG,"command %d stream %d", relay_command, stream_id);
118
  num_seen++;
119
  log_fn(LOG_DEBUG,"Now seen %d relay cells here.", num_seen);
120

121
122
  /* either conn is NULL, in which case we've got a control cell, or else
   * conn points to the recognized stream. */
123
124

  if(conn && conn->state != AP_CONN_STATE_OPEN && conn->state != EXIT_CONN_STATE_OPEN) {
125
    if(conn->type == CONN_TYPE_EXIT && relay_command == RELAY_COMMAND_END) {
126
      log_fn(LOG_INFO,"Exit got end before we're connected. Marking for close.");
127
      conn->marked_for_close = 1;
128
129
130
131
      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);
      }
132
      return 0;
133
    } else {
134
135
      log_fn(LOG_WARNING,"Got an unexpected relay cell, not in 'open' state. Closing.");
      return -1;
136
137
138
    }
  }

139
140
  switch(relay_command) {
    case RELAY_COMMAND_BEGIN:
141
      if(edge_type == EDGE_AP) {
142
        log_fn(LOG_WARNING,"relay begin request unsupported at AP. Dropping.");
143
144
        return 0;
      }
145
      if(conn) {
146
        log_fn(LOG_WARNING,"begin cell for known stream. Dropping.");
147
148
149
        return 0;
      }
      return connection_exit_begin_conn(cell, circ);
150
    case RELAY_COMMAND_DATA:
151
      ++stats_n_data_cells_received;
152
153
      if((edge_type == EDGE_AP && --layer_hint->deliver_window < 0) ||
         (edge_type == EDGE_EXIT && --circ->deliver_window < 0)) {
154
        log_fn(LOG_WARNING,"(relay data) circ deliver_window below 0. Killing.");
155
        return -1;
156
      }
157
      log_fn(LOG_DEBUG,"circ deliver_window now %d.", edge_type == EDGE_AP ? layer_hint->deliver_window : circ->deliver_window);
158
159
160
161

      if(circuit_consider_sending_sendme(circ, edge_type, layer_hint) < 0)
        return -1;

162
      if(!conn) {
163
        log_fn(LOG_INFO,"relay cell dropped, unknown stream %d.",*(int*)conn->stream_id);
164
165
        return 0;
      }
166
167

      if(--conn->deliver_window < 0) { /* is it below 0 after decrement? */
168
        log_fn(LOG_WARNING,"(relay data) conn deliver_window below 0. Killing.");
169
170
171
        return -1; /* somebody's breaking protocol. kill the whole circuit. */
      }

172
//      printf("New text for buf (%d bytes): '%s'", cell->length - RELAY_HEADER_SIZE, cell->payload + RELAY_HEADER_SIZE);
173
      stats_n_data_bytes_received += (cell->length - RELAY_HEADER_SIZE);
174
175
176
      connection_write_to_buf(cell->payload + RELAY_HEADER_SIZE,
                             cell->length - RELAY_HEADER_SIZE, conn);
      connection_consider_sending_sendme(conn, edge_type);
177
      return 0;
178
    case RELAY_COMMAND_END:
179
      if(!conn) {
180
        log_fn(LOG_INFO,"end cell dropped, unknown stream %d.",*(int*)conn->stream_id);
181
182
        return 0;
      }
183
      log_fn(LOG_INFO,"end cell for stream %d. Removing stream.",*(int*)conn->stream_id);
184
185
186
187
188

#ifdef HALF_OPEN
      conn->done_sending = 1;
      shutdown(conn->s, 1); /* XXX check return; refactor NM */
      if (conn->done_receiving)
189
/*ENDCLOSE*/  conn->marked_for_close = 1;
190
#endif
191
/*ENDCLOSE*/  conn->marked_for_close = 1;
192
      break;
193
194
    case RELAY_COMMAND_EXTEND:
      if(conn) {
195
        log_fn(LOG_WARNING,"'extend' for non-zero stream. Dropping.");
196
197
198
199
200
        return 0;
      }
      return circuit_extend(cell, circ);
    case RELAY_COMMAND_EXTENDED:
      if(edge_type == EDGE_EXIT) {
201
        log_fn(LOG_WARNING,"'extended' unsupported at exit. Dropping.");
202
203
        return 0;
      }
204
      log_fn(LOG_DEBUG,"Got an extended cell! Yay.");
205
      if(circuit_finish_handshake(circ, cell->payload+RELAY_HEADER_SIZE) < 0) {
206
        log_fn(LOG_WARNING,"circuit_finish_handshake failed.");
207
208
209
        return -1;
      }
      return circuit_send_next_onion_skin(circ);
210
211
    case RELAY_COMMAND_TRUNCATE:
      if(edge_type == EDGE_AP) {
212
        log_fn(LOG_WARNING,"'truncate' unsupported at AP. Dropping.");
213
214
215
216
217
218
        return 0;
      }
      if(circ->n_conn) {
        connection_send_destroy(circ->n_aci, circ->n_conn);
        circ->n_conn = NULL;
      }
219
      log_fn(LOG_DEBUG, "Processed 'truncate', replying.");
220
221
222
      return connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED);
    case RELAY_COMMAND_TRUNCATED:
      if(edge_type == EDGE_EXIT) {
223
        log_fn(LOG_WARNING,"'truncated' unsupported at exit. Dropping.");
224
225
226
        return 0;
      }
      return circuit_truncated(circ, layer_hint);
227
    case RELAY_COMMAND_CONNECTED:
228
      if(edge_type == EDGE_EXIT) {
229
        log_fn(LOG_WARNING,"'connected' unsupported at exit. Dropping.");
230
231
232
        return 0;
      }
      if(!conn) {
233
        log_fn(LOG_INFO,"connected cell dropped, unknown stream %d.",*(int*)conn->stream_id);
234
235
        break;
      }
236
      log_fn(LOG_INFO,"Connected! Notifying application.");
Roger Dingledine's avatar
Roger Dingledine committed
237
      if(connection_ap_handshake_socks_reply(conn, NULL, 0, 1) < 0) {
238
/*ENDCLOSE*/    conn->marked_for_close = 1;
239
240
      }
      break;
241
    case RELAY_COMMAND_SENDME:
242
      if(!conn) {
243
244
245
        if(edge_type == EDGE_AP) {
          assert(layer_hint);
          layer_hint->package_window += CIRCWINDOW_INCREMENT;
246
          log_fn(LOG_DEBUG,"circ-level sendme at AP, packagewindow %d.", layer_hint->package_window);
247
248
249
250
          circuit_resume_edge_reading(circ, EDGE_AP, layer_hint);
        } else {
          assert(!layer_hint);
          circ->package_window += CIRCWINDOW_INCREMENT;
251
          log_fn(LOG_DEBUG,"circ-level sendme at exit, packagewindow %d.", circ->package_window);
252
253
          circuit_resume_edge_reading(circ, EDGE_EXIT, layer_hint);
        }
254
255
        return 0;
      }
256
      conn->package_window += STREAMWINDOW_INCREMENT;
257
      log_fn(LOG_DEBUG,"stream-level sendme, packagewindow now %d.", conn->package_window);
258
      connection_start_reading(conn);
259
      connection_package_raw_inbuf(conn); /* handle whatever might still be on the inbuf */
260
261
      break;
    default:
262
263
      log_fn(LOG_WARNING,"unknown relay command %d.",relay_command);
      return -1;
264
265
266
267
268
269
270
271
272
273
274
275
  }
  return 0;
}

int connection_edge_finished_flushing(connection_t *conn) {
  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:
276
      if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0)  { /* not yet */
277
        if(!ERRNO_CONN_EINPROGRESS(errno)) {
278
          /* yuck. kill it. */
279
          log_fn(LOG_DEBUG,"in-progress exit connect failed. Removing.");
280
281
          return -1;
        } else {
282
          log_fn(LOG_DEBUG,"in-progress exit connect still waiting.");
283
284
285
286
287
          return 0; /* no change, see if next time is better */
        }
      }
      /* the connect has finished. */

288
      log_fn(LOG_INFO,"Exit connection to %s:%u established.",
289
290
291
292
          conn->address,conn->port);

      conn->state = EXIT_CONN_STATE_OPEN;
      connection_watch_events(conn, POLLIN); /* stop writing, continue reading */
293
      if(connection_wants_to_flush(conn)) /* in case there are any queued relay cells */
294
295
        connection_start_writing(conn);
      return
296
        connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_CONNECTED) || /* deliver a 'connected' relay cell back through the circuit. */
297
298
299
300
        connection_process_inbuf(conn); /* in case the server has written anything */
    case AP_CONN_STATE_OPEN:
    case EXIT_CONN_STATE_OPEN:
      connection_stop_writing(conn);
301
302
      connection_consider_sending_sendme(conn, conn->type);
      return 0;
Roger Dingledine's avatar
Roger Dingledine committed
303
304
305
    case AP_CONN_STATE_SOCKS_WAIT:
      connection_stop_writing(conn);
      return 0;
306
    default:
307
308
      log_fn(LOG_WARNING,"BUG: called in unexpected state.");
      return -1;
309
310
311
312
  }
  return 0;
}

313
314
315
316
317
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;

318
319
320
321
322
323
324
325
326
327
328
329
int connection_package_raw_inbuf(connection_t *conn) {
  int amount_to_process;
  cell_t cell;
  circuit_t *circ;
 
  assert(conn);
  assert(!connection_speaks_cells(conn));
 
repeat_connection_package_raw_inbuf:
 
  circ = circuit_get_by_conn(conn);
  if(!circ) {
330
    log_fn(LOG_INFO,"conn has no circuits! Closing.");
331
332
333
334
335
336
337
    return -1;
  }
 
  if(circuit_consider_stop_edge_reading(circ, conn->type, conn->cpath_layer))
    return 0;
 
  if(conn->package_window <= 0) {
338
    log_fn(LOG_WARNING,"called with package_window %d. Tell Roger.", conn->package_window);
339
340
341
342
    connection_stop_reading(conn);
    return 0;
  }
 
343
  amount_to_process = buf_datalen(conn->inbuf);
344
345
346
347
348
349
350
351
352
353
354
355
 
  if(!amount_to_process)
    return 0;
 
  /* Initialize the cell with 0's */
  memset(&cell, 0, sizeof(cell_t));
 
  if(amount_to_process > CELL_PAYLOAD_SIZE - RELAY_HEADER_SIZE) {
    cell.length = CELL_PAYLOAD_SIZE - RELAY_HEADER_SIZE;
  } else {
    cell.length = amount_to_process;
  }
356
357
  stats_n_data_bytes_packaged += cell.length;
  stats_n_data_cells_packaged += 1;
358
359
360
 
  connection_fetch_from_buf(cell.payload+RELAY_HEADER_SIZE, cell.length, conn);
 
361
362
  log_fn(LOG_DEBUG,"(%d) Packaging %d bytes (%d waiting).",conn->s,cell.length,
         (int)buf_datalen(conn->inbuf));
363
364
365
366
367
368
369
370
371
 
  cell.command = CELL_RELAY;
  SET_CELL_RELAY_COMMAND(cell, RELAY_COMMAND_DATA);
  SET_CELL_STREAM_ID(cell, conn->stream_id);
  cell.length += RELAY_HEADER_SIZE;
 
  if(conn->type == CONN_TYPE_EXIT) {
    cell.aci = circ->p_aci;
    if(circuit_deliver_relay_cell(&cell, circ, CELL_DIRECTION_IN, NULL) < 0) {
372
      log_fn(LOG_WARNING,"circuit_deliver_relay_cell (backward) failed. Closing.");
373
374
375
376
377
378
379
380
381
      circuit_close(circ);
      return 0;
    }
    assert(circ->package_window > 0);
    circ->package_window--;
  } else { /* send it forward. we're an AP */
    assert(conn->type == CONN_TYPE_AP);
    cell.aci = circ->n_aci;
    if(circuit_deliver_relay_cell(&cell, circ, CELL_DIRECTION_OUT, conn->cpath_layer) < 0) {
382
      log_fn(LOG_WARNING,"circuit_deliver_relay_cell (forward) failed. Closing.");
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
      circuit_close(circ);
      return 0;
    }
    assert(conn->cpath_layer->package_window > 0);
    conn->cpath_layer->package_window--;
  }
 
  assert(conn->package_window > 0);
  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 */
  goto repeat_connection_package_raw_inbuf;
}

403
void connection_consider_sending_sendme(connection_t *conn, int edge_type) {
404
405
406
407
  circuit_t *circ;
  cell_t cell;
 
  if(connection_outbuf_too_full(conn))
408
    return;
409
410
411
412
 
  circ = circuit_get_by_conn(conn);
  if(!circ) {
    /* this can legitimately happen if the destroy has already arrived and torn down the circuit */
413
    log_fn(LOG_INFO,"No circuit associated with conn. Skipping.");
414
    return;
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
  }
 
  memset(&cell, 0, sizeof(cell_t));
  cell.command = CELL_RELAY;
  SET_CELL_RELAY_COMMAND(cell, RELAY_COMMAND_SENDME);
  SET_CELL_STREAM_ID(cell, conn->stream_id);
  cell.length += RELAY_HEADER_SIZE;
 
  if(edge_type == EDGE_EXIT)
    cell.aci = circ->p_aci;
  else
    cell.aci = circ->n_aci;
 
  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;
    if(circuit_deliver_relay_cell(&cell, circ, CELL_DIRECTION(edge_type), conn->cpath_layer) < 0) {
432
      log_fn(LOG_WARNING,"circuit_deliver_relay_cell failed. Closing.");
433
      circuit_close(circ);
434
      return;
435
436
437
438
439
440
    }
  }
}

static int connection_ap_handshake_process_socks(connection_t *conn) {
  circuit_t *circ;
441
  char destaddr[200]; /* XXX why 200? but not 256, because it won't fit in a cell */
Roger Dingledine's avatar
Roger Dingledine committed
442
  char reply[256];
443
  uint16_t destport;
Roger Dingledine's avatar
Roger Dingledine committed
444
445
  int replylen=0;
  int sockshere;
446
447
448
449
450

  assert(conn);

  log_fn(LOG_DEBUG,"entered.");

Roger Dingledine's avatar
Roger Dingledine committed
451
452
453
454
455
456
457
  sockshere = fetch_from_buf_socks(conn->inbuf, &conn->socks_version, reply, &replylen,
                                   destaddr, sizeof(destaddr), &destport);
  if(sockshere == -1 || sockshere == 0) {
    if(replylen) { /* we should send reply back */
      log_fn(LOG_DEBUG,"reply is already set for us. Using it.");
      connection_ap_handshake_socks_reply(conn, reply, replylen, 0);
    } else if(sockshere == -1) { /* send normal reject */
458
      log_fn(LOG_WARNING,"Fetching socks handshake failed. Closing.");
Roger Dingledine's avatar
Roger Dingledine committed
459
460
461
462
463
464
      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 */
465
466

  /* find the circuit that we should use, if there is one. */
Roger Dingledine's avatar
Roger Dingledine committed
467
  circ = circuit_get_newest_open();
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484

  if(!circ) {
    log_fn(LOG_INFO,"No circuit ready. Closing.");
    return -1;
  }

  circ->dirty = 1;

  /* add it into the linked list of streams on this circuit */
  log_fn(LOG_DEBUG,"attaching new conn to circ. n_aci %d.", circ->n_aci);
  conn->next_stream = circ->p_streams;
  circ->p_streams = conn;

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

485
  if(connection_ap_handshake_send_begin(conn, circ, destaddr, destport) < 0) {
486
487
488
489
490
491
492
    circuit_close(circ);
    return -1;
  }

  return 0;
}

493
494
495
/* deliver the destaddr:destport in a relay cell */
static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ,
                                              char *destaddr, uint16_t destport) {
496
497
  cell_t cell;
  memset(&cell, 0, sizeof(cell_t));
498

499
500
501
502
503
504
505
506
507
508
509
  cell.command = CELL_RELAY;
  cell.aci = circ->n_aci;
  SET_CELL_RELAY_COMMAND(cell, RELAY_COMMAND_BEGIN);
  if(crypto_pseudo_rand(STREAM_ID_SIZE, ap_conn->stream_id) < 0)
    return -1;
  /* FIXME check for collisions */
  SET_CELL_STREAM_ID(cell, ZERO_STREAM);

  memcpy(cell.payload+RELAY_HEADER_SIZE, ap_conn->stream_id, STREAM_ID_SIZE);
  cell.length = 
    snprintf(cell.payload+RELAY_HEADER_SIZE+STREAM_ID_SIZE, CELL_PAYLOAD_SIZE-RELAY_HEADER_SIZE-STREAM_ID_SIZE,
510
             "%s:%d", destaddr, destport) + 
511
512
513
    1 + STREAM_ID_SIZE + RELAY_HEADER_SIZE;
  log_fn(LOG_DEBUG,"Sending relay cell (id %d) to begin stream %d.", *(int *)(cell.payload+1),*(int *)ap_conn->stream_id);
  if(circuit_deliver_relay_cell(&cell, circ, CELL_DIRECTION_OUT, ap_conn->cpath_layer) < 0) {
514
    log_fn(LOG_WARNING,"failed to deliver begin cell. Closing.");
515
516
517
518
519
520
521
522
523
    return -1;
  }
  ap_conn->package_window = STREAMWINDOW_START;
  ap_conn->deliver_window = STREAMWINDOW_START;
  ap_conn->state = AP_CONN_STATE_OPEN;
  log_fn(LOG_INFO,"Address/port sent, ap socket %d, n_aci %d",ap_conn->s,circ->n_aci);
  return 0;
}

Roger Dingledine's avatar
Roger Dingledine committed
524
525
526
static int connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
                                               int replylen, char success) {
  char buf[256];
527

Roger Dingledine's avatar
Roger Dingledine committed
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
  if(replylen) { /* we already have a reply in mind */
    connection_write_to_buf(reply, replylen, conn);
    return connection_flush_buf(conn); /* try to flush it */
  }
  if(conn->socks_version == 4) {
    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);
    return connection_flush_buf(conn); /* try to flush it */
  }
  if(conn->socks_version == 5) {
    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 */
    memset(buf+4,0,6); /* XXX set external addr/port to 0, see what breaks */
    connection_write_to_buf(buf,10,conn);
    return connection_flush_buf(conn); /* try to flush it */
  }
  assert(0);
553
554
}

555
/*ENDCLOSE*/ static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ) {
556
557
558
  connection_t *n_stream;
  char *colon;

Roger Dingledine's avatar
Roger Dingledine committed
559
560
  if(!memchr(cell->payload+RELAY_HEADER_SIZE+STREAM_ID_SIZE,0,
             cell->length-RELAY_HEADER_SIZE-STREAM_ID_SIZE)) {
561
562
563
564
565
566
567
568
569
570
571
    log_fn(LOG_WARNING,"relay begin cell has no \\0. Dropping.");
    return 0;
  }
  colon = strchr(cell->payload+RELAY_HEADER_SIZE+STREAM_ID_SIZE, ':');
  if(!colon) {
    log_fn(LOG_WARNING,"relay begin cell has no colon. Dropping.");
    return 0;
  }
  *colon = 0;

  if(!atoi(colon+1)) { /* bad port */
572
    log_fn(LOG_WARNING,"relay begin cell has invalid port. Dropping.");
573
574
575
576
577
578
579
    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);
580
  n_stream->address = tor_strdup(cell->payload + RELAY_HEADER_SIZE + STREAM_ID_SIZE);
581
582
583
584
585
586
  n_stream->port = atoi(colon+1);
  n_stream->state = EXIT_CONN_STATE_RESOLVING;
  n_stream->s = -1; /* not yet valid */
  n_stream->package_window = STREAMWINDOW_START;
  n_stream->deliver_window = STREAMWINDOW_START;
  if(connection_add(n_stream) < 0) { /* no space, forget it */
587
    log_fn(LOG_WARNING,"connection_add failed. Dropping.");
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
    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 */
      if(connection_exit_connect(n_stream) >= 0)
        return 0;
      /* else fall through */
    case -1: /* resolve failed */
603
      log_fn(LOG_WARNING,"Couldn't queue resolve request.");
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
      connection_remove(n_stream);
      connection_free(n_stream);
    case 0: /* resolve added to pending list */
      ;
  }
  return 0;
}

int connection_exit_connect(connection_t *conn) {

  if(router_compare_to_exit_policy(conn) < 0) {
    log_fn(LOG_INFO,"%s:%d failed exit policy. Closing.", conn->address, conn->port);
    return -1;
  }

619
620
  switch(connection_connect(conn, conn->address, conn->addr, conn->port)) {
    case -1:
621
      return -1;
622
    case 0:
623
624
625
626
627
628
629
      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. */
      return 0;
630
    /* case 1: fall through */
631
632
633
634
635
  }

  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 */
636
    log_fn(LOG_WARNING,"tell roger: newly connected conn had data waiting!");
637
638
639
640
641
642
643
644
645
//    connection_start_writing(conn);
  }
//   connection_process_inbuf(conn);
  connection_watch_events(conn, POLLIN);

  /* also, deliver a 'connected' cell back through the circuit. */
  return connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_CONNECTED);
}

646
647
648
649
650
651
652
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/
653