connection_edge.c 31.7 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
22
static uint32_t client_dns_lookup_entry(const char *address);
static void client_dns_set_entry(const char *address, uint32_t val);
static void client_dns_clean(void);

23
24
25
26
27
28
29
30
31
32
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 */
33
    if (conn->done_sending) {
34
      connection_edge_end(conn, END_STREAM_REASON_DONE, conn->cpath_layer);
35
36
37
38
    } else {
      connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_END,
                                   NULL, 0, conn->cpath_layer);
    }
39
40
41
    return 0;
#else 
    /* eof reached, kill it. */
42
    log_fn(LOG_INFO,"conn (fd %d) reached eof. Closing.", conn->s);
43
    connection_edge_end(conn, END_STREAM_REASON_DONE, conn->cpath_layer);
44
    return -1;
45
46
47
48
49
#endif
  }

  switch(conn->state) {
    case AP_CONN_STATE_SOCKS_WAIT:
50
      if(connection_ap_handshake_process_socks(conn) < 0) {
51
        connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
52
53
54
        return -1;
      }
      return 0;
55
56
    case AP_CONN_STATE_OPEN:
    case EXIT_CONN_STATE_OPEN:
57
      if(connection_edge_package_raw_inbuf(conn) < 0) {
58
        connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
59
60
        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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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;
95
96
97
98
99
100

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

101
102
103
104
105
106
107
  payload[0] = reason;
  if(reason == END_STREAM_REASON_EXITPOLICY) {
    *(uint32_t *)(payload+1) = htonl(conn->addr);
    payload_len += 6;
  }

  circ = circuit_get_by_conn(conn);
108
109
110
111
112
113
114
115
116
117
  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;
}

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

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

129
130
131
  if(!fromconn || relay_command == RELAY_COMMAND_BEGIN) /* XXX more */
    is_control_cell = 1;

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

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

149
150
151
152
  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
153
  log_fn(LOG_DEBUG,"delivering %d cell %s.", relay_command,
154
         cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward");
155

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

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

  assert(cell && circ);

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

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

  if(conn && conn->state != AP_CONN_STATE_OPEN && conn->state != EXIT_CONN_STATE_OPEN) {
183
    if(conn->type == CONN_TYPE_EXIT && relay_command == RELAY_COMMAND_END) {
184
185
      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));
186
187
188
189
      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);
      }
190
191
      conn->marked_for_close = 1;
      conn->has_sent_end = 1;
192
      return 0;
193
    } else {
Roger Dingledine's avatar
Roger Dingledine committed
194
      log_fn(LOG_WARN,"Got an unexpected relay cell, not in 'open' state. Closing.");
195
      connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
196
      return -1;
197
198
199
    }
  }

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

221
222
      if(circuit_consider_sending_sendme(circ, edge_type, layer_hint) < 0) {
        conn->has_sent_end = 1; /* we failed because conn is broken. can't send end. */
223
        return -1;
224
      }
225

226
      if(!conn) {
227
        log_fn(LOG_INFO,"relay cell dropped, unknown stream %d.",*(int*)conn->stream_id);
228
229
        return 0;
      }
230
231

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

236
//      printf("New text for buf (%d bytes): '%s'", cell->length - RELAY_HEADER_SIZE, cell->payload + RELAY_HEADER_SIZE);
237
      stats_n_data_bytes_received += (cell->length - RELAY_HEADER_SIZE);
238
239
      connection_write_to_buf(cell->payload + RELAY_HEADER_SIZE,
                             cell->length - RELAY_HEADER_SIZE, conn);
240
      connection_edge_consider_sending_sendme(conn);
241
      return 0;
242
    case RELAY_COMMAND_END:
243
      if(!conn) {
244
245
246
        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);
247
248
        return 0;
      }
249
250
      if(cell->length-RELAY_HEADER_SIZE >= 5 && 
         *(cell->payload+RELAY_HEADER_SIZE) == END_STREAM_REASON_EXITPOLICY) {
251
252
253
254
255
256
        /* No need to close the connection. We'll hold it open while
         * we try a new exit node.
         * cell->payload+RELAY_HEADER_SIZE+1 holds the addr and then
         * port of the destination. Which is good, because we've
         * forgotten it.
         */
257
        addr = ntohl(*cell->payload+RELAY_HEADER_SIZE+1);
258
259
260
261
        client_dns_set_entry(conn->socks_request->addr, addr);
        conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
        /* XXX Build another circuit as required */
        return 0;
262
      }
