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"
2003-09-17 22:09:06 +02:00
static int directory_send_command ( connection_t * conn , int command ) ;
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-02-29 02:31:33 +01:00
extern int has_fetched_directory ;
2002-09-26 14:09:10 +02:00
2004-03-12 13:43:13 +01:00
# define MAX_HEADERS_SIZE 2048
# define MAX_BODY_SIZE 500000
2002-09-26 14:09:10 +02:00
/********* END VARIABLES ************/
2003-09-17 22:09:06 +02:00
void directory_initiate_command ( routerinfo_t * router , int command ) {
2002-09-26 14:09:10 +02:00
connection_t * conn ;
2004-03-30 00:18:05 +02:00
switch ( command ) {
case DIR_CONN_STATE_CONNECTING_FETCH :
log_fn ( LOG_DEBUG , " initiating directory fetch " ) ;
break ;
case DIR_CONN_STATE_CONNECTING_UPLOAD :
log_fn ( LOG_DEBUG , " initiating directory upload " ) ;
break ;
}
2002-09-28 07:53:00 +02:00
2003-11-10 09:06:55 +01:00
if ( ! router ) { /* i guess they didn't have one in mind for me to use */
log_fn ( LOG_WARN , " No running dirservers known. Not trying. " ) ;
return ;
}
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 ) ;
2003-11-10 09:06:55 +01:00
assert ( router - > identity_pkey ) ;
conn - > identity_pkey = crypto_pk_dup_key ( router - > identity_pkey ) ;
2002-09-26 14:09:10 +02:00
2004-03-26 23:07:45 +01:00
conn - > state = command ;
2003-09-16 03:58:46 +02:00
if ( connection_add ( conn ) < 0 ) { /* no space, forget it */
2002-09-26 14:09:10 +02:00
connection_free ( conn ) ;
return ;
}
2003-09-16 03:58:46 +02:00
switch ( connection_connect ( conn , router - > address , router - > addr , router - > dir_port ) ) {
case - 1 :
2003-09-30 23:27:16 +02:00
router_mark_as_down ( conn - > nickname ) ; /* don't try him again */
2004-03-21 04:18:45 +01:00
connection_mark_for_close ( conn , 0 ) ;
2002-09-26 14:09:10 +02:00
return ;
2003-09-16 03:58:46 +02:00
case 0 :
connection_set_poll_socket ( conn ) ;
2003-08-14 19:13:52 +02:00
connection_watch_events ( conn , POLLIN | POLLOUT | POLLERR ) ;
/* writable indicates finish, readable indicates broken link,
error indicates broken link in windowsland . */
2002-09-26 14:09:10 +02:00
return ;
2003-09-16 03:58:46 +02:00
/* case 1: fall through */
2002-09-26 14:09:10 +02:00
}
2003-09-16 03:58:46 +02:00
connection_set_poll_socket ( conn ) ;
2003-09-17 22:09:06 +02:00
if ( directory_send_command ( conn , command ) < 0 ) {
2004-03-21 04:18:45 +01:00
connection_mark_for_close ( conn , 0 ) ;
2002-09-26 14:09:10 +02:00
}
}
2003-09-17 22:09:06 +02:00
static int directory_send_command ( connection_t * conn , int command ) {
2004-03-12 13:43:13 +01:00
char fetchstring [ ] = " GET / HTTP/1.0 \r \n \r \n " ;
2003-09-25 12:42:07 +02:00
const char * s ;
char tmp [ 8192 ] ;
2002-09-26 14:09:10 +02:00
assert ( conn & & conn - > type = = CONN_TYPE_DIR ) ;
2003-09-17 22:09:06 +02:00
switch ( command ) {
2003-09-21 08:15:43 +02:00
case DIR_CONN_STATE_CONNECTING_FETCH :
2003-10-04 04:38:18 +02:00
connection_write_to_buf ( fetchstring , strlen ( fetchstring ) , conn ) ;
2004-03-27 00:37:13 +01:00
conn - > state = DIR_CONN_STATE_CLIENT_SENDING_FETCH ;
2003-09-17 22:09:06 +02:00
break ;
2003-09-21 08:15:43 +02:00
case DIR_CONN_STATE_CONNECTING_UPLOAD :
2003-09-17 22:09:06 +02:00
s = router_get_my_descriptor ( ) ;
if ( ! s ) {
2003-10-10 03:48:32 +02:00
log_fn ( LOG_WARN , " Failed to get my descriptor. " ) ;
2003-09-17 22:09:06 +02:00
return - 1 ;
}
2003-09-25 12:42:07 +02:00
snprintf ( tmp , sizeof ( tmp ) , " POST / HTTP/1.0 \r \n Content-Length: %d \r \n \r \n %s " ,
2003-12-16 06:33:11 +01:00
( int ) strlen ( s ) , s ) ;
2003-10-04 04:38:18 +02:00
connection_write_to_buf ( tmp , strlen ( tmp ) , conn ) ;
2004-03-27 00:37:13 +01:00
conn - > state = DIR_CONN_STATE_CLIENT_SENDING_UPLOAD ;
2003-09-17 22:09:06 +02:00
break ;
2002-09-26 14:09:10 +02:00
}
return 0 ;
}
2004-03-12 13:43:13 +01:00
/* Parse "HTTP/1.%d %d%s\r\n".
* If it ' s well - formed , assign * code , point * message to the first
* non - space character after code if there is one and message is non - NULL
* ( else leave it alone ) , and return 0.
* Otherwise , return - 1.
*/
int parse_http_response ( char * headers , int * code , char * * message ) {
int n1 , n2 ;
assert ( headers & & code ) ;
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 ;
}
2002-09-26 14:09:10 +02:00
int connection_dir_process_inbuf ( connection_t * conn ) {
2003-12-17 10:42:28 +01:00
char * directory ;
2004-03-12 13:43:13 +01:00
char * headers ;
int status_code ;
2002-09-26 14:09:10 +02:00
assert ( conn & & conn - > type = = CONN_TYPE_DIR ) ;
if ( conn - > inbuf_reached_eof ) {
2004-03-12 13:43:13 +01:00
if ( conn - > state ! = DIR_CONN_STATE_CLIENT_READING_FETCH & &
conn - > state ! = DIR_CONN_STATE_CLIENT_READING_UPLOAD ) {
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 , 0 ) ;
return - 1 ;
}
switch ( fetch_from_buf_http ( conn - > inbuf ,
& headers , MAX_HEADERS_SIZE ,
& directory , MAX_DIR_SIZE ) ) {
case - 1 : /* overflow */
log_fn ( LOG_WARN , " 'fetch' response too large. Failing. " ) ;
2004-02-28 05:11:53 +01:00
connection_mark_for_close ( conn , 0 ) ;
2004-03-12 13:43:13 +01:00
return - 1 ;
case 0 :
log_fn ( LOG_INFO , " 'fetch' response not all here, but we're at eof. Closing. " ) ;
connection_mark_for_close ( conn , 0 ) ;
return - 1 ;
/* case 1, fall through */
}
if ( parse_http_response ( headers , & status_code , NULL ) < 0 ) {
log_fn ( LOG_WARN , " Unparseable headers. Closing. " ) ;
free ( directory ) ; free ( headers ) ;
connection_mark_for_close ( conn , 0 ) ;
return - 1 ;
}
if ( conn - > state = = DIR_CONN_STATE_CLIENT_READING_FETCH ) {
/* fetch/process the directory to learn about new routers. */
int directorylen ;
directorylen = strlen ( directory ) ;
log_fn ( LOG_INFO , " Received directory (size %d): \n %s " , directorylen , directory ) ;
if ( status_code = = 503 | | directorylen = = 0 ) {
log_fn ( LOG_INFO , " Empty directory. Ignoring. " ) ;
free ( directory ) ; free ( headers ) ;
2004-02-28 05:11:53 +01:00
connection_mark_for_close ( conn , 0 ) ;
return 0 ;
2004-03-12 13:43:13 +01:00
}
if ( status_code ! = 200 ) {
log_fn ( LOG_WARN , " Received http status code %d from dirserver. Failing. " ,
status_code ) ;
free ( directory ) ; free ( headers ) ;
2004-02-28 05:11:53 +01:00
connection_mark_for_close ( conn , 0 ) ;
2003-09-17 22:09:06 +02:00
return - 1 ;
2004-03-12 13:43:13 +01:00
}
if ( router_set_routerlist_from_directory ( directory , conn - > identity_pkey ) < 0 ) {
log_fn ( LOG_INFO , " ...but parsing failed. Ignoring. " ) ;
} else {
log_fn ( LOG_INFO , " updated routers. " ) ;
}
has_fetched_directory = 1 ;
if ( options . ORPort ) { /* connect to them all */
router_retry_connections ( ) ;
}
free ( directory ) ; free ( headers ) ;
connection_mark_for_close ( conn , 0 ) ;
return 0 ;
}
if ( conn - > state = = DIR_CONN_STATE_CLIENT_READING_UPLOAD ) {
switch ( status_code ) {
case 200 :
log_fn ( LOG_INFO , " eof (status 200) while reading upload response: finished. " ) ;
break ;
case 400 :
2004-03-30 01:23:01 +02:00
log_fn ( LOG_WARN , " http status 400 (bad request) response from dirserver. " ) ;
2004-03-12 13:43:13 +01:00
break ;
case 403 :
2004-03-30 01:23:01 +02:00
log_fn ( LOG_WARN , " http status 403 (unapproved server) response from dirserver. Is your clock skewed? Have you mailed arma your identity fingerprint? Are you using the right key? " ) ;
2004-03-12 13:43:13 +01:00
break ;
}
free ( directory ) ; free ( headers ) ;
connection_mark_for_close ( conn , 0 ) ;
return 0 ;
2002-09-28 02:52:59 +02:00
}
2002-09-26 14:09:10 +02:00
}
2003-09-17 22:09:06 +02:00
if ( conn - > state = = DIR_CONN_STATE_SERVER_COMMAND_WAIT )
return directory_handle_command ( conn ) ;
/* 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 " ;
static char answer503 [ ] = " HTTP/1.0 503 Directory unavailable \r \n \r \n " ;
2003-12-17 10:42:28 +01:00
static int directory_handle_command_get ( connection_t * conn ,
char * headers , char * body ) {
2003-09-28 10:06:18 +02:00
size_t dlen ;
2003-09-27 23:30:10 +02:00
const char * cp ;
2002-09-26 14:09:10 +02:00
2003-12-17 10:42:28 +01:00
/* XXX should check url and http version */
log_fn ( LOG_DEBUG , " Received GET command. " ) ;
dlen = dirserv_get_directory ( & cp ) ;
if ( dlen = = 0 ) {
log_fn ( LOG_WARN , " My directory is empty. Closing. " ) ;
2004-03-12 13:43:13 +01:00
connection_write_to_buf ( answer503 , strlen ( answer503 ) , conn ) ;
conn - > state = DIR_CONN_STATE_SERVER_WRITING ;
return 0 ;
2003-12-17 10:42:28 +01:00
}
2003-12-17 22:09:31 +01:00
log_fn ( LOG_DEBUG , " Dumping directory to client. " ) ;
2004-03-12 13:43:13 +01:00
connection_write_to_buf ( answer200 , strlen ( answer200 ) , conn ) ;
2003-12-17 10:42:28 +01:00
connection_write_to_buf ( cp , dlen , conn ) ;
conn - > state = DIR_CONN_STATE_SERVER_WRITING ;
return 0 ;
}
static int directory_handle_command_post ( connection_t * conn ,
char * headers , char * body ) {
const char * cp ;
/* XXX should check url and http version */
log_fn ( LOG_DEBUG , " Received POST command. " ) ;
cp = body ;
2004-03-12 13:43:13 +01:00
switch ( dirserv_add_descriptor ( & cp ) ) {
case - 1 :
2004-03-30 01:23:01 +02:00
/* malformed descriptor, or something wrong */
2004-03-12 13:43:13 +01:00
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 ;
2003-12-17 10:42:28 +01:00
}
conn - > state = DIR_CONN_STATE_SERVER_WRITING ;
return 0 ;
}
static int directory_handle_command ( connection_t * conn ) {
char * headers = NULL , * body = NULL ;
int r ;
2002-09-26 14:09:10 +02:00
assert ( conn & & conn - > type = = CONN_TYPE_DIR ) ;
2003-09-25 07:17:11 +02:00
switch ( fetch_from_buf_http ( conn - > inbuf ,
2003-12-17 10:42:28 +01:00
& headers , MAX_HEADERS_SIZE , & body , 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 ) )
r = directory_handle_command_get ( conn , headers , body ) ;
else if ( ! strncasecmp ( headers , " POST " , 4 ) )
r = directory_handle_command_post ( conn , headers , body ) ;
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
}
int connection_dir_finished_flushing ( connection_t * conn ) {
int e , len = sizeof ( e ) ;
assert ( conn & & conn - > type = = CONN_TYPE_DIR ) ;
switch ( conn - > state ) {
2003-09-21 08:15:43 +02:00
case DIR_CONN_STATE_CONNECTING_FETCH :
case DIR_CONN_STATE_CONNECTING_UPLOAD :
2003-08-12 05:08:41 +02:00
if ( getsockopt ( conn - > s , SOL_SOCKET , SO_ERROR , ( void * ) & e , & len ) < 0 ) { /* not yet */
2003-08-14 19:13:52 +02:00
if ( ! ERRNO_CONN_EINPROGRESS ( errno ) ) {
2003-06-18 00:18:26 +02:00
log_fn ( LOG_DEBUG , " in-progress connect failed. Removing. " ) ;
2003-09-30 23:27:16 +02:00
router_mark_as_down ( conn - > nickname ) ; /* don't try him again */
2004-02-28 05:11:53 +01:00
connection_mark_for_close ( conn , 0 ) ;
2002-09-26 14:09:10 +02:00
return - 1 ;
} else {
return 0 ; /* no change, see if next time is better */
}
}
/* the connect has finished. */
2003-09-26 12:03:50 +02:00
log_fn ( LOG_INFO , " Dir connection to router %s:%u established. " ,
2002-09-26 14:09:10 +02:00
conn - > address , conn - > port ) ;
2003-09-17 22:09:06 +02:00
return directory_send_command ( conn , conn - > state ) ;
2003-09-21 08:15:43 +02:00
case DIR_CONN_STATE_CLIENT_SENDING_FETCH :
log_fn ( LOG_DEBUG , " client finished sending fetch command. " ) ;
conn - > state = DIR_CONN_STATE_CLIENT_READING_FETCH ;
2003-09-17 22:09:06 +02:00
connection_watch_events ( conn , POLLIN ) ;
return 0 ;
2003-09-21 08:15:43 +02:00
case DIR_CONN_STATE_CLIENT_SENDING_UPLOAD :
log_fn ( LOG_DEBUG , " client finished sending upload command. " ) ;
conn - > state = DIR_CONN_STATE_CLIENT_READING_UPLOAD ;
2002-09-26 14:09:10 +02:00
connection_watch_events ( conn , POLLIN ) ;
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-02-28 05:11:53 +01:00
connection_mark_for_close ( conn , 0 ) ;
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 ;
}
2003-04-07 04:12:02 +02:00
/*
Local Variables :
mode : c
indent - tabs - mode : nil
c - basic - offset : 2
End :
*/