aboutsummaryrefslogtreecommitdiff
path: root/src/or/circuit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/circuit.c')
-rw-r--r--src/or/circuit.c154
1 files changed, 105 insertions, 49 deletions
diff --git a/src/or/circuit.c b/src/or/circuit.c
index e2d1c0d24..81b117dc9 100644
--- a/src/or/circuit.c
+++ b/src/or/circuit.c
@@ -281,7 +281,8 @@ circuit_t *circuit_get_newest_ap(void) {
return bestcirc;
}
-int circuit_deliver_relay_cell_from_edge(cell_t *cell, circuit_t *circ, char edge_type) {
+int circuit_deliver_relay_cell_from_edge(cell_t *cell, circuit_t *circ,
+ char edge_type, crypt_path_t *layer_hint) {
int cell_direction;
static int numsent_ap=0, numsent_exit=0;
@@ -293,7 +294,7 @@ int circuit_deliver_relay_cell_from_edge(cell_t *cell, circuit_t *circ, char edg
log(LOG_DEBUG,"circuit_deliver_relay_cell_from_edge(): now sent %d relay cells from ap", numsent_ap);
if(circ->p_receive_circwindow <= 0) {
log(LOG_DEBUG,"circuit_deliver_relay_cell_from_edge(): pwindow 0, queueing for later.");
- circ->relay_queue = relay_queue_add(circ->relay_queue, cell);
+ circ->relay_queue = relay_queue_add(circ->relay_queue, cell, layer_hint);
return 0;
}
circ->p_receive_circwindow--;
@@ -304,13 +305,13 @@ int circuit_deliver_relay_cell_from_edge(cell_t *cell, circuit_t *circ, char edg
log(LOG_DEBUG,"circuit_deliver_relay_cell_from_edge(): now sent %d relay cells from exit", numsent_exit);
if(circ->n_receive_circwindow <= 0) {
log(LOG_DEBUG,"circuit_deliver_relay_cell_from_edge(): nwindow 0, queueing for later.");
- circ->relay_queue = relay_queue_add(circ->relay_queue, cell);
+ circ->relay_queue = relay_queue_add(circ->relay_queue, cell, layer_hint);
return 0;
}
circ->n_receive_circwindow--;
}
- if(circuit_deliver_relay_cell(cell, circ, cell_direction) < 0) {
+ if(circuit_deliver_relay_cell(cell, circ, cell_direction, layer_hint) < 0) {
return -1;
}
@@ -318,113 +319,168 @@ int circuit_deliver_relay_cell_from_edge(cell_t *cell, circuit_t *circ, char edg
return 0;
}
-int circuit_deliver_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction) {
- connection_t *conn;
+int circuit_deliver_relay_cell(cell_t *cell, circuit_t *circ,
+ int cell_direction, crypt_path_t *layer_hint) {
+ connection_t *conn=NULL;
+ char recognized=0;
+ char buf[256];
assert(cell && circ);
assert(cell_direction == CELL_DIRECTION_OUT || cell_direction == CELL_DIRECTION_IN);
- if(cell_direction == CELL_DIRECTION_OUT)
- conn = circ->n_conn;
- else
- conn = circ->p_conn;
- /* first crypt cell->length */
- if(circuit_crypt(circ, &(cell->length), 1, cell_direction) < 0) {
- log(LOG_DEBUG,"circuit_deliver_relay_cell(): length crypt failed. Dropping connection.");
- return -1;
- }
+ buf[0] = cell->length;
+ memcpy(buf+1, cell->payload, CELL_PAYLOAD_SIZE);
- /* then crypt the payload */
- if(circuit_crypt(circ, (char *)&(cell->payload), CELL_PAYLOAD_SIZE, cell_direction) < 0) {
- log(LOG_DEBUG,"circuit_deliver_relay_cell(): payload crypt failed. Dropping connection.");
+ log(LOG_DEBUG,"circuit_deliver_relay_cell(): streamid %d before crypt.", *(int*)(cell->payload+1));
+
+ if(relay_crypt(circ, buf, 1+CELL_PAYLOAD_SIZE, cell_direction, layer_hint, &recognized, &conn) < 0) {
+ log(LOG_DEBUG,"circuit_deliver_relay_cell(): relay crypt failed. Dropping connection.");
return -1;
}
- if(cell_direction == CELL_DIRECTION_OUT && (!conn || conn->type == CONN_TYPE_EXIT)) {
- log(LOG_DEBUG,"circuit_deliver_relay_cell(): Sending to exit.");
- return connection_edge_process_relay_cell(cell, circ, EDGE_EXIT);
+ cell->length = buf[0];
+ memcpy(cell->payload, buf+1, CELL_PAYLOAD_SIZE);
+
+ if(recognized) {
+ if(cell_direction == CELL_DIRECTION_OUT) {
+ log(LOG_DEBUG,"circuit_deliver_relay_cell(): Sending to exit.");
+ return connection_edge_process_relay_cell(cell, circ, conn, EDGE_EXIT);
+ }
+ if(cell_direction == CELL_DIRECTION_IN) {
+ log(LOG_DEBUG,"circuit_deliver_relay_cell(): Sending to AP.");
+ return connection_edge_process_relay_cell(cell, circ, conn, EDGE_AP);
+ }
}
- if(cell_direction == CELL_DIRECTION_IN && (!conn || conn->type == CONN_TYPE_AP)) {
- log(LOG_DEBUG,"circuit_deliver_relay_cell(): Sending to AP.");
- return connection_edge_process_relay_cell(cell, circ, EDGE_AP);
+
+ /* not recognized. pass it on. */
+ if(cell_direction == CELL_DIRECTION_OUT)
+ conn = circ->n_conn;
+ else
+ conn = circ->p_conn;
+
+ if(!conn || !connection_speaks_cells(conn)) {
+ log(LOG_INFO,"circuit_deliver_relay_cell(): Didn't recognize cell (%d), but circ stops here! Dropping.", *(int *)(cell->payload+1));
+ return 0;
}
- /* else send it as a cell */
- assert(conn);
- //log(LOG_DEBUG,"circuit_deliver_relay_cell(): Sending to connection.");
+
return connection_write_cell_to_buf(cell, conn);
}
-int circuit_crypt(circuit_t *circ, char *in, int inlen, char cell_direction) {
- char *out;
+int relay_crypt(circuit_t *circ, char *in, int inlen, char cell_direction,
+ crypt_path_t *layer_hint, char *recognized, connection_t **conn) {
crypt_path_t *thishop;
+ char out[256];
- assert(circ && in);
+ assert(circ && in && recognized && conn);
- out = (char *)malloc(inlen);
- if(!out)
- return -1;
+ assert(inlen < 256);
if(cell_direction == CELL_DIRECTION_IN) {
if(circ->cpath) { /* we're at the beginning of the circuit. We'll want to do layered crypts. */
thishop = circ->cpath;
- /* Remember: cpath is in forward order, that is, first hop first. */
- do {
+ do { /* Remember: cpath is in forward order, that is, first hop first. */
assert(thishop);
+
/* decrypt */
if(crypto_cipher_decrypt(thishop->b_crypto, in, inlen, out)) {
log(LOG_ERR,"Error performing decryption:%s",crypto_perror());
- free(out);
return -1;
}
-
- /* copy ciphertext back to buf */
memcpy(in,out,inlen);
+
+ if( (*recognized = relay_check_recognized(circ, cell_direction, in+2, conn)))
+ return 0;
+
thishop = thishop->next;
} while(thishop != circ->cpath);
+ log(LOG_INFO,"relay_crypt(): in-cell at OP not recognized. Killing circuit.");
+ return -1;
} else { /* we're in the middle. Just one crypt. */
- if(crypto_cipher_encrypt(circ->p_crypto,in, inlen, out)) {
+
+ if(crypto_cipher_encrypt(circ->p_crypto, in, inlen, out)) {
log(LOG_ERR,"circuit_encrypt(): Encryption failed for ACI : %u (%s).",
circ->p_aci, crypto_perror());
- free(out);
return -1;
}
memcpy(in,out,inlen);
+
+ /* don't check for recognized. only the OP can recognize a stream on the way back. */
+
}
} else if(cell_direction == CELL_DIRECTION_OUT) {
if(circ->cpath) { /* we're at the beginning of the circuit. We'll want to do layered crypts. */
- thishop = circ->cpath->prev;
+
+ thishop = layer_hint; /* we already know which layer, from when we package_raw_inbuf'ed */
/* moving from last to first hop */
do {
assert(thishop);
+
/* encrypt */
- if(crypto_cipher_encrypt(thishop->f_crypto, in, inlen, (unsigned char *)out)) {
+ if(crypto_cipher_encrypt(thishop->f_crypto, in, inlen, out)) {
log(LOG_ERR,"Error performing encryption:%s",crypto_perror());
- free(out);
return -1;
}
-
- /* copy ciphertext back to buf */
memcpy(in,out,inlen);
+
thishop = thishop->prev;
} while(thishop != circ->cpath->prev);
} else { /* we're in the middle. Just one crypt. */
+
if(crypto_cipher_decrypt(circ->n_crypto,in, inlen, out)) {
log(LOG_ERR,"circuit_crypt(): Decryption failed for ACI : %u (%s).",
circ->n_aci, crypto_perror());
- free(out);
return -1;
}
memcpy(in,out,inlen);
+
+ if( (*recognized = relay_check_recognized(circ, cell_direction, in+2, conn)))
+ return 0;
+
}
} else {
log(LOG_ERR,"circuit_crypt(): unknown cell direction %d.", cell_direction);
assert(0);
}
- free(out);
return 0;
}
+int relay_check_recognized(circuit_t *circ, int cell_direction, char *stream, connection_t **conn) {
+/* FIXME can optimize by passing thishop in */
+ connection_t *tmpconn;
+
+ log(LOG_DEBUG,"relay_check_recognized(): entering");
+ if(!memcmp(stream,ZERO_STREAM,STREAM_ID_SIZE))
+ return 1; /* the zero stream is always recognized */
+
+ if(cell_direction == CELL_DIRECTION_OUT)
+ tmpconn = circ->n_conn;
+ else
+ tmpconn = circ->p_conn;
+
+ log(LOG_DEBUG,"relay_check_recognized(): not the zero stream.");
+ if(!tmpconn)
+ return 0; /* no conns? don't recognize it */
+
+ while(tmpconn && tmpconn->type == CONN_TYPE_OR) {
+ log(LOG_DEBUG,"relay_check_recognized(): skipping over an OR conn");
+ tmpconn = tmpconn->next_stream;
+ }
+
+ for( ; tmpconn; tmpconn=tmpconn->next_stream) {
+ if(!memcmp(stream,tmpconn->stream_id, STREAM_ID_SIZE)) {
+ log(LOG_DEBUG,"relay_check_recognized(): recognized stream %d.", *(int*)stream);
+ *conn = tmpconn;
+ return 1;
+ }
+ log(LOG_DEBUG,"relay_check_recognized(): considered stream %d, not it.",*(int*)tmpconn->stream_id);
+ }
+
+ log(LOG_DEBUG,"relay_check_recognized(): Didn't recognize. Giving up.");
+ return 0;
+
+}
+
void circuit_resume_edge_reading(circuit_t *circ, int edge_type) {
connection_t *conn;
struct relay_queue_t *tmpd;
@@ -438,7 +494,7 @@ void circuit_resume_edge_reading(circuit_t *circ, int edge_type) {
circ->n_receive_circwindow--;
assert(circ->n_receive_circwindow >= 0);
- if(circuit_deliver_relay_cell(circ->relay_queue->cell, circ, CELL_DIRECTION_IN) < 0) {
+ if(circuit_deliver_relay_cell(circ->relay_queue->cell, circ, CELL_DIRECTION_IN, circ->relay_queue->layer_hint) < 0) {
circuit_close(circ);
return;
}
@@ -446,7 +502,7 @@ void circuit_resume_edge_reading(circuit_t *circ, int edge_type) {
circ->p_receive_circwindow--;
assert(circ->p_receive_circwindow >= 0);
- if(circuit_deliver_relay_cell(circ->relay_queue->cell, circ, CELL_DIRECTION_OUT) < 0) {
+ if(circuit_deliver_relay_cell(circ->relay_queue->cell, circ, CELL_DIRECTION_OUT, circ->relay_queue->layer_hint) < 0) {
circuit_close(circ);
return;
}