Back to index

tor  0.2.3.19-rc
Classes | Defines | Typedefs | Functions | Variables
control.c File Reference

Implementation for Tor's control-socket interface. More...

#include "or.h"
#include "buffers.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
#include "connection_or.h"
#include "control.h"
#include "directory.h"
#include "dirserv.h"
#include "dnsserv.h"
#include "geoip.h"
#include "hibernate.h"
#include "main.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
#include "reasons.h"
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
#include <pwd.h>
#include <sys/resource.h>
#include "procmon.h"

Go to the source code of this file.

Classes

struct  control_event_t
 Helper structure: maps event values to their names. More...
struct  getinfo_item_t
 A single item for the GETINFO question-to-answer-function table. More...

Defines

#define CONTROL_PRIVATE
#define STATE_IS_OPEN(s)   ((s) == CONTROL_CONN_STATE_OPEN)
 Yield true iff s is the state of a control_connection_t that has finished authentication and is accepting commands.
#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
#define EVENT_CIRCUIT_STATUS_MINOR   0x0005
#define EVENT_NEW_DESC   0x0006
#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
#define EVENT_ADDRMAP   0x000C
#define EVENT_DESCCHANGED   0x000E
#define EVENT_STATUS_CLIENT   0x0010
#define EVENT_STATUS_SERVER   0x0011
#define EVENT_STATUS_GENERAL   0x0012
#define EVENT_GUARD   0x0013
#define EVENT_STREAM_BANDWIDTH_USED   0x0014
#define EVENT_CLIENTS_SEEN   0x0015
#define EVENT_NEWCONSENSUS   0x0016
#define EVENT_BUILDTIMEOUT_SET   0x0017
#define EVENT_SIGNAL   0x0018
#define EVENT_CONF_CHANGED   0x0019
#define _EVENT_MAX   0x0019
#define EVENT_IS_INTERESTING(e)   (global_event_mask & (1<<(e)))
 Macro: true if any control connection is interested in events of type e.
#define AUTHENTICATION_COOKIE_LEN   32
 If we're using cookie-type authentication, how long should our cookies be?
#define SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT   "Tor safe cookie authentication server-to-controller hash"
#define SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT   "Tor safe cookie authentication controller-to-server hash"
#define SAFECOOKIE_SERVER_NONCE_LEN   DIGEST256_LEN
#define BOOTSTRAP_MSG_LEN   1024
 A sufficiently large size to record the last bootstrap phase string.
#define ALL_FORMATS   1
 Flag for event_format_t.
#define ITEM(name, fn, desc)   { name, getinfo_helper_##fn, desc, 0 }
#define PREFIX(name, fn, desc)   { name, getinfo_helper_##fn, desc, 1 }
#define DOC(name, desc)   { name, NULL, desc, 0 }
#define MAX_COMMAND_LINE_LENGTH   (1024*1024)
 Do not accept any control command of more than 1MB in length.
#define BOOTSTRAP_PROBLEM_THRESHOLD   10

Typedefs

typedef uint32_t event_mask_t
 Bitfield: The bit 1<<e is set if any open control connection is interested in events of type e.
typedef int event_format_t
 Bit field of flags to select how to format a controller event.
typedef int(* getinfo_helper_t )(control_connection_t *, const char *q, char **a, const char **error_out)
 Callback function for GETINFO: on a given control connection, try to answer the question q and store the newly-allocated answer in *a.
typedef struct getinfo_item_t getinfo_item_t
 A single item for the GETINFO question-to-answer-function table.

Functions

static void connection_printf_to_buf (control_connection_t *conn, const char *format,...)
 Acts like sprintf, but writes its formatted string to the end of conn->outbuf.
static void static void send_control_event_impl (uint16_t event, event_format_t which, const char *format, va_list ap)
 Helper for send_control_event and control_event_status: Send an event to all v1 controllers that are listening for code event.
static void static void static int control_event_status (int type, int severity, const char *format, va_list args)
 Helper: sends a status event where type is one of EVENT_STATUS_{GENERAL,CLIENT,SERVER}, where severity is one of LOG_{NOTICE,WARN,ERR}, and where format is a printf-style format string corresponding to args.
static void static void static
int static void 
send_control_done (control_connection_t *conn)
 Send a "DONE" message down the control connection conn.
static void send_control_event (uint16_t event, event_format_t which, const char *format,...)
 Send an event to all v1 controllers that are listening for code event.
static void static int handle_control_setconf (control_connection_t *conn, uint32_t len, char *body)
 Called when we receive a SETCONF message: parse the body and try to update our configuration.
static int handle_control_resetconf (control_connection_t *conn, uint32_t len, char *body)
 Called when we receive a RESETCONF message: parse the body and try to update our configuration.
static int handle_control_getconf (control_connection_t *conn, uint32_t body_len, const char *body)
 Called when we receive a GETCONF message.