263
264
265
      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);
266
267
268
269

#ifdef HALF_OPEN
      conn->done_sending = 1;
      shutdown(conn->s, 1); /* XXX check return; refactor NM */
270
271
272
273
274
275
276
      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! */
277
278
#endif
      break;
279
280
    case RELAY_COMMAND_EXTEND:
      if(conn) {
Roger Dingledine's avatar
Roger Dingledine committed
281
        log_fn(LOG_WARN,"'extend' for non-zero stream. Dropping.");
282
283
284
285
286
        return 0;
      }
      return circuit_extend(cell, circ);
    case RELAY_COMMAND_EXTENDED:
      if(edge_type == EDGE_EXIT) {
Roger Dingledine's avatar
Roger Dingledine committed
287
        log_fn(LOG_WARN,"'extended' unsupported at exit. Dropping.");
288
289
        return 0;
      }
290
      log_fn(LOG_DEBUG,"Got an extended cell! Yay.");
291
      if(circuit_finish_handshake(circ, cell->payload+RELAY_HEADER_SIZE) < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
292
        log_fn(LOG_WARN,"circuit_finish_handshake failed.");
293
294
295
        return -1;
      }
      return circuit_send_next_onion_skin(circ);
296
297
    case RELAY_COMMAND_TRUNCATE:
      if(edge_type == EDGE_AP) {
Roger Dingledine's avatar
Roger Dingledine committed
298
        log_fn(LOG_WARN,"'truncate' unsupported at AP. Dropping.");
299
300
301
        return 0;
      }
      if(circ->n_conn) {
302
        connection_send_destroy(circ->n_circ_id, circ->n_conn);
303
304
        circ->n_conn = NULL;
      }
305
      log_fn(LOG_DEBUG, "Processed 'truncate', replying.");
306
307
308
      connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED,
                                   NULL, 0, NULL);
      return 0;
309
310
    case RELAY_COMMAND_TRUNCATED:
      if(edge_type == EDGE_EXIT) {
Roger Dingledine's avatar
Roger Dingledine committed
311
        log_fn(LOG_WARN,"'truncated' unsupported at exit. Dropping.");
312
313
314
        return 0;
      }
      return circuit_truncated(circ, layer_hint);
315
    case RELAY_COMMAND_CONNECTED:
316
      if(edge_type == EDGE_EXIT) {
Roger Dingledine's avatar
Roger Dingledine committed
317
        log_fn(LOG_WARN,"'connected' unsupported at exit. Dropping.");
318
319
320
        return 0;
      }
      if(!conn) {
321
        log_fn(LOG_INFO,"connected cell dropped, unknown stream %d.",*(int*)conn->stream_id);
322
323
        break;
      }
324
      log_fn(LOG_INFO,"Connected! Notifying application.");
325
326
327
328
      if (cell->length-RELAY_HEADER_SIZE == 4) {
        addr = htonl(*(uint32_t*)(cell->payload + RELAY_HEADER_SIZE));
        client_dns_set_entry(conn->socks_request->addr, addr);
      }
Roger Dingledine's avatar
Roger Dingledine committed
329
      if(connection_ap_handshake_socks_reply(conn, NULL, 0, 1) < 0) {
330
        log_fn(LOG_INFO,"Writing to socks-speaking application failed. Closing.");
331
        connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
332
333
      }
      break;
334
    case RELAY_COMMAND_SENDME:
335
      if(!conn) {
336
337
338
        if(edge_type == EDGE_AP) {
          assert(layer_hint);
          layer_hint->package_window += CIRCWINDOW_INCREMENT;
339
          log_fn(LOG_DEBUG,"circ-level sendme at AP, packagewindow %d.", layer_hint->package_window);
340
341
342
343
          circuit_resume_edge_reading(circ, EDGE_AP, layer_hint);
        } else {
          assert(!layer_hint);
          circ->package_window += CIRCWINDOW_INCREMENT;
344
          log_fn(LOG_DEBUG,"circ-level sendme at exit, packagewindow %d.", circ->package_window);
345
346
          circuit_resume_edge_reading(circ, EDGE_EXIT, layer_hint);
        }
347
348
        return 0;
      }
349
      conn->package_window += STREAMWINDOW_INCREMENT;
350
      log_fn(LOG_DEBUG,"stream-level sendme, packagewindow now %d.", conn->package_window);
