| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660 |
- /* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */
- /* See LICENSE for licensing information */
- /* $Id$ */
- #define OR_PUBLICKEY_END_TAG "-----END RSA PUBLIC KEY-----\n"
- #include "or.h"
- /****************************************************************************/
- /* router array */
- static routerinfo_t **router_array = NULL;
- static int rarray_len = 0;
- extern or_options_t options; /* command-line and config-file options */
- extern routerinfo_t *my_routerinfo; /* from main.c */
- /****************************************************************************/
- /* static function prototypes */
- static void routerlist_free(routerinfo_t *list);
- static routerinfo_t **make_rarray(routerinfo_t* list, int *len);
- static char *eat_whitespace(char *s);
- static char *find_whitespace(char *s);
- static routerinfo_t *router_get_entry_from_string(char **s);
- static void router_add_exit_policy(routerinfo_t *router, char *string);
- static void router_free_exit_policy(routerinfo_t *router);
- /****************************************************************************/
- int learn_my_address(struct sockaddr_in *me) {
- /* local host information */
- char localhostname[512];
- struct hostent *localhost;
- /* obtain local host information */
- if(gethostname(localhostname,512) < 0) {
- log(LOG_ERR,"Error obtaining local hostname.");
- return -1;
- }
- log(LOG_DEBUG,"learn_my_address(): localhostname is '%s'.",localhostname);
- localhost = gethostbyname(localhostname);
- if (!localhost) {
- log(LOG_ERR,"Error obtaining local host info.");
- return -1;
- }
- memset(me,0,sizeof(struct sockaddr_in));
- me->sin_family = AF_INET;
- memcpy((void *)&me->sin_addr,(void *)localhost->h_addr,sizeof(struct in_addr));
- me->sin_port = htons(options.ORPort);
- log(LOG_DEBUG,"learn_my_address(): chose address as '%s'.",inet_ntoa(me->sin_addr));
- return 0;
- }
- void router_retry_connections(void) {
- int i;
- routerinfo_t *router;
- for (i=0;i<rarray_len;i++) {
- router = router_array[i];
- if(!connection_exact_get_by_addr_port(router->addr,router->or_port)) { /* not in the list */
- log(LOG_DEBUG,"retry_all_connections(): connecting to OR %s:%u.",router->address,router->or_port);
- connection_or_connect_as_or(router);
- }
- }
- }
- routerinfo_t *router_pick_directory_server(void) {
- /* currently, pick the first router with a positive dir_port */
- int i;
- routerinfo_t *router;
-
- if(!router_array)
- return NULL;
- for(i=0;i<rarray_len;i++) {
- router = router_array[i];
- if(router->dir_port > 0)
- return router;
- }
- return NULL;
- }
- routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) {
- int i;
- routerinfo_t *router;
- assert(router_array);
- for(i=0;i<rarray_len;i++) {
- router = router_array[i];
- if ((router->addr == addr) && (router->or_port == port))
- return router;
- }
- return NULL;
- }
- routerinfo_t *router_get_first_in_route(unsigned int *route, int routelen) {
- return router_array[route[routelen-1]];
- }
- /* a wrapper around new_route. put all these in routers.c perhaps? */
- unsigned int *router_new_route(int *routelen) {
- return new_route(options.CoinWeight, router_array, rarray_len, routelen);
- }
- /* a wrapper around create_onion */
- unsigned char *router_create_onion(unsigned int *route, int routelen, int *len, crypt_path_t **cpath) {
- return create_onion(router_array,rarray_len,route,routelen,len,cpath);
- }
- /* return 1 if addr and port corresponds to my addr and my or_listenport. else 0,
- * or -1 for failure.
- */
- int router_is_me(uint32_t addr, uint16_t port)
- {
- struct sockaddr_in me; /* my router identity */
- if(!options.ORPort) {
- /* we're not an OR. This obviously isn't us. */
- return 0;
- }
-
- if(learn_my_address(&me) < 0)
- return -1;
- if(ntohl(me.sin_addr.s_addr) == addr && ntohs(me.sin_port) == port)
- return 1;
- return 0;
- #if 0
- /* local host information */
- char localhostname[512];
- struct hostent *localhost;
- struct in_addr *a;
- char *tmp1;
-
- char *addr = NULL;
- int i = 0;
- /* obtain local host information */
- if (gethostname(localhostname,512) < 0) {
- log(LOG_ERR,"router_is_me(): Error obtaining local hostname.");
- return -1;
- }
- localhost = gethostbyname(localhostname);
- if (!localhost) {
- log(LOG_ERR,"router_is_me(): Error obtaining local host info.");
- return -1;
- }
-
- /* check host addresses for a match with or_address above */
- addr = localhost->h_addr_list[i++]; /* set to the first local address */
- while(addr)
- {
- a = (struct in_addr *)addr;
- tmp1 = strdup(inet_ntoa(*a)); /* can't call inet_ntoa twice in the same
- printf, since it overwrites its static
- memory each time */
- log(LOG_DEBUG,"router_is_me(): Comparing '%s' to '%s'.",tmp1,
- inet_ntoa( *((struct in_addr *)&or_address) ) );
- free(tmp1);
- if (!memcmp((void *)&or_address, (void *)addr, sizeof(uint32_t))) { /* addresses match */
- log(LOG_DEBUG,"router_is_me(): Addresses match. Comparing ports.");
- if (or_listenport == my_or_listenport) { /* ports also match */
- log(LOG_DEBUG,"router_is_me(): Ports match too.");
- return 1;
- }
- }
-
- addr = localhost->h_addr_list[i++];
- }
- return 0;
- #endif
- }
- /* delete a list of routers from memory */
- static void routerlist_free(routerinfo_t *list)
- {
- routerinfo_t *tmp = NULL;
-
- if (!list)
- return;
-
- do
- {
- tmp=list->next;
- free((void *)list->address);
- crypto_free_pk_env(list->pkey);
- free((void *)list);
- list = tmp;
- }
- while (list != NULL);
-
- return;
- }
- void rarray_free(routerinfo_t **list) {
- if(!list)
- return;
- routerlist_free(*list);
- free(list);
- }
- void router_forget_router(uint32_t addr, uint16_t port) {
- int i;
- routerinfo_t *router;
- router = router_get_by_addr_port(addr,port);
- if(!router) /* we don't seem to know about him in the first place */
- return;
- /* now walk down router_array until we get to router */
- for(i=0;i<rarray_len;i++)
- if(router_array[i] == router)
- break;
- assert(i != rarray_len); /* if so then router_get_by_addr_port should have returned null */
- // free(router); /* don't actually free; we'll free it when we free the whole thing */
- // log(LOG_DEBUG,"router_forget_router(): Forgot about router %d:%d",addr,port);
- for(; i<rarray_len-1;i++)
- router_array[i] = router_array[i+1];
- }
- /* create a 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 */
- static routerinfo_t **make_rarray(routerinfo_t* list, int *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 */
- int router_get_list_from_file(char *routerfile)
- {
- int fd; /* router file */
- struct stat statbuf;
- char *string;
- assert(routerfile);
-
- if (strcspn(routerfile,CONFIG_LEGAL_FILENAME_CHARACTERS) != 0) {
- log(LOG_ERR,"router_get_list_from_file(): Filename %s contains illegal characters.",routerfile);
- return -1;
- }
-
- if(stat(routerfile, &statbuf) < 0) {
- log(LOG_ERR,"router_get_list_from_file(): Could not stat %s.",routerfile);
- return -1;
- }
- /* open the router list */
- fd = open(routerfile,O_RDONLY,0);
- if (fd<0) {
- log(LOG_ERR,"router_get_list_from_file(): Could not open %s.",routerfile);
- return -1;
- }
- string = malloc(statbuf.st_size+1);
- if(!string) {
- log(LOG_ERR,"router_get_list_from_file(): Out of memory.");
- return -1;
- }
- if(read(fd,string,statbuf.st_size) != statbuf.st_size) {
- log(LOG_ERR,"router_get_list_from_file(): Couldn't read all %d bytes of file '%s'.",statbuf.st_size,routerfile);
- free(string);
- close(fd);
- return -1;
- }
- close(fd);
-
- string[statbuf.st_size] = 0; /* null terminate it */
- if(router_get_list_from_string(string) < 0) {
- log(LOG_ERR,"router_get_list_from_file(): The routerfile itself was corrupt.");
- free(string);
- return -1;
- }
- free(string);
- return 0;
- }
- int router_get_list_from_string(char *s) {
- routerinfo_t *routerlist=NULL;
- routerinfo_t *router;
- routerinfo_t **new_router_array;
- int new_rarray_len;
- assert(s);
- while(*s) { /* while not at the end of the string */
- router = router_get_entry_from_string(&s);
- if(router == NULL) {
- routerlist_free(routerlist);
- return -1;
- }
- switch(router_is_me(router->addr, router->or_port)) {
- case 0: /* it's not me */
- router->next = routerlist;
- routerlist = router;
- break;
- case 1: /* it is me */
- if(!my_routerinfo) /* save it, so we can use it for directories */
- my_routerinfo = router;
- else
- routerlist_free(router);
- break;
- default:
- log(LOG_ERR,"router_get_list_from_string(): router_is_me returned error.");
- routerlist_free(routerlist);
- return -1;
- }
- s = eat_whitespace(s);
- }
-
- new_router_array = make_rarray(routerlist, &new_rarray_len);
- if(new_router_array) { /* success! replace the old one */
- rarray_free(router_array); /* free the old one first */
- router_array = new_router_array;
- rarray_len = new_rarray_len;
- return 0;
- }
- return -1;
- }
- /* return the first char of s that is not whitespace and not a comment */
- static char *eat_whitespace(char *s) {
- assert(s);
- while(isspace(*s) || *s == '#') {
- while(isspace(*s))
- s++;
- if(*s == '#') { /* read to a \n or \0 */
- while(*s && *s != '\n')
- s++;
- if(!*s)
- return s;
- }
- }
- return s;
- }
- /* return the first char of s that is whitespace or '#' or '\0 */
- static char *find_whitespace(char *s) {
- assert(s);
- while(*s && !isspace(*s) && *s != '#')
- s++;
- return s;
- }
- /* reads a single router entry from s.
- * updates s so it points to after the router it just read.
- * mallocs a new router, returns it if all goes well, else returns NULL.
- */
- static routerinfo_t *router_get_entry_from_string(char **s) {
- routerinfo_t *router;
- char *next;
- struct hostent *rent;
- router = malloc(sizeof(routerinfo_t));
- if (!router) {
- log(LOG_ERR,"router_get_entry_from_string(): Could not allocate memory.");
- return NULL;
- }
- memset(router,0,sizeof(routerinfo_t)); /* zero it out first */
- /* Bug: if find_whitespace returns a '#', we'll squish it. */
- #define NEXT_TOKEN(s, next) \
- *s = eat_whitespace(*s); \
- next = find_whitespace(*s); \
- if(!*next) { \
- goto router_read_failed; \
- } \
- *next = 0;
- /* read router->address */
- NEXT_TOKEN(s, next);
- router->address = strdup(*s);
- *s = next+1;
- rent = (struct hostent *)gethostbyname(router->address);
- if (!rent) {
- log(LOG_ERR,"router_get_entry_from_string(): Could not get address for router %s.",router->address);
- goto router_read_failed;
- }
- assert(rent->h_length == 4);
- memcpy(&router->addr, rent->h_addr,rent->h_length);
- router->addr = ntohl(router->addr); /* get it back into host order */
- /* read router->or_port */
- NEXT_TOKEN(s, next);
- router->or_port = atoi(*s);
- if(!router->or_port) {
- log(LOG_ERR,"router_get_entry_from_string(): or_port '%s' unreadable or 0. Failing.",*s);
- goto router_read_failed;
- }
- *s = next+1;
-
- /* read router->op_port */
- NEXT_TOKEN(s, next);
- router->op_port = atoi(*s);
- *s = next+1;
-
- /* read router->ap_port */
- NEXT_TOKEN(s, next);
- router->ap_port = atoi(*s);
- *s = next+1;
-
- /* read router->dir_port */
- NEXT_TOKEN(s, next);
- router->dir_port = atoi(*s);
- *s = next+1;
- /* read router->bandwidth */
- NEXT_TOKEN(s, next);
- router->bandwidth = atoi(*s);
- if(!router->bandwidth) {
- log(LOG_ERR,"router_get_entry_from_string(): bandwidth '%s' unreadable or 0. Failing.",*s);
- goto router_read_failed;
- }
- *s = next+1;
- log(LOG_DEBUG,"or_port %d, op_port %d, ap_port %d, dir_port %d, bandwidth %d.",
- router->or_port, router->op_port, router->ap_port, router->dir_port, router->bandwidth);
- *s = eat_whitespace(*s);
- next = strstr(*s,OR_PUBLICKEY_END_TAG);
- router->pkey = crypto_new_pk_env(CRYPTO_PK_RSA);
- if(!next || !router->pkey) {
- log(LOG_ERR,"router_get_entry_from_string(): Couldn't find pk in string");
- goto router_read_failed;
- }
-
- /* now advance *s so it's at the end of this router entry */
- next = strchr(next, '\n');
- assert(next); /* can't fail, we just checked it was here */
- *next = 0;
- // log(LOG_DEBUG,"Key about to be read is: '%s'",*s);
- if((crypto_pk_read_public_key_from_string(router->pkey, *s, strlen(*s))<0)) {
- log(LOG_ERR,"router_get_entry_from_string(): Couldn't read pk from string");
- goto router_read_failed;
- }
- log(LOG_DEBUG,"router_get_entry_from_string(): Public key size = %u.", crypto_pk_keysize(router->pkey));
- if (crypto_pk_keysize(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->or_port);
- goto router_read_failed;
- }
- // test_write_pkey(router->pkey);
- *s = next+1;
- while(**s != '\n') {
- /* pull in a line of exit policy */
- next = strchr(*s, '\n');
- if(!next)
- goto router_read_failed;
- *next = 0;
- router_add_exit_policy(router, *s);
- *s = next+1;
- }
- return router;
- router_read_failed:
- if(router->address)
- free(router->address);
- if(router->pkey)
- crypto_free_pk_env(router->pkey);
- router_free_exit_policy(router);
- free(router);
- return NULL;
- }
- static void router_free_exit_policy(routerinfo_t *router) {
- struct exit_policy_t *tmpe;
- while(router->exit_policy) {
- tmpe = router->exit_policy;
- router->exit_policy = tmpe->next;
- free(tmpe->string);
- free(tmpe->address);
- free(tmpe->port);
- free(tmpe);
- }
- }
- #if 0
- void test_write_pkey(crypto_pk_env_t *pkey) {
- char *string;
- int len;
- log(LOG_DEBUG,"Trying test write.");
- if(crypto_pk_write_public_key_to_string(pkey,&string,&len)<0) {
- log(LOG_DEBUG,"router_get_entry_from_string(): write pkey to string failed\n");
- return;
- }
- log(LOG_DEBUG,"I did it: len %d, string '%s'.",len,string);
- free(string);
- }
- #endif
- static void router_add_exit_policy(routerinfo_t *router, char *string) {
- struct exit_policy_t *tmpe, *newe;
- char *n;
- string = eat_whitespace(string);
- if(!*string) /* it was all whitespace or comment */
- return;
- newe = malloc(sizeof(struct exit_policy_t));
- memset(newe,0,sizeof(struct exit_policy_t));
- newe->string = strdup(string);
- n = find_whitespace(string);
- *n = 0;
- if(!strcasecmp(string,"reject")) {
- newe->policy_type = EXIT_POLICY_REJECT;
- } else if(!strcasecmp(string,"accept")) {
- newe->policy_type = EXIT_POLICY_ACCEPT;
- } else {
- goto policy_read_failed;
- }
- string = eat_whitespace(n+1);
- if(!*string) {
- goto policy_read_failed;
- }
- n = strchr(string,':');
- if(!n)
- goto policy_read_failed;
- *n = 0;
- newe->address = strdup(string);
- string = n+1;
- n = find_whitespace(string);
- *n = 0;
- newe->port = strdup(string);
- log(LOG_DEBUG,"router_add_exit_policy(): type %d, address '%s', port '%s'.",
- newe->policy_type, newe->address, newe->port);
- /* now link newe onto the end of exit_policy */
- if(!router->exit_policy) {
- router->exit_policy = newe;
- return;
- }
- for(tmpe=router->exit_policy; tmpe->next; tmpe=tmpe->next) ;
- tmpe->next = newe;
- return;
- policy_read_failed:
- assert(newe->string);
- log(LOG_INFO,"router_add_exit_policy(): Couldn't parse line '%s'. Dropping", newe->string);
- if(newe->string)
- free(newe->string);
- if(newe->address)
- free(newe->address);
- if(newe->port)
- free(newe->port);
- free(newe);
- return;
- }
- /* Return 0 if my exit policy says to allow connection to conn.
- * Else return -1.
- */
- int router_compare_to_exit_policy(connection_t *conn) {
- struct exit_policy_t *tmpe;
- if(!my_routerinfo) {
- log(LOG_WARNING, "router_compare_to_exit_policy(): my_routerinfo undefined! Rejected.");
- return -1;
- }
- for(tmpe=my_routerinfo->exit_policy; tmpe; tmpe=tmpe->next) {
- assert(tmpe->address);
- assert(tmpe->port);
- /* Totally ignore the address field of the exit policy, for now. */
- if(!strcmp(tmpe->port,"*") || atoi(tmpe->port) == conn->port) {
- log(LOG_INFO,"router_compare_to_exit_policy(): Port '%s' matches '%d'. %s.",
- tmpe->port, conn->port,
- tmpe->policy_type == EXIT_POLICY_ACCEPT ? "Accepting" : "Rejecting");
- if(tmpe->policy_type == EXIT_POLICY_ACCEPT)
- return 0;
- else
- return -1;
- }
- }
- return 0; /* accept all by default. */
- }
- /*
- Local Variables:
- mode:c
- indent-tabs-mode:nil
- c-basic-offset:2
- End:
- */
|