static int handle_control_loadconf (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a +LOADCONF message.
static int handle_control_setevents (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a SETEVENTS message: update conn->event_mask, and reply with DONE or ERROR.
static int handle_control_authenticate (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get an AUTHENTICATE message.
static int handle_control_signal (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a SIGNAL command.
static int handle_control_mapaddress (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a MAPADDRESS command; try to bind all listed addresses, and report success or failure.
static char * list_getinfo_options (void)
 Allocate and return a list of recognized GETINFO options.
static int handle_control_getinfo (control_connection_t *conn, uint32_t len, const char *body)
 Called when we receive a GETINFO command.
static int handle_control_extendcircuit (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get an EXTENDCIRCUIT message.
static int handle_control_setcircuitpurpose (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a SETCIRCUITPURPOSE message.
static int handle_control_attachstream (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get an ATTACHSTREAM message.
static int handle_control_postdescriptor (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a POSTDESCRIPTOR message.
static int handle_control_redirectstream (control_connection_t *conn, uint32_t len, const char *body)
 Called when we receive a REDIRECTSTERAM command.
static int handle_control_closestream (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a CLOSESTREAM command; try to close the named stream and report success or failure.
static int handle_control_closecircuit (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a CLOSECIRCUIT command; try to close the named circuit and report success or failure.
static int handle_control_resolve (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a RESOLVE command: start trying to resolve the listed addresses.
static int handle_control_usefeature (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a USEFEATURE command: parse the feature list, and set up the control_connection's options properly.
static int write_stream_target_to_buf (entry_connection_t *conn, char *buf, size_t len)
 Given an AP connection conn and a len-character buffer buf, determine the address:port combination requested on conn, and write it to buf.
static void orconn_target_get_name (char *name, size_t len, or_connection_t *conn)
 Figure out the best name for the target router of an OR connection conn, and write it into the len-character buffer name.
static char * get_cookie_file (void)
 Helper: Return a newly allocated string containing a path to the file where we store our authentication cookie.
static INLINE int event_to_log_severity (int event)
 Given a control event code for a message event, return the corresponding log severity.
static INLINE int log_severity_to_event (int severity)
 Given a log severity, return the corresponding control event code.
void control_update_global_event_mask (void)
 Set global_event_mask* to the bitwise OR of each live control connection's event_mask field.
void control_adjust_event_log_severity (void)
 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.
int control_event_is_interesting (int event)
 Return true iff the event with code c is being sent to any current control connection.
static INLINE void connection_write_str_to_buf (const char *s, control_connection_t *conn)
 Append a NUL-terminated string s to the end of conn->outbuf.
size_t write_escaped_data (const char *data, size_t len, char **out)
 Given a len-character string in data, made of lines terminated by CRLF, allocate a new string in *out, and copy the contents of data into *out, adding a period before any period that appears at the start of a line, and adding a period-CRLF line at the end.
size_t read_escaped_data (const char *data, size_t len, char **out)
 Given a len-character string in data, made of lines terminated by CRLF, allocate a new string in *out, and copy the contents of data into *out, removing any period that appears at the start of a line, and replacing all CRLF sequences with LF.
static INLINE int get_escaped_string_length (const char *start, size_t in_len_max, int *chars_out)
 If the first in_len_max characters in start contain a double-quoted string with escaped characters, return the length of that string (as encoded, including quotes).
static const char * extract_escaped_string (const char *start, size_t in_len_max, char **out, size_t *out_len)
 As decode_escaped_string, but does not decode the string: copies the entire thing, including quotation marks.
static const char * decode_escaped_string (const char *start, size_t in_len_max, char **out, size_t *out_len)
 Given a pointer to a string starting at start containing in_len_max characters, decode a string beginning with one double quote, containing any number of non-quote characters or characters escaped with a backslash, and ending with a final double quote.
void control_ports_write_to_file (void)
 Write all of the open control ports to ControlPortWriteToFile.
static void send_control_event_string (uint16_t event, event_format_t which, const char *msg)
 Send an event to all v1 controllers that are listening for code event.
static origin_circuit_tget_circ (const char *id)
 Given a text circuit id, return the corresponding circuit.
static entry_connection_tget_stream (const char *id)
 Given a text stream id, return the corresponding AP connection.
static int control_setconf_helper (control_connection_t *conn, uint32_t len, char *body, int use_defaults)
 Helper for setconf and resetconf.
smartlist_tdecode_hashed_passwords (config_line_t *passwords)
 Decode the hashed, base64'd passwords stored in passwords.
static int handle_control_saveconf (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a SAVECONF command.
static int handle_control_takeownership (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a TAKEOWNERSHIP command.
static int address_is_invalid_mapaddress_target (const char *addr)
 Return true iff addr is unusable as a mapaddress target because of containing funny characters.
static int getinfo_helper_misc (control_connection_t *conn, const char *question, char **answer, const char **errmsg)
 Implementation helper for GETINFO: knows the answers for various trivial-to-implement questions.
static char * munge_extrainfo_into_routerinfo (const char *ri_body, const signed_descriptor_t *ri, const signed_descriptor_t *ei)
 Awful hack: return a newly allocated string based on a routerinfo and (possibly) an extrainfo, sticking the read-history and write-history from ei into the resulting string.
static int getinfo_helper_listeners (control_connection_t *control_conn, const char *question, char **answer, const char **errmsg)
 Implementation helper for GETINFO: answers requests for information about which ports are bound.
static int getinfo_helper_dir (control_connection_t *control_conn, const char *question, char **answer, const char **errmsg)
 Implementation helper for GETINFO: knows the answers for questions about directory information.
static char * circuit_describe_status_for_controller (origin_circuit_t *circ)
 Allocate and return a description of circ's current status, including its path (if any).
static int getinfo_helper_events (control_connection_t *control_conn, const char *question, char **answer, const char **errmsg)
 Implementation helper for GETINFO: knows how to generate summaries of the current states of things we send events about.
static int handle_getinfo_helper (control_connection_t *control_conn, const char *question, char **answer, const char **err_out)
 Lookup the 'getinfo' entry question, and return the answer in *answer (or NULL if key not recognized).
static uint8_t circuit_purpose_from_string (const char *string)
 Given a string, convert it to a circuit purpose.
static smartlist_tgetargs_helper (const char *command, control_connection_t *conn, const char *body, int min_args, int max_args)
 Return a newly allocated smartlist containing the arguments to the command waiting in body.
static const char * find_element_starting_with (smartlist_t *sl, int start_at, const char *prefix)
 Helper.
static int is_keyval_pair (const char *s)
 Helper.
static int handle_control_protocolinfo (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get a PROTOCOLINFO command: send back a reply.
static int handle_control_authchallenge (control_connection_t *conn, uint32_t len, const char *body)
 Called when we get an AUTHCHALLENGE command.
int connection_control_finished_flushing (control_connection_t *conn)
 Called when conn has no more bytes left on its outbuf.
int connection_control_reached_eof (control_connection_t *conn)
 Called when conn has gotten its socket closed.
static void lost_owning_controller (const char *owner_type, const char *loss_manner)
 Shut down this Tor instance in the same way that SIGINT would, but with a log message appropriate for the loss of an owning controller.
void connection_control_closed (control_connection_t *conn)
 Called when conn is being freed.
static int is_valid_initial_command (control_connection_t *conn, const char *cmd)
 Return true iff cmd is allowable (or at least forgivable) at this stage of the protocol.
static int peek_connection_has_control0_command (connection_t *conn)
 Wrapper around peek_(evbuffer|buf)_has_control0 command: presents the same interface as those underlying functions, but takes a connection_t intead of an evbuffer or a buf_t.
int connection_control_process_inbuf (control_connection_t *conn)
 Called when data has arrived on a v1 control connection: Try to fetch commands from conn->inbuf, and execute them.
int control_event_circuit_status (origin_circuit_t *circ, circuit_status_event_t tp, int reason_code)
 Something major has happened to circuit circ: tell any interested control connections.
static int control_event_circuit_status_minor (origin_circuit_t *circ, circuit_status_minor_event_t e, int purpose, const struct timeval *tv)
 Something minor has happened to circuit circ: tell any interested control connections.
int control_event_circuit_purpose_changed (origin_circuit_t *circ, int old_purpose)
 circ has changed its purpose from old_purpose: tell any interested controllers.
int control_event_circuit_cannibalized (origin_circuit_t *circ, int old_purpose, const struct timeval *old_tv_created)
 circ has changed its purpose from old_purpose, and its created-time from old_tv_created: tell any interested controllers.
int control_event_stream_status (entry_connection_t *conn, stream_status_event_t tp, int reason_code)
 Something has happened to the stream associated with AP connection conn: tell any interested control connections.
int control_event_or_conn_status (or_connection_t *conn, or_conn_status_event_t tp, int reason)
 Called when the status of an OR connection conn changes: tell any interested control connections.
int control_event_stream_bandwidth (edge_connection_t *edge_conn)
 Print out STREAM_BW event for a single conn.
int control_event_stream_bandwidth_used (void)
 A second or more has elapsed: tell any interested control connections how much bandwidth streams have used.
int control_event_bandwidth_used (uint32_t n_read, uint32_t n_written)
 A second or more has elapsed: tell any interested control connections how much bandwidth we used.
void disable_control_logging (void)
 Called when we are sending a log message to the controllers: suspend sending further log messages to the controllers until we're done.
void enable_control_logging (void)
 We're done sending a log message to the controllers: re-enable controller logging.
void control_event_logmsg (int severity, uint32_t domain, const char *msg)
 We got a log message: tell any interested control connections.
int control_event_descriptors_changed (smartlist_t *routers)
 Called whenever we receive new router descriptors: tell any interested control connections.
int control_event_address_mapped (const char *from, const char *to, time_t expires, const char *error)
 Called when an address mapping on from from changes to to.
int control_event_or_authdir_new_descriptor (const char *action, const char *desc, size_t desclen, const char *msg)
 The authoritative dirserver has received a new descriptor that has passed basic syntax checks and is properly self-signed.
static int control_event_networkstatus_changed_helper (smartlist_t *statuses, uint16_t event, const char *event_string)
 Helper function for NS-style events.
int control_event_networkstatus_changed (smartlist_t *statuses)
 Called when the routerstatus_ts statuses have changed: sends an NS event to any controller that cares.
int control_event_newconsensus (const networkstatus_t *consensus)
 Called when we get a new consensus networkstatus.
int control_event_buildtimeout_set (const circuit_build_times_t *cbt, buildtimeout_set_event_t type)
 Called when we compute a new circuitbuildtimeout.
int control_event_signal (uintptr_t signal)
 Called when a signal has been processed from signal_callback.
int control_event_networkstatus_changed_single (const routerstatus_t *rs)
 Called when a single local_routerstatus_t has changed: Sends an NS event to any controller that cares.
int control_event_my_descriptor_changed (void)
 Our own router descriptor has changed; tell any controllers that care.
int control_event_general_status (int severity, const char *format,...)
 Format and send an EVENT_STATUS_GENERAL event whose main text is obtained by formatting the arguments using the printf-style format.
int control_event_client_status (int severity, const char *format,...)
 Format and send an EVENT_STATUS_CLIENT event whose main text is obtained by formatting the arguments using the printf-style format.
int control_event_server_status (int severity, const char *format,...)
 Format and send an EVENT_STATUS_SERVER event whose main text is obtained by formatting the arguments using the printf-style format.
int control_event_guard (const char *nickname, const char *digest, const char *status)
 Called when the status of an entry guard with the given nickname and identity digest has changed to status: tells any controllers that care.
int control_event_conf_changed (smartlist_t *elements)
 Called when a configuration option changes.
int init_cookie_authentication (int enabled)
 Choose a random authentication cookie and write it to disk.
static void owning_controller_procmon_cb (void *unused)
 Process-termination monitor callback for Tor's owning controller process.
void monitor_owning_controller_process (const char *process_spec)
 Set process_spec as Tor's owning controller process.
static int bootstrap_status_to_string (bootstrap_status_t s, const char **tag, const char **summary)
 Convert the name of a bootstrapping phase s into strings tag and summary suitable for display by the controller.
void control_event_bootstrap (bootstrap_status_t status, int progress)
 Called when Tor has made progress at bootstrapping its directory information and initial circuits.
void control_event_bootstrap_problem (const char *warn, int reason)
 Called when Tor has failed to make bootstrapping progress in a way that indicates a problem.
void control_event_clients_seen (const char *controller_str)
 We just generated a new summary of which countries we've seen clients from recently.

Variables

static event_mask_t global_event_mask = 0
 An event mask of all the events that any controller is interested in receiving.
static int disable_log_messages = 0
 True iff we have disabled log messages from being sent to the controller.
static int authentication_cookie_is_set = 0
 If true, we've set authentication_cookie to a secret code and stored it to disk.
static char authentication_cookie [AUTHENTICATION_COOKIE_LEN]
 If authentication_cookie_is_set, a secret cookie that we've stored to disk and which we're using to authenticate controllers.
static char last_sent_bootstrap_message [BOOTSTRAP_MSG_LEN]
 What was the last bootstrap phase message we sent? We keep track of this so we can respond to getinfo status/bootstrap-phase queries.
static struct control_event_t []
 Table mapping event values to their names.
static const getinfo_item_t getinfo_items []
 Table mapping questions accepted by GETINFO to the functions that know how to answer them.
static char * owning_controller_process_spec = NULL
 A copy of the process specifier of Tor's owning controller, or NULL if this Tor instance is not currently owned by a process.
static tor_process_monitor_towning_controller_process_monitor = NULL
 A process-termination monitor for Tor's owning controller, or NULL if this Tor instance is not currently owned by a process.
static int bootstrap_percent = BOOTSTRAP_STATUS_UNDEF
 What percentage through the bootstrap process are we? We remember this so we can avoid sending redundant bootstrap status events, and so we can guess context for the bootstrap messages which are ambiguous.
static int bootstrap_problems = 0
 How many problems have we had getting to the next bootstrapping phase? These include failure to establish a connection to a Tor relay, failures to finish the TLS handshake, failures to validate the consensus document, etc.

Detailed Description

Implementation for Tor's control-socket interface.

See doc/spec/control-spec.txt for full details on protocol.

Definition in file control.c.


Class Documentation

struct control_event_t

Helper structure: maps event values to their names.

Definition at line 924 of file control.c.

Class Members
uint16_t event_code
const char * event_name
struct getinfo_item_t

A single item for the GETINFO question-to-answer-function table.

Definition at line 2087 of file control.c.

Collaboration diagram for getinfo_item_t:
Class Members
const char * desc Description of the variable.
getinfo_helper_t fn The function that knows the answer: NULL if this entry is documentation-only.
int is_prefix
const char * varname The value (or prefix) of the question.

Define Documentation

#define _EVENT_MAX   0x0019

Definition at line 79 of file control.c.

#define _EVENT_MIN   0x0001

Definition at line 53 of file control.c.

#define ALL_FORMATS   1

Flag for event_format_t.

Indicates that we should use the one standard format.

Definition at line 130 of file control.c.

#define AUTHENTICATION_COOKIE_LEN   32

If we're using cookie-type authentication, how long should our cookies be?

Definition at line 104 of file control.c.

#define BOOTSTRAP_MSG_LEN   1024

A sufficiently large size to record the last bootstrap phase string.

Definition at line 121 of file control.c.

#define BOOTSTRAP_PROBLEM_THRESHOLD   10

Definition at line 4583 of file control.c.

#define CONTROL_PRIVATE

Definition at line 11 of file control.c.

#define DOC (   name,
  desc 
)    { name, NULL, desc, 0 }

Definition at line 2097 of file control.c.

#define EVENT_ADDRMAP   0x000C

Definition at line 65 of file control.c.

#define EVENT_BANDWIDTH_USED   0x0004

Definition at line 57 of file control.c.

#define EVENT_BUILDTIMEOUT_SET   0x0017

Definition at line 76 of file control.c.

#define EVENT_CIRCUIT_STATUS   0x0001

Definition at line 54 of file control.c.

#define EVENT_CIRCUIT_STATUS_MINOR   0x0005

Definition at line 58 of file control.c.

#define EVENT_CLIENTS_SEEN   0x0015

Definition at line 74 of file control.c.

#define EVENT_CONF_CHANGED   0x0019

Definition at line 78 of file control.c.

#define EVENT_DEBUG_MSG   0x0007

Definition at line 60 of file control.c.

#define EVENT_DESCCHANGED   0x000E

Definition at line 67 of file control.c.

#define EVENT_ERR_MSG   0x000B

Definition at line 64 of file control.c.

#define EVENT_GUARD   0x0013

Definition at line 72 of file control.c.

#define EVENT_INFO_MSG   0x0008

Definition at line 61 of file control.c.

#define EVENT_IS_INTERESTING (   e)    (global_event_mask & (1<<(e)))

Macro: true if any control connection is interested in events of type e.

Definition at line 99 of file control.c.

#define EVENT_NEW_DESC   0x0006

Definition at line 59 of file control.c.

#define EVENT_NEWCONSENSUS   0x0016

Definition at line 75 of file control.c.

#define EVENT_NOTICE_MSG   0x0009

Definition at line 62 of file control.c.

#define EVENT_OR_CONN_STATUS   0x0003

Definition at line 56 of file control.c.

#define EVENT_SIGNAL   0x0018

Definition at line 77 of file control.c.

#define EVENT_STATUS_CLIENT   0x0010

Definition at line 69 of file control.c.

#define EVENT_STATUS_GENERAL   0x0012

Definition at line 71 of file control.c.

#define EVENT_STATUS_SERVER   0x0011

Definition at line 70 of file control.c.

#define EVENT_STREAM_BANDWIDTH_USED   0x0014

Definition at line 73 of file control.c.

#define EVENT_STREAM_STATUS   0x0002

Definition at line 55 of file control.c.

#define EVENT_WARN_MSG   0x000A

Definition at line 63 of file control.c.

#define ITEM (   name,
  fn,
  desc 
)    { name, getinfo_helper_##fn, desc, 0 }

Definition at line 2095 of file control.c.

#define MAX_COMMAND_LINE_LENGTH   (1024*1024)

Do not accept any control command of more than 1MB in length.

Anything that needs to be anywhere near this long probably means that one of our interfaces is broken.

Definition at line 3212 of file control.c.

#define PREFIX (   name,
  fn,
  desc 
)    { name, getinfo_helper_##fn, desc, 1 }

Definition at line 2096 of file control.c.

#define SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT   "Tor safe cookie authentication controller-to-server hash"

Definition at line 116 of file control.c.

Definition at line 118 of file control.c.

#define SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT   "Tor safe cookie authentication server-to-controller hash"

Definition at line 114 of file control.c.

#define STATE_IS_OPEN (   s)    ((s) == CONTROL_CONN_STATE_OPEN)

Yield true iff s is the state of a control_connection_t that has finished authentication and is accepting commands.

Definition at line 47 of file control.c.


Typedef Documentation

typedef int event_format_t

Bit field of flags to select how to format a controller event.

Recognized flag is ALL_FORMATS.

Definition at line 134 of file control.c.

typedef uint32_t event_mask_t

Bitfield: The bit 1<<e is set if any open control connection is interested in events of type e.

We use this so that we can decide to skip generating event messages that nobody has interest in without having to walk over the global connection list to find out.

Definition at line 88 of file control.c.

typedef int(* getinfo_helper_t)(control_connection_t *, const char *q, char **a, const char **error_out)

Callback function for GETINFO: on a given control connection, try to answer the question q and store the newly-allocated answer in *a.

If an internal error occurs, return -1 and optionally set *error_out to point to an error message to be delivered to the controller. On success, or if the key is not recognized, return 0. Do not set a if the key is not recognized.

Definition at line 2082 of file control.c.

A single item for the GETINFO question-to-answer-function table.


Function Documentation

static int address_is_invalid_mapaddress_target ( const char *  addr) [static]

Return true iff addr is unusable as a mapaddress target because of containing funny characters.

Definition at line 1319 of file control.c.

{
  if (!strcmpstart(addr, "*."))
    return address_is_invalid_destination(addr+2, 1);
  else
    return address_is_invalid_destination(addr, 1);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int bootstrap_status_to_string ( bootstrap_status_t  s,
const char **  tag,
const char **  summary 
) [static]

Convert the name of a bootstrapping phase s into strings tag and summary suitable for display by the controller.

Definition at line 4496 of file control.c.

{
  switch (s) {
    case BOOTSTRAP_STATUS_UNDEF:
      *tag = "undef";
      *summary = "Undefined";
      break;
    case BOOTSTRAP_STATUS_STARTING:
      *tag = "starting";
      *summary = "Starting";
      break;
    case BOOTSTRAP_STATUS_CONN_DIR:
      *tag = "conn_dir";
      *summary = "Connecting to directory server";
      break;
    case BOOTSTRAP_STATUS_HANDSHAKE:
      *tag = "status_handshake";
      *summary = "Finishing handshake";
      break;
    case BOOTSTRAP_STATUS_HANDSHAKE_DIR:
      *tag = "handshake_dir";
      *summary = "Finishing handshake with directory server";
      break;
    case BOOTSTRAP_STATUS_ONEHOP_CREATE:
      *tag = "onehop_create";
      *summary = "Establishing an encrypted directory connection";
      break;
    case BOOTSTRAP_STATUS_REQUESTING_STATUS:
      *tag = "requesting_status";
      *summary = "Asking for networkstatus consensus";
      break;
    case BOOTSTRAP_STATUS_LOADING_STATUS:
      *tag = "loading_status";
      *summary = "Loading networkstatus consensus";
      break;
    case BOOTSTRAP_STATUS_LOADING_KEYS:
      *tag = "loading_keys";
      *summary = "Loading authority key certs";
      break;
    case BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS:
      *tag = "requesting_descriptors";
      *summary = "Asking for relay descriptors";
      break;
    case BOOTSTRAP_STATUS_LOADING_DESCRIPTORS:
      *tag = "loading_descriptors";
      *summary = "Loading relay descriptors";
      break;
    case BOOTSTRAP_STATUS_CONN_OR:
      *tag = "conn_or";
      *summary = "Connecting to the Tor network";
      break;
    case BOOTSTRAP_STATUS_HANDSHAKE_OR:
      *tag = "handshake_or";
      *summary = "Finishing handshake with first hop";
      break;
    case BOOTSTRAP_STATUS_CIRCUIT_CREATE:
      *tag = "circuit_create";
      *summary = "Establishing a Tor circuit";
      break;
    case BOOTSTRAP_STATUS_DONE:
      *tag = "done";
      *summary = "Done";
      break;
    default:
//      log_warn(LD_BUG, "Unrecognized bootstrap status code %d", s);
      *tag = *summary = "unknown";
      return -1;
  }
  return 0;
}

Here is the caller graph for this function:

static char* circuit_describe_status_for_controller ( origin_circuit_t circ) [static]

Allocate and return a description of circ's current status, including its path (if any).

Definition at line 1802 of file control.c.

{
  char *rv;
  smartlist_t *descparts = smartlist_new();

  {
    char *vpath = circuit_list_path_for_controller(circ);
    if (*vpath) {
      smartlist_add(descparts, vpath);
    } else {
      tor_free(vpath); /* empty path; don't put an extra space in the result */
    }
  }

  {
    cpath_build_state_t *build_state = circ->build_state;
    smartlist_t *flaglist = smartlist_new();
    char *flaglist_joined;

    if (build_state->onehop_tunnel)
      smartlist_add(flaglist, (void *)"ONEHOP_TUNNEL");
    if (build_state->is_internal)
      smartlist_add(flaglist, (void *)"IS_INTERNAL");
    if (build_state->need_capacity)
      smartlist_add(flaglist, (void *)"NEED_CAPACITY");
    if (build_state->need_uptime)
      smartlist_add(flaglist, (void *)"NEED_UPTIME");

    /* Only emit a BUILD_FLAGS argument if it will have a non-empty value. */
    if (smartlist_len(flaglist)) {
      flaglist_joined = smartlist_join_strings(flaglist, ",", 0, NULL);

      smartlist_add_asprintf(descparts, "BUILD_FLAGS=%s", flaglist_joined);

      tor_free(flaglist_joined);
    }

    smartlist_free(flaglist);
  }

  smartlist_add_asprintf(descparts, "PURPOSE=%s",
                    circuit_purpose_to_controller_string(circ->_base.purpose));

  {
    const char *hs_state =
      circuit_purpose_to_controller_hs_state_string(circ->_base.purpose);

    if (hs_state != NULL) {
      smartlist_add_asprintf(descparts, "HS_STATE=%s", hs_state);
    }
  }

  if (circ->rend_data != NULL) {
    smartlist_add_asprintf(descparts, "REND_QUERY=%s",
                 circ->rend_data->onion_address);
  }

  {
    char tbuf[ISO_TIME_USEC_LEN+1];
    format_iso_time_nospace_usec(tbuf, &circ->_base.timestamp_created);

    smartlist_add_asprintf(descparts, "TIME_CREATED=%s", tbuf);
  }

  rv = smartlist_join_strings(descparts, " ", 0, NULL);

  SMARTLIST_FOREACH(descparts, char *, cp, tor_free(cp));
  smartlist_free(descparts);

  return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static uint8_t circuit_purpose_from_string ( const char *  string) [static]

Given a string, convert it to a circuit purpose.

Definition at line 2323 of file control.c.

{
  if (!strcasecmpstart(string, "purpose="))
    string += strlen("purpose=");

  if (!strcasecmp(string, "general"))
    return CIRCUIT_PURPOSE_C_GENERAL;
  else if (!strcasecmp(string, "controller"))
    return CIRCUIT_PURPOSE_CONTROLLER;
  else
    return CIRCUIT_PURPOSE_UNKNOWN;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Called when conn is being freed.

Definition at line 3179 of file control.c.

{
  tor_assert(conn);

  conn->event_mask = 0;
  control_update_global_event_mask();

  if (conn->is_owning_control_connection) {
    lost_owning_controller("connection", "closed");
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

Called when conn has no more bytes left on its outbuf.

Definition at line 3137 of file control.c.

{
  tor_assert(conn);
  return 0;
}

Here is the caller graph for this function:

Called when data has arrived on a v1 control connection: Try to fetch commands from conn->inbuf, and execute them.

Definition at line 3233 of file control.c.

{
  size_t data_len;
  uint32_t cmd_data_len;
  int cmd_len;
  char *args;

  tor_assert(conn);
  tor_assert(conn->_base.state == CONTROL_CONN_STATE_OPEN ||
             conn->_base.state == CONTROL_CONN_STATE_NEEDAUTH);

  if (!conn->incoming_cmd) {
    conn->incoming_cmd = tor_malloc(1024);
    conn->incoming_cmd_len = 1024;
    conn->incoming_cmd_cur_len = 0;
  }

  if (conn->_base.state == CONTROL_CONN_STATE_NEEDAUTH &&
      peek_connection_has_control0_command(TO_CONN(conn))) {
    /* Detect v0 commands and send a "no more v0" message. */
    size_t body_len;
    char buf[128];
    set_uint16(buf+2, htons(0x0000)); /* type == error */
    set_uint16(buf+4, htons(0x0001)); /* code == internal error */
    strlcpy(buf+6, "The v0 control protocol is not supported by Tor 0.1.2.17 "
            "and later; upgrade your controller.",
            sizeof(buf)-6);
    body_len = 2+strlen(buf+6)+2; /* code, msg, nul. */
    set_uint16(buf+0, htons(body_len));
    connection_write_to_buf(buf, 4+body_len, TO_CONN(conn));

    connection_mark_and_flush(TO_CONN(conn));
    return 0;
  }

 again:
  while (1) {
    size_t last_idx;
    int r;
    /* First, fetch a line. */
    do {
      data_len = conn->incoming_cmd_len - conn->incoming_cmd_cur_len;
      r = connection_fetch_from_buf_line(TO_CONN(conn),
                              conn->incoming_cmd+conn->incoming_cmd_cur_len,
                              &data_len);
      if (r == 0)
        /* Line not all here yet. Wait. */
        return 0;
      else if (r == -1) {
        if (data_len + conn->incoming_cmd_cur_len > MAX_COMMAND_LINE_LENGTH) {
          connection_write_str_to_buf("500 Line too long.\r\n", conn);
          connection_stop_reading(TO_CONN(conn));
          connection_mark_and_flush(TO_CONN(conn));
        }
        while (conn->incoming_cmd_len < data_len+conn->incoming_cmd_cur_len)
          conn->incoming_cmd_len *= 2;
        conn->incoming_cmd = tor_realloc(conn->incoming_cmd,
                                         conn->incoming_cmd_len);
      }
    } while (r != 1);

    tor_assert(data_len);

    last_idx = conn->incoming_cmd_cur_len;
    conn->incoming_cmd_cur_len += (int)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;
    /* XXXX this code duplication is kind of dumb. */
    if (last_idx+3 == conn->incoming_cmd_cur_len &&
        tor_memeq(conn->incoming_cmd + last_idx, ".\r\n", 3)) {
      /* Just appended ".\r\n"; we're done. Remove it. */
      conn->incoming_cmd[last_idx] = '\0';
      conn->incoming_cmd_cur_len -= 3;
      break;
    } else if (last_idx+2 == conn->incoming_cmd_cur_len &&
               tor_memeq(conn->incoming_cmd + last_idx, ".\n", 2)) {
      /* Just appended ".\n"; we're done. Remove it. */
      conn->incoming_cmd[last_idx] = '\0';
      conn->incoming_cmd_cur_len -= 2;
      break;
    }
    /* 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.
   */
  cmd_len = 0;
  while ((size_t)cmd_len < data_len
         && !TOR_ISSPACE(conn->incoming_cmd[cmd_len]))
    ++cmd_len;

  conn->incoming_cmd[cmd_len]='\0';
  args = conn->incoming_cmd+cmd_len+1;
  tor_assert(data_len>(size_t)cmd_len);
  data_len -= (cmd_len+1); /* skip the command and NUL we added after it */
  while (TOR_ISSPACE(*args)) {
    ++args;
    --data_len;
  }

  /* If the connection is already closing, ignore further commands */
  if (TO_CONN(conn)->marked_for_close) {
    return 0;
  }

  /* Otherwise, Quit is always valid. */
  if (!strcasecmp(conn->incoming_cmd, "QUIT")) {
    connection_write_str_to_buf("250 closing connection\r\n", conn);
    connection_mark_and_flush(TO_CONN(conn));
    return 0;
  }

  if (conn->_base.state == CONTROL_CONN_STATE_NEEDAUTH &&
      !is_valid_initial_command(conn, conn->incoming_cmd)) {
    connection_write_str_to_buf("514 Authentication required.\r\n", conn);
    connection_mark_for_close(TO_CONN(conn));
    return 0;
  }

  if (data_len >= UINT32_MAX) {
    connection_write_str_to_buf("500 A 4GB command? Nice try.\r\n", conn);
    connection_mark_for_close(TO_CONN(conn));
    return 0;
  }

  /* XXXX Why is this not implemented as a table like the GETINFO
   * items are?  Even handling the plus signs at the beginnings of
   * commands wouldn't be very hard with proper macros. */
  cmd_data_len = (uint32_t)data_len;
  if (!strcasecmp(conn->incoming_cmd, "SETCONF")) {
    if (handle_control_setconf(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "RESETCONF")) {
    if (handle_control_resetconf(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "GETCONF")) {
    if (handle_control_getconf(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "+LOADCONF")) {
    if (handle_control_loadconf(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "SETEVENTS")) {
    if (handle_control_setevents(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "AUTHENTICATE")) {
    if (handle_control_authenticate(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "SAVECONF")) {
    if (handle_control_saveconf(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "SIGNAL")) {
    if (handle_control_signal(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "TAKEOWNERSHIP")) {
    if (handle_control_takeownership(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "MAPADDRESS")) {
    if (handle_control_mapaddress(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "GETINFO")) {
    if (handle_control_getinfo(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "EXTENDCIRCUIT")) {
    if (handle_control_extendcircuit(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "SETCIRCUITPURPOSE")) {
    if (handle_control_setcircuitpurpose(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "SETROUTERPURPOSE")) {
    connection_write_str_to_buf("511 SETROUTERPURPOSE is obsolete.\r\n", conn);
  } else if (!strcasecmp(conn->incoming_cmd, "ATTACHSTREAM")) {
    if (handle_control_attachstream(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "+POSTDESCRIPTOR")) {
    if (handle_control_postdescriptor(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "REDIRECTSTREAM")) {
    if (handle_control_redirectstream(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "CLOSESTREAM")) {
    if (handle_control_closestream(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "CLOSECIRCUIT")) {
    if (handle_control_closecircuit(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "USEFEATURE")) {
    if (handle_control_usefeature(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "RESOLVE")) {
    if (handle_control_resolve(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "PROTOCOLINFO")) {
    if (handle_control_protocolinfo(conn, cmd_data_len, args))
      return -1;
  } else if (!strcasecmp(conn->incoming_cmd, "AUTHCHALLENGE")) {
    if (handle_control_authchallenge(conn, cmd_data_len, args))
      return -1;
  } else {
    connection_printf_to_buf(conn, "510 Unrecognized command \"%s\"\r\n",
                             conn->incoming_cmd);
  }

  conn->incoming_cmd_cur_len = 0;
  goto again;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Called when conn has gotten its socket closed.

Definition at line 3145 of file control.c.

{
  tor_assert(conn);

  log_info(LD_CONTROL,"Control connection reached EOF. Closing.");
  connection_mark_for_close(TO_CONN(conn));
  return 0;
}

Here is the caller graph for this function:

static void connection_printf_to_buf ( control_connection_t conn,
const char *  format,
  ... 
) [static]

Acts like sprintf, but writes its formatted string to the end of conn->outbuf.

Definition at line 508 of file control.c.

{
  va_list ap;
  char *buf = NULL;
  int len;

  va_start(ap,format);
  len = tor_vasprintf(&buf, format, ap);
  va_end(ap);

  if (len < 0) {
    log_err(LD_BUG, "Unable to format string for controller.");
    tor_assert(0);
  }

  connection_write_to_buf(buf, (size_t)len, TO_CONN(conn));

  tor_free(buf);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static INLINE void connection_write_str_to_buf ( const char *  s,
control_connection_t conn 
) [static]

Append a NUL-terminated string s to the end of conn->outbuf.

Definition at line 318 of file control.c.

{
  size_t len = strlen(s);
  connection_write_to_buf(s, len, TO_CONN(conn));
}

Here is the call graph for this function:

Here is the caller graph for this function:

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.

Definition at line 272 of file control.c.

{
  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_STATUS_GENERAL)) {
    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;
  }
  if (min_log_event <= max_log_event)
    change_callback_log_severity(event_to_log_severity(min_log_event),
                                 event_to_log_severity(max_log_event),
                                 control_event_logmsg);
  else
    change_callback_log_severity(LOG_ERR, LOG_ERR,
                                 control_event_logmsg);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_address_mapped ( const char *  from,
const char *  to,
time_t  expires,
const char *  error 
)

Called when an address mapping on from from changes to to.

expires values less than 3 are special; see connection_edge.c. If error is non-NULL, it is an error code describing the failure mode of the mapping.

Definition at line 3975 of file control.c.

{
  if (!EVENT_IS_INTERESTING(EVENT_ADDRMAP))
    return 0;

  if (expires < 3 || expires == TIME_MAX)
    send_control_event(EVENT_ADDRMAP, ALL_FORMATS,
                                "650 ADDRMAP %s %s NEVER %s\r\n", from, to,
                                error?error:"");
  else {
    char buf[ISO_TIME_LEN+1];
    char buf2[ISO_TIME_LEN+1];
    format_local_iso_time(buf,expires);
    format_iso_time(buf2,expires);
    send_control_event(EVENT_ADDRMAP, ALL_FORMATS,
                                "650 ADDRMAP %s %s \"%s\""
                                " %s%sEXPIRES=\"%s\"\r\n",
                                from, to, buf,
                                error?error:"", error?" ":"",
                                buf2);
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_bandwidth_used ( uint32_t  n_read,
uint32_t  n_written 
)

A second or more has elapsed: tell any interested control connections how much bandwidth we used.

Definition at line 3859 of file control.c.

{
  if (EVENT_IS_INTERESTING(EVENT_BANDWIDTH_USED)) {
    send_control_event(EVENT_BANDWIDTH_USED, ALL_FORMATS,
                       "650 BW %lu %lu\r\n",
                       (unsigned long)n_read,
                       (unsigned long)n_written);
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void control_event_bootstrap ( bootstrap_status_t  status,
int  progress 
)

Called when Tor has made progress at bootstrapping its directory information and initial circuits.

status is the new status, that is, what task we will be doing next. progress is zero if we just started this task, else it represents progress on the task.

Definition at line 4592 of file control.c.

{
  const char *tag, *summary;
  char buf[BOOTSTRAP_MSG_LEN];

  if (bootstrap_percent == BOOTSTRAP_STATUS_DONE)
    return; /* already bootstrapped; nothing to be done here. */

  /* special case for handshaking status, since our TLS handshaking code
   * can't distinguish what the connection is going to be for. */
  if (status == BOOTSTRAP_STATUS_HANDSHAKE) {
    if (bootstrap_percent < BOOTSTRAP_STATUS_CONN_OR) {
      status =  BOOTSTRAP_STATUS_HANDSHAKE_DIR;
    } else {
      status = BOOTSTRAP_STATUS_HANDSHAKE_OR;
    }
  }

  if (status > bootstrap_percent ||
      (progress && progress > bootstrap_percent)) {
    bootstrap_status_to_string(status, &tag, &summary);
    log(status ? LOG_NOTICE : LOG_INFO, LD_CONTROL,
        "Bootstrapped %d%%: %s.", progress ? progress : status, summary);
    tor_snprintf(buf, sizeof(buf),
        "BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\"",
        progress ? progress : status, tag, summary);
    tor_snprintf(last_sent_bootstrap_message,
                 sizeof(last_sent_bootstrap_message),
                 "NOTICE %s", buf);
    control_event_client_status(LOG_NOTICE, "%s", buf);
    if (status > bootstrap_percent) {
      bootstrap_percent = status; /* new milestone reached */
    }
    if (progress > bootstrap_percent) {
      /* incremental progress within a milestone */
      bootstrap_percent = progress;
      bootstrap_problems = 0; /* Progress! Reset our problem counter. */
    }
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

void control_event_bootstrap_problem ( const char *  warn,
int  reason 
)

Called when Tor has failed to make bootstrapping progress in a way that indicates a problem.

warn gives a hint as to why, and reason provides an "or_conn_end_reason" tag.

Definition at line 4638 of file control.c.

{
  int status = bootstrap_percent;
  const char *tag, *summary;
  char buf[BOOTSTRAP_MSG_LEN];
  const char *recommendation = "ignore";
  int severity;

  /* bootstrap_percent must not be in "undefined" state here. */
  tor_assert(status >= 0);

  if (bootstrap_percent == 100)
    return; /* already bootstrapped; nothing to be done here. */

  bootstrap_problems++;

  if (bootstrap_problems >= BOOTSTRAP_PROBLEM_THRESHOLD)
    recommendation = "warn";

  if (reason == END_OR_CONN_REASON_NO_ROUTE)
    recommendation = "warn";

  if (get_options()->UseBridges &&
      !any_bridge_descriptors_known() &&
      !any_pending_bridge_descriptor_fetches())
    recommendation = "warn";

  while (status>=0 && bootstrap_status_to_string(status, &tag, &summary) < 0)
    status--; /* find a recognized status string based on current progress */
  status = bootstrap_percent; /* set status back to the actual number */

  severity = !strcmp(recommendation, "warn") ? LOG_WARN : LOG_INFO;

  log_fn(severity,
         LD_CONTROL, "Problem bootstrapping. Stuck at %d%%: %s. (%s; %s; "
         "count %d; recommendation %s)",
         status, summary, warn,
         orconn_end_reason_to_control_string(reason),
         bootstrap_problems, recommendation);

  connection_or_report_broken_states(severity, LD_HANDSHAKE);

  tor_snprintf(buf, sizeof(buf),
      "BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\" WARNING=\"%s\" REASON=%s "
      "COUNT=%d RECOMMENDATION=%s",
      bootstrap_percent, tag, summary, warn,
      orconn_end_reason_to_control_string(reason), bootstrap_problems,
      recommendation);
  tor_snprintf(last_sent_bootstrap_message,
               sizeof(last_sent_bootstrap_message),
               "WARN %s", buf);
  control_event_client_status(LOG_WARN, "%s", buf);
}

Here is the call graph for this function:

Here is the caller graph for this function:

Called when we compute a new circuitbuildtimeout.

Definition at line 4101 of file control.c.

{
  const char *type_string = NULL;
  double qnt;

  if (!control_event_is_interesting(EVENT_BUILDTIMEOUT_SET))
    return 0;

  qnt = circuit_build_times_quantile_cutoff();

  switch (type) {
    case BUILDTIMEOUT_SET_EVENT_COMPUTED:
      type_string = "COMPUTED";
      break;
    case BUILDTIMEOUT_SET_EVENT_RESET:
      type_string = "RESET";
      qnt = 1.0;
      break;
    case BUILDTIMEOUT_SET_EVENT_SUSPENDED:
      type_string = "SUSPENDED";
      qnt = 1.0;
      break;
    case BUILDTIMEOUT_SET_EVENT_DISCARD:
      type_string = "DISCARD";
      qnt = 1.0;
      break;
    case BUILDTIMEOUT_SET_EVENT_RESUME:
      type_string = "RESUME";
      break;
    default:
      type_string = "UNKNOWN";
      break;
  }

  send_control_event(EVENT_BUILDTIMEOUT_SET, ALL_FORMATS,
                     "650 BUILDTIMEOUT_SET %s TOTAL_TIMES=%lu "
                     "TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f "
                     "TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f\r\n",
                     type_string, (unsigned long)cbt->total_build_times,
                     (unsigned long)cbt->timeout_ms,
                     (unsigned long)cbt->Xm, cbt->alpha, qnt,
                     circuit_build_times_timeout_rate(cbt),
                     (unsigned long)cbt->close_ms,
                     circuit_build_times_close_rate(cbt));

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_circuit_cannibalized ( origin_circuit_t circ,
int  old_purpose,
const struct timeval old_tv_created 
)

circ has changed its purpose from old_purpose, and its created-time from old_tv_created: tell any interested controllers.

Definition at line 3589 of file control.c.

{
  return control_event_circuit_status_minor(circ,
                                            CIRC_MINOR_EVENT_CANNIBALIZED,
                                            old_purpose,
                                            old_tv_created);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_circuit_purpose_changed ( origin_circuit_t circ,
int  old_purpose 
)

circ has changed its purpose from old_purpose: tell any interested controllers.

Definition at line 3575 of file control.c.

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_circuit_status ( origin_circuit_t circ,
circuit_status_event_t  tp,
int  reason_code 
)

Something major has happened to circuit circ: tell any interested control connections.

Definition at line 3446 of file control.c.

{
  const char *status;
  char reasons[64] = "";
  if (!EVENT_IS_INTERESTING(EVENT_CIRCUIT_STATUS))
    return 0;
  tor_assert(circ);

  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:
      log_warn(LD_BUG, "Unrecognized status code %d", (int)tp);
      tor_fragile_assert();
      return 0;
    }

  if (tp == CIRC_EVENT_FAILED || tp == CIRC_EVENT_CLOSED) {
    const char *reason_str = circuit_end_reason_to_control_string(reason_code);
    char unk_reason_buf[16];
    if (!reason_str) {
      tor_snprintf(unk_reason_buf, 16, "UNKNOWN_%d", reason_code);
      reason_str = unk_reason_buf;
    }
    if (reason_code > 0 && reason_code & END_CIRC_REASON_FLAG_REMOTE) {
      tor_snprintf(reasons, sizeof(reasons),
                   " REASON=DESTROYED REMOTE_REASON=%s", reason_str);
    } else {
      tor_snprintf(reasons, sizeof(reasons),
                   " REASON=%s", reason_str);
    }
  }

  {
    char *circdesc = circuit_describe_status_for_controller(circ);
    const char *sp = strlen(circdesc) ? " " : "";
    send_control_event(EVENT_CIRCUIT_STATUS, ALL_FORMATS,
                                "650 CIRC %lu %s%s%s%s\r\n",
                                (unsigned long)circ->global_identifier,
                                status, sp,
                                circdesc,
                                reasons);
    tor_free(circdesc);
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int control_event_circuit_status_minor ( origin_circuit_t circ,
circuit_status_minor_event_t  e,
int  purpose,
const struct timeval tv 
) [static]

Something minor has happened to circuit circ: tell any interested control connections.

Definition at line 3502 of file control.c.

{
  const char *event_desc;
  char event_tail[160] = "";
  if (!EVENT_IS_INTERESTING(EVENT_CIRCUIT_STATUS_MINOR))
    return 0;
  tor_assert(circ);

  switch (e)
    {
    case CIRC_MINOR_EVENT_PURPOSE_CHANGED:
      event_desc = "PURPOSE_CHANGED";

      {
        /* event_tail can currently be up to 68 chars long */
        const char *hs_state_str =
          circuit_purpose_to_controller_hs_state_string(purpose);
        tor_snprintf(event_tail, sizeof(event_tail),
                     " OLD_PURPOSE=%s%s%s",
                     circuit_purpose_to_controller_string(purpose),
                     (hs_state_str != NULL) ? " OLD_HS_STATE=" : "",
                     (hs_state_str != NULL) ? hs_state_str : "");
      }

      break;
    case CIRC_MINOR_EVENT_CANNIBALIZED:
      event_desc = "CANNIBALIZED";

      {
        /* event_tail can currently be up to 130 chars long */
        const char *hs_state_str =
          circuit_purpose_to_controller_hs_state_string(purpose);
        const struct timeval *old_timestamp_created = tv;
        char tbuf[ISO_TIME_USEC_LEN+1];
        format_iso_time_nospace_usec(tbuf, old_timestamp_created);

        tor_snprintf(event_tail, sizeof(event_tail),
                     " OLD_PURPOSE=%s%s%s OLD_TIME_CREATED=%s",
                     circuit_purpose_to_controller_string(purpose),
                     (hs_state_str != NULL) ? " OLD_HS_STATE=" : "",
                     (hs_state_str != NULL) ? hs_state_str : "",
                     tbuf);
      }

      break;
    default:
      log_warn(LD_BUG, "Unrecognized status code %d", (int)e);
      tor_fragile_assert();
      return 0;
    }

  {
    char *circdesc = circuit_describe_status_for_controller(circ);
    const char *sp = strlen(circdesc) ? " " : "";
    send_control_event(EVENT_CIRCUIT_STATUS_MINOR, ALL_FORMATS,
                       "650 CIRC_MINOR %lu %s%s%s%s\r\n",
                       (unsigned long)circ->global_identifier,
                       event_desc, sp,
                       circdesc,
                       event_tail);
    tor_free(circdesc);
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_client_status ( int  severity,
const char *  format,
  ... 
)

Format and send an EVENT_STATUS_CLIENT event whose main text is obtained by formatting the arguments using the printf-style format.

Definition at line 4283 of file control.c.

{
  va_list ap;
  int r;
  if (!EVENT_IS_INTERESTING(EVENT_STATUS_CLIENT))
    return 0;

  va_start(ap, format);
  r = control_event_status(EVENT_STATUS_CLIENT, severity, format, ap);
  va_end(ap);
  return r;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void control_event_clients_seen ( const char *  controller_str)

We just generated a new summary of which countries we've seen clients from recently.

Send a copy to the controller in case it wants to display it for the user.

Definition at line 4696 of file control.c.

{
  send_control_event(EVENT_CLIENTS_SEEN, 0,
    "650 CLIENTS_SEEN %s\r\n", controller_str);
}

Here is the call graph for this function:

Here is the caller graph for this function:

Called when a configuration option changes.

This is generally triggered by SETCONF requests and RELOAD/SIGHUP signals. The elements is a smartlist_t containing (key, value, ...) pairs in sequence. value can be NULL.

Definition at line 4343 of file control.c.

{
  int i;
  char *result;
  smartlist_t *lines;
  if (!EVENT_IS_INTERESTING(EVENT_CONF_CHANGED) ||
      smartlist_len(elements) == 0) {
    return 0;
  }
  lines = smartlist_new();
  for (i = 0; i < smartlist_len(elements); i += 2) {
    char *k = smartlist_get(elements, i);
    char *v = smartlist_get(elements, i+1);
    if (v == NULL) {
      smartlist_add_asprintf(lines, "650-%s", k);
    } else {
      smartlist_add_asprintf(lines, "650-%s=%s", k, v);
    }
  }
  result = smartlist_join_strings(lines, "\r\n", 0, NULL);
  send_control_event(EVENT_CONF_CHANGED, 0,
    "650-CONF_CHANGED\r\n%s\r\n650 OK\r\n", result);
  tor_free(result);
  SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
  smartlist_free(lines);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Called whenever we receive new router descriptors: tell any interested control connections.

routers is a list of routerinfo_t's.

Definition at line 3943 of file control.c.

{
  char *msg;

  if (!EVENT_IS_INTERESTING(EVENT_NEW_DESC))
    return 0;

  {
    smartlist_t *names = smartlist_new();
    char *ids;
    SMARTLIST_FOREACH(routers, routerinfo_t *, ri, {
        char *b = tor_malloc(MAX_VERBOSE_NICKNAME_LEN+1);
        router_get_verbose_nickname(b, ri);
        smartlist_add(names, b);
      });
    ids = smartlist_join_strings(names, " ", 0, NULL);
    tor_asprintf(&msg, "650 NEWDESC %s\r\n", ids);
    send_control_event_string(EVENT_NEW_DESC, ALL_FORMATS, msg);
    tor_free(ids);
    tor_free(msg);
    SMARTLIST_FOREACH(names, char *, cp, tor_free(cp));
    smartlist_free(names);
  }
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_general_status ( int  severity,
const char *  format,
  ... 
)

Format and send an EVENT_STATUS_GENERAL event whose main text is obtained by formatting the arguments using the printf-style format.

Definition at line 4267 of file control.c.

{
  va_list ap;
  int r;
  if (!EVENT_IS_INTERESTING(EVENT_STATUS_GENERAL))
    return 0;

  va_start(ap, format);
  r = control_event_status(EVENT_STATUS_GENERAL, severity, format, ap);
  va_end(ap);
  return r;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_guard ( const char *  nickname,
const char *  digest,
const char *  status 
)

Called when the status of an entry guard with the given nickname and identity digest has changed to status: tells any controllers that care.

Definition at line 4316 of file control.c.

{
  char hbuf[HEX_DIGEST_LEN+1];
  base16_encode(hbuf, sizeof(hbuf), digest, DIGEST_LEN);
  if (!EVENT_IS_INTERESTING(EVENT_GUARD))
    return 0;

  {
    char buf[MAX_VERBOSE_NICKNAME_LEN+1];
    const node_t *node = node_get_by_id(digest);
    if (node) {
      node_get_verbose_nickname(node, buf);
    } else {
      tor_snprintf(buf, sizeof(buf), "$%s~%s", hbuf, nickname);
    }
    send_control_event(EVENT_GUARD, ALL_FORMATS,
                       "650 GUARD ENTRY %s %s\r\n", buf, status);
  }
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_is_interesting ( int  event)

Return true iff the event with code c is being sent to any current control connection.

This is useful if the amount of work needed to prepare to call the appropriate control_event_...() function is high.

Definition at line 309 of file control.c.

{
  return EVENT_IS_INTERESTING(event);
}

Here is the caller graph for this function:

void control_event_logmsg ( int  severity,
uint32_t  domain,
const char *  msg 
)

We got a log message: tell any interested control connections.

Definition at line 3891 of file control.c.

{
  int event;

  /* Don't even think of trying to add stuff to a buffer from a cpuworker
   * thread. */
  if (! in_main_thread())
    return;

  if (disable_log_messages)
    return;

  if (domain == LD_BUG && EVENT_IS_INTERESTING(EVENT_STATUS_GENERAL) &&
      severity <= LOG_NOTICE) {
    char *esc = esc_for_log(msg);
    ++disable_log_messages;
    control_event_general_status(severity, "BUG REASON=%s", esc);
    --disable_log_messages;
    tor_free(esc);
  }

  event = log_severity_to_event(severity);
  if (event >= 0 && EVENT_IS_INTERESTING(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 = ' ';
    }
    switch (severity) {
      case LOG_DEBUG: s = "DEBUG"; break;
      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;
    }
    ++disable_log_messages;
    send_control_event(event, ALL_FORMATS, "650 %s %s\r\n", s, b?b:msg);
    --disable_log_messages;
    tor_free(b);
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

Our own router descriptor has changed; tell any controllers that care.

Definition at line 4207 of file control.c.

{
  send_control_event(EVENT_DESCCHANGED, ALL_FORMATS, "650 DESCCHANGED\r\n");
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Called when the routerstatus_ts statuses have changed: sends an NS event to any controller that cares.

Definition at line 4083 of file control.c.

Here is the call graph for this function:

Here is the caller graph for this function:

static int control_event_networkstatus_changed_helper ( smartlist_t statuses,
uint16_t  event,
const char *  event_string 
) [static]

Helper function for NS-style events.

Constructs and sends an event of type event with string event_string out of the set of networkstatuses statuses. Currently it is used for NS events and NEWCONSENSUS events.

Definition at line 4047 of file control.c.

{
  smartlist_t *strs;
  char *s, *esc = NULL;
  if (!EVENT_IS_INTERESTING(event) || !smartlist_len(statuses))
    return 0;

  strs = smartlist_new();
  smartlist_add(strs, tor_strdup("650+"));
  smartlist_add(strs, tor_strdup(event_string));
  smartlist_add(strs, tor_strdup("\r\n"));
  SMARTLIST_FOREACH(statuses, const routerstatus_t *, rs,
    {
      s = networkstatus_getinfo_helper_single(rs);
      if (!s) continue;
      smartlist_add(strs, s);
    });

  s = smartlist_join_strings(strs, "", 0, NULL);
  write_escaped_data(s, strlen(s), &esc);
  SMARTLIST_FOREACH(strs, char *, cp, tor_free(cp));
  smartlist_free(strs);
  tor_free(s);
  send_control_event_string(event, ALL_FORMATS, esc);
  send_control_event_string(event, ALL_FORMATS,
                            "650 OK\r\n");

  tor_free(esc);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Called when a single local_routerstatus_t has changed: Sends an NS event to any controller that cares.

Definition at line 4189 of file control.c.

{
  smartlist_t *statuses;
  int r;

  if (!EVENT_IS_INTERESTING(EVENT_NS))
    return 0;

  statuses = smartlist_new();
  smartlist_add(statuses, (void*)rs);
  r = control_event_networkstatus_changed(statuses);
  smartlist_free(statuses);
  return r;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_newconsensus ( const networkstatus_t consensus)

Called when we get a new consensus networkstatus.

Sends a NEWCONSENSUS event consisting of an NS-style line for each relay in the consensus.

Definition at line 4091 of file control.c.

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_or_authdir_new_descriptor ( const char *  action,
const char *  desc,
size_t  desclen,
const char *  msg 
)

The authoritative dirserver has received a new descriptor that has passed basic syntax checks and is properly self-signed.

Notify any interested party of the new descriptor and what has been done with it, and also optionally give an explanation/reason.

Definition at line 4007 of file control.c.

{
  char firstline[1024];
  char *buf;
  size_t totallen;
  char *esc = NULL;
  size_t esclen;

  if (!EVENT_IS_INTERESTING(EVENT_AUTHDIR_NEWDESCS))
    return 0;

  tor_snprintf(firstline, sizeof(firstline),
               "650+AUTHDIR_NEWDESC=\r\n%s\r\n%s\r\n",
               action,
               msg ? msg : "");

  /* Escape the server descriptor properly */
  esclen = write_escaped_data(desc, desclen, &esc);

  totallen = strlen(firstline) + esclen + 1;
  buf = tor_malloc(totallen);
  strlcpy(buf, firstline, totallen);
  strlcpy(buf+strlen(firstline), esc, totallen);
  send_control_event_string(EVENT_AUTHDIR_NEWDESCS, ALL_FORMATS,
                            buf);
  send_control_event_string(EVENT_AUTHDIR_NEWDESCS, ALL_FORMATS,
                            "650 OK\r\n");
  tor_free(esc);
  tor_free(buf);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Called when the status of an OR connection conn changes: tell any interested control connections.

tp is the new status for the connection. If conn has just closed or failed, then reason may be the reason why.

Definition at line 3763 of file control.c.

{
  int ncircs = 0;
  const char *status;
  char name[128];
  char ncircs_buf[32] = {0}; /* > 8 + log10(2^32)=10 + 2 */

  if (!EVENT_IS_INTERESTING(EVENT_OR_CONN_STATUS))
    return 0;

  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;
    case OR_CONN_EVENT_NEW: status = "NEW"; break;
    default:
      log_warn(LD_BUG, "Unrecognized status code %d", (int)tp);
      return 0;
    }
  ncircs = circuit_count_pending_on_or_conn(conn);
  ncircs += conn->n_circuits;
  if (ncircs && (tp == OR_CONN_EVENT_FAILED || tp == OR_CONN_EVENT_CLOSED)) {
    tor_snprintf(ncircs_buf, sizeof(ncircs_buf), "%sNCIRCS=%d",
                 reason ? " " : "", ncircs);
  }

  orconn_target_get_name(name, sizeof(name), conn);
  send_control_event(EVENT_OR_CONN_STATUS, ALL_FORMATS,
                              "650 ORCONN %s %s %s%s%s\r\n",
                              name, status,
                              reason ? "REASON=" : "",
                              orconn_end_reason_to_control_string(reason),
                              ncircs_buf);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_server_status ( int  severity,
const char *  format,
  ... 
)

Format and send an EVENT_STATUS_SERVER event whose main text is obtained by formatting the arguments using the printf-style format.

Definition at line 4299 of file control.c.

{
  va_list ap;
  int r;
  if (!EVENT_IS_INTERESTING(EVENT_STATUS_SERVER))
    return 0;

  va_start(ap, format);
  r = control_event_status(EVENT_STATUS_SERVER, severity, format, ap);
  va_end(ap);
  return r;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_signal ( uintptr_t  signal)

Called when a signal has been processed from signal_callback.

Definition at line 4152 of file control.c.

{
  const char *signal_string = NULL;

  if (!control_event_is_interesting(EVENT_SIGNAL))
    return 0;

  switch (signal) {
    case SIGHUP:
      signal_string = "RELOAD";
      break;
    case SIGUSR1:
      signal_string = "DUMP";
      break;
    case SIGUSR2:
      signal_string = "DEBUG";
      break;
    case SIGNEWNYM:
      signal_string = "NEWNYM";
      break;
    case SIGCLEARDNSCACHE:
      signal_string = "CLEARDNSCACHE";
      break;
    default:
      log_warn(LD_BUG, "Unrecognized signal %lu in control_event_signal",
               (unsigned long)signal);
      return -1;
  }

  send_control_event(EVENT_SIGNAL, ALL_FORMATS, "650 SIGNAL %s\r\n",
                     signal_string);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int control_event_status ( int  type,
int  severity,
const char *  format,
va_list  args 
) [static]

Helper: sends a status event where type is one of EVENT_STATUS_{GENERAL,CLIENT,SERVER}, where severity is one of LOG_{NOTICE,WARN,ERR}, and where format is a printf-style format string corresponding to args.

Definition at line 4218 of file control.c.

{
  char *user_buf = NULL;
  char format_buf[160];
  const char *status, *sev;

  switch (type) {
    case EVENT_STATUS_GENERAL:
      status = "STATUS_GENERAL";
      break;
    case EVENT_STATUS_CLIENT:
      status = "STATUS_CLIENT";
      break;
    case EVENT_STATUS_SERVER:
      status = "STATUS_SERVER";
      break;
    default:
      log_warn(LD_BUG, "Unrecognized status type %d", type);
      return -1;
  }
  switch (severity) {
    case LOG_NOTICE:
      sev = "NOTICE";
      break;
    case LOG_WARN:
      sev = "WARN";
      break;
    case LOG_ERR:
      sev = "ERR";
      break;
    default:
      log_warn(LD_BUG, "Unrecognized status severity %d", severity);
      return -1;
  }
  if (tor_snprintf(format_buf, sizeof(format_buf), "650 %s %s",
                   status, sev)<0) {
    log_warn(LD_BUG, "Format string too long.");
    return -1;
  }
  tor_vasprintf(&user_buf, format, args);

  send_control_event(type, ALL_FORMATS, "%s %s\r\n", format_buf, user_buf);
  tor_free(user_buf);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Print out STREAM_BW event for a single conn.

Definition at line 3807 of file control.c.

{
  if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) {
    if (!edge_conn->n_read && !edge_conn->n_written)
      return 0;

    send_control_event(EVENT_STREAM_BANDWIDTH_USED, ALL_FORMATS,
                       "650 STREAM_BW "U64_FORMAT" %lu %lu\r\n",
                       U64_PRINTF_ARG(edge_conn->_base.global_identifier),
                       (unsigned long)edge_conn->n_read,
                       (unsigned long)edge_conn->n_written);

    edge_conn->n_written = edge_conn->n_read = 0;
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

A second or more has elapsed: tell any interested control connections how much bandwidth streams have used.

Definition at line 3828 of file control.c.

{
  if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) {
    smartlist_t *conns = get_connection_array();
    edge_connection_t *edge_conn;

    SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn)
    {
        if (conn->type != CONN_TYPE_AP)
          continue;
        edge_conn = TO_EDGE_CONN(conn);
        if (!edge_conn->n_read && !edge_conn->n_written)
          continue;

        send_control_event(EVENT_STREAM_BANDWIDTH_USED, ALL_FORMATS,
                           "650 STREAM_BW "U64_FORMAT" %lu %lu\r\n",
                           U64_PRINTF_ARG(edge_conn->_base.global_identifier),
                           (unsigned long)edge_conn->n_read,
                           (unsigned long)edge_conn->n_written);

        edge_conn->n_written = edge_conn->n_read = 0;
    }
    SMARTLIST_FOREACH_END(conn);
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int control_event_stream_status ( entry_connection_t conn,
stream_status_event_t  tp,
int  reason_code 
)

Something has happened to the stream associated with AP connection conn: tell any interested control connections.

Definition at line 3625 of file control.c.

{
  char reason_buf[64];
  char addrport_buf[64];
  const char *status;
  circuit_t *circ;
  origin_circuit_t *origin_circ = NULL;
  char buf[256];
  const char *purpose = "";
  tor_assert(conn->socks_request);

  if (!EVENT_IS_INTERESTING(EVENT_STREAM_STATUS))
    return 0;

  if (tp == STREAM_EVENT_CLOSED &&
      (reason_code & END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED))
    return 0;

  write_stream_target_to_buf(conn, buf, sizeof(buf));

  reason_buf[0] = '\0';
  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;
    case STREAM_EVENT_REMAP: status = "REMAP"; break;
    default:
      log_warn(LD_BUG, "Unrecognized status code %d", (int)tp);
      return 0;
    }
  if (reason_code && (tp == STREAM_EVENT_FAILED ||
                      tp == STREAM_EVENT_CLOSED ||
                      tp == STREAM_EVENT_FAILED_RETRIABLE)) {
    const char *reason_str = stream_end_reason_to_control_string(reason_code);
    char *r = NULL;
    if (!reason_str) {
      tor_asprintf(&r, " UNKNOWN_%d", reason_code);
      reason_str = r;
    }
    if (reason_code & END_STREAM_REASON_FLAG_REMOTE)
      tor_snprintf(reason_buf, sizeof(reason_buf),
                   " REASON=END REMOTE_REASON=%s", reason_str);
    else
      tor_snprintf(reason_buf, sizeof(reason_buf),
                   " REASON=%s", reason_str);
    tor_free(r);
  } else if (reason_code && tp == STREAM_EVENT_REMAP) {
    switch (reason_code) {
    case REMAP_STREAM_SOURCE_CACHE:
      strlcpy(reason_buf, " SOURCE=CACHE", sizeof(reason_buf));
      break;
    case REMAP_STREAM_SOURCE_EXIT:
      strlcpy(reason_buf, " SOURCE=EXIT", sizeof(reason_buf));
      break;
    default:
      tor_snprintf(reason_buf, sizeof(reason_buf), " REASON=UNKNOWN_%d",
                   reason_code);
      /* XXX do we want SOURCE=UNKNOWN_%d above instead? -RD */
      break;
    }
  }

  if (tp == STREAM_EVENT_NEW) {
    tor_snprintf(addrport_buf,sizeof(addrport_buf), " SOURCE_ADDR=%s:%d",
                 ENTRY_TO_CONN(conn)->address, ENTRY_TO_CONN(conn)->port);
  } else {
    addrport_buf[0] = '\0';
  }

  if (tp == STREAM_EVENT_NEW_RESOLVE) {
    purpose = " PURPOSE=DNS_REQUEST";
  } else if (tp == STREAM_EVENT_NEW) {
    if (ENTRY_TO_EDGE_CONN(conn)->is_dns_request ||
        (conn->socks_request &&
         SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)))
      purpose = " PURPOSE=DNS_REQUEST";
    else if (conn->use_begindir) {
      connection_t *linked = ENTRY_TO_CONN(conn)->linked_conn;
      int linked_dir_purpose = -1;
      if (linked && linked->type == CONN_TYPE_DIR)
        linked_dir_purpose = linked->purpose;
      if (DIR_PURPOSE_IS_UPLOAD(linked_dir_purpose))
        purpose = " PURPOSE=DIR_UPLOAD";
      else
        purpose = " PURPOSE=DIR_FETCH";
    } else
      purpose = " PURPOSE=USER";
  }

  circ = circuit_get_by_edge_conn(ENTRY_TO_EDGE_CONN(conn));
  if (circ && CIRCUIT_IS_ORIGIN(circ))
    origin_circ = TO_ORIGIN_CIRCUIT(circ);
  send_control_event(EVENT_STREAM_STATUS, ALL_FORMATS,
                        "650 STREAM "U64_FORMAT" %s %lu %s%s%s%s\r\n",
                     U64_PRINTF_ARG(ENTRY_TO_CONN(conn)->global_identifier),
                     status,
                        origin_circ?
                           (unsigned long)origin_circ->global_identifier : 0ul,
                        buf, reason_buf, addrport_buf, purpose);

  /* XXX need to specify its intended exit, etc? */

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void control_ports_write_to_file ( void  )

Write all of the open control ports to ControlPortWriteToFile.

Definition at line 530 of file control.c.

{
  smartlist_t *lines;
  char *joined = NULL;
  const or_options_t *options = get_options();

  if (!options->ControlPortWriteToFile)
    return;

  lines = smartlist_new();

  SMARTLIST_FOREACH_BEGIN(get_connection_array(), const connection_t *, conn) {
    if (conn->type != CONN_TYPE_CONTROL_LISTENER || conn->marked_for_close)
      continue;
#ifdef AF_UNIX
    if (conn->socket_family == AF_UNIX) {
      smartlist_add_asprintf(lines, "UNIX_PORT=%s\n", conn->address);
      continue;
    }
#endif
    smartlist_add_asprintf(lines, "PORT=%s:%d\n", conn->address, conn->port);
  } SMARTLIST_FOREACH_END(conn);

  joined = smartlist_join_strings(lines, "", 0, NULL);

  if (write_str_to_file(options->ControlPortWriteToFile, joined, 0) < 0) {
    log_warn(LD_CONTROL, "Writing %s failed: %s",
             options->ControlPortWriteToFile, strerror(errno));
  }
#ifndef _WIN32
  if (options->ControlPortFileGroupReadable) {
    if (chmod(options->ControlPortWriteToFile, 0640)) {
      log_warn(LD_FS,"Unable to make %s group-readable.",
               options->ControlPortWriteToFile);
    }
  }
#endif
  tor_free(joined);
  SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
  smartlist_free(lines);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int control_setconf_helper ( control_connection_t conn,
uint32_t  len,
char *  body,
int  use_defaults 
) [static]

Helper for setconf and resetconf.

Acts like setconf, except it passes use_defaults on to options_trial_assign(). Modifies the contents of body.

Definition at line 689 of file control.c.

{
  setopt_err_t opt_err;
  config_line_t *lines=NULL;
  char *start = body;
  char *errstring = NULL;
  const int clear_first = 1;

  char *config;
  smartlist_t *entries = smartlist_new();

  /* We have a string, "body", of the format '(key(=val|="val")?)' entries
   * separated by space.  break it into a list of configuration entries. */
  while (*body) {
    char *eq = body;
    char *key;
    char *entry;
    while (!TOR_ISSPACE(*eq) && *eq != '=')
      ++eq;
    key = tor_strndup(body, eq-body);
    body = eq+1;
    if (*eq == '=') {
      char *val=NULL;
      size_t val_len=0;
      if (*body != '\"') {
        char *val_start = body;
        while (!TOR_ISSPACE(*body))
          body++;
        val = tor_strndup(val_start, body-val_start);
        val_len = strlen(val);
      } else {
        body = (char*)extract_escaped_string(body, (len - (body-start)),
                                             &val, &val_len);
        if (!body) {
          connection_write_str_to_buf("551 Couldn't parse string\r\n", conn);
          SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp));
          smartlist_free(entries);
          tor_free(key);
          return 0;
        }
      }
      tor_asprintf(&entry, "%s %s", key, val);
      tor_free(key);
      tor_free(val);
    } else {
      entry = key;
    }
    smartlist_add(entries, entry);
    while (TOR_ISSPACE(*body))
      ++body;
  }

  smartlist_add(entries, tor_strdup(""));
  config = smartlist_join_strings(entries, "\n", 0, NULL);
  SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp));
  smartlist_free(entries);

  if (config_get_lines(config, &lines, 0) < 0) {
    log_warn(LD_CONTROL,"Controller gave us config lines we can't parse.");
    connection_write_str_to_buf("551 Couldn't parse configuration\r\n",
                                conn);
    tor_free(config);
    return 0;
  }
  tor_free(config);

  opt_err = options_trial_assign(lines, use_defaults, clear_first, &errstring);
  {
    const char *msg;
    switch (opt_err) {
      case SETOPT_ERR_MISC:
        msg = "552 Unrecognized option";
        break;
      case SETOPT_ERR_PARSE:
        msg = "513 Unacceptable option value";
        break;
      case SETOPT_ERR_TRANSITION:
        msg = "553 Transition not allowed";
        break;
      case SETOPT_ERR_SETTING:
      default:
        msg = "553 Unable to set option";
        break;
      case SETOPT_OK:
        config_free_lines(lines);
        send_control_done(conn);
        return 0;
    }
    log_warn(LD_CONTROL,
             "Controller gave us config lines that didn't validate: %s",
             errstring);
    connection_printf_to_buf(conn, "%s: %s\r\n", msg, errstring);
    config_free_lines(lines);
    tor_free(errstring);
    return 0;
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

Set global_event_mask* to the bitwise OR of each live control connection's event_mask field.

Definition at line 232 of file control.c.

{
  smartlist_t *conns = get_connection_array();
  event_mask_t old_mask, new_mask;
  old_mask = global_event_mask;

  global_event_mask = 0;
  SMARTLIST_FOREACH(conns, connection_t *, _conn,
  {
    if (_conn->type == CONN_TYPE_CONTROL &&
        STATE_IS_OPEN(_conn->state)) {
      control_connection_t *conn = TO_CONTROL_CONN(_conn);
      global_event_mask |= conn->event_mask;
    }
  });

  new_mask = global_event_mask;

  /* Handle the aftermath.  Set up the log callback to tell us only what
   * we want to hear...*/
  control_adjust_event_log_severity();

  /* ...then, if we've started logging stream bw, clear the appropriate
   * fields. */
  if (! (old_mask & EVENT_STREAM_BANDWIDTH_USED) &&
      (new_mask & EVENT_STREAM_BANDWIDTH_USED)) {
    SMARTLIST_FOREACH(conns, connection_t *, conn,
    {
      if (conn->type == CONN_TYPE_AP) {
        edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
        edge_conn->n_written = edge_conn->n_read = 0;
      }
    });
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

static const char* decode_escaped_string ( const char *  start,
size_t  in_len_max,
char **  out,
size_t *  out_len 
) [static]

Given a pointer to a string starting at start containing in_len_max characters, decode a string beginning with one double quote, containing any number of non-quote characters or characters escaped with a backslash, and ending with a final double quote.

Place the resulting string (unquoted, unescaped) into a newly allocated string in *out; store its length in out_len. On success, return a pointer to the character immediately following the escaped string. On failure, return NULL.

Definition at line 477 of file control.c.

{
  const char *cp, *end;
  char *outp;
  int len, n_chars = 0;

  len = get_escaped_string_length(start, in_len_max, &n_chars);
  if (len<0)
    return NULL;

  end = start+len-1; /* Index of last quote. */
  tor_assert(*end == '\"');
  outp = *out = tor_malloc(len+1);
  *out_len = n_chars;

  cp = start+1;
  while (cp < end) {
    if (*cp == '\\')
      ++cp;
    *outp++ = *cp++;
  }
  *outp = '\0';
  tor_assert((outp - *out) == (int)*out_len);

  return end+1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Decode the hashed, base64'd passwords stored in passwords.

Return a smartlist of acceptable passwords (unterminated strings of length S2K_SPECIFIER_LEN+DIGEST_LEN) on success, or NULL on failure.

Definition at line 1012 of file control.c.

{
  char decoded[64];
  config_line_t *cl;
  smartlist_t *sl = smartlist_new();

  tor_assert(passwords);

  for (cl = passwords; cl; cl = cl->next) {
    const char *hashed = cl->value;

    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) {
        goto err;
      }
    } else {
        if (base64_decode(decoded, sizeof(decoded), hashed, strlen(hashed))
            != S2K_SPECIFIER_LEN+DIGEST_LEN) {
          goto err;
        }
    }
    smartlist_add(sl, tor_memdup(decoded, S2K_SPECIFIER_LEN+DIGEST_LEN));
  }

  return sl;

 err:
  SMARTLIST_FOREACH(sl, char*, cp, tor_free(cp));
  smartlist_free(sl);
  return NULL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void disable_control_logging ( void  )

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.

Definition at line 3875 of file control.c.

Here is the caller graph for this function:

void enable_control_logging ( void  )

We're done sending a log message to the controllers: re-enable controller logging.

Used by CONN_LOG_PROTECT.

Definition at line 3883 of file control.c.

{
  if (--disable_log_messages < 0)
    tor_assert(0);
}

Here is the caller graph for this function:

static INLINE int event_to_log_severity ( int  event) [static]

Given a control event code for a message event, return the corresponding log severity.

Definition at line 203 of file control.c.

{
  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;
  }
}

Here is the caller graph for this function:

static const char* extract_escaped_string ( const char *  start,
size_t  in_len_max,
char **  out,
size_t *  out_len 
) [static]

As decode_escaped_string, but does not decode the string: copies the entire thing, including quotation marks.

Definition at line 457 of file control.c.

{
  int length = get_escaped_string_length(start, in_len_max, NULL);
  if (length<0)
    return NULL;
  *out_len = length;
  *out = tor_strndup(start, *out_len);
  return start+length;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static const char* find_element_starting_with ( smartlist_t sl,
int  start_at,
const char *  prefix 
) [static]

Helper.

Return the first element of sl at index start_at or higher that starts with prefix, case-insensitive. Return NULL if no such element exists.

Definition at line 2366 of file control.c.

{
  int i;
  for (i = start_at; i < smartlist_len(sl); ++i) {
    const char *elt = smartlist_get(sl, i);
    if (!strcasecmpstart(elt, prefix))
      return elt;
  }
  return NULL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static origin_circuit_t* get_circ ( const char *  id) [static]

Given a text circuit id, return the corresponding circuit.

Definition at line 658 of file control.c.

{
  uint32_t n_id;
  int ok;
  n_id = (uint32_t) tor_parse_ulong(id, 10, 0, UINT32_MAX, &ok, NULL);
  if (!ok)
    return NULL;
  return circuit_get_by_global_id(n_id);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static char * get_cookie_file ( void  ) [static]

Helper: Return a newly allocated string containing a path to the file where we store our authentication cookie.

Definition at line 4374 of file control.c.

{
  const or_options_t *options = get_options();
  if (options->CookieAuthFile && strlen(options->CookieAuthFile)) {
    return tor_strdup(options->CookieAuthFile);
  } else {
    return get_datadir_fname("control_auth_cookie");
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

static INLINE int get_escaped_string_length ( const char *  start,
size_t  in_len_max,
int *  chars_out 
) [static]

If the first in_len_max characters in start contain a double-quoted string with escaped characters, return the length of that string (as encoded, including quotes).

Otherwise return -1.

Definition at line 421 of file control.c.

{
  const char *cp, *end;
  int chars = 0;

  if (*start != '\"')
    return -1;

  cp = start+1;
  end = start+in_len_max;

  /* Calculate length. */
  while (1) {
    if (cp >= end) {
      return -1; /* Too long. */
    } else if (*cp == '\\') {
      if (++cp == end)
        return -1; /* Can't escape EOS. */
      ++cp;
      ++chars;
    } else if (*cp == '\"') {
      break;
    } else {
      ++cp;
      ++chars;
    }
  }
  if (chars_out)
    *chars_out = chars;
  return (int)(cp - start+1);
}

Here is the caller graph for this function:

static entry_connection_t* get_stream ( const char *  id) [static]

Given a text stream id, return the corresponding AP connection.

Definition at line 670 of file control.c.

{
  uint64_t n_id;
  int ok;
  connection_t *conn;
  n_id = tor_parse_uint64(id, 10, 0, UINT64_MAX, &ok, NULL);
  if (!ok)
    return NULL;
  conn = connection_get_by_global_id(n_id);
  if (!conn || conn->type != CONN_TYPE_AP || conn->marked_for_close)
    return NULL;
  return TO_ENTRY_CONN(conn);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static smartlist_t* getargs_helper ( const char *  command,
control_connection_t conn,
const char *  body,
int  min_args,
int  max_args 
) [static]

Return a newly allocated smartlist containing the arguments to the command waiting in body.

If there are fewer than min_args arguments, or if max_args is nonnegative and there are more than max_args arguments, send a 512 error to the controller, using command as the command name in the error message.

Definition at line 2342 of file control.c.

{
  smartlist_t *args = smartlist_new();
  smartlist_split_string(args, body, " ",
                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
  if (smartlist_len(args) < min_args) {
    connection_printf_to_buf(conn, "512 Missing argument to %s\r\n",command);
    goto err;
  } else if (max_args >= 0 && smartlist_len(args) > max_args) {
    connection_printf_to_buf(conn, "512 Too many arguments to %s\r\n",command);
    goto err;
  }
  return args;
 err:
  SMARTLIST_FOREACH(args, char *, s, tor_free(s));
  smartlist_free(args);
  return NULL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int getinfo_helper_dir ( control_connection_t control_conn,
const char *  question,
char **  answer,
const char **  errmsg 
) [static]

Implementation helper for GETINFO: knows the answers for questions about directory information.

Definition at line 1605 of file control.c.

{
  const routerinfo_t *ri;
  (void) control_conn;
  if (!strcmpstart(question, "desc/id/")) {
    ri = router_get_by_hexdigest(question+strlen("desc/id/"));
    if (ri) {
      const char *body = signed_descriptor_get_body(&ri->cache_info);
      if (body)
        *answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
    }
  } else if (!strcmpstart(question, "desc/name/")) {
    /* XXX023 Setting 'warn_if_unnamed' here is a bit silly -- the
     * warning goes to the user, not to the controller. */
    ri = router_get_by_nickname(question+strlen("desc/name/"),1);
    if (ri) {
      const char *body = signed_descriptor_get_body(&ri->cache_info);
      if (body)
        *answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
    }
  } else if (!strcmp(question, "desc/all-recent")) {
    routerlist_t *routerlist = router_get_routerlist();
    smartlist_t *sl = smartlist_new();
    if (routerlist && routerlist->routers) {
      SMARTLIST_FOREACH(routerlist->routers, const routerinfo_t *, ri,
      {
        const char *body = signed_descriptor_get_body(&ri->cache_info);
        if (body)
          smartlist_add(sl,
                  tor_strndup(body, ri->cache_info.signed_descriptor_len));
      });
    }
    *answer = smartlist_join_strings(sl, "", 0, NULL);
    SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
    smartlist_free(sl);
  } else if (!strcmp(question, "desc/all-recent-extrainfo-hack")) {
    /* XXXX Remove this once Torstat asks for extrainfos. */
    routerlist_t *routerlist = router_get_routerlist();
    smartlist_t *sl = smartlist_new();
    if (routerlist && routerlist->routers) {
      SMARTLIST_FOREACH(routerlist->routers, const routerinfo_t *, ri,
      {
        const char *body = signed_descriptor_get_body(&ri->cache_info);
        signed_descriptor_t *ei = extrainfo_get_by_descriptor_digest(
                                     ri->cache_info.extra_info_digest);
        if (ei && body) {
          smartlist_add(sl, munge_extrainfo_into_routerinfo(body,
                                                        &ri->cache_info, ei));
        } else if (body) {
          smartlist_add(sl,
                  tor_strndup(body, ri->cache_info.signed_descriptor_len));
        }
      });
    }
    *answer = smartlist_join_strings(sl, "", 0, NULL);
    SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
    smartlist_free(sl);
  } else if (!strcmpstart(question, "md/id/")) {
    const node_t *node = node_get_by_hex_id(question+strlen("md/id/"));
    const microdesc_t *md = NULL;
    if (node) md = node->md;
    if (md) {
      tor_assert(md->body);
      *answer = tor_strndup(md->body, md->bodylen);
    }
  } else if (!strcmpstart(question, "md/name/")) {
    /* XXX023 Setting 'warn_if_unnamed' here is a bit silly -- the
     * warning goes to the user, not to the controller. */
    const node_t *node = node_get_by_nickname(question+strlen("md/name/"), 1);
    /* XXXX duplicated code */
    const microdesc_t *md = NULL;
    if (node) md = node->md;
    if (md) {
      tor_assert(md->body);
      *answer = tor_strndup(md->body, md->bodylen);
    }
  } else if (!strcmpstart(question, "desc-annotations/id/")) {
    ri = router_get_by_hexdigest(question+
                                 strlen("desc-annotations/id/"));
    if (ri) {
      const char *annotations =
        signed_descriptor_get_annotations(&ri->cache_info);
      if (annotations)
        *answer = tor_strndup(annotations,
                              ri->cache_info.annotations_len);
    }
  } else if (!strcmpstart(question, "dir/server/")) {
    size_t answer_len = 0;
    char *url = NULL;
    smartlist_t *descs = smartlist_new();
    const char *msg;
    int res;
    char *cp;
    tor_asprintf(&url, "/tor/%s", question+4);
    res = dirserv_get_routerdescs(descs, url, &msg);
    if (res) {
      log_warn(LD_CONTROL, "getinfo '%s': %s", question, msg);
      smartlist_free(descs);
      tor_free(url);
      *errmsg = msg;
      return -1;
    }
    SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd,
                      answer_len += sd->signed_descriptor_len);
    cp = *answer = tor_malloc(answer_len+1);
    SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd,
                      {
                        memcpy(cp, signed_descriptor_get_body(sd),
                               sd->signed_descriptor_len);
                        cp += sd->signed_descriptor_len;
                      });
    *cp = '\0';
    tor_free(url);
    smartlist_free(descs);
  } else if (!strcmpstart(question, "dir/status/")) {
    if (directory_permits_controller_requests(get_options())) {
      size_t len=0;
      char *cp;
      smartlist_t *status_list = smartlist_new();
      dirserv_get_networkstatus_v2(status_list,
                                   question+strlen("dir/status/"));
      SMARTLIST_FOREACH(status_list, cached_dir_t *, d, len += d->dir_len);
      cp = *answer = tor_malloc(len+1);
      SMARTLIST_FOREACH(status_list, cached_dir_t *, d, {
          memcpy(cp, d->dir, d->dir_len);
          cp += d->dir_len;
        });
      *cp = '\0';
      smartlist_free(status_list);
    } else {
      smartlist_t *fp_list = smartlist_new();
      smartlist_t *status_list = smartlist_new();
      dirserv_get_networkstatus_v2_fingerprints(
                             fp_list, question+strlen("dir/status/"));
      SMARTLIST_FOREACH(fp_list, const char *, fp, {
          char *s;
          char *fname = networkstatus_get_cache_filename(fp);
          s = read_file_to_str(fname, 0, NULL);
          if (s)
            smartlist_add(status_list, s);
          tor_free(fname);
        });
      SMARTLIST_FOREACH(fp_list, char *, fp, tor_free(fp));
      smartlist_free(fp_list);
      *answer = smartlist_join_strings(status_list, "", 0, NULL);
      SMARTLIST_FOREACH(status_list, char *, s, tor_free(s));
      smartlist_free(status_list);
    }
  } else if (!strcmp(question, "dir/status-vote/current/consensus")) { /* v3 */
    if (directory_caches_dir_info(get_options())) {
      const cached_dir_t *consensus = dirserv_get_consensus("ns");
      if (consensus)
        *answer = tor_strdup(consensus->dir);
    }
    if (!*answer) { /* try loading it from disk */
      char *filename = get_datadir_fname("cached-consensus");
      *answer = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
      tor_free(filename);
    }
  } else if (!strcmp(question, "network-status")) { /* v1 */
    routerlist_t *routerlist = router_get_routerlist();
    if (!routerlist || !routerlist->routers ||
        list_server_status_v1(routerlist->routers, answer, 1) < 0) {
      return -1;
    }
  } else if (!strcmpstart(question, "extra-info/digest/")) {
    question += strlen("extra-info/digest/");
    if (strlen(question) == HEX_DIGEST_LEN) {
      char d[DIGEST_LEN];
      signed_descriptor_t *sd = NULL;
      if (base16_decode(d, sizeof(d), question, strlen(question))==0) {
        /* XXXX this test should move into extrainfo_get_by_descriptor_digest,
         * but I don't want to risk affecting other parts of the code,
         * especially since the rules for using our own extrainfo (including
         * when it might be freed) are different from those for using one
         * we have downloaded. */
        if (router_extrainfo_digest_is_me(d))
          sd = &(router_get_my_extrainfo()->cache_info);
        else
          sd = extrainfo_get_by_descriptor_digest(d);
      }
      if (sd) {
        const char *body = signed_descriptor_get_body(sd);
        if (body)
          *answer = tor_strndup(body, sd->signed_descriptor_len);
      }
    }
  }

  return 0;
}

Here is the call graph for this function:

static int getinfo_helper_events ( control_connection_t control_conn,
const char *  question,
char **  answer,
const char **  errmsg 
) [static]

Implementation helper for GETINFO: knows how to generate summaries of the current states of things we send events about.

Definition at line 1877 of file control.c.

{
  (void) control_conn;
  if (!strcmp(question, "circuit-status")) {
    circuit_t *circ_;
    smartlist_t *status = smartlist_new();
    for (circ_ = _circuit_get_global_list(); circ_; circ_ = circ_->next) {
      origin_circuit_t *circ;
      char *circdesc;
      const char *state;
      if (! CIRCUIT_IS_ORIGIN(circ_) || circ_->marked_for_close)
        continue;
      circ = TO_ORIGIN_CIRCUIT(circ_);

      if (circ->_base.state == CIRCUIT_STATE_OPEN)
        state = "BUILT";
      else if (circ->cpath)
        state = "EXTENDED";
      else
        state = "LAUNCHED";

      circdesc = circuit_describe_status_for_controller(circ);

      smartlist_add_asprintf(status, "%lu %s%s%s",
                   (unsigned long)circ->global_identifier,
                   state, *circdesc ? " " : "", circdesc);
      tor_free(circdesc);
    }
    *answer = smartlist_join_strings(status, "\r\n", 0, NULL);
    SMARTLIST_FOREACH(status, char *, cp, tor_free(cp));
    smartlist_free(status);
  } else if (!strcmp(question, "stream-status")) {
    smartlist_t *conns = get_connection_array();
    smartlist_t *status = smartlist_new();
    char buf[256];
    SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
      const char *state;
      entry_connection_t *conn;
      circuit_t *circ;
      origin_circuit_t *origin_circ = NULL;
      if (base_conn->type != CONN_TYPE_AP ||
          base_conn->marked_for_close ||
          base_conn->state == AP_CONN_STATE_SOCKS_WAIT ||
          base_conn->state == AP_CONN_STATE_NATD_WAIT)
        continue;
      conn = TO_ENTRY_CONN(base_conn);
      switch (base_conn->state)
        {
        case AP_CONN_STATE_CONTROLLER_WAIT:
        case AP_CONN_STATE_CIRCUIT_WAIT:
          if (conn->socks_request &&
              SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command))
            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:
          log_warn(LD_BUG, "Asked for stream in unknown state %d",
                   base_conn->state);
          continue;
        }
      circ = circuit_get_by_edge_conn(ENTRY_TO_EDGE_CONN(conn));
      if (circ && CIRCUIT_IS_ORIGIN(circ))
        origin_circ = TO_ORIGIN_CIRCUIT(circ);
      write_stream_target_to_buf(conn, buf, sizeof(buf));
      smartlist_add_asprintf(status, "%lu %s %lu %s",
                   (unsigned long) base_conn->global_identifier,state,
                   origin_circ?
                         (unsigned long)origin_circ->global_identifier : 0ul,
                   buf);
    } SMARTLIST_FOREACH_END(base_conn);
    *answer = smartlist_join_strings(status, "\r\n", 0, NULL);
    SMARTLIST_FOREACH(status, char *, cp, tor_free(cp));
    smartlist_free(status);
  } else if (!strcmp(question, "orconn-status")) {
    smartlist_t *conns = get_connection_array();
    smartlist_t *status = smartlist_new();
    SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
      const char *state;
      char name[128];
      or_connection_t *conn;
      if (base_conn->type != CONN_TYPE_OR || base_conn->marked_for_close)
        continue;
      conn = TO_OR_CONN(base_conn);
      if (conn->_base.state == OR_CONN_STATE_OPEN)
        state = "CONNECTED";
      else if (conn->nickname)
        state = "LAUNCHED";
      else
        state = "NEW";
      orconn_target_get_name(name, sizeof(name), conn);
      smartlist_add_asprintf(status, "%s %s", name, state);
    } SMARTLIST_FOREACH_END(base_conn);
    *answer = smartlist_join_strings(status, "\r\n", 0, NULL);
    SMARTLIST_FOREACH(status, char *, cp, tor_free(cp));
    smartlist_free(status);
  } else if (!strcmpstart(question, "address-mappings/")) {
    time_t min_e, max_e;
    smartlist_t *mappings;
    question += strlen("address-mappings/");
    if (!strcmp(question, "all")) {
      min_e = 0; max_e = TIME_MAX;
    } else if (!strcmp(question, "cache")) {
      min_e = 2; max_e = TIME_MAX;
    } else if (!strcmp(question, "config")) {
      min_e = 0; max_e = 0;
    } else if (!strcmp(question, "control")) {
      min_e = 1; max_e = 1;
    } else {
      return 0;
    }
    mappings = smartlist_new();
    addressmap_get_mappings(mappings, min_e, max_e, 1);
    *answer = smartlist_join_strings(mappings, "\r\n", 0, NULL);
    SMARTLIST_FOREACH(mappings, char *, cp, tor_free(cp));
    smartlist_free(mappings);
  } else if (!strcmpstart(question, "status/")) {
    /* Note that status/ is not a catch-all for events; there's only supposed
     * to be a status GETINFO if there's a corresponding STATUS event. */
    if (!strcmp(question, "status/circuit-established")) {
      *answer = tor_strdup(can_complete_circuit ? "1" : "0");
    } else if (!strcmp(question, "status/enough-dir-info")) {
      *answer = tor_strdup(router_have_minimum_dir_info() ? "1" : "0");
    } else if (!strcmp(question, "status/good-server-descriptor") ||
               !strcmp(question, "status/accepted-server-descriptor")) {
      /* They're equivalent for now, until we can figure out how to make
       * good-server-descriptor be what we want. See comment in
       * control-spec.txt. */
      *answer = tor_strdup(directories_have_accepted_server_descriptor()
                           ? "1" : "0");
    } else if (!strcmp(question, "status/reachability-succeeded/or")) {
      *answer = tor_strdup(check_whether_orport_reachable() ? "1" : "0");
    } else if (!strcmp(question, "status/reachability-succeeded/dir")) {
      *answer = tor_strdup(check_whether_dirport_reachable() ? "1" : "0");
    } else if (!strcmp(question, "status/reachability-succeeded")) {
      tor_asprintf(answer, "OR=%d DIR=%d",
                   check_whether_orport_reachable() ? 1 : 0,
                   check_whether_dirport_reachable() ? 1 : 0);
    } else if (!strcmp(question, "status/bootstrap-phase")) {
      *answer = tor_strdup(last_sent_bootstrap_message);
    } else if (!strcmpstart(question, "status/version/")) {
      int is_server = server_mode(get_options());
      networkstatus_t *c = networkstatus_get_latest_consensus();
      version_status_t status;
      const char *recommended;
      if (c) {
        recommended = is_server ? c->server_versions : c->client_versions;
        status = tor_version_is_obsolete(VERSION, recommended);
      } else {
        recommended = "?";
        status = VS_UNKNOWN;
      }

      if (!strcmp(question, "status/version/recommended")) {
        *answer = tor_strdup(recommended);
        return 0;
      }
      if (!strcmp(question, "status/version/current")) {
        switch (status)
          {
          case VS_RECOMMENDED: *answer = tor_strdup("recommended"); break;
          case VS_OLD: *answer = tor_strdup("obsolete"); break;
          case VS_NEW: *answer = tor_strdup("new"); break;
          case VS_NEW_IN_SERIES: *answer = tor_strdup("new in series"); break;
          case VS_UNRECOMMENDED: *answer = tor_strdup("unrecommended"); break;
          case VS_EMPTY: *answer = tor_strdup("none recommended"); break;
          case VS_UNKNOWN: *answer = tor_strdup("unknown"); break;
          default: tor_fragile_assert();
          }
      } else if (!strcmp(question, "status/version/num-versioning") ||
                 !strcmp(question, "status/version/num-concurring")) {
        tor_asprintf(answer, "%d", get_n_authorities(V3_DIRINFO));
        log_warn(LD_GENERAL, "%s is deprecated; it no longer gives useful "
                 "information", question);
      }
    } else if (!strcmp(question, "status/clients-seen")) {
      char *bridge_stats = geoip_get_bridge_stats_controller(time(NULL));
      if (!bridge_stats) {
        *errmsg = "No bridge-client stats available";
        return -1;
      }
      *answer = bridge_stats;
    } else {
      return 0;
    }
  }
  return 0;
}

Here is the call graph for this function:

static int getinfo_helper_listeners ( control_connection_t control_conn,
const char *  question,
char **  answer,
const char **  errmsg 
) [static]

Implementation helper for GETINFO: answers requests for information about which ports are bound.

Definition at line 1550 of file control.c.

{
  int type;
  smartlist_t *res;

  (void)control_conn;
  (void)errmsg;

  if (!strcmp(question, "net/listeners/or"))
    type = CONN_TYPE_OR_LISTENER;
  else if (!strcmp(question, "net/listeners/dir"))
    type = CONN_TYPE_DIR_LISTENER;
  else if (!strcmp(question, "net/listeners/socks"))
    type = CONN_TYPE_AP_LISTENER;
  else if (!strcmp(question, "net/listeners/trans"))
    type = CONN_TYPE_AP_TRANS_LISTENER;
  else if (!strcmp(question, "net/listeners/natd"))
    type = CONN_TYPE_AP_NATD_LISTENER;
  else if (!strcmp(question, "net/listeners/dns"))
    type = CONN_TYPE_AP_DNS_LISTENER;
  else if (!strcmp(question, "net/listeners/control"))
    type = CONN_TYPE_CONTROL_LISTENER;
  else
    return 0; /* unknown key */

  res = smartlist_new();
  SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
    struct sockaddr_storage ss;
    socklen_t ss_len = sizeof(ss);

    if (conn->type != type || conn->marked_for_close || !SOCKET_OK(conn->s))
      continue;

    if (getsockname(conn->s, (struct sockaddr *)&ss, &ss_len) < 0) {
      smartlist_add_asprintf(res, "%s:%d", conn->address, (int)conn->port);
    } else {
      char *tmp = tor_sockaddr_to_str((struct sockaddr *)&ss);
      smartlist_add(res, esc_for_log(tmp));
      tor_free(tmp);
    }

  } SMARTLIST_FOREACH_END(conn);

  *answer = smartlist_join_strings(res, " ", 0, NULL);

  SMARTLIST_FOREACH(res, char *, cp, tor_free(cp));
  smartlist_free(res);
  return 0;
}

Here is the call graph for this function:

static int getinfo_helper_misc ( control_connection_t conn,
const char *  question,
char **  answer,
const char **  errmsg 
) [static]

Implementation helper for GETINFO: knows the answers for various trivial-to-implement questions.

Definition at line 1407 of file control.c.

{
  (void) conn;
  if (!strcmp(question, "version")) {
    *answer = tor_strdup(get_version());
  } else if (!strcmp(question, "config-file")) {
    *answer = tor_strdup(get_torrc_fname(0));
  } else if (!strcmp(question, "config-defaults-file")) {
    *answer = tor_strdup(get_torrc_fname(1));
  } else if (!strcmp(question, "config-text")) {
    *answer = options_dump(get_options(), 1);
  } else if (!strcmp(question, "info/names")) {
    *answer = list_getinfo_options();
  } else if (!strcmp(question, "dormant")) {
    int dormant = rep_hist_circbuilding_dormant(time(NULL));
    *answer = tor_strdup(dormant ? "1" : "0");
  } else if (!strcmp(question, "events/names")) {
    int i;
    smartlist_t *event_names = smartlist_new();

    for (i = 0; control_event_table[i].event_name != NULL; ++i) {
      smartlist_add(event_names, (char *)control_event_table[i].event_name);
    }

    *answer = smartlist_join_strings(event_names, " ", 0, NULL);

    smartlist_free(event_names);
  } else if (!strcmp(question, "features/names")) {
    *answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS");
  } else if (!strcmp(question, "address")) {
    uint32_t addr;
    if (router_pick_published_address(get_options(), &addr) < 0) {
      *errmsg = "Address unknown";
      return -1;
    }
    *answer = tor_dup_ip(addr);
  } else if (!strcmp(question, "traffic/read")) {
    tor_asprintf(answer, U64_FORMAT, U64_PRINTF_ARG(get_bytes_read()));
  } else if (!strcmp(question, "traffic/written")) {
    tor_asprintf(answer, U64_FORMAT, U64_PRINTF_ARG(get_bytes_written()));
  } else if (!strcmp(question, "process/pid")) {
    int myPid = -1;

    #ifdef _WIN32
      myPid = _getpid();
    #else
      myPid = getpid();
    #endif

    tor_asprintf(answer, "%d", myPid);
  } else if (!strcmp(question, "process/uid")) {
    #ifdef _WIN32
      *answer = tor_strdup("-1");
    #else
      int myUid = geteuid();
      tor_asprintf(answer, "%d", myUid);
    #endif
  } else if (!strcmp(question, "process/user")) {
    #ifdef _WIN32
      *answer = tor_strdup("");
    #else
      int myUid = geteuid();
      struct passwd *myPwEntry = getpwuid(myUid);

      if (myPwEntry) {
        *answer = tor_strdup(myPwEntry->pw_name);
      } else {
        *answer = tor_strdup("");
      }
    #endif
  } else if (!strcmp(question, "process/descriptor-limit")) {
    int max_fds=-1;
    set_max_file_descriptors(0, &max_fds);
    tor_asprintf(answer, "%d", max_fds);
  } else if (!strcmp(question, "dir-usage")) {
    *answer = directory_dump_request_log();
  } else if (!strcmp(question, "fingerprint")) {
    crypto_pk_t *server_key;
    if (!server_mode(get_options())) {
      *errmsg = "Not running in server mode";
      return -1;
    }
    server_key = get_server_identity_key();
    *answer = tor_malloc(HEX_DIGEST_LEN+1);
    crypto_pk_get_fingerprint(server_key, *answer, 0);
  }
  return 0;
}

Here is the call graph for this function:

static int handle_control_attachstream ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get an ATTACHSTREAM message.

Try to attach the requested stream, and report success or failure.

Definition at line 2566 of file control.c.

{
  entry_connection_t *ap_conn = NULL;
  origin_circuit_t *circ = NULL;
  int zero_circ;
  smartlist_t *args;
  crypt_path_t *cpath=NULL;
  int hop=0, hop_line_ok=1;
  (void) len;

  args = getargs_helper("ATTACHSTREAM", conn, body, 2, -1);
  if (!args)
    return 0;

  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));
  } else if (circ) {
    const char *hopstring = find_element_starting_with(args,2,"HOP=");
    if (hopstring) {
      hopstring += strlen("HOP=");
      hop = (int) tor_parse_ulong(hopstring, 10, 0, INT_MAX,
                                  &hop_line_ok, NULL);
      if (!hop_line_ok) { /* broken hop line */
        connection_printf_to_buf(conn, "552 Bad value hop=%s\r\n", hopstring);
      }
    }
  }
  SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
  smartlist_free(args);
  if (!ap_conn || (!zero_circ && !circ) || !hop_line_ok)
    return 0;

  if (ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONTROLLER_WAIT &&
      ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONNECT_WAIT &&
      ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_RESOLVE_WAIT) {
    connection_write_str_to_buf(
                    "555 Connection is not managed by controller.\r\n",
                    conn);
    return 0;
  }

  /* Do we need to detach it first? */
  if (ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONTROLLER_WAIT) {
    edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn);
    circuit_t *tmpcirc = circuit_get_by_edge_conn(edge_conn);
    connection_edge_end(edge_conn, END_STREAM_REASON_TIMEOUT);
    /* Un-mark it as ending, since we're going to reuse it. */
    edge_conn->edge_has_sent_end = 0;
    edge_conn->end_reason = 0;
    if (tmpcirc)
      circuit_detach_stream(tmpcirc, edge_conn);
    TO_CONN(edge_conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
  }

  if (circ && (circ->_base.state != CIRCUIT_STATE_OPEN)) {
    connection_write_str_to_buf(
                    "551 Can't attach stream to non-open origin circuit\r\n",
                    conn);
    return 0;
  }
  /* Is this a single hop circuit? */
  if (circ && (circuit_get_cpath_len(circ)<2 || hop==1)) {
    const node_t *node = NULL;
    char *exit_digest;
    if (circ->build_state &&
        circ->build_state->chosen_exit &&
        !tor_digest_is_zero(circ->build_state->chosen_exit->identity_digest)) {
      exit_digest = circ->build_state->chosen_exit->identity_digest;
      node = node_get_by_id(exit_digest);
    }
    /* Do both the client and relay allow one-hop exit circuits? */
    if (!node ||
        !node_allows_single_hop_exits(node) ||
        !get_options()->AllowSingleHopCircuits) {
      connection_write_str_to_buf(
      "551 Can't attach stream to this one-hop circuit.\r\n", conn);
      return 0;
    }
    ap_conn->chosen_exit_name = tor_strdup(hex_str(exit_digest, DIGEST_LEN));
  }

  if (circ && hop>0) {
    /* find this hop in the circuit, and set cpath */
    cpath = circuit_get_cpath_hop(circ, hop);
    if (!cpath) {
      connection_printf_to_buf(conn,
                               "551 Circuit doesn't have %d hops.\r\n", hop);
      return 0;
    }
  }
  if (connection_ap_handshake_rewrite_and_attach(ap_conn, circ, cpath) < 0) {
    connection_write_str_to_buf("551 Unable to attach stream\r\n", conn);
    return 0;
  }
  send_control_done(conn);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_authchallenge ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get an AUTHCHALLENGE command.

Definition at line 2985 of file control.c.

{
  const char *cp = body;
  char *client_nonce;
  size_t client_nonce_len;
  char server_hash[DIGEST256_LEN];
  char server_hash_encoded[HEX_DIGEST256_LEN+1];
  char server_nonce[SAFECOOKIE_SERVER_NONCE_LEN];
  char server_nonce_encoded[(2*SAFECOOKIE_SERVER_NONCE_LEN) + 1];

  cp += strspn(cp, " \t\n\r");
  if (!strcasecmpstart(cp, "SAFECOOKIE")) {
    cp += strlen("SAFECOOKIE");
  } else {
    connection_write_str_to_buf("513 AUTHCHALLENGE only supports SAFECOOKIE "
                                "authentication\r\n", conn);
    connection_mark_for_close(TO_CONN(conn));
    return -1;
  }

  if (!authentication_cookie_is_set) {
    connection_write_str_to_buf("515 Cookie authentication is disabled\r\n",
                                conn);
    connection_mark_for_close(TO_CONN(conn));
    return -1;
  }

  cp += strspn(cp, " \t\n\r");
  if (*cp == '"') {
    const char *newcp =
      decode_escaped_string(cp, len - (cp - body),
                            &client_nonce, &client_nonce_len);
    if (newcp == NULL) {
      connection_write_str_to_buf("513 Invalid quoted client nonce\r\n",
                                  conn);
      connection_mark_for_close(TO_CONN(conn));
      return -1;
    }
    cp = newcp;
  } else {
    size_t client_nonce_encoded_len = strspn(cp, "0123456789ABCDEFabcdef");

    client_nonce_len = client_nonce_encoded_len / 2;
    client_nonce = tor_malloc_zero(client_nonce_len);

    if (base16_decode(client_nonce, client_nonce_len,
                      cp, client_nonce_encoded_len) < 0) {
      connection_write_str_to_buf("513 Invalid base16 client nonce\r\n",
                                  conn);
      connection_mark_for_close(TO_CONN(conn));
      tor_free(client_nonce);
      return -1;
    }

    cp += client_nonce_encoded_len;
  }

  cp += strspn(cp, " \t\n\r");
  if (*cp != '\0' ||
      cp != body + len) {
    connection_write_str_to_buf("513 Junk at end of AUTHCHALLENGE command\r\n",
                                conn);
    connection_mark_for_close(TO_CONN(conn));
    tor_free(client_nonce);
    return -1;
  }

  tor_assert(!crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN));

  /* Now compute and send the server-to-controller response, and the
   * server's nonce. */
  tor_assert(authentication_cookie != NULL);

  {
    size_t tmp_len = (AUTHENTICATION_COOKIE_LEN +
                      client_nonce_len +
                      SAFECOOKIE_SERVER_NONCE_LEN);
    char *tmp = tor_malloc_zero(tmp_len);
    char *client_hash = tor_malloc_zero(DIGEST256_LEN);
    memcpy(tmp, authentication_cookie, AUTHENTICATION_COOKIE_LEN);
    memcpy(tmp + AUTHENTICATION_COOKIE_LEN, client_nonce, client_nonce_len);
    memcpy(tmp + AUTHENTICATION_COOKIE_LEN + client_nonce_len,
           server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);

    crypto_hmac_sha256(server_hash,
                       SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT,
                       strlen(SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT),
                       tmp,
                       tmp_len);

    crypto_hmac_sha256(client_hash,
                       SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT,
                       strlen(SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT),
                       tmp,
                       tmp_len);

    conn->safecookie_client_hash = client_hash;

    tor_free(tmp);
  }

  base16_encode(server_hash_encoded, sizeof(server_hash_encoded),
                server_hash, sizeof(server_hash));
  base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded),
                server_nonce, sizeof(server_nonce));

  connection_printf_to_buf(conn,
                           "250 AUTHCHALLENGE SERVERHASH=%s "
                           "SERVERNONCE=%s\r\n",
                           server_hash_encoded,
                           server_nonce_encoded);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_authenticate ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

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.

We only get here if both kinds of authentication failed.

Definition at line 1050 of file control.c.

{
  int used_quoted_string = 0;
  const or_options_t *options = get_options();
  const char *errstr = NULL;
  char *password;
  size_t password_len;
  const char *cp;
  int i;
  int bad_cookie=0, bad_password=0;
  smartlist_t *sl = NULL;

  if (!len) {
    password = tor_strdup("");
    password_len = 0;
  } else if (TOR_ISXDIGIT(body[0])) {
    cp = body;
    while (TOR_ISXDIGIT(*cp))
      ++cp;
    i = (int)(cp - body);
    tor_assert(i>0);
    password_len = i/2;
    password = tor_malloc(password_len + 1);
    if (base16_decode(password, password_len+1, body, i)<0) {
      connection_write_str_to_buf(
            "551 Invalid hexadecimal encoding.  Maybe you tried a plain text "
            "password?  If so, the standard requires that you put it in "
            "double quotes.\r\n", conn);
      connection_mark_for_close(TO_CONN(conn));
      tor_free(password);
      return 0;
    }
  } else {
    if (!decode_escaped_string(body, len, &password, &password_len)) {
      connection_write_str_to_buf("551 Invalid quoted string.  You need "
            "to put the password in double quotes.\r\n", conn);
      connection_mark_for_close(TO_CONN(conn));
      return 0;
    }
    used_quoted_string = 1;
  }

  if (conn->safecookie_client_hash != NULL) {
    /* The controller has chosen safe cookie authentication; the only
     * acceptable authentication value is the controller-to-server
     * response. */

    tor_assert(authentication_cookie_is_set);

    if (password_len != DIGEST256_LEN) {
      log_warn(LD_CONTROL,
               "Got safe cookie authentication response with wrong length "
               "(%d)", (int)password_len);
      errstr = "Wrong length for safe cookie response.";
      goto err;
    }

    if (tor_memneq(conn->safecookie_client_hash, password, DIGEST256_LEN)) {
      log_warn(LD_CONTROL,
               "Got incorrect safe cookie authentication response");
      errstr = "Safe cookie response did not match expected value.";
      goto err;
    }

    tor_free(conn->safecookie_client_hash);
    goto ok;
  }

  if (!options->CookieAuthentication && !options->HashedControlPassword &&
      !options->HashedControlSessionPassword) {
    /* if Tor doesn't demand any stronger authentication, then
     * the controller can get in with anything. */
    goto ok;
  }

  if (options->CookieAuthentication) {
    int also_password = options->HashedControlPassword != NULL ||
      options->HashedControlSessionPassword != NULL;
    if (password_len != AUTHENTICATION_COOKIE_LEN) {
      if (!also_password) {
        log_warn(LD_CONTROL, "Got authentication cookie with wrong length "
                 "(%d)", (int)password_len);
        errstr = "Wrong length on authentication cookie.";
        goto err;
      }
      bad_cookie = 1;
    } else if (tor_memneq(authentication_cookie, password, password_len)) {
      if (!also_password) {
        log_warn(LD_CONTROL, "Got mismatched authentication cookie");
        errstr = "Authentication cookie did not match expected value.";
        goto err;
      }
      bad_cookie = 1;
    } else {
      goto ok;
    }
  }

  if (options->HashedControlPassword ||
      options->HashedControlSessionPassword) {
    int bad = 0;
    smartlist_t *sl_tmp;
    char received[DIGEST_LEN];
    int also_cookie = options->CookieAuthentication;
    sl = smartlist_new();
    if (options->HashedControlPassword) {
      sl_tmp = decode_hashed_passwords(options->HashedControlPassword);
      if (!sl_tmp)
        bad = 1;
      else {
        smartlist_add_all(sl, sl_tmp);
        smartlist_free(sl_tmp);
      }
    }
    if (options->HashedControlSessionPassword) {
      sl_tmp = decode_hashed_passwords(options->HashedControlSessionPassword);
      if (!sl_tmp)
        bad = 1;
      else {
        smartlist_add_all(sl, sl_tmp);
        smartlist_free(sl_tmp);
      }
    }
    if (bad) {
      if (!also_cookie) {
        log_warn(LD_CONTROL,
                 "Couldn't decode HashedControlPassword: invalid base16");
        errstr="Couldn't decode HashedControlPassword value in configuration.";
      }
      bad_password = 1;
      SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
      smartlist_free(sl);
    } else {
      SMARTLIST_FOREACH(sl, char *, expected,
      {
        secret_to_key(received,DIGEST_LEN,password,password_len,expected);
        if (tor_memeq(expected+S2K_SPECIFIER_LEN, received, DIGEST_LEN))
          goto ok;
      });
      SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
      smartlist_free(sl);

      if (used_quoted_string)
        errstr = "Password did not match HashedControlPassword value from "
          "configuration";
      else
        errstr = "Password did not match HashedControlPassword value from "
          "configuration. Maybe you tried a plain text password? "
          "If so, the standard requires that you put it in double quotes.";
      bad_password = 1;
      if (!also_cookie)
        goto err;
    }
  }

  tor_assert(bad_password && bad_cookie);
  log_warn(LD_CONTROL, "Bad password or authentication cookie on controller.");
  errstr = "Password did not match HashedControlPassword *or* authentication "
    "cookie.";

 err:
  tor_free(password);
  connection_printf_to_buf(conn, "515 Authentication failed: %s\r\n",
                           errstr ? errstr : "Unknown reason.");
  connection_mark_for_close(TO_CONN(conn));
  return 0;
 ok:
  log_info(LD_CONTROL, "Authenticated control connection (%d)", conn->_base.s);
  send_control_done(conn);
  conn->_base.state = CONTROL_CONN_STATE_OPEN;
  tor_free(password);
  if (sl) { /* clean up */
    SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
    smartlist_free(sl);
  }
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_closecircuit ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get a CLOSECIRCUIT command; try to close the named circuit and report success or failure.

Definition at line 2829 of file control.c.

{
  origin_circuit_t *circ = NULL;
  int safe = 0;
  smartlist_t *args;
  (void) len;

  args = getargs_helper("CLOSECIRCUIT", conn, body, 1, -1);
  if (!args)
    return 0;

  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
        log_info(LD_CONTROL, "Skipping unknown option %s",
                 (char*)smartlist_get(args,i));
    }
  }
  SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
  smartlist_free(args);
  if (!circ)
    return 0;

  if (!safe || !circ->p_streams) {
    circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_REQUESTED);
  }

  send_control_done(conn);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_closestream ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get a CLOSESTREAM command; try to close the named stream and report success or failure.

Definition at line 2791 of file control.c.

{
  entry_connection_t *ap_conn=NULL;
  uint8_t reason=0;
  smartlist_t *args;
  int ok;
  (void) len;

  args = getargs_helper("CLOSESTREAM", conn, body, 2, -1);
  if (!args)
    return 0;

  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;

  connection_mark_unattached_ap(ap_conn, reason);
  send_control_done(conn);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_extendcircuit ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get an EXTENDCIRCUIT message.

Try to extend the listed circuit, and report success or failure.

Definition at line 2390 of file control.c.

{
  smartlist_t *router_nicknames=NULL, *nodes=NULL;
  origin_circuit_t *circ = NULL;
  int zero_circ;
  uint8_t intended_purpose = CIRCUIT_PURPOSE_C_GENERAL;
  smartlist_t *args;
  (void) len;

  router_nicknames = smartlist_new();

  args = getargs_helper("EXTENDCIRCUIT", conn, body, 1, -1);
  if (!args)
    goto done;

  zero_circ = !strcmp("0", (char*)smartlist_get(args,0));

  if (zero_circ) {
    const char *purp = find_element_starting_with(args, 1, "PURPOSE=");

    if (purp) {
      intended_purpose = circuit_purpose_from_string(purp);
      if (intended_purpose == CIRCUIT_PURPOSE_UNKNOWN) {
        connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp);
        SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
        smartlist_free(args);
        goto done;
      }
    }

    if ((smartlist_len(args) == 1) ||
        (smartlist_len(args) >= 2 && is_keyval_pair(smartlist_get(args, 1)))) {
      // "EXTENDCIRCUIT 0" || EXTENDCIRCUIT 0 foo=bar"
      circ = circuit_launch(intended_purpose, CIRCLAUNCH_NEED_CAPACITY);
      if (!circ) {
        connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn);
      } else {
        connection_printf_to_buf(conn, "250 EXTENDED %lu\r\n",
                  (unsigned long)circ->global_identifier);
      }
      SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
      smartlist_free(args);
      goto done;
    }
    // "EXTENDCIRCUIT 0 router1,router2" ||
    // "EXTENDCIRCUIT 0 router1,router2 PURPOSE=foo"
  }

  if (!zero_circ && !(circ = get_circ(smartlist_get(args,0)))) {
    connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n",
                             (char*)smartlist_get(args, 0));
    SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
    smartlist_free(args);
    goto done;
  }

  smartlist_split_string(router_nicknames, smartlist_get(args,1), ",", 0, 0);

  SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
  smartlist_free(args);

  nodes = smartlist_new();
  SMARTLIST_FOREACH(router_nicknames, const char *, n,
  {
    const node_t *node = node_get_by_nickname(n, 1);
    if (!node) {
      connection_printf_to_buf(conn, "552 No such router \"%s\"\r\n", n);
      goto done;
    }
    if (!node_has_descriptor(node)) {
      connection_printf_to_buf(conn, "552 descriptor for \"%s\"\r\n", n);
      goto done;
    }
    smartlist_add(nodes, (void*)node);
  });
  if (!smartlist_len(nodes)) {
    connection_write_str_to_buf("512 No router names provided\r\n", conn);
    goto done;
  }

  if (zero_circ) {
    /* start a new circuit */
    circ = origin_circuit_init(intended_purpose, 0);
  }

  /* now circ refers to something that is ready to be extended */
  SMARTLIST_FOREACH(nodes, const node_t *, node,
  {
    extend_info_t *info = extend_info_from_node(node, 0);
    tor_assert(info); /* True, since node_has_descriptor(node) == true */
    circuit_append_new_exit(circ, info);
    extend_info_free(info);
  });

  /* now that we've populated the cpath, start extending */
  if (zero_circ) {
    int err_reason = 0;
    if ((err_reason = circuit_handle_first_hop(circ)) < 0) {
      circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason);
      connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn);
      goto done;
    }
  } else {
    if (circ->_base.state == CIRCUIT_STATE_OPEN) {
      int err_reason = 0;
      circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
      if ((err_reason = circuit_send_next_onion_skin(circ)) < 0) {
        log_info(LD_CONTROL,
                 "send_next_onion_skin failed; circuit marked for closing.");
        circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason);
        connection_write_str_to_buf("551 Couldn't send onion skin\r\n", conn);
        goto done;
      }
    }
  }

  connection_printf_to_buf(conn, "250 EXTENDED %lu\r\n",
                             (unsigned long)circ->global_identifier);
  if (zero_circ) /* send a 'launched' event, for completeness */
    control_event_circuit_status(circ, CIRC_EVENT_LAUNCHED, 0);
 done:
  SMARTLIST_FOREACH(router_nicknames, char *, n, tor_free(n));
  smartlist_free(router_nicknames);
  smartlist_free(nodes);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_getconf ( control_connection_t conn,
uint32_t  body_len,
const char *  body 
) [static]

Called when we receive a GETCONF message.

Parse the request, and reply with a CONFVALUE or an ERROR message

Definition at line 809 of file control.c.

{
  smartlist_t *questions = smartlist_new();
  smartlist_t *answers = smartlist_new();
  smartlist_t *unrecognized = smartlist_new();
  char *msg = NULL;
  size_t msg_len;
  const or_options_t *options = get_options();
  int i, len;

  (void) body_len; /* body is NUL-terminated; so we can ignore len. */
  smartlist_split_string(questions, body, " ",
                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
  SMARTLIST_FOREACH(questions, const char *, q,
  {
    if (!option_is_recognized(q)) {
      smartlist_add(unrecognized, (char*) q);
    } else {
      config_line_t *answer = option_get_assignment(options,q);
      if (!answer) {
        const char *name = option_get_canonical_name(q);
        smartlist_add_asprintf(answers, "250-%s\r\n", name);
      }

      while (answer) {
        config_line_t *next;
        smartlist_add_asprintf(answers, "250-%s=%s\r\n",
                     answer->key, answer->value);

        next = answer->next;
        tor_free(answer->key);
        tor_free(answer->value);
        tor_free(answer);
        answer = next;
      }
    }
  });

  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, TO_CONN(conn));
  } else {
    connection_write_str_to_buf("250 OK\r\n", conn);
  }

  SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp));
  smartlist_free(answers);
  SMARTLIST_FOREACH(questions, char *, cp, tor_free(cp));
  smartlist_free(questions);
  smartlist_free(unrecognized);

  tor_free(msg);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_getinfo ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we receive a GETINFO command.

Try to fetch all requested information, and reply with information or error message.

Definition at line 2254 of file control.c.

{
  smartlist_t *questions = smartlist_new();
  smartlist_t *answers = smartlist_new();
  smartlist_t *unrecognized = smartlist_new();
  char *msg = NULL, *ans = NULL;
  int i;
  (void) len; /* body is NUL-terminated, so it's safe to ignore the length. */

  smartlist_split_string(questions, body, " ",
                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
  SMARTLIST_FOREACH_BEGIN(questions, const char *, q) {
    const char *errmsg = NULL;
    if (handle_getinfo_helper(conn, q, &ans, &errmsg) < 0) {
      if (!errmsg)
        errmsg = "Internal error";
      connection_printf_to_buf(conn, "551 %s\r\n", errmsg);
      goto done;
    }
    if (!ans) {
      smartlist_add(unrecognized, (char*)q);
    } else {
      smartlist_add(answers, tor_strdup(q));
      smartlist_add(answers, ans);
    }
  } SMARTLIST_FOREACH_END(q);
  if (smartlist_len(unrecognized)) {
    for (i=0; i < smartlist_len(unrecognized)-1; ++i)
      connection_printf_to_buf(conn,
                               "552-Unrecognized key \"%s\"\r\n",
                               (char*)smartlist_get(unrecognized, i));
    connection_printf_to_buf(conn,
                             "552 Unrecognized key \"%s\"\r\n",
                             (char*)smartlist_get(unrecognized, i));
    goto done;
  }

  for (i = 0; i < smartlist_len(answers); i += 2) {
    char *k = smartlist_get(answers, i);
    char *v = smartlist_get(answers, i+1);
    if (!strchr(v, '\n') && !strchr(v, '\r')) {
      connection_printf_to_buf(conn, "250-%s=", k);
      connection_write_str_to_buf(v, conn);
      connection_write_str_to_buf("\r\n", conn);
    } else {
      char *esc = NULL;
      size_t esc_len;
      esc_len = write_escaped_data(v, strlen(v), &esc);
      connection_printf_to_buf(conn, "250+%s=\r\n", k);
      connection_write_to_buf(esc, esc_len, TO_CONN(conn));
      tor_free(esc);
    }
  }
  connection_write_str_to_buf("250 OK\r\n", conn);

 done:
  SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp));
  smartlist_free(answers);
  SMARTLIST_FOREACH(questions, char *, cp, tor_free(cp));
  smartlist_free(questions);
  smartlist_free(unrecognized);
  tor_free(msg);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_loadconf ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get a +LOADCONF message.

Definition at line 879 of file control.c.

{
  setopt_err_t retval;
  char *errstring = NULL;
  const char *msg = NULL;
  (void) len;

  retval = options_init_from_string(NULL, body, CMD_RUN_TOR, NULL, &errstring);

  if (retval != SETOPT_OK)
    log_warn(LD_CONTROL,
             "Controller gave us config file that didn't validate: %s",
             errstring);

  switch (retval) {
  case SETOPT_ERR_PARSE:
    msg = "552 Invalid config file";
    break;
  case SETOPT_ERR_TRANSITION:
    msg = "553 Transition not allowed";
    break;
  case SETOPT_ERR_SETTING:
    msg = "553 Unable to set option";
    break;
  case SETOPT_ERR_MISC:
  default:
    msg = "550 Unable to load config";
    break;
  case SETOPT_OK:
    break;
  }
  if (msg) {
    if (errstring)
      connection_printf_to_buf(conn, "%s: %s\r\n", msg, errstring);
    else
      connection_printf_to_buf(conn, "%s\r\n", msg);
  } else {
    send_control_done(conn);
  }
  tor_free(errstring);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_mapaddress ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get a MAPADDRESS command; try to bind all listed addresses, and report success or failure.

Definition at line 1330 of file control.c.

{
  smartlist_t *elts;
  smartlist_t *lines;
  smartlist_t *reply;
  char *r;
  size_t sz;
  (void) len; /* body is NUL-terminated, so it's safe to ignore the length. */

  lines = smartlist_new();
  elts = smartlist_new();
  reply = smartlist_new();
  smartlist_split_string(lines, body, " ",
                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
  SMARTLIST_FOREACH_BEGIN(lines, char *, line) {
    tor_strlower(line);
    smartlist_split_string(elts, line, "=", 0, 2);
    if (smartlist_len(elts) == 2) {
      const char *from = smartlist_get(elts,0);
      const char *to = smartlist_get(elts,1);
      if (address_is_invalid_mapaddress_target(to)) {
        smartlist_add_asprintf(reply,
                     "512-syntax error: invalid address '%s'", to);
        log_warn(LD_CONTROL,
                 "Skipping invalid argument '%s' in MapAddress msg", to);
      } else if (!strcmp(from, ".") || !strcmp(from, "0.0.0.0")) {
        const char *address = addressmap_register_virtual_address(
              !strcmp(from,".") ? RESOLVED_TYPE_HOSTNAME : RESOLVED_TYPE_IPV4,
               tor_strdup(to));
        if (!address) {
          smartlist_add_asprintf(reply,
                       "451-resource exhausted: skipping '%s'", line);
          log_warn(LD_CONTROL,
                   "Unable to allocate address for '%s' in MapAddress msg",
                   safe_str_client(line));
        } else {
          smartlist_add_asprintf(reply, "250-%s=%s", address, to);
        }
      } else {
        addressmap_register(from, tor_strdup(to), 1,
                            ADDRMAPSRC_CONTROLLER, 0, 0);
        smartlist_add_asprintf(reply, "250-%s", line);
      }
    } else {
      smartlist_add_asprintf(reply, "512-syntax error: mapping '%s' is "
                   "not of expected form 'foo=bar'.", line);
      log_info(LD_CONTROL, "Skipping MapAddress '%s': wrong "
                           "number of items.",
                           safe_str_client(line));
    }
    SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
    smartlist_clear(elts);
  } SMARTLIST_FOREACH_END(line);
  SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
  smartlist_free(lines);
  smartlist_free(elts);

  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, TO_CONN(conn));
    tor_free(r);
  } else {
    const char *response =
      "512 syntax error: not enough arguments to mapaddress.\r\n";
    connection_write_to_buf(response, strlen(response), TO_CONN(conn));
  }

  SMARTLIST_FOREACH(reply, char *, cp, tor_free(cp));
  smartlist_free(reply);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_postdescriptor ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get a POSTDESCRIPTOR message.

Try to learn the provided descriptor, and report success or failure.

Definition at line 2674 of file control.c.

{
  char *desc;
  const char *msg=NULL;
  uint8_t purpose = ROUTER_PURPOSE_GENERAL;
  int cache = 0; /* eventually, we may switch this to 1 */

  char *cp = memchr(body, '\n', len);
  smartlist_t *args = smartlist_new();
  tor_assert(cp);
  *cp++ = '\0';

  smartlist_split_string(args, body, " ",
                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
  SMARTLIST_FOREACH(args, char *, option,
  {
    if (!strcasecmpstart(option, "purpose=")) {
      option += strlen("purpose=");
      purpose = router_purpose_from_string(option);
      if (purpose == ROUTER_PURPOSE_UNKNOWN) {
        connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n",
                                 option);
        goto done;
      }
    } else if (!strcasecmpstart(option, "cache=")) {
      option += strlen("cache=");
      if (!strcasecmp(option, "no"))
        cache = 0;
      else if (!strcasecmp(option, "yes"))
        cache = 1;
      else {
        connection_printf_to_buf(conn, "552 Unknown cache request \"%s\"\r\n",
                                 option);
        goto done;
      }
    } else { /* unrecognized argument? */
      connection_printf_to_buf(conn,
        "512 Unexpected argument \"%s\" to postdescriptor\r\n", option);
      goto done;
    }
  });

  read_escaped_data(cp, len-(cp-body), &desc);

  switch (router_load_single_router(desc, purpose, cache, &msg)) {
  case -1:
    if (!msg) msg = "Could not parse descriptor";
    connection_printf_to_buf(conn, "554 %s\r\n", msg);
    break;
  case 0:
    if (!msg) msg = "Descriptor not added";
    connection_printf_to_buf(conn, "251 %s\r\n",msg);
    break;
  case 1:
    send_control_done(conn);
    break;
  }

  tor_free(desc);
 done:
  SMARTLIST_FOREACH(args, char *, arg, tor_free(arg));
  smartlist_free(args);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_protocolinfo ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get a PROTOCOLINFO command: send back a reply.

Definition at line 2912 of file control.c.

{
  const char *bad_arg = NULL;
  smartlist_t *args;
  (void)len;

  conn->have_sent_protocolinfo = 1;
  args = smartlist_new();
  smartlist_split_string(args, body, " ",
                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
  SMARTLIST_FOREACH(args, const char *, arg, {
      int ok;
      tor_parse_long(arg, 10, 0, LONG_MAX, &ok, NULL);
      if (!ok) {
        bad_arg = arg;
        break;
      }
    });
  if (bad_arg) {
    connection_printf_to_buf(conn, "513 No such version %s\r\n",
                             escaped(bad_arg));
    /* Don't tolerate bad arguments when not authenticated. */
    if (!STATE_IS_OPEN(TO_CONN(conn)->state))
      connection_mark_for_close(TO_CONN(conn));
    goto done;
  } else {
    const or_options_t *options = get_options();
    int cookies = options->CookieAuthentication;
    char *cfile = get_cookie_file();
    char *abs_cfile;
    char *esc_cfile;
    char *methods;
    abs_cfile = make_path_absolute(cfile);
    esc_cfile = esc_for_log(abs_cfile);
    {
      int passwd = (options->HashedControlPassword != NULL ||
                    options->HashedControlSessionPassword != NULL);
      smartlist_t *mlist = smartlist_new();
      if (cookies) {
        smartlist_add(mlist, (char*)"COOKIE");
        smartlist_add(mlist, (char*)"SAFECOOKIE");
      }
      if (passwd)
        smartlist_add(mlist, (char*)"HASHEDPASSWORD");
      if (!cookies && !passwd)
        smartlist_add(mlist, (char*)"NULL");
      methods = smartlist_join_strings(mlist, ",", 0, NULL);
      smartlist_free(mlist);
    }

    connection_printf_to_buf(conn,
                             "250-PROTOCOLINFO 1\r\n"
                             "250-AUTH METHODS=%s%s%s\r\n"
                             "250-VERSION Tor=%s\r\n"
                             "250 OK\r\n",
                             methods,
                             cookies?" COOKIEFILE=":"",
                             cookies?esc_cfile:"",
                             escaped(VERSION));
    tor_free(methods);
    tor_free(cfile);
    tor_free(abs_cfile);
    tor_free(esc_cfile);
  }
 done:
  SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
  smartlist_free(args);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_redirectstream ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we receive a REDIRECTSTERAM command.

Try to change the target address of the named AP stream, and report success or failure.

Definition at line 2743 of file control.c.

{
  entry_connection_t *ap_conn = NULL;
  char *new_addr = NULL;
  uint16_t new_port = 0;
  smartlist_t *args;
  (void) len;

  args = getargs_helper("REDIRECTSTREAM", conn, body, 2, -1);
  if (!args)
    return 0;

  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 {
    int ok = 1;
    if (smartlist_len(args) > 2) { /* 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));
    }
  }

  SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
  smartlist_free(args);
  if (!new_addr)
    return 0;

  strlcpy(ap_conn->socks_request->address, new_addr,
          sizeof(ap_conn->socks_request->address));
  if (new_port)
    ap_conn->socks_request->port = new_port;
  tor_free(new_addr);
  send_control_done(conn);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_resetconf ( control_connection_t conn,
uint32_t  len,
char *  body 
) [static]

Called when we receive a RESETCONF message: parse the body and try to update our configuration.

Reply with a DONE or ERROR message. Modifies the contents of body.

Definition at line 801 of file control.c.

{
  return control_setconf_helper(conn, len, body, 1);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_resolve ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get a RESOLVE command: start trying to resolve the listed addresses.

Definition at line 2870 of file control.c.

{
  smartlist_t *args, *failed;
  int is_reverse = 0;
  (void) len; /* body is nul-terminated; it's safe to ignore the length */

  if (!(conn->event_mask & ((uint32_t)1L<<EVENT_ADDRMAP))) {
    log_warn(LD_CONTROL, "Controller asked us to resolve an address, but "
             "isn't listening for ADDRMAP events.  It probably won't see "
             "the answer.");
  }
  args = smartlist_new();
  smartlist_split_string(args, body, " ",
                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
  {
    const char *modearg = find_element_starting_with(args, 0, "mode=");
    if (modearg && !strcasecmp(modearg, "mode=reverse"))
      is_reverse = 1;
  }
  failed = smartlist_new();
  SMARTLIST_FOREACH(args, const char *, arg, {
      if (!is_keyval_pair(arg)) {
          if (dnsserv_launch_request(arg, is_reverse)<0)
            smartlist_add(failed, (char*)arg);
      }
  });

  send_control_done(conn);
  SMARTLIST_FOREACH(failed, const char *, arg, {
      control_event_address_mapped(arg, arg, time(NULL),
                                   "Unable to launch resolve request");
  });

  SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
  smartlist_free(args);
  smartlist_free(failed);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_saveconf ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get a SAVECONF command.

Try to flush the current options to disk, and report success or failure.

Definition at line 1233 of file control.c.

{
  (void) len;
  (void) body;
  if (options_save_current()<0) {
    connection_write_str_to_buf(
      "551 Unable to write configuration to disk.\r\n", conn);
  } else {
    send_control_done(conn);
  }
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_setcircuitpurpose ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get a SETCIRCUITPURPOSE message.

If we can find the circuit and it's a valid purpose, change it.

Definition at line 2521 of file control.c.

{
  origin_circuit_t *circ = NULL;
  uint8_t new_purpose;
  smartlist_t *args;
  (void) len; /* body is NUL-terminated, so it's safe to ignore the length. */

  args = getargs_helper("SETCIRCUITPURPOSE", conn, body, 2, -1);
  if (!args)
    goto done;

  if (!(circ = get_circ(smartlist_get(args,0)))) {
    connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n",
                             (char*)smartlist_get(args, 0));
    goto done;
  }

  {
    const char *purp = find_element_starting_with(args,1,"PURPOSE=");
    if (!purp) {
      connection_write_str_to_buf("552 No purpose given\r\n", conn);
      goto done;
    }
    new_purpose = circuit_purpose_from_string(purp);
    if (new_purpose == CIRCUIT_PURPOSE_UNKNOWN) {
      connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp);
      goto done;
    }
  }

  circuit_change_purpose(TO_CIRCUIT(circ), new_purpose);
  connection_write_str_to_buf("250 OK\r\n", conn);

 done:
  if (args) {
    SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
    smartlist_free(args);
  }
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_setconf ( control_connection_t conn,
uint32_t  len,
char *  body 
) [static]

Called when we receive a SETCONF message: parse the body and try to update our configuration.

Reply with a DONE or ERROR message. Modifies the contents of body.

Definition at line 792 of file control.c.

{
  return control_setconf_helper(conn, len, body, 0);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_setevents ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

Called when we get a SETEVENTS message: update conn->event_mask, and reply with DONE or ERROR.

Definition at line 962 of file control.c.

{
  int event_code = -1;
  uint32_t event_mask = 0;
  smartlist_t *events = smartlist_new();

  (void) len;

  smartlist_split_string(events, body, " ",
                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
  SMARTLIST_FOREACH_BEGIN(events, const char *, ev)
    {
      if (!strcasecmp(ev, "EXTENDED")) {
        continue;
      } else {
        int i;
        for (i = 0; control_event_table[i].event_name != NULL; ++i) {
          if (!strcasecmp(ev, control_event_table[i].event_name)) {
            event_code = control_event_table[i].event_code;
            break;
          }
        }

        if (event_code == -1) {
          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_END(ev);
  SMARTLIST_FOREACH(events, char *, e, tor_free(e));
  smartlist_free(events);

  conn->event_mask = event_mask;

  control_update_global_event_mask();
  send_control_done(conn);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int handle_control_signal ( control_connection_t conn,
uint32_t  len,
const char *  body 
) [static]

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.)

Definition at line 1251 of file control.c.

{
  int sig;
  int n = 0;
  char *s;

  (void) len;

  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 if (!strcasecmp(s, "NEWNYM"))
    sig = SIGNEWNYM;
  else if (!strcasecmp(s, "CLEARDNSCACHE"))
    sig = SIGCLEARDNSCACHE;
  else {
    connection_printf_to_buf(conn, "552 Unrecognized signal code \"%s\"\r\n&qu