aboutsummaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/Makefile18
-rw-r--r--src/or/args.c99
-rw-r--r--src/or/buffers.c174
-rw-r--r--src/or/cell.c23
-rw-r--r--src/or/circuit.c319
-rw-r--r--src/or/command.c212
-rw-r--r--src/or/config.c49
-rw-r--r--src/or/connection.c362
-rw-r--r--src/or/connection_app.c212
-rw-r--r--src/or/connection_op.c130
-rw-r--r--src/or/connection_or.c580
-rw-r--r--src/or/main.c406
-rw-r--r--src/or/onion.c62
-rw-r--r--src/or/or.h411
-rw-r--r--src/or/routers.c365
15 files changed, 3422 insertions, 0 deletions
diff --git a/src/or/Makefile b/src/or/Makefile
new file mode 100644
index 000000000..edf2398b4
--- /dev/null
+++ b/src/or/Makefile
@@ -0,0 +1,18 @@
+SRC=args.c buffers.c cell.c circuit.c command.c connection.c connection_app.c connection_op.c connection_or.c config.c main.c onion.c routers.c
+OBJ=${SRC:.c=.o}
+PROGS=or
+LIB=
+LIBS=
+INCLUDE = -I/usr/local/ssl/include
+
+CFLAGS= $(INCLUDE) -Wall -Wpointer-arith -ggdb
+LDFLAGS = $(LIB) $(LIBS)
+
+all: ${OBJ} ${PROGS}
+
+or: ${OBJ}
+ gcc -o or $(CFLAGS) *.o ../common/*.o -lcrypto
+
+clean:
+ rm -f *.o ${PROGS}
+
diff --git a/src/or/args.c b/src/or/args.c
new file mode 100644
index 000000000..fc3837ca8
--- /dev/null
+++ b/src/or/args.c
@@ -0,0 +1,99 @@
+/**
+ * args.c
+ * Routines for processing command-line arguments.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1 2002/06/26 22:45:50 arma
+ * Initial revision
+ *
+ * Revision 1.3 2002/01/27 00:42:50 mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.2 2002/01/04 10:05:28 badbytes
+ * Completed.
+ *
+ * Revision 1.1 2002/01/03 10:23:43 badbytes
+ * Code based on that in op. Needs to be modified.
+ */
+
+#include "or.h"
+
+/* prints help on using or */
+void print_usage()
+{
+ char *program = "or";
+ printf("\n%s - Onion Router.\nUsage : %s -f config [-l loglevel -h]\n-h : display this help\n-f config : config file\n-l loglevel : logging threshold; one of alert|crit|err|warning|notice|info|debug\n\n", program,program);
+}
+
+
+/* get command-line arguments */
+int getargs(int argc,char *argv[], char *args, char **conf_filename, int *loglevel)
+{
+ char c; /* next option character */
+ int gotf=0;
+
+ if ((!args) || (!conf_filename) || (!loglevel))
+ return -1;
+
+ while ((c = getopt(argc,argv,args)) != -1)
+ {
+ switch(c)
+ {
+ case 'f': /* config file */
+ *conf_filename = optarg;
+ gotf=1;
+ break;
+ case 'h':
+ print_usage(argv[0]);
+ exit(0);
+ case 'l':
+ if (!strcmp(optarg,"emerg"))
+ *loglevel = LOG_EMERG;
+ else if (!strcmp(optarg,"alert"))
+ *loglevel = LOG_ALERT;
+ else if (!strcmp(optarg,"crit"))
+ *loglevel = LOG_CRIT;
+ else if (!strcmp(optarg,"err"))
+ *loglevel = LOG_ERR;
+ else if (!strcmp(optarg,"warning"))
+ *loglevel = LOG_WARNING;
+ else if (!strcmp(optarg,"notice"))
+ *loglevel = LOG_NOTICE;
+ else if (!strcmp(optarg,"info"))
+ *loglevel = LOG_INFO;
+ else if (!strcmp(optarg,"debug"))
+ *loglevel = LOG_DEBUG;
+ else
+ {
+ log(LOG_ERR,"Error : argument to -l must be one of alert|crit|err|warning|notice|info|debug.");
+ print_usage(argv[0]);
+ return -1;
+ }
+ break;
+ case '?':
+ if (isprint(c))
+ log(LOG_ERR,"Missing argument or unknown option '-%c'. See help (-h).",optopt);
+ else
+ log(LOG_ERR,"Unknown option character 'x%x'. See help (-h).",optopt);
+ print_usage(argv[0]);
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ /* the -f option is mandatory */
+ if (!gotf)
+ {
+ log(LOG_ERR,"You must specify a config file with the -f option. See help (-h).");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/or/buffers.c b/src/or/buffers.c
new file mode 100644
index 000000000..6900ca300
--- /dev/null
+++ b/src/or/buffers.c
@@ -0,0 +1,174 @@
+
+/* buffers.c */
+
+#include "or.h"
+
+int buf_new(char **pbuf, size_t *pbuflen, size_t *pbuf_datalen) {
+
+ if (!pbuf || !pbuflen || !pbuf_datalen) /* invalid parameters */
+ return -1;
+
+ *pbuf = (char *)malloc(MAX_BUF_SIZE);
+ if(!*pbuf)
+ return -1;
+ memset(*pbuf,0,MAX_BUF_SIZE);
+ *pbuflen = MAX_BUF_SIZE;
+ *pbuf_datalen = 0;
+
+ return 0;
+}
+
+int buf_free(char *buf) {
+
+ free(buf);
+
+ return 0;
+}
+
+int read_to_buf(int s, char **pbuf, size_t *pbuflen, size_t *pbuf_datalen, int *preached_eof) {
+
+ /* grab from s, put onto buf, return how many bytes read */
+
+ int read_result;
+ char *buf;
+ size_t buflen;
+ size_t buf_datalen;
+
+ if (!pbuf || !pbuflen || !pbuf_datalen || !preached_eof) /* invalid parameters */
+ return -1 ;
+
+ if(s<0) {
+ log(LOG_DEBUG,"read_to_buf() received negative socket %d.",s);
+ return -1;
+ }
+
+ /* this is the point where you would grow the buffer, if you want to */
+ buf = *pbuf, buflen = *pbuflen, buf_datalen = *pbuf_datalen;
+
+ if (!buf) /* invalid parameter */
+ return -1;
+
+ read_result = read(s, buf+buf_datalen, buflen - buf_datalen);
+ if (read_result < 0) {
+ if(errno!=EAGAIN) { /* it's a real error */
+ return -1;
+ }
+ return 0;
+ } else if (read_result == 0) {
+ log(LOG_DEBUG,"read_to_buf(): Encountered eof");
+ *preached_eof = 1;
+ return 0;
+ } else { /* we read some bytes */
+ *pbuf_datalen = buf_datalen + read_result;
+ log(LOG_DEBUG,"read_to_buf(): Read %d bytes. %d on inbuf.",read_result, *pbuf_datalen);
+ return read_result;
+ }
+
+}
+
+int flush_buf(int s, char **pbuf, size_t *pbuflen, size_t *pbuf_datalen) {
+
+ /* push from buf onto s
+ * then memmove to front of buf
+ * return -1 or how many bytes remain on the buf */
+
+ int write_result;
+ char *buf;
+ size_t buflen;
+ size_t buf_datalen;
+
+ if (!pbuf || !pbuflen || !pbuf_datalen) /* invalid parameters */
+ return -1;
+
+ if(s<0) {
+ log(LOG_DEBUG,"flush_buf() received negative socket %d.",s);
+ return -1;
+ }
+
+
+ if(*pbuf_datalen == 0) /* nothing to flush */
+ return 0;
+
+ /* this is the point where you would grow the buffer, if you want to */
+ buf = *pbuf, buflen = *pbuflen, buf_datalen = *pbuf_datalen;
+
+ if (!buf) /* invalid parameter */
+ return -1;
+
+ write_result = write(s, buf, buf_datalen);
+ if (write_result < 0) {
+ if(errno!=EAGAIN) { /* it's a real error */
+ return -1;
+ }
+ log(LOG_DEBUG,"flush_buf(): write() would block, returning.");
+ return 0;
+ } else {
+ *pbuf_datalen -= write_result;
+ memmove(buf, buf+write_result, *pbuf_datalen);
+ log(LOG_DEBUG,"flush_buf(): flushed %d bytes, %d remain.",write_result,*pbuf_datalen);
+ return *pbuf_datalen;
+ }
+
+}
+
+int write_to_buf(char *string, size_t string_len,
+ char **pbuf, size_t *pbuflen, size_t *pbuf_datalen) {
+
+ /* append string to buf (growing as needed, return -1 if "too big")
+ * return total number of bytes on the buf
+ */
+
+ char *buf;
+ size_t buflen;
+ size_t buf_datalen;
+
+ if (!string || !pbuf || !pbuflen || !pbuf_datalen) /* invalid parameters */
+ return -1;
+
+ /* this is the point where you would grow the buffer, if you want to */
+ buf = *pbuf, buflen = *pbuflen, buf_datalen = *pbuf_datalen;
+
+ if (!buf) /* invalid parameter */
+ return -1;
+
+ if (string_len + buf_datalen > buflen) { /* we're out of luck */
+ log(LOG_DEBUG, "write_to_buf(): buflen too small. Time to implement growing dynamic bufs.");
+ return -1;
+ }
+
+ memcpy(buf+buf_datalen, string, string_len);
+ *pbuf_datalen += string_len;
+ log(LOG_DEBUG,"write_to_buf(): added %d bytes to buf (now %d total).",string_len, *pbuf_datalen);
+ return *pbuf_datalen;
+
+}
+
+int fetch_from_buf(char *string, size_t string_len,
+ char **pbuf, size_t *pbuflen, size_t *pbuf_datalen) {
+
+ /* if there is string_len bytes in buf, write them onto string,
+ * then memmove buf back (that is, remove them from buf) */
+
+ char *buf;
+ size_t buflen;
+ size_t buf_datalen;
+
+ if (!string || !pbuf || !pbuflen || !pbuf_datalen) /* invalid parameters */
+ return -1;
+
+ /* this is the point where you would grow the buffer, if you want to */
+ buf = *pbuf, buflen = *pbuflen, buf_datalen = *pbuf_datalen;
+
+ if (!buf) /* invalid parameter */
+ return -1;
+
+ if(string_len > buf_datalen) /* we want too much. sorry. */
+ return -1;
+
+ memcpy(string,buf,string_len);
+ *pbuf_datalen -= string_len;
+ memmove(buf, buf+string_len, *pbuf_datalen);
+ return *pbuf_datalen;
+
+}
+
diff --git a/src/or/cell.c b/src/or/cell.c
new file mode 100644
index 000000000..ef53a9eae
--- /dev/null
+++ b/src/or/cell.c
@@ -0,0 +1,23 @@
+
+#include "or.h"
+
+int check_sane_cell(cell_t *cell) {
+
+ if(!cell)
+ return -1;
+
+ if(cell->aci == 0) {
+ log(LOG_DEBUG,"check_sane_cell(): Cell has aci=0. Dropping.");
+ return -1;
+ }
+
+#if 0 /* actually, the length is sometimes encrypted. so it's ok. */
+ if(cell->length > 120) {
+ log(LOG_DEBUG,"check_sane_cell(): Cell claims to have payload length %d. Dropping.",cell->length);
+ return -1;
+ }
+#endif
+
+ return 0; /* looks good */
+}
+
diff --git a/src/or/circuit.c b/src/or/circuit.c
new file mode 100644
index 000000000..b910fd351
--- /dev/null
+++ b/src/or/circuit.c
@@ -0,0 +1,319 @@
+
+#include "or.h"
+
+/********* START VARIABLES **********/
+
+circuit_t *global_circuitlist=NULL;
+
+/********* END VARIABLES ************/
+
+void circuit_add(circuit_t *circ) {
+
+ if(!global_circuitlist) { /* first one */
+ global_circuitlist = circ;
+ circ->next = NULL;
+ } else {
+ circ->next = global_circuitlist;
+ global_circuitlist = circ;
+ }
+
+}
+
+void circuit_remove(circuit_t *circ) {
+ circuit_t *tmpcirc;
+
+ if(!circ || !global_circuitlist)
+ return;
+
+ if(global_circuitlist == circ) {
+ global_circuitlist = global_circuitlist->next;
+ return;
+ }
+
+ for(tmpcirc = global_circuitlist;tmpcirc->next;tmpcirc = tmpcirc->next) {
+ if(tmpcirc->next == circ) {
+ tmpcirc->next = circ->next;
+ return;
+ }
+ }
+
+}
+
+circuit_t *circuit_new(aci_t p_aci, connection_t *p_conn) {
+
+ circuit_t *circ;
+
+ circ = (circuit_t *)malloc(sizeof(circuit_t));
+ if(!circ)
+ return NULL;
+ memset(circ,0,sizeof(circuit_t)); /* zero it out */
+
+ circ->p_aci = p_aci;
+ circ->p_conn = p_conn;
+
+ circ->state = CIRCUIT_STATE_OPEN_WAIT;
+ circ->onion = NULL;
+ circ->onionlen=0;
+ circ->recvlen=0;
+
+ /* ACIs */
+ circ->p_aci = p_aci;
+ circ->n_aci = 0; /* we need to have identified the next hop to choose a correct ACI */
+
+ circuit_add(circ);
+
+ return circ;
+}
+
+void circuit_free(circuit_t *circ) {
+
+ EVP_CIPHER_CTX_cleanup(&circ->n_ctx);
+ EVP_CIPHER_CTX_cleanup(&circ->p_ctx);
+
+ if(circ->onion)
+ free(circ->onion);
+
+ free(circ);
+
+}
+
+aci_t get_unique_aci_by_addr_port(uint32_t addr, uint16_t port, int aci_type) {
+ aci_t test_aci;
+ connection_t *conn;
+
+ log(LOG_DEBUG,"get_unique_aci_by_addr_port() trying to get a unique aci");
+
+ RAND_pseudo_bytes((unsigned char *)&test_aci, 2);
+
+ if(aci_type == ACI_TYPE_LOWER)
+ test_aci &= htons(0x00FF);
+ if(aci_type == ACI_TYPE_HIGHER)
+ test_aci &= htons(0xFF00);
+
+ if(test_aci == 0)
+ return get_unique_aci_by_addr_port(addr, port, aci_type); /* try again */
+
+ conn = connection_get_by_addr_port(addr,port);
+ if(!conn) /* there can't be a conflict -- no connection of that sort yet */
+ return test_aci;
+
+ if(circuit_get_by_aci_conn(test_aci, conn))
+ return get_unique_aci_by_addr_port(addr, port, aci_type); /* try again */
+
+ return test_aci;
+
+}
+
+int circuit_init(circuit_t *circ, int aci_type) {
+ onion_layer_t *ol;
+ int retval = 0;
+ unsigned char digest1[20];
+ unsigned char digest2[20];
+
+ if (!circ)
+ return -1;
+
+ ol = (onion_layer_t *)circ->onion;
+ if (!ol)
+ return -1;
+
+ log(LOG_DEBUG,"circuit_init(): starting");
+ circ->n_addr = ol->addr;
+ circ->n_port = ol->port;
+ log(LOG_DEBUG,"circuit_init(): Set port to %u.",ntohs(ol->port));
+ circ->p_f = ol->backf;
+ log(LOG_DEBUG,"circuit_init(): Set BACKF to %u.",ol->backf);
+ circ->n_f = ol->forwf;
+ log(LOG_DEBUG,"circuit_init(): Set FORWF to %u.",ol->forwf);
+ circ->state = CIRCUIT_STATE_OPEN;
+
+ log(LOG_DEBUG,"circuit_init(): aci_type = %u.",aci_type);
+
+ circ->n_aci = get_unique_aci_by_addr_port(circ->n_addr, circ->n_port, aci_type);
+
+ log(LOG_DEBUG,"circuit_init(): Chosen ACI %u.",circ->n_aci);
+
+ /* keys */
+ SHA1(ol->keyseed,16,digest1);
+ SHA1(digest1,20,digest2);
+ SHA1(digest2,20,digest1);
+ memcpy(circ->p_key,digest2,16);
+ memcpy(circ->n_key,digest1,16);
+ log(LOG_DEBUG,"circuit_init(): Computed keys.");
+
+ /* set IVs to zero */
+ memset(circ->n_iv,0,16);
+ memset(circ->p_iv,0,16);
+
+ /* initialize cipher context */
+ EVP_CIPHER_CTX_init(&circ->n_ctx);
+ EVP_CIPHER_CTX_init(&circ->p_ctx);
+
+ /* initialize crypto engines */
+ switch(circ->p_f)
+ {
+ case ONION_CIPHER_DES :
+ retval = EVP_EncryptInit(&circ->p_ctx, EVP_des_ofb(), circ->p_key, circ->p_iv);
+ break;
+ case ONION_CIPHER_RC4 :
+ retval = EVP_EncryptInit(&circ->p_ctx, EVP_rc4(), circ->p_key,circ->p_iv);
+ break;
+ case ONION_CIPHER_IDENTITY :
+ retval = EVP_EncryptInit(&circ->p_ctx, EVP_enc_null(), circ->p_key, circ->p_iv);
+ break;
+ default :
+ log(LOG_ERR,"Onion contains unrecognized cipher(%u) for ACI : %u.",circ->p_f,circ->n_aci);
+ return -1;
+ break;
+ }
+
+ if (!retval) /* EVP_EncryptInit() error */
+ {
+ log(LOG_ERR,"Cipher initialization failed (ACI %u).",circ->n_aci);
+ EVP_CIPHER_CTX_cleanup(&circ->n_ctx);
+ EVP_CIPHER_CTX_cleanup(&circ->p_ctx);
+ return -1;
+ }
+ switch(circ->n_f)
+ {
+ case ONION_CIPHER_DES :
+ retval = EVP_DecryptInit(&circ->n_ctx, EVP_des_ofb(), circ->n_key, circ->n_iv);
+ break;
+ case ONION_CIPHER_RC4 :
+ retval = EVP_DecryptInit(&circ->n_ctx, EVP_rc4(), circ->n_key,circ->n_iv);
+ break;
+ case ONION_CIPHER_IDENTITY :
+ retval = EVP_DecryptInit(&circ->n_ctx, EVP_enc_null(), circ->n_key, circ->n_iv);
+ break;
+ default :
+ log(LOG_ERR,"Onion contains unrecognized cipher for ACI : %u.",circ->n_aci);
+ return -1;
+ break;
+ }
+ if (!retval) /* EVP_EncryptInit() error */
+ {
+ log(LOG_ERR,"Cipher initialization failed (ACI %u).",circ->n_aci);
+ EVP_CIPHER_CTX_cleanup(&circ->n_ctx);
+ EVP_CIPHER_CTX_cleanup(&circ->p_ctx);
+ return -1;
+ }
+ log(LOG_DEBUG,"circuit_init(): Cipher initialization complete.");
+
+ circ->expire = ol->expire;
+
+ return 0;
+}
+
+circuit_t *circuit_get_by_aci_conn(aci_t aci, connection_t *conn) {
+ circuit_t *circ;
+
+ for(circ=global_circuitlist;circ;circ = circ->next) {
+ if(circ->p_conn == conn && circ->p_aci == aci)
+ return circ;
+ if(circ->n_conn == conn && circ->n_aci == aci)
+ return circ;
+ }
+ return NULL;
+}
+
+circuit_t *circuit_get_by_conn(connection_t *conn) {
+ circuit_t *circ;
+
+ for(circ=global_circuitlist;circ;circ = circ->next) {
+ if(circ->p_conn == conn)
+ return circ;
+ if(circ->n_conn == conn)
+ return circ;
+ }
+ return NULL;
+}
+
+int circuit_deliver_data_cell(cell_t *cell, circuit_t *circ, connection_t *conn, int crypt_type) {
+
+ /* first decrypt cell->length */
+ if(circuit_crypt(circ, &(cell->length), 1, crypt_type) < 0) {
+ log(LOG_DEBUG,"circuit_deliver_data_cell(): length decryption failed. Dropping connection.");
+ return -1;
+ }
+
+ /* then decrypt the payload */
+ if(circuit_crypt(circ, (char *)&(cell->payload), CELL_PAYLOAD_SIZE, crypt_type) < 0) {
+ log(LOG_DEBUG,"circuit_deliver_data_cell(): payload decryption failed. Dropping connection.");
+ return -1;
+ }
+
+ if(conn->type == CONN_TYPE_APP) { /* send payload directly */
+ log(LOG_DEBUG,"circuit_deliver_data_cell(): Sending to application.");
+ if(connection_app_process_data_cell(cell, conn) < 0) {
+ return -1;
+ }
+ } else { /* send it as a cell */
+ log(LOG_DEBUG,"circuit_deliver_data_cell(): Sending to connection.");
+ if(connection_write_cell_to_buf(cell, conn) < 0) {
+ return -1;
+ }
+ }
+ return 0; /* success */
+}
+
+int circuit_crypt(circuit_t *circ, char *in, size_t inlen, char crypt_type) {
+ char *out;
+ int outlen;
+
+ if(!circ || !in)
+ return -1;
+
+ out = malloc(inlen);
+ if(!out)
+ return -1;
+
+ if(crypt_type == 'e') {
+ log(LOG_DEBUG,"circuit_crypt(): Encrypting %d bytes.",inlen);
+ if(!EVP_EncryptUpdate(&circ->p_ctx,out,&outlen,in,inlen)) {
+ log(LOG_ERR,"circuit_encrypt(): Encryption failed for ACI : %u (%s).",circ->p_aci, ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+ } else if(crypt_type == 'd') {
+ log(LOG_DEBUG,"circuit_crypt(): Decrypting %d bytes.",inlen);
+ if(!EVP_DecryptUpdate(&circ->n_ctx,out,&outlen,in,inlen)) {
+ log(LOG_ERR,"circuit_crypt(): Decryption failed for ACI : %u (%s).",circ->n_aci, ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+ }
+
+ if(outlen != inlen) {
+ log(LOG_DEBUG,"circuit_crypt(): %d bytes crypted to %d bytes. Weird.",inlen,outlen);
+ return -1;
+ }
+
+ memcpy(in,out,inlen);
+ free(out);
+
+ return 0;
+}
+
+void circuit_close(circuit_t *circ) {
+ circuit_remove(circ);
+ connection_send_destroy(circ->n_aci, circ->n_conn);
+ connection_send_destroy(circ->p_aci, circ->p_conn);
+ circuit_free(circ);
+}
+
+void circuit_about_to_close_connection(connection_t *conn) {
+ /* send destroys for all circuits using conn */
+ /* currently, we assume it's too late to flush conn's buf here.
+ * down the road, maybe we'll consider that eof doesn't mean can't-write
+ */
+
+ circuit_t *circ;
+
+ while((circ = circuit_get_by_conn(conn))) {
+ circuit_remove(circ);
+ if(circ->n_conn == conn) /* it's closing in front of us */
+ connection_send_destroy(circ->p_aci, circ->p_conn);
+ if(circ->p_conn == conn) /* it's closing behind us */
+ connection_send_destroy(circ->n_aci, circ->n_conn);
+ circuit_free(circ);
+ }
+}
+
diff --git a/src/or/command.c b/src/or/command.c
new file mode 100644
index 000000000..2392a9bf2
--- /dev/null
+++ b/src/or/command.c
@@ -0,0 +1,212 @@
+
+#include "or.h"
+
+void command_process_cell(cell_t *cell, connection_t *conn) {
+
+ if(check_sane_cell(cell) < 0)
+ return;
+
+ switch(cell->command) {
+ case CELL_PADDING:
+ /* do nothing */
+ break;
+ case CELL_CREATE:
+ command_process_create_cell(cell, conn);
+ break;
+ case CELL_DATA:
+ command_process_data_cell(cell, conn);
+ break;
+ case CELL_DESTROY:
+ command_process_destroy_cell(cell, conn);
+ break;
+ }
+}
+
+void command_process_create_cell(cell_t *cell, connection_t *conn) {
+ circuit_t *circ;
+ connection_t *n_conn;
+ unsigned char *cellbuf; /* array of cells */
+ int cellbuflen; /* size of cellbuf in bytes */
+ cell_t *tmpcell; /* pointer to an arbitrary cell */
+ int retval, i;
+
+ circ = circuit_get_by_aci_conn(cell->aci, conn);
+
+ if(circ && circ->state != CIRCUIT_STATE_OPEN_WAIT) {
+ log(LOG_DEBUG,"command_process_create_cell(): received CREATE cell, not in open_wait. Dropping.");
+ return;
+ }
+
+ 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);
+ log(LOG_DEBUG,"command_process_create_cell(): Onion length is %u.",circ->onionlen);
+ if(circ->onionlen > 50000) { /* too big */
+ log(LOG_DEBUG,"That's ludicrous. Closing.");
+ circuit_close(circ);
+ return;
+ }
+ circ->onion = (unsigned char *)malloc(circ->onionlen);
+ if(!circ->onion) {
+ log(LOG_DEBUG,"command_process_create_cell(): Out of memory. Closing.");
+ circuit_close(circ);
+ return;
+ }
+ if(circ->onionlen < cell->length-4) { /* protect from buffer overflow */
+ log(LOG_DEBUG,"command_process_create_cell(): Onion too small. Closing.");
+ circuit_close(circ);
+ return;
+ }
+ memcpy((void *)circ->onion,(void *)(cell->payload+4),cell->length-4);
+ circ->recvlen = cell->length-4;
+ log(LOG_DEBUG,"command_process_create_cell(): Primary create cell handled, have received %d of %d onion bytes.",
+ circ->recvlen,circ->onionlen);
+
+ } else { /* pull over as much of the onion as we can */
+ if(cell->length + circ->recvlen > circ->onionlen) { /* protect from buffer overflow */
+ log(LOG_DEBUG,"command_process_create_cell(): payload too big for onion. Closing.");
+ circuit_close(circ);
+ return;
+ }
+ memcpy((void *)(circ->onion+circ->recvlen),(void *)cell->payload,cell->length);
+ circ->recvlen += cell->length;
+ log(LOG_DEBUG,"command_process_create_cell(): Secondary create cell handled, have received %d of %d onion bytes.",
+ circ->recvlen,circ->onionlen);
+ }
+
+ if(circ->recvlen != circ->onionlen) {
+ log(LOG_DEBUG,"command_process_create_cell(): Onion not all here yet. Ok.");
+ return;
+ }
+
+ /* we're all ready to go now. */
+ circ->state = CIRCUIT_STATE_OPEN;
+
+ if(process_onion(circ, conn) < 0) {
+ log(LOG_DEBUG,"command_process_create_cell(): Onion processing failed. Closing.");
+ circuit_close(circ);
+ return;
+ }
+
+ if(circ->n_addr && circ->n_port) { /* must send create cells to the next router */
+ n_conn = connection_get_by_addr_port(circ->n_addr,circ->n_port);
+ if(!n_conn || n_conn->type != CONN_TYPE_OR) {
+ /* i've disabled making connections through OPs, but it's definitely
+ * possible here. I'm not sure if it would be a bug or a feature. -RD
+ */
+ log(LOG_DEBUG,"command_process_create_cell(): Next router not connected. Closing.");
+ circuit_close(circ);
+ }
+ circ->n_conn = n_conn;
+ log(LOG_DEBUG,"command_process_create_cell(): n_conn is %s:%u",n_conn->address,ntohs(n_conn->port));
+
+ /* send the CREATE cells on to the next hop */
+ pad_onion(circ->onion,circ->onionlen, sizeof(onion_layer_t));
+ log(LOG_DEBUG,"command_process_create_cell(): Padded the onion with random data.");
+
+ retval = pack_create(circ->n_aci, circ->onion, circ->onionlen, &cellbuf, &cellbuflen);
+ free((void *)circ->onion);
+ circ->onion = NULL;
+ if (retval == -1) /* pack_create() error */
+ {
+ log(LOG_DEBUG,"command_process_create_cell(): Could not pack the onion into CREATE cells. Closing the connection.");
+ circuit_close(circ);
+ return;
+ }
+ log(LOG_DEBUG,"command_process_create_cell(): Onion packed into CREATE cells. Buffering the cells.");
+ /* queue the create cells for transmission to the next hop */
+ tmpcell = (cell_t *)cellbuf;
+ for (i=0;i<cellbuflen/sizeof(cell_t);i++)
+ {
+ retval = connection_write_cell_to_buf(tmpcell, n_conn);
+ if (retval == -1) /* buffering failed, drop the connection */
+ {
+ log(LOG_DEBUG,"command_process_create_cell(): Could not buffer new create cells. Closing.");
+ circuit_close(circ);
+ return;
+ }
+ tmpcell++;
+ }
+ free((void *)cellbuf);
+ return;
+
+ } else { /* this is destined for an app */
+ log(LOG_DEBUG,"command_process_create_cell(): Creating new application connection.");
+ n_conn = connection_new(CONN_TYPE_APP);
+ if(!n_conn) {
+ log(LOG_DEBUG,"command_process_create_cell(): connection_new failed. Closing.");
+ circuit_close(circ);
+ return;
+ }
+ n_conn->state = APP_CONN_STATE_CONNECTING_WAIT;
+ n_conn->s = -1; /* not yet valid */
+ if(connection_add(n_conn) < 0) { /* no space, forget it */
+ log(LOG_DEBUG,"command_process_create_cell(): connection_add failed. Closing.");
+ connection_free(n_conn);
+ circuit_close(circ);
+ return;
+ }
+ circ->n_conn = n_conn;
+ return;
+ }
+
+}
+
+void command_process_data_cell(cell_t *cell, connection_t *conn) {
+ circuit_t *circ;
+
+ /* FIXME do something with 'close' state, here */
+
+ circ = circuit_get_by_aci_conn(cell->aci, conn);
+
+ if(!circ) {
+ log(LOG_DEBUG,"command_process_data_cell(): received DATA cell for unknown circuit. Dropping.");
+ return;
+ }
+
+ if(circ->state == CIRCUIT_STATE_OPEN_WAIT) {
+ log(LOG_DEBUG,"command_process_data_cell(): circuit in open_wait. Dropping data cell.");
+ return;
+ }
+
+ /* at this point both circ->n_conn and circ->p_conn are guaranteed to be set */
+
+ if(cell->aci == circ->p_aci) { /* it's an outgoing cell */
+ cell->aci = circ->n_aci; /* switch it */
+ if(circuit_deliver_data_cell(cell, circ, circ->n_conn, 'd') < 0) {
+ log(LOG_DEBUG,"command_process_data_cell(): circuit_deliver_data_cell (forward) failed. Closing.");
+ circuit_close(circ);
+ return;
+ }
+ } else { /* it's an ingoing cell */
+ cell->aci = circ->p_aci; /* switch it */
+ if(circuit_deliver_data_cell(cell, circ, circ->p_conn, 'e') < 0) {
+ log(LOG_DEBUG,"command_process_data_cell(): circuit_deliver_data_cell (backward) failed. Closing.");
+ circuit_close(circ);
+ return;
+ }
+ }
+}
+
+void command_process_destroy_cell(cell_t *cell, connection_t *conn) {
+ circuit_t *circ;
+
+ circ = circuit_get_by_aci_conn(cell->aci, conn);
+
+ if(!circ) {
+ log(LOG_DEBUG,"command_process_destroy_cell(): received DESTROY cell for unknown circuit. Dropping.");
+ return;
+ }
+
+ log(LOG_DEBUG,"command_process_destroy_cell(): Received for aci %d.",cell->aci);
+ circuit_remove(circ);
+ if(cell->aci == circ->p_aci) /* the destroy came from behind */
+ connection_send_destroy(circ->n_aci, circ->n_conn);
+ if(cell->aci == circ->n_aci) /* the destroy came from ahead */
+ connection_send_destroy(circ->p_aci, circ->p_conn);
+ circuit_free(circ);
+
+}
+
diff --git a/src/or/config.c b/src/or/config.c
new file mode 100644
index 000000000..535f7638d
--- /dev/null
+++ b/src/or/config.c
@@ -0,0 +1,49 @@
+/**
+ * config.c
+ * Routines for loading the configuration file.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1 2002/06/26 22:45:50 arma
+ * Initial revision
+ *
+ * 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"
+
+/* loads the configuration file */
+int getconfig(char *conf_filename, config_opt_t *options)
+{
+ FILE *cf = NULL;
+ int retval = 0;
+
+ if ((!conf_filename) || (!options))
+ return -1;
+
+ /* load config file */
+ cf = open_config(conf_filename);
+ if (!cf)
+ {
+ log(LOG_ERR,"Could not open configuration file %s.",conf_filename);
+ return -1;
+ }
+ retval = parse_config(cf,options);
+ if (retval)
+ return -1;
+
+ return 0;
+}
+
diff --git a/src/or/connection.c b/src/or/connection.c
new file mode 100644
index 000000000..7411852ac
--- /dev/null
+++ b/src/or/connection.c
@@ -0,0 +1,362 @@
+
+#include "or.h"
+
+/********* START VARIABLES **********/
+
+char *conn_type_to_string[] = {
+ "OP listener", /* 0 */
+ "OP", /* 1 */
+ "OR listener", /* 2 */
+ "OR", /* 3 */
+ "App" /* 4 */
+};
+
+char *conn_state_to_string[][10] = {
+ { "ready" }, /* op listener, 0 */
+ { "awaiting keys", /* op, 0 */
+ "open", /* 1 */
+ "close", /* 2 */
+ "close_wait" }, /* 3 */
+ { "ready" }, /* or listener, 0 */
+ { "connecting (as client)", /* or, 0 */
+ "sending auth (as client)", /* 1 */
+ "waiting for auth (as client)", /* 2 */
+ "sending nonce (as client)", /* 3 */
+ "waiting for auth (as server)", /* 4 */
+ "sending auth (as server)", /* 5 */
+ "waiting for nonce (as server)",/* 6 */
+ "open" }, /* 7 */
+ { "connecting", /* app, 0 */
+ "open", /* 1 */
+ "waiting for dest info", /* 2 */
+ "flushing buffer, then will close",/* 3 */
+ "close_wait" } /* 4 */
+};
+
+/********* END VARIABLES ************/
+
+connection_t *connection_new(int type) {
+ connection_t *conn;
+
+ conn = (connection_t *)malloc(sizeof(connection_t));
+ if(!conn)
+ return NULL;
+ memset(conn,0,sizeof(connection_t)); /* zero it out to start */
+
+ conn->type = type;
+ buf_new(&conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen);
+ buf_new(&conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen);
+
+ return conn;
+}
+
+void connection_free(connection_t *conn) {
+ assert(conn);
+
+ buf_free(conn->inbuf);
+ buf_free(conn->outbuf);
+ if(conn->address)
+ free(conn->address);
+
+ /* FIXME should we do these for all connections, or just ORs, or what */
+ if(conn->type == CONN_TYPE_OR ||
+ conn->type == CONN_TYPE_OP) {
+// EVP_CIPHER_CTX_cleanup(&conn->f_ctx);
+// EVP_CIPHER_CTX_cleanup(&conn->b_ctx);
+ }
+
+ if(conn->s > 0)
+ close(conn->s);
+ free(conn);
+}
+
+int connection_create_listener(RSA *prkey, struct sockaddr_in *local, int type) {
+ connection_t *conn;
+ int s;
+ int one=1;
+
+ s = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+ if (s < 0)
+ {
+ log(LOG_ERR,"connection_create_listener(): Socket creation failed.");
+ return -1;
+ }
+
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+
+ if(bind(s,(struct sockaddr *)local,sizeof(*local)) < 0) {
+ perror("bind ");
+ log(LOG_ERR,"Could not bind to local port %u.",ntohs(local->sin_port));
+ return -1;
+ }
+
+ /* start local server */
+ if(listen(s,SOMAXCONN) < 0) {
+ log(LOG_ERR,"Could not listen on local port %u.",ntohs(local->sin_port));
+ return -1;
+ }
+
+ fcntl(s, F_SETFL, O_NONBLOCK); /* set s to non-blocking */
+
+ conn = connection_new(type);
+ conn->s = s;
+
+ if(connection_add(conn) < 0) { /* no space, forget it */
+ connection_free(conn);
+ return -1;
+ }
+
+ /* remember things so you can tell the baby sockets */
+ memcpy(&conn->local,local,sizeof(struct sockaddr_in));
+ conn->prkey = prkey;
+
+ log(LOG_DEBUG,"connection_create_listener(): Listening on local port %u.",ntohs(local->sin_port));
+
+ conn->state = LISTENER_STATE_READY;
+ connection_watch_events(conn, POLLIN);
+
+ return 0;
+}
+
+int connection_handle_listener_read(connection_t *conn, int new_type, int new_state) {
+
+ int news; /* the new socket */
+ connection_t *newconn;
+ struct sockaddr_in remote; /* information about the remote peer when connecting to other routers */
+ int remotelen = sizeof(struct sockaddr_in); /* length of the remote address */
+
+ news = accept(conn->s,(struct sockaddr *)&remote,&remotelen);
+ if (news == -1) { /* accept() error */
+ if(errno==EAGAIN)
+ return 0; /* he hung up before we could accept(). that's fine. */
+ /* else there was a real error. */
+ log(LOG_ERR,"connection_handle_listener_read(): accept() failed. Closing.");
+ return -1;
+ }
+ log(LOG_DEBUG,"Connection accepted on socket %d.",news);
+
+ newconn = connection_new(new_type);
+ newconn->s = news;
+
+ /* learn things from parent, so we can perform auth */
+ memcpy(&newconn->local,&conn->local,sizeof(struct sockaddr_in));
+ newconn->prkey = conn->prkey;
+// newconn->address = strdup(get_string_from_remote()) FIXME ;
+
+ if(connection_add(newconn) < 0) { /* no space, forget it */
+ connection_free(newconn);
+ return -1;
+ }
+
+ log(LOG_DEBUG,"connection_handle_listener_read(): socket %d entered state %d.",newconn->s, new_state);
+ newconn->state = new_state;
+ connection_watch_events(newconn, POLLIN);
+
+ return 0;
+}
+
+int retry_all_connections(routerinfo_t **router_array, int rarray_len,
+ RSA *prkey, uint16_t or_port, uint16_t op_port) {
+
+ /* start all connections that should be up but aren't */
+
+ routerinfo_t *router;
+ int i;
+
+ /* local host information */
+ char localhostname[512];
+ struct hostent *localhost;
+ struct sockaddr_in local; /* local address */
+
+ /* obtain local host information */
+ if(gethostname(localhostname,512) < 0) {
+ log(LOG_ERR,"Error obtaining local hostname.");
+ return -1;
+ }
+ localhost = gethostbyname(localhostname);
+ if (!localhost) {
+ log(LOG_ERR,"Error obtaining local host info.");
+ return -1;
+ }
+ memset((void *)&local,0,sizeof(local));
+ local.sin_family = AF_INET;
+ local.sin_addr.s_addr = INADDR_ANY;
+ local.sin_port = htons(or_port);
+ memcpy((void *)&local.sin_addr,(void *)localhost->h_addr,sizeof(struct in_addr));
+
+ for (i=0;i<rarray_len;i++) {
+ router = router_array[i];
+ if(!connection_get_by_addr_port(router->addr,router->port)) { /* not in the list */
+ connect_to_router(router, prkey, &local);
+ }
+ }
+
+ if(!connection_get_by_type(CONN_TYPE_OR_LISTENER)) {
+ connection_or_create_listener(prkey, &local);
+ }
+
+ local.sin_port = htons(op_port);
+ if(!connection_get_by_type(CONN_TYPE_OP_LISTENER)) {
+ connection_op_create_listener(prkey, &local);
+ }
+
+ return 0;
+
+}
+
+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 connection_fetch_from_buf(char *string, int len, connection_t *conn) {
+ return fetch_from_buf(string, len, &conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen);
+}
+
+int connection_flush_buf(connection_t *conn) {
+ return flush_buf(conn->s, &conn->outbuf, &conn->outbuflen, &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);
+ return write_to_buf(string, len, &conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen);
+}
+
+int connection_send_destroy(aci_t aci, connection_t *conn) {
+ cell_t cell;
+
+ if(!conn)
+ return -1;
+
+ if(conn->type == CONN_TYPE_OP ||
+ conn->type == CONN_TYPE_APP) {
+ log(LOG_DEBUG,"connection_send_destroy(): At an edge. Marking connection for close.");
+ conn->marked_for_close = 1;
+ return 0;
+ }
+
+ cell.aci = aci;
+ cell.command = CELL_DESTROY;
+ log(LOG_DEBUG,"connection_send_destroy(): Sending destroy (aci %d).",aci);
+ return connection_write_cell_to_buf(&cell, conn);
+
+}
+
+int connection_write_cell_to_buf(cell_t *cellp, connection_t *conn) {
+ /* FIXME in the future, we should modify windows, etc, here */
+
+ if(connection_encrypt_cell_header(cellp,conn)<0) {
+ return -1;
+ }
+
+ return connection_write_to_buf((char *)cellp, sizeof(cell_t), conn);
+
+}
+
+int connection_encrypt_cell_header(cell_t *cellp, connection_t *conn) {
+ char newheader[8];
+ int newsize;
+ int x;
+ char *px;
+
+ printf("Sending: Cell header plaintext: ");
+ px = (char *)cellp;
+ for(x=0;x<8;x++) {
+ printf("%u ",px[x]);
+ }
+ printf("\n");
+
+ 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;
+ }
+ printf("Sending: Cell header crypttext: ");
+ for(x=0;x<8;x++) {
+ printf("%u ",newheader[x]);
+ }
+ printf("\n");
+
+ memcpy(cellp,newheader,8);
+ return 0;
+}
+
+int connection_process_inbuf(connection_t *conn) {
+
+ assert(conn);
+
+ switch(conn->type) {
+ case CONN_TYPE_OP:
+ return connection_op_process_inbuf(conn);
+ case CONN_TYPE_OR:
+ return connection_or_process_inbuf(conn);
+ case CONN_TYPE_APP:
+ return connection_app_process_inbuf(conn);
+ default:
+ log(LOG_DEBUG,"connection_process_inbuf() got unexpected conn->type.");
+ return -1;
+ }
+}
+
+int connection_finished_flushing(connection_t *conn) {
+
+ assert(conn);
+
+ log(LOG_DEBUG,"connection_finished_flushing() entered. Socket %u.", conn->s);
+
+ switch(conn->type) {
+ case CONN_TYPE_OP:
+ return connection_op_finished_flushing(conn);
+ case CONN_TYPE_OR:
+ return connection_or_finished_flushing(conn);
+ case CONN_TYPE_APP:
+ return connection_app_finished_flushing(conn);
+ default:
+ log(LOG_DEBUG,"connection_finished_flushing() got unexpected conn->type.");
+ return -1;
+ }
+}
+
+int connection_process_cell_from_inbuf(connection_t *conn) {
+ /* check if there's a whole cell there.
+ * if yes, pull it off, decrypt it, and process it.
+ */
+ char crypted[128];
+ char outbuf[1024];
+ int outlen;
+ int x;
+ cell_t *cellp;
+
+ if(conn->inbuf_datalen < 128) /* entire response available? */
+ return 0; /* not yet */
+
+ if(connection_fetch_from_buf(crypted,128,conn) < 0) {
+ return -1;
+ }
+
+ printf("Cell header crypttext: ");
+ for(x=0;x<8;x++) {
+ printf("%u ",crypted[x]);
+ }
+ printf("\n");
+ /* 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);
+ printf("Cell header plaintext: ");
+ for(x=0;x<8;x++) {
+ printf("%u ",outbuf[x]);
+ }
+ printf("\n");
+
+ /* copy the rest of the cell */
+ memcpy((char *)outbuf+8, (char *)crypted+8, sizeof(cell_t)-8);
+ cellp = (cell_t *)outbuf;
+ log(LOG_DEBUG,"connection_process_cell_from_inbuf(): Decrypted cell is of type %u (ACI %u).",cellp->command,cellp->aci);
+ command_process_cell(cellp, conn);
+
+ return connection_process_inbuf(conn); /* process the remainder of the buffer */
+}
+
diff --git a/src/or/connection_app.c b/src/or/connection_app.c
new file mode 100644
index 000000000..94276fd59
--- /dev/null
+++ b/src/or/connection_app.c
@@ -0,0 +1,212 @@
+
+#include "or.h"
+
+connection_t *connection_app_new(void) {
+ return connection_new(CONN_TYPE_APP);
+}
+
+int connection_app_process_inbuf(connection_t *conn) {
+
+ assert(conn && conn->type == CONN_TYPE_APP);
+
+ if(conn->inbuf_reached_eof) {
+ /* eof reached, kill it. */
+ log(LOG_DEBUG,"connection_app_process_inbuf(): conn reached eof. Closing.");
+ return -1;
+ }
+
+ log(LOG_DEBUG,"connection_app_process_inbuf(): state %d.",conn->state);
+
+ switch(conn->state) {
+ case APP_CONN_STATE_CONNECTING:
+ log(LOG_DEBUG,"connection_app_process_inbuf(): text from app server while in 'connecting' state. Leaving it on buffer.");
+ return 0;
+ case APP_CONN_STATE_OPEN:
+ return connection_app_package_inbuf(conn);
+ }
+ return 0;
+
+}
+
+int connection_app_package_inbuf(connection_t *conn) {
+ int amount_to_process;
+ cell_t cell;
+ circuit_t *circ;
+
+ assert(conn && conn->type == CONN_TYPE_APP);
+
+ amount_to_process = conn->inbuf_datalen;
+
+ if(!amount_to_process)
+ return 0;
+
+ if(amount_to_process > CELL_PAYLOAD_SIZE) {
+ cell.length = CELL_PAYLOAD_SIZE;
+ } else {
+ cell.length = amount_to_process;
+ }
+
+ if(connection_fetch_from_buf(cell.payload,cell.length,conn) < 0) {
+ return -1;
+ }
+
+ circ = circuit_get_by_conn(conn);
+ if(!circ) {
+ log(LOG_DEBUG,"connection_app_package_inbuf(): conn has no circuits!");
+ return -1;
+ }
+
+ log(LOG_DEBUG,"connection_app_package_inbuf(): Packaging %d bytes.",cell.length);
+ cell.aci = circ->p_aci;
+ cell.command = CELL_DATA;
+ if(circuit_deliver_data_cell(&cell, circ, circ->p_conn, 'e') < 0) {
+ log(LOG_DEBUG,"connection_app_package_inbuf(): circuit_deliver_data_cell (backward) failed. Closing.");
+ circuit_close(circ);
+ return 0;
+ }
+ if(amount_to_process > CELL_PAYLOAD_SIZE)
+ return(connection_app_package_inbuf(conn));
+ return 0;
+}
+
+int connection_app_finished_flushing(connection_t *conn) {
+ int e, len=sizeof(e);
+
+ assert(conn && conn->type == CONN_TYPE_APP);
+
+ switch(conn->state) {
+ case APP_CONN_STATE_CONNECTING:
+ if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, &e, &len) < 0) { /* not yet */
+ if(errno != EINPROGRESS){
+ /* yuck. kill it. */
+ log(LOG_DEBUG,"connection_app_finished_flushing(): in-progress connect failed. Removing.");
+ return -1;
+ } else {
+ return 0; /* no change, see if next time is better */
+ }
+ }
+ /* the connect has finished. */
+
+ log(LOG_DEBUG,"connection_app_finished_flushing() : Connection to %s:%u established.",
+ conn->address,ntohs(conn->port));
+
+ conn->state = APP_CONN_STATE_OPEN;
+ connection_watch_events(conn, POLLIN);
+ return 0;
+ case APP_CONN_STATE_OPEN:
+ /* FIXME down the road, we'll clear out circuits that are pending to close */
+ connection_watch_events(conn, POLLIN);
+ return 0;
+ default:
+ log(LOG_DEBUG,"Bug: connection_app_finished_flushing() called in unexpected state.");
+ return 0;
+ }
+
+ return 0;
+}
+
+int connection_app_process_data_cell(cell_t *cell, connection_t *conn) {
+ struct hostent *rent;
+ struct sockaddr_in dest_addr;
+ int s;
+
+ /* an outgoing data cell has arrived */
+
+ assert(conn && conn->type == CONN_TYPE_APP);
+
+ switch(conn->state) {
+ case APP_CONN_STATE_CONNECTING_WAIT:
+ log(LOG_DEBUG,"connection_app_process_cell(): state is connecting_wait. cell length %d.", cell->length);
+ if(!conn->ss_received) { /* this cell contains the ss */
+ if(cell->length != sizeof(ss_t)) {
+ log(LOG_DEBUG,"connection_app_process_cell(): Supposed to contain SS but wrong size. Closing.");
+ return -1;
+ }
+ memcpy(&conn->ss, cell->payload, cell->length);
+ if(conn->ss.addr_fmt != SS_ADDR_FMT_ASCII_HOST_PORT) { /* unrecognized address format */
+ log(LOG_DEBUG,"connection_app_process_cell(): SS has unrecognized address format. Closing.");
+ return -1;
+ }
+ conn->ss_received = 1;
+ log(LOG_DEBUG,"connection_app_process_cell(): SS received.");
+ } else if (!conn->addr) { /* this cell contains the dest addr */
+ if(!memchr(cell->payload,0,cell->length)) {
+ log(LOG_DEBUG,"connection_app_process_cell(): dest_addr cell has no \\0. Closing.");
+ return -1;
+ }
+ conn->address = strdup(cell->payload);
+ rent = gethostbyname(cell->payload);
+ if (!rent) {
+ log(LOG_ERR,"connection_app_process_cell(): Could not resolve dest addr %s.",cell->payload);
+ return -1;
+ }
+ memcpy(&conn->addr, rent->h_addr,rent->h_length);
+ log(LOG_DEBUG,"connection_app_process_cell(): addr %s resolves to %d.",cell->payload,conn->addr);
+ } else if (!conn->port) { /* this cell contains the dest port */
+ if(!memchr(cell->payload,'\0',cell->length)) {
+ log(LOG_DEBUG,"connection_app_process_cell(): dest_port cell has no \\0. Closing.");
+ return -1;
+ }
+ conn->port = atoi(cell->payload);
+ if(!conn->port) { /* bad port */
+ log(LOG_DEBUG,"connection_app_process_cell(): dest_port cell isn't a valid number. Closing.");
+ return -1;
+ }
+ /* all the necessary info is here. Start the connect() */
+ s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+ if (s < 0)
+ {
+ log(LOG_ERR,"connection_app_process_cell(): Error creating network socket.");
+ return -1;
+ }
+ fcntl(s, F_SETFL, O_NONBLOCK); /* set s to non-blocking */
+
+ memset((void *)&dest_addr,0,sizeof(dest_addr));
+ dest_addr.sin_family = AF_INET;
+ dest_addr.sin_port = conn->port;
+ memcpy((void *)&dest_addr.sin_addr, &conn->addr, sizeof(uint32_t));
+
+ log(LOG_DEBUG,"connection_app_process_cell(): Connecting to %s:%u.",conn->address,ntohs(conn->port));
+
+ if(connect(s,(struct sockaddr *)&dest_addr,sizeof(dest_addr)) < 0){
+ if(errno != EINPROGRESS){
+ /* yuck. kill it. */
+ log(LOG_DEBUG,"connection_app_process_cell(): Connect failed.");
+ return -1;
+ } else {
+ /* it's in progress. set state appropriately and return. */
+ conn->s = s;
+ connection_set_poll_socket(conn);
+ conn->state = APP_CONN_STATE_CONNECTING;
+
+ /* i think only pollout is needed, but i'm curious if pollin ever gets caught -RD */
+ log(LOG_DEBUG,"connection_app_process_cell(): connect in progress, socket %d.",s);
+ connection_watch_events(conn, POLLOUT | POLLIN);
+ return 0;
+ }
+ }
+
+ /* it succeeded. we're connected. */
+ log(LOG_DEBUG,"connection_app_process_cell(): Connection to %s:%u established.",conn->address,ntohs(conn->port));
+
+ conn->s = s;
+ connection_set_poll_socket(conn);
+ conn->state = APP_CONN_STATE_OPEN;
+ connection_watch_events(conn, POLLIN);
+ } else { /* i'm not sure what this would be */
+ log(LOG_DEBUG,"connection_app_process_cell(): in connecting_wait, not sure why.");
+ }
+ return 0;
+ case APP_CONN_STATE_CONNECTING:
+ log(LOG_DEBUG,"connection_app_process_cell(): Data receiving while connecting. Queueing.");
+ /* FIXME kludge. shouldn't call write_to_buf directly. */
+ return write_to_buf(cell->payload, cell->length, &conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen);
+ case APP_CONN_STATE_OPEN:
+ return connection_write_to_buf(cell->payload, cell->length, conn);
+ }
+
+ return 0;
+}
+
+
+
diff --git a/src/or/connection_op.c b/src/or/connection_op.c
new file mode 100644
index 000000000..03d36dbe6
--- /dev/null
+++ b/src/or/connection_op.c
@@ -0,0 +1,130 @@
+
+#include "or.h"
+
+connection_t *connection_op_new(void) {
+ return connection_new(CONN_TYPE_OP);
+}
+
+connection_t *connection_op_listener_new(void) {
+ return connection_new(CONN_TYPE_OP_LISTENER);
+}
+
+int connection_op_process_inbuf(connection_t *conn) {
+
+ assert(conn && conn->type == CONN_TYPE_OP);
+
+ if(conn->inbuf_reached_eof) {
+ /* eof reached, kill it. */
+ log(LOG_DEBUG,"connection_op_process_inbuf(): conn reached eof. Closing.");
+ return -1;
+ }
+
+ log(LOG_DEBUG,"connection_op_process_inbuf(): state %d.",conn->state);
+
+ switch(conn->state) {
+ case OP_CONN_STATE_AWAITING_KEYS:
+ return op_handshake_process_keys(conn);
+ case OP_CONN_STATE_OPEN:
+ return connection_process_cell_from_inbuf(conn);
+ default:
+ log(LOG_DEBUG,"connection_op_process_inbuf() called in state where I'm writing. Ignoring buf for now.")
+;
+ }
+
+ return 0;
+
+}
+
+int op_handshake_process_keys(connection_t *conn) {
+ int retval;
+ int x;
+
+ /* key exchange message */
+ unsigned char auth_cipher[128];
+ unsigned char auth_plain[128];
+
+ assert(conn);
+
+ log(LOG_DEBUG,"op_handshake_process_keys() entered.");
+
+ if(conn->inbuf_datalen < 128) /* entire response available? */
+ return 0; /* not yet */
+
+ if(connection_fetch_from_buf(auth_cipher,128,conn) < 0) {
+ return -1;
+ }
+ log(LOG_DEBUG,"op_handshake_process_keys() : Received auth.");
+
+ /* decrypt response */
+ retval = RSA_private_decrypt(128,auth_cipher,auth_plain,conn->prkey,RSA_PKCS1_PADDING);
+ if (retval == -1)
+ {
+ log(LOG_ERR,"Decrypting keys from new OP failed.");
+ log(LOG_DEBUG,"op_handshake_process_keys() : Reason : %s.",
+ ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+
+ log(LOG_DEBUG,"Successfully decrypted keys from new OP.");
+
+ conn->bandwidth = ntohl(*((uint32_t *)auth_plain));
+
+ memcpy(conn->b_session_key, auth_plain+4, 8);
+ memcpy(conn->f_session_key, auth_plain+12, 8);
+ printf("f_session_key: ");
+ for(x=0;x<8;x++) {
+ printf("%d ",conn->f_session_key[x]);
+ }
+ printf("\nb_session_key: ");
+ for(x=0;x<8;x++) {
+ printf("%d ",conn->b_session_key[x]);
+ }
+ printf("\n");
+
+ memset((void *)conn->f_session_iv, 0, 8);
+ memset((void *)conn->b_session_iv, 0, 8);
+
+ EVP_CIPHER_CTX_init(&conn->f_ctx);
+ EVP_CIPHER_CTX_init(&conn->b_ctx);
+ EVP_EncryptInit(&conn->b_ctx, EVP_des_ofb(), conn->b_session_key, conn->b_session_iv);
+ EVP_DecryptInit(&conn->f_ctx, EVP_des_ofb(), conn->f_session_key, conn->f_session_iv);
+
+#if 0
+ /* FIXME must choose conn->aci here? What does it mean for a connection to have an aci? */
+ log(LOG_DEBUG,"new_entry_connection : Chosen ACI %u.",conn->aci);
+#endif
+
+ conn->state = OP_CONN_STATE_OPEN;
+ connection_watch_events(conn, POLLIN);
+
+ return 0;
+}
+
+int connection_op_finished_flushing(connection_t *conn) {
+
+ assert(conn && conn->type == CONN_TYPE_OP);
+
+ switch(conn->state) {
+ case OP_CONN_STATE_OPEN:
+ /* FIXME down the road, we'll clear out circuits that are pending to close */
+ connection_watch_events(conn, POLLIN);
+ return 0;
+ default:
+ log(LOG_DEBUG,"Bug: connection_op_finished_flushing() called in unexpected state.");
+ return 0;
+ }
+
+ return 0;
+
+}
+
+int connection_op_create_listener(RSA *prkey, struct sockaddr_in *local) {
+ log(LOG_DEBUG,"connection_create_op_listener starting");
+ return connection_create_listener(prkey, local, CONN_TYPE_OP_LISTENER);
+}
+
+int connection_op_handle_listener_read(connection_t *conn) {
+ log(LOG_NOTICE,"OP: Received a connection request. Waiting for keys.");
+ return connection_handle_listener_read(conn, CONN_TYPE_OP, OP_CONN_STATE_AWAITING_KEYS);
+}
+
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
new file mode 100644
index 000000000..813cc8975
--- /dev/null
+++ b/src/or/connection_or.c
@@ -0,0 +1,580 @@
+
+#include "or.h"
+
+/*
+ *
+ * these two functions are the main ways 'in' to connection_or
+ *
+ */
+
+int connection_or_process_inbuf(connection_t *conn) {
+
+ assert(conn && conn->type == CONN_TYPE_OR);
+
+ if(conn->inbuf_reached_eof) {
+ /* eof reached, kill it. */
+ log(LOG_DEBUG,"connection_or_process_inbuf(): conn reached eof. Closing.");
+ return -1;
+ }
+
+ log(LOG_DEBUG,"connection_or_process_inbuf(): state %d.",conn->state);
+
+ switch(conn->state) {
+ case OR_CONN_STATE_CLIENT_AUTH_WAIT:
+ return or_handshake_client_process_auth(conn);
+ case OR_CONN_STATE_SERVER_AUTH_WAIT:
+ return or_handshake_server_process_auth(conn);
+ case OR_CONN_STATE_SERVER_NONCE_WAIT:
+ return or_handshake_server_process_nonce(conn);
+ case OR_CONN_STATE_OPEN:
+ return connection_process_cell_from_inbuf(conn);
+ default:
+ log(LOG_DEBUG,"connection_or_process_inbuf() called in state where I'm writing. Ignoring buf for now.");
+ }
+
+ return 0;
+
+}
+
+int connection_or_finished_flushing(connection_t *conn) {
+ int e, len=sizeof(e);
+
+ assert(conn && conn->type == CONN_TYPE_OR);
+
+ switch(conn->state) {
+ case OR_CONN_STATE_CLIENT_CONNECTING:
+ if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, &e, &len) < 0) { /* not yet */
+ if(errno != EINPROGRESS){
+ /* yuck. kill it. */
+ log(LOG_DEBUG,"connection_or_finished_flushing(): in-progress connect failed. Removing.");
+ return -1;
+ } else {
+ return 0; /* no change, see if next time is better */
+ }
+ }
+ /* the connect has finished. */
+
+ log(LOG_DEBUG,"connection_or_finished_flushing() : Connection to router %s:%u established.",
+ conn->address,ntohs(conn->port));
+
+ return or_handshake_client_send_auth(conn);
+ case OR_CONN_STATE_CLIENT_SENDING_AUTH:
+ log(LOG_DEBUG,"connection_or_finished_flushing(): client finished sending auth.");
+ conn->state = OR_CONN_STATE_CLIENT_AUTH_WAIT;
+ connection_watch_events(conn, POLLIN);
+ return 0;
+ case OR_CONN_STATE_CLIENT_SENDING_NONCE:
+ log(LOG_DEBUG,"connection_or_finished_flushing(): client finished sending nonce.");
+ conn_or_init_crypto(conn);
+ conn->state = OR_CONN_STATE_OPEN;
+ connection_watch_events(conn, POLLIN);
+ return 0;
+ case OR_CONN_STATE_SERVER_SENDING_AUTH:
+ log(LOG_DEBUG,"connection_or_finished_flushing(): server finished sending auth.");
+ conn->state = OR_CONN_STATE_SERVER_NONCE_WAIT;
+ connection_watch_events(conn, POLLIN);
+ return 0;
+ case OR_CONN_STATE_OPEN:
+ /* FIXME down the road, we'll clear out circuits that are pending to close */
+ connection_watch_events(conn, POLLIN);
+ return 0;
+ default:
+ log(LOG_DEBUG,"Bug: connection_or_finished_flushing() called in unexpected state.");
+ return 0;
+ }
+
+ return 0;
+
+}
+
+/*********************/
+
+connection_t *connection_or_new(void) {
+ return connection_new(CONN_TYPE_OR);
+}
+
+connection_t *connection_or_new_listener(void) {
+ return connection_new(CONN_TYPE_OR_LISTENER);
+}
+
+void conn_or_init_crypto(connection_t *conn) {
+ int x;
+
+ assert(conn);
+ printf("f_session_key: ");
+ for(x=0;x<8;x++) {
+ printf("%d ",conn->f_session_key[x]);
+ }
+ printf("\nb_session_key: ");
+ for(x=0;x<8;x++) {
+ printf("%d ",conn->b_session_key[x]);
+ }
+ printf("\n");
+
+ memset(conn->f_session_iv,0,8);
+ memset(conn->b_session_iv,0,8);
+ EVP_CIPHER_CTX_init(&conn->f_ctx);
+ EVP_CIPHER_CTX_init(&conn->b_ctx);
+ EVP_EncryptInit(&conn->f_ctx, EVP_des_ofb(), conn->f_session_key, conn->f_session_iv);
+ EVP_DecryptInit(&conn->b_ctx, EVP_des_ofb(), conn->b_session_key, conn->b_session_iv);
+ /* always encrypt with f, always decrypt with b */
+
+}
+
+/*
+ *
+ * auth handshake, as performed by OR *initiating* the connection
+ *
+ */
+
+int connect_to_router(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local) {
+ connection_t *conn;
+ struct sockaddr_in router_addr;
+ int s;
+
+ if ((!router) || (!prkey) || (!local))
+ return -1;
+
+ if(router->addr == local->sin_addr.s_addr && router->port == local->sin_port) {
+ /* this is me! don't connect to me. */
+ return 0;
+ }
+
+ conn = connection_or_new();
+
+ /* set up conn so it's got all the data we need to remember */
+ conn->addr = router->addr, conn->port = router->port;
+ conn->prkey = prkey;
+ conn->min = router->min, conn->max = router->max;
+ conn->pkey = router->pkey;
+ conn->address = strdup(router->address);
+ memcpy(&conn->local,local,sizeof(struct sockaddr_in));
+
+ s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+ if (s < 0)
+ {
+ log(LOG_ERR,"Error creating network socket.");
+ connection_free(conn);
+ return -1;
+ }
+ fcntl(s, F_SETFL, O_NONBLOCK); /* set s to non-blocking */
+
+ memset((void *)&router_addr,0,sizeof(router_addr));
+ router_addr.sin_family = AF_INET;
+ router_addr.sin_port = router->port;
+ memcpy((void *)&router_addr.sin_addr, &router->addr, sizeof(uint32_t));
+
+ log(LOG_DEBUG,"connect_to_router() : Trying to connect to %s:%u.",inet_ntoa(*(struct in_addr *)&router->addr),ntohs(router->port));
+
+ if(connect(s,(struct sockaddr *)&router_addr,sizeof(router_addr)) < 0){
+ if(errno != EINPROGRESS){
+ /* yuck. kill it. */
+ connection_free(conn);
+ return -1;
+ } else {
+ /* it's in progress. set state appropriately and return. */
+ conn->s = s;
+
+ conn->state = OR_CONN_STATE_CLIENT_CONNECTING;
+
+ if(connection_add(conn) < 0) { /* no space, forget it */
+ connection_free(conn);
+ return -1;
+ }
+ /* i think only pollout is needed, but i'm curious if pollin ever gets caught -RD */
+ log(LOG_DEBUG,"connect_to_router() : connect in progress.");
+ connection_watch_events(conn, POLLOUT | POLLIN);
+ return 0;
+ }
+ }
+
+ /* it succeeded. we're connected. */
+ conn->s = s;
+
+ if(connection_add(conn) < 0) { /* no space, forget it */
+ connection_free(conn);
+ return -1;
+ }
+
+ log(LOG_DEBUG,"connect_to_router() : Connection to router %s:%u established.",router->address,ntohs(router->port));
+
+ /* move to the next step in the handshake */
+ if(or_handshake_client_send_auth(conn) < 0) {
+ connection_remove(conn);
+ connection_free(conn);
+ return -1;
+ }
+ return 0;
+}
+
+int or_handshake_client_send_auth(connection_t *conn) {
+ int retval;
+ char keys[16];
+ char buf[44];
+ char cipher[128];
+
+ if (!conn)
+ return -1;
+
+ /* generate random keys */
+ retval = RAND_bytes(keys,16);
+ if (retval != 1) /* error */
+ {
+ log(LOG_ERR,"Cannot generate a secure DES key.");
+ return -1;
+ }
+ log(LOG_DEBUG,"or_handshake_client_send_auth() : Generated DES keys.");
+
+ /* save keys */
+ memcpy(conn->f_session_key,keys,8);
+ memcpy(conn->b_session_key,keys+8,8);
+
+ /* generate first message */
+ memcpy(buf,&conn->local.sin_addr,4); /* local address */
+ memcpy(buf+4,(void *)&conn->local.sin_port,2); /* local port */
+ memcpy(buf+6, (void *)&conn->addr, 4); /* remote address */
+ memcpy(buf+10, (void *)&conn->port, 2); /* remote port */
+ memcpy(buf+12,keys,16); /* keys */
+ *((uint32_t *)(buf+28)) = htonl(conn->min); /* min link utilisation */
+ *((uint32_t *)(buf+32)) = htonl(conn->max); /* maximum 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 */
+ {
+ log(LOG_ERR,"Public-key encryption failed during authentication to %s:%u.",conn->address,ntohs(conn->port));
+ log(LOG_DEBUG,"or_handshake_client_send_auth() : Reason : %s.",ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+ log(LOG_DEBUG,"or_handshake_client_send_auth() : Encrypted authentication message.");
+
+ /* send message */
+
+ if(connection_write_to_buf(cipher, 128, conn) < 0) {
+ log(LOG_DEBUG,"or_handshake_client_send_auth(): my outbuf is full. Oops.");
+ return -1;
+ }
+ retval = connection_flush_buf(conn);
+ if(retval < 0) {
+ log(LOG_DEBUG,"or_handshake_client_send_auth(): bad socket while flushing.");
+ return -1;
+ }
+ if(retval > 0) {
+ /* still stuff on the buffer. */
+ conn->state = OR_CONN_STATE_CLIENT_SENDING_AUTH;
+ connection_watch_events(conn, POLLOUT | POLLIN);
+ return 0;
+ }
+
+ /* it finished sending */
+ log(LOG_DEBUG,"or_handshake_client_send_auth(): Finished sending authentication message.");
+ conn->state = OR_CONN_STATE_CLIENT_AUTH_WAIT;
+ connection_watch_events(conn, POLLIN);
+ return 0;
+
+}
+
+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;
+ int retval;
+
+ assert(conn);
+
+ if(conn->inbuf_datalen < 128) /* entire response available? */
+ return 0; /* not yet */
+
+ if(connection_fetch_from_buf(cipher,128,conn) < 0) {
+ return -1;
+ }
+ log(LOG_DEBUG,"or_handshake_client_process_auth() : Received auth.");
+
+ /* decrypt response */
+ retval = RSA_private_decrypt(128,cipher,buf,conn->prkey,RSA_PKCS1_PADDING);
+ if (retval == -1)
+ {
+ log(LOG_ERR,"Public-key decryption failed during authentication to %s:%u.",
+ conn->address,ntohs(conn->port));
+ log(LOG_DEBUG,"or_handshake_client_process_auth() : Reason : %s.",
+ ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+ else if (retval != 44)
+ {
+ log(LOG_ERR,"Received an incorrect response from router %s:%u during authentication.",
+ conn->address,ntohs(conn->port));
+ return -1;
+ }
+ log(LOG_DEBUG,"or_handshake_client_process_auth() : Decrypted response.");
+ /* check validity */
+ if ( (memcmp(&conn->local.sin_addr, buf, 4)) || /* local address */
+ (memcmp(&conn->local.sin_port, buf+4, 2)) || /* local port */
+ (memcmp(&conn->addr, buf+6, 4)) || /* remote address */
+ (memcmp(&conn->port, buf+10, 2)) || /* remote port */
+ (memcmp(&conn->f_session_key, buf+12, 8)) || /* keys */
+ (memcmp(&conn->b_session_key, buf+20, 8)))
+ { /* incorrect response */
+ log(LOG_ERR,"Router %s:%u failed to authenticate. Either the key I have is obsolete or they're doing something they're not supposed to.",conn->address,ntohs(conn->port));
+ return -1;
+ }
+
+ 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);
+
+ if (conn->min > min)
+ conn->min = min;
+ if (conn->max > max)
+ conn->max = max;
+
+ /* reply is just local addr/port, remote addr/port, nonce */
+ memcpy(buf+12, buf+36, 8);
+
+ /* encrypt reply */
+ retval = RSA_public_encrypt(20,buf,cipher,conn->pkey,RSA_PKCS1_PADDING);
+ if (retval == -1) /* error */
+ {
+ log(LOG_ERR,"Public-key encryption failed during authentication to %s:%u.",conn->address,ntohs(conn->port));
+ log(LOG_DEBUG,"or_handshake_client_process_auth() : Reason : %s.",ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+
+ /* send the message */
+
+ if(connection_write_to_buf(cipher, 128, conn) < 0) {
+ log(LOG_DEBUG,"or_handshake_client_process_auth(): my outbuf is full. Oops.");
+ return -1;
+ }
+ retval = connection_flush_buf(conn);
+ if(retval < 0) {
+ log(LOG_DEBUG,"or_handshake_client_process_auth(): bad socket while flushing.");
+ return -1;
+ }
+ if(retval > 0) {
+ /* still stuff on the buffer. */
+ conn->state = OR_CONN_STATE_CLIENT_SENDING_NONCE;
+ connection_watch_events(conn, POLLOUT | POLLIN);
+/* return(connection_process_inbuf(conn)); process the rest of the inbuf */
+ return 0;
+ }
+
+ /* it finished sending */
+ log(LOG_DEBUG,"or_handshake_client_process_auth(): Finished sending nonce.");
+ conn_or_init_crypto(conn);
+ conn->state = OR_CONN_STATE_OPEN;
+ connection_watch_events(conn, POLLIN);
+ return connection_process_inbuf(conn); /* process the rest of the inbuf */
+
+}
+
+/*
+ *
+ * auth handshake, as performed by OR *receiving* the connection
+ *
+ */
+
+int or_handshake_server_process_auth(connection_t *conn) {
+ int retval;
+
+ char buf[128]; /* only 42 of this is expected to be used */
+ char cipher[128];
+
+ uint32_t addr;
+ uint16_t port;
+
+ uint32_t min,max;
+ routerinfo_t *router;
+
+ assert(conn);
+
+ log(LOG_DEBUG,"or_handshake_server_process_auth() entered.");
+
+ if(conn->inbuf_datalen < 128) /* entire response available? */
+ return 0; /* not yet */
+
+ if(connection_fetch_from_buf(cipher,128,conn) < 0) {
+ return -1;
+ }
+ log(LOG_DEBUG,"or_handshake_server_process_auth() : Received auth.");
+
+ /* decrypt response */
+ retval = RSA_private_decrypt(128,cipher,buf,conn->prkey,RSA_PKCS1_PADDING);
+ if (retval == -1)
+ {
+ log(LOG_ERR,"Public-key decryption failed during authentication to %s:%u.",
+ conn->address,ntohs(conn->port));
+ log(LOG_DEBUG,"or_handshake_server_process_auth() : Reason : %s.",
+ ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+ else if (retval != 36)
+ {
+ log(LOG_ERR,"Received an incorrect authentication request.");
+ return -1;
+ }
+ log(LOG_DEBUG,"or_handshake_server_process_auth() : Decrypted authentication message.");
+
+ /* identify the router */
+ memcpy(&addr,buf,4); /* save the IP address */
+ memcpy(&port,buf+4,2); /* save the port */
+
+ router = router_get_by_addr_port(addr,port);
+ if (!router)
+ {
+ log(LOG_DEBUG,"or_handshake_server_process_auth() : Received a connection from an unknown router. Will drop.");
+ return -1;
+ }
+ log(LOG_DEBUG,"or_handshake_server_process_auth() : Router identified as %s:%u.",
+ router->address,ntohs(router->port));
+
+ if(connection_get_by_addr_port(addr,port)) {
+ log(LOG_DEBUG,"or_handshake_server_process_auth(): That router is already connected. Dropping.");
+ return -1;
+ }
+
+ /* save keys */
+ memcpy(conn->b_session_key,buf+12,8);
+ 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);
+
+ conn->min = router->min;
+ conn->max = router->max;
+
+ if (conn->min > min)
+ conn->min = min;
+ if (conn->max > max)
+ conn->max = max;
+
+ /* copy all relevant info to conn */
+ conn->addr = router->addr, conn->port = router->port;
+ conn->pkey = router->pkey;
+ conn->address = strdup(router->address);
+
+ /* generate a nonce */
+ retval = RAND_pseudo_bytes(conn->nonce,8);
+ if (retval == -1) /* error */
+ {
+ log(LOG_ERR,"Cannot generate a nonce.");
+ return -1;
+ }
+ log(LOG_DEBUG,"or_handshake_server_process_auth() : Nonce generated.");
+
+ /* 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 */
+
+ /* encrypt message */
+ retval = RSA_public_encrypt(44,buf,cipher,conn->pkey,RSA_PKCS1_PADDING);
+ if (retval == -1) /* error */
+ {
+ log(LOG_ERR,"Public-key encryption failed during authentication to %s:%u.",conn->address,ntohs(conn->port));
+ log(LOG_DEBUG,"or_handshake_server_process_auth() : Reason : %s.",ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+ log(LOG_DEBUG,"or_handshake_server_process_auth() : Reply encrypted.");
+
+ /* send message */
+
+ if(connection_write_to_buf(cipher, 128, conn) < 0) {
+ log(LOG_DEBUG,"or_handshake_server_process_auth(): my outbuf is full. Oops.");
+ return -1;
+ }
+ retval = connection_flush_buf(conn);
+ if(retval < 0) {
+ log(LOG_DEBUG,"or_handshake_server_process_auth(): bad socket while flushing.");
+ return -1;
+ }
+ if(retval > 0) {
+ /* still stuff on the buffer. */
+ conn->state = OR_CONN_STATE_SERVER_SENDING_AUTH;
+ connection_watch_events(conn, POLLOUT | POLLIN);
+ return 0;
+ }
+
+ /* it finished sending */
+ log(LOG_DEBUG,"or_handshake_server_process_auth(): Finished sending auth.");
+ conn->state = OR_CONN_STATE_SERVER_NONCE_WAIT;
+ connection_watch_events(conn, POLLIN);
+ return 0;
+
+}
+
+int or_handshake_server_process_nonce(connection_t *conn) {
+
+ char buf[128];
+ char cipher[128];
+ int retval;
+
+ assert(conn);
+
+ if(conn->inbuf_datalen < 128) /* entire response available? */
+ return 0; /* not yet */
+
+ if(connection_fetch_from_buf(cipher,128,conn) < 0) {
+ return -1;
+ }
+ log(LOG_DEBUG,"or_handshake_server_process_nonce() : Received auth.");
+
+ /* decrypt response */
+ retval = RSA_private_decrypt(128,cipher,buf,conn->prkey,RSA_PKCS1_PADDING);
+ if (retval == -1)
+ {
+ log(LOG_ERR,"Public-key decryption failed during authentication to %s:%u.",
+ conn->address,ntohs(conn->port));
+ log(LOG_DEBUG,"or_handshake_server_process_nonce() : Reason : %s.",
+ ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+ else if (retval != 20)
+ {
+ log(LOG_ERR,"Received an incorrect response from router %s:%u during authentication.",
+ conn->address,ntohs(conn->port));
+ return -1;
+ }
+ log(LOG_DEBUG,"or_handshake_server_process_nonce() : Response decrypted.");
+
+ /* check validity */
+ if ((memcmp(&conn->addr,buf, 4)) || /* remote address */
+ (memcmp(&conn->port,buf+4,2)) || /* remote port */
+ (memcmp(&conn->local.sin_addr,buf+6,4)) || /* local address */
+ (memcmp(&conn->local.sin_port,buf+10,2)) || /* local port */
+ (memcmp(conn->nonce,buf+12,8))) /* nonce */
+ {
+ log(LOG_ERR,"Router %s:%u failed to authenticate. Either the key I have is obsolete or they're doing something they're not supposed to.",conn->address,ntohs(conn->port));
+ return -1;
+ }
+ log(LOG_DEBUG,"or_handshake_server_process_nonce() : Response valid. Authentication complete.");
+
+ conn_or_init_crypto(conn);
+ conn->state = OR_CONN_STATE_OPEN;
+ connection_watch_events(conn, POLLIN);
+ return connection_process_inbuf(conn); /* process the rest of the inbuf */
+
+}
+
+
+/* ********************************** */
+
+
+int connection_or_create_listener(RSA *prkey, struct sockaddr_in *local) {
+ log(LOG_DEBUG,"connection_create_or_listener starting");
+ return connection_create_listener(prkey, local, CONN_TYPE_OR_LISTENER);
+}
+
+int connection_or_handle_listener_read(connection_t *conn) {
+ log(LOG_NOTICE,"OR: Received a connection request from a router. Attempting to authenticate.");
+ return connection_handle_listener_read(conn, CONN_TYPE_OR, OR_CONN_STATE_SERVER_AUTH_WAIT);
+}
+
diff --git a/src/or/main.c b/src/or/main.c
new file mode 100644
index 000000000..85f91ab30
--- /dev/null
+++ b/src/or/main.c
@@ -0,0 +1,406 @@
+
+#include "or.h"
+
+/********* START VARIABLES **********/
+
+/* valid command-line options */
+static char *args = "hf:e:n:l:";
+
+int loglevel = LOG_DEBUG;
+
+/* valid config file options */
+config_opt_t options[] =
+{
+ {"RouterFile", CONFIG_TYPE_STRING, {0}, 0},
+ {"PrivateKeyFile", CONFIG_TYPE_STRING, {0}, 0},
+ {"EntryPort", CONFIG_TYPE_INT, {0}, 0},
+ {"NetworkPort", CONFIG_TYPE_INT, {0}, 0},
+ {"MaxConn", CONFIG_TYPE_INT, {0}, 0},
+ {"MaxConnTimeout", CONFIG_TYPE_INT, {0}, 0},
+ {"TrafficShaping", CONFIG_TYPE_INT, {0}, 0},
+ {0}
+};
+enum opts {
+ RouterFile=0, PrivateKeyFile, EntryPort, NetworkPort, MaxConn, MaxConnTimeout, TrafficShaping
+};
+
+connection_t *connection_array[MAXCONNECTIONS] =
+ { NULL };
+
+struct pollfd poll_array[MAXCONNECTIONS] =
+ { [0 ... MAXCONNECTIONS-1] = { -1, 0, 0 } };
+
+int nfds=0; /* number of connections currently active */
+
+/* default logging threshold */
+extern int loglevel;
+
+/* private key */
+RSA *prkey = NULL;
+
+/* router array */
+routerinfo_t **router_array = NULL;
+int rarray_len = 0;
+
+/********* END VARIABLES ************/
+
+int connection_add(connection_t *conn) {
+
+ if(nfds >= MAXCONNECTIONS-2) { /* 2, for some breathing room. should count the fenceposts. */
+ /* FIXME should use the 'max connections' option */
+ log(LOG_DEBUG,"connection_add(): failing because nfds is too high.");
+ return -1;
+ }
+
+ conn->poll_index = nfds;
+ connection_set_poll_socket(conn);
+ connection_array[nfds] = conn;
+
+ /* zero these out here, because otherwise we'll inherit values from the previously freed one */
+ poll_array[nfds].events = 0;
+ poll_array[nfds].revents = 0;
+
+ nfds++;
+
+ log(LOG_DEBUG,"connection_add(): new conn type %d, socket %d, nfds %d.",conn->type, conn->s, nfds);
+
+ return 0;
+
+}
+
+void connection_set_poll_socket(connection_t *conn) {
+ poll_array[conn->poll_index].fd = conn->s;
+}
+
+int connection_remove(connection_t *conn) {
+
+ int current_index;
+
+ assert(conn);
+ assert(nfds>0);
+
+ circuit_about_to_close_connection(conn); /* flush and send destroys for all circuits on this conn */
+
+ current_index = conn->poll_index;
+ if(current_index == nfds-1) { /* this is the end */
+// connection_free(conn);
+ nfds--;
+ log(LOG_DEBUG,"connection_remove(): nfds now %d.",nfds);
+ return 0;
+ }
+
+ /* we replace this one with the one at the end, then free it */
+ nfds--;
+ poll_array[current_index].fd = poll_array[nfds].fd;
+ poll_array[current_index].events = poll_array[nfds].events;
+ poll_array[current_index].revents = poll_array[nfds].revents;
+ connection_array[current_index] = connection_array[nfds];
+ connection_array[current_index]->poll_index = current_index;
+
+ log(LOG_DEBUG,"connection_remove(): nfds now %d.",nfds);
+
+ return 0;
+}
+
+connection_t *connection_get_by_addr_port(uint32_t addr, uint16_t port) {
+
+ int i;
+ connection_t *conn;
+
+ for(i=0;i<nfds;i++) {
+ conn = connection_array[i];
+ assert(conn);
+ if(conn->addr == addr && conn->port == port)
+ return conn;
+ }
+
+ return NULL;
+
+}
+
+connection_t *connection_get_by_type(int type) {
+
+ int i;
+ connection_t *conn;
+
+ for(i=0;i<nfds;i++) {
+ conn = connection_array[i];
+ if(conn->type == type)
+ return conn;
+ }
+
+ return NULL;
+
+}
+
+routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) {
+ int i;
+ routerinfo_t *router;
+
+ if (!router_array)
+ return NULL;
+
+ for(i=0;i<rarray_len;i++)
+ {
+ router = router_array[i];
+ if ((router->addr == addr) && (router->port == port))
+ return router;
+ }
+
+ return NULL;
+
+}
+
+void connection_watch_events(connection_t *conn, short events) {
+
+ assert(conn && conn->poll_index < nfds);
+
+ poll_array[conn->poll_index].events = events;
+}
+
+void check_conn_read(int i) {
+
+ int retval;
+ connection_t *conn;
+
+ if(poll_array[i].revents & POLLIN) { /* something to read */
+
+ conn = connection_array[i];
+ assert(conn);
+ 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);
+ } else if (conn->type == CONN_TYPE_OR_LISTENER) {
+ retval = connection_or_handle_listener_read(conn);
+ } else {
+ /* else it's an OP, OR, or app */
+ 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);
+ }
+ }
+
+ if(retval < 0) { /* this connection is broken. remove it */
+ log(LOG_DEBUG,"check_conn_read(): Connection broken, removing.");
+ connection_remove(conn);
+ connection_free(conn);
+ if(i<nfds) { /* we just replaced the one at i with a new one.
+ process it too. */
+ check_conn_read(i);
+ }
+ }
+ }
+}
+
+void check_conn_write(int i) {
+
+ int retval;
+ connection_t *conn;
+
+ if(poll_array[i].revents & POLLOUT) { /* something to write */
+
+ conn = connection_array[i];
+ log(LOG_DEBUG,"check_conn_write(): socket %d wants to write.",conn->s);
+
+ if(conn->type == CONN_TYPE_OP_LISTENER ||
+ conn->type == CONN_TYPE_OR_LISTENER) {
+ log(LOG_DEBUG,"check_conn_write(): Got a listener socket. Can't happen!");
+ retval = -1;
+ } else {
+ /* else it's an OP, OR, or app */
+ retval = connection_flush_buf(conn); /* conns in CONNECTING state will fall through... */
+ if(retval == 0) { /* it's done flushing */
+ retval = connection_finished_flushing(conn); /* ...and get handled here. */
+ }
+ }
+
+ if(retval < 0) { /* this connection is broken. remove it. */
+ log(LOG_DEBUG,"check_conn_write(): Connection broken, removing.");
+ connection_remove(conn);
+ connection_free(conn);
+ if(i<nfds) { /* we just replaced the one at i with a new one.
+ process it too. */
+ check_conn_read(i);
+ }
+ }
+ }
+}
+
+void check_conn_marked(int i) {
+ connection_t *conn;
+
+ conn = connection_array[i];
+ assert(conn);
+ if(conn->marked_for_close) {
+ log(LOG_DEBUG,"check_conn_marked(): Cleaning up connection.");
+ connection_flush_buf(conn); /* flush it first */
+ connection_remove(conn);
+ connection_free(conn);
+ if(i<nfds) { /* we just replaced the one at i with a new one.
+ process it too. */
+ check_conn_read(i);
+ }
+ }
+}
+
+#if 0
+void check_conn_hup(int i) {
+ connection_t *conn;
+
+ if(poll_array[i].revents & POLLHUP) { /* they've hung up */
+ conn = connection_array[i];
+ log(LOG_DEBUG,"check_conn_hup(): socket %d has hung up.",conn->s);
+ connection_remove(conn);
+ connection_free(conn);
+
+ if(i<nfds) { /* we just replaced the one at i with a new one.
+ process it too. */
+ check_conn_hup(i);
+ }
+ }
+}
+#endif
+
+int do_main_loop(void) {
+
+ int i;
+
+ /* load the routers file */
+ router_array = getrouters(options[RouterFile].r.str,&rarray_len);
+ if (!router_array)
+ {
+ log(LOG_ERR,"Error loading router list.");
+ exit(1);
+ }
+
+ /* load the private key */
+ ERR_load_crypto_strings();
+ prkey = load_prkey(options[PrivateKeyFile].r.str);
+ if (!prkey)
+ {
+ log(LOG_ERR,"Error loading private key.");
+ exit(1);
+ }
+ log(LOG_DEBUG,"core : Loaded private key of size %u bytes.",RSA_size(prkey));
+ ERR_free_strings();
+
+ /* try to connect to all the other ORs, and start the listeners */
+ retry_all_connections(router_array, rarray_len, prkey,
+ options[NetworkPort].r.i,options[EntryPort].r.i);
+
+ for(;;) {
+ poll(poll_array, nfds, -1); /* poll until we have an event */
+
+ /* do all the reads first, so we can detect closed sockets */
+ for(i=0;i<nfds;i++)
+ check_conn_read(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);
+
+#if 0 /* no, check_conn_read() takes care of hups. */
+ /* remove the ones that have disconnected */
+ for(i=0;i<nfds;i++)
+ check_conn_hup(i);
+#endif
+ }
+
+}
+
+void catch ()
+{
+ errno = 0; /* netcat does this. it looks fun. */
+
+ log(LOG_DEBUG,"Catching ^c, exiting cleanly.");
+
+ exit(0);
+
+}
+
+int main(int argc, char *argv[])
+{
+ int retval = 0;
+
+ char *conf_filename = NULL; /* configuration file */
+ signal (SIGINT, catch); /* to catch ^c so we can exit cleanly */
+
+ /* get command-line arguments */
+ retval = getargs(argc,argv,args,&conf_filename,&loglevel);
+ if (retval == -1)
+ {
+ log(LOG_ERR,"Error processing command-line arguments.");
+ exit(1);
+ }
+
+ /* load config file */
+ retval = getconfig(conf_filename,options);
+ if (retval == -1)
+ {
+ log(LOG_ERR,"Error loading configuration file.");
+ exit(1);
+ }
+ else if (options[RouterFile].err != 1)
+ {
+ log(LOG_ERR,"RouterFile option required, but not found.");
+ exit(1);
+ }
+ else if (options[PrivateKeyFile].err != 1)
+ {
+ log(LOG_ERR,"PrivateKeyFile option required but not found.");
+ exit(1);
+ }
+ else if (options[EntryPort].err != 1)
+ {
+ log(LOG_ERR,"EntryPort option required but not found.");
+ exit(1);
+ }
+ else if (options[NetworkPort].err != 1)
+ {
+ log(LOG_ERR,"NetworkPort option required but not found.");
+ exit(1);
+ }
+ else if (options[MaxConn].err != 1)
+ {
+ log(LOG_ERR,"MaxConn option required but not found.");
+ exit(1);
+ }
+#if 0
+ else if (options[MaxConnTimeout].err != 1)
+ {
+ conn_tout.tv_sec = OR_DEFAULT_CONN_TIMEOUT;
+ }
+ else
+ {
+ if (!options[MaxConnTimeout].r.i)
+ conn_toutp = NULL;
+ else
+ conn_tout.tv_sec = options[MaxConnTimeout].r.i;
+ }
+ conn_tout.tv_usec = 0;
+
+ if (!options[TrafficShaping].err)
+ {
+ options[TrafficShaping].r.i = DEFAULT_POLICY;
+ }
+ else if ((options[TrafficShaping].r.i < 0) || (options[TrafficShaping].r.i > 1))
+ {
+ log(LOG_ERR,"Invalid value for the TrafficShaping option.");
+ exit(1);
+ }
+#endif
+
+ ERR_load_crypto_strings();
+ retval = do_main_loop();
+ ERR_free_strings();
+
+ return retval;
+
+}
+
diff --git a/src/or/onion.c b/src/or/onion.c
new file mode 100644
index 000000000..d9c8a9c5d
--- /dev/null
+++ b/src/or/onion.c
@@ -0,0 +1,62 @@
+
+#include "or.h"
+
+/********* START VARIABLES **********/
+
+tracked_onion_t *tracked_onions = NULL; /* linked list of tracked onions */
+tracked_onion_t *last_tracked_onion = NULL;
+
+/********* END VARIABLES ************/
+
+
+int decide_aci_type(uint32_t local_addr, uint16_t local_port,
+ uint32_t remote_addr, uint16_t remote_port) {
+
+ if(local_addr > remote_addr)
+ return ACI_TYPE_HIGHER;
+ if(local_addr < remote_addr)
+ return ACI_TYPE_LOWER;
+ if(local_port > remote_port)
+ return ACI_TYPE_HIGHER;
+ /* else */
+ return ACI_TYPE_LOWER;
+}
+
+int process_onion(circuit_t *circ, connection_t *conn) {
+ aci_t aci_type;
+
+ if(!decrypt_onion((onion_layer_t *)circ->onion,circ->onionlen,conn->prkey)) {
+ log(LOG_DEBUG,"command_process_create_cell(): decrypt_onion() failed, closing circuit.");
+ return -1;
+ }
+ log(LOG_DEBUG,"command_process_create_cell(): Onion decrypted.");
+
+ /* check freshness */
+ if (((onion_layer_t *)circ->onion)->expire < time(NULL)) /* expired onion */
+ {
+ log(LOG_NOTICE,"I have just received an expired onion. This could be a replay attack.");
+ return -1;
+ }
+
+ aci_type = decide_aci_type(conn->local.sin_addr.s_addr, conn->local.sin_port,
+ ((onion_layer_t *)circ->onion)->addr,((onion_layer_t *)circ->onion)->port);
+
+ if(circuit_init(circ, aci_type) < 0) {
+ log(LOG_ERR,"process_onion(): init_circuit() failed.");
+ return -1;
+ }
+
+ /* check for replay */
+ if(id_tracked_onion(circ->onion, circ->onionlen, tracked_onions)) {
+ log(LOG_NOTICE,"process_onion(): I have just received a replayed onion. This could be a replay attack.");
+ return -1;
+ }
+
+ /* track the new onion */
+ if(!new_tracked_onion(circ->onion,circ->onionlen, &tracked_onions, &last_tracked_onion)) {
+ log(LOG_DEBUG,"process_onion(): Onion tracking failed. Will ignore.");
+ }
+
+ return 0;
+}
+
diff --git a/src/or/or.h b/src/or/or.h
new file mode 100644
index 000000000..1d73e123a
--- /dev/null
+++ b/src/or/or.h
@@ -0,0 +1,411 @@
+/* Copyright (c) 2002 Roger Dingledine. See LICENSE for licensing information */
+/* $Id$ */
+
+#ifndef __OR_H
+#define __OR_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#include "../common/cell.h"
+#include "../common/config.h"
+#include "../common/key.h"
+#include "../common/log.h"
+#include "../common/onion.h"
+#include "../common/ss.h"
+
+#define MAXCONNECTIONS 200 /* upper bound on max connections.
+ can be overridden by config file */
+
+#define MAX_BUF_SIZE (64*1024)
+
+#define ACI_TYPE_LOWER 0
+#define ACI_TYPE_HIGHER 1
+
+#define CONN_TYPE_OP_LISTENER 1
+#define CONN_TYPE_OP 2
+#define CONN_TYPE_OR_LISTENER 3
+#define CONN_TYPE_OR 4
+#define CONN_TYPE_APP 5
+
+#define LISTENER_STATE_READY 0
+
+#define OP_CONN_STATE_AWAITING_KEYS 0
+#define OP_CONN_STATE_OPEN 1
+#if 0
+#define OP_CONN_STATE_CLOSE 2 /* flushing the buffer, then will close */
+#define OP_CONN_STATE_CLOSE_WAIT 3 /* have sent a destroy, awaiting a confirmation */
+#endif
+
+#define OR_CONN_STATE_CLIENT_CONNECTING 0
+#define OR_CONN_STATE_CLIENT_SENDING_AUTH 1 /* sending address and info */
+#define OR_CONN_STATE_CLIENT_AUTH_WAIT 2 /* have sent address and info, waiting */
+#define OR_CONN_STATE_CLIENT_SENDING_NONCE 3 /* sending nonce, last piece of handshake */
+#define OR_CONN_STATE_SERVER_AUTH_WAIT 4 /* waiting for address and info */
+#define OR_CONN_STATE_SERVER_SENDING_AUTH 5 /* writing auth and nonce */
+#define OR_CONN_STATE_SERVER_NONCE_WAIT 6 /* waiting for confirmation of nonce */
+#define OR_CONN_STATE_OPEN 7 /* ready to send/receive cells. */
+
+#define APP_CONN_STATE_CONNECTING_WAIT 0 /* waiting for standard structure or dest info */
+#define APP_CONN_STATE_CONNECTING 1
+#define APP_CONN_STATE_OPEN 2
+#if 0
+#define APP_CONN_STATE_CLOSE 3 /* flushing the buffer, then will close */
+#define APP_CONN_STATE_CLOSE_WAIT 4 /* have sent a destroy, awaiting a confirmation */
+#endif
+
+#define CIRCUIT_STATE_OPEN_WAIT 0 /* receiving/processing the onion */
+#define CIRCUIT_STATE_OPEN 1 /* onion processed, ready to send data along the connection */
+#define CIRCUIT_STATE_CLOSE_WAIT1 2 /* sent two "destroy" signals, waiting for acks */
+#define CIRCUIT_STATE_CLOSE_WAIT2 3 /* received one ack, waiting for one more
+ (or if just one was sent, waiting for that one */
+//#define CIRCUIT_STATE_CLOSE 4 /* both acks received, connection is dead */ /* NOT USED */
+
+typedef uint16_t aci_t;
+
+typedef struct
+{
+
+/* Used by all types: */
+
+ unsigned char type;
+ int state;
+ int s; /* our socket */
+ int poll_index;
+ int marked_for_close;
+
+ char *inbuf;
+ size_t inbuflen;
+ size_t inbuf_datalen;
+ int inbuf_reached_eof;
+
+ char *outbuf;
+ size_t outbuflen;
+ size_t outbuf_datalen;
+
+/* used by OP and App: */
+
+ 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? */
+
+ /* link encryption */
+ unsigned char f_session_key[8];
+ unsigned char b_session_key[8];
+ unsigned char f_session_iv[8];
+ unsigned char b_session_iv[8];
+ EVP_CIPHER_CTX f_ctx;
+ EVP_CIPHER_CTX b_ctx;
+
+// struct timeval lastsend; /* time of last transmission to the client */
+// struct timeval interval; /* transmission interval */
+
+ uint32_t addr; /* these two uniquely identify a router */
+ uint16_t port;
+
+/* used by app: */
+
+ ss_t ss; /* standard structure */
+ int ss_received; /* size of ss, received so far */
+
+/* used by OR, to keep state while connect()ing: Kludge. */
+
+ RSA *prkey;
+ struct sockaddr_in local;
+
+ /* link info */
+ uint32_t min;
+ uint32_t max;
+
+ char *address; /* strdup into this, gets free_connection frees it */
+ RSA *pkey; /* public RSA key for the other side */
+
+ char nonce[8];
+
+} connection_t;
+
+/* config stuff we know about the other ORs in the network */
+typedef struct
+{
+ char *address;
+
+ uint32_t addr;
+ uint16_t port;
+
+ RSA *pkey; /* public RSA key */
+
+ /* link info */
+ uint32_t min;
+ uint32_t max;
+// struct timeval min_interval;
+
+ /* time when last data was sent to that router */
+// struct timeval lastsend;
+
+ /* socket */
+// int s;
+
+ void *next;
+} routerinfo_t;
+
+/* per-anonymous-connection struct */
+typedef struct
+{
+#if 0
+ uint32_t p_addr; /* all in network order */
+ uint16_t p_port;
+#endif
+ uint32_t n_addr;
+ uint16_t n_port;
+ connection_t *p_conn;
+ connection_t *n_conn;
+
+ aci_t p_aci; /* connection identifiers */
+ aci_t n_aci;
+
+ unsigned char p_f; /* crypto functions */
+ unsigned char n_f;
+
+ unsigned char p_key[128]; /* crypto keys */
+ unsigned char n_key[128];
+
+ unsigned char p_iv[16]; /* initialization vectors */
+ unsigned char n_iv[16];
+
+ EVP_CIPHER_CTX p_ctx; /* cipher context */
+ EVP_CIPHER_CTX n_ctx;
+
+ uint32_t expire; /* expiration time for the corresponding onion */
+
+ int state;
+
+ unsigned char *onion; /* stores the onion when state is CONN_STATE_OPEN_WAIT */
+ uint32_t onionlen; /* total onion length */
+ uint32_t recvlen; /* length of the onion so far */
+
+ void *next;
+} circuit_t;
+
+
+
+
+ /* 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 **pbuf, size_t *pbuflen, size_t *pbuf_datalen);
+
+int buf_free(char *buf);
+
+int read_to_buf(int s, char **pbuf, size_t *pbuflen, size_t *pbuf_datalen, int *preached_eof);
+ /* grab from s, put onto buf, return how many bytes read */
+
+int flush_buf(int s, char **pbuf, size_t *pbuflen, size_t *pbuf_datalen);
+ /* push from buf onto s
+ * then memmove to front of buf
+ * return -1 or how many bytes remain on the buf */
+
+int write_to_buf(char *string, size_t string_len,
+ char **pbuf, size_t *pbuflen, size_t *pbuf_datalen);
+ /* append string to buf (growing as needed, return -1 if "too big")
+ * return total number of bytes on the buf
+ */
+
+int fetch_from_buf(char *string, size_t string_len,
+ char **pbuf, size_t *pbuflen, size_t *pbuf_datalen);
+ /* if there is string_len bytes in buf, write them onto string,
+ * * then memmove buf back (that is, remove them from buf) */
+
+/********************************* cell.c ***************************/
+
+int check_sane_cell(cell_t *cell);
+
+/********************************* circuit.c ***************************/
+
+void circuit_add(circuit_t *circ);
+void circuit_remove(circuit_t *circ);
+
+circuit_t *circuit_new(aci_t p_aci, connection_t *p_conn);
+
+/* internal */
+aci_t get_unique_aci_by_addr_port(uint32_t addr, uint16_t port, int aci_type);
+
+circuit_t *circuit_get_by_aci_conn(aci_t aci, connection_t *conn);
+circuit_t *circuit_get_by_conn(connection_t *conn);
+
+int circuit_deliver_data_cell(cell_t *cell, circuit_t *circ, connection_t *conn, int crypt_type);
+int circuit_crypt(circuit_t *circ, char *in, size_t inlen, char crypt_type);
+
+int circuit_init(circuit_t *circ, int aci_type);
+void circuit_free(circuit_t *circ);
+
+void circuit_close(circuit_t *circ);
+
+void circuit_about_to_close_connection(connection_t *conn);
+ /* flush and send destroys for all circuits using conn */
+
+/********************************* command.c ***************************/
+
+void command_process_cell(cell_t *cell, connection_t *conn);
+
+void command_process_create_cell(cell_t *cell, connection_t *conn);
+void command_process_data_cell(cell_t *cell, connection_t *conn);
+void command_process_destroy_cell(cell_t *cell, connection_t *conn);
+
+/********************************* config.c ***************************/
+
+/* loads the configuration file */
+int getconfig(char *filename, config_opt_t *options);
+
+/********************************* connection.c ***************************/
+
+connection_t *connection_new(int type);
+
+void connection_free(connection_t *conn);
+
+int connection_create_listener(RSA *prkey, struct sockaddr_in *local, int type);
+
+int connection_handle_listener_read(connection_t *conn, int new_type, int new_state);
+
+/* start all connections that should be up but aren't */
+int retry_all_connections(routerinfo_t **router_array, int rarray_len,
+ RSA *prkey, uint16_t or_port, uint16_t op_port);
+
+int connection_read_to_buf(connection_t *conn);
+
+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);
+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);
+
+int connection_process_inbuf(connection_t *conn);
+int connection_process_cell_from_inbuf(connection_t *conn);
+
+int connection_finished_flushing(connection_t *conn);
+
+/********************************* connection_or.c ***************************/
+
+int connection_or_process_inbuf(connection_t *conn);
+int connection_or_finished_flushing(connection_t *conn);
+
+connection_t *connection_or_new(void);
+connection_t *connection_or_listener_new(void);
+
+void conn_or_init_crypto(connection_t *conn);
+
+int or_handshake_client_process_auth(connection_t *conn);
+int or_handshake_client_send_auth(connection_t *conn);
+
+int or_handshake_server_process_auth(connection_t *conn);
+int or_handshake_server_process_nonce(connection_t *conn);
+
+int connect_to_router(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local);
+
+int connection_or_create_listener(RSA *prkey, struct sockaddr_in *local);
+int connection_or_handle_listener_read(connection_t *conn);
+
+/********************************* connection_op.c ***************************/
+
+connection_t *connection_op_new(void);
+connection_t *connection_op_listener_new(void);
+
+int op_handshake_process_keys(connection_t *conn);
+
+int connection_op_process_inbuf(connection_t *conn);
+
+int connection_op_finished_flushing(connection_t *conn);
+
+int connection_op_create_listener(RSA *prkey, struct sockaddr_in *local);
+
+int connection_op_handle_listener_read(connection_t *conn);
+
+/********************************* connection_app.c ***************************/
+
+connection_t *connection_app_new(void);
+
+int connection_app_process_inbuf(connection_t *conn);
+int connection_app_package_inbuf(connection_t *conn);
+int connection_app_process_data_cell(cell_t *cell, connection_t *conn);
+
+int connection_app_finished_flushing(connection_t *conn);
+
+/********************************* main.c ***************************/
+
+int connection_add(connection_t *conn);
+int connection_remove(connection_t *conn);
+void connection_set_poll_socket(connection_t *conn);
+
+connection_t *connection_get_by_addr_port(uint32_t addr, uint16_t port);
+
+connection_t *connection_get_by_type(int type);
+
+routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port);
+
+void connection_watch_events(connection_t *conn, short events);
+
+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 do_main_loop(void);
+
+int main(int argc, char *argv[]);
+
+/********************************* onion.c ***************************/
+
+int decide_aci_type(uint32_t local_addr, uint16_t local_port,
+ uint32_t remote_addr, uint16_t remote_port);
+
+int process_onion(circuit_t *circ, connection_t *conn);
+
+/********************************* routers.c ***************************/
+
+routerinfo_t **getrouters(char *routerfile, size_t *listlenp);
+void delete_routerlist(routerinfo_t *list);
+/* create an NULL-terminated array of pointers pointing to elements of a router list */
+routerinfo_t **make_rarray(routerinfo_t* list, size_t *listlenp);
+
+
+#endif
diff --git a/src/or/routers.c b/src/or/routers.c
new file mode 100644
index 000000000..d24d4d1bd
--- /dev/null
+++ b/src/or/routers.c
@@ -0,0 +1,365 @@
+/**
+ * routers.c
+ * Routines for loading the list of routers and their public RSA keys.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+#define OR_ROUTERLIST_SEPCHARS " \t\n"
+#define OR_PUBLICKEY_BEGIN_TAG "-----BEGIN RSA PUBLIC KEY-----\n"
+
+#include "or.h"
+
+/* delete a list of routers from memory */
+void delete_routerlist(routerinfo_t *list)
+{
+ routerinfo_t *tmp = NULL;
+
+ if (!list)
+ return;
+
+ do
+ {
+ tmp=list->next;
+ free((void *)list->address);
+ RSA_free(list->pkey);
+ free((void *)list);
+ list = tmp;
+ }
+ while (list != NULL);
+
+ return;
+}
+
+/* create an NULL-terminated array of pointers pointing to elements of a router list */
+/* this is done in two passes through the list - inefficient but irrelevant as this is
+ * only done once when op/or start up */
+routerinfo_t **make_rarray(routerinfo_t* list, size_t *len)
+{
+ routerinfo_t *tmp=NULL;
+ int listlen = 0;
+ routerinfo_t **array=NULL;
+ routerinfo_t **p=NULL;
+
+ if ((!list) || (!len))
+ return NULL;
+
+ /* get the length of the list */
+ tmp = list;
+ do
+ {
+ listlen++;
+ tmp = tmp->next;
+ }
+ while (tmp != NULL);
+
+ array = malloc((listlen+1)*sizeof(routerinfo_t *));
+ if (!array)
+ {
+ log(LOG_ERR,"Error allocating memory.");
+ return NULL;
+ }
+
+ tmp=list;
+ p = array;
+ do
+ {
+ *p = tmp;
+ p++;
+ tmp = tmp->next;
+ }
+ while(tmp != NULL);
+ *p=NULL;
+
+ *len = listlen;
+ return array;
+}
+
+/* load the router list */
+routerinfo_t **getrouters(char *routerfile, size_t *lenp)
+{
+ int retval = 0;
+ char *retp = NULL;
+ routerinfo_t *router=NULL, *routerlist=NULL, *lastrouter=NULL;
+ FILE *rf; /* router file */
+ fpos_t fpos;
+ char line[512];
+ char *token;
+ char *errtest; /* detecting errors in strtoul() calls */
+ struct hostent *rent;
+
+ if ((!routerfile) || (!lenp))
+ return NULL;
+
+ if (strcspn(routerfile,CONFIG_LEGAL_FILENAME_CHARACTERS) != 0)
+ {
+ log(LOG_ERR,"Filename %s contains illegal characters.",routerfile);
+ return NULL;
+ }
+
+ /* open the router list */
+ rf = fopen(routerfile,"r");
+ if (!rf)
+ {
+ log(LOG_ERR,"Could not open %s.",routerfile);
+ return NULL;
+ }
+
+ retp= fgets(line,512,rf);
+ while (retp)
+ {
+ log(LOG_DEBUG,"getrouters():Line :%s",line);
+ token = (char *)strtok(line,OR_ROUTERLIST_SEPCHARS);
+ if (token)
+ {
+ log(LOG_DEBUG,"getrouters():Token : %s",token);
+ if (token[0] != '#') /* ignore comment lines */
+ {
+ router = malloc(sizeof(routerinfo_t));
+ if (!router)
+ {
+ log(LOG_ERR,"Could not allocate memory.");
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+
+#if 0
+ router->conn_bufs = NULL; /* no output buffers */
+ router->last_conn_buf = NULL;
+ router->next_to_service = 0;
+
+ router->s = -1; /* to signify this router is as yet unconnected */
+ router->celllen = 0; /* cell buffer is empty */
+#endif
+
+ /* read the address */
+ router->address = malloc(strlen(token)+1);
+ if (!router->address)
+ {
+ log(LOG_ERR,"Could not allocate memory.");
+ fclose(rf);
+ free((void *)router);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ strcpy(router->address,token);
+
+ rent = (struct hostent *)gethostbyname(router->address);
+ if (!rent)
+ {
+ log(LOG_ERR,"Could not get address for router %s.",router->address);
+ fclose(rf);
+ free((void *)router->address);
+ free((void *)router);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+
+ memcpy(&router->addr, rent->h_addr,rent->h_length);
+
+ /* read the port */
+ token = (char *)strtok(NULL,OR_ROUTERLIST_SEPCHARS);
+ if (token)
+ {
+ log(LOG_DEBUG,"getrouters():Token :%s",token);
+ router->port = (uint16_t)strtoul(token,&errtest,0);
+ if ((*token != '\0') && (*errtest == '\0')) /* conversion was successful */
+ {
+ /* convert port to network format */
+ router->port = htons(router->port);
+
+ /* read min bandwidth */
+ token = (char *)strtok(NULL,OR_ROUTERLIST_SEPCHARS);
+ if (token) /* min bandwidth */
+ {
+ router->min = (uint32_t)strtoul(token,&errtest,0);
+ if ((*token != '\0') && (*errtest == '\0')) /* conversion was successful */
+ {
+ if (router->min) /* must not be zero */
+ {
+ /* read max bandwidth */
+ token = (char *)strtok(NULL,OR_ROUTERLIST_SEPCHARS);
+ if (token) /* max bandwidth */
+ {
+ router->max = (uint32_t)strtoul(token,&errtest,0);
+ if ((*token != '\0') && (*errtest == '\0')) /* conversion was successful */
+ {
+ if (router->max) /* must not be zero */
+ {
+ /* check that there is a public key entry for that router */
+ retval = fgetpos(rf, &fpos); /* save the current file position
+ * we wil return to it later if we find a public key */
+ if (retval == -1)
+ {
+ log(LOG_ERR,"Could not save position in %s.",routerfile);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ do /* read through to the next non-empty line */
+ {
+ retp=fgets(line,512,rf);
+ if (!retp)
+ {
+ log(LOG_ERR,"Could not find a public key entry for router %s:%u.",router->address,router->port);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ log(LOG_DEBUG,"getrouters():Line:%s",line);
+ if ((*line != '#') && ( strspn(line,OR_ROUTERLIST_SEPCHARS) != strlen(line) ))
+ {
+ break;
+ }
+ } while (1);
+
+ if (!strcmp(line,OR_PUBLICKEY_BEGIN_TAG)) /* we've got the public key */
+ {
+ retval = fsetpos(rf,&fpos); /* get us back to where we were otherwise crypto lib won't find the key */
+ if (retval == -1)
+ {
+ log(LOG_ERR,"Could not set position in %s.",routerfile);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ }
+ else /* we found something else; this isn't right */
+ {
+ log(LOG_ERR,"Could not find a public key entry for router %s:%u.",router->address,router->port);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+
+ log(LOG_DEBUG,"getrouters():Reading the key ...");
+ /* read the public key into router->pkey */
+ router->pkey=NULL;
+ router->pkey = PEM_read_RSAPublicKey(rf,&router->pkey,NULL,NULL);
+ if (!router->pkey) /* something went wrong */
+ {
+ log(LOG_ERR,"Could not read public key for router %s:%u.",router->address,router->port);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ else /* read the key */
+ {
+ log(LOG_DEBUG,"getrouters():Public key size = %u.", RSA_size(router->pkey));
+ if (RSA_size(router->pkey) != 128) /* keys MUST be 1024 bits in size */
+ {
+ log(LOG_ERR,"Key for router %s:%u is not 1024 bits. All keys must be exactly 1024 bits long.",router->address,router->port);
+ free((void *)router->address);
+ RSA_free(router->pkey);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ router->next = NULL;
+ /* save the entry into the routerlist linked list */
+ if (!routerlist) /* this is the first entry */
+ routerlist = router;
+ else
+ lastrouter->next = (void *)router;
+ lastrouter = router;
+ }
+ }
+ else /* maximum link utilisation is zero */
+ {
+ log(LOG_ERR,"Entry for router %s doesn't contain a valid maximum bandwidth entry (must be > 0).",router->address);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ }
+ else
+ {
+ log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid maximum bandwidth entry.",router->address);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ }
+ else
+ {
+ log(LOG_ERR,"Entry for router %s doesn't seem to contain a maximum bandwidth entry.",router->address);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ }
+ else
+ {
+ log(LOG_ERR,"Entry for router %s doesn't contain a valid minimum bandwidth entry (must be > 0).",router->address);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ }
+ else
+ {
+ log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid minimum bandwidth entry.",router->address);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ }
+ else
+ {
+ log(LOG_ERR,"Entry for router %s doesn't seem to contain a minimum bandwidth entry.",router->address);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ }
+ else
+ {
+ log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid port number.",router->address);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ }
+ else
+ {
+ log(LOG_ERR,"Entry for router %s doesn't seem to contain a port number.",router->address);
+ free((void *)router->address);
+ free((void *)router);
+ fclose(rf);
+ delete_routerlist(routerlist);
+ return NULL;
+ }
+ }
+ }
+ retp=fgets(line,512,rf);
+ }
+
+ fclose(rf);
+ return make_rarray(routerlist, lenp);
+}