\n" "It appears you have configured your web browser to use Tor's control port" " as an HTTP proxy.\n" "This is not correct: Tor's default SOCKS proxy port is 9050.\n" "Please configure your client accordingly.\n" "
\n" "\n" "See " "https://www.torproject.org/documentation.html for more " "information.\n" "\n" "
\n" "\n" "\n"; /** Called when data has arrived on a v1 control connection: Try to fetch * commands from conn->inbuf, and execute them. */ int connection_control_process_inbuf(control_connection_t *conn) { 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_buf_add(buf, 4+body_len, TO_CONN(conn)); connection_mark_and_flush(TO_CONN(conn)); return 0; } /* If the user has the HTTP proxy port and the control port confused. */ if (conn->base_.state == CONTROL_CONN_STATE_NEEDAUTH && peek_connection_has_http_command(TO_CONN(conn))) { connection_write_str_to_buf(CONTROLPORT_IS_NOT_AN_HTTP_PROXY_MSG, conn); log_notice(LD_CONTROL, "Received HTTP request on ControlPort"); 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_buf_get_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, "DROPOWNERSHIP")) { if (handle_control_dropownership(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 if (!strcasecmp(conn->incoming_cmd, "DROPGUARDS")) { if (handle_control_dropguards(conn, cmd_data_len, args)) return -1; } else if (!strcasecmp(conn->incoming_cmd, "HSFETCH")) { if (handle_control_hsfetch(conn, cmd_data_len, args)) return -1; } else if (!strcasecmp(conn->incoming_cmd, "+HSPOST")) { if (handle_control_hspost(conn, cmd_data_len, args)) return -1; } else if (!strcasecmp(conn->incoming_cmd, "ADD_ONION")) { int ret = handle_control_add_onion(conn, cmd_data_len, args); memwipe(args, 0, cmd_data_len); /* Scrub the private key. */ if (ret) return -1; } else if (!strcasecmp(conn->incoming_cmd, "DEL_ONION")) { int ret = handle_control_del_onion(conn, cmd_data_len, args); memwipe(args, 0, cmd_data_len); /* Scrub the service id/pk. */ if (ret) 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; } /** Cached liveness for network liveness events and GETINFO */ static int network_is_live = 0; int get_cached_network_liveness(void) { return network_is_live; } void set_cached_network_liveness(int liveness) { network_is_live = liveness; } /** Helper: Return a newly allocated string containing a path to the * file where we store our authentication cookie. */ char * get_controller_cookie_file_name(void) { 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"); } } /* Initialize the cookie-based authentication system of the * ControlPort. If enabled is 0, then disable the cookie * authentication system. */ int init_control_cookie_authentication(int enabled) { char *fname = NULL; int retval; if (!enabled) { authentication_cookie_is_set = 0; return 0; } fname = get_controller_cookie_file_name(); retval = init_cookie_authentication(fname, "", /* no header */ AUTHENTICATION_COOKIE_LEN, get_options()->CookieAuthFileGroupReadable, &authentication_cookie, &authentication_cookie_is_set); tor_free(fname); return retval; } /** 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 char *owning_controller_process_spec = NULL; /** A process-termination monitor for Tor's owning controller, or NULL * if this Tor instance is not currently owned by a process. */ static tor_process_monitor_t *owning_controller_process_monitor = NULL; /** Process-termination monitor callback for Tor's owning controller * process. */ static void owning_controller_procmon_cb(void *unused) { (void)unused; lost_owning_controller("process", "vanished"); } /** Set process_spec as Tor's owning controller process. * Exit on failure. */ void monitor_owning_controller_process(const char *process_spec) { const char *msg; tor_assert((owning_controller_process_spec == NULL) == (owning_controller_process_monitor == NULL)); if (owning_controller_process_spec != NULL) { if ((process_spec != NULL) && !strcmp(process_spec, owning_controller_process_spec)) { /* Same process -- return now, instead of disposing of and * recreating the process-termination monitor. */ return; } /* We are currently owned by a process, and we should no longer be * owned by it. Free the process-termination monitor. */ tor_process_monitor_free(owning_controller_process_monitor); owning_controller_process_monitor = NULL; tor_free(owning_controller_process_spec); owning_controller_process_spec = NULL; } tor_assert((owning_controller_process_spec == NULL) && (owning_controller_process_monitor == NULL)); if (process_spec == NULL) return; owning_controller_process_spec = tor_strdup(process_spec); owning_controller_process_monitor = tor_process_monitor_new(tor_libevent_get_base(), owning_controller_process_spec, LD_CONTROL, owning_controller_procmon_cb, NULL, &msg); if (owning_controller_process_monitor == NULL) { log_err(LD_BUG, "Couldn't create process-termination monitor for " "owning controller: %s. Exiting.", msg); owning_controller_process_spec = NULL; tor_shutdown_event_loop_and_exit(1); } } /** Return a longname the node whose identity is id_digest. If * node_get_by_id() returns NULL, base 16 encoding of id_digest is * returned instead. * * This function is not thread-safe. Each call to this function invalidates * previous values returned by this function. */ MOCK_IMPL(const char *, node_describe_longname_by_id,(const char *id_digest)) { static char longname[MAX_VERBOSE_NICKNAME_LEN+1]; node_get_verbose_nickname_by_id(id_digest, longname); return longname; } /** Free any leftover allocated memory of the control.c subsystem. */ void control_free_all(void) { control_events_free_all(); if (authentication_cookie) /* Free the auth cookie */ tor_free(authentication_cookie); if (detached_onion_services) { /* Free the detached onion services */ SMARTLIST_FOREACH(detached_onion_services, char *, cp, tor_free(cp)); smartlist_free(detached_onion_services); } control_event_bootstrap_reset(); authentication_cookie_is_set = 0; }