2003-12-13 23:53:17 +01:00
/* Copyright 2001,2002,2003 Roger Dingledine. */
2002-09-26 14:09:10 +02:00
/* See LICENSE for licensing information */
/* $Id$ */
# include "or.h"
2004-05-09 18:47:25 +02:00
/**
* \ file directory . c
* \ brief Implement directory HTTP protocol .
* */
2004-05-05 04:50:38 +02:00
2004-05-13 01:48:57 +02:00
/* In-points to directory.c:
*
* - directory_post_to_dirservers ( ) , called from
* router_upload_dir_desc_to_dirservers ( ) in router . c
* upload_service_descriptor ( ) in rendservice . c
* - directory_get_from_dirserver ( ) , called from
* rend_client_refetch_renddesc ( ) in rendclient . c
* run_scheduled_events ( ) in main . c
* do_hup ( ) in main . c
* - connection_dir_process_inbuf ( ) , called from
* connection_process_inbuf ( ) in connection . c
* - connection_dir_finished_flushing ( ) , called from
* connection_finished_flushing ( ) in connection . c
* - connection_dir_finished_connecting ( ) , called from
* connection_finished_connecting ( ) in connection . c
*/
static void
directory_initiate_command ( routerinfo_t * router , uint8_t purpose ,
const char * payload , int payload_len ) ;
2004-04-01 00:02:13 +02:00
static void directory_send_command ( connection_t * conn , int purpose ,
const char * payload , int payload_len ) ;
2003-09-16 07:41:49 +02:00
static int directory_handle_command ( connection_t * conn ) ;
2002-09-26 14:09:10 +02:00
/********* START VARIABLES **********/
extern or_options_t options ; /* command-line and config-file options */
2004-05-09 18:47:25 +02:00
/** URL for publishing rendezvous descriptors. */
2004-04-03 05:33:57 +02:00
char rend_publish_string [ ] = " /rendezvous/publish " ;
2004-05-09 18:47:25 +02:00
/** Prefix for downloading rendezvous descriptors. */
2004-04-03 05:33:57 +02:00
char rend_fetch_url [ ] = " /rendezvous/ " ;
2004-06-02 21:44:41 +02:00
# define MAX_HEADERS_SIZE 50000
2004-03-12 13:43:13 +01:00
# define MAX_BODY_SIZE 500000
2002-09-26 14:09:10 +02:00
/********* END VARIABLES ************/
2004-05-13 01:48:57 +02:00
/** Start a connection to every known directory server, using
* connection purpose ' purpose ' and uploading the payload ' payload '
* ( length ' payload_len ' ) . The purpose should be one of
* ' DIR_PURPOSE_UPLOAD_DIR ' or ' DIR_PURPOSE_UPLOAD_RENDDESC ' .
*/
void
directory_post_to_dirservers ( uint8_t purpose , const char * payload ,
int payload_len )
{
int i ;
routerinfo_t * router ;
routerlist_t * rl ;
router_get_routerlist ( & rl ) ;
if ( ! rl )
return ;
for ( i = 0 ; i < smartlist_len ( rl - > routers ) ; i + + ) {
router = smartlist_get ( rl - > routers , i ) ;
2004-06-16 23:08:29 +02:00
if ( router - > is_trusted_dir )
2004-05-13 01:48:57 +02:00
directory_initiate_command ( router , purpose , payload , payload_len ) ;
}
}
/** Start a connection to a random running directory server, using
* connection purpose ' purpose ' requesting ' payload ' ( length
* ' payload_len ' ) . The purpose should be one of
* ' DIR_PURPOSE_FETCH_DIR ' or ' DIR_PURPOSE_FETCH_RENDDESC ' .
*/
void
directory_get_from_dirserver ( uint8_t purpose , const char * payload ,
int payload_len )
{
2004-07-20 08:44:16 +02:00
routerinfo_t * ds ;
if ( purpose = = DIR_PURPOSE_FETCH_DIR ) {
2004-07-21 02:12:42 +02:00
if ( advertised_server_mode ( ) ) {
/* only ask authdirservers, and don't ask myself */
2004-07-20 08:44:16 +02:00
ds = router_pick_directory_server ( 1 , 1 ) ;
} else {
/* anybody with a non-zero dirport will do */
ds = router_pick_directory_server ( 0 , 1 ) ;
}
} else { // (purpose == DIR_PURPOSE_FETCH_RENDDESC)
/* only ask authdirservers, any of them will do */
ds = router_pick_directory_server ( 1 , 0 ) ;
}
if ( ! ds ) { /* no viable dirserver found */
log_fn ( LOG_WARN , " No running dirservers known. Not trying. (purpose %d) " , purpose ) ;
return ;
}
directory_initiate_command ( ds , purpose , payload , payload_len ) ;
2004-05-13 01:48:57 +02:00
}
2004-05-10 06:34:48 +02:00
/** Launch a new connection to the directory server <b>router</b> to upload or
* download a service or rendezvous descriptor . < b > purpose < / b > determines what
2004-05-05 04:50:38 +02:00
* kind of directory connection we ' re launching , and must be one of
* DIR_PURPOSE_ { FETCH | UPLOAD } _ { DIR | RENDDESC } .
*
2004-05-10 06:34:48 +02:00
* When uploading , < b > payload < / b > and < b > payload_len < / b > determine the content
* of the HTTP post . When fetching a rendezvous descriptor , < b > payload < / b >
* and < b > payload_len < / b > are the service ID we want to fetch .
2004-05-05 04:50:38 +02:00
*/
2004-05-13 01:48:57 +02:00
static void
directory_initiate_command ( routerinfo_t * router , uint8_t purpose ,
const char * payload , int payload_len )
{
2002-09-26 14:09:10 +02:00
connection_t * conn ;
2004-07-20 08:44:16 +02:00
tor_assert ( router ) ;
tor_assert ( router - > dir_port ) ;
2004-05-05 04:50:38 +02:00
switch ( purpose )
{
case DIR_PURPOSE_FETCH_DIR :
log_fn ( LOG_DEBUG , " initiating directory fetch " ) ;
break ;
case DIR_PURPOSE_FETCH_RENDDESC :
log_fn ( LOG_DEBUG , " initiating hidden-service descriptor fetch " ) ;
break ;
case DIR_PURPOSE_UPLOAD_DIR :
log_fn ( LOG_DEBUG , " initiating server descriptor upload " ) ;
break ;
case DIR_PURPOSE_UPLOAD_RENDDESC :
log_fn ( LOG_DEBUG , " initiating hidden-service descriptor upload " ) ;
break ;
default :
log_fn ( LOG_ERR , " Unrecognized directory connection purpose. " ) ;
tor_assert ( 0 ) ;
}
2002-09-28 07:53:00 +02:00
2002-09-26 14:09:10 +02:00
conn = connection_new ( CONN_TYPE_DIR ) ;
/* set up conn so it's got all the data we need to remember */
2002-10-14 08:44:48 +02:00
conn - > addr = router - > addr ;
conn - > port = router - > dir_port ;
2003-10-04 05:29:09 +02:00
conn - > address = tor_strdup ( router - > address ) ;
conn - > nickname = tor_strdup ( router - > nickname ) ;
2004-04-25 22:37:37 +02:00
tor_assert ( router - > identity_pkey ) ;
2003-11-10 09:06:55 +01:00
conn - > identity_pkey = crypto_pk_dup_key ( router - > identity_pkey ) ;
2004-07-01 03:16:59 +02:00
crypto_pk_get_digest ( conn - > identity_pkey , conn - > identity_digest ) ;
2002-09-26 14:09:10 +02:00
2004-03-31 00:57:49 +02:00
conn - > purpose = purpose ;
2004-03-26 23:07:45 +01:00
2004-04-17 22:19:43 +02:00
/* give it an initial state */
conn - > state = DIR_CONN_STATE_CONNECTING ;
2004-03-31 00:57:49 +02:00
if ( purpose = = DIR_PURPOSE_FETCH_DIR | |
purpose = = DIR_PURPOSE_UPLOAD_DIR ) {
/* then we want to connect directly */
switch ( connection_connect ( conn , conn - > address , conn - > addr , conn - > port ) ) {
case - 1 :
2004-07-03 01:40:03 +02:00
router_mark_as_down ( conn - > identity_digest ) ; /* don't try him again */
2004-07-12 22:41:20 +02:00
if ( purpose = = DIR_PURPOSE_FETCH_DIR & & ! all_directory_servers_down ( ) ) {
2004-07-12 22:39:40 +02:00
log_fn ( LOG_INFO , " Giving up on dirserver %s; trying another. " , conn - > nickname ) ;
directory_get_from_dirserver ( purpose , payload , payload_len ) ;
}
2004-05-05 03:26:57 +02:00
connection_free ( conn ) ;
2004-03-31 00:57:49 +02:00
return ;
case 1 :
conn - > state = DIR_CONN_STATE_CLIENT_SENDING ; /* start flushing conn */
/* fall through */
case 0 :
2004-05-09 18:33:04 +02:00
/* queue the command on the outbuf */
directory_send_command ( conn , purpose , payload , payload_len ) ;
2004-03-31 00:57:49 +02:00
connection_watch_events ( conn , POLLIN | POLLOUT | POLLERR ) ;
/* writable indicates finish, readable indicates broken link,
error indicates broken link in windowsland . */
}
} else { /* we want to connect via tor */
/* make an AP connection
2004-07-20 08:44:16 +02:00
* populate it and add it at the right state
2004-03-31 00:57:49 +02:00
* socketpair and hook up both sides
*/
2004-04-01 00:02:13 +02:00
conn - > s = connection_ap_make_bridge ( conn - > address , conn - > port ) ;
if ( conn - > s < 0 ) {
log_fn ( LOG_WARN , " Making AP bridge to dirserver failed. " ) ;
2004-05-12 23:12:33 +02:00
connection_mark_for_close ( conn ) ;
2004-04-01 00:02:13 +02:00
return ;
}
2004-03-31 00:57:49 +02:00
2004-04-07 21:57:40 +02:00
conn - > state = DIR_CONN_STATE_CLIENT_SENDING ;
2004-05-05 03:26:57 +02:00
connection_add ( conn ) ;
2004-05-09 18:33:04 +02:00
/* queue the command on the outbuf */
directory_send_command ( conn , purpose , payload , payload_len ) ;
connection_watch_events ( conn , POLLIN | POLLOUT | POLLERR ) ;
2002-09-26 14:09:10 +02:00
}
}
2004-05-10 06:34:48 +02:00
/** Queue an appropriate HTTP command on conn-\>outbuf. The args
* < b > purpose < / b > , < b > payload < / b > , and < b > payload_len < / b > are as in
2004-05-05 04:50:38 +02:00
* directory_initiate_command .
*/
2004-04-01 00:02:13 +02:00
static void directory_send_command ( connection_t * conn , int purpose ,
const char * payload , int payload_len ) {
2004-06-16 23:08:29 +02:00
char fetchwholedir [ ] = " GET / HTTP/1.0 \r \n \r \n " ;
char fetchrunninglist [ ] = " GET /running-routers HTTP/1.0 \r \n \r \n " ;
2003-09-25 12:42:07 +02:00
char tmp [ 8192 ] ;
2002-09-26 14:09:10 +02:00
2004-04-25 22:37:37 +02:00
tor_assert ( conn & & conn - > type = = CONN_TYPE_DIR ) ;
2002-09-26 14:09:10 +02:00
2004-03-31 00:57:49 +02:00
switch ( purpose ) {
case DIR_PURPOSE_FETCH_DIR :
2004-04-25 22:37:37 +02:00
tor_assert ( payload = = NULL ) ;
2004-06-16 23:08:29 +02:00
connection_write_to_buf ( fetchwholedir , strlen ( fetchwholedir ) , conn ) ;
break ;
case DIR_PURPOSE_FETCH_RUNNING_LIST :
tor_assert ( payload = = NULL ) ;
connection_write_to_buf ( fetchrunninglist , strlen ( fetchrunninglist ) , conn ) ;
2003-09-17 22:09:06 +02:00
break ;
2004-03-31 00:57:49 +02:00
case DIR_PURPOSE_UPLOAD_DIR :
2004-04-25 22:37:37 +02:00
tor_assert ( payload ) ;
2004-04-08 03:47:33 +02:00
snprintf ( tmp , sizeof ( tmp ) , " POST / HTTP/1.0 \r \n Content-Length: %d \r \n \r \n " ,
payload_len ) ;
2004-03-31 00:57:49 +02:00
connection_write_to_buf ( tmp , strlen ( tmp ) , conn ) ;
2004-04-08 03:47:33 +02:00
connection_write_to_buf ( payload , payload_len , conn ) ;
2004-03-31 00:57:49 +02:00
break ;
2004-04-01 23:32:01 +02:00
case DIR_PURPOSE_FETCH_RENDDESC :
2004-04-25 22:37:37 +02:00
tor_assert ( payload ) ;
2004-04-01 23:32:01 +02:00
/* this must be true or we wouldn't be doing the lookup */
2004-04-25 22:37:37 +02:00
tor_assert ( payload_len < = REND_SERVICE_ID_LEN ) ;
2004-05-05 04:50:38 +02:00
/* This breaks the function abstraction. */
2004-04-01 23:32:01 +02:00
memcpy ( conn - > rend_query , payload , payload_len ) ;
conn - > rend_query [ payload_len ] = 0 ;
2004-04-03 05:33:57 +02:00
snprintf ( tmp , sizeof ( tmp ) , " GET %s%s HTTP/1.0 \r \n \r \n " , rend_fetch_url , payload ) ;
2004-03-31 00:57:49 +02:00
connection_write_to_buf ( tmp , strlen ( tmp ) , conn ) ;
break ;
2004-04-01 23:32:01 +02:00
case DIR_PURPOSE_UPLOAD_RENDDESC :
2004-04-25 22:37:37 +02:00
tor_assert ( payload ) ;
2004-03-31 00:57:49 +02:00
snprintf ( tmp , sizeof ( tmp ) ,
2004-04-03 05:33:57 +02:00
" POST %s HTTP/1.0 \r \n Content-Length: %d \r \n \r \n " , rend_publish_string , payload_len ) ;
2003-10-04 04:38:18 +02:00
connection_write_to_buf ( tmp , strlen ( tmp ) , conn ) ;
2004-04-01 00:02:13 +02:00
/* could include nuls, need to write it separately */
connection_write_to_buf ( payload , payload_len , conn ) ;
2003-09-17 22:09:06 +02:00
break ;
2002-09-26 14:09:10 +02:00
}
2004-03-31 00:57:49 +02:00
}
2004-05-10 06:34:48 +02:00
/** Parse an HTTP request string <b>headers</b> of the form "\%s \%s HTTP/1..."
* If it ' s well - formed , point * < b > url < / b > to the second \ % s ,
2004-03-31 00:57:49 +02:00
* null - terminate it ( this modifies headers ! ) and return 0.
* Otherwise , return - 1.
*/
2004-05-13 01:48:57 +02:00
static int
parse_http_url ( char * headers , char * * url )
{
2004-03-31 00:57:49 +02:00
char * s , * tmp ;
s = ( char * ) eat_whitespace_no_nl ( headers ) ;
if ( ! * s ) return - 1 ;
s = ( char * ) find_whitespace ( s ) ; /* get past GET/POST */
if ( ! * s ) return - 1 ;
s = ( char * ) eat_whitespace_no_nl ( s ) ;
if ( ! * s ) return - 1 ;
tmp = s ; /* this is it, assuming it's valid */
s = ( char * ) find_whitespace ( s ) ;
if ( ! * s ) return - 1 ;
* s = 0 ;
* url = tmp ;
2002-09-26 14:09:10 +02:00
return 0 ;
}
2004-05-10 06:34:48 +02:00
/** Parse an HTTP response string <b>headers</b> of the form
* " HTTP/1. \ %d \ %d \ %s \r \n ... " .
* If it ' s well - formed , assign * < b > code < / b > , point * < b > message < / b > to the first
2004-03-12 13:43:13 +01:00
* non - space character after code if there is one and message is non - NULL
* ( else leave it alone ) , and return 0.
* Otherwise , return - 1.
*/
2004-05-13 01:48:57 +02:00
static int
parse_http_response ( char * headers , int * code , char * * message )
{
2004-03-12 13:43:13 +01:00
int n1 , n2 ;
2004-04-25 22:37:37 +02:00
tor_assert ( headers & & code ) ;
2004-03-12 13:43:13 +01:00
2004-03-19 21:50:12 +01:00
while ( isspace ( ( int ) * headers ) ) headers + + ; /* tolerate leading whitespace */
2004-03-12 13:43:13 +01:00
if ( sscanf ( headers , " HTTP/1.%d %d " , & n1 , & n2 ) < 2 | |
( n1 ! = 0 & & n1 ! = 1 ) | |
( n2 < 100 | | n2 > = 600 ) ) {
log_fn ( LOG_WARN , " Failed to parse header '%s' " , headers ) ;
return - 1 ;
}
* code = n2 ;
if ( message ) {
/* XXX should set *message correctly */
}
return 0 ;
}
2004-07-20 04:44:26 +02:00
/** We are a client, and we've finished reading the server's
* response . Parse and it and act appropriately .
*
* Return - 1 if an error has occurred , or 0 normally . The caller
* will take care of marking the connection for close .
2004-05-05 04:50:38 +02:00
*/
2004-07-20 04:44:26 +02:00
static int
2004-07-20 08:44:16 +02:00
connection_dir_client_reached_eof ( connection_t * conn )
2004-07-20 04:44:26 +02:00
{
2004-04-01 23:32:01 +02:00
char * body ;
2004-03-12 13:43:13 +01:00
char * headers ;
2004-04-01 23:32:01 +02:00
int body_len = 0 ;
2004-03-12 13:43:13 +01:00
int status_code ;
2002-09-26 14:09:10 +02:00
2004-07-20 04:44:26 +02:00
switch ( fetch_from_buf_http ( conn - > inbuf ,
& headers , MAX_HEADERS_SIZE ,
& body , & body_len , MAX_DIR_SIZE ) ) {
case - 1 : /* overflow */
log_fn ( LOG_WARN , " 'fetch' response too large. Failing. " ) ;
return - 1 ;
case 0 :
log_fn ( LOG_INFO , " 'fetch' response not all here, but we're at eof. Closing. " ) ;
return - 1 ;
/* case 1, fall through */
}
2002-09-26 14:09:10 +02:00
2004-07-20 04:44:26 +02:00
if ( parse_http_response ( headers , & status_code , NULL ) < 0 ) {
log_fn ( LOG_WARN , " Unparseable headers. Closing. " ) ;
free ( body ) ; free ( headers ) ;
return - 1 ;
}
if ( conn - > purpose = = DIR_PURPOSE_FETCH_DIR ) {
/* fetch/process the directory to learn about new routers. */
log_fn ( LOG_INFO , " Received directory (size %d): \n %s " , body_len , body ) ;
if ( status_code = = 503 | | body_len = = 0 ) {
log_fn ( LOG_INFO , " Empty directory. Ignoring. " ) ;
free ( body ) ; free ( headers ) ;
return 0 ;
}
if ( status_code ! = 200 ) {
log_fn ( LOG_WARN , " Received http status code %d from dirserver. Failing. " ,
status_code ) ;
free ( body ) ; free ( headers ) ;
2004-03-12 13:43:13 +01:00
return - 1 ;
}
2004-07-20 04:44:26 +02:00
if ( router_load_routerlist_from_directory ( body , NULL ) < 0 ) {
log_fn ( LOG_INFO , " ...but parsing failed. Ignoring. " ) ;
} else {
log_fn ( LOG_INFO , " updated routers. " ) ;
2004-03-12 13:43:13 +01:00
}
2004-07-20 04:44:26 +02:00
directory_has_arrived ( ) ; /* do things we've been waiting to do */
}
2004-03-12 13:43:13 +01:00
2004-07-20 04:44:26 +02:00
if ( conn - > purpose = = DIR_PURPOSE_FETCH_RUNNING_LIST ) {
running_routers_t * rrs ;
routerlist_t * rl ;
/* just update our list of running routers, if this list is new info */
log_fn ( LOG_INFO , " Received running-routers list (size %d): \n %s " , body_len , body ) ;
if ( status_code ! = 200 ) {
log_fn ( LOG_WARN , " Received http status code %d from dirserver. Failing. " ,
status_code ) ;
2004-04-01 23:32:01 +02:00
free ( body ) ; free ( headers ) ;
2004-03-12 13:43:13 +01:00
return - 1 ;
}
2004-07-20 04:44:26 +02:00
if ( ! ( rrs = router_parse_runningrouters ( body ) ) ) {
log_fn ( LOG_WARN , " Can't parse runningrouters list " ) ;
free ( body ) ; free ( headers ) ;
return - 1 ;
2004-03-12 13:43:13 +01:00
}
2004-07-20 04:44:26 +02:00
router_get_routerlist ( & rl ) ;
routerlist_update_from_runningrouters ( rl , rrs ) ;
running_routers_free ( rrs ) ;
}
2004-03-12 13:43:13 +01:00
2004-07-20 04:44:26 +02:00
if ( conn - > purpose = = DIR_PURPOSE_UPLOAD_DIR ) {
switch ( status_code ) {
case 200 :
log_fn ( LOG_INFO , " eof (status 200) after uploading server descriptor: finished. " ) ;
break ;
case 400 :
log_fn ( LOG_WARN , " http status 400 (bad request) response from dirserver. Malformed server descriptor? " ) ;
break ;
case 403 :
log_fn ( LOG_WARN , " http status 403 (unapproved server) response from dirserver. Is your clock skewed? Have you mailed us your identity fingerprint? Are you using the right key? See README. " ) ;
break ;
default :
log_fn ( LOG_WARN , " http status %d response unrecognized. " , status_code ) ;
break ;
2004-06-16 23:08:29 +02:00
}
2004-07-20 04:44:26 +02:00
}
2004-06-16 23:08:29 +02:00
2004-07-20 04:44:26 +02:00
if ( conn - > purpose = = DIR_PURPOSE_FETCH_RENDDESC ) {
log_fn ( LOG_INFO , " Received rendezvous descriptor (size %d, status code %d) " ,
body_len , status_code ) ;
switch ( status_code ) {
case 200 :
if ( rend_cache_store ( body , body_len ) < 0 ) {
log_fn ( LOG_WARN , " Failed to store rendezvous descriptor. " ) ;
/* alice's ap_stream will notice when connection_mark_for_close
* cleans it up */
} else {
/* success. notify pending connections about this. */
rend_client_desc_fetched ( conn - > rend_query , 1 ) ;
conn - > purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC ;
}
break ;
case 404 :
/* not there. pending connections will be notified when
* connection_mark_for_close cleans it up . */
break ;
case 400 :
log_fn ( LOG_WARN , " http status 400 (bad request). Dirserver didn't like our rendezvous query? " ) ;
break ;
2002-09-28 02:52:59 +02:00
}
2004-07-20 04:44:26 +02:00
}
2004-03-31 00:57:49 +02:00
2004-07-20 04:44:26 +02:00
if ( conn - > purpose = = DIR_PURPOSE_UPLOAD_RENDDESC ) {
switch ( status_code ) {
case 200 :
log_fn ( LOG_INFO , " eof (status 200) after uploading rendezvous descriptor: finished. " ) ;
break ;
case 400 :
log_fn ( LOG_WARN , " http status 400 (bad request) response from dirserver. Malformed rendezvous descriptor? " ) ;
break ;
default :
log_fn ( LOG_WARN , " http status %d response unrecognized. " , status_code ) ;
break ;
2004-03-31 00:57:49 +02:00
}
2004-07-20 04:44:26 +02:00
}
free ( body ) ; free ( headers ) ;
return 0 ;
}
/** Read handler for directory connections. (That's connections <em>to</em>
* directory servers and connections < em > at < / em > directory servers . )
*/
int connection_dir_process_inbuf ( connection_t * conn ) {
int retval ;
2004-03-31 00:57:49 +02:00
2004-07-20 04:44:26 +02:00
tor_assert ( conn & & conn - > type = = CONN_TYPE_DIR ) ;
/* Directory clients write, then read data until they receive EOF;
* directory servers read data until they get an HTTP command , then
* write their response ( when it ' s finished flushing , they mark for
* close ) .
*/
if ( conn - > inbuf_reached_eof ) {
if ( conn - > state ! = DIR_CONN_STATE_CLIENT_READING ) {
log_fn ( LOG_INFO , " conn reached eof, not reading. Closing. " ) ;
connection_close_immediate ( conn ) ; /* it was an error; give up on flushing */
connection_mark_for_close ( conn ) ;
return - 1 ;
2004-03-31 00:57:49 +02:00
}
2004-07-20 04:44:26 +02:00
2004-07-20 08:44:16 +02:00
retval = connection_dir_client_reached_eof ( conn ) ;
2004-05-12 23:12:33 +02:00
connection_mark_for_close ( conn ) ;
2004-07-20 04:44:26 +02:00
return retval ;
2004-05-05 04:50:38 +02:00
} /* endif 'reached eof' */
2002-09-26 14:09:10 +02:00
2004-05-05 04:50:38 +02:00
/* If we're on the dirserver side, look for a command. */
2004-03-31 00:57:49 +02:00
if ( conn - > state = = DIR_CONN_STATE_SERVER_COMMAND_WAIT ) {
if ( directory_handle_command ( conn ) < 0 ) {
2004-05-12 23:12:33 +02:00
connection_mark_for_close ( conn ) ;
2004-03-31 00:57:49 +02:00
return - 1 ;
}
2004-03-31 07:01:30 +02:00
return 0 ;
2004-03-31 00:57:49 +02:00
}
2003-09-17 22:09:06 +02:00
/* XXX for READ states, might want to make sure inbuf isn't too big */
2002-09-26 14:09:10 +02:00
2003-09-17 22:09:06 +02:00
log_fn ( LOG_DEBUG , " Got data, not eof. Leaving on inbuf. " ) ;
2002-09-26 14:09:10 +02:00
return 0 ;
}
2004-03-12 13:43:13 +01:00
static char answer200 [ ] = " HTTP/1.0 200 OK \r \n \r \n " ;
static char answer400 [ ] = " HTTP/1.0 400 Bad request \r \n \r \n " ;
static char answer403 [ ] = " HTTP/1.0 403 Unapproved server \r \n \r \n " ;
2004-03-31 00:57:49 +02:00
static char answer404 [ ] = " HTTP/1.0 404 Not found \r \n \r \n " ;
2004-03-12 13:43:13 +01:00
static char answer503 [ ] = " HTTP/1.0 503 Directory unavailable \r \n \r \n " ;
2004-05-09 18:47:25 +02:00
/** Helper function: called when a dirserver gets a complete HTTP GET
2004-05-06 13:08:04 +02:00
* request . Look for a request for a directory or for a rendezvous
* service descriptor . On finding one , write a response into
2004-05-10 06:34:48 +02:00
* conn - \ > outbuf . If the request is unrecognized , send a 404.
2004-05-06 13:08:04 +02:00
* Always return 0. */
2004-05-13 01:48:57 +02:00
static int
directory_handle_command_get ( connection_t * conn , char * headers ,
char * body , int body_len )
{
2003-09-28 10:06:18 +02:00
size_t dlen ;
2003-09-27 23:30:10 +02:00
const char * cp ;
2004-03-31 00:57:49 +02:00
char * url ;
2004-03-31 07:01:30 +02:00
char tmp [ 8192 ] ;
2002-09-26 14:09:10 +02:00
2003-12-17 10:42:28 +01:00
log_fn ( LOG_DEBUG , " Received GET command. " ) ;
2004-03-31 00:57:49 +02:00
conn - > state = DIR_CONN_STATE_SERVER_WRITING ;
2003-12-17 10:42:28 +01:00
2004-03-31 00:57:49 +02:00
if ( parse_http_url ( headers , & url ) < 0 ) {
connection_write_to_buf ( answer400 , strlen ( answer400 ) , conn ) ;
2004-03-12 13:43:13 +01:00
return 0 ;
2003-12-17 10:42:28 +01:00
}
2004-03-31 00:57:49 +02:00
if ( ! strcmp ( url , " / " ) ) { /* directory fetch */
dlen = dirserv_get_directory ( & cp ) ;
if ( dlen = = 0 ) {
log_fn ( LOG_WARN , " My directory is empty. Closing. " ) ;
connection_write_to_buf ( answer503 , strlen ( answer503 ) , conn ) ;
return 0 ;
}
log_fn ( LOG_DEBUG , " Dumping directory to client. " ) ;
2004-04-27 23:48:37 +02:00
snprintf ( tmp , sizeof ( tmp ) , " HTTP/1.0 200 OK \r \n Content-Length: %d \r \n Content-Type: text/plain \r \n \r \n " ,
2004-04-08 03:47:33 +02:00
( int ) dlen ) ;
2004-03-31 07:01:30 +02:00
connection_write_to_buf ( tmp , strlen ( tmp ) , conn ) ;
2004-04-08 03:47:33 +02:00
connection_write_to_buf ( cp , strlen ( cp ) , conn ) ;
2004-03-31 00:57:49 +02:00
return 0 ;
}
2004-06-16 23:08:29 +02:00
if ( ! strcmp ( url , " /running-routers " ) ) { /* running-routers fetch */
2004-07-21 10:40:57 +02:00
if ( ! authdir_mode ( ) ) {
/* XXX008 for now, we don't cache running-routers. Reject. */
connection_write_to_buf ( answer400 , strlen ( answer400 ) , conn ) ;
return 0 ;
}
2004-06-16 23:08:29 +02:00
dlen = dirserv_get_runningrouters ( & cp ) ;
2004-06-30 23:48:02 +02:00
if ( dlen < 0 ) { /* we failed to create cp */
connection_write_to_buf ( answer503 , strlen ( answer503 ) , conn ) ;
return 0 ;
}
2004-06-16 23:08:29 +02:00
snprintf ( tmp , sizeof ( tmp ) , " HTTP/1.0 200 OK \r \n Content-Length: %d \r \n Content-Type: text/plain \r \n \r \n " ,
( int ) dlen ) ;
connection_write_to_buf ( tmp , strlen ( tmp ) , conn ) ;
connection_write_to_buf ( cp , strlen ( cp ) , conn ) ;
return 0 ;
}
2004-03-31 07:01:30 +02:00
if ( ! strncmp ( url , rend_fetch_url , strlen ( rend_fetch_url ) ) ) {
/* rendezvous descriptor fetch */
2004-03-31 05:42:56 +02:00
const char * descp ;
int desc_len ;
2004-03-31 00:57:49 +02:00
2004-07-21 10:40:57 +02:00
if ( ! authdir_mode ( ) ) {
/* We don't hand out rend descs. In fact, it could be a security
* risk , since rend_cache_lookup_desc ( ) below would provide it
* if we ' re gone to the site recently , and 404 if we haven ' t .
*
* Reject . */
connection_write_to_buf ( answer400 , strlen ( answer400 ) , conn ) ;
return 0 ;
}
2004-04-08 00:00:54 +02:00
switch ( rend_cache_lookup_desc ( url + strlen ( rend_fetch_url ) , & descp , & desc_len ) ) {
2004-03-31 01:41:24 +02:00
case 1 : /* valid */
2004-04-27 23:48:37 +02:00
snprintf ( tmp , sizeof ( tmp ) , " HTTP/1.0 200 OK \r \n Content-Length: %d \r \n Content-Type: application/octet-stream \r \n \r \n " ,
2004-03-31 07:01:30 +02:00
desc_len ) ; /* can't include descp here, because it's got nuls */
connection_write_to_buf ( tmp , strlen ( tmp ) , conn ) ;
connection_write_to_buf ( descp , desc_len , conn ) ;
2004-03-31 01:41:24 +02:00
break ;
case 0 : /* well-formed but not present */
connection_write_to_buf ( answer404 , strlen ( answer404 ) , conn ) ;
break ;
case - 1 : /* not well-formed */
connection_write_to_buf ( answer400 , strlen ( answer400 ) , conn ) ;
break ;
}
return 0 ;
2004-03-31 00:57:49 +02:00
}
/* we didn't recognize the url */
connection_write_to_buf ( answer404 , strlen ( answer404 ) , conn ) ;
2003-12-17 10:42:28 +01:00
return 0 ;
}
2004-05-09 18:47:25 +02:00
/** Helper function: called when a dirserver gets a complete HTTP POST
2004-05-06 13:08:04 +02:00
* request . Look for an uploaded server descriptor or rendezvous
* service descriptor . On finding one , process it and write a
2004-05-10 06:34:48 +02:00
* response into conn - \ > outbuf . If the request is unrecognized , send a
2004-05-06 13:08:04 +02:00
* 404. Always return 0. */
2004-05-13 01:48:57 +02:00
static int
directory_handle_command_post ( connection_t * conn , char * headers ,
char * body , int body_len )
{
2003-12-17 10:42:28 +01:00
const char * cp ;
2004-03-31 00:57:49 +02:00
char * url ;
2003-12-17 10:42:28 +01:00
log_fn ( LOG_DEBUG , " Received POST command. " ) ;
2004-03-31 00:57:49 +02:00
2003-12-17 10:42:28 +01:00
conn - > state = DIR_CONN_STATE_SERVER_WRITING ;
2004-03-31 00:57:49 +02:00
2004-07-21 10:40:57 +02:00
if ( ! authdir_mode ( ) ) {
/* we just provide cached directories; we don't want to
* receive anything . */
connection_write_to_buf ( answer400 , strlen ( answer400 ) , conn ) ;
return 0 ;
}
2004-03-31 00:57:49 +02:00
if ( parse_http_url ( headers , & url ) < 0 ) {
connection_write_to_buf ( answer400 , strlen ( answer400 ) , conn ) ;
return 0 ;
}
2004-04-03 05:33:57 +02:00
log_fn ( LOG_INFO , " url '%s' posted to us. " , url ) ;
2004-03-31 00:57:49 +02:00
if ( ! strcmp ( url , " / " ) ) { /* server descriptor post */
cp = body ;
switch ( dirserv_add_descriptor ( & cp ) ) {
case - 1 :
/* malformed descriptor, or something wrong */
connection_write_to_buf ( answer400 , strlen ( answer400 ) , conn ) ;
break ;
case 0 :
/* descriptor was well-formed but server has not been approved */
connection_write_to_buf ( answer403 , strlen ( answer403 ) , conn ) ;
break ;
case 1 :
dirserv_get_directory ( & cp ) ; /* rebuild and write to disk */
connection_write_to_buf ( answer200 , strlen ( answer200 ) , conn ) ;
break ;
}
2004-03-31 07:01:30 +02:00
return 0 ;
2004-03-31 00:57:49 +02:00
}
2004-03-31 07:01:30 +02:00
if ( ! strncmp ( url , rend_publish_string , strlen ( rend_publish_string ) ) ) {
/* rendezvous descriptor post */
2004-03-31 06:10:10 +02:00
if ( rend_cache_store ( body , body_len ) < 0 )
2004-03-31 01:41:24 +02:00
connection_write_to_buf ( answer400 , strlen ( answer400 ) , conn ) ;
else
connection_write_to_buf ( answer200 , strlen ( answer200 ) , conn ) ;
2004-03-31 07:01:30 +02:00
return 0 ;
2004-03-31 00:57:49 +02:00
}
/* we didn't recognize the url */
connection_write_to_buf ( answer404 , strlen ( answer404 ) , conn ) ;
2003-12-17 10:42:28 +01:00
return 0 ;
}
2004-05-09 18:47:25 +02:00
/** Called when a dirserver receives data on a directory connection;
2004-05-05 04:50:38 +02:00
* looks for an HTTP request . If the request is complete , remove it
* from the inbuf , try to process it ; otherwise , leave it on the
* buffer . Return a 0 on success , or - 1 on error .
*/
2003-12-17 10:42:28 +01:00
static int directory_handle_command ( connection_t * conn ) {
char * headers = NULL , * body = NULL ;
2004-03-31 07:01:30 +02:00
int body_len = 0 ;
2003-12-17 10:42:28 +01:00
int r ;
2004-04-25 22:37:37 +02:00
tor_assert ( conn & & conn - > type = = CONN_TYPE_DIR ) ;
2002-09-26 14:09:10 +02:00
2003-09-25 07:17:11 +02:00
switch ( fetch_from_buf_http ( conn - > inbuf ,
2004-03-31 07:01:30 +02:00
& headers , MAX_HEADERS_SIZE ,
& body , & body_len , MAX_BODY_SIZE ) ) {
2003-09-17 22:09:06 +02:00
case - 1 : /* overflow */
2003-10-10 03:48:32 +02:00
log_fn ( LOG_WARN , " input too large. Failing. " ) ;
2003-09-17 22:09:06 +02:00
return - 1 ;
case 0 :
log_fn ( LOG_DEBUG , " command not all here yet. " ) ;
return 0 ;
/* case 1, fall through */
2002-09-26 14:09:10 +02:00
}
2003-12-17 10:42:28 +01:00
log_fn ( LOG_DEBUG , " headers '%s', body '%s'. " , headers , body ) ;
2002-09-26 14:09:10 +02:00
2003-12-17 10:42:28 +01:00
if ( ! strncasecmp ( headers , " GET " , 3 ) )
2004-03-31 07:01:30 +02:00
r = directory_handle_command_get ( conn , headers , body , body_len ) ;
2003-12-17 10:42:28 +01:00
else if ( ! strncasecmp ( headers , " POST " , 4 ) )
2004-03-31 07:01:30 +02:00
r = directory_handle_command_post ( conn , headers , body , body_len ) ;
2003-12-17 10:42:28 +01:00
else {
log_fn ( LOG_WARN , " Got headers '%s' with unknown command. Closing. " , headers ) ;
r = - 1 ;
2002-09-26 14:09:10 +02:00
}
2003-12-17 10:42:28 +01:00
tor_free ( headers ) ; tor_free ( body ) ;
return r ;
2002-09-26 14:09:10 +02:00
}
2004-05-09 18:47:25 +02:00
/** Write handler for directory connections; called when all data has
2004-05-12 21:17:09 +02:00
* been flushed . Close the connection or wait for a response as
* appropriate .
2004-05-05 04:50:38 +02:00
*/
2002-09-26 14:09:10 +02:00
int connection_dir_finished_flushing ( connection_t * conn ) {
2004-04-25 22:37:37 +02:00
tor_assert ( conn & & conn - > type = = CONN_TYPE_DIR ) ;
2002-09-26 14:09:10 +02:00
switch ( conn - > state ) {
2004-03-31 00:57:49 +02:00
case DIR_CONN_STATE_CLIENT_SENDING :
log_fn ( LOG_DEBUG , " client finished sending command. " ) ;
conn - > state = DIR_CONN_STATE_CLIENT_READING ;
connection_stop_writing ( conn ) ;
2002-09-26 14:09:10 +02:00
return 0 ;
2003-09-17 22:09:06 +02:00
case DIR_CONN_STATE_SERVER_WRITING :
2003-09-26 12:03:50 +02:00
log_fn ( LOG_INFO , " Finished writing server response. Closing. " ) ;
2004-05-12 23:12:33 +02:00
connection_mark_for_close ( conn ) ;
2004-02-28 05:11:53 +01:00
return 0 ;
2002-09-26 14:09:10 +02:00
default :
2004-01-31 01:36:00 +01:00
log_fn ( LOG_WARN , " BUG: called in unexpected state %d. " , conn - > state ) ;
2003-09-26 12:03:50 +02:00
return - 1 ;
2002-09-26 14:09:10 +02:00
}
return 0 ;
}
2004-05-12 21:17:09 +02:00
/** Connected handler for directory connections: begin sending data to the
* server */
int connection_dir_finished_connecting ( connection_t * conn )
{
tor_assert ( conn & & conn - > type = = CONN_TYPE_DIR ) ;
tor_assert ( conn - > state = = DIR_CONN_STATE_CONNECTING ) ;
log_fn ( LOG_INFO , " Dir connection to router %s:%u established. " ,
conn - > address , conn - > port ) ;
conn - > state = DIR_CONN_STATE_CLIENT_SENDING ; /* start flushing conn */
return 0 ;
}
2003-04-07 04:12:02 +02:00
/*
Local Variables :
mode : c
indent - tabs - mode : nil
c - basic - offset : 2
End :
*/