351
      connection_start_reading(conn);
Nick Mathewson's avatar
Nick Mathewson committed
352
      connection_edge_package_raw_inbuf(conn); /* handle whatever might still be on the inbuf */
353
354
      break;
    default:
Roger Dingledine's avatar
Roger Dingledine committed
355
      log_fn(LOG_WARN,"unknown relay command %d.",relay_command);
356
      return -1;
357
358
359
360
361
362
363
364
365
366
367
368
  }
  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:
369
      if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0)  { /* not yet */
370
        if(!ERRNO_CONN_EINPROGRESS(errno)) {
371
          /* yuck. kill it. */
372
          log_fn(LOG_DEBUG,"in-progress exit connect failed. Removing.");
373
374
          return -1;
        } else {
375
          log_fn(LOG_DEBUG,"in-progress exit connect still waiting.");
376
377
378
379
380
          return 0; /* no change, see if next time is better */
        }
      }
      /* the connect has finished. */

381
      log_fn(LOG_INFO,"Exit connection to %s:%u established.",
382
383
384
385
          conn->address,conn->port);

      conn->state = EXIT_CONN_STATE_OPEN;
      connection_watch_events(conn, POLLIN); /* stop writing, continue reading */
386
      if(connection_wants_to_flush(conn)) /* in case there are any queued relay cells */
387
        connection_start_writing(conn);
388
      /* deliver a 'connected' relay cell back through the circuit. */
389
390
391
      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 */
392
      return connection_process_inbuf(conn); /* in case the server has written anything */
393
394
395
    case AP_CONN_STATE_OPEN:
    case EXIT_CONN_STATE_OPEN:
      connection_stop_writing(conn);
396
      connection_edge_consider_sending_sendme(conn);
397
      return 0;
Roger Dingledine's avatar
Roger Dingledine committed
398
399
400
    case AP_CONN_STATE_SOCKS_WAIT:
      connection_stop_writing(conn);
      return 0;
401
    default:
402
      log_fn(LOG_WARN,"BUG: called in unexpected state: %d", conn->state);
403
      return -1;
404
405
406
407
  }
  return 0;
}

408
409
410
411
412
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
413
int connection_edge_package_raw_inbuf(connection_t *conn) {
414
415
  int amount_to_process, length;
  char payload[CELL_PAYLOAD_SIZE];
416
417
418
419
420
  circuit_t *circ;
 
  assert(conn);
  assert(!connection_speaks_cells(conn));
 
Nick Mathewson's avatar
Nick Mathewson committed
421
repeat_connection_edge_package_raw_inbuf:
422
423
424
 
  circ = circuit_get_by_conn(conn);
  if(!circ) {
425
    log_fn(LOG_INFO,"conn has no circuits! Closing.");
426
427
428
429
430
431
432
    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
433
    log_fn(LOG_WARN,"called with package_window %d. Tell Roger.", conn->package_window);
434
435
436
437
    connection_stop_reading(conn);
    return 0;
  }
 
438
  amount_to_process = buf_datalen(conn->inbuf);
439
440
441
442
443
 
  if(!amount_to_process)
    return 0;
 
  if(amount_to_process > CELL_PAYLOAD_SIZE - RELAY_HEADER_SIZE) {
444
    length = CELL_PAYLOAD_SIZE - RELAY_HEADER_SIZE;
445
  } else {
446
    length = amount_to_process;
447
  }
448
  stats_n_data_bytes_packaged += length;
449
  stats_n_data_cells_packaged += 1;
450
 
451
452
453
  connection_fetch_from_buf(payload, length, conn);

  log_fn(LOG_DEBUG,"(%d) Packaging %d bytes (%d waiting).",conn->s,length,
454
         (int)buf_datalen(conn->inbuf));
455
 
456
457
458
  if(connection_edge_send_command(conn, circ, RELAY_COMMAND_DATA,
                               payload, length, conn->cpath_layer) < 0)
    return 0; /* circuit is closed, don't continue */
459

460
461
462
  if(conn->type == CONN_TYPE_EXIT) {
    assert(circ->package_window > 0);
    circ->package_window--;
463
  } else { /* we're an AP */
464
465
466
467
468
469
470
471
472
473
474
475
476
477
    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
478
  goto repeat_connection_edge_package_raw_inbuf;
479
480
}

481
482
483
/* Tell any APs that are waiting for a new circuit that one is available */
void connection_ap_attach_pending(void)
{
484
485
486
487
488
489
490
491
492
493
494
495
496
  connection_t **carray;
  int n, i;
  int need_new_circuit = 0;

  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;
    if (connection_ap_handshake_attach_circuit(carray[i])) {
      need_new_circuit = 1; /* XXX act on this. */
    }
497
498
499
  }
}

