2005-04-01 22:15:56 +02:00
/* Copyright 2004-2005 Roger Dingledine, Nick Mathewson. */
2004-11-03 02:32:26 +01:00
/* See LICENSE for licensing information */
/* $Id$ */
2004-11-29 23:25:31 +01:00
const char control_c_id [ ] = " $Id$ " ;
2004-11-03 02:32:26 +01:00
2004-11-07 22:37:50 +01:00
/**
2004-11-30 07:17:35 +01:00
* \ file control . c
* \ brief Implementation for Tor ' s control - socket interface .
2005-06-11 07:31:17 +02:00
* */
2004-11-07 22:37:50 +01:00
2004-11-03 02:32:26 +01:00
# include "or.h"
2005-06-17 20:49:55 +02:00
# define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN_V0 || \
( s ) = = CONTROL_CONN_STATE_OPEN_V1 )
# define STATE_IS_V0(s) ((s) == CONTROL_CONN_STATE_NEEDAUTH_V0 || \
( s ) = = CONTROL_CONN_STATE_OPEN_V0 )
/*
* See control - spec . txt and control - spec - v0 . txt for full details on protocol ( s ) .
2004-11-07 22:37:50 +01:00
*/
/* Recognized message type codes. */
2005-06-17 20:49:55 +02:00
# define CONTROL0_CMD_ERROR 0x0000
# define CONTROL0_CMD_DONE 0x0001
# define CONTROL0_CMD_SETCONF 0x0002
# define CONTROL0_CMD_GETCONF 0x0003
# define CONTROL0_CMD_CONFVALUE 0x0004
# define CONTROL0_CMD_SETEVENTS 0x0005
# define CONTROL0_CMD_EVENT 0x0006
# define CONTROL0_CMD_AUTHENTICATE 0x0007
# define CONTROL0_CMD_SAVECONF 0x0008
# define CONTROL0_CMD_SIGNAL 0x0009
# define CONTROL0_CMD_MAPADDRESS 0x000A
# define CONTROL0_CMD_GETINFO 0x000B
# define CONTROL0_CMD_INFOVALUE 0x000C
# define CONTROL0_CMD_EXTENDCIRCUIT 0x000D
# define CONTROL0_CMD_ATTACHSTREAM 0x000E
# define CONTROL0_CMD_POSTDESCRIPTOR 0x000F
# define CONTROL0_CMD_FRAGMENTHEADER 0x0010
# define CONTROL0_CMD_FRAGMENT 0x0011
# define CONTROL0_CMD_REDIRECTSTREAM 0x0012
# define CONTROL0_CMD_CLOSESTREAM 0x0013
# define CONTROL0_CMD_CLOSECIRCUIT 0x0014
# define _CONTROL0_CMD_MAX_RECOGNIZED 0x0014
2004-11-03 02:32:26 +01:00
2004-11-07 22:37:50 +01:00
/* Recognized error codes. */
2004-11-03 02:32:26 +01:00
# define ERR_UNSPECIFIED 0x0000
2004-11-07 23:58:35 +01:00
# define ERR_INTERNAL 0x0001
# define ERR_UNRECOGNIZED_TYPE 0x0002
# define ERR_SYNTAX 0x0003
# define ERR_UNRECOGNIZED_CONFIG_KEY 0x0004
# define ERR_INVALID_CONFIG_VALUE 0x0005
# define ERR_UNRECOGNIZED_EVENT_CODE 0x0006
# define ERR_UNAUTHORIZED 0x0007
# define ERR_REJECTED_AUTHENTICATION 0x0008
2005-02-25 07:16:28 +01:00
# define ERR_RESOURCE_EXHAUSETED 0x0009
2005-03-12 05:22:01 +01:00
# define ERR_NO_STREAM 0x000A
# define ERR_NO_CIRC 0x000B
2005-03-19 07:05:55 +01:00
# define ERR_NO_ROUTER 0x000C
2004-11-03 02:32:26 +01:00
2004-11-09 02:24:10 +01:00
/* Recognized asynchronous event types. */
2004-11-03 02:32:26 +01:00
# define _EVENT_MIN 0x0001
# define EVENT_CIRCUIT_STATUS 0x0001
# define EVENT_STREAM_STATUS 0x0002
# define EVENT_OR_CONN_STATUS 0x0003
# define EVENT_BANDWIDTH_USED 0x0004
2005-04-06 00:56:17 +02:00
# define EVENT_LOG_OBSOLETE 0x0005
2005-02-25 07:16:28 +01:00
# define EVENT_NEW_DESC 0x0006
2005-04-06 00:56:17 +02:00
# define EVENT_DEBUG_MSG 0x0007
# define EVENT_INFO_MSG 0x0008
# define EVENT_NOTICE_MSG 0x0009
# define EVENT_WARN_MSG 0x000A
# define EVENT_ERR_MSG 0x000B
2005-09-22 01:30:15 +02:00
# define LAST_V0_EVENT 0x000B
2005-06-19 22:40:41 +02:00
# define EVENT_ADDRMAP 0x000C
# define _EVENT_MAX 0x000C
2004-11-03 02:32:26 +01:00
2004-11-07 22:37:50 +01:00
/** Array mapping from message type codes to human-readable message
2005-08-13 03:55:23 +02:00
* type names . Used for compatibility with version 0 of the control
* protocol . */
2005-06-17 20:49:55 +02:00
static const char * CONTROL0_COMMANDS [ _CONTROL0_CMD_MAX_RECOGNIZED + 1 ] = {
2004-11-03 02:32:26 +01:00
" error " ,
" done " ,
" setconf " ,
" getconf " ,
" confvalue " ,
" setevents " ,
" events " ,
" authenticate " ,
2004-11-07 23:37:59 +01:00
" saveconf " ,
2005-02-25 07:16:28 +01:00
" signal " ,
" mapaddress " ,
" getinfo " ,
" infovalue " ,
" extendcircuit " ,
" attachstream " ,
2005-03-02 21:22:10 +01:00
" postdescriptor " ,
" fragmentheader " ,
" fragment " ,
2004-11-03 02:32:26 +01:00
} ;
2004-11-07 23:37:59 +01:00
/** Bitfield: The bit 1<<e is set if <b>any</b> open control
2004-11-07 22:37:50 +01:00
* connection is interested in events of type < b > e < / b > . We use this
* so that we can decide to skip generating event messages that nobody
2004-11-09 02:24:10 +01:00
* has interest in without having to walk over the global connection
2004-11-07 22:37:50 +01:00
* list to find out .
* */
2005-06-17 22:37:21 +02:00
static uint32_t global_event_mask0 = 0 ;
static uint32_t global_event_mask1 = 0 ;
2004-11-03 02:32:26 +01:00
2005-07-13 07:14:42 +02:00
/** True iff we have disabled log messages from being sent to the controller */
static int disable_log_messages = 0 ;
2004-11-07 22:37:50 +01:00
/** Macro: true if any control connection is interested in events of type
* < b > e < / b > . */
2005-06-17 22:37:21 +02:00
# define EVENT_IS_INTERESTING0(e) (global_event_mask0 & (1<<(e)))
# define EVENT_IS_INTERESTING1(e) (global_event_mask1 & (1<<(e)))
# define EVENT_IS_INTERESTING(e) \
( ( global_event_mask0 | global_event_mask1 ) & ( 1 < < ( e ) ) )
2004-11-07 22:37:50 +01:00
/** If we're using cookie-type authentication, how long should our cookies be?
*/
2004-11-03 20:49:03 +01:00
# define AUTHENTICATION_COOKIE_LEN 32
2004-11-07 22:37:50 +01:00
/** If true, we've set authentication_cookie to a secret code and
* stored it to disk . */
2004-11-03 20:49:03 +01:00
static int authentication_cookie_is_set = 0 ;
static char authentication_cookie [ AUTHENTICATION_COOKIE_LEN ] ;
2005-06-17 22:37:21 +02:00
static void connection_printf_to_buf ( connection_t * conn , const char * format , . . . )
CHECK_PRINTF ( 2 , 3 ) ;
2005-08-12 19:24:53 +02:00
/*static*/ size_t write_escaped_data ( const char * data , size_t len ,
int translate_newlines , char * * out ) ;
/*static*/ size_t read_escaped_data ( const char * data , size_t len ,
int translate_newlines , char * * out ) ;
2005-06-17 20:49:55 +02:00
static void send_control0_message ( connection_t * conn , uint16_t type ,
2005-03-02 21:22:10 +01:00
uint32_t len , const char * body ) ;
2004-11-03 02:32:26 +01:00
static void send_control_done ( connection_t * conn ) ;
2005-02-25 21:46:13 +01:00
static void send_control_done2 ( connection_t * conn , const char * msg , size_t len ) ;
2005-06-17 20:49:55 +02:00
static void send_control0_error ( connection_t * conn , uint16_t error ,
2004-11-03 02:32:26 +01:00
const char * message ) ;
2005-06-17 20:49:55 +02:00
static void send_control0_event ( uint16_t event , uint32_t len , const char * body ) ;
2005-06-17 22:37:21 +02:00
static void send_control1_event ( uint16_t event , const char * format , . . . )
CHECK_PRINTF ( 2 , 3 ) ;
2005-03-02 21:22:10 +01:00
static int handle_control_setconf ( connection_t * conn , uint32_t len ,
2004-11-04 23:31:50 +01:00
char * body ) ;
2005-09-08 05:18:51 +02:00
static int handle_control_resetconf ( connection_t * conn , uint32_t len ,
char * body ) ;
2005-03-02 21:22:10 +01:00
static int handle_control_getconf ( connection_t * conn , uint32_t len ,
2004-11-03 02:32:26 +01:00
const char * body ) ;
2005-03-02 21:22:10 +01:00
static int handle_control_setevents ( connection_t * conn , uint32_t len ,
2004-11-03 02:32:26 +01:00
const char * body ) ;
2005-03-02 21:22:10 +01:00
static int handle_control_authenticate ( connection_t * conn , uint32_t len ,
2004-11-03 02:32:26 +01:00
const char * body ) ;
2005-03-02 21:22:10 +01:00
static int handle_control_saveconf ( connection_t * conn , uint32_t len ,
2004-11-07 23:37:59 +01:00
const char * body ) ;
2005-03-02 21:22:10 +01:00
static int handle_control_signal ( connection_t * conn , uint32_t len ,
2005-01-05 07:40:47 +01:00
const char * body ) ;
2005-03-02 21:22:10 +01:00
static int handle_control_mapaddress ( connection_t * conn , uint32_t len ,
2005-02-25 07:16:28 +01:00
const char * body ) ;
2005-03-02 21:22:10 +01:00
static int handle_control_getinfo ( connection_t * conn , uint32_t len ,
2005-02-25 07:16:28 +01:00
const char * body ) ;
2005-03-02 21:22:10 +01:00
static int handle_control_extendcircuit ( connection_t * conn , uint32_t len ,
2005-02-25 07:16:28 +01:00
const char * body ) ;
2005-03-02 21:22:10 +01:00
static int handle_control_attachstream ( connection_t * conn , uint32_t len ,
2005-02-25 07:16:28 +01:00
const char * body ) ;
2005-03-02 21:22:10 +01:00
static int handle_control_postdescriptor ( connection_t * conn , uint32_t len ,
2005-02-25 07:16:28 +01:00
const char * body ) ;
2005-03-19 07:05:55 +01:00
static int handle_control_redirectstream ( connection_t * conn , uint32_t len ,
const char * body ) ;
2005-03-22 20:36:38 +01:00
static int handle_control_closestream ( connection_t * conn , uint32_t len ,
const char * body ) ;
static int handle_control_closecircuit ( connection_t * conn , uint32_t len ,
const char * body ) ;
2005-06-19 22:40:41 +02:00
static int write_stream_target_to_buf ( connection_t * conn , char * buf , size_t len ) ;
2004-11-03 02:32:26 +01:00
2004-11-07 22:37:50 +01:00
/** Given a possibly invalid message type code <b>cmd</b>, return a
* human - readable string equivalent . */
2004-11-03 02:32:26 +01:00
static INLINE const char *
control_cmd_to_string ( uint16_t cmd )
{
2005-06-17 20:49:55 +02:00
return ( cmd < = _CONTROL0_CMD_MAX_RECOGNIZED ) ? CONTROL0_COMMANDS [ cmd ] : " Unknown " ;
2004-11-03 02:32:26 +01:00
}
2005-09-30 22:04:55 +02:00
/** Given a control event code for a message event, return the corresponding
* log severity . */
2005-04-06 00:56:17 +02:00
static INLINE int
event_to_log_severity ( int event )
{
switch ( event ) {
case EVENT_DEBUG_MSG : return LOG_DEBUG ;
case EVENT_INFO_MSG : return LOG_INFO ;
case EVENT_NOTICE_MSG : return LOG_NOTICE ;
case EVENT_WARN_MSG : return LOG_WARN ;
case EVENT_ERR_MSG : return LOG_ERR ;
default : return - 1 ;
}
}
2005-09-30 22:04:55 +02:00
/** Given a log severity, return the corresponding control event code. */
2005-04-06 00:56:17 +02:00
static INLINE int
log_severity_to_event ( int severity )
{
switch ( severity ) {
case LOG_DEBUG : return EVENT_DEBUG_MSG ;
case LOG_INFO : return EVENT_INFO_MSG ;
case LOG_NOTICE : return EVENT_NOTICE_MSG ;
case LOG_WARN : return EVENT_WARN_MSG ;
case LOG_ERR : return EVENT_ERR_MSG ;
default : return - 1 ;
}
}
2005-08-08 19:48:23 +02:00
/** Set <b>global_event_maskX</b> (where X is 0 or 1) to the bitwise OR
* of each live control connection ' s event_mask field . */
2005-08-07 21:20:55 +02:00
void
control_update_global_event_mask ( void )
2004-11-03 02:32:26 +01:00
{
connection_t * * conns ;
int n_conns , i ;
2005-06-17 22:37:21 +02:00
global_event_mask0 = 0 ;
global_event_mask1 = 0 ;
2004-11-03 02:32:26 +01:00
get_connection_array ( & conns , & n_conns ) ;
for ( i = 0 ; i < n_conns ; + + i ) {
if ( conns [ i ] - > type = = CONN_TYPE_CONTROL & &
2005-06-17 20:49:55 +02:00
STATE_IS_OPEN ( conns [ i ] - > state ) ) {
2005-06-17 22:37:21 +02:00
if ( STATE_IS_V0 ( conns [ i ] - > state ) )
global_event_mask0 | = conns [ i ] - > event_mask ;
else
global_event_mask1 | = conns [ i ] - > event_mask ;
2004-11-03 02:32:26 +01:00
}
}
2005-04-06 00:56:17 +02:00
2005-07-13 07:14:42 +02:00
control_adjust_event_log_severity ( ) ;
2005-04-06 00:56:17 +02:00
}
2005-09-30 22:04:55 +02:00
/** Adjust the log severities that result in control_event_logmsg being called
* to match the severity of log messages that any controllers are interested
* in . */
2005-06-11 20:52:12 +02:00
void
2005-07-13 07:14:42 +02:00
control_adjust_event_log_severity ( void )
2005-06-11 20:52:12 +02:00
{
2005-04-06 00:56:17 +02:00
int i ;
int min_log_event = EVENT_ERR_MSG , max_log_event = EVENT_DEBUG_MSG ;
for ( i = EVENT_DEBUG_MSG ; i < = EVENT_ERR_MSG ; + + i ) {
if ( EVENT_IS_INTERESTING ( i ) ) {
min_log_event = i ;
break ;
}
}
for ( i = EVENT_ERR_MSG ; i > = EVENT_DEBUG_MSG ; - - i ) {
if ( EVENT_IS_INTERESTING ( i ) ) {
max_log_event = i ;
break ;
}
}
if ( EVENT_IS_INTERESTING ( EVENT_LOG_OBSOLETE ) ) {
if ( min_log_event > EVENT_NOTICE_MSG )
min_log_event = EVENT_NOTICE_MSG ;
if ( max_log_event < EVENT_ERR_MSG )
max_log_event = EVENT_ERR_MSG ;
}
change_callback_log_severity ( event_to_log_severity ( min_log_event ) ,
event_to_log_severity ( max_log_event ) ,
control_event_logmsg ) ;
2004-11-03 02:32:26 +01:00
}
2005-07-15 21:31:11 +02:00
/** Append a NUL-terminated string <b>s</b> to the end of
* < b > conn < / b > - \ > outbuf
*/
2005-06-17 20:49:55 +02:00
static INLINE void
connection_write_str_to_buf ( const char * s , connection_t * conn )
{
size_t len = strlen ( s ) ;
connection_write_to_buf ( s , len , conn ) ;
}
2005-07-15 21:31:11 +02:00
/** Given a <b>len</b>-character string in <b>data</b>, made of lines
* terminated by CRLF , allocate a new string in * < b > out < / b > , and copy
* the contents of < b > data < / b > into * < b > out < / b > , adding a period
* before any period that that appears at the start of a line , and
* adding a period - CRLF line at the end . If < b > translate_newlines < / b >
* is true , replace all LF characters sequences with CRLF . Return the
* number of bytes in * < b > out < / b > .
*/
/* static */ size_t
2005-06-18 04:39:25 +02:00
write_escaped_data ( const char * data , size_t len , int translate_newlines ,
char * * out )
{
size_t sz_out = len + 8 ;
char * outp ;
const char * end ;
int i ;
int start_of_line ;
2005-06-28 01:35:04 +02:00
for ( i = 0 ; i < ( int ) len ; + + i ) {
2005-06-18 04:39:25 +02:00
if ( data [ i ] = = ' \n ' )
2005-09-22 01:13:29 +02:00
sz_out + = 2 ; /* Maybe add a CR; maybe add a dot. */
2005-06-18 04:39:25 +02:00
}
* out = outp = tor_malloc ( sz_out + 1 ) ;
end = data + len ;
start_of_line = 1 ;
while ( data < end ) {
if ( * data = = ' \n ' ) {
if ( translate_newlines )
* outp + + = ' \r ' ;
start_of_line = 1 ;
} else if ( * data = = ' . ' ) {
if ( start_of_line ) {
start_of_line = 0 ;
* outp + + = ' . ' ;
}
} else {
start_of_line = 0 ;
}
* outp + + = * data + + ;
}
2005-06-19 22:40:41 +02:00
if ( outp < * out + 2 | | memcmp ( outp - 2 , " \r \n " , 2 ) ) {
* outp + + = ' \r ' ;
* outp + + = ' \n ' ;
}
2005-06-18 04:39:25 +02:00
* outp + + = ' . ' ;
* outp + + = ' \r ' ;
* outp + + = ' \n ' ;
2005-09-22 01:13:29 +02:00
* outp = ' \0 ' ; /* NUL-terminate just in case. */
2005-09-29 22:49:41 +02:00
tor_assert ( ( outp - * out ) < = ( int ) sz_out ) ;
2005-06-18 04:39:25 +02:00
return outp - * out ;
}
2005-07-15 21:31:11 +02:00
/** Given a <b>len</b>-character string in <b>data</b>, made of lines
* terminated by CRLF , allocate a new string in * < b > out < / b > , and copy
* the contents of < b > data < / b > into * < b > out < / b > , removing any period
* that appears at the start of a line . If < b > translate_newlines < / b >
* is true , replace all CRLF sequences with LF . Return the number of
* bytes in * < b > out < / b > . */
/*static*/ size_t
2005-06-18 04:39:25 +02:00
read_escaped_data ( const char * data , size_t len , int translate_newlines ,
char * * out )
{
char * outp ;
const char * next ;
2005-07-15 21:31:11 +02:00
const char * end ;
* out = outp = tor_malloc ( len + 1 ) ;
2005-06-18 04:39:25 +02:00
2005-07-15 21:31:11 +02:00
end = data + len ;
2005-06-18 04:39:25 +02:00
2005-07-15 21:31:11 +02:00
while ( data < end ) {
2005-06-18 04:39:25 +02:00
if ( * data = = ' . ' )
+ + data ;
if ( translate_newlines )
2005-07-15 21:31:11 +02:00
next = tor_memmem ( data , end - data , " \r \n " , 2 ) ;
2005-06-18 04:39:25 +02:00
else
2005-07-15 21:31:11 +02:00
next = tor_memmem ( data , end - data , " \r \n . " , 3 ) ;
2005-06-18 04:39:25 +02:00
if ( next ) {
memcpy ( outp , data , next - data ) ;
outp + = ( next - data ) ;
data = next + 2 ;
} else {
2005-07-15 21:31:11 +02:00
memcpy ( outp , data , end - data ) ;
outp + = ( end - data ) ;
* outp = ' \0 ' ;
2005-06-18 04:39:25 +02:00
return outp - * out ;
}
if ( translate_newlines ) {
* outp + + = ' \n ' ;
} else {
* outp + + = ' \r ' ;
* outp + + = ' \n ' ;
}
}
2005-07-15 21:31:11 +02:00
* outp = ' \0 ' ;
2005-06-18 04:39:25 +02:00
return outp - * out ;
}
2005-06-17 20:49:55 +02:00
2005-09-30 22:04:55 +02:00
/** Given a pointer to a string starting at <b>start</b> containing
* < b > in_len_max < / b > characters , decode a string beginning with a single
* quote , containing any number of non - quote characters or characters escaped
* with a backslash , and ending with a final quote . Place the resulting
* string ( unquoted , unescaped ) into a newly allocated string in * < b > out < / b > ;
* store its length in < b > out_len < / b > . On success , return a pointer to the
* character immediately following the escaped string . On failure , return
2005-10-04 08:53:59 +02:00
* NULL . */
2005-06-18 05:50:08 +02:00
static const char *
2005-06-19 22:40:41 +02:00
get_escaped_string ( const char * start , size_t in_len_max ,
char * * out , size_t * out_len )
2005-06-18 05:50:08 +02:00
{
2005-06-19 22:40:41 +02:00
const char * cp , * end ;
char * outp ;
size_t len = 0 ;
if ( * start ! = ' \" ' )
return NULL ;
cp = start + 1 ;
end = start + in_len_max ;
2005-06-18 05:50:08 +02:00
2005-06-19 22:40:41 +02:00
/* Calculate length. */
while ( 1 ) {
if ( cp > = end )
return NULL ;
else if ( * cp = = ' \\ ' ) {
if ( + + cp = = end )
return NULL ; /* Can't escape EOS. */
+ + cp ;
+ + len ;
} else if ( * cp = = ' \" ' ) {
break ;
} else {
+ + cp ;
+ + len ;
}
}
end = cp ;
outp = * out = tor_malloc ( len + 1 ) ;
* out_len = len ;
2005-06-17 22:37:21 +02:00
2005-06-19 22:40:41 +02:00
cp = start + 1 ;
while ( cp < end ) {
if ( * cp = = ' \\ ' )
+ + cp ;
* outp + + = * cp + + ;
}
* outp = ' \0 ' ;
2005-09-08 08:37:50 +02:00
tor_assert ( ( outp - * out ) = = ( int ) * out_len ) ;
2005-06-19 22:40:41 +02:00
return end + 1 ;
}
2005-07-15 21:31:11 +02:00
/** Acts like sprintf, but writes its formatted string to the end of
* < b > conn < / b > - \ > outbuf . The message may be truncated if it is too long ,
* but it will always end with a CRLF sequence .
2005-10-18 18:45:43 +02:00
*
* Currently the length of the message is limited to 1024 ( including the
* ending \ n \ r \ 0. */
2005-06-17 20:49:55 +02:00
static void
connection_printf_to_buf ( connection_t * conn , const char * format , . . . )
{
2005-10-18 18:45:43 +02:00
# define CONNECTION_PRINTF_TO_BUF_BUFFERSIZE 1024
2005-06-17 20:49:55 +02:00
va_list ap ;
2005-10-18 18:45:43 +02:00
char buf [ CONNECTION_PRINTF_TO_BUF_BUFFERSIZE ] ;
2005-06-17 20:49:55 +02:00
int r ;
size_t len ;
va_start ( ap , format ) ;
2005-06-17 22:37:21 +02:00
r = tor_vsnprintf ( buf , sizeof ( buf ) , format , ap ) ;
2005-06-17 20:49:55 +02:00
va_end ( ap ) ;
len = strlen ( buf ) ;
if ( memcmp ( " \r \n \0 " , buf + len - 2 , 3 ) ) {
2005-10-18 18:45:43 +02:00
buf [ CONNECTION_PRINTF_TO_BUF_BUFFERSIZE - 1 ] = ' \0 ' ;
buf [ CONNECTION_PRINTF_TO_BUF_BUFFERSIZE - 2 ] = ' \n ' ;
buf [ CONNECTION_PRINTF_TO_BUF_BUFFERSIZE - 3 ] = ' \r ' ;
2005-06-17 20:49:55 +02:00
}
connection_write_to_buf ( buf , len , conn ) ;
}
2004-11-07 22:37:50 +01:00
/** Send a message of type <b>type</b> containing <b>len</b> bytes
* from < b > body < / b > along the control connection < b > conn < / b > */
2004-11-03 02:32:26 +01:00
static void
2005-06-17 20:49:55 +02:00
send_control0_message ( connection_t * conn , uint16_t type , uint32_t len ,
2004-11-03 02:32:26 +01:00
const char * body )
{
2005-03-02 21:22:10 +01:00
char buf [ 10 ] ;
2004-11-03 02:32:26 +01:00
tor_assert ( conn ) ;
2005-06-17 20:49:55 +02:00
tor_assert ( STATE_IS_V0 ( conn - > state ) ) ;
2004-11-09 11:08:42 +01:00
tor_assert ( len | | ! body ) ;
2005-06-17 20:49:55 +02:00
tor_assert ( type < = _CONTROL0_CMD_MAX_RECOGNIZED ) ;
2005-03-02 21:22:10 +01:00
if ( len < 65536 ) {
set_uint16 ( buf , htons ( len ) ) ;
set_uint16 ( buf + 2 , htons ( type ) ) ;
connection_write_to_buf ( buf , 4 , conn ) ;
if ( len )
connection_write_to_buf ( body , len , conn ) ;
} else {
set_uint16 ( buf , htons ( 65535 ) ) ;
2005-06-17 20:49:55 +02:00
set_uint16 ( buf + 2 , htons ( CONTROL0_CMD_FRAGMENTHEADER ) ) ;
2005-03-02 21:22:10 +01:00
set_uint16 ( buf + 4 , htons ( type ) ) ;
set_uint32 ( buf + 6 , htonl ( len ) ) ;
connection_write_to_buf ( buf , 10 , conn ) ;
connection_write_to_buf ( body , 65535 - 6 , conn ) ;
len - = ( 65535 - 6 ) ;
body + = ( 65535 - 6 ) ;
while ( len ) {
size_t chunklen = ( len < 65535 ) ? len : 65535 ;
set_uint16 ( buf , htons ( ( uint16_t ) chunklen ) ) ;
2005-06-17 20:49:55 +02:00
set_uint16 ( buf + 2 , htons ( CONTROL0_CMD_FRAGMENT ) ) ;
2005-03-02 21:22:10 +01:00
connection_write_to_buf ( buf , 4 , conn ) ;
connection_write_to_buf ( body , chunklen , conn ) ;
len - = chunklen ;
body + = chunklen ;
}
}
2004-11-03 02:32:26 +01:00
}
2004-11-07 22:37:50 +01:00
/** Send a "DONE" message down the control connection <b>conn</b> */
2004-11-03 02:32:26 +01:00
static void
send_control_done ( connection_t * conn )
{
2005-06-17 20:49:55 +02:00
if ( STATE_IS_V0 ( conn - > state ) ) {
send_control0_message ( conn , CONTROL0_CMD_DONE , 0 , NULL ) ;
} else {
connection_write_str_to_buf ( " 250 OK \r \n " , conn ) ;
}
2004-11-03 02:32:26 +01:00
}
2005-09-30 22:04:55 +02:00
/** Send a "DONE" message down the v0 control message <b>conn</b>, with body
* as provided in the < b > len < / b > bytes at < b > msg < / b > .
*/
2005-06-11 20:52:12 +02:00
static void
send_control_done2 ( connection_t * conn , const char * msg , size_t len )
2005-02-25 21:46:13 +01:00
{
2005-04-03 00:02:13 +02:00
if ( len = = 0 )
len = strlen ( msg ) ;
2005-06-17 20:49:55 +02:00
send_control0_message ( conn , CONTROL0_CMD_DONE , len , msg ) ;
2005-02-25 21:46:13 +01:00
}
2004-11-07 22:37:50 +01:00
/** Send an error message with error code <b>error</b> and body
* < b > message < / b > down the connection < b > conn < / b > */
2004-11-03 02:32:26 +01:00
static void
2005-06-17 20:49:55 +02:00
send_control0_error ( connection_t * conn , uint16_t error , const char * message )
2004-11-03 02:32:26 +01:00
{
char buf [ 256 ] ;
size_t len ;
set_uint16 ( buf , htons ( error ) ) ;
len = strlen ( message ) ;
tor_assert ( len < ( 256 - 2 ) ) ;
memcpy ( buf + 2 , message , len ) ;
2005-06-17 20:49:55 +02:00
send_control0_message ( conn , CONTROL0_CMD_ERROR , ( uint16_t ) ( len + 2 ) , buf ) ;
2004-11-03 02:32:26 +01:00
}
2004-11-07 22:37:50 +01:00
/** Send an 'event' message of event type <b>event</b>, containing
* < b > len < / b > bytes in < b > body < / b > to every control connection that
* is interested in it . */
2004-11-03 02:32:26 +01:00
static void
2005-06-17 20:49:55 +02:00
send_control0_event ( uint16_t event , uint32_t len , const char * body )
2004-11-03 02:32:26 +01:00
{
connection_t * * conns ;
int n_conns , i ;
2004-11-11 01:54:53 +01:00
size_t buflen ;
char * buf ;
2005-09-22 01:30:15 +02:00
tor_assert ( event > = _EVENT_MIN & & event < = LAST_V0_EVENT ) ;
2005-04-18 00:52:02 +02:00
2004-11-11 01:54:53 +01:00
buflen = len + 2 ;
buf = tor_malloc_zero ( buflen ) ;
set_uint16 ( buf , htons ( event ) ) ;
memcpy ( buf + 2 , body , len ) ;
2004-11-03 02:32:26 +01:00
get_connection_array ( & conns , & n_conns ) ;
for ( i = 0 ; i < n_conns ; + + i ) {
if ( conns [ i ] - > type = = CONN_TYPE_CONTROL & &
2005-08-07 21:27:38 +02:00
! conns [ i ] - > marked_for_close & &
2005-06-17 20:49:55 +02:00
conns [ i ] - > state = = CONTROL_CONN_STATE_OPEN_V0 & &
2004-11-03 02:32:26 +01:00
conns [ i ] - > event_mask & ( 1 < < event ) ) {
2005-06-17 20:49:55 +02:00
send_control0_message ( conns [ i ] , CONTROL0_CMD_EVENT , buflen , buf ) ;
2005-06-19 22:40:41 +02:00
if ( event = = EVENT_ERR_MSG )
2005-04-08 05:36:39 +02:00
_connection_controller_force_write ( conns [ i ] ) ;
2004-11-03 02:32:26 +01:00
}
}
2004-11-11 01:54:53 +01:00
2004-11-12 17:39:03 +01:00
tor_free ( buf ) ;
2004-11-03 02:32:26 +01:00
}
2005-09-30 22:04:55 +02:00
/* Send an event to all v1 controllers that are listening for code
2005-10-18 18:45:43 +02:00
* < b > event < / b > . The event ' s body is given by < b > msg < / b > . */
2005-06-17 22:37:21 +02:00
static void
2005-10-18 18:45:43 +02:00
send_control1_event_string ( uint16_t event , const char * msg )
2005-06-17 22:37:21 +02:00
{
connection_t * * conns ;
2005-10-18 18:45:43 +02:00
int n_conns , i ;
2005-06-17 22:37:21 +02:00
tor_assert ( event > = _EVENT_MIN & & event < = _EVENT_MAX ) ;
get_connection_array ( & conns , & n_conns ) ;
for ( i = 0 ; i < n_conns ; + + i ) {
if ( conns [ i ] - > type = = CONN_TYPE_CONTROL & &
2005-08-07 21:27:38 +02:00
! conns [ i ] - > marked_for_close & &
2005-06-17 22:37:21 +02:00
conns [ i ] - > state = = CONTROL_CONN_STATE_OPEN_V1 & &
conns [ i ] - > event_mask & ( 1 < < event ) ) {
2005-10-18 18:45:43 +02:00
connection_write_to_buf ( msg , strlen ( msg ) , conns [ i ] ) ;
2005-06-19 22:40:41 +02:00
if ( event = = EVENT_ERR_MSG )
2005-06-17 22:37:21 +02:00
_connection_controller_force_write ( conns [ i ] ) ;
}
}
}
2005-10-18 18:45:43 +02:00
/* Send an event to all v1 controllers that are listening for code
* < b > event < / b > . The event ' s body is created by the printf - style format in
* < b > format < / b > , and other arguments as provided .
*
* Currently the length of the message is limited to 1024 ( including the
* ending \ n \ r \ 0. */
static void
send_control1_event ( uint16_t event , const char * format , . . . )
{
# define SEND_CONTROL1_EVENT_BUFFERSIZE 1024
int r ;
char buf [ SEND_CONTROL1_EVENT_BUFFERSIZE ] ; /* XXXX Length */
va_list ap ;
size_t len ;
va_start ( ap , format ) ;
r = tor_vsnprintf ( buf , sizeof ( buf ) , format , ap ) ;
va_end ( ap ) ;
len = strlen ( buf ) ;
if ( memcmp ( " \r \n \0 " , buf + len - 2 , 3 ) ) {
/* if it is not properly terminated, do it now */
buf [ SEND_CONTROL1_EVENT_BUFFERSIZE - 1 ] = ' \0 ' ;
buf [ SEND_CONTROL1_EVENT_BUFFERSIZE - 2 ] = ' \n ' ;
buf [ SEND_CONTROL1_EVENT_BUFFERSIZE - 3 ] = ' \r ' ;
}
send_control1_event_string ( event , buf ) ;
}
2005-09-30 22:04:55 +02:00
/** Given a text circuit <b>id</b>, return the corresponding circuit. */
2005-06-18 04:39:25 +02:00
static circuit_t *
get_circ ( const char * id )
{
unsigned long n_id ;
int ok ;
n_id = tor_parse_ulong ( id , 10 , 0 , ULONG_MAX , & ok , NULL ) ;
if ( ! ok )
return NULL ;
return circuit_get_by_global_id ( n_id ) ;
}
2005-09-30 22:04:55 +02:00
/** Given a text stream <b>id</b>, return the corresponding AP connection. */
2005-06-18 04:39:25 +02:00
static connection_t *
get_stream ( const char * id )
{
unsigned long n_id ;
int ok ;
connection_t * conn ;
n_id = tor_parse_ulong ( id , 10 , 0 , ULONG_MAX , & ok , NULL ) ;
if ( ! ok )
return NULL ;
conn = connection_get_by_global_id ( n_id ) ;
2005-09-12 05:32:30 +02:00
if ( ! conn | | conn - > type ! = CONN_TYPE_AP )
2005-06-18 04:39:25 +02:00
return NULL ;
return conn ;
}
2005-09-08 05:18:51 +02:00
/** Helper for setconf and resetconf. Acts like setconf, except
2005-09-14 04:07:35 +02:00
* it passes < b > use_defaults < / b > on to options_trial_assign ( ) .
2005-09-08 05:18:51 +02:00
*/
2004-11-03 19:33:07 +01:00
static int
2005-09-14 04:07:35 +02:00
control_setconf_helper ( connection_t * conn , uint32_t len , char * body ,
int use_defaults , int clear_first )
2004-11-03 02:32:26 +01:00
{
2004-11-07 23:58:35 +01:00
int r ;
2005-07-22 23:12:10 +02:00
config_line_t * lines = NULL ;
2005-06-19 22:40:41 +02:00
char * start = body ;
2005-06-18 05:50:08 +02:00
int v0 = STATE_IS_V0 ( conn - > state ) ;
2004-11-04 23:31:50 +01:00
2005-06-18 05:50:08 +02:00
if ( ! v0 ) {
char * config = tor_malloc ( len + 1 ) ;
char * outp = config ;
while ( * body ) {
char * eq = body ;
while ( ! TOR_ISSPACE ( * eq ) & & * eq ! = ' = ' )
+ + eq ;
memcpy ( outp , body , eq - body ) ;
outp + = ( eq - body ) ;
2005-06-19 22:40:41 +02:00
* outp + + = ' ' ;
2005-06-18 05:50:08 +02:00
body = eq + 1 ;
if ( * eq = = ' = ' ) {
if ( * body ! = ' \" ' ) {
while ( ! TOR_ISSPACE ( * body ) )
* outp + + = * body + + ;
} else {
char * val ;
size_t val_len ;
2005-06-19 22:40:41 +02:00
body = ( char * ) get_escaped_string ( body , ( len - ( body - start ) ) ,
& val , & val_len ) ;
2005-06-18 05:50:08 +02:00
if ( ! body ) {
connection_write_str_to_buf ( " 551 Couldn't parse string \r \n " , conn ) ;
tor_free ( config ) ;
return 0 ;
}
memcpy ( outp , val , val_len ) ;
outp + = val_len ;
2005-06-19 22:40:41 +02:00
tor_free ( val ) ;
2005-06-18 05:50:08 +02:00
}
}
while ( TOR_ISSPACE ( * body ) )
+ + body ;
* outp + + = ' \n ' ;
}
* outp = ' \0 ' ;
if ( config_get_lines ( config , & lines ) < 0 ) {
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Controller gave us config lines we can't parse. " ) ;
2005-06-18 05:50:08 +02:00
connection_write_str_to_buf ( " 551 Couldn't parse configuration \r \n " , conn ) ;
tor_free ( config ) ;
return 0 ;
}
tor_free ( config ) ;
} else {
if ( config_get_lines ( body , & lines ) < 0 ) {
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Controller gave us config lines we can't parse. " ) ;
2005-06-18 05:50:08 +02:00
send_control0_error ( conn , ERR_SYNTAX , " Couldn't parse configuration " ) ;
return 0 ;
}
2004-11-06 06:18:11 +01:00
}
2004-11-04 23:31:50 +01:00
2005-09-14 04:07:35 +02:00
if ( ( r = options_trial_assign ( lines , use_defaults , clear_first ) ) < 0 ) {
2005-09-14 04:36:29 +02:00
int v0_err ;
const char * msg ;
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Controller gave us config lines that didn't validate. " ) ;
2005-09-14 04:36:29 +02:00
switch ( r ) {
case - 1 :
v0_err = ERR_UNRECOGNIZED_CONFIG_KEY ;
2005-11-17 23:01:46 +01:00
msg = " 552 Unrecognized option " ;
2005-09-14 04:36:29 +02:00
break ;
case - 2 :
v0_err = ERR_INVALID_CONFIG_VALUE ;
2005-11-17 23:01:46 +01:00
msg = " 513 Unrecognized option value " ;
2005-09-14 04:36:29 +02:00
break ;
case - 3 :
v0_err = ERR_INVALID_CONFIG_VALUE ;
2005-11-17 23:01:46 +01:00
msg = " 553 Transition not allowed " ;
2005-09-14 04:36:29 +02:00
break ;
case - 4 :
default :
v0_err = ERR_INVALID_CONFIG_VALUE ;
2005-11-17 23:01:46 +01:00
msg = " 553 Unable to set option " ;
2005-09-14 04:36:29 +02:00
break ;
}
if ( v0 ) {
send_control0_error ( conn , v0_err , msg ) ;
2004-11-07 23:58:35 +01:00
} else {
2005-11-17 23:01:46 +01:00
connection_printf_to_buf ( conn , " %s \r \n " , msg ) ;
2004-11-07 23:58:35 +01:00
}
2004-11-06 06:18:11 +01:00
config_free_lines ( lines ) ;
return 0 ;
}
2005-08-16 01:46:18 +02:00
config_free_lines ( lines ) ;
2004-11-06 06:18:11 +01:00
send_control_done ( conn ) ;
2004-11-03 02:32:26 +01:00
return 0 ;
}
2004-11-03 19:33:07 +01:00
2005-09-08 05:18:51 +02:00
/** Called when we receive a SETCONF message: parse the body and try
* to update our configuration . Reply with a DONE or ERROR message . */
static int
handle_control_setconf ( connection_t * conn , uint32_t len , char * body )
{
2005-09-14 04:07:35 +02:00
return control_setconf_helper ( conn , len , body , 0 , 1 ) ;
2005-09-08 05:18:51 +02:00
}
/** Called when we receive a RESETCONF message: parse the body and try
* to update our configuration . Reply with a DONE or ERROR message . */
static int
handle_control_resetconf ( connection_t * conn , uint32_t len , char * body )
{
int v0 = STATE_IS_V0 ( conn - > state ) ;
tor_assert ( ! v0 ) ;
2005-09-14 04:07:35 +02:00
return control_setconf_helper ( conn , len , body , 1 , 1 ) ;
2005-09-08 05:18:51 +02:00
}
2004-11-07 22:37:50 +01:00
/** Called when we receive a GETCONF message. Parse the request, and
* reply with a CONFVALUE or an ERROR message */
2004-11-04 23:31:50 +01:00
static int
2005-03-02 21:22:10 +01:00
handle_control_getconf ( connection_t * conn , uint32_t body_len , const char * body )
2004-11-03 02:32:26 +01:00
{
2004-11-04 23:31:50 +01:00
smartlist_t * questions = NULL ;
smartlist_t * answers = NULL ;
2005-06-17 22:37:21 +02:00
smartlist_t * unrecognized = NULL ;
2004-11-03 19:33:07 +01:00
char * msg = NULL ;
size_t msg_len ;
2004-11-06 06:18:11 +01:00
or_options_t * options = get_options ( ) ;
2005-06-17 22:37:21 +02:00
int v0 = STATE_IS_V0 ( conn - > state ) ;
2004-11-03 19:33:07 +01:00
2004-11-04 23:31:50 +01:00
questions = smartlist_create ( ) ;
2005-06-17 22:37:21 +02:00
if ( v0 ) {
smartlist_split_string ( questions , body , " \n " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , 0 ) ;
} else {
smartlist_split_string ( questions , body , " " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , 0 ) ;
}
2004-11-04 23:31:50 +01:00
answers = smartlist_create ( ) ;
2005-06-17 22:37:21 +02:00
unrecognized = smartlist_create ( ) ;
SMARTLIST_FOREACH ( questions , char * , q ,
2004-11-04 23:31:50 +01:00
{
2005-07-23 03:58:05 +02:00
if ( ! option_is_recognized ( q ) ) {
2005-06-17 22:37:21 +02:00
if ( v0 ) {
send_control0_error ( conn , ERR_UNRECOGNIZED_CONFIG_KEY , q ) ;
goto done ;
} else {
smartlist_add ( unrecognized , q ) ;
}
2004-11-03 19:33:07 +01:00
} else {
2005-07-23 03:58:05 +02:00
config_line_t * answer = option_get_assignment ( options , q ) ;
2005-06-20 00:38:36 +02:00
if ( ! v0 & & ! answer ) {
2005-07-23 03:58:05 +02:00
const char * name = option_get_canonical_name ( q ) ;
2005-07-11 20:11:54 +02:00
size_t alen = strlen ( name ) + 8 ;
2005-06-20 00:38:36 +02:00
char * astr = tor_malloc ( alen ) ;
2005-07-11 20:11:54 +02:00
tor_snprintf ( astr , alen , " 250-%s \r \n " , name ) ;
2005-06-20 00:38:36 +02:00
smartlist_add ( answers , astr ) ;
}
2004-11-09 07:40:32 +01:00
2004-11-03 19:33:07 +01:00
while ( answer ) {
2005-07-22 23:12:10 +02:00
config_line_t * next ;
2005-06-17 22:37:21 +02:00
size_t alen = strlen ( answer - > key ) + strlen ( answer - > value ) + 8 ;
2004-11-04 23:31:50 +01:00
char * astr = tor_malloc ( alen ) ;
2005-06-17 22:37:21 +02:00
if ( v0 )
tor_snprintf ( astr , alen , " %s %s \n " , answer - > key , answer - > value ) ;
else
tor_snprintf ( astr , alen , " 250-%s=%s \r \n " , answer - > key , answer - > value ) ;
2004-11-04 23:31:50 +01:00
smartlist_add ( answers , astr ) ;
2004-11-03 19:33:07 +01:00
next = answer - > next ;
2004-11-04 23:31:50 +01:00
tor_free ( answer - > key ) ;
tor_free ( answer - > value ) ;
2004-11-03 19:33:07 +01:00
tor_free ( answer ) ;
answer = next ;
}
}
2004-11-04 23:31:50 +01:00
} ) ;
2004-11-03 19:33:07 +01:00
2005-06-17 22:37:21 +02:00
if ( v0 ) {
msg = smartlist_join_strings ( answers , " " , 0 , & msg_len ) ;
send_control0_message ( conn , CONTROL0_CMD_CONFVALUE ,
( uint16_t ) msg_len , msg_len ? msg : NULL ) ;
} else {
int i , len ;
if ( ( len = smartlist_len ( unrecognized ) ) ) {
for ( i = 0 ; i < len - 1 ; + + i )
connection_printf_to_buf ( conn ,
" 552-Unrecognized configuration key \" %s \" \r \n " ,
( char * ) smartlist_get ( unrecognized , i ) ) ;
connection_printf_to_buf ( conn ,
" 552 Unrecognized configuration key \" %s \" \r \n " ,
( char * ) smartlist_get ( unrecognized , len - 1 ) ) ;
} else if ( ( len = smartlist_len ( answers ) ) ) {
char * tmp = smartlist_get ( answers , len - 1 ) ;
tor_assert ( strlen ( tmp ) > 4 ) ;
tmp [ 3 ] = ' ' ;
msg = smartlist_join_strings ( answers , " " , 0 , & msg_len ) ;
connection_write_to_buf ( msg , msg_len , conn ) ;
} else {
connection_write_str_to_buf ( " 250 OK \r \n " , conn ) ;
}
}
2004-11-03 19:33:07 +01:00
done :
2005-10-25 09:02:13 +02:00
if ( answers ) {
SMARTLIST_FOREACH ( answers , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( answers ) ;
}
if ( questions ) {
SMARTLIST_FOREACH ( questions , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( questions ) ;
}
2005-06-17 22:37:21 +02:00
smartlist_free ( unrecognized ) ;
2004-11-03 19:33:07 +01:00
tor_free ( msg ) ;
2004-11-03 02:32:26 +01:00
return 0 ;
}
2004-11-04 23:31:50 +01:00
2004-11-07 22:37:50 +01:00
/** Called when we get a SETEVENTS message: update conn->event_mask,
* and reply with DONE or ERROR . */
static int
2005-03-02 21:22:10 +01:00
handle_control_setevents ( connection_t * conn , uint32_t len , const char * body )
2004-11-03 02:32:26 +01:00
{
uint16_t event_code ;
uint32_t event_mask = 0 ;
2005-10-12 06:31:44 +02:00
unsigned int extended = 0 ;
2004-11-03 02:32:26 +01:00
2005-06-17 22:37:21 +02:00
if ( STATE_IS_V0 ( conn - > state ) ) {
if ( len % 2 ) {
send_control0_error ( conn , ERR_SYNTAX ,
" Odd number of bytes in setevents message " ) ;
2004-11-03 02:32:26 +01:00
return 0 ;
}
2005-06-17 22:37:21 +02:00
for ( ; len ; len - = 2 , body + = 2 ) {
event_code = ntohs ( get_uint16 ( body ) ) ;
2005-09-22 01:30:15 +02:00
if ( event_code < _EVENT_MIN | | event_code > LAST_V0_EVENT ) {
2005-06-17 22:37:21 +02:00
send_control0_error ( conn , ERR_UNRECOGNIZED_EVENT_CODE ,
" Unrecognized event code " ) ;
return 0 ;
}
event_mask | = ( 1 < < event_code ) ;
}
} else {
smartlist_t * events = smartlist_create ( ) ;
smartlist_split_string ( events , body , " " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , 0 ) ;
SMARTLIST_FOREACH ( events , const char * , ev ,
{
2005-10-12 06:31:44 +02:00
if ( ! strcasecmp ( ev , " EXTENDED " ) ) {
extended = 1 ;
continue ;
} else if ( ! strcasecmp ( ev , " CIRC " ) )
2005-06-17 22:37:21 +02:00
event_code = EVENT_CIRCUIT_STATUS ;
else if ( ! strcasecmp ( ev , " STREAM " ) )
event_code = EVENT_STREAM_STATUS ;
else if ( ! strcasecmp ( ev , " ORCONN " ) )
event_code = EVENT_OR_CONN_STATUS ;
else if ( ! strcasecmp ( ev , " BW " ) )
event_code = EVENT_BANDWIDTH_USED ;
else if ( ! strcasecmp ( ev , " DEBUG " ) )
2005-07-13 07:14:42 +02:00
event_code = EVENT_DEBUG_MSG ;
2005-06-17 22:37:21 +02:00
else if ( ! strcasecmp ( ev , " INFO " ) )
event_code = EVENT_INFO_MSG ;
else if ( ! strcasecmp ( ev , " NOTICE " ) )
event_code = EVENT_NOTICE_MSG ;
else if ( ! strcasecmp ( ev , " WARN " ) )
event_code = EVENT_WARN_MSG ;
else if ( ! strcasecmp ( ev , " ERR " ) )
event_code = EVENT_ERR_MSG ;
else if ( ! strcasecmp ( ev , " NEWDESC " ) )
event_code = EVENT_NEW_DESC ;
2005-06-19 22:40:41 +02:00
else if ( ! strcasecmp ( ev , " ADDRMAP " ) )
event_code = EVENT_ADDRMAP ;
2005-06-17 22:37:21 +02:00
else {
connection_printf_to_buf ( conn , " 552 Unrecognized event \" %s \" \r \n " ,
ev ) ;
SMARTLIST_FOREACH ( events , char * , e , tor_free ( e ) ) ;
smartlist_free ( events ) ;
return 0 ;
}
event_mask | = ( 1 < < event_code ) ;
} ) ;
SMARTLIST_FOREACH ( events , char * , e , tor_free ( e ) ) ;
smartlist_free ( events ) ;
}
2004-11-03 02:32:26 +01:00
conn - > event_mask = event_mask ;
2005-10-12 06:31:44 +02:00
conn - > control_events_are_extended = extended ;
2004-11-03 02:32:26 +01:00
2005-08-07 21:20:55 +02:00
control_update_global_event_mask ( ) ;
2004-11-03 02:32:26 +01:00
send_control_done ( conn ) ;
return 0 ;
}
2004-11-04 23:31:50 +01:00
2004-12-13 19:32:29 +01:00
/** Decode the hashed, base64'd password stored in <b>hashed</b>. If
* < b > buf < / b > is provided , store the hashed password in the first
* S2K_SPECIFIER_LEN + DIGEST_LEN bytes of < b > buf < / b > . Return 0 on
* success , - 1 on failure .
*/
int
decode_hashed_password ( char * buf , const char * hashed )
{
char decoded [ 64 ] ;
2005-05-23 04:31:53 +02:00
if ( ! strcmpstart ( hashed , " 16: " ) ) {
if ( base16_decode ( decoded , sizeof ( decoded ) , hashed + 3 , strlen ( hashed + 3 ) ) < 0
| | strlen ( hashed + 3 ) ! = ( S2K_SPECIFIER_LEN + DIGEST_LEN ) * 2 ) {
return - 1 ;
}
} else {
if ( base64_decode ( decoded , sizeof ( decoded ) , hashed , strlen ( hashed ) )
! = S2K_SPECIFIER_LEN + DIGEST_LEN ) {
return - 1 ;
}
2004-12-13 19:32:29 +01:00
}
if ( buf )
2005-06-05 16:28:47 +02:00
memcpy ( buf , decoded , S2K_SPECIFIER_LEN + DIGEST_LEN ) ;
2004-12-13 19:32:29 +01:00
return 0 ;
}
2004-11-07 22:37:50 +01:00
/** Called when we get an AUTHENTICATE message. Check whether the
* authentication is valid , and if so , update the connection ' s state to
* OPEN . Reply with DONE or ERROR .
*/
static int
2005-03-02 21:22:10 +01:00
handle_control_authenticate ( connection_t * conn , uint32_t len , const char * body )
2004-11-03 02:32:26 +01:00
{
2005-10-12 21:45:35 +02:00
int used_quoted_string = 0 ;
2004-11-06 06:18:11 +01:00
or_options_t * options = get_options ( ) ;
2005-06-18 05:50:08 +02:00
char * password ;
size_t password_len ;
if ( STATE_IS_V0 ( conn - > state ) ) {
password = ( char * ) body ;
password_len = len ;
} else {
if ( TOR_ISXDIGIT ( body [ 0 ] ) ) {
int i = 0 ;
while ( TOR_ISXDIGIT ( body [ i ] ) )
+ + i ;
password = tor_malloc ( i / 2 + 1 ) ;
if ( base16_decode ( password , i / 2 + 1 , body , i ) < 0 ) {
2005-10-12 21:45:35 +02:00
connection_write_str_to_buf ( " 551 Invalid hexadecimal encoding. Maybe you tried a plain text password? If so, the standard requires you put it in double quotes. \r \n " , conn ) ;
2005-06-18 05:50:08 +02:00
tor_free ( password ) ;
return 0 ;
}
password_len = i / 2 ;
} else if ( TOR_ISSPACE ( body [ 0 ] ) ) {
password = tor_strdup ( " " ) ;
password_len = 0 ;
} else {
2005-06-19 22:40:41 +02:00
if ( ! get_escaped_string ( body , len , & password , & password_len ) ) {
2005-10-12 21:45:35 +02:00
connection_write_str_to_buf ( " 551 Invalid quoted string. You need to put the password in double quotes. \r \n " , conn ) ;
2005-06-18 05:50:08 +02:00
return 0 ;
}
2005-10-12 21:45:35 +02:00
used_quoted_string = 1 ;
2005-06-18 05:50:08 +02:00
}
}
2004-11-12 17:39:03 +01:00
if ( options - > CookieAuthentication ) {
if ( len = = AUTHENTICATION_COOKIE_LEN & &
2005-06-18 05:50:08 +02:00
! memcmp ( authentication_cookie , password , password_len ) ) {
2004-11-12 17:39:03 +01:00
goto ok ;
}
} else if ( options - > HashedControlPassword ) {
2004-11-03 20:49:03 +01:00
char expected [ S2K_SPECIFIER_LEN + DIGEST_LEN ] ;
char received [ DIGEST_LEN ] ;
2004-12-13 19:32:29 +01:00
if ( decode_hashed_password ( expected , options - > HashedControlPassword ) < 0 ) {
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Couldn't decode HashedControlPassword: invalid base16 " ) ;
2004-11-03 20:49:03 +01:00
goto err ;
}
2005-06-18 05:50:08 +02:00
secret_to_key ( received , DIGEST_LEN , password , password_len , expected ) ;
2004-11-03 20:49:03 +01:00
if ( ! memcmp ( expected + S2K_SPECIFIER_LEN , received , DIGEST_LEN ) )
goto ok ;
2004-11-07 23:37:59 +01:00
goto err ;
2004-11-12 17:39:03 +01:00
} else {
2005-06-17 22:37:21 +02:00
/* if Tor doesn't demand any stronger authentication, then
* the controller can get in with anything . */
goto ok ;
2004-11-07 12:33:04 +01:00
}
2004-11-03 20:49:03 +01:00
err :
2005-06-17 22:37:21 +02:00
if ( STATE_IS_V0 ( conn - > state ) )
send_control0_error ( conn , ERR_REJECTED_AUTHENTICATION , " Authentication failed " ) ;
2005-06-18 05:50:08 +02:00
else {
tor_free ( password ) ;
2005-10-12 21:45:35 +02:00
if ( used_quoted_string )
connection_write_str_to_buf ( " 515 Authentication failed \r \n " , conn ) ;
else
connection_write_str_to_buf ( " 515 Authentication failed. Maybe you tried a plain text password? If so, the standard requires you put it in double quotes. \r \n " , conn ) ;
2005-06-18 05:50:08 +02:00
}
2004-11-03 02:32:26 +01:00
return 0 ;
2004-11-03 20:49:03 +01:00
ok :
2005-10-19 00:21:29 +02:00
info ( LD_CONTROL , " Authenticated control connection (%d) " , conn - > s ) ;
2004-11-03 20:49:03 +01:00
send_control_done ( conn ) ;
2005-06-17 20:49:55 +02:00
if ( STATE_IS_V0 ( conn - > state ) )
conn - > state = CONTROL_CONN_STATE_OPEN_V0 ;
2005-06-18 05:50:08 +02:00
else {
2005-06-17 20:49:55 +02:00
conn - > state = CONTROL_CONN_STATE_OPEN_V1 ;
2005-06-18 05:50:08 +02:00
tor_free ( password ) ;
}
2004-11-03 20:49:03 +01:00
return 0 ;
2004-11-03 02:32:26 +01:00
}
2005-09-30 22:04:55 +02:00
/** Called when we get a SAVECONF command. Try to flush the current options to
* disk , and report success or failure . */
2004-11-07 23:37:59 +01:00
static int
2005-03-02 21:22:10 +01:00
handle_control_saveconf ( connection_t * conn , uint32_t len ,
2004-11-07 23:37:59 +01:00
const char * body )
{
2005-07-23 03:58:05 +02:00
if ( options_save_current ( ) < 0 ) {
2005-06-17 22:37:21 +02:00
if ( STATE_IS_V0 ( conn - > state ) )
send_control0_error ( conn , ERR_INTERNAL ,
" Unable to write configuration to disk. " ) ;
else
connection_write_str_to_buf ( " 551 Unable to write configuration to disk. " ,
conn ) ;
2004-11-15 05:02:59 +01:00
} else {
send_control_done ( conn ) ;
2004-11-14 21:51:28 +01:00
}
2004-11-07 23:37:59 +01:00
return 0 ;
}
2005-09-30 22:04:55 +02:00
/** Called when we get a SIGNAL command. React to the provided signal, and
* report success or failure . ( If the signal results in a shutdown , success
* may not be reported . ) */
2005-01-05 07:40:47 +01:00
static int
2005-03-02 21:22:10 +01:00
handle_control_signal ( connection_t * conn , uint32_t len ,
2005-01-05 07:40:47 +01:00
const char * body )
{
2005-06-17 22:37:21 +02:00
int sig ;
if ( STATE_IS_V0 ( conn - > state ) ) {
if ( len ! = 1 ) {
send_control0_error ( conn , ERR_SYNTAX ,
" Body of SIGNAL command too long or too short. " ) ;
return 0 ;
} else {
sig = ( uint8_t ) body [ 0 ] ;
}
} else {
int n = 0 ;
char * s ;
while ( body [ n ] & & ! TOR_ISSPACE ( body [ n ] ) )
+ + n ;
s = tor_strndup ( body , n ) ;
if ( ! strcasecmp ( s , " RELOAD " ) | | ! strcasecmp ( s , " HUP " ) )
sig = SIGHUP ;
else if ( ! strcasecmp ( s , " SHUTDOWN " ) | | ! strcasecmp ( s , " INT " ) )
sig = SIGINT ;
else if ( ! strcasecmp ( s , " DUMP " ) | | ! strcasecmp ( s , " USR1 " ) )
sig = SIGUSR1 ;
else if ( ! strcasecmp ( s , " DEBUG " ) | | ! strcasecmp ( s , " USR2 " ) )
sig = SIGUSR2 ;
else if ( ! strcasecmp ( s , " HALT " ) | | ! strcasecmp ( s , " TERM " ) )
sig = SIGTERM ;
else {
connection_printf_to_buf ( conn , " 552 Unrecognized signal code \" %s \" \r \n " ,
body ) ;
sig = - 1 ;
}
tor_free ( s ) ;
if ( sig < 0 )
return 0 ;
}
if ( control_signal_act ( sig ) < 0 ) {
if ( STATE_IS_V0 ( conn - > state ) )
send_control0_error ( conn , ERR_SYNTAX , " Unrecognized signal number. " ) ;
else
connection_write_str_to_buf ( " 551 Internal error acting on signal \r \n " ,
conn ) ;
2005-01-05 07:40:47 +01:00
} else {
send_control_done ( conn ) ;
}
return 0 ;
}
2005-09-30 22:04:55 +02:00
/** Called when we get a MAPADDRESS command; try to bind all listed addresses,
* and report success or failrue . */
2005-02-25 07:16:28 +01:00
static int
2005-03-02 21:22:10 +01:00
handle_control_mapaddress ( connection_t * conn , uint32_t len , const char * body )
2005-02-25 07:16:28 +01:00
{
2005-02-25 21:46:13 +01:00
smartlist_t * elts ;
smartlist_t * lines ;
smartlist_t * reply ;
char * r ;
size_t sz ;
2005-06-19 22:40:41 +02:00
int v0 = STATE_IS_V0 ( conn - > state ) ;
2005-02-25 21:46:13 +01:00
lines = smartlist_create ( ) ;
elts = smartlist_create ( ) ;
reply = smartlist_create ( ) ;
2005-06-19 22:40:41 +02:00
if ( v0 )
smartlist_split_string ( lines , body , " \n " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , 0 ) ;
else
smartlist_split_string ( lines , body , " " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , 0 ) ;
/* XXXX Make errors conformant. */
2005-03-10 07:15:46 +01:00
SMARTLIST_FOREACH ( lines , char * , line ,
2005-02-25 21:46:13 +01:00
{
2005-03-03 07:37:54 +01:00
tor_strlower ( line ) ;
2005-06-19 22:40:41 +02:00
if ( v0 )
smartlist_split_string ( elts , line , " " , 0 , 2 ) ;
else
smartlist_split_string ( elts , line , " = " , 0 , 2 ) ;
2005-02-25 21:46:13 +01:00
if ( smartlist_len ( elts ) = = 2 ) {
const char * from = smartlist_get ( elts , 0 ) ;
const char * to = smartlist_get ( elts , 1 ) ;
if ( ! is_plausible_address ( from ) ) {
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Skipping invalid argument '%s' in MapAddress msg " , from ) ;
2005-02-25 21:46:13 +01:00
} else if ( ! is_plausible_address ( to ) ) {
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Skipping invalid argument '%s' in MapAddress msg " , to ) ;
2005-03-02 20:26:46 +01:00
} else if ( ! strcmp ( from , " . " ) | | ! strcmp ( from , " 0.0.0.0 " ) ) {
2005-10-05 04:06:36 +02:00
const char * address = addressmap_register_virtual_address (
2005-06-19 22:40:41 +02:00
! strcmp ( from , " . " ) ? RESOLVED_TYPE_HOSTNAME : RESOLVED_TYPE_IPV4 ,
2005-03-02 22:02:11 +01:00
tor_strdup ( to ) ) ;
2005-10-05 04:06:36 +02:00
if ( ! address ) {
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL ,
" Unable to allocate address for '%s' in MapAddress msg " ,
safe_str ( line ) ) ;
2005-03-02 20:26:46 +01:00
} else {
2005-10-05 04:06:36 +02:00
size_t anslen = strlen ( address ) + strlen ( to ) + 8 ;
2005-03-02 21:22:10 +01:00
char * ans = tor_malloc ( anslen ) ;
2005-06-19 22:40:41 +02:00
if ( v0 )
2005-10-05 04:06:36 +02:00
tor_snprintf ( ans , anslen , " %s %s " , address , to ) ;
2005-06-19 22:40:41 +02:00
else
2005-10-05 04:06:36 +02:00
tor_snprintf ( ans , anslen , " 250-%s=%s " , address , to ) ;
2005-03-02 20:26:46 +01:00
smartlist_add ( reply , ans ) ;
}
2005-02-25 21:46:13 +01:00
} else {
2005-03-03 07:37:54 +01:00
addressmap_register ( from , tor_strdup ( to ) , 1 ) ;
2005-06-19 22:40:41 +02:00
if ( v0 )
smartlist_add ( reply , tor_strdup ( line ) ) ;
else {
size_t anslen = strlen ( line ) + 8 ;
char * ans = tor_malloc ( anslen ) ;
tor_snprintf ( ans , anslen , " 250-%s " , line ) ;
smartlist_add ( reply , ans ) ;
}
2005-02-25 21:46:13 +01:00
}
} else {
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Skipping MapAddress line with wrong number of items. " ) ;
2005-02-25 21:46:13 +01:00
}
SMARTLIST_FOREACH ( elts , char * , cp , tor_free ( cp ) ) ;
smartlist_clear ( elts ) ;
} ) ;
SMARTLIST_FOREACH ( lines , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( lines ) ;
smartlist_free ( elts ) ;
2005-06-19 22:40:41 +02:00
if ( v0 ) {
r = smartlist_join_strings ( reply , " \n " , 1 , & sz ) ;
send_control_done2 ( conn , r , sz ) ;
} else {
if ( smartlist_len ( reply ) )
( ( char * ) smartlist_get ( reply , smartlist_len ( reply ) - 1 ) ) [ 3 ] = ' ' ;
r = smartlist_join_strings ( reply , " \r \n " , 1 , & sz ) ;
connection_write_to_buf ( r , sz , conn ) ;
}
2005-02-25 21:46:13 +01:00
SMARTLIST_FOREACH ( reply , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( reply ) ;
tor_free ( r ) ;
2005-02-25 07:16:28 +01:00
return 0 ;
}
2005-02-25 07:37:07 +01:00
2005-09-30 22:04:55 +02:00
/** Return a newly allocated string listing all valid GETINFO fields as
* required by GETINFO info / names . */
2005-08-04 21:56:41 +02:00
static char *
list_getinfo_options ( void )
{
return tor_strdup (
" accounting/bytes Number of bytes read/written so far in interval. \n "
" accounting/bytes-left Number of bytes left to read/write in interval. \n "
" accounting/enabled Is accounting currently enabled? \n "
" accounting/hibernating Are we hibernating or awake? \n "
" accounting/interval-end Time when interval ends. \n "
" accounting/interval-start Time when interval starts. \n "
" accounting/interval-wake Time to wake up in this interval. \n "
" addr-mappings/all All current remapped addresses. \n "
" addr-mappings/cache Addresses remapped by DNS cache. \n "
" addr-mappings/configl Addresses remapped from configuration options. \n "
" addr-mappings/control Addresses remapped by a controller. \n "
" circuit-status Status of each current circuit. \n "
" config/names List of configuration options, types, and documentation. \n "
" desc/id/* Server descriptor by hex ID \n "
" desc/name/* Server descriptor by nickname. \n "
" helper-nodes Which nodes will we use as helpers? \n "
" info/names List of GETINFO options, types, and documentation. \n "
" network-status List of hex IDs, nicknames, server statuses. \n "
" orconn-status Status of each current OR connection. \n "
" stream-status Status of each current application stream. \n "
" version The current version of Tor. \n " ) ;
}
2005-03-22 04:27:51 +01:00
/** Lookup the 'getinfo' entry <b>question</b>, and return
* the answer in < b > * answer < / b > ( or NULL if key not recognized ) .
* Return 0 if success , or - 1 if internal error . */
static int
handle_getinfo_helper ( const char * question , char * * answer )
2005-02-25 07:37:07 +01:00
{
2005-03-22 04:27:51 +01:00
* answer = NULL ; /* unrecognized key by default */
2005-02-25 07:37:07 +01:00
if ( ! strcmp ( question , " version " ) ) {
2005-03-22 04:27:51 +01:00
* answer = tor_strdup ( VERSION ) ;
2005-08-10 20:05:20 +02:00
} else if ( ! strcmp ( question , " config-file " ) ) {
* answer = tor_strdup ( get_torrc_fname ( ) ) ;
2005-07-22 16:55:09 +02:00
} else if ( ! strcmpstart ( question , " accounting/ " ) ) {
return accounting_getinfo_helper ( question , answer ) ;
2005-08-04 21:56:41 +02:00
} else if ( ! strcmpstart ( question , " helper-nodes " ) ) {
return helper_nodes_getinfo_helper ( question , answer ) ;
} else if ( ! strcmpstart ( question , " config/ " ) ) {
return config_getinfo_helper ( question , answer ) ;
} else if ( ! strcmp ( question , " info/names " ) ) {
* answer = list_getinfo_options ( ) ;
2005-02-25 07:37:07 +01:00
} else if ( ! strcmpstart ( question , " desc/id/ " ) ) {
2005-02-25 21:46:13 +01:00
routerinfo_t * ri = router_get_by_hexdigest ( question + strlen ( " desc/id/ " ) ) ;
2005-11-05 21:15:27 +01:00
if ( ri & & ri - > cache_info . signed_descriptor )
* answer = tor_strdup ( ri - > cache_info . signed_descriptor ) ;
2005-03-23 09:40:11 +01:00
} else if ( ! strcmpstart ( question , " desc/name/ " ) ) {
2005-10-05 00:23:31 +02:00
routerinfo_t * ri = router_get_by_nickname ( question + strlen ( " desc/name/ " ) , 1 ) ;
2005-11-05 21:15:27 +01:00
if ( ri & & ri - > cache_info . signed_descriptor )
* answer = tor_strdup ( ri - > cache_info . signed_descriptor ) ;
2005-08-13 04:20:00 +02:00
} else if ( ! strcmpstart ( question , " unregistered-servers- " ) ) {
* answer = dirserver_getinfo_unregistered ( question +
strlen ( " unregistered-servers- " ) ) ;
2005-03-22 04:27:51 +01:00
} else if ( ! strcmp ( question , " network-status " ) ) {
2005-10-18 19:43:54 +02:00
routerlist_t * routerlist = router_get_routerlist ( ) ;
2005-03-23 20:15:10 +01:00
if ( ! routerlist | | ! routerlist - > routers | |
2005-05-02 23:22:31 +02:00
list_server_status ( routerlist - > routers , answer ) < 0 ) {
2005-03-22 04:27:51 +01:00
return - 1 ;
2005-03-23 20:15:10 +01:00
}
2005-06-19 22:40:41 +02:00
} else if ( ! strcmp ( question , " circuit-status " ) ) {
circuit_t * circ ;
smartlist_t * status = smartlist_create ( ) ;
for ( circ = _circuit_get_global_list ( ) ; circ ; circ = circ - > next ) {
char * s , * path ;
size_t slen ;
const char * state ;
if ( ! CIRCUIT_IS_ORIGIN ( circ ) | | circ - > marked_for_close )
continue ;
path = circuit_list_path ( circ , 0 ) ;
if ( circ - > state = = CIRCUIT_STATE_OPEN )
state = " BUILT " ;
else if ( strlen ( path ) )
state = " EXTENDED " ;
else
state = " LAUNCHED " ;
slen = strlen ( path ) + strlen ( state ) + 20 ;
s = tor_malloc ( slen + 1 ) ;
tor_snprintf ( s , slen , " %lu %s %s " , ( unsigned long ) circ - > global_identifier ,
state , path ) ;
smartlist_add ( status , s ) ;
tor_free ( path ) ;
}
2005-08-09 07:16:29 +02:00
* answer = smartlist_join_strings ( status , " \r \n " , 0 , NULL ) ;
2005-06-19 22:40:41 +02:00
SMARTLIST_FOREACH ( status , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( status ) ;
} else if ( ! strcmp ( question , " stream-status " ) ) {
connection_t * * conns ;
int n_conns , i ;
char buf [ 256 ] ;
smartlist_t * status = smartlist_create ( ) ;
get_connection_array ( & conns , & n_conns ) ;
for ( i = 0 ; i < n_conns ; + + i ) {
const char * state ;
char * s ;
size_t slen ;
circuit_t * circ ;
if ( conns [ i ] - > type ! = CONN_TYPE_AP | |
conns [ i ] - > marked_for_close | |
conns [ i ] - > state = = AP_CONN_STATE_SOCKS_WAIT )
continue ;
switch ( conns [ i ] - > state )
{
case AP_CONN_STATE_CONTROLLER_WAIT :
case AP_CONN_STATE_CIRCUIT_WAIT :
if ( conns [ i ] - > socks_request & &
conns [ i ] - > socks_request - > command = = SOCKS_COMMAND_RESOLVE )
state = " NEWRESOLVE " ;
else
state = " NEW " ;
break ;
case AP_CONN_STATE_RENDDESC_WAIT :
case AP_CONN_STATE_CONNECT_WAIT :
state = " SENTCONNECT " ; break ;
case AP_CONN_STATE_RESOLVE_WAIT :
state = " SENTRESOLVE " ; break ;
case AP_CONN_STATE_OPEN :
state = " SUCCEEDED " ; break ;
default :
2005-10-24 21:39:45 +02:00
warn ( LD_BUG , " Asked for stream in unknown state %d " ,
2005-06-19 22:40:41 +02:00
conns [ i ] - > state ) ;
continue ;
}
circ = circuit_get_by_edge_conn ( conns [ i ] ) ;
write_stream_target_to_buf ( conns [ i ] , buf , sizeof ( buf ) ) ;
slen = strlen ( buf ) + strlen ( state ) + 32 ;
s = tor_malloc ( slen + 1 ) ;
tor_snprintf ( s , slen , " %lu %s %lu %s " ,
( unsigned long ) conns [ i ] - > global_identifier , state ,
circ ? ( unsigned long ) circ - > global_identifier : 0ul ,
buf ) ;
smartlist_add ( status , s ) ;
}
2005-08-09 07:16:29 +02:00
* answer = smartlist_join_strings ( status , " \r \n " , 0 , NULL ) ;
2005-06-19 22:40:41 +02:00
SMARTLIST_FOREACH ( status , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( status ) ;
} else if ( ! strcmp ( question , " orconn-status " ) ) {
connection_t * * conns ;
int n_conns , i ;
smartlist_t * status = smartlist_create ( ) ;
get_connection_array ( & conns , & n_conns ) ;
for ( i = 0 ; i < n_conns ; + + i ) {
const char * state ;
char * s ;
size_t slen ;
if ( conns [ i ] - > type ! = CONN_TYPE_OR | | conns [ i ] - > marked_for_close )
continue ;
if ( conns [ i ] - > state = = OR_CONN_STATE_OPEN )
state = " CONNECTED " ;
else
state = " LAUNCHED " ;
slen = strlen ( conns [ i ] - > nickname ) + strlen ( state ) + 2 ;
s = tor_malloc ( slen + 1 ) ;
tor_snprintf ( s , slen , " %s %s " , conns [ i ] - > nickname , state ) ;
smartlist_add ( status , s ) ;
}
2005-08-09 07:16:29 +02:00
* answer = smartlist_join_strings ( status , " \r \n " , 0 , NULL ) ;
2005-06-19 22:40:41 +02:00
SMARTLIST_FOREACH ( status , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( status ) ;
2005-03-03 07:37:54 +01:00
} else if ( ! strcmpstart ( question , " addr-mappings/ " ) ) {
time_t min_e , max_e ;
smartlist_t * mappings ;
if ( ! strcmp ( question , " addr-mappings/all " ) ) {
min_e = 0 ; max_e = TIME_MAX ;
} else if ( ! strcmp ( question , " addr-mappings/cache " ) ) {
min_e = 2 ; max_e = TIME_MAX ;
} else if ( ! strcmp ( question , " addr-mappings/config " ) ) {
min_e = 0 ; max_e = 0 ;
} else if ( ! strcmp ( question , " addr-mappings/control " ) ) {
min_e = 1 ; max_e = 1 ;
} else {
2005-03-22 04:27:51 +01:00
return 0 ;
2005-03-03 07:37:54 +01:00
}
mappings = smartlist_create ( ) ;
addressmap_get_mappings ( mappings , min_e , max_e ) ;
2005-08-09 07:16:29 +02:00
* answer = smartlist_join_strings ( mappings , " \n " , 0 , NULL ) ;
2005-03-03 07:37:54 +01:00
SMARTLIST_FOREACH ( mappings , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( mappings ) ;
2005-02-25 07:37:07 +01:00
}
2005-03-22 04:27:51 +01:00
return 0 ;
2005-02-25 07:37:07 +01:00
}
2005-09-30 22:04:55 +02:00
/** Called when we receive a GETINFO command. Try to fetch all requested
* information , and reply with information or error message . */
2005-02-25 07:16:28 +01:00
static int
2005-03-02 21:22:10 +01:00
handle_control_getinfo ( connection_t * conn , uint32_t len , const char * body )
2005-02-25 07:16:28 +01:00
{
2005-02-25 07:37:07 +01:00
smartlist_t * questions = NULL ;
smartlist_t * answers = NULL ;
2005-06-18 04:39:25 +02:00
smartlist_t * unrecognized = NULL ;
2005-08-13 03:55:23 +02:00
char * msg = NULL , * ans = NULL ;
2005-02-25 07:37:07 +01:00
size_t msg_len ;
2005-06-18 04:39:25 +02:00
int v0 = STATE_IS_V0 ( conn - > state ) ;
2005-02-25 07:37:07 +01:00
questions = smartlist_create ( ) ;
2005-06-18 04:39:25 +02:00
if ( v0 )
smartlist_split_string ( questions , body , " \n " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , 0 ) ;
else
smartlist_split_string ( questions , body , " " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , 0 ) ;
2005-02-25 07:37:07 +01:00
answers = smartlist_create ( ) ;
2005-06-18 04:39:25 +02:00
unrecognized = smartlist_create ( ) ;
2005-02-25 07:37:07 +01:00
SMARTLIST_FOREACH ( questions , const char * , q ,
{
2005-03-22 04:27:51 +01:00
if ( handle_getinfo_helper ( q , & ans ) < 0 ) {
2005-06-18 04:39:25 +02:00
if ( v0 )
send_control0_error ( conn , ERR_INTERNAL , body ) ;
else
connection_write_str_to_buf ( " 551 Internal error \r \n " , conn ) ;
2005-02-25 07:37:07 +01:00
goto done ;
}
2005-06-18 04:39:25 +02:00
if ( ! ans ) {
if ( v0 ) {
send_control0_error ( conn , ERR_UNRECOGNIZED_CONFIG_KEY , body ) ;
goto done ;
} else
smartlist_add ( unrecognized , ( char * ) q ) ;
} else {
smartlist_add ( answers , tor_strdup ( q ) ) ;
smartlist_add ( answers , ans ) ;
}
2005-02-25 07:37:07 +01:00
} ) ;
2005-06-18 04:39:25 +02:00
if ( smartlist_len ( unrecognized ) ) {
int i ;
tor_assert ( ! v0 ) ;
2005-06-19 22:40:41 +02:00
for ( i = 0 ; i < smartlist_len ( unrecognized ) - 1 ; + + i )
2005-06-18 04:39:25 +02:00
connection_printf_to_buf ( conn ,
2005-06-19 22:40:41 +02:00
" 552-Unrecognized key \" %s \" \r \n " ,
2005-06-18 04:39:25 +02:00
( char * ) smartlist_get ( unrecognized , i ) ) ;
connection_printf_to_buf ( conn ,
2005-06-19 22:40:41 +02:00
" 552 Unrecognized key \" %s \" \r \n " ,
( char * ) smartlist_get ( unrecognized , i ) ) ;
2005-06-18 04:39:25 +02:00
goto done ;
}
2005-02-25 07:37:07 +01:00
2005-06-18 04:39:25 +02:00
if ( v0 ) {
msg = smartlist_join_strings2 ( answers , " \0 " , 1 , 1 , & msg_len ) ;
tor_assert ( msg_len > 0 ) ; /* it will at least be terminated */
send_control0_message ( conn , CONTROL0_CMD_INFOVALUE ,
msg_len , msg ) ;
2005-11-17 22:45:38 +01:00
} else {
2005-06-18 04:39:25 +02:00
int i ;
for ( i = 0 ; i < smartlist_len ( answers ) ; i + = 2 ) {
char * k = smartlist_get ( answers , i ) ;
char * v = smartlist_get ( answers , i + 1 ) ;
/*XXXX Not an adequate test! XXXX011 */
if ( ! strchr ( v , ' \n ' ) & & ! strchr ( v , ' \r ' ) ) {
2005-06-19 22:40:41 +02:00
connection_printf_to_buf ( conn , " 250-%s= " , k ) ;
connection_write_str_to_buf ( v , conn ) ;
connection_write_str_to_buf ( " \r \n " , conn ) ;
2005-06-18 04:39:25 +02:00
} else {
char * esc = NULL ;
size_t len ;
len = write_escaped_data ( v , strlen ( v ) , 1 , & esc ) ;
2005-06-19 22:40:41 +02:00
connection_printf_to_buf ( conn , " 250+%s= \r \n " , k ) ;
2005-06-18 04:39:25 +02:00
connection_write_to_buf ( esc , len , conn ) ;
tor_free ( esc ) ;
}
}
connection_write_str_to_buf ( " 250 OK \r \n " , conn ) ;
}
2005-02-25 07:37:07 +01:00
done :
2005-10-25 09:02:13 +02:00
if ( answers ) {
SMARTLIST_FOREACH ( answers , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( answers ) ;
}
if ( questions ) {
SMARTLIST_FOREACH ( questions , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( questions ) ;
}
2005-06-18 04:39:25 +02:00
smartlist_free ( unrecognized ) ;
2005-02-25 07:37:07 +01:00
tor_free ( msg ) ;
2005-02-25 07:16:28 +01:00
return 0 ;
}
2005-06-11 20:52:12 +02:00
2005-10-05 04:09:27 +02:00
/** Called when we get an EXTENDCIRCUIT message. Try to extend the listed
* circuit , and report success or failure . */
2005-02-25 07:16:28 +01:00
static int
2005-03-02 21:22:10 +01:00
handle_control_extendcircuit ( connection_t * conn , uint32_t len ,
2005-02-25 07:16:28 +01:00
const char * body )
{
2005-06-18 05:09:43 +02:00
smartlist_t * router_nicknames = NULL , * routers = NULL ;
2005-03-19 07:05:55 +01:00
uint32_t circ_id ;
2005-06-18 05:09:43 +02:00
circuit_t * circ = NULL ;
int zero_circ , v0 ;
2005-03-24 02:08:25 +01:00
char reply [ 4 ] ;
2005-03-19 07:05:55 +01:00
2005-06-18 05:09:43 +02:00
v0 = STATE_IS_V0 ( conn - > state ) ;
2005-03-19 07:05:55 +01:00
router_nicknames = smartlist_create ( ) ;
2005-06-18 05:09:43 +02:00
if ( v0 ) {
if ( len < 5 ) {
send_control0_error ( conn , ERR_SYNTAX , " extendcircuit message too short " ) ;
goto done ;
}
smartlist_split_string ( router_nicknames , body + 4 , " , " , 0 , 0 ) ;
circ_id = ntohl ( get_uint32 ( body ) ) ;
if ( ! circ_id ) {
/* start a new circuit */
zero_circ = 1 ;
} else {
circ = circuit_get_by_global_id ( circ_id ) ;
zero_circ = 0 ;
if ( ! circ ) {
send_control0_error ( conn , ERR_NO_CIRC ,
" No circuit found with given ID " ) ;
2005-03-19 07:05:55 +01:00
goto done ;
}
2005-06-18 05:09:43 +02:00
}
2005-07-17 23:06:00 +02:00
} else { /* v1 */
2005-06-18 05:09:43 +02:00
smartlist_t * args ;
args = smartlist_create ( ) ;
smartlist_split_string ( args , body , " " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , 0 ) ;
if ( smartlist_len ( args ) < 2 )
2005-07-17 23:06:00 +02:00
connection_printf_to_buf ( conn , " 512 Missing argument to EXTENDCIRCUIT \r \n " ) ;
2005-06-18 05:09:43 +02:00
2005-07-17 23:06:00 +02:00
zero_circ = ! strcmp ( " 0 " , ( char * ) smartlist_get ( args , 0 ) ) ;
if ( ! zero_circ & & ! ( circ = get_circ ( smartlist_get ( args , 0 ) ) ) ) {
2005-06-18 05:09:43 +02:00
connection_printf_to_buf ( conn , " 552 Unknown circuit \" %s \" \r \n " ,
2005-07-17 23:06:00 +02:00
( char * ) smartlist_get ( args , 0 ) ) ;
2005-06-18 05:09:43 +02:00
}
2005-07-17 23:06:00 +02:00
smartlist_split_string ( router_nicknames , smartlist_get ( args , 1 ) , " , " , 0 , 0 ) ;
2005-06-18 05:09:43 +02:00
SMARTLIST_FOREACH ( args , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( args ) ;
2005-07-17 23:06:00 +02:00
if ( ! zero_circ & & ! circ ) {
goto done ;
}
2005-03-22 01:42:38 +01:00
}
2005-06-18 05:09:43 +02:00
routers = smartlist_create ( ) ;
SMARTLIST_FOREACH ( router_nicknames , const char * , n ,
{
2005-10-05 00:23:31 +02:00
routerinfo_t * r = router_get_by_nickname ( n , 1 ) ;
2005-06-18 05:09:43 +02:00
if ( ! r ) {
if ( v0 )
send_control0_error ( conn , ERR_NO_ROUTER , n ) ;
else
connection_printf_to_buf ( conn , " 552 No such router \" %s \" \r \n " , n ) ;
2005-03-22 01:42:38 +01:00
goto done ;
}
2005-06-18 05:09:43 +02:00
smartlist_add ( routers , r ) ;
} ) ;
if ( ! smartlist_len ( routers ) ) {
if ( v0 )
send_control0_error ( conn , ERR_SYNTAX , " No router names provided " ) ;
else
connection_write_str_to_buf ( " 512 No router names provided \r \n " , conn ) ;
goto done ;
2005-03-22 01:42:38 +01:00
}
2005-07-17 23:22:18 +02:00
if ( zero_circ ) {
2005-07-17 23:13:36 +02:00
/* start a new circuit */
circ = circuit_init ( CIRCUIT_PURPOSE_C_GENERAL , 0 , 0 , 0 ) ;
}
2005-03-22 01:42:38 +01:00
/* now circ refers to something that is ready to be extended */
2005-03-19 07:05:55 +01:00
SMARTLIST_FOREACH ( routers , routerinfo_t * , r ,
2005-06-18 05:09:43 +02:00
{
2005-06-29 23:46:55 +02:00
extend_info_t * info = extend_info_from_router ( r ) ;
circuit_append_new_exit ( circ , info ) ;
extend_info_free ( info ) ;
2005-06-18 05:09:43 +02:00
} ) ;
2005-03-22 01:42:38 +01:00
/* now that we've populated the cpath, start extending */
2005-06-18 05:09:43 +02:00
if ( zero_circ ) {
2005-03-22 01:42:38 +01:00
if ( circuit_handle_first_hop ( circ ) < 0 ) {
circuit_mark_for_close ( circ ) ;
2005-06-18 05:09:43 +02:00
if ( v0 )
send_control0_error ( conn , ERR_INTERNAL , " couldn't start circuit " ) ;
else
connection_write_str_to_buf ( " 551 Couldn't start circuit \r \n " , conn ) ;
2005-03-24 02:08:25 +01:00
goto done ;
2005-03-22 01:42:38 +01:00
}
} else {
if ( circ - > state = = CIRCUIT_STATE_OPEN ) {
circ - > state = CIRCUIT_STATE_BUILDING ;
if ( circuit_send_next_onion_skin ( circ ) < 0 ) {
2005-10-19 00:21:29 +02:00
info ( LD_CONTROL , " send_next_onion_skin failed; circuit marked for closing. " ) ;
2005-03-22 01:42:38 +01:00
circuit_mark_for_close ( circ ) ;
2005-06-18 05:09:43 +02:00
if ( v0 )
send_control0_error ( conn , ERR_INTERNAL , " couldn't send onion skin " ) ;
else
connection_write_str_to_buf ( " 551 Couldn't send onion skinr \n " , conn ) ;
2005-03-24 02:08:25 +01:00
goto done ;
2005-03-22 01:42:38 +01:00
}
}
}
2005-03-19 07:05:55 +01:00
2005-06-18 05:09:43 +02:00
if ( v0 ) {
set_uint32 ( reply , htonl ( circ - > global_identifier ) ) ;
send_control_done2 ( conn , reply , sizeof ( reply ) ) ;
} else {
connection_printf_to_buf ( conn , " 250 EXTENDED %lu \r \n " ,
( unsigned long ) circ - > global_identifier ) ;
}
2005-03-19 07:05:55 +01:00
done :
SMARTLIST_FOREACH ( router_nicknames , char * , n , tor_free ( n ) ) ;
smartlist_free ( router_nicknames ) ;
2005-07-17 23:06:00 +02:00
if ( routers )
smartlist_free ( routers ) ;
2005-02-25 07:16:28 +01:00
return 0 ;
}
2005-06-11 20:52:12 +02:00
2005-10-05 04:09:27 +02:00
/** Called when we get an ATTACHSTREAM message. Try to attach the requested
* stream , and report success or failure . */
2005-06-11 20:52:12 +02:00
static int
handle_control_attachstream ( connection_t * conn , uint32_t len ,
2005-06-18 05:09:43 +02:00
const char * body )
2005-02-25 07:16:28 +01:00
{
2005-06-18 05:09:43 +02:00
connection_t * ap_conn = NULL ;
circuit_t * circ = NULL ;
int zero_circ ;
2005-03-12 05:22:01 +01:00
2005-06-18 05:09:43 +02:00
if ( STATE_IS_V0 ( conn - > state ) ) {
uint32_t conn_id ;
uint32_t circ_id ;
if ( len < 8 ) {
send_control0_error ( conn , ERR_SYNTAX , " attachstream message too short " ) ;
return 0 ;
}
2005-03-12 05:22:01 +01:00
2005-06-18 05:09:43 +02:00
conn_id = ntohl ( get_uint32 ( body ) ) ;
circ_id = ntohl ( get_uint32 ( body + 4 ) ) ;
zero_circ = circ_id = = 0 ;
2005-03-12 05:22:01 +01:00
2005-06-18 05:09:43 +02:00
if ( ! ( ap_conn = connection_get_by_global_id ( conn_id ) ) ) {
send_control0_error ( conn , ERR_NO_STREAM ,
" No connection found with given ID " ) ;
return 0 ;
}
if ( circ_id & & ! ( circ = circuit_get_by_global_id ( circ_id ) ) ) {
send_control0_error ( conn , ERR_NO_CIRC , " No circuit found with given ID " ) ;
return 0 ;
}
} else {
smartlist_t * args ;
args = smartlist_create ( ) ;
smartlist_split_string ( args , body , " " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , 0 ) ;
if ( smartlist_len ( args ) < 2 )
connection_printf_to_buf ( conn , " 512 Missing argument to ATTACHSTREAM \r \n " ) ;
zero_circ = ! strcmp ( " 0 " , ( char * ) smartlist_get ( args , 1 ) ) ;
if ( ! ( ap_conn = get_stream ( smartlist_get ( args , 0 ) ) ) ) {
connection_printf_to_buf ( conn , " 552 Unknown stream \" %s \" \r \n " ,
( char * ) smartlist_get ( args , 0 ) ) ;
} else if ( ! zero_circ & & ! ( circ = get_circ ( smartlist_get ( args , 1 ) ) ) ) {
connection_printf_to_buf ( conn , " 552 Unknown circuit \" %s \" \r \n " ,
( char * ) smartlist_get ( args , 1 ) ) ;
}
SMARTLIST_FOREACH ( args , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( args ) ;
if ( ! ap_conn | | ( ! zero_circ & & ! circ ) )
return 0 ;
2005-03-12 05:22:01 +01:00
}
2005-06-18 05:09:43 +02:00
2005-03-12 05:22:01 +01:00
if ( ap_conn - > state ! = AP_CONN_STATE_CONTROLLER_WAIT ) {
2005-06-18 05:09:43 +02:00
if ( STATE_IS_V0 ( conn - > state ) ) {
send_control0_error ( conn , ERR_NO_STREAM ,
" Connection is not managed by controller. " ) ;
} else {
connection_write_str_to_buf (
" 555 Connection is not managed by controller. \r \n " ,
conn ) ;
}
2005-03-12 05:22:01 +01:00
return 0 ;
}
2005-06-18 05:09:43 +02:00
if ( zero_circ ) {
2005-03-12 05:22:01 +01:00
ap_conn - > state = AP_CONN_STATE_CIRCUIT_WAIT ;
if ( connection_ap_handshake_attach_circuit ( ap_conn ) < 0 )
2005-04-03 00:11:24 +02:00
connection_mark_unattached_ap ( ap_conn , END_STREAM_REASON_CANT_ATTACH ) ;
2005-03-24 07:28:21 +01:00
send_control_done ( conn ) ;
2005-03-12 05:22:01 +01:00
return 0 ;
}
2005-03-24 07:18:59 +01:00
if ( circ - > state ! = CIRCUIT_STATE_OPEN ) {
2005-06-18 05:09:43 +02:00
if ( STATE_IS_V0 ( conn - > state ) )
send_control0_error ( conn , ERR_INTERNAL , " Refuse to attach stream to non-open circ. " ) ;
else
connection_write_str_to_buf (
" 551 Can't attach stream to non-open circuit \r \n " ,
conn ) ;
2005-03-24 07:18:59 +01:00
return 0 ;
}
2005-03-12 05:22:01 +01:00
if ( connection_ap_handshake_attach_chosen_circuit ( ap_conn , circ ) ! = 1 ) {
2005-06-18 05:09:43 +02:00
if ( STATE_IS_V0 ( conn - > state ) )
send_control0_error ( conn , ERR_INTERNAL , " Unable to attach stream. " ) ;
else
connection_write_str_to_buf ( " 551 Unable to attach stream \r \n " , conn ) ;
2005-03-12 05:22:01 +01:00
return 0 ;
}
2005-03-19 07:05:55 +01:00
send_control_done ( conn ) ;
2005-02-25 07:16:28 +01:00
return 0 ;
}
2005-06-11 20:52:12 +02:00
2005-10-05 04:09:27 +02:00
/** Called when we get a POSTDESCRIPTOR message. Try to learn the provided
* descriptor , and report success or failure . */
2005-02-25 07:16:28 +01:00
static int
2005-03-02 21:22:10 +01:00
handle_control_postdescriptor ( connection_t * conn , uint32_t len ,
2005-02-25 07:16:28 +01:00
const char * body )
{
2005-06-18 04:39:25 +02:00
char * desc ;
int v0 = STATE_IS_V0 ( conn - > state ) ;
2005-06-20 11:38:29 +02:00
const char * msg = NULL ;
2005-06-18 04:39:25 +02:00
if ( v0 )
desc = ( char * ) body ;
else {
const char * cp = memchr ( body , ' \n ' , len ) ;
tor_assert ( cp ) ;
read_escaped_data ( cp , len - ( cp - body ) , 1 , & desc ) ;
}
switch ( router_load_single_router ( desc , & msg ) ) {
2005-04-03 00:02:13 +02:00
case - 1 :
2005-06-18 04:39:25 +02:00
if ( ! msg ) msg = " Could not parse descriptor " ;
if ( v0 )
send_control0_error ( conn , ERR_SYNTAX , msg ) ;
else
connection_printf_to_buf ( conn , " 554 %s \r \n " , msg ) ;
2005-04-03 00:02:13 +02:00
break ;
case 0 :
2005-06-18 04:39:25 +02:00
if ( ! msg ) msg = " Descriptor not added " ;
if ( v0 )
send_control_done2 ( conn , msg , 0 ) ;
else
connection_printf_to_buf ( conn , " 251 %s \r \n " , msg ) ;
2005-04-03 00:02:13 +02:00
break ;
case 1 :
send_control_done ( conn ) ;
2005-04-11 04:52:09 +02:00
break ;
2005-02-25 21:46:13 +01:00
}
2005-06-18 05:09:43 +02:00
if ( ! v0 )
tor_free ( desc ) ;
2005-02-25 07:16:28 +01:00
return 0 ;
}
2005-06-11 20:52:12 +02:00
2005-09-30 22:04:55 +02:00
/** Called when we receive a REDIRECTSTERAM command. Try to change the target
2005-11-18 12:32:59 +01:00
* address of the named AP stream , and report success or failure . */
2005-03-19 07:05:55 +01:00
static int
handle_control_redirectstream ( connection_t * conn , uint32_t len ,
const char * body )
{
2005-06-18 04:39:25 +02:00
connection_t * ap_conn = NULL ;
2005-03-19 07:05:55 +01:00
uint32_t conn_id ;
2005-06-18 04:39:25 +02:00
char * new_addr = NULL ;
2005-11-18 12:32:59 +01:00
uint16_t new_port = 0 ;
2005-06-18 04:39:25 +02:00
if ( STATE_IS_V0 ( conn - > state ) ) {
if ( len < 6 ) {
send_control0_error ( conn , ERR_SYNTAX , " redirectstream message too short " ) ;
return 0 ;
}
conn_id = ntohl ( get_uint32 ( body ) ) ;
2005-03-19 07:05:55 +01:00
2005-06-18 04:39:25 +02:00
if ( ! ( ap_conn = connection_get_by_global_id ( conn_id ) )
| | ap_conn - > state ! = CONN_TYPE_AP
| | ! ap_conn - > socks_request ) {
send_control0_error ( conn , ERR_NO_STREAM ,
" No AP connection found with given ID " ) ;
return 0 ;
}
new_addr = tor_strdup ( body + 4 ) ;
} else {
smartlist_t * args ;
args = smartlist_create ( ) ;
smartlist_split_string ( args , body , " " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , 0 ) ;
2005-11-18 12:32:59 +01:00
if ( smartlist_len ( args ) < 2 )
2005-06-18 04:39:25 +02:00
connection_printf_to_buf ( conn , " 512 Missing argument to REDIRECTSTREAM \r \n " ) ;
else if ( ! ( ap_conn = get_stream ( smartlist_get ( args , 0 ) ) )
| | ! ap_conn - > socks_request ) {
connection_printf_to_buf ( conn , " 552 Unknown stream \" %s \" \r \n " ,
( char * ) smartlist_get ( args , 0 ) ) ;
} else {
2005-11-18 12:32:59 +01:00
int ok ;
if ( smartlist_len ( args ) < 3 ) { /* they included a port too */
new_port = ( uint16_t ) tor_parse_ulong ( smartlist_get ( args , 2 ) ,
10 , 1 , 65535 , & ok , NULL ) ;
}
if ( ! ok ) {
connection_printf_to_buf ( conn , " 512 Cannot parse port \" %s \" \r \n " ,
( char * ) smartlist_get ( args , 2 ) ) ;
} else {
new_addr = tor_strdup ( smartlist_get ( args , 1 ) ) ;
}
2005-06-18 04:39:25 +02:00
}
SMARTLIST_FOREACH ( args , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( args ) ;
if ( ! new_addr )
return 0 ;
2005-03-19 07:05:55 +01:00
}
2005-06-18 04:39:25 +02:00
strlcpy ( ap_conn - > socks_request - > address , new_addr ,
sizeof ( ap_conn - > socks_request - > address ) ) ;
2005-11-18 12:32:59 +01:00
if ( new_port )
ap_conn - > socks_request - > port = new_port ;
2005-06-18 04:39:25 +02:00
tor_free ( new_addr ) ;
2005-03-19 07:05:55 +01:00
send_control_done ( conn ) ;
return 0 ;
}
2005-06-11 20:52:12 +02:00
2005-09-30 22:04:55 +02:00
/** Called when we get a CLOSESTREAM command; try to close the named stream
* and report success or failure . */
2005-03-22 20:36:38 +01:00
static int
handle_control_closestream ( connection_t * conn , uint32_t len ,
const char * body )
{
2005-06-18 04:39:25 +02:00
connection_t * ap_conn = NULL ;
uint8_t reason = 0 ;
2005-03-22 20:36:38 +01:00
2005-06-18 04:39:25 +02:00
if ( STATE_IS_V0 ( conn - > state ) ) {
uint32_t conn_id ;
if ( len < 6 ) {
send_control0_error ( conn , ERR_SYNTAX , " closestream message too short " ) ;
return 0 ;
}
2005-03-22 20:36:38 +01:00
2005-06-18 04:39:25 +02:00
conn_id = ntohl ( get_uint32 ( body ) ) ;
reason = * ( uint8_t * ) ( body + 4 ) ;
2005-03-22 20:36:38 +01:00
2005-06-18 04:39:25 +02:00
if ( ! ( ap_conn = connection_get_by_global_id ( conn_id ) )
| | ap_conn - > state ! = CONN_TYPE_AP
| | ! ap_conn - > socks_request ) {
send_control0_error ( conn , ERR_NO_STREAM ,
" No AP connection found with given ID " ) ;
return 0 ;
}
} else {
smartlist_t * args ;
int ok ;
args = smartlist_create ( ) ;
smartlist_split_string ( args , body , " " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , 0 ) ;
if ( smartlist_len ( args ) < 2 )
2005-07-17 23:36:33 +02:00
connection_printf_to_buf ( conn , " 512 Missing argument to CLOSESTREAM \r \n " ) ;
2005-06-18 04:39:25 +02:00
else if ( ! ( ap_conn = get_stream ( smartlist_get ( args , 0 ) ) ) )
connection_printf_to_buf ( conn , " 552 Unknown stream \" %s \" \r \n " ,
( char * ) smartlist_get ( args , 0 ) ) ;
else {
reason = ( uint8_t ) tor_parse_ulong ( smartlist_get ( args , 1 ) , 10 , 0 , 255 ,
& ok , NULL ) ;
if ( ! ok ) {
connection_printf_to_buf ( conn , " 552 Unrecognized reason \" %s \" \r \n " ,
( char * ) smartlist_get ( args , 1 ) ) ;
ap_conn = NULL ;
}
}
SMARTLIST_FOREACH ( args , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( args ) ;
if ( ! ap_conn )
return 0 ;
2005-03-22 20:36:38 +01:00
}
2005-06-18 04:39:25 +02:00
2005-04-03 00:11:24 +02:00
connection_mark_unattached_ap ( ap_conn , reason ) ;
2005-03-22 20:36:38 +01:00
send_control_done ( conn ) ;
return 0 ;
}
2005-03-27 06:55:13 +02:00
2005-09-30 22:04:55 +02:00
/** Called when we get a CLOSECIRCUIT command; try to close the named circuit
* and report success or failure . */
2005-03-22 20:36:38 +01:00
static int
handle_control_closecircuit ( connection_t * conn , uint32_t len ,
const char * body )
{
2005-06-18 04:39:25 +02:00
circuit_t * circ = NULL ;
int safe = 0 ;
2005-03-19 07:05:55 +01:00
2005-06-18 04:39:25 +02:00
if ( STATE_IS_V0 ( conn - > state ) ) {
uint32_t circ_id ;
if ( len < 5 ) {
send_control0_error ( conn , ERR_SYNTAX , " closecircuit message too short " ) ;
return 0 ;
}
circ_id = ntohl ( get_uint32 ( body ) ) ;
safe = ( * ( uint8_t * ) ( body + 4 ) ) & 1 ;
2005-03-22 20:36:38 +01:00
2005-06-18 04:39:25 +02:00
if ( ! ( circ = circuit_get_by_global_id ( circ_id ) ) ) {
send_control0_error ( conn , ERR_NO_CIRC ,
" No circuit found with given ID " ) ;
return 0 ;
}
} else {
smartlist_t * args ;
args = smartlist_create ( ) ;
smartlist_split_string ( args , body , " " ,
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK , 0 ) ;
if ( smartlist_len ( args ) < 1 )
connection_printf_to_buf ( conn , " 512 Missing argument to CLOSECIRCUIT \r \n " ) ;
else if ( ! ( circ = get_circ ( smartlist_get ( args , 0 ) ) ) )
connection_printf_to_buf ( conn , " 552 Unknown circuit \" %s \" \r \n " ,
( char * ) smartlist_get ( args , 0 ) ) ;
else {
int i ;
for ( i = 1 ; i < smartlist_len ( args ) ; + + i ) {
if ( ! strcasecmp ( smartlist_get ( args , i ) , " IfUnused " ) )
safe = 1 ;
else
2005-10-19 00:21:29 +02:00
info ( LD_CONTROL , " Skipping unknown option %s " ,
( char * ) smartlist_get ( args , i ) ) ;
2005-06-18 04:39:25 +02:00
}
}
SMARTLIST_FOREACH ( args , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( args ) ;
if ( ! circ )
return 0 ;
2005-03-22 20:36:38 +01:00
}
if ( ! safe | | ! circ - > p_streams ) {
circuit_mark_for_close ( circ ) ;
}
send_control_done ( conn ) ;
return 0 ;
}
2005-02-25 07:16:28 +01:00
2005-09-30 22:04:55 +02:00
/** Called when we get a v0 FRAGMENTHEADER or FRAGMENT command; try to append
* the data to conn - > incoming_cmd , setting conn - > incoming_ ( type | len | cur_len )
* as appropriate . If the command is malformed , drop it and all pending
* fragments and report failure .
*/
2005-04-28 00:01:34 +02:00
static int
handle_control_fragments ( connection_t * conn , uint16_t command_type ,
uint32_t body_len , char * body )
{
2005-06-17 20:49:55 +02:00
if ( command_type = = CONTROL0_CMD_FRAGMENTHEADER ) {
2005-04-28 00:01:34 +02:00
if ( conn - > incoming_cmd ) {
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Dropping incomplete fragmented command " ) ;
2005-04-28 00:01:34 +02:00
tor_free ( conn - > incoming_cmd ) ;
}
if ( body_len < 6 ) {
2005-06-17 20:49:55 +02:00
send_control0_error ( conn , ERR_SYNTAX , " FRAGMENTHEADER too short. " ) ;
2005-04-28 00:01:34 +02:00
return 0 ;
}
conn - > incoming_cmd_type = ntohs ( get_uint16 ( body ) ) ;
conn - > incoming_cmd_len = ntohl ( get_uint32 ( body + 2 ) ) ;
conn - > incoming_cmd_cur_len = 0 ;
conn - > incoming_cmd = tor_malloc ( conn - > incoming_cmd_len ) ;
body + = 6 ;
body_len - = 6 ;
2005-06-17 20:49:55 +02:00
} else if ( command_type = = CONTROL0_CMD_FRAGMENT ) {
2005-04-28 00:01:34 +02:00
if ( ! conn - > incoming_cmd ) {
2005-06-17 20:49:55 +02:00
send_control0_error ( conn , ERR_SYNTAX , " Out-of-place FRAGMENT " ) ;
2005-04-28 00:01:34 +02:00
return 0 ;
}
} else {
tor_assert ( 0 ) ;
}
if ( conn - > incoming_cmd_cur_len + body_len > conn - > incoming_cmd_len ) {
tor_free ( conn - > incoming_cmd ) ;
2005-06-17 20:49:55 +02:00
send_control0_error ( conn , ERR_SYNTAX ,
2005-04-28 00:01:34 +02:00
" Fragmented data exceeds declared length " ) ;
return 0 ;
}
memcpy ( conn - > incoming_cmd + conn - > incoming_cmd_cur_len ,
body , body_len ) ;
conn - > incoming_cmd_cur_len + = body_len ;
return 0 ;
}
2004-11-07 22:37:50 +01:00
/** Called when <b>conn</b> has no more bytes left on its outbuf. */
int
2005-06-11 20:52:12 +02:00
connection_control_finished_flushing ( connection_t * conn )
{
2004-11-03 02:32:26 +01:00
tor_assert ( conn ) ;
tor_assert ( conn - > type = = CONN_TYPE_CONTROL ) ;
connection_stop_writing ( conn ) ;
return 0 ;
}
2004-11-21 11:14:57 +01:00
/** Called when <b>conn</b> has gotten its socket closed. */
2005-06-11 20:52:12 +02:00
int
connection_control_reached_eof ( connection_t * conn )
{
2005-06-17 20:49:55 +02:00
tor_assert ( conn ) ;
tor_assert ( conn - > type = = CONN_TYPE_CONTROL ) ;
2005-10-19 00:21:29 +02:00
info ( LD_CONTROL , " Control connection reached EOF. Closing. " ) ;
2004-11-21 11:14:57 +01:00
connection_mark_for_close ( conn ) ;
return 0 ;
}
2005-09-30 22:04:55 +02:00
/** Called when data has arrived on a v1 control connection: Try to fetch
* commands from conn - > inbuf , and execute them .
*/
2005-06-17 20:49:55 +02:00
static int
connection_control_process_inbuf_v1 ( connection_t * conn )
2005-06-11 20:52:12 +02:00
{
2005-08-12 19:24:53 +02:00
size_t data_len ;
int cmd_len ;
2005-06-17 22:37:21 +02:00
char * args ;
2004-11-03 02:32:26 +01:00
tor_assert ( conn ) ;
tor_assert ( conn - > type = = CONN_TYPE_CONTROL ) ;
2005-06-17 20:49:55 +02:00
tor_assert ( conn - > state = = CONTROL_CONN_STATE_OPEN_V1 | |
conn - > state = = CONTROL_CONN_STATE_NEEDAUTH_V1 ) ;
if ( ! conn - > incoming_cmd ) {
conn - > incoming_cmd = tor_malloc ( 1024 ) ;
conn - > incoming_cmd_len = 1024 ;
conn - > incoming_cmd_cur_len = 0 ;
}
again :
while ( 1 ) {
size_t last_idx ;
int r ;
2005-07-15 21:31:11 +02:00
/* First, fetch a line. */
2005-06-17 22:37:21 +02:00
do {
2005-06-17 20:49:55 +02:00
data_len = conn - > incoming_cmd_len - conn - > incoming_cmd_cur_len ;
r = fetch_from_buf_line ( conn - > inbuf ,
2005-07-15 21:31:11 +02:00
conn - > incoming_cmd + conn - > incoming_cmd_cur_len ,
& data_len ) ;
2005-06-17 20:49:55 +02:00
if ( r = = 0 )
/* Line not all here yet. Wait. */
return 0 ;
else if ( r = = - 1 ) {
2005-06-17 22:37:21 +02:00
while ( conn - > incoming_cmd_len < data_len + conn - > incoming_cmd_cur_len )
2005-06-17 20:49:55 +02:00
conn - > incoming_cmd_len * = 2 ;
2005-07-15 21:31:11 +02:00
conn - > incoming_cmd = tor_realloc ( conn - > incoming_cmd ,
conn - > incoming_cmd_len ) ;
2005-06-17 20:49:55 +02:00
}
} while ( r ! = 1 ) ;
tor_assert ( data_len ) ;
last_idx = conn - > incoming_cmd_cur_len ;
conn - > incoming_cmd_cur_len + = data_len ;
/* We have appended a line to incoming_cmd. Is the command done? */
if ( last_idx = = 0 & & * conn - > incoming_cmd ! = ' + ' )
/* One line command, didn't start with '+'. */
break ;
if ( last_idx + 3 = = conn - > incoming_cmd_cur_len & &
2005-07-15 21:31:11 +02:00
! memcmp ( conn - > incoming_cmd + last_idx , " . \r \n " , 3 ) ) {
/* Just appended ".\r\n"; we're done. Remove it. */
conn - > incoming_cmd_cur_len - = 3 ;
2005-06-17 20:49:55 +02:00
break ;
2005-07-15 21:31:11 +02:00
}
2005-06-17 20:49:55 +02:00
/* Otherwise, read another line. */
}
data_len = conn - > incoming_cmd_cur_len ;
/* Okay, we now have a command sitting on conn->incoming_cmd. See if we
* recognize it .
*/
2005-06-17 22:37:21 +02:00
cmd_len = 0 ;
2005-09-08 08:37:50 +02:00
while ( ( size_t ) cmd_len < data_len
& & ! TOR_ISSPACE ( conn - > incoming_cmd [ cmd_len ] ) )
2005-06-17 20:49:55 +02:00
+ + cmd_len ;
2005-06-17 22:37:21 +02:00
data_len - = cmd_len ;
conn - > incoming_cmd [ cmd_len ] = ' \0 ' ;
args = conn - > incoming_cmd + cmd_len + 1 ;
while ( * args = = ' ' | | * args = = ' \t ' ) {
+ + args ;
- - data_len ;
}
2005-08-13 03:55:23 +02:00
if ( ! strcasecmp ( conn - > incoming_cmd , " QUIT " ) ) {
connection_write_str_to_buf ( " 250 closing connection \r \n " , conn ) ;
connection_mark_for_close ( conn ) ;
return 0 ;
}
2005-06-17 20:49:55 +02:00
if ( conn - > state = = CONTROL_CONN_STATE_NEEDAUTH_V1 & &
2005-06-17 22:37:21 +02:00
strcasecmp ( conn - > incoming_cmd , " AUTHENTICATE " ) ) {
2005-06-17 20:49:55 +02:00
connection_write_str_to_buf ( " 514 Authentication required. \r \n " , conn ) ;
conn - > incoming_cmd_cur_len = 0 ;
goto again ;
}
2005-06-17 22:37:21 +02:00
if ( ! strcasecmp ( conn - > incoming_cmd , " SETCONF " ) ) {
if ( handle_control_setconf ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-09-08 05:18:51 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " RESETCONF " ) ) {
if ( handle_control_resetconf ( conn , data_len , args ) )
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " GETCONF " ) ) {
if ( handle_control_getconf ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " SETEVENTS " ) ) {
if ( handle_control_setevents ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " AUTHENTICATE " ) ) {
if ( handle_control_authenticate ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " SAVECONF " ) ) {
if ( handle_control_saveconf ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " SIGNAL " ) ) {
if ( handle_control_signal ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " MAPADDRESS " ) ) {
if ( handle_control_mapaddress ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " GETINFO " ) ) {
if ( handle_control_getinfo ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " EXTENDCIRCUIT " ) ) {
if ( handle_control_extendcircuit ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " ATTACHSTREAM " ) ) {
if ( handle_control_attachstream ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " +POSTDESCRIPTOR " ) ) {
if ( handle_control_postdescriptor ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " REDIRECTSTREAM " ) ) {
if ( handle_control_redirectstream ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " CLOSESTREAM " ) ) {
if ( handle_control_closestream ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
2005-06-17 22:37:21 +02:00
} else if ( ! strcasecmp ( conn - > incoming_cmd , " CLOSECIRCUIT " ) ) {
if ( handle_control_closecircuit ( conn , data_len , args ) )
2005-06-17 20:49:55 +02:00
return - 1 ;
} else {
2005-06-17 22:37:21 +02:00
connection_printf_to_buf ( conn , " 510 Unrecognized command \" %s \" \r \n " ,
2005-06-17 20:49:55 +02:00
conn - > incoming_cmd ) ;
}
conn - > incoming_cmd_cur_len = 0 ;
goto again ;
}
2005-09-30 22:04:55 +02:00
/** Called when data has arrived on a v0 control connection: Try to fetch
* commands from conn - > inbuf , and execute them .
*/
2005-06-17 20:49:55 +02:00
static int
connection_control_process_inbuf_v0 ( connection_t * conn )
{
uint32_t body_len ;
uint16_t command_type ;
2005-07-12 07:48:15 +02:00
char * body = NULL ;
2004-11-03 02:32:26 +01:00
again :
2004-11-07 22:37:50 +01:00
/* Try to suck a control message from the buffer. */
2005-06-17 20:49:55 +02:00
switch ( fetch_from_buf_control0 ( conn - > inbuf , & body_len , & command_type , & body ,
conn - > state = = CONTROL_CONN_STATE_NEEDAUTH_V0 ) )
2004-11-03 02:32:26 +01:00
{
2005-06-17 20:49:55 +02:00
case - 2 :
tor_free ( body ) ;
2005-10-19 00:21:29 +02:00
info ( LD_CONTROL , " Detected v1 control protocol on connection (fd %d) " ,
conn - > s ) ;
2005-06-17 20:49:55 +02:00
conn - > state = CONTROL_CONN_STATE_NEEDAUTH_V1 ;
return connection_control_process_inbuf_v1 ( conn ) ;
2004-11-03 02:32:26 +01:00
case - 1 :
2004-11-07 23:58:35 +01:00
tor_free ( body ) ;
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Error in control command. Failing. " ) ;
2004-11-03 02:32:26 +01:00
return - 1 ;
case 0 :
/* Control command not all here yet. Wait. */
return 0 ;
case 1 :
/* We got a command. Process it. */
break ;
default :
tor_assert ( 0 ) ;
}
/* We got a command. If we need authentication, only authentication
* commands will be considered . */
2005-06-17 20:49:55 +02:00
if ( conn - > state = = CONTROL_CONN_STATE_NEEDAUTH_V0 & &
command_type ! = CONTROL0_CMD_AUTHENTICATE ) {
2005-10-19 00:21:29 +02:00
info ( LD_CONTROL , " Rejecting '%s' command; authentication needed. " ,
control_cmd_to_string ( command_type ) ) ;
2005-06-17 20:49:55 +02:00
send_control0_error ( conn , ERR_UNAUTHORIZED , " Authentication required " ) ;
2004-11-03 02:32:26 +01:00
tor_free ( body ) ;
goto again ;
}
2005-06-17 20:49:55 +02:00
if ( command_type = = CONTROL0_CMD_FRAGMENTHEADER | |
command_type = = CONTROL0_CMD_FRAGMENT ) {
2005-04-28 00:01:34 +02:00
if ( handle_control_fragments ( conn , command_type , body_len , body ) )
return - 1 ;
tor_free ( body ) ;
if ( conn - > incoming_cmd_cur_len ! = conn - > incoming_cmd_len )
goto again ;
command_type = conn - > incoming_cmd_type ;
body_len = conn - > incoming_cmd_len ;
body = conn - > incoming_cmd ;
conn - > incoming_cmd = NULL ;
} else if ( conn - > incoming_cmd ) {
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Dropping incomplete fragmented command " ) ;
2005-04-28 00:01:34 +02:00
tor_free ( conn - > incoming_cmd ) ;
}
2004-11-07 22:37:50 +01:00
/* Okay, we're willing to process the command. */
2004-11-28 10:05:49 +01:00
switch ( command_type )
2004-11-03 02:32:26 +01:00
{
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_SETCONF :
2004-11-03 02:32:26 +01:00
if ( handle_control_setconf ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_GETCONF :
2004-11-03 02:32:26 +01:00
if ( handle_control_getconf ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_SETEVENTS :
2004-11-03 02:32:26 +01:00
if ( handle_control_setevents ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_AUTHENTICATE :
2004-11-03 02:32:26 +01:00
if ( handle_control_authenticate ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_SAVECONF :
2004-11-07 23:37:59 +01:00
if ( handle_control_saveconf ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_SIGNAL :
2005-01-05 07:40:47 +01:00
if ( handle_control_signal ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_MAPADDRESS :
2005-02-25 07:16:28 +01:00
if ( handle_control_mapaddress ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_GETINFO :
2005-02-25 07:16:28 +01:00
if ( handle_control_getinfo ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_EXTENDCIRCUIT :
2005-02-25 07:16:28 +01:00
if ( handle_control_extendcircuit ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_ATTACHSTREAM :
2005-02-25 07:16:28 +01:00
if ( handle_control_attachstream ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_POSTDESCRIPTOR :
2005-02-25 07:16:28 +01:00
if ( handle_control_postdescriptor ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_REDIRECTSTREAM :
2005-03-19 07:05:55 +01:00
if ( handle_control_redirectstream ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_CLOSESTREAM :
2005-03-22 20:36:38 +01:00
if ( handle_control_closestream ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_CLOSECIRCUIT :
2005-03-22 20:36:38 +01:00
if ( handle_control_closecircuit ( conn , body_len , body ) )
return - 1 ;
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_ERROR :
case CONTROL0_CMD_DONE :
case CONTROL0_CMD_CONFVALUE :
case CONTROL0_CMD_EVENT :
case CONTROL0_CMD_INFOVALUE :
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Received client-only '%s' command; ignoring. " ,
2004-11-03 02:32:26 +01:00
control_cmd_to_string ( command_type ) ) ;
2005-06-17 20:49:55 +02:00
send_control0_error ( conn , ERR_UNRECOGNIZED_TYPE ,
2004-11-03 11:08:44 +01:00
" Command type only valid from server to tor client " ) ;
2004-11-03 02:32:26 +01:00
break ;
2005-06-17 20:49:55 +02:00
case CONTROL0_CMD_FRAGMENTHEADER :
case CONTROL0_CMD_FRAGMENT :
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Recieved command fragment out of order; ignoring. " ) ;
2005-06-17 20:49:55 +02:00
send_control0_error ( conn , ERR_SYNTAX , " Bad fragmentation on command. " ) ;
2004-11-03 02:32:26 +01:00
default :
2005-10-19 00:21:29 +02:00
warn ( LD_CONTROL , " Received unrecognized command type %d; ignoring. " ,
2004-11-03 02:32:26 +01:00
( int ) command_type ) ;
2005-06-17 20:49:55 +02:00
send_control0_error ( conn , ERR_UNRECOGNIZED_TYPE ,
2004-11-03 02:32:26 +01:00
" Unrecognized command type " ) ;
break ;
}
tor_free ( body ) ;
goto again ; /* There might be more data. */
}
2005-06-17 20:49:55 +02:00
/** Called when <b>conn</b> has received more bytes on its inbuf.
*/
int
connection_control_process_inbuf ( connection_t * conn )
{
tor_assert ( conn ) ;
tor_assert ( conn - > type = = CONN_TYPE_CONTROL ) ;
if ( STATE_IS_V0 ( conn - > state ) )
return connection_control_process_inbuf_v0 ( conn ) ;
else
return connection_control_process_inbuf_v1 ( conn ) ;
}
2004-11-07 22:37:50 +01:00
/** Something has happened to circuit <b>circ</b>: tell any interested
* control connections . */
int
control_event_circuit_status ( circuit_t * circ , circuit_status_event_t tp )
2004-11-03 02:32:26 +01:00
{
2004-11-03 19:33:07 +01:00
char * path , * msg ;
2004-11-03 02:32:26 +01:00
if ( ! EVENT_IS_INTERESTING ( EVENT_CIRCUIT_STATUS ) )
return 0 ;
2004-11-03 19:33:07 +01:00
tor_assert ( circ ) ;
tor_assert ( CIRCUIT_IS_ORIGIN ( circ ) ) ;
2004-11-23 01:11:36 +01:00
path = circuit_list_path ( circ , 0 ) ;
2005-06-17 22:37:21 +02:00
if ( EVENT_IS_INTERESTING0 ( EVENT_CIRCUIT_STATUS ) ) {
size_t path_len = strlen ( path ) ;
msg = tor_malloc ( 1 + 4 + path_len + 1 ) ; /* event, circid, path, NUL. */
msg [ 0 ] = ( uint8_t ) tp ;
set_uint32 ( msg + 1 , htonl ( circ - > global_identifier ) ) ;
strlcpy ( msg + 5 , path , path_len + 1 ) ;
send_control0_event ( EVENT_CIRCUIT_STATUS , ( uint32_t ) ( path_len + 6 ) , msg ) ;
tor_free ( msg ) ;
}
if ( EVENT_IS_INTERESTING1 ( EVENT_CIRCUIT_STATUS ) ) {
const char * status ;
switch ( tp )
{
case CIRC_EVENT_LAUNCHED : status = " LAUNCHED " ; break ;
case CIRC_EVENT_BUILT : status = " BUILT " ; break ;
case CIRC_EVENT_EXTENDED : status = " EXTENDED " ; break ;
case CIRC_EVENT_FAILED : status = " FAILED " ; break ;
case CIRC_EVENT_CLOSED : status = " CLOSED " ; break ;
default :
2005-10-24 21:39:45 +02:00
warn ( LD_BUG , " Unrecognized status code %d " , ( int ) tp ) ;
2005-06-17 22:37:21 +02:00
return 0 ;
}
send_control1_event ( EVENT_CIRCUIT_STATUS ,
" 650 CIRC %lu %s %s \r \n " ,
( unsigned long ) circ - > global_identifier ,
status , path ) ;
}
2004-11-03 19:33:07 +01:00
tor_free ( path ) ;
2005-06-17 22:37:21 +02:00
2004-11-03 02:32:26 +01:00
return 0 ;
}
2005-09-30 22:04:55 +02:00
/** Given an AP connection <b>conn</b> and a <b>len</b>-character buffer
* < b > buf < / b > , determine the address : port combination requested on
* < b > conn < / b > , and write it to < b > buf < / b > . Return 0 on success , - 1 on
* failure . */
2005-06-19 22:40:41 +02:00
static int
write_stream_target_to_buf ( connection_t * conn , char * buf , size_t len )
{
char buf2 [ 256 ] ;
if ( conn - > chosen_exit_name )
if ( tor_snprintf ( buf2 , sizeof ( buf2 ) , " .%s.exit " , conn - > chosen_exit_name ) < 0 )
return - 1 ;
2005-11-17 00:54:24 +01:00
if ( tor_snprintf ( buf , len , " %s%s%s:%d " ,
2005-06-19 22:40:41 +02:00
conn - > socks_request - > address ,
conn - > chosen_exit_name ? buf2 : " " ,
2005-11-17 00:54:24 +01:00
! conn - > chosen_exit_name & &
connection_edge_is_rendezvous_stream ( conn ) ? " .onion " : " " ,
2005-06-19 22:40:41 +02:00
conn - > socks_request - > port ) < 0 )
return - 1 ;
return 0 ;
}
2004-11-07 22:37:50 +01:00
/** Something has happened to the stream associated with AP connection
* < b > conn < / b > : tell any interested control connections . */
int
control_event_stream_status ( connection_t * conn , stream_status_event_t tp )
2004-11-03 02:32:26 +01:00
{
2004-11-03 19:33:07 +01:00
char * msg ;
size_t len ;
2005-06-19 22:40:41 +02:00
char buf [ 256 ] ;
2004-11-03 02:32:26 +01:00
tor_assert ( conn - > type = = CONN_TYPE_AP ) ;
2004-11-03 19:33:07 +01:00
tor_assert ( conn - > socks_request ) ;
2004-11-03 02:32:26 +01:00
if ( ! EVENT_IS_INTERESTING ( EVENT_STREAM_STATUS ) )
return 0 ;
2005-06-19 22:40:41 +02:00
write_stream_target_to_buf ( conn , buf , sizeof ( buf ) ) ;
2005-06-17 22:37:21 +02:00
if ( EVENT_IS_INTERESTING0 ( EVENT_STREAM_STATUS ) ) {
len = strlen ( buf ) ;
msg = tor_malloc ( 5 + len + 1 ) ;
msg [ 0 ] = ( uint8_t ) tp ;
set_uint32 ( msg + 1 , htonl ( conn - > global_identifier ) ) ;
strlcpy ( msg + 5 , buf , len + 1 ) ;
send_control0_event ( EVENT_STREAM_STATUS , ( uint32_t ) ( 5 + len + 1 ) , msg ) ;
tor_free ( msg ) ;
}
2005-07-18 00:28:01 +02:00
if ( EVENT_IS_INTERESTING1 ( EVENT_STREAM_STATUS ) ) {
2005-06-17 22:37:21 +02:00
const char * status ;
2005-06-19 22:40:41 +02:00
circuit_t * circ ;
2005-06-17 22:37:21 +02:00
switch ( tp )
{
case STREAM_EVENT_SENT_CONNECT : status = " SENTCONNECT " ; break ;
case STREAM_EVENT_SENT_RESOLVE : status = " SENTRESOLVE " ; break ;
case STREAM_EVENT_SUCCEEDED : status = " SUCCEEDED " ; break ;
case STREAM_EVENT_FAILED : status = " FAILED " ; break ;
case STREAM_EVENT_CLOSED : status = " CLOSED " ; break ;
case STREAM_EVENT_NEW : status = " NEW " ; break ;
case STREAM_EVENT_NEW_RESOLVE : status = " NEWRESOLVE " ; break ;
case STREAM_EVENT_FAILED_RETRIABLE : status = " DETACHED " ; break ;
default :
2005-10-24 21:39:45 +02:00
warn ( LD_BUG , " Unrecognized status code %d " , ( int ) tp ) ;
2005-06-17 22:37:21 +02:00
return 0 ;
}
2005-06-19 22:40:41 +02:00
circ = circuit_get_by_edge_conn ( conn ) ;
2005-06-17 22:37:21 +02:00
send_control1_event ( EVENT_STREAM_STATUS ,
2005-06-19 22:40:41 +02:00
" 650 STREAM %lu %s %lu %s \r \n " ,
( unsigned long ) conn - > global_identifier , status ,
circ ? ( unsigned long ) circ - > global_identifier : 0ul ,
buf ) ;
2005-08-15 05:25:40 +02:00
/* XXX need to specify its intended exit, etc? */
2005-06-17 22:37:21 +02:00
}
2004-11-03 02:32:26 +01:00
return 0 ;
}
2004-11-07 22:37:50 +01:00
/** Something has happened to the OR connection <b>conn</b>: tell any
* interested control connections . */
int
control_event_or_conn_status ( connection_t * conn , or_conn_status_event_t tp )
2004-11-03 02:32:26 +01:00
{
2004-11-03 19:33:07 +01:00
char buf [ HEX_DIGEST_LEN + 3 ] ; /* status, dollar, identity, NUL */
size_t len ;
2004-11-03 02:32:26 +01:00
tor_assert ( conn - > type = = CONN_TYPE_OR ) ;
if ( ! EVENT_IS_INTERESTING ( EVENT_OR_CONN_STATUS ) )
return 0 ;
2005-06-17 22:37:21 +02:00
if ( EVENT_IS_INTERESTING0 ( EVENT_OR_CONN_STATUS ) ) {
buf [ 0 ] = ( uint8_t ) tp ;
strlcpy ( buf + 1 , conn - > nickname , sizeof ( buf ) - 1 ) ;
len = strlen ( buf + 1 ) ;
send_control0_event ( EVENT_OR_CONN_STATUS , ( uint32_t ) ( len + 1 ) , buf ) ;
}
if ( EVENT_IS_INTERESTING1 ( EVENT_OR_CONN_STATUS ) ) {
const char * status ;
switch ( tp )
{
case OR_CONN_EVENT_LAUNCHED : status = " LAUNCHED " ; break ;
case OR_CONN_EVENT_CONNECTED : status = " CONNECTED " ; break ;
case OR_CONN_EVENT_FAILED : status = " FAILED " ; break ;
case OR_CONN_EVENT_CLOSED : status = " CLOSED " ; break ;
default :
2005-10-24 21:39:45 +02:00
warn ( LD_BUG , " Unrecognized status code %d " , ( int ) tp ) ;
2005-06-17 22:37:21 +02:00
return 0 ;
}
send_control1_event ( EVENT_OR_CONN_STATUS ,
" 650 ORCONN %s %s \r \n " ,
conn - > nickname , status ) ;
}
2004-11-03 02:32:26 +01:00
return 0 ;
}
2004-11-07 22:37:50 +01:00
/** A second or more has elapsed: tell any interested control
* connections how much bandwidth we used . */
int
control_event_bandwidth_used ( uint32_t n_read , uint32_t n_written )
2004-11-03 02:32:26 +01:00
{
char buf [ 8 ] ;
2005-06-17 22:37:21 +02:00
if ( EVENT_IS_INTERESTING0 ( EVENT_BANDWIDTH_USED ) ) {
set_uint32 ( buf , htonl ( n_read ) ) ;
set_uint32 ( buf + 4 , htonl ( n_written ) ) ;
send_control0_event ( EVENT_BANDWIDTH_USED , 8 , buf ) ;
}
if ( EVENT_IS_INTERESTING1 ( EVENT_BANDWIDTH_USED ) ) {
send_control1_event ( EVENT_BANDWIDTH_USED ,
" 650 BW %lu %lu \r \n " ,
( unsigned long ) n_read ,
( unsigned long ) n_written ) ;
}
2004-11-03 02:32:26 +01:00
return 0 ;
}
2005-09-30 22:04:55 +02:00
/** Called when we are sending a log message to the controllers: suspend
* sending further log messages to the controllers until we ' re done . Used by
* CONN_LOG_PROTECT . */
2005-07-13 07:14:42 +02:00
void
disable_control_logging ( void )
{
+ + disable_log_messages ;
}
2005-09-30 22:04:55 +02:00
/** We're done sending a log message to the controllers: re-enable controller
* logging . Used by CONN_LOG_PROTECT . */
2005-07-13 07:14:42 +02:00
void
enable_control_logging ( void )
{
if ( - - disable_log_messages < 0 )
tor_assert ( 0 ) ;
}
2004-11-07 22:37:50 +01:00
/** We got a log message: tell any interested control connections. */
void
2005-10-25 09:02:13 +02:00
control_event_logmsg ( int severity , unsigned int domain , const char * msg )
2004-11-03 02:32:26 +01:00
{
2005-04-18 00:52:02 +02:00
int oldlog , event ;
2005-07-13 07:14:42 +02:00
if ( disable_log_messages )
2005-04-18 00:52:02 +02:00
return ;
2005-06-18 04:39:25 +02:00
oldlog = EVENT_IS_INTERESTING0 ( EVENT_LOG_OBSOLETE ) & &
2005-04-06 00:56:17 +02:00
( severity = = LOG_NOTICE | | severity = = LOG_WARN | | severity = = LOG_ERR ) ;
2005-04-18 00:52:02 +02:00
event = log_severity_to_event ( severity ) ;
2005-04-06 00:56:17 +02:00
2005-06-18 04:39:25 +02:00
if ( event < 0 | | ! EVENT_IS_INTERESTING0 ( event ) )
2005-04-06 00:56:17 +02:00
event = 0 ;
if ( oldlog | | event ) {
size_t len = strlen ( msg ) ;
2005-09-30 22:04:55 +02:00
+ + disable_log_messages ;
2005-04-06 00:56:17 +02:00
if ( event )
2005-06-17 20:49:55 +02:00
send_control0_event ( event , ( uint32_t ) ( len + 1 ) , msg ) ;
2005-04-06 00:56:17 +02:00
if ( oldlog )
2005-06-17 20:49:55 +02:00
send_control0_event ( EVENT_LOG_OBSOLETE , ( uint32_t ) ( len + 1 ) , msg ) ;
2005-09-30 22:04:55 +02:00
- - disable_log_messages ;
2005-04-06 00:56:17 +02:00
}
2005-06-18 04:39:25 +02:00
event = log_severity_to_event ( severity ) ;
if ( event > = 0 & & EVENT_IS_INTERESTING1 ( event ) ) {
char * b = NULL ;
const char * s ;
if ( strchr ( msg , ' \n ' ) ) {
char * cp ;
b = tor_strdup ( msg ) ;
for ( cp = b ; * cp ; + + cp )
if ( * cp = = ' \r ' | | * cp = = ' \n ' )
* cp = ' ' ;
}
2005-06-19 22:40:41 +02:00
switch ( severity ) {
2005-07-13 07:14:42 +02:00
case LOG_DEBUG : s = " DEBUG " ; break ;
2005-06-18 04:39:25 +02:00
case LOG_INFO : s = " INFO " ; break ;
case LOG_NOTICE : s = " NOTICE " ; break ;
case LOG_WARN : s = " WARN " ; break ;
case LOG_ERR : s = " ERR " ; break ;
default : s = " UnknownLogSeverity " ; break ;
}
2005-09-30 22:04:55 +02:00
+ + disable_log_messages ;
2005-06-18 04:39:25 +02:00
send_control1_event ( event , " 650 %s %s \r \n " , s , b ? b : msg ) ;
2005-09-30 22:04:55 +02:00
- - disable_log_messages ;
2005-06-18 04:39:25 +02:00
tor_free ( b ) ;
}
2004-11-03 02:32:26 +01:00
}
2005-03-17 13:38:37 +01:00
/** Called whenever we receive new router descriptors: tell any
* interested control connections . < b > routers < / b > is a list of
* DIGEST_LEN - byte identity digests .
*/
2005-06-11 20:52:12 +02:00
int
control_event_descriptors_changed ( smartlist_t * routers )
2005-03-02 23:29:58 +01:00
{
size_t len ;
char * msg ;
smartlist_t * identities ;
char buf [ HEX_DIGEST_LEN + 1 ] ;
if ( ! EVENT_IS_INTERESTING ( EVENT_NEW_DESC ) )
return 0 ;
identities = smartlist_create ( ) ;
SMARTLIST_FOREACH ( routers , routerinfo_t * , r ,
{
2005-11-05 21:15:27 +01:00
base16_encode ( buf , sizeof ( buf ) , r - > cache_info . identity_digest , DIGEST_LEN ) ;
2005-03-02 23:29:58 +01:00
smartlist_add ( identities , tor_strdup ( buf ) ) ;
} ) ;
2005-06-17 22:37:21 +02:00
if ( EVENT_IS_INTERESTING0 ( EVENT_NEW_DESC ) ) {
msg = smartlist_join_strings ( identities , " , " , 0 , & len ) ;
send_control0_event ( EVENT_NEW_DESC , len + 1 , msg ) ;
tor_free ( msg ) ;
}
if ( EVENT_IS_INTERESTING1 ( EVENT_NEW_DESC ) ) {
msg = smartlist_join_strings ( identities , " " , 0 , & len ) ;
send_control1_event ( EVENT_NEW_DESC , " 650 NEWDESC %s \r \n " , msg ) ;
tor_free ( msg ) ;
}
2005-03-02 23:29:58 +01:00
SMARTLIST_FOREACH ( identities , char * , cp , tor_free ( cp ) ) ;
smartlist_free ( identities ) ;
2005-06-17 22:37:21 +02:00
2005-03-02 23:29:58 +01:00
return 0 ;
}
2005-09-30 22:04:55 +02:00
/** Called whenever an address mapping on <b>from<b> from changes to <b>to</b>.
* < b > expires < / b > values less than 3 are special ; see connection_edge . c . */
2005-06-19 22:40:41 +02:00
int
control_event_address_mapped ( const char * from , const char * to , time_t expires )
{
if ( ! EVENT_IS_INTERESTING1 ( EVENT_ADDRMAP ) )
return 0 ;
if ( expires < 3 )
send_control1_event ( EVENT_ADDRMAP , " 650 ADDRMAP %s %s NEVER \r \n " , from , to ) ;
else {
char buf [ ISO_TIME_LEN + 1 ] ;
format_local_iso_time ( buf , expires ) ;
send_control1_event ( EVENT_ADDRMAP , " 650 ADDRMAP %s %s \" %s \" \r \n " ,
from , to , buf ) ;
}
return 0 ;
}
2004-11-07 22:37:50 +01:00
/** Choose a random authentication cookie and write it to disk.
* Anybody who can read the cookie from disk will be considered
* authorized to use the control connection . */
int
2004-11-12 17:39:03 +01:00
init_cookie_authentication ( int enabled )
2004-11-03 20:49:03 +01:00
{
char fname [ 512 ] ;
2004-12-07 20:36:43 +01:00
if ( ! enabled ) {
2004-11-12 17:39:03 +01:00
authentication_cookie_is_set = 0 ;
2004-12-07 20:36:43 +01:00
return 0 ;
}
2004-11-03 20:49:03 +01:00
tor_snprintf ( fname , sizeof ( fname ) , " %s/control_auth_cookie " ,
2004-11-09 08:05:53 +01:00
get_options ( ) - > DataDirectory ) ;
2004-11-03 20:49:03 +01:00
crypto_rand ( authentication_cookie , AUTHENTICATION_COOKIE_LEN ) ;
authentication_cookie_is_set = 1 ;
if ( write_bytes_to_file ( fname , authentication_cookie ,
AUTHENTICATION_COOKIE_LEN , 1 ) ) {
2005-10-19 00:21:29 +02:00
warn ( LD_FS , " Error writing authentication cookie. " ) ;
2004-11-03 20:49:03 +01:00
return - 1 ;
}
return 0 ;
}
2005-06-11 20:59:24 +02:00