mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-20 02:09:24 +01:00
directory servers in and functional
proxies now periodically pull down an hourly-updated directory, and replace their router list with it if it parses correctly. svn:r112
This commit is contained in:
parent
d6360c57bc
commit
01f6727306
@ -9,7 +9,7 @@ or_LDADD = -L../common -lor
|
||||
|
||||
or_SOURCES = buffers.c cell.c circuit.c command.c connection.c \
|
||||
connection_exit.c connection_ap.c connection_op.c connection_or.c config.c \
|
||||
main.c onion.c routers.c
|
||||
main.c onion.c routers.c directory.c
|
||||
|
||||
test_config_SOURCES = test_config.c
|
||||
|
||||
|
@ -85,6 +85,8 @@ RETURN VALUE: 0 on success, non-zero on error
|
||||
0, "onion proxy port", "<port>" },
|
||||
{ "ORPort", 'p', POPT_ARG_INT, &options->ORPort,
|
||||
0, "onion router port", "<port>" },
|
||||
{ "DirPort", 'd', POPT_ARG_INT, &options->DirPort,
|
||||
0, "directory server port", "<port>" },
|
||||
{ "PrivateKeyFile", 'k', POPT_ARG_STRING, &options->PrivateKeyFile,
|
||||
0, "maximum number of incoming connections", "<file>" },
|
||||
{ "RouterFile", 'r', POPT_ARG_STRING, &options->RouterFile,
|
||||
@ -92,8 +94,14 @@ RETURN VALUE: 0 on success, non-zero on error
|
||||
{ "TrafficShaping", 't', POPT_ARG_INT, &options->TrafficShaping,
|
||||
0, "which traffic shaping policy to use", "<policy>" },
|
||||
{ "LinkPadding", 'P', POPT_ARG_INT, &options->LinkPadding,
|
||||
0, "whether to use link padding", "<padding>" },
|
||||
{ "Role", 'g', POPT_ARG_INT, &options->Role,
|
||||
0, "whether to use link padding", "<padding>" },
|
||||
{ "DirRebuildPeriod",'D', POPT_ARG_INT, &options->DirRebuildPeriod,
|
||||
0, "how many seconds between directory rebuilds", "<rebuildperiod>" },
|
||||
{ "DirFetchPeriod", 'F', POPT_ARG_INT, &options->DirFetchPeriod,
|
||||
0, "how many seconds between directory fetches", "<fetchperiod>" },
|
||||
// { "ReconnectPeriod", 'e', POPT_ARG_INT, &options->ReconnectPeriod,
|
||||
// 0, "how many seconds between retrying all OR connections", "<reconnectperiod>" },
|
||||
{ "Role", 'R', POPT_ARG_INT, &options->Role,
|
||||
0, "4-bit global role id", "<role>" },
|
||||
{ "Verbose", 'v', POPT_ARG_NONE, &Verbose,
|
||||
0, "display options selected before execution", NULL },
|
||||
@ -112,7 +120,11 @@ RETURN VALUE: 0 on success, non-zero on error
|
||||
options->loglevel = LOG_DEBUG;
|
||||
options->CoinWeight = 0.8;
|
||||
options->LinkPadding = 0;
|
||||
options->Role = ROLE_OR_LISTEN | ROLE_OR_CONNECT_ALL | ROLE_OP_LISTEN | ROLE_AP_LISTEN;
|
||||
options->DirRebuildPeriod = 600;
|
||||
options->DirFetchPeriod = 6000;
|
||||
// options->ReconnectPeriod = 6001;
|
||||
options->Role = ROLE_OR_LISTEN | ROLE_OR_CONNECT_ALL | ROLE_OP_LISTEN | ROLE_AP_LISTEN |
|
||||
ROLE_DIR_LISTEN | ROLE_DIR_SERVER;
|
||||
|
||||
code = poptGetNextOpt(optCon); /* first we handle command-line args */
|
||||
if ( code == -1 )
|
||||
@ -151,14 +163,17 @@ RETURN VALUE: 0 on success, non-zero on error
|
||||
printf("RouterFile=%s, PrivateKeyFile=%s\n",
|
||||
options->RouterFile,
|
||||
options->PrivateKeyFile);
|
||||
printf("ORPort=%d, OPPort=%d, APPort=%d\n",
|
||||
printf("ORPort=%d, OPPort=%d, APPort=%d DirPort=%d\n",
|
||||
options->ORPort,options->OPPort,
|
||||
options->APPort);
|
||||
options->APPort,options->DirPort);
|
||||
printf("CoinWeight=%6.4f, MaxConn=%d, TrafficShaping=%d, LinkPadding=%d\n",
|
||||
options->CoinWeight,
|
||||
options->MaxConn,
|
||||
options->TrafficShaping,
|
||||
options->LinkPadding);
|
||||
printf("DirRebuildPeriod=%d, DirFetchPeriod=%d\n",
|
||||
options->DirRebuildPeriod,
|
||||
options->DirFetchPeriod);
|
||||
}
|
||||
|
||||
/* Validate options */
|
||||
@ -188,9 +203,9 @@ RETURN VALUE: 0 on success, non-zero on error
|
||||
}
|
||||
}
|
||||
|
||||
if ( options->Role < 0 || options->Role > 15 )
|
||||
if ( options->Role < 0 || options->Role > 63 )
|
||||
{
|
||||
log(LOG_ERR,"Role option must be an integer between 0 and 15 (inclusive).");
|
||||
log(LOG_ERR,"Role option must be an integer between 0 and 63 (inclusive).");
|
||||
code = -1;
|
||||
}
|
||||
|
||||
@ -224,6 +239,12 @@ RETURN VALUE: 0 on success, non-zero on error
|
||||
code = -1;
|
||||
}
|
||||
|
||||
if ( (options->Role & ROLE_DIR_LISTEN) && options->DirPort < 1 )
|
||||
{
|
||||
log(LOG_ERR,"DirPort option required and must be a positive integer value.");
|
||||
code = -1;
|
||||
}
|
||||
|
||||
if ( (options->Role & ROLE_AP_LISTEN) &&
|
||||
(options->CoinWeight < 0.0 || options->CoinWeight >= 1.0) )
|
||||
{
|
||||
@ -255,6 +276,18 @@ RETURN VALUE: 0 on success, non-zero on error
|
||||
code = -1;
|
||||
}
|
||||
|
||||
if ( options->DirRebuildPeriod < 1)
|
||||
{
|
||||
log(LOG_ERR,"DirRebuildPeriod option must be positive.");
|
||||
code = -1;
|
||||
}
|
||||
|
||||
if ( options->DirFetchPeriod < 1)
|
||||
{
|
||||
log(LOG_ERR,"DirFetchPeriod option must be positive.");
|
||||
code = -1;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
@ -16,10 +16,12 @@ char *conn_type_to_string[] = {
|
||||
"OR", /* 4 */
|
||||
"Exit", /* 5 */
|
||||
"App listener",/* 6 */
|
||||
"App" /* 7 */
|
||||
"App", /* 7 */
|
||||
"Dir listener",/* 8 */
|
||||
"Dir", /* 9 */
|
||||
};
|
||||
|
||||
char *conn_state_to_string[][10] = {
|
||||
char *conn_state_to_string[][15] = {
|
||||
{ }, /* no type associated with 0 */
|
||||
{ "ready" }, /* op listener, 0 */
|
||||
{ "awaiting keys", /* op, 0 */
|
||||
@ -43,7 +45,13 @@ char *conn_state_to_string[][10] = {
|
||||
{ "ready" }, /* app listener, 0 */
|
||||
{ "awaiting dest info", /* app, 0 */
|
||||
"waiting for OR connection", /* 1 */
|
||||
"open" } /* 2 */
|
||||
"open" }, /* 2 */
|
||||
{ "ready" }, /* dir listener, 0 */
|
||||
{ "connecting", /* 0 */
|
||||
"sending command", /* 1 */
|
||||
"reading", /* 2 */
|
||||
"awaiting command", /* 3 */
|
||||
"writing" }, /* 4 */
|
||||
};
|
||||
|
||||
/********* END VARIABLES ************/
|
||||
@ -261,27 +269,20 @@ static int learn_local(struct sockaddr_in *local) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int retry_all_connections(int role, routerinfo_t **router_array, int rarray_len,
|
||||
crypto_pk_env_t *prkey, uint16_t or_listenport, uint16_t op_listenport, uint16_t ap_listenport) {
|
||||
int retry_all_connections(int role, crypto_pk_env_t *prkey, uint16_t or_listenport,
|
||||
uint16_t op_listenport, uint16_t ap_listenport, uint16_t dir_listenport) {
|
||||
|
||||
/* start all connections that should be up but aren't */
|
||||
|
||||
routerinfo_t *router;
|
||||
int i;
|
||||
struct sockaddr_in local; /* local address */
|
||||
|
||||
if(learn_local(&local) < 0)
|
||||
return -1;
|
||||
|
||||
local.sin_port = htons(or_listenport);
|
||||
|
||||
if(role & ROLE_OR_CONNECT_ALL) {
|
||||
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, prkey, &local);
|
||||
}
|
||||
}
|
||||
router_retry_connections(prkey, &local);
|
||||
}
|
||||
|
||||
if(role & ROLE_OR_LISTEN) {
|
||||
@ -304,6 +305,13 @@ int retry_all_connections(int role, routerinfo_t **router_array, int rarray_len,
|
||||
}
|
||||
}
|
||||
|
||||
if(role & ROLE_DIR_LISTEN) {
|
||||
local.sin_port = htons(dir_listenport);
|
||||
if(!connection_get_by_type(CONN_TYPE_DIR_LISTENER)) {
|
||||
connection_dir_create_listener(NULL, &local); /* no need to tell it the private key. */
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -415,7 +423,8 @@ int connection_speaks_cells(connection_t *conn) {
|
||||
int connection_is_listener(connection_t *conn) {
|
||||
if(conn->type == CONN_TYPE_OP_LISTENER ||
|
||||
conn->type == CONN_TYPE_OR_LISTENER ||
|
||||
conn->type == CONN_TYPE_AP_LISTENER)
|
||||
conn->type == CONN_TYPE_AP_LISTENER ||
|
||||
conn->type == CONN_TYPE_DIR_LISTENER)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@ -586,6 +595,8 @@ int connection_process_inbuf(connection_t *conn) {
|
||||
return connection_exit_process_inbuf(conn);
|
||||
case CONN_TYPE_AP:
|
||||
return connection_ap_process_inbuf(conn);
|
||||
case CONN_TYPE_DIR:
|
||||
return connection_dir_process_inbuf(conn);
|
||||
default:
|
||||
log(LOG_DEBUG,"connection_process_inbuf() got unexpected conn->type.");
|
||||
return -1;
|
||||
@ -709,6 +720,8 @@ int connection_finished_flushing(connection_t *conn) {
|
||||
return connection_or_finished_flushing(conn);
|
||||
case CONN_TYPE_EXIT:
|
||||
return connection_exit_finished_flushing(conn);
|
||||
case CONN_TYPE_DIR:
|
||||
return connection_dir_finished_flushing(conn);
|
||||
default:
|
||||
log(LOG_DEBUG,"connection_finished_flushing() got unexpected conn->type.");
|
||||
return -1;
|
||||
|
@ -179,8 +179,8 @@ int connection_exit_process_data_cell(cell_t *cell, connection_t *conn) {
|
||||
/* also, deliver a 'connected' cell back through the circuit. */
|
||||
return connection_exit_send_connected(conn);
|
||||
} else {
|
||||
log(LOG_DEBUG,"connection_exit_process_data_cell(): in connecting_wait, but I've already received everything. Closing.");
|
||||
return -1;
|
||||
log(LOG_DEBUG,"connection_exit_process_data_cell(): in connecting_wait, but I've already received everything. Closing.");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
case EXIT_CONN_STATE_CONNECTING:
|
||||
|
250
src/or/directory.c
Normal file
250
src/or/directory.c
Normal file
@ -0,0 +1,250 @@
|
||||
/* Copyright 2001,2002 Roger Dingledine, Matej Pfaêfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#include "or.h"
|
||||
|
||||
#define MAX_DIR_SIZE 50000 /* XXX, big enough? */
|
||||
|
||||
/********* START VARIABLES **********/
|
||||
|
||||
extern or_options_t options; /* command-line and config-file options */
|
||||
|
||||
static char the_directory[MAX_DIR_SIZE+1];
|
||||
static int directorylen=0;
|
||||
|
||||
static char getstring[] = "GET / HTTP/1.0\n\r";
|
||||
|
||||
/********* END VARIABLES ************/
|
||||
|
||||
void directory_initiate_fetch(routerinfo_t *router) {
|
||||
connection_t *conn;
|
||||
struct sockaddr_in router_addr;
|
||||
int s;
|
||||
|
||||
log(LOG_DEBUG,"directory_initiate_fetch(): initiating directory fetch");
|
||||
|
||||
if(!router) /* i guess they didn't have one in mind for me to use */
|
||||
return;
|
||||
|
||||
conn = connection_new(CONN_TYPE_DIR);
|
||||
if(!conn)
|
||||
return;
|
||||
|
||||
/* set up conn so it's got all the data we need to remember */
|
||||
conn->addr = router->addr, conn->port = router->or_port; /* NOTE we store or_port here always */
|
||||
conn->address = strdup(router->address);
|
||||
conn->receiver_bucket = -1; /* edge connections don't do receiver buckets */
|
||||
conn->bandwidth = -1;
|
||||
|
||||
s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
|
||||
if (s < 0)
|
||||
{
|
||||
log(LOG_ERR,"directory_initiate_fetch(): Error creating network socket.");
|
||||
connection_free(conn);
|
||||
return;
|
||||
}
|
||||
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 = htons(router->dir_port);
|
||||
router_addr.sin_addr.s_addr = router->addr;
|
||||
|
||||
log(LOG_DEBUG,"directory_initiate_fetch(): Trying to connect to %s:%u.",inet_ntoa(*(struct in_addr *)&router->addr),router->dir_port);
|
||||
|
||||
if(connect(s,(struct sockaddr *)&router_addr,sizeof(router_addr)) < 0){
|
||||
if(errno != EINPROGRESS){
|
||||
/* yuck. kill it. */
|
||||
connection_free(conn);
|
||||
return;
|
||||
} else {
|
||||
/* it's in progress. set state appropriately and return. */
|
||||
conn->s = s;
|
||||
|
||||
if(connection_add(conn) < 0) { /* no space, forget it */
|
||||
connection_free(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
log(LOG_DEBUG,"directory_initiate_fetch(): connect in progress.");
|
||||
connection_watch_events(conn, POLLIN | POLLOUT); /* writable indicates finish, readable indicates broken link */
|
||||
conn->state = DIR_CONN_STATE_CONNECTING;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* it succeeded. we're connected. */
|
||||
conn->s = s;
|
||||
|
||||
if(connection_add(conn) < 0) { /* no space, forget it */
|
||||
connection_free(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
log(LOG_DEBUG,"directory_initiate_fetch(): Connection to router %s:%u established.",router->address,router->dir_port);
|
||||
|
||||
if(directory_send_command(conn) < 0) {
|
||||
connection_remove(conn);
|
||||
connection_free(conn);
|
||||
}
|
||||
}
|
||||
|
||||
int directory_send_command(connection_t *conn) {
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_DIR);
|
||||
|
||||
if(connection_write_to_buf(getstring, strlen(getstring), conn) < 0) {
|
||||
log(LOG_DEBUG,"directory_send_command(): Couldn't write command to buffer.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn->state = DIR_CONN_STATE_SENDING_COMMAND;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void directory_rebuild(void) {
|
||||
|
||||
dump_directory_to_string(the_directory, MAX_DIR_SIZE);
|
||||
log(LOG_DEBUG,"New directory:\n'%s'",the_directory);
|
||||
directorylen = strlen(the_directory);
|
||||
|
||||
}
|
||||
|
||||
int connection_dir_process_inbuf(connection_t *conn) {
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_DIR);
|
||||
|
||||
if(conn->inbuf_reached_eof) {
|
||||
if(conn->state != DIR_CONN_STATE_READING) {
|
||||
log(LOG_DEBUG,"connection_dir_process_inbuf(): conn reached eof, not reading. Closing.");
|
||||
return -1;
|
||||
}
|
||||
/* eof reached, kill it, but first process the_directory and learn about new routers. */
|
||||
log(LOG_DEBUG,"connection_dir_process_inbuf(): conn reached eof. Processing directory.");
|
||||
log(LOG_DEBUG,"connection_dir_process_inbuf(): Received directory (size %d) '%s'", directorylen, the_directory);
|
||||
if(directorylen == 0) {
|
||||
log(LOG_DEBUG,"connection_dir_process_inbuf(): Empty directory. Ignoring.");
|
||||
return -1;
|
||||
}
|
||||
if(router_get_list_from_string(the_directory, options.ORPort) < 0) {
|
||||
log(LOG_DEBUG,"connection_dir_process_inbuf(): ...but parsing failed. Ignoring.");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch(conn->state) {
|
||||
case DIR_CONN_STATE_COMMAND_WAIT:
|
||||
return directory_handle_command(conn);
|
||||
case DIR_CONN_STATE_READING:
|
||||
return directory_handle_reading(conn);
|
||||
default:
|
||||
log(LOG_DEBUG,"connection_dir_process_inbuf(): Got data while writing; Ignoring.");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int directory_handle_command(connection_t *conn) {
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_DIR);
|
||||
|
||||
if(conn->inbuf_datalen < strlen(getstring)) { /* entire response available? */
|
||||
log(LOG_DEBUG,"directory_handle_command(): Entire command not here yet. Waiting.");
|
||||
return 0; /* not yet */
|
||||
}
|
||||
|
||||
/* for now, don't bother reading it. */
|
||||
|
||||
if(directorylen == 0) {
|
||||
log(LOG_DEBUG,"directory_handle_command(): My directory is empty. Closing.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
log(LOG_DEBUG,"directory_handle_command(): Dumping directory to client.");
|
||||
if(connection_write_to_buf(the_directory, directorylen, conn) < 0) {
|
||||
log(LOG_DEBUG,"directory_handle_command(): my outbuf is full. Oops.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn->state = DIR_CONN_STATE_WRITING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int directory_handle_reading(connection_t *conn) {
|
||||
int amt;
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_DIR);
|
||||
|
||||
amt = conn->inbuf_datalen;
|
||||
|
||||
if(amt + directorylen >= MAX_DIR_SIZE) {
|
||||
log(LOG_DEBUG,"directory_handle_reading(): Directory too large. Failing messily.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
log(LOG_DEBUG,"directory_handle_reading(): Pulling %d bytes in at offset %d.",
|
||||
amt, directorylen);
|
||||
|
||||
if(connection_fetch_from_buf(the_directory+directorylen,amt,conn) < 0) {
|
||||
log(LOG_DEBUG,"directory_handle_reading(): fetch_from_buf failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
directorylen += amt;
|
||||
|
||||
the_directory[directorylen] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connection_dir_finished_flushing(connection_t *conn) {
|
||||
int e, len=sizeof(e);
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_DIR);
|
||||
|
||||
switch(conn->state) {
|
||||
case DIR_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_dir_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_dir_finished_flushing(): DIR connection to router %s:%u established.",
|
||||
conn->address,conn->port);
|
||||
|
||||
return directory_send_command(conn);
|
||||
case DIR_CONN_STATE_SENDING_COMMAND:
|
||||
log(LOG_DEBUG,"connection_dir_finished_flushing(): client finished sending command.");
|
||||
directorylen = 0;
|
||||
conn->state = DIR_CONN_STATE_READING;
|
||||
connection_watch_events(conn, POLLIN);
|
||||
return 0;
|
||||
case DIR_CONN_STATE_WRITING:
|
||||
log(LOG_DEBUG,"connection_dir_finished_flushing(): Finished writing directory. Closing.");
|
||||
return -1; /* kill it */
|
||||
default:
|
||||
log(LOG_DEBUG,"Bug: connection_dir_finished_flushing() called in unexpected state.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connection_dir_create_listener(crypto_pk_env_t *prkey, struct sockaddr_in *local) {
|
||||
log(LOG_DEBUG,"connection_create_dir_listener starting");
|
||||
return connection_create_listener(prkey, local, CONN_TYPE_DIR_LISTENER);
|
||||
}
|
||||
|
||||
int connection_dir_handle_listener_read(connection_t *conn) {
|
||||
log(LOG_NOTICE,"Dir: Received a connection request. Waiting for command.");
|
||||
return connection_handle_listener_read(conn, CONN_TYPE_DIR, DIR_CONN_STATE_COMMAND_WAIT);
|
||||
}
|
||||
|
139
src/or/main.c
139
src/or/main.c
@ -21,10 +21,6 @@ static int please_dumpstats=0; /* whether we should dump stats during the loop *
|
||||
/* private key */
|
||||
static crypto_pk_env_t *prkey;
|
||||
|
||||
/* router array */
|
||||
static routerinfo_t **router_array = NULL;
|
||||
static int rarray_len = 0;
|
||||
|
||||
/********* END VARIABLES ************/
|
||||
|
||||
/****************************************************************************
|
||||
@ -154,44 +150,6 @@ connection_t *connection_get_by_type(int type) {
|
||||
|
||||
|
||||
|
||||
/* the next 4 functions should move to routers.c once we get it
|
||||
* cleaned up more. The router_array and rarray_len variables should
|
||||
* move there too.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* FIXME can we cut this function out? */
|
||||
connection_t *connect_to_router_as_op(routerinfo_t *router) {
|
||||
return connection_connect_to_router_as_op(router, options.ORPort);
|
||||
@ -252,12 +210,13 @@ void check_conn_read(int i) {
|
||||
retval = connection_or_handle_listener_read(conn);
|
||||
} else if (conn->type == CONN_TYPE_AP_LISTENER) {
|
||||
retval = connection_ap_handle_listener_read(conn);
|
||||
} else if (conn->type == CONN_TYPE_DIR_LISTENER) {
|
||||
retval = connection_dir_handle_listener_read(conn);
|
||||
} else {
|
||||
/* else it's an OP, OR, or exit */
|
||||
retval = connection_read_to_buf(conn);
|
||||
if (retval >= 0) { /* all still well */
|
||||
retval = connection_process_inbuf(conn);
|
||||
// log(LOG_DEBUG,"check_conn_read(): connection_process_inbuf returned %d.",retval);
|
||||
// log(LOG_DEBUG,"check_conn_read(): connection_process_inbuf returned %d.",retval);
|
||||
if(retval >= 0 && !connection_state_is_open(conn) && conn->receiver_bucket == 0) {
|
||||
log(LOG_DEBUG,"check_conn_read(): receiver bucket reached 0 before handshake finished. Closing.");
|
||||
retval = -1;
|
||||
@ -286,8 +245,7 @@ void check_conn_write(int i) {
|
||||
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) {
|
||||
if(connection_is_listener(conn)) {
|
||||
log(LOG_DEBUG,"check_conn_write(): Got a listener socket. Can't happen!");
|
||||
retval = -1;
|
||||
} else {
|
||||
@ -337,6 +295,8 @@ int prepare_for_poll(int *timeout) {
|
||||
connection_t *tmpconn;
|
||||
struct timeval now, soonest;
|
||||
static int current_second = 0; /* from previous calls to gettimeofday */
|
||||
static int time_to_rebuild_directory = 0;
|
||||
static int time_to_fetch_directory = 0;
|
||||
int ms_until_conn;
|
||||
|
||||
*timeout = -1; /* set it to never timeout, possibly overridden below */
|
||||
@ -352,6 +312,34 @@ int prepare_for_poll(int *timeout) {
|
||||
if(gettimeofday(&now,NULL) < 0)
|
||||
return -1;
|
||||
|
||||
if(options.Role & ROLE_DIR_SERVER) {
|
||||
if(time_to_rebuild_directory < now.tv_sec) {
|
||||
/* it's time to rebuild our directory */
|
||||
if(time_to_rebuild_directory == 0) {
|
||||
/* we just started up. if we build a directory now it will be meaningless. */
|
||||
log(LOG_DEBUG,"prepare_for_poll(): Delaying initial dir build for 15 seconds.");
|
||||
time_to_rebuild_directory = now.tv_sec + 15; /* try in 15 seconds */
|
||||
} else {
|
||||
directory_rebuild();
|
||||
time_to_rebuild_directory = now.tv_sec + options.DirRebuildPeriod;
|
||||
}
|
||||
}
|
||||
*timeout = 1000*(time_to_rebuild_directory - now.tv_sec) + (1000 - (now.tv_usec / 1000));
|
||||
// log(LOG_DEBUG,"prepare_for_poll(): DirBuild timeout is %d",*timeout);
|
||||
}
|
||||
|
||||
if(!(options.Role & ROLE_DIR_SERVER)) {
|
||||
if(time_to_fetch_directory < now.tv_sec) {
|
||||
/* it's time to fetch a new directory */
|
||||
/* NOTE directory servers do not currently fetch directories.
|
||||
* Hope this doesn't bite us later.
|
||||
*/
|
||||
directory_initiate_fetch(router_pick_directory_server());
|
||||
time_to_fetch_directory = now.tv_sec + options.DirFetchPeriod;
|
||||
}
|
||||
*timeout = 1000*(time_to_fetch_directory - now.tv_sec) + (1000 - (now.tv_usec / 1000));
|
||||
}
|
||||
|
||||
if(need_to_refill_buckets) {
|
||||
if(now.tv_sec > current_second) { /* the second has already rolled over! */
|
||||
// log(LOG_DEBUG,"prepare_for_poll(): The second has rolled over, immediately refilling.");
|
||||
@ -359,8 +347,8 @@ int prepare_for_poll(int *timeout) {
|
||||
connection_increment_receiver_bucket(connection_array[i]);
|
||||
current_second = now.tv_sec; /* remember which second it is, for next time */
|
||||
}
|
||||
/* this timeout is definitely sooner than either of the above two */
|
||||
*timeout = 1000 - (now.tv_usec / 1000); /* how many milliseconds til the next second? */
|
||||
// log(LOG_DEBUG,"prepare_for_poll(): %d milliseconds til next second.",*timeout);
|
||||
}
|
||||
|
||||
if(options.LinkPadding) {
|
||||
@ -406,9 +394,7 @@ int do_main_loop(void) {
|
||||
int poll_result;
|
||||
|
||||
/* load the routers file */
|
||||
router_array = router_get_list_from_file(options.RouterFile,&rarray_len, options.ORPort);
|
||||
if (!router_array)
|
||||
{
|
||||
if(router_get_list_from_file(options.RouterFile, options.ORPort) < 0) {
|
||||
log(LOG_ERR,"Error loading router list.");
|
||||
return -1;
|
||||
}
|
||||
@ -429,8 +415,8 @@ int do_main_loop(void) {
|
||||
|
||||
/* start-up the necessary connections based on global_role. This is where we
|
||||
* try to connect to all the other ORs, and start the listeners */
|
||||
retry_all_connections(options.Role, router_array, rarray_len, prkey,
|
||||
options.ORPort, options.OPPort, options.APPort);
|
||||
retry_all_connections(options.Role, prkey, options.ORPort,
|
||||
options.OPPort, options.APPort, options.DirPort);
|
||||
|
||||
for(;;) {
|
||||
if(please_dumpstats) {
|
||||
@ -496,7 +482,7 @@ void dumpstats (void) { /* dump stats to stdout */
|
||||
int i;
|
||||
connection_t *conn;
|
||||
extern char *conn_type_to_string[];
|
||||
extern char *conn_state_to_string[][10];
|
||||
extern char *conn_state_to_string[][15];
|
||||
|
||||
printf("Dumping stats:\n");
|
||||
|
||||
@ -515,6 +501,53 @@ void dumpstats (void) { /* dump stats to stdout */
|
||||
please_dumpstats = 0;
|
||||
}
|
||||
|
||||
void dump_directory_to_string(char *s, int maxlen) {
|
||||
int i;
|
||||
connection_t *conn;
|
||||
char *pkey;
|
||||
int pkeylen;
|
||||
int written;
|
||||
routerinfo_t *router;
|
||||
|
||||
for(i=0;i<nfds;i++) {
|
||||
conn = connection_array[i];
|
||||
|
||||
if(conn->type != CONN_TYPE_OR)
|
||||
continue; /* we only want to list ORs */
|
||||
router = router_get_by_addr_port(conn->addr,conn->port);
|
||||
if(!router) {
|
||||
log(LOG_ERR,"dump_directory_to_string(): couldn't find router %d:%d!",conn->addr,conn->port);
|
||||
return;
|
||||
}
|
||||
if(crypto_pk_write_public_key_to_string(router->pkey,&pkey,&pkeylen)<0) {
|
||||
log(LOG_ERR,"dump_directory_to_string(): write pkey to string failed!");
|
||||
return;
|
||||
}
|
||||
written = snprintf(s, maxlen, "%s %d %d %d %d %d\n%s\n",
|
||||
router->address,
|
||||
router->or_port,
|
||||
router->op_port,
|
||||
router->ap_port,
|
||||
router->dir_port,
|
||||
router->bandwidth,
|
||||
pkey);
|
||||
|
||||
free(pkey);
|
||||
|
||||
if(written < 0 || written > maxlen) {
|
||||
/* apparently different glibcs do different things on error.. so check both */
|
||||
log(LOG_ERR,"dump_directory_to_string(): tried to exceed string length.");
|
||||
s[maxlen-1] = 0; /* make sure it's null terminated */
|
||||
return;
|
||||
}
|
||||
|
||||
maxlen -= written;
|
||||
s += written;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int retval = 0;
|
||||
|
||||
|
43
src/or/or.h
43
src/or/or.h
@ -58,6 +58,8 @@
|
||||
#define ROLE_OR_CONNECT_ALL 2
|
||||
#define ROLE_OP_LISTEN 4
|
||||
#define ROLE_AP_LISTEN 8
|
||||
#define ROLE_DIR_LISTEN 16
|
||||
#define ROLE_DIR_SERVER 32
|
||||
|
||||
#define ROLE_IS_OR(role) ((role & ROLE_OR_LISTEN) || (role & ROLE_OR_CONNECT_ALL) || (role & ROLE_OP_LISTEN))
|
||||
|
||||
@ -68,6 +70,8 @@
|
||||
#define CONN_TYPE_EXIT 5
|
||||
#define CONN_TYPE_AP_LISTENER 6
|
||||
#define CONN_TYPE_AP 7
|
||||
#define CONN_TYPE_DIR_LISTENER 8
|
||||
#define CONN_TYPE_DIR 9
|
||||
|
||||
#define LISTENER_STATE_READY 0
|
||||
|
||||
@ -105,6 +109,12 @@
|
||||
#define AP_CONN_STATE_OR_WAIT 1
|
||||
#define AP_CONN_STATE_OPEN 2
|
||||
|
||||
#define DIR_CONN_STATE_CONNECTING 0
|
||||
#define DIR_CONN_STATE_SENDING_COMMAND 1
|
||||
#define DIR_CONN_STATE_READING 2
|
||||
#define DIR_CONN_STATE_COMMAND_WAIT 3
|
||||
#define DIR_CONN_STATE_WRITING 4
|
||||
|
||||
#define CIRCUIT_STATE_OPEN_WAIT 0 /* receiving/processing the onion */
|
||||
#define CIRCUIT_STATE_OR_WAIT 1 /* I'm at the beginning of the path, my firsthop is still connecting */
|
||||
#define CIRCUIT_STATE_OPEN 2 /* onion processed, ready to send data along the connection */
|
||||
@ -342,9 +352,12 @@ typedef struct
|
||||
int ORPort;
|
||||
int OPPort;
|
||||
int APPort;
|
||||
int DirPort;
|
||||
int MaxConn;
|
||||
int TrafficShaping;
|
||||
int LinkPadding;
|
||||
int DirRebuildPeriod;
|
||||
int DirFetchPeriod;
|
||||
int Role;
|
||||
int loglevel;
|
||||
} or_options_t;
|
||||
@ -441,8 +454,8 @@ int connection_create_listener(crypto_pk_env_t *prkey, struct sockaddr_in *local
|
||||
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(int role, routerinfo_t **router_array, int rarray_len,
|
||||
crypto_pk_env_t *prkey, uint16_t or_port, uint16_t op_port, uint16_t ap_port);
|
||||
int retry_all_connections(int role, crypto_pk_env_t *prkey, uint16_t or_listenport,
|
||||
uint16_t op_listenport, uint16_t ap_listenport, uint16_t dir_listenport);
|
||||
connection_t *connection_connect_to_router_as_op(routerinfo_t *router, uint16_t local_or_port);
|
||||
|
||||
int connection_read_to_buf(connection_t *conn);
|
||||
@ -548,6 +561,18 @@ connection_t *connection_or_connect_as_op(routerinfo_t *router, struct sockaddr_
|
||||
int connection_or_create_listener(crypto_pk_env_t *prkey, struct sockaddr_in *local);
|
||||
int connection_or_handle_listener_read(connection_t *conn);
|
||||
|
||||
/********************************* directory.c ***************************/
|
||||
|
||||
void directory_initiate_fetch(routerinfo_t *router);
|
||||
int directory_send_command(connection_t *conn);
|
||||
void directory_rebuild(void);
|
||||
int connection_dir_process_inbuf(connection_t *conn);
|
||||
int directory_handle_command(connection_t *conn);
|
||||
int directory_handle_reading(connection_t *conn);
|
||||
int connection_dir_finished_flushing(connection_t *conn);
|
||||
int connection_dir_create_listener(crypto_pk_env_t *prkey, struct sockaddr_in *local);
|
||||
int connection_dir_handle_listener_read(connection_t *conn);
|
||||
|
||||
/********************************* main.c ***************************/
|
||||
|
||||
int connection_add(connection_t *conn);
|
||||
@ -559,10 +584,6 @@ connection_t *connection_exact_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);
|
||||
unsigned int *router_new_route(int *routelen);
|
||||
unsigned char *router_create_onion(unsigned int *route, int routelen, int *len, crypt_path_t **cpath);
|
||||
routerinfo_t *router_get_first_in_route(unsigned int *route, int routelen);
|
||||
connection_t *connect_to_router_as_op(routerinfo_t *router);
|
||||
|
||||
void connection_watch_events(connection_t *conn, short events);
|
||||
@ -582,6 +603,7 @@ int do_main_loop(void);
|
||||
void catchint();
|
||||
void catchusr1();
|
||||
void dumpstats(void);
|
||||
void dump_directory_to_string(char *s, int maxlen);
|
||||
|
||||
int main(int argc, char *argv[]);
|
||||
|
||||
@ -625,6 +647,13 @@ tracked_onion_t *id_tracked_onion(unsigned char *onion, uint32_t onionlen, track
|
||||
|
||||
/********************************* routers.c ***************************/
|
||||
|
||||
routerinfo_t **router_get_list_from_file(char *routerfile, int *len, uint16_t or_listenport);
|
||||
void router_retry_connections(crypto_pk_env_t *prkey, struct sockaddr_in *local);
|
||||
routerinfo_t *router_pick_directory_server(void);
|
||||
routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port);
|
||||
unsigned int *router_new_route(int *routelen);
|
||||
unsigned char *router_create_onion(unsigned int *route, int routelen, int *len, crypt_path_t **cpath);
|
||||
routerinfo_t *router_get_first_in_route(unsigned int *route, int routelen);
|
||||
int router_get_list_from_file(char *routerfile, uint16_t or_listenport);
|
||||
int router_get_list_from_string(char *s, uint16_t or_listenport);
|
||||
|
||||
#endif
|
||||
|
135
src/or/routers.c
135
src/or/routers.c
@ -13,7 +13,16 @@
|
||||
|
||||
#include "or.h"
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* router array */
|
||||
static routerinfo_t **router_array = NULL;
|
||||
static int rarray_len = 0;
|
||||
|
||||
extern int global_role; /* from main.c */
|
||||
extern or_options_t options; /* command-line and config-file options */
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* static function prototypes */
|
||||
static int router_is_me(uint32_t or_address, uint16_t or_listenport, uint16_t my_or_listenport);
|
||||
@ -23,6 +32,67 @@ static char *eat_whitespace(char *s);
|
||||
static char *find_whitespace(char *s);
|
||||
static routerinfo_t *router_get_entry_from_string(char **s);
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
void router_retry_connections(crypto_pk_env_t *prkey, struct sockaddr_in *local) {
|
||||
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, prkey, local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* private function, to determine whether the current entry in the router list is actually us */
|
||||
static int router_is_me(uint32_t or_address, uint16_t or_listenport, uint16_t my_or_listenport)
|
||||
{
|
||||
@ -58,8 +128,8 @@ static int router_is_me(uint32_t or_address, uint16_t or_listenport, uint16_t my
|
||||
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 */
|
||||
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);
|
||||
@ -151,63 +221,86 @@ static routerinfo_t **make_rarray(routerinfo_t* list, int *len)
|
||||
|
||||
|
||||
/* load the router list */
|
||||
routerinfo_t **router_get_list_from_file(char *routerfile, int *len, uint16_t or_listenport)
|
||||
int router_get_list_from_file(char *routerfile, uint16_t or_listenport)
|
||||
{
|
||||
routerinfo_t *routerlist=NULL;
|
||||
routerinfo_t *router;
|
||||
int fd; /* router file */
|
||||
struct stat statbuf;
|
||||
char *string;
|
||||
char *tmps;
|
||||
|
||||
assert(routerfile && len);
|
||||
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 NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(stat(routerfile, &statbuf) < 0) {
|
||||
log(LOG_ERR,"router_get_list_from_file(): Could not stat %s.",routerfile);
|
||||
return NULL;
|
||||
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 NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
string = malloc(statbuf.st_size+1);
|
||||
if(!string) {
|
||||
log(LOG_ERR,"router_get_list_from_file(): Out of memory.");
|
||||
return NULL;
|
||||
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);
|
||||
return NULL;
|
||||
free(string);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
string[statbuf.st_size] = 0; /* null terminate it */
|
||||
tmps = string;
|
||||
while(*tmps) { /* while not at the end of the string */
|
||||
router = router_get_entry_from_string(&tmps);
|
||||
|
||||
if(router_get_list_from_string(string, or_listenport) < 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, uint16_t or_listenport) {
|
||||
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);
|
||||
free(string);
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
if(!router_is_me(router->addr, router->or_port, or_listenport)) {
|
||||
router->next = routerlist;
|
||||
routerlist = router;
|
||||
}
|
||||
tmps = eat_whitespace(tmps);
|
||||
s = eat_whitespace(s);
|
||||
}
|
||||
free(string);
|
||||
return make_rarray(routerlist, len);
|
||||
|
||||
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 */
|
||||
@ -322,7 +415,7 @@ static routerinfo_t *router_get_entry_from_string(char **s) {
|
||||
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);
|
||||
// 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;
|
||||
|
Loading…
Reference in New Issue
Block a user