500
static void connection_edge_consider_sending_sendme(connection_t *conn) {
501
502
503
  circuit_t *circ;
 
  if(connection_outbuf_too_full(conn))
504
    return;
505
506
507
 
  circ = circuit_get_by_conn(conn);
  if(!circ) {
508
509
    /* this can legitimately happen if the destroy has already
     * arrived and torn down the circuit */
510
    log_fn(LOG_INFO,"No circuit associated with conn. Skipping.");
511
    return;
512
513
514
515
516
  }
 
  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;
517
518
519
520
    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 */
521
522
523
524
525
    }
  }
}

static int connection_ap_handshake_process_socks(connection_t *conn) {
526
  socks_request_t *socks;
Roger Dingledine's avatar
Roger Dingledine committed
527
  int sockshere;
528
529

  assert(conn);
530
531
532
533
  assert(conn->type == CONN_TYPE_AP);
  assert(conn->state == AP_CONN_STATE_SOCKS_WAIT);
  assert(conn->socks_request);
  socks = conn->socks_request;
534
535
536

  log_fn(LOG_DEBUG,"entered.");

537
  sockshere = fetch_from_buf_socks(conn->inbuf, socks);
Roger Dingledine's avatar
Roger Dingledine committed
538
  if(sockshere == -1 || sockshere == 0) {
539
    if(socks->replylen) { /* we should send reply back */
Roger Dingledine's avatar
Roger Dingledine committed
540
      log_fn(LOG_DEBUG,"reply is already set for us. Using it.");
541
      connection_ap_handshake_socks_reply(conn, socks->reply, socks->replylen, 0);
Roger Dingledine's avatar
Roger Dingledine committed
542
    } else if(sockshere == -1) { /* send normal reject */
Roger Dingledine's avatar
Roger Dingledine committed
543
      log_fn(LOG_WARN,"Fetching socks handshake failed. Closing.");
Roger Dingledine's avatar
Roger Dingledine committed
544
545
546
547
548
549
      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 */
550

551
  conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
552
553
554
  if (connection_ap_handshake_attach_circuit(conn)) {
    /* XXX we need a circuit */
  }      
555
556
557
558
  return 0;
}

/* Try to find a live circuit.  If we don't find one, tell 'conn' to
559
560
561
 * stop reading and return 0.  Otherwise, associate the CONN_TYPE_AP
 * connection 'conn' with the newest live circuit, start sending a
 * BEGIN cell down the circuit, and return 1.
562
563
564
565
566
567
568
569
570
 */
static int connection_ap_handshake_attach_circuit(connection_t *conn) {
  circuit_t *circ;
  
  assert(conn);
  assert(conn->type == CONN_TYPE_AP);
  assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
  assert(conn->socks_request);
  
571
  /* find the circuit that we should use, if there is one. */
Roger Dingledine's avatar
Roger Dingledine committed
572
  circ = circuit_get_newest_open();
573
574

  if(!circ) {
575
    log_fn(LOG_INFO,"No circuit ready for edge connection; delaying.");
Roger Dingledine's avatar
Roger Dingledine committed
576
577
578
579
580
581
    connection_stop_reading(conn);
    /* XXX both this and the start_reading below can go away if we
     *     remove our notion that we shouldn't read from a socks
     *     client until we're connected. the socks spec promises that it
     *     won't write. is that good enough?
     */
582
583
584
585
586
587
588
589
590
591
    return -1;
  }
  if (connection_ap_can_use_exit(conn,
                  router_get_by_addr_port(circ->cpath->prev->addr,
                                          circ->cpath->prev->port)) < 0) {
    /* The exit on this circuit will _definitely_ reject this connection. */
    log_fn(LOG_INFO,"Most recent circuit will reject AP connection; delaying.");
    /* XXX Build another circuit. */
    connection_stop_reading(conn);
    return -1;
592
  }
593

Roger Dingledine's avatar
Roger Dingledine committed
594
  connection_start_reading(conn);
595
596
597
598

  circ->dirty = 1;

  /* 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
601
602
603
604
605
606
  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;

607
  connection_ap_handshake_send_begin(conn, circ);
608

609
  return 0;
610
611
}

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

620
621
622
  assert(ap_conn->type == CONN_TYPE_AP);
  assert(ap_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
  assert(ap_conn->socks_request);
Roger Dingledine's avatar
Roger Dingledine committed
623
  assert(ap_conn->socks_request->addr);
624

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

628
629
630
  in.s_addr = client_dns_lookup_entry(ap_conn->socks_request->addr);
  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
635
636
             "%s:%d", 
             string_addr ? string_addr : ap_conn->socks_request->addr, 
             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
682
683
    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);
684
    return flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen); /* try to flush it */
Roger Dingledine's avatar
Roger Dingledine committed
685
  }
686
  return 0; /* if socks_version isn't 4 or 5, don't send anything */
687
688
}

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

693
694
695
696
  /* 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
697
698
  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
699
    log_fn(LOG_WARN,"relay begin cell has no \\0. Dropping.");
700
701
702
703
    return 0;
  }
  colon = strchr(cell->payload+RELAY_HEADER_SIZE+STREAM_ID_SIZE, ':');
  if(!colon) {
Roger Dingledine's avatar
Roger Dingledine committed
704
    log_fn(LOG_WARN,"relay begin cell has no colon. Dropping.");
705
706
707
708
709
    return 0;
  }
  *colon = 0;

  if(!atoi(colon+1)) { /* bad port */
Roger Dingledine's avatar
Roger Dingledine committed
710
    log_fn(LOG_WARN,"relay begin cell has invalid port. Dropping.");
711
712
713
714
715
716
717
    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);
