2004-11-07 02:33:06 +01:00
/* Copyright 2001-2004 Roger Dingledine.
* Copyright 2004 Roger Dingledine , Nick Mathewson . */
/* See LICENSE for licensing information */
2002-09-26 14:09:10 +02:00
/* $Id$ */
2004-11-29 23:25:31 +01:00
const char directory_c_id [ ] = " $Id$ " ;
2002-09-26 14:09:10 +02:00
# 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
2004-10-12 17:55:20 +02:00
directory_initiate_command_router ( routerinfo_t * router , uint8_t purpose ,
2004-11-12 17:39:03 +01:00
const char * resource ,
2004-10-14 04:47:09 +02:00
const char * payload , size_t payload_len ) ;
2004-10-12 17:55:20 +02:00
static void
directory_initiate_command_trusted_dir ( trusted_dir_server_t * dirserv ,
2004-11-12 17:39:03 +01:00
uint8_t purpose , const char * resource ,
const char * payload , size_t payload_len ) ;
2004-10-12 17:55:20 +02:00
static void
directory_initiate_command ( const char * address , uint32_t addr , uint16_t port ,
const char * platform ,
const char * digest , uint8_t purpose ,
2004-11-12 17:39:03 +01:00
const char * resource ,
2004-10-14 04:47:09 +02:00
const char * payload , size_t payload_len ) ;
2004-10-12 17:55:20 +02:00
2004-10-08 07:53:59 +02:00
static void
2004-10-12 17:55:20 +02:00
directory_send_command ( connection_t * conn , const char * platform ,
2004-11-12 17:39:03 +01:00
int purpose , const char * resource ,
const char * payload , size_t payload_len ) ;
2003-09-16 07:41:49 +02:00
static int directory_handle_command ( connection_t * conn ) ;
2005-01-19 23:47:48 +01:00
static int body_is_plausible ( const char * body , size_t body_len , int purpose ) ;
2003-09-16 07:41:49 +02:00
2002-09-26 14:09:10 +02:00
/********* START VARIABLES **********/
2004-12-04 02:14:36 +01:00
static addr_policy_t * dir_policy = NULL ;
2004-10-25 08:16:26 +02:00
2004-09-28 05:30:28 +02:00
#if 0 /* commented out for now, since for now what clients send is
different from what servers want to receive */
2004-05-09 18:47:25 +02:00
/** URL for publishing rendezvous descriptors. */
2004-09-27 05:39:30 +02:00
char rend_publish_string [ ] = " /tor/rendezvous/publish " ;
2004-05-09 18:47:25 +02:00
/** Prefix for downloading rendezvous descriptors. */
2004-09-27 05:39:30 +02:00
char rend_fetch_url [ ] = " /tor/rendezvous/ " ;
2004-09-28 05:30:28 +02:00
# endif
2004-04-03 05:33:57 +02:00
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
2004-08-15 22:30:15 +02:00
# define ALLOW_DIRECTORY_TIME_SKEW 30*60
2002-09-26 14:09:10 +02:00
/********* END VARIABLES ************/
2004-11-12 20:39:13 +01:00
/** Parse get_options()->DirPolicy, and put the processed version in
* & dir_policy . Ignore port specifiers .
2004-10-25 08:16:26 +02:00
*/
2004-11-12 20:39:13 +01:00
void
parse_dir_policy ( void )
2004-10-25 08:16:26 +02:00
{
2004-12-04 02:14:36 +01:00
addr_policy_t * n ;
2004-10-25 08:16:26 +02:00
if ( dir_policy ) {
2004-11-12 20:39:13 +01:00
addr_policy_free ( dir_policy ) ;
2004-10-25 08:16:26 +02:00
dir_policy = NULL ;
}
2004-11-12 20:39:13 +01:00
config_parse_addr_policy ( get_options ( ) - > DirPolicy , & dir_policy ) ;
2004-10-25 08:16:26 +02:00
/* ports aren't used. */
for ( n = dir_policy ; n ; n = n - > next ) {
n - > prt_min = 1 ;
n - > prt_max = 65535 ;
}
}
/** Return 1 if <b>addr</b> is permitted to connect to our dir port,
* based on < b > dir_policy < / b > . Else return 0.
*/
int dir_policy_permits_address ( uint32_t addr )
{
int a ;
2004-11-28 10:05:49 +01:00
if ( ! dir_policy ) /* 'no dir policy' means 'accept' */
2004-10-25 08:16:26 +02:00
return 1 ;
2004-11-12 20:39:13 +01:00
a = router_compare_addr_to_addr_policy ( addr , 1 , dir_policy ) ;
2004-10-25 08:16:26 +02:00
if ( a = = - 1 )
return 0 ;
else if ( a = = 0 )
return 1 ;
tor_assert ( a = = 1 ) ;
2004-12-13 01:44:39 +01:00
log_fn ( LOG_WARN , " Bug: got unexpected 'maybe' answer from dir policy " ) ;
2004-10-25 08:16:26 +02:00
return 0 ;
}
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 ,
2004-10-14 04:47:09 +02:00
size_t payload_len )
2004-05-13 01:48:57 +02:00
{
2004-10-15 21:04:38 +02:00
smartlist_t * dirservers ;
2004-05-13 01:48:57 +02:00
2004-10-15 21:04:38 +02:00
router_get_trusted_dir_servers ( & dirservers ) ;
tor_assert ( dirservers ) ;
2004-10-15 21:17:36 +02:00
/* This tries dirservers which we believe to be down, but ultimately, that's
* harmless , and we may as well err on the side of getting things uploaded .
*/
2004-10-15 21:04:38 +02:00
SMARTLIST_FOREACH ( dirservers , trusted_dir_server_t * , ds ,
{
/* Pay attention to fascistfirewall when we're uploading a
* router descriptor , but not when uploading a service
* descriptor - - those use Tor . */
2004-11-06 06:18:11 +01:00
if ( get_options ( ) - > FascistFirewall & & purpose = = DIR_PURPOSE_UPLOAD_DIR & &
! get_options ( ) - > HttpProxy ) {
2005-01-12 05:58:23 +01:00
if ( ! smartlist_string_num_isin ( get_options ( ) - > FirewallPorts , ds - > dir_port ) )
2004-10-15 21:04:38 +02:00
continue ;
}
2004-11-12 17:39:03 +01:00
directory_initiate_command_trusted_dir ( ds , purpose , NULL ,
payload , payload_len ) ;
2004-10-15 21:04:38 +02:00
} ) ;
2004-05-13 01:48:57 +02:00
}
2004-11-15 04:53:03 +01:00
/** Start a connection to a random running directory server, using
* connection purpose ' purpose ' requesting ' resource ' . The purpose
* should be one of ' DIR_PURPOSE_FETCH_DIR ' ,
* ' DIR_PURPOSE_FETCH_RENDDESC ' , ' DIR_PURPOSE_FETCH_RUNNING_LIST . '
2005-01-03 21:51:24 +01:00
* If < b > retry_if_no_servers < / b > , then if all the possible servers seem
* down , mark them up and try again .
2004-05-13 01:48:57 +02:00
*/
void
2005-01-03 21:51:24 +01:00
directory_get_from_dirserver ( uint8_t purpose , const char * resource ,
int retry_if_no_servers )
2004-05-13 01:48:57 +02:00
{
2004-10-12 17:55:20 +02:00
routerinfo_t * r = NULL ;
trusted_dir_server_t * ds = NULL ;
2004-11-15 04:53:03 +01:00
int fascistfirewall = get_options ( ) - > FascistFirewall ;
2005-01-10 18:39:41 +01:00
int directconn = purpose = = DIR_PURPOSE_FETCH_DIR | |
purpose = = DIR_PURPOSE_FETCH_RUNNING_LIST ;
int fetch_fresh_first = advertised_server_mode ( ) ;
2004-07-20 08:44:16 +02:00
2005-01-10 18:39:41 +01:00
if ( directconn ) {
if ( fetch_fresh_first ) {
2004-07-21 02:12:42 +02:00
/* only ask authdirservers, and don't ask myself */
2005-01-03 21:51:24 +01:00
ds = router_pick_trusteddirserver ( 1 , fascistfirewall ,
retry_if_no_servers ) ;
2005-01-10 18:39:41 +01:00
}
if ( ! ds ) {
2004-07-20 08:44:16 +02:00
/* anybody with a non-zero dirport will do */
2004-11-29 22:01:34 +01:00
r = router_pick_directory_server ( 1 , fascistfirewall ,
2005-01-03 21:51:24 +01:00
purpose = = DIR_PURPOSE_FETCH_RUNNING_LIST ,
retry_if_no_servers ) ;
2004-10-14 03:44:32 +02:00
if ( ! r ) {
2004-11-29 22:01:34 +01:00
log_fn ( LOG_INFO , " No router found for %s; falling back to dirserver list " ,
purpose = = DIR_PURPOSE_FETCH_RUNNING_LIST
? " status list " : " directory " ) ;
2005-01-03 21:51:24 +01:00
ds = router_pick_trusteddirserver ( 1 , fascistfirewall ,
retry_if_no_servers ) ;
2004-10-14 03:44:32 +02:00
}
2004-07-20 08:44:16 +02:00
}
} else { // (purpose == DIR_PURPOSE_FETCH_RENDDESC)
/* only ask authdirservers, any of them will do */
2004-10-15 06:57:36 +02:00
/* Never use fascistfirewall; we're going via Tor. */
2005-01-03 21:51:24 +01:00
ds = router_pick_trusteddirserver ( 0 , 0 , retry_if_no_servers ) ;
2004-07-20 08:44:16 +02:00
}
2004-10-12 17:55:20 +02:00
if ( r )
2004-11-12 17:39:03 +01:00
directory_initiate_command_router ( r , purpose , resource , NULL , 0 ) ;
2004-10-12 17:55:20 +02:00
else if ( ds )
2004-11-12 17:39:03 +01:00
directory_initiate_command_trusted_dir ( ds , purpose , resource , NULL , 0 ) ;
2005-01-03 21:51:24 +01:00
else {
2005-01-10 18:39:41 +01:00
log_fn ( LOG_NOTICE , " No running dirservers known. Not trying. (purpose %d) " ,
purpose ) ;
if ( directconn ) {
/* remember we tried them all and failed. */
directory_all_unreachable ( time ( NULL ) ) ;
}
2005-01-03 21:51:24 +01:00
}
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
2004-11-12 17:39:03 +01:00
* of the HTTP post . Otherwise , < b > payload < / b > should be NULL .
*
* When fetching a rendezvous descriptor , < b > resource < / b > is 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
2004-10-12 17:55:20 +02:00
directory_initiate_command_router ( routerinfo_t * router , uint8_t purpose ,
2004-11-12 17:39:03 +01:00
const char * resource ,
2004-10-14 04:47:09 +02:00
const char * payload , size_t payload_len )
2004-10-12 17:55:20 +02:00
{
directory_initiate_command ( router - > address , router - > addr , router - > dir_port ,
router - > platform , router - > identity_digest ,
2004-11-12 17:39:03 +01:00
purpose , resource , payload , payload_len ) ;
2004-10-12 17:55:20 +02:00
}
2004-11-12 17:39:03 +01:00
/** As directory_initiate_command_router, but send the command to a trusted
* directory server < b > dirserv < / b > . * */
2004-10-12 17:55:20 +02:00
static void
directory_initiate_command_trusted_dir ( trusted_dir_server_t * dirserv ,
2004-11-12 17:39:03 +01:00
uint8_t purpose , const char * resource ,
const char * payload , size_t payload_len )
2004-10-12 17:55:20 +02:00
{
2004-10-14 03:44:32 +02:00
directory_initiate_command ( dirserv - > address , dirserv - > addr , dirserv - > dir_port ,
2004-11-12 17:39:03 +01:00
NULL , dirserv - > digest , purpose , resource , payload , payload_len ) ;
2004-10-12 17:55:20 +02:00
}
2005-01-03 21:07:07 +01:00
/** Called when we are unable to complete our connection to a
* directory server : Mark the router as down and try again if possible .
*/
2005-01-04 02:16:20 +01:00
void
2005-01-03 21:07:07 +01:00
connection_dir_connect_failed ( connection_t * conn )
{
router_mark_as_down ( conn - > identity_digest ) ; /* don't try him again */
if ( conn - > purpose = = DIR_PURPOSE_FETCH_DIR | |
conn - > purpose = = DIR_PURPOSE_FETCH_RUNNING_LIST ) {
2005-01-03 22:14:09 +01:00
log_fn ( LOG_INFO , " Giving up on directory server at '%s'; retrying " ,
conn - > address ) ;
2005-01-03 21:51:24 +01:00
directory_get_from_dirserver ( conn - > purpose , NULL ,
0 /* don't retry_if_no_servers */ ) ;
2005-01-03 21:07:07 +01:00
}
}
2005-01-12 05:58:23 +01:00
/** Helper for directory_initiate_command_(router|trusted_dir): send the
2004-11-12 17:39:03 +01:00
* command to a server whose address is < b > address < / b > , whose IP is
* < b > addr < / b > , whose directory port is < b > dir_port < / b > , whose tor version is
* < b > platform < / b > , and whose identity key digest is < b > digest < / b > . The
* < b > platform < / b > argument is optional ; the others are required . */
2004-10-12 17:55:20 +02:00
static void
directory_initiate_command ( const char * address , uint32_t addr ,
uint16_t dir_port , const char * platform ,
const char * digest , uint8_t purpose ,
2004-11-12 17:39:03 +01:00
const char * resource ,
2004-10-14 04:47:09 +02:00
const char * payload , size_t payload_len )
2004-05-13 01:48:57 +02:00
{
2002-09-26 14:09:10 +02:00
connection_t * conn ;
2004-10-17 00:14:52 +02:00
tor_assert ( address ) ;
tor_assert ( addr ) ;
tor_assert ( dir_port ) ;
tor_assert ( digest ) ;
2004-07-20 08:44:16 +02:00
2004-10-08 07:53:59 +02:00
switch ( purpose ) {
2004-05-05 04:50:38 +02:00
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 ;
2004-11-14 22:35:30 +01:00
case DIR_PURPOSE_FETCH_RUNNING_LIST :
log_fn ( LOG_DEBUG , " initiating running-routers fetch " ) ;
break ;
2004-05-05 04:50:38 +02:00
default :
log_fn ( LOG_ERR , " Unrecognized directory connection purpose. " ) ;
tor_assert ( 0 ) ;
2004-10-08 07:53:59 +02:00
}
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 */
2004-10-17 06:06:48 +02:00
conn - > addr = addr ;
conn - > port = dir_port ;
2004-11-28 10:05:49 +01:00
if ( get_options ( ) - > HttpProxy ) {
2004-11-06 06:18:11 +01:00
addr = get_options ( ) - > HttpProxyAddr ;
dir_port = get_options ( ) - > HttpProxyPort ;
2004-10-08 07:53:59 +02:00
}
2004-10-17 06:06:48 +02:00
2004-10-12 17:55:20 +02:00
conn - > address = tor_strdup ( address ) ;
/* conn->nickname = tor_strdup(router->nickname); */
/* tor_assert(router->identity_pkey); */
/* conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); */
/* crypto_pk_get_digest(conn->identity_pkey, conn->identity_digest); */
memcpy ( conn - > identity_digest , digest , DIGEST_LEN ) ;
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-11-28 10:05:49 +01:00
if ( purpose = = DIR_PURPOSE_FETCH_DIR | |
2004-11-28 12:39:53 +01:00
purpose = = DIR_PURPOSE_UPLOAD_DIR | |
purpose = = DIR_PURPOSE_FETCH_RUNNING_LIST ) {
2004-03-31 00:57:49 +02:00
/* then we want to connect directly */
2004-11-28 10:05:49 +01:00
switch ( connection_connect ( conn , conn - > address , addr , dir_port ) ) {
2004-03-31 00:57:49 +02:00
case - 1 :
2005-01-03 21:07:07 +01:00
connection_dir_connect_failed ( conn ) ;
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 */
2004-11-12 17:39:03 +01:00
directory_send_command ( conn , platform , purpose , resource ,
payload , payload_len ) ;
2005-01-12 07:42:32 +01:00
connection_watch_events ( conn , EV_READ | EV_WRITE ) ;
2004-03-31 00:57:49 +02:00
/* 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 ) ;
2004-11-28 10:05:49 +01:00
if ( conn - > s < 0 ) {
2004-04-01 00:02:13 +02:00
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 */
2004-11-12 17:39:03 +01:00
directory_send_command ( conn , platform , purpose , resource ,
payload , payload_len ) ;
2005-01-12 07:42:32 +01:00
connection_watch_events ( conn , EV_READ | EV_WRITE ) ;
2002-09-26 14:09:10 +02:00
}
}
2004-11-12 22:59:27 +01:00
/** Queue an appropriate HTTP command on conn-\>outbuf. The other args
* are as in directory_initiate_command .
2004-05-05 04:50:38 +02:00
*/
2004-10-08 07:53:59 +02:00
static void
2004-10-12 17:55:20 +02:00
directory_send_command ( connection_t * conn , const char * platform ,
2004-11-12 17:39:03 +01:00
int purpose , const char * resource ,
const char * payload , size_t payload_len ) {
2003-09-25 12:42:07 +02:00
char tmp [ 8192 ] ;
2004-10-08 07:53:59 +02:00
char proxystring [ 128 ] ;
char hoststring [ 128 ] ;
char url [ 128 ] ;
2004-09-27 08:00:43 +02:00
int use_newer = 0 ;
2004-10-27 23:14:11 +02:00
const char * httpcommand = NULL ;
2002-09-26 14:09:10 +02:00
2004-10-17 00:14:52 +02:00
tor_assert ( conn ) ;
tor_assert ( conn - > type = = CONN_TYPE_DIR ) ;
2004-09-27 05:39:30 +02:00
2004-10-12 17:55:20 +02:00
/* If we don't know the platform, assume it's up-to-date. */
use_newer = platform ? tor_version_as_new_as ( platform , " 0.0.9pre1 " ) : 1 ;
2004-09-27 08:00:43 +02:00
2004-11-28 10:05:49 +01:00
if ( conn - > port = = 80 ) {
2004-10-12 17:55:20 +02:00
strlcpy ( hoststring , conn - > address , sizeof ( hoststring ) ) ;
2004-10-08 07:53:59 +02:00
} else {
2004-10-27 08:37:34 +02:00
tor_snprintf ( hoststring , sizeof ( hoststring ) , " %s:%d " , conn - > address , conn - > port ) ;
2004-10-08 07:53:59 +02:00
}
2004-11-28 10:05:49 +01:00
if ( get_options ( ) - > HttpProxy ) {
2004-10-27 08:37:34 +02:00
tor_snprintf ( proxystring , sizeof ( proxystring ) , " http://%s " , hoststring ) ;
2004-10-08 07:53:59 +02:00
} else {
proxystring [ 0 ] = 0 ;
}
2004-11-28 10:05:49 +01:00
switch ( purpose ) {
2004-03-31 00:57:49 +02:00
case DIR_PURPOSE_FETCH_DIR :
2004-11-12 17:39:03 +01:00
tor_assert ( ! resource ) ;
tor_assert ( ! payload ) ;
2004-10-08 07:53:59 +02:00
log_fn ( LOG_DEBUG , " Asking for %scompressed directory from server running %s " ,
2004-10-12 17:55:20 +02:00
use_newer ? " " : " un " , platform ? platform : " <unknown version> " ) ;
2004-10-08 07:53:59 +02:00
httpcommand = " GET " ;
strlcpy ( url , use_newer ? " /tor/dir.z " : " / " , sizeof ( url ) ) ;
2004-06-16 23:08:29 +02:00
break ;
case DIR_PURPOSE_FETCH_RUNNING_LIST :
2004-11-12 17:39:03 +01:00
tor_assert ( ! resource ) ;
tor_assert ( ! payload ) ;
2004-10-08 07:53:59 +02:00
httpcommand = " GET " ;
strlcpy ( url , use_newer ? " /tor/running-routers " : " /running-routers " , sizeof ( url ) ) ;
2003-09-17 22:09:06 +02:00
break ;
2004-03-31 00:57:49 +02:00
case DIR_PURPOSE_UPLOAD_DIR :
2004-11-12 17:39:03 +01:00
tor_assert ( ! resource ) ;
2004-04-25 22:37:37 +02:00
tor_assert ( payload ) ;
2004-10-08 07:53:59 +02:00
httpcommand = " POST " ;
strlcpy ( url , use_newer ? " /tor/ " : " / " , sizeof ( url ) ) ;
2004-03-31 00:57:49 +02:00
break ;
2004-04-01 23:32:01 +02:00
case DIR_PURPOSE_FETCH_RENDDESC :
2004-11-12 17:39:03 +01:00
tor_assert ( resource ) ;
tor_assert ( ! payload ) ;
2004-04-01 23:32:01 +02:00
/* this must be true or we wouldn't be doing the lookup */
2004-11-12 22:59:27 +01:00
tor_assert ( strlen ( resource ) < = REND_SERVICE_ID_LEN ) ;
2004-05-05 04:50:38 +02:00
/* This breaks the function abstraction. */
2004-11-12 17:39:03 +01:00
strlcpy ( conn - > rend_query , resource , sizeof ( conn - > rend_query ) ) ;
2004-04-01 23:32:01 +02:00
2004-10-08 07:53:59 +02:00
httpcommand = " GET " ;
2004-11-12 17:39:03 +01:00
tor_snprintf ( url , sizeof ( url ) , " %s/rendezvous/%s " , use_newer ? " /tor " : " " , resource ) ;
2004-10-12 08:03:10 +02:00
2004-03-31 00:57:49 +02:00
break ;
2004-04-01 23:32:01 +02:00
case DIR_PURPOSE_UPLOAD_RENDDESC :
2004-11-12 17:39:03 +01:00
tor_assert ( ! resource ) ;
2004-04-25 22:37:37 +02:00
tor_assert ( payload ) ;
2004-10-08 07:53:59 +02:00
httpcommand = " POST " ;
2004-10-27 08:37:34 +02:00
tor_snprintf ( url , sizeof ( url ) , " %s/rendezvous/publish " , use_newer ? " /tor " : " " ) ;
2003-09-17 22:09:06 +02:00
break ;
2002-09-26 14:09:10 +02:00
}
2004-10-08 07:53:59 +02:00
2004-10-27 08:37:34 +02:00
tor_snprintf ( tmp , sizeof ( tmp ) , " %s %s%s HTTP/1.0 \r \n Content-Length: %lu \r \n Host: %s \r \n \r \n " ,
2004-10-08 07:53:59 +02:00
httpcommand ,
proxystring ,
url ,
2004-11-12 22:59:27 +01:00
payload ? ( unsigned long ) payload_len : 0 ,
2004-10-08 07:53:59 +02:00
hoststring ) ;
connection_write_to_buf ( tmp , strlen ( tmp ) , conn ) ;
2004-11-28 10:05:49 +01:00
if ( payload ) {
2004-10-08 07:53:59 +02:00
/* then send the payload afterwards too */
connection_write_to_buf ( payload , payload_len , conn ) ;
}
2004-03-31 00:57:49 +02:00
}
2004-10-08 07:53:59 +02:00
/** Parse an HTTP request string <b>headers</b> of the form
* " \ %s [http[s]://] \ %s HTTP/1... "
2004-09-27 05:39:30 +02:00
* If it ' s well - formed , strdup the second \ % s into * < b > url < / b > , and
* null - terminate it . If the url doesn ' t start with " /tor/ " , rewrite it
* so it does . Return 0.
2004-03-31 00:57:49 +02:00
* Otherwise , return - 1.
*/
2004-05-13 01:48:57 +02:00
static int
parse_http_url ( char * headers , char * * url )
{
2004-10-08 07:53:59 +02:00
char * s , * start , * tmp ;
2004-03-31 00:57:49 +02:00
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 ;
2004-09-27 05:39:30 +02:00
start = s ; /* this is it, assuming it's valid */
2004-09-27 08:45:32 +02:00
s = ( char * ) find_whitespace ( start ) ;
2004-03-31 00:57:49 +02:00
if ( ! * s ) return - 1 ;
2004-10-08 07:53:59 +02:00
/* tolerate the http[s] proxy style of putting the hostname in the url */
2004-11-28 10:05:49 +01:00
if ( s - start > = 4 & & ! strcmpstart ( start , " http " ) ) {
2004-10-08 07:53:59 +02:00
tmp = start + 4 ;
2004-11-28 10:05:49 +01:00
if ( * tmp = = ' s ' )
2004-10-08 07:53:59 +02:00
tmp + + ;
2004-11-28 10:05:49 +01:00
if ( s - tmp > = 3 & & ! strcmpstart ( tmp , " :// " ) ) {
2004-10-08 07:53:59 +02:00
tmp = strchr ( tmp + 3 , ' / ' ) ;
2004-11-28 10:05:49 +01:00
if ( tmp & & tmp < s ) {
2004-10-08 07:53:59 +02:00
log_fn ( LOG_DEBUG , " Skipping over 'http[s]://hostname' string " ) ;
start = tmp ;
}
}
}
2004-11-28 10:05:49 +01:00
if ( s - start < 5 | | strcmpstart ( start , " /tor/ " ) ) { /* need to rewrite it */
2004-09-27 05:39:30 +02:00
* url = tor_malloc ( s - start + 5 ) ;
2004-10-27 08:48:16 +02:00
strlcpy ( * url , " /tor " , s - start + 5 ) ;
strlcat ( ( * url ) + 4 , start , s - start + 1 ) ;
2004-09-27 05:39:30 +02:00
} else {
* url = tor_strndup ( start , s - start ) ;
}
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 ... " .
2004-11-12 17:39:03 +01:00
* If it ' s well - formed , assign * < b > code < / b > , point and return 0.
2004-08-15 22:30:15 +02:00
* If < b > date < / b > is provided , set * date to the Date header in the
2005-01-19 23:40:33 +01:00
* http headers , or 0 if no such header is found . If < b > compression < / b >
* is provided , set * < b > compression < / b > to the compression method given
* in the Content - Encoding header , or 0 if no such header is found , or - 1
* if the value of the header is not recognized .
2004-03-12 13:43:13 +01:00
* Otherwise , return - 1.
*/
2004-05-13 01:48:57 +02:00
static int
2004-11-12 17:39:03 +01:00
parse_http_response ( const char * headers , int * code , time_t * date ,
2004-09-08 08:52:33 +02:00
int * compression )
2004-05-13 01:48:57 +02:00
{
2004-03-12 13:43:13 +01:00
int n1 , n2 ;
2004-08-15 22:30:15 +02:00
char datestr [ RFC1123_TIME_LEN + 1 ] ;
2004-09-08 08:52:33 +02:00
smartlist_t * parsed_headers ;
2004-10-17 00:14:52 +02:00
tor_assert ( headers ) ;
tor_assert ( code ) ;
2004-03-12 13:43:13 +01:00
2004-12-08 01:42:50 +01:00
while ( TOR_ISSPACE ( * headers ) ) headers + + ; /* tolerate leading whitespace */
2004-03-12 13:43:13 +01:00
2004-11-28 10:05:49 +01:00
if ( sscanf ( headers , " HTTP/1.%d %d " , & n1 , & n2 ) < 2 | |
2004-11-28 12:39:53 +01:00
( n1 ! = 0 & & n1 ! = 1 ) | |
( n2 < 100 | | n2 > = 600 ) ) {
2004-03-12 13:43:13 +01:00
log_fn ( LOG_WARN , " Failed to parse header '%s' " , headers ) ;
return - 1 ;
}
* code = n2 ;
2004-11-12 17:39:03 +01:00
2004-09-08 08:52:33 +02:00
parsed_headers = smartlist_create ( ) ;
smartlist_split_string ( parsed_headers , headers , " \n " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , - 1 ) ;
2004-08-15 22:30:15 +02:00
if ( date ) {
* date = 0 ;
2004-09-08 08:52:33 +02:00
SMARTLIST_FOREACH ( parsed_headers , const char * , s ,
if ( ! strcmpstart ( s , " Date: " ) ) {
strlcpy ( datestr , s + 6 , sizeof ( datestr ) ) ;
2004-08-15 22:30:15 +02:00
/* This will do nothing on failure, so we don't need to check
the result . We shouldn ' t warn , since there are many other valid
date formats besides the one we use . */
parse_rfc1123_time ( datestr , date ) ;
break ;
2004-09-08 08:52:33 +02:00
} ) ;
}
if ( compression ) {
const char * enc = NULL ;
SMARTLIST_FOREACH ( parsed_headers , const char * , s ,
if ( ! strcmpstart ( s , " Content-Encoding: " ) ) {
2004-10-01 06:45:14 +02:00
enc = s + 18 ; break ;
2004-09-08 08:52:33 +02:00
} ) ;
2004-10-01 06:45:14 +02:00
if ( ! enc | | ! strcmp ( enc , " identity " ) ) {
2004-09-08 08:52:33 +02:00
* compression = 0 ;
} else if ( ! strcmp ( enc , " deflate " ) | | ! strcmp ( enc , " x-deflate " ) ) {
* compression = ZLIB_METHOD ;
} else if ( ! strcmp ( enc , " gzip " ) | | ! strcmp ( enc , " x-gzip " ) ) {
* compression = GZIP_METHOD ;
} else {
2005-01-19 23:40:33 +01:00
log_fn ( LOG_INFO , " Unrecognized content encoding: '%s' " , enc ) ;
* compression = - 1 ;
2004-08-15 22:30:15 +02:00
}
}
2004-09-08 08:52:33 +02:00
SMARTLIST_FOREACH ( parsed_headers , char * , s , tor_free ( s ) ) ;
smartlist_free ( parsed_headers ) ;
2004-08-15 22:30:15 +02:00
2004-03-12 13:43:13 +01:00
return 0 ;
}
2005-01-19 23:40:33 +01:00
/** Return true iff <b>body</b> doesn't start with a plausible router or
* running - list or directory opening . This is a sign of possible compression .
* */
static int
2005-01-19 23:47:48 +01:00
body_is_plausible ( const char * body , size_t len , int purpose )
2005-01-19 23:40:33 +01:00
{
int i ;
if ( len < 32 )
return 0 ;
2005-01-19 23:47:48 +01:00
if ( purpose ! = DIR_PURPOSE_FETCH_RENDDESC ) {
if ( ! strcmpstart ( body , " router " ) | |
! strcmpstart ( body , " signed-directory " ) | |
! strcmpstart ( body , " network-status " ) | |
! strcmpstart ( body , " running-routers " ) )
return 1 ;
for ( i = 0 ; i < 32 ; + + i ) {
if ( ! isprint ( body [ i ] ) & & ! isspace ( body [ i ] ) )
return 0 ;
}
return 1 ;
} else {
2005-01-19 23:40:33 +01:00
return 1 ;
}
}
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-10-14 04:47:09 +02:00
size_t body_len = 0 ;
2004-03-12 13:43:13 +01:00
int status_code ;
2004-08-15 22:30:15 +02:00
time_t now , date_header = 0 ;
int delta ;
2004-09-08 08:52:33 +02:00
int compression ;
2005-01-19 23:40:33 +01:00
int plausible ;
2002-09-26 14:09:10 +02:00
2004-11-28 10:05:49 +01:00
switch ( fetch_from_buf_http ( conn - > inbuf ,
2004-11-28 12:39:53 +01:00
& headers , MAX_HEADERS_SIZE ,
& body , & body_len , MAX_DIR_SIZE ) ) {
2004-07-20 04:44:26 +02:00
case - 1 : /* overflow */
2005-01-01 08:40:59 +01:00
log_fn ( LOG_WARN , " 'fetch' response too large (server '%s'). Failing. " , conn - > address ) ;
2004-07-20 04:44:26 +02:00
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-11-28 10:05:49 +01:00
if ( parse_http_response ( headers , & status_code , & date_header ,
2004-11-28 12:39:53 +01:00
& compression ) < 0 ) {
2005-01-01 08:40:59 +01:00
log_fn ( LOG_WARN , " Unparseable headers (server '%s'). Closing. " , conn - > address ) ;
2004-09-29 08:52:36 +02:00
tor_free ( body ) ; tor_free ( headers ) ;
2004-07-20 04:44:26 +02:00
return - 1 ;
}
2004-08-15 22:30:15 +02:00
if ( date_header > 0 ) {
now = time ( NULL ) ;
delta = now - date_header ;
if ( abs ( delta ) > ALLOW_DIRECTORY_TIME_SKEW ) {
2005-01-21 01:45:18 +01:00
routerinfo_t * router = router_get_by_digest ( conn - > identity_digest ) ;
log_fn ( ( router & & router - > is_verified ) ? LOG_WARN : LOG_INFO ,
" Received directory with skewed time (server '%s'): we are %d minutes %s, or the directory is %d minutes %s. " ,
2005-01-01 08:40:59 +01:00
conn - > address ,
2004-08-15 22:30:15 +02:00
abs ( delta ) / 60 , delta > 0 ? " ahead " : " behind " ,
abs ( delta ) / 60 , delta > 0 ? " behind " : " ahead " ) ;
} else {
log_fn ( LOG_INFO , " Time on received directory is within tolerance; we are %d seconds skewed. (That's okay.) " , delta ) ;
}
}
2004-07-20 04:44:26 +02:00
2005-01-19 23:47:48 +01:00
plausible = body_is_plausible ( body , body_len , conn - > purpose ) ;
2005-01-19 23:40:33 +01:00
if ( compression | | ! plausible ) {
char * new_body = NULL ;
size_t new_len = 0 ;
int guessed = detect_compression_method ( body , body_len ) ;
if ( compression < = 0 | | guessed ! = compression ) {
/* Tell the user if we don't believe what we're told about compression.*/
const char * description1 , * description2 ;
if ( compression = = ZLIB_METHOD )
description1 = " as deflated " ;
2005-01-19 23:47:48 +01:00
else if ( compression = = GZIP_METHOD )
2005-01-19 23:40:33 +01:00
description1 = " as gzipped " ;
else if ( compression = = 0 )
description1 = " as uncompressed " ;
else
description1 = " with an unknown Content-Encoding " ;
if ( guessed = = ZLIB_METHOD )
description2 = " deflated " ;
else if ( guessed = = GZIP_METHOD )
description2 = " gzipped " ;
else if ( ! plausible )
description2 = " confusing binary junk " ;
else
description2 = " uncompressed " ;
log_fn ( LOG_INFO , " HTTP body from server '%s' was labeled %s, "
" but it seems to be %s.%s " ,
conn - > address , description1 , description2 ,
( compression > 0 & & guessed > 0 ) ? " Trying both. " : " " ) ;
}
/* Try declared compression first if we can. */
if ( compression > 0 )
tor_gzip_uncompress ( & new_body , & new_len , body , body_len , compression ) ;
/* Okay, if that didn't work, and we think that it was compressed
* differently , try that . */
if ( ! new_body & & guessed > 0 & & compression ! = guessed )
tor_gzip_uncompress ( & new_body , & new_len , body , body_len , guessed ) ;
/* If we're pretty sure that we have a compressed directory, and
* we didn ' t manage to uncompress it , then warn and bail . */
if ( ! plausible & & ! new_body ) {
2005-01-01 08:40:59 +01:00
log_fn ( LOG_WARN , " Unable to decompress HTTP body (server '%s'). " , conn - > address ) ;
2004-09-08 08:52:33 +02:00
tor_free ( body ) ; tor_free ( headers ) ;
return - 1 ;
}
2005-01-19 23:40:33 +01:00
if ( new_body ) {
tor_free ( body ) ;
body = new_body ;
body_len = new_len ;
}
2004-09-08 08:52:33 +02:00
}
2004-11-28 10:05:49 +01:00
if ( conn - > purpose = = DIR_PURPOSE_FETCH_DIR ) {
2004-07-20 04:44:26 +02:00
/* fetch/process the directory to learn about new routers. */
2005-01-01 08:40:59 +01:00
log_fn ( LOG_INFO , " Received directory (size %d) from server '%s' " ,
( int ) body_len , conn - > address ) ;
2004-11-28 10:05:49 +01:00
if ( status_code = = 503 | | body_len = = 0 ) {
2004-07-20 04:44:26 +02:00
log_fn ( LOG_INFO , " Empty directory. Ignoring. " ) ;
2004-09-29 08:52:36 +02:00
tor_free ( body ) ; tor_free ( headers ) ;
2004-07-20 04:44:26 +02:00
return 0 ;
}
2004-11-28 10:05:49 +01:00
if ( status_code ! = 200 ) {
2005-01-01 08:40:59 +01:00
log_fn ( LOG_WARN , " Received http status code %d from server '%s'. Failing. " ,
status_code , conn - > address ) ;
2004-09-29 08:52:36 +02:00
tor_free ( body ) ; tor_free ( headers ) ;
2004-03-12 13:43:13 +01:00
return - 1 ;
}
2005-01-06 21:11:52 +01:00
if ( router_load_routerlist_from_directory ( body , NULL , 1 , 0 ) < 0 ) {
2004-09-29 08:52:36 +02:00
log_fn ( LOG_WARN , " I failed to parse the directory I fetched from %s:%d. Ignoring. " , conn - > address , conn - > port ) ;
2004-07-20 04:44:26 +02:00
} else {
log_fn ( LOG_INFO , " updated routers. " ) ;
2004-03-12 13:43:13 +01:00
}
2004-10-28 20:37:52 +02:00
directory_has_arrived ( time ( NULL ) ) ; /* do things we've been waiting to do */
2004-07-20 04:44:26 +02:00
}
2004-03-12 13:43:13 +01:00
2004-11-28 10:05:49 +01:00
if ( conn - > purpose = = DIR_PURPOSE_FETCH_RUNNING_LIST ) {
2004-07-20 04:44:26 +02:00
running_routers_t * rrs ;
routerlist_t * rl ;
/* just update our list of running routers, if this list is new info */
2004-12-05 08:10:08 +01:00
log_fn ( LOG_INFO , " Received running-routers list (size %d) " , ( int ) body_len ) ;
2004-11-28 10:05:49 +01:00
if ( status_code ! = 200 ) {
2005-01-01 08:40:59 +01:00
log_fn ( LOG_WARN , " Received http status code %d from server '%s'. Failing. " ,
status_code , conn - > address ) ;
2004-09-29 08:52:36 +02:00
tor_free ( body ) ; tor_free ( headers ) ;
2004-03-12 13:43:13 +01:00
return - 1 ;
}
2005-01-06 21:11:52 +01:00
if ( ! ( rrs = router_parse_runningrouters ( body , 1 ) ) ) {
2005-01-01 08:40:59 +01:00
log_fn ( LOG_WARN , " Can't parse runningrouters list (server '%s') " , conn - > address ) ;
2004-09-29 08:52:36 +02:00
tor_free ( body ) ; tor_free ( headers ) ;
2004-07-20 04:44:26 +02:00
return - 1 ;
2004-03-12 13:43:13 +01:00
}
2004-07-20 04:44:26 +02:00
router_get_routerlist ( & rl ) ;
2004-10-14 05:44:45 +02:00
if ( rl )
routerlist_update_from_runningrouters ( rl , rrs ) ;
2004-07-20 04:44:26 +02:00
running_routers_free ( rrs ) ;
}
2004-03-12 13:43:13 +01:00
2004-11-28 10:05:49 +01:00
if ( conn - > purpose = = DIR_PURPOSE_UPLOAD_DIR ) {
switch ( status_code ) {
2004-07-20 04:44:26 +02:00
case 200 :
log_fn ( LOG_INFO , " eof (status 200) after uploading server descriptor: finished. " ) ;
break ;
case 400 :
2005-01-01 08:40:59 +01:00
log_fn ( LOG_WARN , " http status 400 (bad request) response from dirserver '%s'. Malformed server descriptor? " , conn - > address ) ;
2004-07-20 04:44:26 +02:00
break ;
case 403 :
2005-01-06 22:03:27 +01:00
log_fn ( LOG_WARN , " http status 403 (unapproved server) response from dirserver '%s'. Is your clock skewed? Have you mailed us your key fingerprint? Are you using the right key? Are you using a private IP address? See http://tor.eff.org/doc/tor-doc.html#server. " , conn - > address ) ;
2004-07-20 04:44:26 +02:00
break ;
default :
2005-01-01 08:40:59 +01:00
log_fn ( LOG_WARN , " http status %d response unrecognized (server '%s'). " , status_code , conn - > address ) ;
2004-07-20 04:44:26 +02:00
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-11-28 10:05:49 +01:00
if ( conn - > purpose = = DIR_PURPOSE_FETCH_RENDDESC ) {
2004-07-20 04:44:26 +02:00
log_fn ( LOG_INFO , " Received rendezvous descriptor (size %d, status code %d) " ,
2004-10-14 05:18:14 +02:00
( int ) body_len , status_code ) ;
2004-11-28 10:05:49 +01:00
switch ( status_code ) {
2004-07-20 04:44:26 +02:00
case 200 :
2004-11-28 10:05:49 +01:00
if ( rend_cache_store ( body , body_len ) < 0 ) {
2004-07-20 04:44:26 +02:00
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. */
conn - > purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC ;
2005-01-20 00:15:59 +01:00
rend_client_desc_here ( conn - > rend_query ) ;
2004-07-20 04:44:26 +02:00
}
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-11-28 10:05:49 +01:00
if ( conn - > purpose = = DIR_PURPOSE_UPLOAD_RENDDESC ) {
switch ( status_code ) {
2004-07-20 04:44:26 +02:00
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
}
2004-09-29 08:52:36 +02:00
tor_free ( body ) ; tor_free ( headers ) ;
2004-07-20 04:44:26 +02:00
return 0 ;
}
2004-11-21 11:14:57 +01:00
int connection_dir_reached_eof ( connection_t * conn ) {
int retval ;
2004-11-28 10:05:49 +01:00
if ( conn - > state ! = DIR_CONN_STATE_CLIENT_READING ) {
2004-11-21 11:14:57 +01:00
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 ;
}
retval = connection_dir_client_reached_eof ( conn ) ;
connection_mark_for_close ( conn ) ;
return retval ;
}
2004-07-20 04:44:26 +02:00
/** 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 ) {
2004-03-31 00:57:49 +02:00
2004-10-17 00:14:52 +02:00
tor_assert ( conn ) ;
tor_assert ( conn - > type = = CONN_TYPE_DIR ) ;
2004-07-20 04:44:26 +02:00
/* 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 ) .
*/
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-11-28 10:05:49 +01:00
if ( conn - > state = = DIR_CONN_STATE_SERVER_COMMAND_WAIT ) {
2004-03-31 00:57:49 +02:00
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 ;
}
2005-01-20 21:07:36 +01:00
/** Create an http response for the client <b>conn</b> out of
* < b > status < / b > and < b > reason_phrase < / b > . Write it to < b > conn < / b > .
*/
2005-01-20 20:46:02 +01:00
static void
write_http_status_line ( connection_t * conn , int status ,
const char * reason_phrase )
{
2005-01-20 21:07:36 +01:00
char buf [ 256 ] ;
if ( tor_snprintf ( buf , sizeof ( buf ) , " HTTP/1.0 %d %s \r \n \r \n " ,
status , reason_phrase ) < 0 ) {
log_fn ( LOG_WARN , " Bug: status line too long. " ) ;
2005-01-20 20:46:02 +01:00
return ;
2005-01-20 21:07:36 +01:00
}
2005-01-20 20:46:02 +01:00
connection_write_to_buf ( buf , strlen ( buf ) , conn ) ;
}
2004-03-12 13:43:13 +01:00
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-09-27 05:39:30 +02:00
* conn - \ > outbuf . If the request is unrecognized , send a 400.
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 ,
2004-10-14 04:47:09 +02:00
char * body , size_t body_len )
2004-05-13 01:48:57 +02:00
{
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 ] ;
2004-08-04 03:15:57 +02:00
char date [ RFC1123_TIME_LEN + 1 ] ;
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 ) {
2005-01-20 20:46:02 +01:00
write_http_status_line ( conn , 400 , " Bad request " ) ;
2004-03-12 13:43:13 +01:00
return 0 ;
2003-12-17 10:42:28 +01:00
}
2004-09-27 05:50:58 +02:00
log_fn ( LOG_INFO , " rewritten url as '%s'. " , url ) ;
2003-12-17 10:42:28 +01:00
2004-11-28 10:05:49 +01:00
if ( ! strcmp ( url , " /tor/ " ) | | ! strcmp ( url , " /tor/dir.z " ) ) { /* directory fetch */
2004-09-27 05:39:30 +02:00
int deflated = ! strcmp ( url , " /tor/dir.z " ) ;
2004-09-21 20:09:38 +02:00
dlen = dirserv_get_directory ( & cp , deflated ) ;
2004-03-31 00:57:49 +02:00
2004-09-27 05:39:30 +02:00
tor_free ( url ) ;
2004-11-28 10:05:49 +01:00
if ( dlen = = 0 ) {
2004-12-13 01:44:39 +01:00
log_fn ( LOG_NOTICE , " My directory is empty. Closing. " ) ;
2005-01-20 20:46:02 +01:00
write_http_status_line ( conn , 503 , " Directory unavailable " ) ;
2004-03-31 00:57:49 +02:00
return 0 ;
}
2004-11-09 21:04:00 +01:00
log_fn ( LOG_DEBUG , " Dumping %sdirectory to client. " ,
2004-09-21 20:09:38 +02:00
deflated ? " deflated " : " " ) ;
2004-08-15 22:30:15 +02:00
format_rfc1123_time ( date , time ( NULL ) ) ;
2004-10-27 08:37:34 +02:00
tor_snprintf ( tmp , sizeof ( tmp ) , " HTTP/1.0 200 OK \r \n Date: %s \r \n Content-Length: %d \r \n Content-Type: text/plain \r \n Content-Encoding: %s \r \n \r \n " ,
2004-08-15 22:30:15 +02:00
date ,
2004-09-08 08:52:33 +02:00
( int ) dlen ,
2004-09-21 20:09:38 +02:00
deflated ? " deflate " : " identity " ) ;
2004-03-31 07:01:30 +02:00
connection_write_to_buf ( tmp , strlen ( tmp ) , conn ) ;
2004-09-29 07:48:25 +02:00
connection_write_to_buf ( cp , dlen , conn ) ;
2004-03-31 00:57:49 +02:00
return 0 ;
}
2004-11-28 10:05:49 +01:00
if ( ! strcmp ( url , " /tor/running-routers " ) | |
2004-11-28 12:39:53 +01:00
! strcmp ( url , " /tor/running-routers.z " ) ) { /* running-routers fetch */
2004-11-15 05:04:20 +01:00
int deflated = ! strcmp ( url , " /tor/dir.z " ) ;
2004-09-27 05:39:30 +02:00
tor_free ( url ) ;
2004-11-15 05:04:20 +01:00
dlen = dirserv_get_runningrouters ( & cp , deflated ) ;
2004-11-28 10:05:49 +01:00
if ( ! dlen ) { /* we failed to create/cache cp */
2005-01-20 20:46:02 +01:00
write_http_status_line ( conn , 503 , " Directory unavailable " ) ;
2004-06-30 23:48:02 +02:00
return 0 ;
}
2004-06-16 23:08:29 +02:00
2004-08-07 04:46:16 +02:00
format_rfc1123_time ( date , time ( NULL ) ) ;
2004-11-15 05:04:20 +01:00
tor_snprintf ( tmp , sizeof ( tmp ) , " HTTP/1.0 200 OK \r \n Date: %s \r \n Content-Length: %d \r \n Content-Type: text/plain \r \n Content-Encoding: %s \r \n \r \n " ,
date ,
( int ) dlen ,
deflated ? " deflate " : " identity " ) ;
2004-06-16 23:08:29 +02:00
connection_write_to_buf ( tmp , strlen ( tmp ) , conn ) ;
connection_write_to_buf ( cp , strlen ( cp ) , conn ) ;
return 0 ;
}
2004-11-28 10:05:49 +01:00
if ( ! strcmpstart ( url , " /tor/rendezvous/ " ) ) {
2004-03-31 07:01:30 +02:00
/* rendezvous descriptor fetch */
2004-03-31 05:42:56 +02:00
const char * descp ;
2004-10-14 04:47:09 +02:00
size_t desc_len ;
2004-03-31 00:57:49 +02:00
2004-11-28 10:05:49 +01:00
if ( ! authdir_mode ( get_options ( ) ) ) {
2004-07-21 10:40:57 +02:00
/* 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 . */
2005-01-20 21:07:36 +01:00
write_http_status_line ( conn , 400 , " Nonauthoritative directory does not not store rendezvous descriptors. " ) ;
2004-09-27 05:39:30 +02:00
tor_free ( url ) ;
2004-07-21 10:40:57 +02:00
return 0 ;
}
2004-11-28 10:05:49 +01:00
switch ( rend_cache_lookup_desc ( url + strlen ( " /tor/rendezvous/ " ) , & descp , & desc_len ) ) {
2004-03-31 01:41:24 +02:00
case 1 : /* valid */
2004-08-07 04:46:16 +02:00
format_rfc1123_time ( date , time ( NULL ) ) ;
2004-10-27 08:37:34 +02:00
tor_snprintf ( tmp , sizeof ( tmp ) , " HTTP/1.0 200 OK \r \n Date: %s \r \n Content-Length: %d \r \n Content-Type: application/octet-stream \r \n \r \n " ,
2004-08-04 03:15:57 +02:00
date ,
2004-10-14 04:47:09 +02:00
( int ) desc_len ) ; /* can't include descp here, because it's got nuls */
2004-03-31 07:01:30 +02:00
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 */
2005-01-20 20:46:02 +01:00
write_http_status_line ( conn , 404 , " Not found " ) ;
2004-03-31 01:41:24 +02:00
break ;
case - 1 : /* not well-formed */
2005-01-20 20:46:02 +01:00
write_http_status_line ( conn , 400 , " Bad request " ) ;
2004-03-31 01:41:24 +02:00
break ;
}
2004-09-27 05:39:30 +02:00
tor_free ( url ) ;
2004-03-31 01:41:24 +02:00
return 0 ;
2004-03-31 00:57:49 +02:00
}
/* we didn't recognize the url */
2005-01-20 20:46:02 +01:00
write_http_status_line ( conn , 404 , " Not found " ) ;
2004-09-27 05:39:30 +02:00
tor_free ( url ) ;
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-09-27 05:39:30 +02:00
* 400. Always return 0. */
2004-05-13 01:48:57 +02:00
static int
directory_handle_command_post ( connection_t * conn , char * headers ,
2004-10-14 04:47:09 +02:00
char * body , size_t body_len )
2004-05-13 01:48:57 +02:00
{
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-11-28 10:05:49 +01:00
if ( ! authdir_mode ( get_options ( ) ) ) {
2004-07-21 10:40:57 +02:00
/* we just provide cached directories; we don't want to
* receive anything . */
2005-01-20 21:07:36 +01:00
write_http_status_line ( conn , 400 , " Nonauthoritative directory does not not store server descriptors " ) ;
2004-07-21 10:40:57 +02:00
return 0 ;
}
2004-03-31 00:57:49 +02:00
if ( parse_http_url ( headers , & url ) < 0 ) {
2005-01-20 20:46:02 +01:00
write_http_status_line ( conn , 400 , " Bad request " ) ;
2004-03-31 00:57:49 +02:00
return 0 ;
}
2004-09-27 05:50:58 +02:00
log_fn ( LOG_INFO , " rewritten url as '%s'. " , url ) ;
2004-03-31 00:57:49 +02:00
2004-11-28 10:05:49 +01:00
if ( ! strcmp ( url , " /tor/ " ) ) { /* server descriptor post */
2005-01-20 21:18:32 +01:00
const char * msg ;
2004-03-31 00:57:49 +02:00
cp = body ;
2005-01-20 21:18:32 +01:00
switch ( dirserv_add_descriptor ( & cp , & msg ) ) {
2004-03-31 00:57:49 +02:00
case - 1 :
/* malformed descriptor, or something wrong */
2005-01-20 21:18:32 +01:00
write_http_status_line ( conn , 400 , msg ? msg : " Malformed or unacceptable server descriptor " ) ;
2004-03-31 00:57:49 +02:00
break ;
case 0 :
/* descriptor was well-formed but server has not been approved */
2005-01-20 21:18:32 +01:00
write_http_status_line ( conn , 200 , msg ? msg : " Unverified server descriptor accepted " ) ;
2004-03-31 00:57:49 +02:00
break ;
case 1 :
2004-09-02 20:57:09 +02:00
dirserv_get_directory ( & cp , 0 ) ; /* rebuild and write to disk */
2005-01-20 21:18:32 +01:00
write_http_status_line ( conn , 200 , msg ? msg : " Verified server descriptor accepted " ) ;
2004-03-31 00:57:49 +02:00
break ;
}
2004-09-27 05:39:30 +02:00
tor_free ( url ) ;
2004-03-31 07:01:30 +02:00
return 0 ;
2004-03-31 00:57:49 +02:00
}
2004-11-28 10:05:49 +01:00
if ( ! strcmpstart ( url , " /tor/rendezvous/publish " ) ) {
2004-03-31 07:01:30 +02:00
/* rendezvous descriptor post */
2004-11-28 10:05:49 +01:00
if ( rend_cache_store ( body , body_len ) < 0 )
2005-01-20 20:46:02 +01:00
write_http_status_line ( conn , 400 , " Invalid service descriptor rejected " ) ;
2004-03-31 01:41:24 +02:00
else
2005-01-20 20:46:02 +01:00
write_http_status_line ( conn , 200 , " Service descriptor stored " ) ;
2004-09-27 05:39:30 +02:00
tor_free ( url ) ;
2004-03-31 07:01:30 +02:00
return 0 ;
2004-03-31 00:57:49 +02:00
}
/* we didn't recognize the url */
2005-01-20 20:46:02 +01:00
write_http_status_line ( conn , 404 , " Not found " ) ;
2004-09-27 05:39:30 +02:00
tor_free ( url ) ;
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-10-14 04:47:09 +02:00
size_t body_len = 0 ;
2003-12-17 10:42:28 +01:00
int r ;
2004-10-17 00:14:52 +02:00
tor_assert ( conn ) ;
tor_assert ( conn - > type = = CONN_TYPE_DIR ) ;
2002-09-26 14:09:10 +02:00
2004-11-28 10:05:49 +01:00
switch ( fetch_from_buf_http ( conn - > inbuf ,
2004-11-28 12:39:53 +01:00
& headers , MAX_HEADERS_SIZE ,
& body , & body_len , MAX_BODY_SIZE ) ) {
2003-09-17 22:09:06 +02:00
case - 1 : /* overflow */
2004-10-14 06:50:33 +02:00
log_fn ( LOG_WARN , " Invalid input. Closing. " ) ;
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
2004-11-28 10:05:49 +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-10-17 00:14:52 +02:00
tor_assert ( conn ) ;
tor_assert ( conn - > type = = CONN_TYPE_DIR ) ;
2002-09-26 14:09:10 +02:00
2004-11-28 10:05:49 +01: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-12-13 01:44:39 +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 )
{
2004-10-17 00:14:52 +02:00
tor_assert ( conn ) ;
tor_assert ( conn - > type = = CONN_TYPE_DIR ) ;
2004-05-12 21:17:09 +02:00
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 ;
}