diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/or/buffers.c | 42 | ||||
-rw-r--r-- | src/or/cell.c | 5 | ||||
-rw-r--r-- | src/or/circuit.c | 3 | ||||
-rw-r--r-- | src/or/command.c | 6 | ||||
-rw-r--r-- | src/or/config.c | 74 | ||||
-rw-r--r-- | src/or/connection.c | 169 | ||||
-rw-r--r-- | src/or/connection_ap.c | 3 | ||||
-rw-r--r-- | src/or/connection_exit.c | 3 | ||||
-rw-r--r-- | src/or/connection_op.c | 4 | ||||
-rw-r--r-- | src/or/connection_or.c | 50 | ||||
-rw-r--r-- | src/or/main.c | 170 | ||||
-rw-r--r-- | src/or/onion.c | 3 | ||||
-rw-r--r-- | src/or/or.h | 54 | ||||
-rw-r--r-- | src/or/routers.c | 4 |
14 files changed, 452 insertions, 138 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c index cf511f7ad7..65699ebb42 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -1,3 +1,6 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ /* buffers.c */ @@ -21,17 +24,29 @@ void buf_free(char *buf) { free(buf); } -int read_to_buf(int s, char **buf, size_t *buflen, size_t *buf_datalen, int *reached_eof) { +int read_to_buf(int s, int at_most, char **buf, size_t *buflen, size_t *buf_datalen, int *reached_eof) { - /* grab from s, put onto buf, return how many bytes read */ + /* read from socket s, writing onto buf+buf_datalen. Read at most + * 'at_most' bytes, and also don't read more than will fit based on buflen. + * If read() returns 0, set *reached_eof to 1 and return 0. If you want to tear + * down the connection return -1, else return the number of bytes read. + */ int read_result; - assert(buf && *buf && buflen && buf_datalen && reached_eof && (s>=0)); + assert(buf && *buf && buflen && buf_datalen && reached_eof && (s>=0) && (at_most >= 0)); /* this is the point where you would grow the buffer, if you want to */ - read_result = read(s, *buf+*buf_datalen, *buflen - *buf_datalen); + if(*buflen - *buf_datalen < at_most) + at_most = *buflen - *buf_datalen; /* take the min of the two */ + /* (note that this only modifies at_most inside this function) */ + + if(at_most == 0) + return 0; /* we shouldn't read anything */ + + log(LOG_DEBUG,"read_to_buf(): reading at most %d bytes.",at_most); + read_result = read(s, *buf+*buf_datalen, at_most); if (read_result < 0) { if(errno!=EAGAIN) { /* it's a real error */ return -1; @@ -49,22 +64,24 @@ int read_to_buf(int s, char **buf, size_t *buflen, size_t *buf_datalen, int *rea } -int flush_buf(int s, char **buf, size_t *buflen, size_t *buf_datalen) { +int flush_buf(int s, char **buf, size_t *buflen, size_t *buf_flushlen, size_t *buf_datalen) { /* push from buf onto s * then memmove to front of buf - * return -1 or how many bytes remain on the buf */ + * return -1 or how many bytes remain to be flushed */ int write_result; - assert(buf && *buf && buflen && buf_datalen && (s>=0)); + assert(buf && *buf && buflen && buf_flushlen && buf_datalen && (s>=0) && (*buf_flushlen <= *buf_datalen)); - if(*buf_datalen == 0) /* nothing to flush */ + if(*buf_flushlen == 0) /* nothing to flush */ return 0; /* this is the point where you would grow the buffer, if you want to */ - write_result = write(s, *buf, *buf_datalen); + write_result = write(s, *buf, *buf_flushlen > 10240 ? 10240 : *buf_flushlen); + /* try to flush at most 10240 bytes at a time. otherwise write() can hang for + * quite a while trying to get it all out. that's bad. */ if (write_result < 0) { if(errno!=EAGAIN) { /* it's a real error */ return -1; @@ -73,11 +90,12 @@ int flush_buf(int s, char **buf, size_t *buflen, size_t *buf_datalen) { return 0; } else { *buf_datalen -= write_result; + *buf_flushlen -= write_result; memmove(*buf, *buf+write_result, *buf_datalen); - log(LOG_DEBUG,"flush_buf(): flushed %d bytes, %d remain.",write_result,*buf_datalen); - return *buf_datalen; + log(LOG_DEBUG,"flush_buf(): flushed %d bytes, %d ready to flush, %d remain.", + write_result,*buf_flushlen,*buf_datalen); + return *buf_flushlen; } - } int write_to_buf(char *string, size_t string_len, diff --git a/src/or/cell.c b/src/or/cell.c index fef8e8c6dd..bf552cd326 100644 --- a/src/or/cell.c +++ b/src/or/cell.c @@ -1,3 +1,6 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ #include "or.h" @@ -5,10 +8,12 @@ int check_sane_cell(cell_t *cell) { assert(cell); +#if 0 /* actually, the aci is 0 for padding cells */ if(cell->aci == 0) { log(LOG_DEBUG,"check_sane_cell(): Cell has aci=0. Dropping."); return -1; } +#endif #if 0 /* actually, the length is sometimes encrypted. so it's ok. */ if(cell->length > 120) { diff --git a/src/or/circuit.c b/src/or/circuit.c index 20d6e254f0..eeca48cbe9 100644 --- a/src/or/circuit.c +++ b/src/or/circuit.c @@ -1,3 +1,6 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ #include "or.h" diff --git a/src/or/command.c b/src/or/command.c index 3c2ee97165..3f3460d889 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -1,3 +1,6 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ #include "or.h" @@ -40,8 +43,7 @@ void command_process_create_cell(cell_t *cell, connection_t *conn) { if(!circ) { /* if it's not there, create it */ circ = circuit_new(cell->aci, conn); circ->state = CIRCUIT_STATE_OPEN_WAIT; - memcpy((void *)&circ->onionlen,(void *)cell->payload, 4); - circ->onionlen = ntohl(circ->onionlen); + circ->onionlen = ntohl(*(int*)cell->payload); log(LOG_DEBUG,"command_process_create_cell(): Onion length is %u.",circ->onionlen); if(circ->onionlen > 50000 || circ->onionlen < 1) { /* too big or too small */ log(LOG_DEBUG,"That's ludicrous. Closing."); diff --git a/src/or/config.c b/src/or/config.c index e0a583354d..640044fc7e 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1,3 +1,7 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + /** * config.c * Routines for loading the configuration file. @@ -5,50 +9,6 @@ * Matej Pfajfar <mp292@cam.ac.uk> */ -/* - * Changes : - * $Log$ - * Revision 1.10 2002/07/15 16:42:27 montrose - * corrected some string literals - * - * Revision 1.9 2002/07/11 19:03:44 montrose - * finishing touches. think its ready for integration now. - * - * Revision 1.8 2002/07/11 18:38:15 montrose - * added new option GlobalRole to getoptions() - * - * Revision 1.7 2002/07/11 14:50:26 montrose - * cleaned up some, added validation to getoptions() - * - * Revision 1.6 2002/07/10 12:37:49 montrose - * Added usage display on error. - * - * Revision 1.5 2002/07/09 19:51:41 montrose - * Miscellaneous bug fixes / activated "make check" for src/or - * - * Revision 1.4 2002/07/03 19:58:18 montrose - * minor bug fix in error checking - * - * Revision 1.3 2002/07/03 16:53:34 montrose - * added error checking into getoptions() - * - * Revision 1.2 2002/07/03 16:31:22 montrose - * Added getoptions() and made minor adjustment to poptReadDefaultOptions() - * - * Revision 1.1.1.1 2002/06/26 22:45:50 arma - * initial commit: current code - * - * Revision 1.3 2002/04/02 14:28:24 badbytes - * Final finishes. - * - * Revision 1.2 2002/01/27 00:42:50 mp292 - * Reviewed according to Secure-Programs-HOWTO. - * - * Revision 1.1 2002/01/03 10:24:05 badbytes - * COde based on that in op. Needs to be modified. - * - */ - #include "or.h" #include <libgen.h> @@ -119,7 +79,9 @@ RETURN VALUE: 0 on success, non-zero on error 0, "local port on which the onion proxy is running", "<file>" }, { "TrafficShaping", 't', POPT_ARG_INT, &options->TrafficShaping, 0, "which traffic shaping policy to use", "<policy>" }, - { "GlobalRole", 'g', POPT_ARG_INT, &options->GlobalRole, + { "LinkPadding", 'P', POPT_ARG_INT, &options->LinkPadding, + 0, "whether to use link padding", "<padding>" }, + { "Role", 'g', POPT_ARG_INT, &options->Role, 0, "4-bit global role id", "<role>" }, { "Verbose", 'v', POPT_ARG_NONE, &Verbose, 0, "display options selected before execution", NULL }, @@ -137,7 +99,8 @@ RETURN VALUE: 0 on success, non-zero on error options->LogLevel = "debug"; options->loglevel = LOG_DEBUG; options->CoinWeight = 0.8; - options->GlobalRole = ROLE_OR_LISTEN | ROLE_OR_CONNECT_ALL | ROLE_OP_LISTEN | ROLE_AP_LISTEN; + options->LinkPadding = 1; + options->Role = ROLE_OR_LISTEN | ROLE_OR_CONNECT_ALL | ROLE_OP_LISTEN | ROLE_AP_LISTEN; code = poptGetNextOpt(optCon); /* first we handle command-line args */ if ( code == -1 ) @@ -170,19 +133,20 @@ RETURN VALUE: 0 on success, non-zero on error if ( Verbose ) { - printf("LogLevel=%s, GlobalRole=%d\n", + printf("LogLevel=%s, Role=%d\n", options->LogLevel, - options->GlobalRole); + options->Role); printf("RouterFile=%s, PrivateKeyFile=%s\n", options->RouterFile, options->PrivateKeyFile); printf("ORPort=%d, OPPort=%d, APPort=%d\n", options->ORPort,options->OPPort, options->APPort); - printf("CoinWeight=%6.4f, MaxConn=%d, TrafficShaping=%d\n", + printf("CoinWeight=%6.4f, MaxConn=%d, TrafficShaping=%d, LinkPadding=%d\n", options->CoinWeight, options->MaxConn, - options->TrafficShaping); + options->TrafficShaping, + options->LinkPadding); } /* Validate options */ @@ -260,9 +224,15 @@ RETURN VALUE: 0 on success, non-zero on error code = -1; } - if ( options->GlobalRole < 0 || options->GlobalRole > 15 ) + if ( options->LinkPadding != 0 && options->LinkPadding != 1 ) + { + log(LOG_ERR,"LinkPadding option must be either 0 or 1."); + code = -1; + } + + if ( options->Role < 0 || options->Role > 15 ) { - log(LOG_ERR,"GlobalRole option must be an integer between 0 and 15 (inclusive)."); + log(LOG_ERR,"Role option must be an integer between 0 and 15 (inclusive)."); code = -1; } diff --git a/src/or/connection.c b/src/or/connection.c index 0b80db7960..8b4155d15a 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1,8 +1,13 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ #include "or.h" /********* START VARIABLES **********/ +extern or_options_t options; /* command-line and config-file options */ + #if 0 /* these are now out of date :( -RD */ char *conn_type_to_string[] = { @@ -38,6 +43,34 @@ char *conn_state_to_string[][10] = { /********* END VARIABLES ************/ +/**************************************************************/ + +int tv_cmp(struct timeval *a, struct timeval *b) { + if (a->tv_sec > b->tv_sec) + return 1; + if (a->tv_sec < b->tv_sec) + return -1; + if (a->tv_usec > b->tv_usec) + return 1; + if (a->tv_usec < b->tv_usec) + return -1; + return 0; +} + +void tv_add(struct timeval *a, struct timeval *b) { + a->tv_usec += b->tv_usec; + a->tv_sec += b->tv_sec + (a->tv_usec / 1000000); + a->tv_usec %= 1000000; +} + +void tv_addms(struct timeval *a, long ms) { + a->tv_usec += (ms * 1000) % 1000000; + a->tv_sec += ((ms * 1000) / 1000000) + (a->tv_usec / 1000000); + a->tv_usec %= 1000000; +} + +/**************************************************************/ + connection_t *connection_new(int type) { connection_t *conn; @@ -51,6 +84,8 @@ connection_t *connection_new(int type) { buf_new(&conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen) < 0) return NULL; + conn->receiver_bucket = 10240; /* should be enough to do the handshake */ + conn->bandwidth = conn->receiver_bucket / 10; /* give it a default */ return conn; } @@ -245,7 +280,26 @@ connection_t *connection_connect_to_router_as_op(routerinfo_t *router, RSA *prke } int connection_read_to_buf(connection_t *conn) { - return read_to_buf(conn->s, &conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen, &conn->inbuf_reached_eof); + int read_result; + + read_result = read_to_buf(conn->s, conn->receiver_bucket, &conn->inbuf, &conn->inbuflen, + &conn->inbuf_datalen, &conn->inbuf_reached_eof); + log(LOG_DEBUG,"connection_read_to_buf(): read_to_buf returned %d.",read_result); + if(read_result >= 0) { + conn->receiver_bucket -= read_result; + if(conn->receiver_bucket <= 0) { + + connection_stop_reading(conn); + + /* If we're not in 'open' state here, then we're never going to finish the + * handshake, because we'll never increment the receiver_bucket. But we + * can't check for that here, because the buf we just read might have enough + * on it to finish the handshake. So we check for that in check_conn_read(). + */ + } + } + + return read_result; } int connection_fetch_from_buf(char *string, int len, connection_t *conn) { @@ -253,16 +307,114 @@ int connection_fetch_from_buf(char *string, int len, connection_t *conn) { } int connection_flush_buf(connection_t *conn) { - return flush_buf(conn->s, &conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen); + return flush_buf(conn->s, &conn->outbuf, &conn->outbuflen, &conn->outbuf_flushlen, &conn->outbuf_datalen); } int connection_write_to_buf(char *string, int len, connection_t *conn) { if(!len) return 0; - connection_watch_events(conn, POLLOUT | POLLIN); + + if( (conn->type != CONN_TYPE_OR && conn->type != CONN_TYPE_OR) || + (!connection_state_is_open(conn)) || + (options.LinkPadding == 0) ) { + /* connection types other than or and op, or or/op not in 'open' state, should flush immediately */ + /* also flush immediately if we're not doing LinkPadding, since otherwise it will never flush */ + connection_watch_events(conn, POLLOUT | POLLIN); + conn->outbuf_flushlen += len; + } + return write_to_buf(string, len, &conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen); } +int connection_receiver_bucket_should_increase(connection_t *conn) { + assert(conn); + + if(conn->receiver_bucket > 10*conn->bandwidth) + return 0; + + return 1; +} + +void connection_increment_receiver_bucket (connection_t *conn) { + assert(conn); + + if(connection_receiver_bucket_should_increase(conn)) { + /* yes, the receiver_bucket can become overfull here. But not by much. */ + conn->receiver_bucket += conn->bandwidth*1.1; + if(connection_state_is_open(conn)) { + /* if we're in state 'open', then start reading again */ + connection_start_reading(conn); + } + } +} + +int connection_state_is_open(connection_t *conn) { + assert(conn); + + if((conn->type == CONN_TYPE_OR && conn->state == OR_CONN_STATE_OPEN) || + (conn->type == CONN_TYPE_OP && conn->state == OP_CONN_STATE_OPEN) || + (conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN) || + (conn->type == CONN_TYPE_EXIT && conn->state == EXIT_CONN_STATE_OPEN)) + return 1; + + return 0; +} + +void connection_send_cell(connection_t *conn) { + cell_t cell; + + assert(conn); + + if(conn->type != CONN_TYPE_OR && conn->type != CONN_TYPE_OP) { + /* this conn doesn't speak cells. do nothing. */ + return; + } + + if(!connection_state_is_open(conn)) { + /* it's not in 'open' state, all data should already be waiting to be flushed */ + assert(conn->outbuf_datalen == conn->outbuf_flushlen); + return; + } + +#if 0 /* use to send evenly spaced cells, but not padding */ + if(conn->outbuf_datalen - conn->outbuf_flushlen >= sizeof(cell_t)) { + conn->outbuf_flushlen += sizeof(cell_t); /* instruct it to send a cell */ + connection_watch_events(conn, POLLOUT | POLLIN); + } +#endif + +#if 1 /* experimental code, that sends padding cells too. 'probably' works :) */ + if(conn->outbuf_datalen - conn->outbuf_flushlen < sizeof(cell_t)) { + /* we need to queue a padding cell first */ + memset(&cell,0,sizeof(cell_t)); + cell.command = CELL_PADDING; + connection_write_cell_to_buf(&cell, conn); + } + + conn->outbuf_flushlen += sizeof(cell_t); /* instruct it to send a cell */ + connection_watch_events(conn, POLLOUT | POLLIN); +#endif + + connection_increment_send_timeval(conn); /* update when we'll send the next cell */ +} + +void connection_increment_send_timeval(connection_t *conn) { + /* add "1000000 * sizeof(cell_t) / conn->bandwidth" microseconds to conn->send_timeval */ + /* FIXME should perhaps use ceil() of this. For now I simply add 1. */ + + tv_addms(&conn->send_timeval, 1+1000 * sizeof(cell_t) / conn->bandwidth); +} + +void connection_init_timeval(connection_t *conn) { + + assert(conn); + + if(gettimeofday(&conn->send_timeval,NULL) < 0) + return; + + connection_increment_send_timeval(conn); +} + int connection_send_destroy(aci_t aci, connection_t *conn) { cell_t cell; @@ -276,6 +428,8 @@ int connection_send_destroy(aci_t aci, connection_t *conn) { return 0; } + assert(conn->type == CONN_TYPE_OR); + cell.aci = aci; cell.command = CELL_DESTROY; log(LOG_DEBUG,"connection_send_destroy(): Sending destroy (aci %d).",aci); @@ -291,7 +445,6 @@ int connection_write_cell_to_buf(cell_t *cellp, connection_t *conn) { } return connection_write_to_buf((char *)cellp, sizeof(cell_t), conn); - } int connection_encrypt_cell_header(cell_t *cellp, connection_t *conn) { @@ -300,22 +453,26 @@ int connection_encrypt_cell_header(cell_t *cellp, connection_t *conn) { int x; char *px; +#if 0 printf("Sending: Cell header plaintext: "); px = (char *)cellp; for(x=0;x<8;x++) { printf("%u ",px[x]); } printf("\n"); +#endif if(!EVP_EncryptUpdate(&conn->f_ctx, newheader, &newsize, (char *)cellp, 8)) { log(LOG_ERR,"Could not encrypt data for connection %s:%u.",conn->address,ntohs(conn->port)); return -1; } +#if 0 printf("Sending: Cell header crypttext: "); for(x=0;x<8;x++) { printf("%u ",newheader[x]); } printf("\n"); +#endif memcpy(cellp,newheader,8); return 0; @@ -430,22 +587,26 @@ int connection_process_cell_from_inbuf(connection_t *conn) { return -1; } +#if 0 printf("Cell header crypttext: "); for(x=0;x<8;x++) { printf("%u ",crypted[x]); } printf("\n"); +#endif /* decrypt */ if(!EVP_DecryptUpdate(&conn->b_ctx,(unsigned char *)outbuf,&outlen,crypted,8)) { log(LOG_ERR,"connection_process_cell_from_inbuf(): Decryption failed, dropping."); return connection_process_inbuf(conn); /* process the remainder of the buffer */ } log(LOG_DEBUG,"connection_process_cell_from_inbuf(): Cell decrypted (%d bytes).",outlen); +#if 0 printf("Cell header plaintext: "); for(x=0;x<8;x++) { printf("%u ",outbuf[x]); } printf("\n"); +#endif /* copy the rest of the cell */ memcpy((char *)outbuf+8, (char *)crypted+8, sizeof(cell_t)-8); diff --git a/src/or/connection_ap.c b/src/or/connection_ap.c index 0cb1d5314e..95f60445dd 100644 --- a/src/or/connection_ap.c +++ b/src/or/connection_ap.c @@ -1,3 +1,6 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ #include "or.h" diff --git a/src/or/connection_exit.c b/src/or/connection_exit.c index 510f4b58be..00e4ec14bb 100644 --- a/src/or/connection_exit.c +++ b/src/or/connection_exit.c @@ -1,3 +1,6 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ #include "or.h" diff --git a/src/or/connection_op.c b/src/or/connection_op.c index c2c932f5d6..1f3b6c0473 100644 --- a/src/or/connection_op.c +++ b/src/or/connection_op.c @@ -1,3 +1,6 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ #include "or.h" @@ -82,6 +85,7 @@ int op_handshake_process_keys(connection_t *conn) { EVP_DecryptInit(&conn->f_ctx, EVP_des_ofb(), conn->f_session_key, conn->f_session_iv); conn->state = OP_CONN_STATE_OPEN; + connection_init_timeval(conn); connection_watch_events(conn, POLLIN); return 0; diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 7badd19c93..2542e5f4b2 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1,3 +1,6 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ #include "or.h" @@ -17,7 +20,7 @@ int connection_or_process_inbuf(connection_t *conn) { return -1; } - log(LOG_DEBUG,"connection_or_process_inbuf(): state %d.",conn->state); +// log(LOG_DEBUG,"connection_or_process_inbuf(): state %d.",conn->state); switch(conn->state) { case OR_CONN_STATE_CLIENT_AUTH_WAIT: @@ -84,6 +87,7 @@ int connection_or_finished_flushing(connection_t *conn) { log(LOG_DEBUG,"connection_or_finished_flushing(): client finished sending nonce."); conn_or_init_crypto(conn); conn->state = OR_CONN_STATE_OPEN; + connection_init_timeval(conn); connection_watch_events(conn, POLLIN); return 0; case OR_CONN_STATE_SERVER_SENDING_AUTH: @@ -148,7 +152,7 @@ connection_t *connection_or_connect(routerinfo_t *router, RSA *prkey, struct soc /* set up conn so it's got all the data we need to remember */ conn->addr = router->addr, conn->port = router->or_port; /* NOTE we store or_port here always */ conn->prkey = prkey; - conn->min = router->min, conn->max = router->max; + conn->bandwidth = router->min; /* kludge, should make a router->bandwidth and use that */ conn->pkey = router->pkey; conn->address = strdup(router->address); memcpy(&conn->local,local,sizeof(struct sockaddr_in)); @@ -316,6 +320,7 @@ int or_handshake_op_finished_sending_keys(connection_t *conn) { conn_or_init_crypto(conn); conn->state = OR_CONN_STATE_OPEN; + connection_init_timeval(conn); connection_watch_events(conn, POLLIN); /* give it a default, tho the ap_handshake call may change it */ ap_handshake_n_conn_open(conn); /* send the pending onion */ return 0; @@ -367,8 +372,7 @@ int or_handshake_client_send_auth(connection_t *conn) { char buf[44]; char cipher[128]; - if (!conn) - return -1; + assert(conn); /* generate random keys */ if(!RAND_bytes(conn->f_session_key,8) || @@ -385,11 +389,9 @@ int or_handshake_client_send_auth(connection_t *conn) { memcpy(buf+10, (void *)&conn->port, 2); /* remote port */ memcpy(buf+12,conn->f_session_key,8); /* keys */ memcpy(buf+20,conn->b_session_key,8); - *((uint32_t *)(buf+28)) = htonl(conn->min); /* min link utilisation */ - *((uint32_t *)(buf+32)) = htonl(conn->max); /* maximum link utilisation */ + *((uint32_t *)(buf+28)) = htonl(conn->bandwidth); /* max link utilisation */ log(LOG_DEBUG,"or_handshake_client_send_auth() : Generated first authentication message."); - /* encrypt message */ retval = RSA_public_encrypt(36,buf,cipher,conn->pkey,RSA_PKCS1_PADDING); if (retval == -1) /* error */ @@ -429,7 +431,7 @@ int or_handshake_client_send_auth(connection_t *conn) { int or_handshake_client_process_auth(connection_t *conn) { char buf[128]; /* only 44 of this is expected to be used */ char cipher[128]; - uint32_t min,max; + uint32_t bandwidth; int retval; assert(conn); @@ -474,15 +476,10 @@ int or_handshake_client_process_auth(connection_t *conn) { log(LOG_DEBUG,"or_handshake_client_process_auth() : Response valid."); /* update link info */ - min = *(uint32_t *)(buf+28); - max = *(uint32_t *)(buf+32); - min = ntohl(min); - max = ntohl(max); + bandwidth = ntohl(*(uint32_t *)(buf+28)); - if (conn->min > min) - conn->min = min; - if (conn->max > max) - conn->max = max; + if (conn->bandwidth > bandwidth) + conn->bandwidth = bandwidth; /* reply is just local addr/port, remote addr/port, nonce */ memcpy(buf+12, buf+36, 8); @@ -519,6 +516,7 @@ int or_handshake_client_process_auth(connection_t *conn) { log(LOG_DEBUG,"or_handshake_client_process_auth(): Finished sending nonce."); conn_or_init_crypto(conn); conn->state = OR_CONN_STATE_OPEN; + connection_init_timeval(conn); connection_watch_events(conn, POLLIN); return connection_process_inbuf(conn); /* process the rest of the inbuf */ @@ -539,7 +537,7 @@ int or_handshake_server_process_auth(connection_t *conn) { uint32_t addr; uint16_t port; - uint32_t min,max; + uint32_t bandwidth; routerinfo_t *router; assert(conn); @@ -593,18 +591,12 @@ int or_handshake_server_process_auth(connection_t *conn) { memcpy(conn->f_session_key,buf+20,8); /* update link info */ - min = *(uint32_t *)(buf+28); - max = *(uint32_t *)(buf+32); - min = ntohl(min); - max = ntohl(max); + bandwidth = ntohl(*(uint32_t *)(buf+28)); - conn->min = router->min; - conn->max = router->max; + conn->bandwidth = router->min; /* FIXME, should make a router->bandwidth and use that */ - if (conn->min > min) - conn->min = min; - if (conn->max > max) - conn->max = max; + if (conn->bandwidth > bandwidth) + conn->bandwidth = bandwidth; /* copy all relevant info to conn */ conn->addr = router->addr, conn->port = router->or_port; @@ -622,8 +614,7 @@ int or_handshake_server_process_auth(connection_t *conn) { /* generate message */ memcpy(buf+36,conn->nonce,8); /* append the nonce to the end of the message */ - *(uint32_t *)(buf+28) = htonl(conn->min); /* send min link utilisation */ - *(uint32_t *)(buf+32) = htonl(conn->max); /* send max link utilisation */ + *(uint32_t *)(buf+28) = htonl(conn->bandwidth); /* send max link utilisation */ /* encrypt message */ retval = RSA_public_encrypt(44,buf,cipher,conn->pkey,RSA_PKCS1_PADDING); @@ -709,6 +700,7 @@ int or_handshake_server_process_nonce(connection_t *conn) { conn_or_init_crypto(conn); conn->state = OR_CONN_STATE_OPEN; + connection_init_timeval(conn); connection_watch_events(conn, POLLIN); return connection_process_inbuf(conn); /* process the rest of the inbuf */ diff --git a/src/or/main.c b/src/or/main.c index b40ef9ee25..6a7643a638 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1,9 +1,12 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ #include "or.h" /********* START VARIABLES **********/ -static or_options_t options; /* command-line and config-file options */ +or_options_t options; /* command-line and config-file options */ int global_role; static connection_t *connection_array[MAXCONNECTIONS] = @@ -89,13 +92,19 @@ int connection_remove(connection_t *conn) { } connection_t *connection_twin_get_by_addr_port(uint32_t addr, uint16_t port) { + /* Find a connection to the router described by addr and port, + * or alternately any router which knows its key. + * This connection *must* be in 'open' state. + * If not, return NULL. + */ int i; connection_t *conn; /* first check if it's there exactly */ conn = connection_exact_get_by_addr_port(addr,port); - if(conn) + if(conn && connection_state_is_open(conn)) { return conn; + } /* now check if any of the other open connections are a twin for this one */ @@ -185,6 +194,21 @@ void connection_watch_events(connection_t *conn, short events) { poll_array[conn->poll_index].events = events; } +void connection_stop_reading(connection_t *conn) { + + assert(conn && conn->poll_index < nfds); + + if(poll_array[conn->poll_index].events & POLLIN) + poll_array[conn->poll_index].events -= POLLIN; +} + +void connection_start_reading(connection_t *conn) { + + assert(conn && conn->poll_index < nfds); + + poll_array[conn->poll_index].events |= POLLIN; +} + void check_conn_read(int i) { int retval; connection_t *conn; @@ -193,7 +217,7 @@ void check_conn_read(int i) { conn = connection_array[i]; assert(conn); - log(LOG_DEBUG,"check_conn_read(): socket %d has something to read.",conn->s); +// log(LOG_DEBUG,"check_conn_read(): socket %d has something to read.",conn->s); if (conn->type == CONN_TYPE_OP_LISTENER) { retval = connection_op_handle_listener_read(conn); @@ -206,10 +230,14 @@ void check_conn_read(int i) { retval = connection_read_to_buf(conn); if (retval >= 0) { /* all still well */ retval = connection_process_inbuf(conn); - log(LOG_DEBUG,"check_conn_read(): connection_process_inbuf returned %d.",retval); +// log(LOG_DEBUG,"check_conn_read(): connection_process_inbuf returned %d.",retval); + if(retval >= 0 && !connection_state_is_open(conn) && conn->receiver_bucket == 0) { + log(LOG_DEBUG,"check_conn_read(): receiver bucket reached 0 before handshake finished. Closing."); + retval = -1; + } } } - + if(retval < 0) { /* this connection is broken. remove it */ log(LOG_DEBUG,"check_conn_read(): Connection broken, removing."); connection_remove(conn); @@ -275,15 +303,93 @@ void check_conn_marked(int i) { } } +int prepare_for_poll(int *timeout) { + int i; + int need_to_refill_buckets = 0; + connection_t *conn = NULL; + connection_t *tmpconn; + struct timeval now, soonest; + static int current_second = 0; /* from previous calls to gettimeofday */ + int ms_until_conn; + + *timeout = -1; /* set it to never timeout, possibly overridden below */ + + /* first check if we need to refill buckets */ + for(i=0;i<nfds;i++) { + if(connection_receiver_bucket_should_increase(connection_array[i])) { + need_to_refill_buckets = 1; + break; + } + } + + if(gettimeofday(&now,NULL) < 0) + return -1; + + if(need_to_refill_buckets) { + if(now.tv_sec > current_second) { /* the second has already rolled over! */ + log(LOG_DEBUG,"prepare_for_poll(): The second has rolled over, immediately refilling."); + increment_receiver_buckets(); + current_second = now.tv_sec; /* remember which second it is, for next time */ + } + *timeout = 1000 - (now.tv_usec / 1000); /* how many milliseconds til the next second? */ +// log(LOG_DEBUG,"prepare_for_poll(): %d milliseconds til next second.",*timeout); + } + + if(options.LinkPadding) { + /* now check which conn wants to speak soonest */ + for(i=0;i<nfds;i++) { + tmpconn = connection_array[i]; + if(tmpconn->type != CONN_TYPE_OR && tmpconn->type != CONN_TYPE_OP) + continue; /* this conn type doesn't send cells */ + if(!connection_state_is_open(tmpconn)) + continue; /* only conns in state 'open' have a valid send_timeval */ + while(tv_cmp(&tmpconn->send_timeval,&now) <= 0) { /* send_timeval has already passed, let it send a cell */ + log(LOG_DEBUG,"prepare_for_poll(): doing backlogged connection_send_cell on socket %d (%d ms old)",tmpconn->s, + (now.tv_sec - tmpconn->send_timeval.tv_sec)*1000 + + (now.tv_usec - tmpconn->send_timeval.tv_usec)/1000 + ); + connection_send_cell(tmpconn); + } + if(!conn || tv_cmp(&tmpconn->send_timeval, &soonest) < 0) { /* this is the best choice so far */ +// log(LOG_DEBUG,"prepare_for_poll(): chose socket %d as best connection so far",tmpconn->s); + conn = tmpconn; + soonest.tv_sec = conn->send_timeval.tv_sec; + soonest.tv_usec = conn->send_timeval.tv_usec; + } + } + + if(conn) { /* we might want to set *timeout sooner */ + ms_until_conn = (soonest.tv_sec - now.tv_sec)*1000 + + (soonest.tv_usec - now.tv_usec)/1000; +// log(LOG_DEBUG,"prepare_for_poll(): conn %d times out in %d ms.",conn->s, ms_until_conn); + if(*timeout == -1 || ms_until_conn < *timeout) { /* use the new one */ +// log(LOG_DEBUG,"prepare_for_poll(): conn %d soonest, in %d ms.",conn->s,ms_until_conn); + *timeout = ms_until_conn; + } + } + } + + return 0; +} + +void increment_receiver_buckets(void) { + int i; + + for(i=0;i<nfds;i++) + connection_increment_receiver_bucket(connection_array[i]); +} + int do_main_loop(void) { int i; + int timeout; + int poll_result; /* load the routers file */ router_array = getrouters(options.RouterFile,&rarray_len, options.ORPort); if (!router_array) { log(LOG_ERR,"Error loading router list."); - exit(1); + return -1; } /* load the private key */ @@ -291,29 +397,55 @@ int do_main_loop(void) { if (!prkey) { log(LOG_ERR,"Error loading private key."); - exit(1); + return -1; } log(LOG_DEBUG,"core : Loaded private key of size %u bytes.",RSA_size(prkey)); /* start-up the necessary connections based on global_role. This is where we * try to connect to all the other ORs, and start the listeners */ - retry_all_connections(options.GlobalRole, router_array, rarray_len, prkey, + retry_all_connections(options.Role, router_array, rarray_len, prkey, options.ORPort, options.OPPort, options.APPort); for(;;) { - poll(poll_array, nfds, -1); /* poll until we have an event */ + if(prepare_for_poll(&timeout) < 0) { + log(LOG_DEBUG,"do_main_loop(): prepare_for_poll failed, exiting."); + return -1; + } + /* now timeout is the value we'll hand to poll. It's either -1, meaning + * don't timeout, else it indicates the soonest event (either the + * one-second rollover for refilling receiver buckets, or the soonest + * conn that needs to send a cell) + */ + + /* if the timeout is less than 10, set it to 10 */ + if(timeout >= 0 && timeout < 10) + timeout = 10; + + /* poll until we have an event, or it's time to do something */ + poll_result = poll(poll_array, nfds, timeout); + + if(poll_result < 0) { + log(LOG_ERR,"do_main_loop(): poll failed."); + if(errno != EINTR) /* let the program survive things like ^z */ + return -1; + } - /* do all the reads first, so we can detect closed sockets */ - for(i=0;i<nfds;i++) - check_conn_read(i); /* this also blows away broken connections */ + if(poll_result > 0) { /* we have at least one connection to deal with */ + /* do all the reads first, so we can detect closed sockets */ + for(i=0;i<nfds;i++) + check_conn_read(i); /* this also blows away broken connections */ - /* then do the writes */ - for(i=0;i<nfds;i++) - check_conn_write(i); + /* then do the writes */ + for(i=0;i<nfds;i++) + check_conn_write(i); - /* any of the conns need to be closed now? */ - for(i=0;i<nfds;i++) - check_conn_marked(i); + /* any of the conns need to be closed now? */ + for(i=0;i<nfds;i++) + check_conn_marked(i); + } + /* refilling buckets and sending cells happens at the beginning of the + * next iteration of the loop, inside prepare_for_poll() + */ } } @@ -332,7 +464,7 @@ int main(int argc, char *argv[]) { if ( getoptions(argc,argv,&options) ) exit(1); log(options.loglevel,NULL); /* assign logging severity level from options */ - global_role = options.GlobalRole; /* assign global_role from options. FIX: remove from global namespace later. */ + global_role = options.Role; /* assign global_role from options. FIX: remove from global namespace later. */ ERR_load_crypto_strings(); retval = do_main_loop(); diff --git a/src/or/onion.c b/src/or/onion.c index d659272f27..506eb25e9b 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -1,3 +1,6 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ #include "or.h" diff --git a/src/or/or.h b/src/or/or.h index c65195049f..1db2e5462e 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1,4 +1,5 @@ -/* Copyright (c) 2002 Roger Dingledine. See LICENSE for licensing information */ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ /* $Id$ */ #ifndef __OR_H @@ -38,8 +39,8 @@ #define MAXCONNECTIONS 200 /* upper bound on max connections. can be overridden by config file */ -#define MAX_BUF_SIZE (64*1024) -#define DEFAULT_BANDWIDTH_OP 1 +#define MAX_BUF_SIZE (640*1024) +#define DEFAULT_BANDWIDTH_OP 1024 #define ACI_TYPE_LOWER 0 #define ACI_TYPE_HIGHER 1 @@ -130,16 +131,19 @@ typedef struct int inbuf_reached_eof; char *outbuf; - size_t outbuflen; - size_t outbuf_datalen; + size_t outbuflen; /* how many bytes are allocated for the outbuf? */ + size_t outbuf_flushlen; /* how much data should we try to flush from the outbuf? */ + size_t outbuf_datalen; /* how much data is there total on the outbuf? */ // uint16_t aci; /* anonymous connection identifier */ /* used by OR and OP: */ uint32_t bandwidth; /* connection bandwidth */ - int window_sent; /* how many cells can i still send? */ - int window_received; /* how many cells do i still expect to receive? */ + int receiver_bucket; /* when this hits 0, stop receiving. Every second we + * add 'bandwidth' to this, capping it at 10*bandwidth. + */ + struct timeval send_timeval; /* for determining when to send the next cell */ /* link encryption */ unsigned char f_session_key[8]; @@ -169,9 +173,11 @@ typedef struct RSA *prkey; struct sockaddr_in local; - /* link info */ +#if 0 /* obsolete, we now use conn->bandwidth */ + /* link info */ uint32_t min; uint32_t max; +#endif char *address; /* strdup into this, because free_connection frees it */ RSA *pkey; /* public RSA key for the other side */ @@ -295,7 +301,8 @@ typedef struct int APPort; int MaxConn; int TrafficShaping; - int GlobalRole; + int LinkPadding; + int Role; int loglevel; } or_options_t; @@ -303,24 +310,16 @@ typedef struct /* all the function prototypes go here */ -/********************************* args.c ***************************/ - -/* print help*/ -void print_usage(); - -/* get command-line arguments */ -int getargs(int argc,char *argv[], char *args,char **conf_filename, int *loglevel); - /********************************* buffers.c ***************************/ int buf_new(char **buf, size_t *buflen, size_t *buf_datalen); void buf_free(char *buf); -int read_to_buf(int s, char **buf, size_t *buflen, size_t *buf_datalen, int *reached_eof); +int read_to_buf(int s, int at_most, char **buf, size_t *buflen, size_t *buf_datalen, int *reached_eof); /* grab from s, put onto buf, return how many bytes read */ -int flush_buf(int s, char **buf, size_t *buflen, size_t *buf_datalen); +int flush_buf(int s, char **buf, size_t *buflen, size_t *buf_flushlen, size_t *buf_datalen); /* push from buf onto s * then memmove to front of buf * return -1 or how many bytes remain on the buf */ @@ -384,6 +383,8 @@ int getoptions(int argc, char **argv, or_options_t *options); /********************************* connection.c ***************************/ +int tv_cmp(struct timeval *a, struct timeval *b); + connection_t *connection_new(int type); void connection_free(connection_t *conn); @@ -404,6 +405,16 @@ int connection_fetch_from_buf(char *string, int len, connection_t *conn); int connection_flush_buf(connection_t *conn); int connection_write_to_buf(char *string, int len, connection_t *conn); +void connection_send_cell(connection_t *conn); + +int connection_receiver_bucket_should_increase(connection_t *conn); +void connection_increment_receiver_bucket (connection_t *conn); + +void connection_increment_send_timeval(connection_t *conn); +void connection_init_timeval(connection_t *conn); + +int connection_state_is_open(connection_t *conn); + int connection_send_destroy(aci_t aci, connection_t *conn); int connection_encrypt_cell_header(cell_t *cellp, connection_t *conn); int connection_write_cell_to_buf(cell_t *cellp, connection_t *conn); @@ -500,12 +511,15 @@ routerinfo_t *router_get_first_in_route(unsigned int *route, size_t routelen); connection_t *connect_to_router_as_op(routerinfo_t *router); void connection_watch_events(connection_t *conn, short events); +void connection_stop_reading(connection_t *conn); +void connection_start_reading(connection_t *conn); void check_conn_read(int i); void check_conn_marked(int i); void check_conn_write(int i); -void check_conn_hup(int i); +int prepare_for_poll(int *timeout); +void increment_receiver_buckets(void); int do_main_loop(void); diff --git a/src/or/routers.c b/src/or/routers.c index 52778a902d..8af6fd185b 100644 --- a/src/or/routers.c +++ b/src/or/routers.c @@ -1,3 +1,7 @@ +/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + /** * routers.c * Routines for loading the list of routers and their public RSA keys. |