718
  n_stream->address = tor_strdup(cell->payload + RELAY_HEADER_SIZE + STREAM_ID_SIZE);
719
720
  n_stream->port = atoi(colon+1);
  n_stream->state = EXIT_CONN_STATE_RESOLVING;
721
  /* leave n_stream->s at -1, because it's not yet valid */
722
723
724
  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
725
    log_fn(LOG_WARN,"connection_add failed. Dropping.");
726
727
728
729
730
731
732
733
734
735
736
    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 */
737
738
      connection_exit_connect(n_stream);
      return 0;
739
    case -1: /* resolve failed */
740
741
742
      log_fn(LOG_WARN,"Resolve failed (%s).", n_stream->address);
      connection_edge_end(n_stream, END_STREAM_REASON_RESOLVEFAILED, NULL);
    /* case 0, resolve added to pending list */
743
744
745
746
  }
  return 0;
}

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

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

756
757
  switch(connection_connect(conn, conn->address, conn->addr, conn->port)) {
    case -1:
758
759
      connection_edge_end(conn, END_STREAM_REASON_CONNECTFAILED, NULL);
      return;
760
    case 0:
761
762
763
764
765
766
      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. */
767
      return;
768
    /* case 1: fall through */
769
770
771
772
773
  }

  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
774
    log_fn(LOG_WARN,"tell roger: newly connected conn had data waiting!");
775
776
777
778
779
780
//    connection_start_writing(conn);
  }
//   connection_process_inbuf(conn);
  connection_watch_events(conn, POLLIN);

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

786
787
788
789
790
791
792
793
int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit)
{
  uint32_t addr;
  
  addr = client_dns_lookup_entry(conn->socks_request->addr);
  return router_supports_exit_address(addr, conn->port, exit);
}

794
795
796
797
798
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
/* ***** 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)) {
837
    log_fn(LOG_DEBUG, "Using static address %s (%08X)", address, in.s_addr);
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
    return in.s_addr;
  }
  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;
    }
    in.s_addr = ent->addr;
    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)) {
    if (in.s_addr == val)
      return;
    in.s_addr = val;
    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) {
    log_fn(LOG_DEBUG, "Updating entry for address %s", address);
    ent->addr = val;
    ent->expires = now+MAX_DNS_ENTRY_AGE;
  } else {
    in.s_addr = val;
    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;
  }
}
898

899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
static void client_dns_clean()
{
  struct client_dns_entry **expired_entries;
  int n_expired_entries = 0;
  struct client_dns_entry *ent;
  time_t now;
  int i;

  expired_entries = tor_malloc(client_dns_size * 
                               sizeof(struct client_dns_entry *));   
  
  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);
921
922
}

923
924
925
926
927
928
929
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/