connection_edge.c
上传用户:awang829
上传日期:2019-07-14
资源大小:2356k
文件大小:105k
- /* Copyright (c) 2001 Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2009, The Tor Project, Inc. */
- /* See LICENSE for licensing information */
- /**
- * file connection_edge.c
- * brief Handle edge streams.
- **/
- #include "or.h"
- #ifdef HAVE_LINUX_TYPES_H
- #include <linux/types.h>
- #endif
- #ifdef HAVE_LINUX_NETFILTER_IPV4_H
- #include <linux/netfilter_ipv4.h>
- #define TRANS_NETFILTER
- #endif
- #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H)
- #include <net/if.h>
- #include <net/pfvar.h>
- #define TRANS_PF
- #endif
- #define SOCKS4_GRANTED 90
- #define SOCKS4_REJECT 91
- static int connection_ap_handshake_process_socks(edge_connection_t *conn);
- static int connection_ap_process_natd(edge_connection_t *conn);
- static int connection_exit_connect_dir(edge_connection_t *exitconn);
- static int address_is_in_virtual_range(const char *addr);
- static int consider_plaintext_ports(edge_connection_t *conn, uint16_t port);
- static void clear_trackexithost_mappings(const char *exitname);
- /** An AP stream has failed/finished. If it hasn't already sent back
- * a socks reply, send one now (based on endreason). Also set
- * has_sent_end to 1, and mark the conn.
- */
- void
- _connection_mark_unattached_ap(edge_connection_t *conn, int endreason,
- int line, const char *file)
- {
- tor_assert(conn->_base.type == CONN_TYPE_AP);
- conn->edge_has_sent_end = 1; /* no circ yet */
- if (conn->_base.marked_for_close) {
- /* This call will warn as appropriate. */
- _connection_mark_for_close(TO_CONN(conn), line, file);
- return;
- }
- if (!conn->socks_request->has_finished) {
- if (endreason & END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED)
- log_warn(LD_BUG,
- "stream (marked at %s:%d) sending two socks replies?",
- file, line);
- if (SOCKS_COMMAND_IS_CONNECT(conn->socks_request->command))
- connection_ap_handshake_socks_reply(conn, NULL, 0, endreason);
- else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command))
- connection_ap_handshake_socks_resolved(conn,
- RESOLVED_TYPE_ERROR_TRANSIENT,
- 0, NULL, -1, -1);
- else /* unknown or no handshake at all. send no response. */
- conn->socks_request->has_finished = 1;
- }
- _connection_mark_for_close(TO_CONN(conn), line, file);
- conn->_base.hold_open_until_flushed = 1;
- conn->end_reason = endreason;
- }
- /** There was an EOF. Send an end and mark the connection for close.
- */
- int
- connection_edge_reached_eof(edge_connection_t *conn)
- {
- if (buf_datalen(conn->_base.inbuf) &&
- connection_state_is_open(TO_CONN(conn))) {
- /* it still has stuff to process. don't let it die yet. */
- return 0;
- }
- log_info(LD_EDGE,"conn (fd %d) reached eof. Closing.", conn->_base.s);
- if (!conn->_base.marked_for_close) {
- /* only mark it if not already marked. it's possible to
- * get the 'end' right around when the client hangs up on us. */
- connection_edge_end(conn, END_STREAM_REASON_DONE);
- if (conn->socks_request) /* eof, so don't send a socks reply back */
- conn->socks_request->has_finished = 1;
- connection_mark_for_close(TO_CONN(conn));
- }
- return 0;
- }
- /** Handle new bytes on conn->inbuf based on state:
- * - If it's waiting for socks info, try to read another step of the
- * socks handshake out of conn->inbuf.
- * - If it's waiting for the original destination, fetch it.
- * - If it's open, then package more relay cells from the stream.
- * - Else, leave the bytes on inbuf alone for now.
- *
- * Mark and return -1 if there was an unexpected error with the conn,
- * else return 0.
- */
- int
- connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
- {
- tor_assert(conn);
- switch (conn->_base.state) {
- case AP_CONN_STATE_SOCKS_WAIT:
- if (connection_ap_handshake_process_socks(conn) < 0) {
- /* already marked */
- return -1;
- }
- return 0;
- case AP_CONN_STATE_NATD_WAIT:
- if (connection_ap_process_natd(conn) < 0) {
- /* already marked */
- return -1;
- }
- return 0;
- case AP_CONN_STATE_OPEN:
- case EXIT_CONN_STATE_OPEN:
- if (connection_edge_package_raw_inbuf(conn, package_partial) < 0) {
- /* (We already sent an end cell if possible) */
- connection_mark_for_close(TO_CONN(conn));
- return -1;
- }
- return 0;
- case EXIT_CONN_STATE_CONNECTING:
- case AP_CONN_STATE_RENDDESC_WAIT:
- case AP_CONN_STATE_CIRCUIT_WAIT:
- case AP_CONN_STATE_CONNECT_WAIT:
- case AP_CONN_STATE_RESOLVE_WAIT:
- case AP_CONN_STATE_CONTROLLER_WAIT:
- log_info(LD_EDGE,
- "data from edge while in '%s' state. Leaving it on buffer.",
- conn_state_to_string(conn->_base.type, conn->_base.state));
- return 0;
- }
- log_warn(LD_BUG,"Got unexpected state %d. Closing.",conn->_base.state);
- tor_fragile_assert();
- connection_edge_end(conn, END_STREAM_REASON_INTERNAL);
- connection_mark_for_close(TO_CONN(conn));
- return -1;
- }
- /** This edge needs to be closed, because its circuit has closed.
- * Mark it for close and return 0.
- */
- int
- connection_edge_destroy(circid_t circ_id, edge_connection_t *conn)
- {
- if (!conn->_base.marked_for_close) {
- log_info(LD_EDGE,
- "CircID %d: At an edge. Marking connection for close.", circ_id);
- if (conn->_base.type == CONN_TYPE_AP) {
- connection_mark_unattached_ap(conn, END_STREAM_REASON_DESTROY);
- control_event_stream_bandwidth(conn);
- control_event_stream_status(conn, STREAM_EVENT_CLOSED,
- END_STREAM_REASON_DESTROY);
- conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED;
- } else {
- /* closing the circuit, nothing to send an END to */
- conn->edge_has_sent_end = 1;
- conn->end_reason = END_STREAM_REASON_DESTROY;
- conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED;
- connection_mark_for_close(TO_CONN(conn));
- conn->_base.hold_open_until_flushed = 1;
- }
- }
- conn->cpath_layer = NULL;
- conn->on_circuit = NULL;
- return 0;
- }
- /** Send a raw end cell to the stream with ID <b>stream_id</b> out over the
- * <b>circ</b> towards the hop identified with <b>cpath_layer</b>. If this
- * is not a client connection, set the relay end cell's reason for closing
- * as <b>reason</b> */
- static int
- relay_send_end_cell_from_edge(streamid_t stream_id, circuit_t *circ,
- uint8_t reason, crypt_path_t *cpath_layer)
- {
- char payload[1];
- if (CIRCUIT_PURPOSE_IS_CLIENT(circ->purpose)) {
- /* Never send the server an informative reason code; it doesn't need to
- * know why the client stream is failing. */
- reason = END_STREAM_REASON_MISC;
- }
- payload[0] = (char) reason;
- return relay_send_command_from_edge(stream_id, circ, RELAY_COMMAND_END,
- payload, 1, cpath_layer);
- }
- /** Send a relay end cell from stream <b>conn</b> down conn's circuit, and
- * remember that we've done so. If this is not a client connection, set the
- * relay end cell's reason for closing as <b>reason</b>.
- *
- * Return -1 if this function has already been called on this conn,
- * else return 0.
- */
- int
- connection_edge_end(edge_connection_t *conn, uint8_t reason)
- {
- char payload[RELAY_PAYLOAD_SIZE];
- size_t payload_len=1;
- circuit_t *circ;
- uint8_t control_reason = reason;
- if (conn->edge_has_sent_end) {
- log_warn(LD_BUG,"(Harmless.) Calling connection_edge_end (reason %d) "
- "on an already ended stream?", reason);
- tor_fragile_assert();
- return -1;
- }
- if (conn->_base.marked_for_close) {
- log_warn(LD_BUG,
- "called on conn that's already marked for close at %s:%d.",
- conn->_base.marked_for_close_file, conn->_base.marked_for_close);
- return 0;
- }
- circ = circuit_get_by_edge_conn(conn);
- if (circ && CIRCUIT_PURPOSE_IS_CLIENT(circ->purpose)) {
- /* If this is a client circuit, don't send the server an informative
- * reason code; it doesn't need to know why the client stream is
- * failing. */
- reason = END_STREAM_REASON_MISC;
- }
- payload[0] = (char)reason;
- if (reason == END_STREAM_REASON_EXITPOLICY &&
- !connection_edge_is_rendezvous_stream(conn)) {
- int addrlen;
- if (tor_addr_family(&conn->_base.addr) == AF_INET) {
- set_uint32(payload+1, tor_addr_to_ipv4n(&conn->_base.addr));
- addrlen = 4;
- } else {
- memcpy(payload+1, tor_addr_to_in6_addr8(&conn->_base.addr), 16);
- addrlen = 16;
- }
- set_uint32(payload+1+addrlen, htonl(dns_clip_ttl(conn->address_ttl)));
- payload_len += 4+addrlen;
- }
- if (circ && !circ->marked_for_close) {
- log_debug(LD_EDGE,"Sending end on conn (fd %d).",conn->_base.s);
- connection_edge_send_command(conn, RELAY_COMMAND_END,
- payload, payload_len);
- } else {
- log_debug(LD_EDGE,"No circ to send end on conn (fd %d).",
- conn->_base.s);
- }
- conn->edge_has_sent_end = 1;
- conn->end_reason = control_reason;
- return 0;
- }
- /** An error has just occurred on an operation on an edge connection
- * <b>conn</b>. Extract the errno; convert it to an end reason, and send an
- * appropriate relay end cell to the other end of the connection's circuit.
- **/
- int
- connection_edge_end_errno(edge_connection_t *conn)
- {
- uint8_t reason;
- tor_assert(conn);
- reason = errno_to_stream_end_reason(tor_socket_errno(conn->_base.s));
- return connection_edge_end(conn, reason);
- }
- /** Connection <b>conn</b> has finished writing and has no bytes left on
- * its outbuf.
- *
- * If it's in state 'open', stop writing, consider responding with a
- * sendme, and return.
- * Otherwise, stop writing and return.
- *
- * If <b>conn</b> is broken, mark it for close and return -1, else
- * return 0.
- */
- int
- connection_edge_finished_flushing(edge_connection_t *conn)
- {
- tor_assert(conn);
- switch (conn->_base.state) {
- case AP_CONN_STATE_OPEN:
- case EXIT_CONN_STATE_OPEN:
- connection_stop_writing(TO_CONN(conn));
- connection_edge_consider_sending_sendme(conn);
- return 0;
- case AP_CONN_STATE_SOCKS_WAIT:
- case AP_CONN_STATE_NATD_WAIT:
- case AP_CONN_STATE_RENDDESC_WAIT:
- case AP_CONN_STATE_CIRCUIT_WAIT:
- case AP_CONN_STATE_CONNECT_WAIT:
- case AP_CONN_STATE_CONTROLLER_WAIT:
- connection_stop_writing(TO_CONN(conn));
- return 0;
- default:
- log_warn(LD_BUG, "Called in unexpected state %d.",conn->_base.state);
- tor_fragile_assert();
- return -1;
- }
- return 0;
- }
- /** Connected handler for exit connections: start writing pending
- * data, deliver 'CONNECTED' relay cells as appropriate, and check
- * any pending data that may have been received. */
- int
- connection_edge_finished_connecting(edge_connection_t *edge_conn)
- {
- connection_t *conn;
- tor_assert(edge_conn);
- tor_assert(edge_conn->_base.type == CONN_TYPE_EXIT);
- conn = TO_CONN(edge_conn);
- tor_assert(conn->state == EXIT_CONN_STATE_CONNECTING);
- log_info(LD_EXIT,"Exit connection to %s:%u (%s) established.",
- escaped_safe_str(conn->address),conn->port,
- safe_str(fmt_addr(&conn->addr)));
- conn->state = EXIT_CONN_STATE_OPEN;
- connection_watch_events(conn, EV_READ); /* stop writing, continue reading */
- if (connection_wants_to_flush(conn)) /* in case there are any queued relay
- * cells */
- connection_start_writing(conn);
- /* deliver a 'connected' relay cell back through the circuit. */
- if (connection_edge_is_rendezvous_stream(edge_conn)) {
- if (connection_edge_send_command(edge_conn,
- RELAY_COMMAND_CONNECTED, NULL, 0) < 0)
- return 0; /* circuit is closed, don't continue */
- } else {
- char connected_payload[20];
- int connected_payload_len;
- if (tor_addr_family(&conn->addr) == AF_INET) {
- set_uint32(connected_payload, tor_addr_to_ipv4n(&conn->addr));
- set_uint32(connected_payload+4,
- htonl(dns_clip_ttl(edge_conn->address_ttl)));
- connected_payload_len = 8;
- } else {
- memcpy(connected_payload, tor_addr_to_in6_addr8(&conn->addr), 16);
- set_uint32(connected_payload+16,
- htonl(dns_clip_ttl(edge_conn->address_ttl)));
- connected_payload_len = 20;
- }
- if (connection_edge_send_command(edge_conn,
- RELAY_COMMAND_CONNECTED,
- connected_payload, connected_payload_len) < 0)
- return 0; /* circuit is closed, don't continue */
- }
- tor_assert(edge_conn->package_window > 0);
- /* in case the server has written anything */
- return connection_edge_process_inbuf(edge_conn, 1);
- }
- /** Define a schedule for how long to wait between retrying
- * application connections. Rather than waiting a fixed amount of
- * time between each retry, we wait 10 seconds each for the first
- * two tries, and 15 seconds for each retry after
- * that. Hopefully this will improve the expected user experience. */
- static int
- compute_retry_timeout(edge_connection_t *conn)
- {
- if (conn->num_socks_retries < 2) /* try 0 and try 1 */
- return 10;
- return 15;
- }
- /** Find all general-purpose AP streams waiting for a response that sent their
- * begin/resolve cell >=15 seconds ago. Detach from their current circuit, and
- * mark their current circuit as unsuitable for new streams. Then call
- * connection_ap_handshake_attach_circuit() to attach to a new circuit (if
- * available) or launch a new one.
- *
- * For rendezvous streams, simply give up after SocksTimeout seconds (with no
- * retry attempt).
- */
- void
- connection_ap_expire_beginning(void)
- {
- edge_connection_t *conn;
- circuit_t *circ;
- time_t now = time(NULL);
- or_options_t *options = get_options();
- int severity;
- int cutoff;
- int seconds_idle, seconds_since_born;
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) {
- if (c->type != CONN_TYPE_AP || c->marked_for_close)
- continue;
- conn = TO_EDGE_CONN(c);
- /* if it's an internal linked connection, don't yell its status. */
- severity = (tor_addr_is_null(&conn->_base.addr) && !conn->_base.port)
- ? LOG_INFO : LOG_NOTICE;
- seconds_idle = (int)( now - conn->_base.timestamp_lastread );
- seconds_since_born = (int)( now - conn->_base.timestamp_created );
- if (conn->_base.state == AP_CONN_STATE_OPEN)
- continue;
- /* We already consider SocksTimeout in
- * connection_ap_handshake_attach_circuit(), but we need to consider
- * it here too because controllers that put streams in controller_wait
- * state never ask Tor to attach the circuit. */
- if (AP_CONN_STATE_IS_UNATTACHED(conn->_base.state)) {
- if (seconds_since_born >= options->SocksTimeout) {
- log_fn(severity, LD_APP,
- "Tried for %d seconds to get a connection to %s:%d. "
- "Giving up. (%s)",
- seconds_since_born, safe_str(conn->socks_request->address),
- conn->socks_request->port,
- conn_state_to_string(CONN_TYPE_AP, conn->_base.state));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TIMEOUT);
- }
- continue;
- }
- /* We're in state connect_wait or resolve_wait now -- waiting for a
- * reply to our relay cell. See if we want to retry/give up. */
- cutoff = compute_retry_timeout(conn);
- if (seconds_idle < cutoff)
- continue;
- circ = circuit_get_by_edge_conn(conn);
- if (!circ) { /* it's vanished? */
- log_info(LD_APP,"Conn is waiting (address %s), but lost its circ.",
- safe_str(conn->socks_request->address));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TIMEOUT);
- continue;
- }
- if (circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) {
- if (seconds_idle >= options->SocksTimeout) {
- log_fn(severity, LD_REND,
- "Rend stream is %d seconds late. Giving up on address"
- " '%s.onion'.",
- seconds_idle,
- safe_str(conn->socks_request->address));
- connection_edge_end(conn, END_STREAM_REASON_TIMEOUT);
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TIMEOUT);
- }
- continue;
- }
- tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_GENERAL);
- log_fn(cutoff < 15 ? LOG_INFO : severity, LD_APP,
- "We tried for %d seconds to connect to '%s' using exit '%s'."
- " Retrying on a new circuit.",
- seconds_idle, safe_str(conn->socks_request->address),
- conn->cpath_layer ?
- conn->cpath_layer->extend_info->nickname : "*unnamed*");
- /* send an end down the circuit */
- connection_edge_end(conn, END_STREAM_REASON_TIMEOUT);
- /* un-mark it as ending, since we're going to reuse it */
- conn->edge_has_sent_end = 0;
- conn->end_reason = 0;
- /* kludge to make us not try this circuit again, yet to allow
- * current streams on it to survive if they can: make it
- * unattractive to use for new streams */
- tor_assert(circ->timestamp_dirty);
- circ->timestamp_dirty -= options->MaxCircuitDirtiness;
- /* give our stream another 'cutoff' seconds to try */
- conn->_base.timestamp_lastread += cutoff;
- if (conn->num_socks_retries < 250) /* avoid overflow */
- conn->num_socks_retries++;
- /* move it back into 'pending' state, and try to attach. */
- if (connection_ap_detach_retriable(conn, TO_ORIGIN_CIRCUIT(circ),
- END_STREAM_REASON_TIMEOUT)<0) {
- if (!conn->_base.marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
- }
- } SMARTLIST_FOREACH_END(conn);
- }
- /** Tell any AP streams that are waiting for a new circuit to try again,
- * either attaching to an available circ or launching a new one.
- */
- void
- connection_ap_attach_pending(void)
- {
- edge_connection_t *edge_conn;
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
- if (conn->marked_for_close ||
- conn->type != CONN_TYPE_AP ||
- conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
- continue;
- edge_conn = TO_EDGE_CONN(conn);
- if (connection_ap_handshake_attach_circuit(edge_conn) < 0) {
- if (!edge_conn->_base.marked_for_close)
- connection_mark_unattached_ap(edge_conn,
- END_STREAM_REASON_CANT_ATTACH);
- }
- });
- }
- /** Tell any AP streams that are waiting for a one-hop tunnel to
- * <b>failed_digest</b> that they are going to fail. */
- /* XXX022 We should get rid of this function, and instead attach
- * one-hop streams to circ->p_streams so they get marked in
- * circuit_mark_for_close like normal p_streams. */
- void
- connection_ap_fail_onehop(const char *failed_digest,
- cpath_build_state_t *build_state)
- {
- edge_connection_t *edge_conn;
- char digest[DIGEST_LEN];
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
- if (conn->marked_for_close ||
- conn->type != CONN_TYPE_AP ||
- conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
- continue;
- edge_conn = TO_EDGE_CONN(conn);
- if (!edge_conn->want_onehop)
- continue;
- if (hexdigest_to_digest(edge_conn->chosen_exit_name, digest) < 0 ||
- memcmp(digest, failed_digest, DIGEST_LEN))
- continue;
- if (tor_digest_is_zero(digest)) {
- /* we don't know the digest; have to compare addr:port */
- tor_addr_t addr;
- if (!build_state || !build_state->chosen_exit ||
- !edge_conn->socks_request || !edge_conn->socks_request->address)
- continue;
- if (tor_addr_from_str(&addr, edge_conn->socks_request->address)<0 ||
- !tor_addr_eq(&build_state->chosen_exit->addr, &addr) ||
- build_state->chosen_exit->port != edge_conn->socks_request->port)
- continue;
- }
- log_info(LD_APP, "Closing one-hop stream to '%s/%s' because the OR conn "
- "just failed.", edge_conn->chosen_exit_name,
- edge_conn->socks_request->address);
- connection_mark_unattached_ap(edge_conn, END_STREAM_REASON_TIMEOUT);
- } SMARTLIST_FOREACH_END(conn);
- }
- /** A circuit failed to finish on its last hop <b>info</b>. If there
- * are any streams waiting with this exit node in mind, but they
- * don't absolutely require it, make them give up on it.
- */
- void
- circuit_discard_optional_exit_enclaves(extend_info_t *info)
- {
- edge_connection_t *edge_conn;
- routerinfo_t *r1, *r2;
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
- if (conn->marked_for_close ||
- conn->type != CONN_TYPE_AP ||
- conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
- continue;
- edge_conn = TO_EDGE_CONN(conn);
- if (!edge_conn->chosen_exit_optional &&
- !edge_conn->chosen_exit_retries)
- continue;
- r1 = router_get_by_nickname(edge_conn->chosen_exit_name, 0);
- r2 = router_get_by_nickname(info->nickname, 0);
- if (!r1 || !r2 || r1 != r2)
- continue;
- tor_assert(edge_conn->socks_request);
- if (edge_conn->chosen_exit_optional) {
- log_info(LD_APP, "Giving up on enclave exit '%s' for destination %s.",
- safe_str(edge_conn->chosen_exit_name),
- escaped_safe_str(edge_conn->socks_request->address));
- edge_conn->chosen_exit_optional = 0;
- tor_free(edge_conn->chosen_exit_name); /* clears it */
- /* if this port is dangerous, warn or reject it now that we don't
- * think it'll be using an enclave. */
- consider_plaintext_ports(edge_conn, edge_conn->socks_request->port);
- }
- if (edge_conn->chosen_exit_retries) {
- if (--edge_conn->chosen_exit_retries == 0) { /* give up! */
- clear_trackexithost_mappings(edge_conn->chosen_exit_name);
- tor_free(edge_conn->chosen_exit_name); /* clears it */
- /* if this port is dangerous, warn or reject it now that we don't
- * think it'll be using an enclave. */
- consider_plaintext_ports(edge_conn, edge_conn->socks_request->port);
- }
- }
- } SMARTLIST_FOREACH_END(conn);
- }
- /** The AP connection <b>conn</b> has just failed while attaching or
- * sending a BEGIN or resolving on <b>circ</b>, but another circuit
- * might work. Detach the circuit, and either reattach it, launch a
- * new circuit, tell the controller, or give up as a appropriate.
- *
- * Returns -1 on err, 1 on success, 0 on not-yet-sure.
- */
- int
- connection_ap_detach_retriable(edge_connection_t *conn, origin_circuit_t *circ,
- int reason)
- {
- control_event_stream_status(conn, STREAM_EVENT_FAILED_RETRIABLE, reason);
- conn->_base.timestamp_lastread = time(NULL);
- if (!get_options()->LeaveStreamsUnattached || conn->use_begindir) {
- /* If we're attaching streams ourself, or if this connection is
- * a tunneled directory connection, then just attach it. */
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
- circuit_detach_stream(TO_CIRCUIT(circ),conn);
- return connection_ap_handshake_attach_circuit(conn);
- } else {
- conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
- circuit_detach_stream(TO_CIRCUIT(circ),conn);
- return 0;
- }
- }
- /** A client-side struct to remember requests to rewrite addresses
- * to new addresses. These structs are stored in the hash table
- * "addressmap" below.
- *
- * There are 5 ways to set an address mapping:
- * - A MapAddress command from the controller [permanent]
- * - An AddressMap directive in the torrc [permanent]
- * - When a TrackHostExits torrc directive is triggered [temporary]
- * - When a DNS resolve succeeds [temporary]
- * - When a DNS resolve fails [temporary]
- *
- * When an addressmap request is made but one is already registered,
- * the new one is replaced only if the currently registered one has
- * no "new_address" (that is, it's in the process of DNS resolve),
- * or if the new one is permanent (expires==0 or 1).
- *
- * (We overload the 'expires' field, using "0" for mappings set via
- * the configuration file, "1" for mappings set from the control
- * interface, and other values for DNS and TrackHostExit mappings that can
- * expire.)
- */
- typedef struct {
- char *new_address;
- time_t expires;
- addressmap_entry_source_t source:3;
- short num_resolve_failures;
- } addressmap_entry_t;
- /** Entry for mapping addresses to which virtual address we mapped them to. */
- typedef struct {
- char *ipv4_address;
- char *hostname_address;
- } virtaddress_entry_t;
- /** A hash table to store client-side address rewrite instructions. */
- static strmap_t *addressmap=NULL;
- /**
- * Table mapping addresses to which virtual address, if any, we
- * assigned them to.
- *
- * We maintain the following invariant: if [A,B] is in
- * virtaddress_reversemap, then B must be a virtual address, and [A,B]
- * must be in addressmap. We do not require that the converse hold:
- * if it fails, then we could end up mapping two virtual addresses to
- * the same address, which is no disaster.
- **/
- static strmap_t *virtaddress_reversemap=NULL;
- /** Initialize addressmap. */
- void
- addressmap_init(void)
- {
- addressmap = strmap_new();
- virtaddress_reversemap = strmap_new();
- }
- /** Free the memory associated with the addressmap entry <b>_ent</b>. */
- static void
- addressmap_ent_free(void *_ent)
- {
- addressmap_entry_t *ent = _ent;
- tor_free(ent->new_address);
- tor_free(ent);
- }
- /** Free storage held by a virtaddress_entry_t* entry in <b>ent</b>. */
- static void
- addressmap_virtaddress_ent_free(void *_ent)
- {
- virtaddress_entry_t *ent = _ent;
- tor_free(ent->ipv4_address);
- tor_free(ent->hostname_address);
- tor_free(ent);
- }
- /** Free storage held by a virtaddress_entry_t* entry in <b>ent</b>. */
- static void
- addressmap_virtaddress_remove(const char *address, addressmap_entry_t *ent)
- {
- if (ent && ent->new_address &&
- address_is_in_virtual_range(ent->new_address)) {
- virtaddress_entry_t *ve =
- strmap_get(virtaddress_reversemap, ent->new_address);
- /*log_fn(LOG_NOTICE,"remove reverse mapping for %s",ent->new_address);*/
- if (ve) {
- if (!strcmp(address, ve->ipv4_address))
- tor_free(ve->ipv4_address);
- if (!strcmp(address, ve->hostname_address))
- tor_free(ve->hostname_address);
- if (!ve->ipv4_address && !ve->hostname_address) {
- tor_free(ve);
- strmap_remove(virtaddress_reversemap, ent->new_address);
- }
- }
- }
- }
- /** Remove <b>ent</b> (which must be mapped to by <b>address</b>) from the
- * client address maps. */
- static void
- addressmap_ent_remove(const char *address, addressmap_entry_t *ent)
- {
- addressmap_virtaddress_remove(address, ent);
- addressmap_ent_free(ent);
- }
- /** Unregister all TrackHostExits mappings from any address to
- * *.exitname.exit. */
- static void
- clear_trackexithost_mappings(const char *exitname)
- {
- char *suffix;
- size_t suffix_len;
- if (!addressmap || !exitname)
- return;
- suffix_len = strlen(exitname) + 16;
- suffix = tor_malloc(suffix_len);
- tor_snprintf(suffix, suffix_len, ".%s.exit", exitname);
- tor_strlower(suffix);
- STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) {
- if (ent->source == ADDRMAPSRC_TRACKEXIT && !strcmpend(address, suffix)) {
- addressmap_ent_remove(address, ent);
- MAP_DEL_CURRENT(address);
- }
- } STRMAP_FOREACH_END;
- tor_free(suffix);
- }
- /** Remove all entries from the addressmap that were set via the
- * configuration file or the command line. */
- void
- addressmap_clear_configured(void)
- {
- addressmap_get_mappings(NULL, 0, 0, 0);
- }
- /** Remove all entries from the addressmap that are set to expire, ever. */
- void
- addressmap_clear_transient(void)
- {
- addressmap_get_mappings(NULL, 2, TIME_MAX, 0);
- }
- /** Clean out entries from the addressmap cache that were
- * added long enough ago that they are no longer valid.
- */
- void
- addressmap_clean(time_t now)
- {
- addressmap_get_mappings(NULL, 2, now, 0);
- }
- /** Free all the elements in the addressmap, and free the addressmap
- * itself. */
- void
- addressmap_free_all(void)
- {
- if (addressmap) {
- strmap_free(addressmap, addressmap_ent_free);
- addressmap = NULL;
- }
- if (virtaddress_reversemap) {
- strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free);
- virtaddress_reversemap = NULL;
- }
- }
- /** Look at address, and rewrite it until it doesn't want any
- * more rewrites; but don't get into an infinite loop.
- * Don't write more than maxlen chars into address. Return true if the
- * address changed; false otherwise. Set *<b>expires_out</b> to the
- * expiry time of the result, or to <b>time_max</b> if the result does
- * not expire.
- */
- int
- addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out)
- {
- addressmap_entry_t *ent;
- int rewrites;
- char *cp;
- time_t expires = TIME_MAX;
- for (rewrites = 0; rewrites < 16; rewrites++) {
- ent = strmap_get(addressmap, address);
- if (!ent || !ent->new_address) {
- if (expires_out)
- *expires_out = expires;
- return (rewrites > 0); /* done, no rewrite needed */
- }
- cp = tor_strdup(escaped_safe_str(ent->new_address));
- log_info(LD_APP, "Addressmap: rewriting %s to %s",
- escaped_safe_str(address), cp);
- if (ent->expires > 1 && ent->expires < expires)
- expires = ent->expires;
- tor_free(cp);
- strlcpy(address, ent->new_address, maxlen);
- }
- log_warn(LD_CONFIG,
- "Loop detected: we've rewritten %s 16 times! Using it as-is.",
- escaped_safe_str(address));
- /* it's fine to rewrite a rewrite, but don't loop forever */
- if (expires_out)
- *expires_out = TIME_MAX;
- return 1;
- }
- /** If we have a cached reverse DNS entry for the address stored in the
- * <b>maxlen</b>-byte buffer <b>address</b> (typically, a dotted quad) then
- * rewrite to the cached value and return 1. Otherwise return 0. Set
- * *<b>expires_out</b> to the expiry time of the result, or to <b>time_max</b>
- * if the result does not expire. */
- static int
- addressmap_rewrite_reverse(char *address, size_t maxlen, time_t *expires_out)
- {
- size_t len = maxlen + 16;
- char *s = tor_malloc(len), *cp;
- addressmap_entry_t *ent;
- int r = 0;
- tor_snprintf(s, len, "REVERSE[%s]", address);
- ent = strmap_get(addressmap, s);
- if (ent) {
- cp = tor_strdup(escaped_safe_str(ent->new_address));
- log_info(LD_APP, "Rewrote reverse lookup %s -> %s",
- escaped_safe_str(s), cp);
- tor_free(cp);
- strlcpy(address, ent->new_address, maxlen);
- r = 1;
- }
- if (expires_out)
- *expires_out = (ent && ent->expires > 1) ? ent->expires : TIME_MAX;
- tor_free(s);
- return r;
- }
- /** Return 1 if <b>address</b> is already registered, else return 0. If address
- * is already registered, and <b>update_expires</b> is non-zero, then update
- * the expiry time on the mapping with update_expires if it is a
- * mapping created by TrackHostExits. */
- int
- addressmap_have_mapping(const char *address, int update_expiry)
- {
- addressmap_entry_t *ent;
- if (!(ent=strmap_get_lc(addressmap, address)))
- return 0;
- if (update_expiry && ent->source==ADDRMAPSRC_TRACKEXIT)
- ent->expires=time(NULL) + update_expiry;
- return 1;
- }
- /** Register a request to map <b>address</b> to <b>new_address</b>,
- * which will expire on <b>expires</b> (or 0 if never expires from
- * config file, 1 if never expires from controller, 2 if never expires
- * (virtual address mapping) from the controller.)
- *
- * <b>new_address</b> should be a newly dup'ed string, which we'll use or
- * free as appropriate. We will leave address alone.
- *
- * If <b>new_address</b> is NULL, or equal to <b>address</b>, remove
- * any mappings that exist from <b>address</b>.
- */
- void
- addressmap_register(const char *address, char *new_address, time_t expires,
- addressmap_entry_source_t source)
- {
- addressmap_entry_t *ent;
- ent = strmap_get(addressmap, address);
- if (!new_address || !strcasecmp(address,new_address)) {
- /* Remove the mapping, if any. */
- tor_free(new_address);
- if (ent) {
- addressmap_ent_remove(address,ent);
- strmap_remove(addressmap, address);
- }
- return;
- }
- if (!ent) { /* make a new one and register it */
- ent = tor_malloc_zero(sizeof(addressmap_entry_t));
- strmap_set(addressmap, address, ent);
- } else if (ent->new_address) { /* we need to clean up the old mapping. */
- if (expires > 1) {
- log_info(LD_APP,"Temporary addressmap ('%s' to '%s') not performed, "
- "since it's already mapped to '%s'",
- safe_str(address), safe_str(new_address), safe_str(ent->new_address));
- tor_free(new_address);
- return;
- }
- if (address_is_in_virtual_range(ent->new_address) &&
- expires != 2) {
- /* XXX This isn't the perfect test; we want to avoid removing
- * mappings set from the control interface _as virtual mapping */
- addressmap_virtaddress_remove(address, ent);
- }
- tor_free(ent->new_address);
- } /* else { we have an in-progress resolve with no mapping. } */
- ent->new_address = new_address;
- ent->expires = expires==2 ? 1 : expires;
- ent->num_resolve_failures = 0;
- ent->source = source;
- log_info(LD_CONFIG, "Addressmap: (re)mapped '%s' to '%s'",
- safe_str(address), safe_str(ent->new_address));
- control_event_address_mapped(address, ent->new_address, expires, NULL);
- }
- /** An attempt to resolve <b>address</b> failed at some OR.
- * Increment the number of resolve failures we have on record
- * for it, and then return that number.
- */
- int
- client_dns_incr_failures(const char *address)
- {
- addressmap_entry_t *ent = strmap_get(addressmap, address);
- if (!ent) {
- ent = tor_malloc_zero(sizeof(addressmap_entry_t));
- ent->expires = time(NULL) + MAX_DNS_ENTRY_AGE;
- strmap_set(addressmap,address,ent);
- }
- if (ent->num_resolve_failures < SHORT_MAX)
- ++ent->num_resolve_failures; /* don't overflow */
- log_info(LD_APP, "Address %s now has %d resolve failures.",
- safe_str(address), ent->num_resolve_failures);
- return ent->num_resolve_failures;
- }
- /** If <b>address</b> is in the client DNS addressmap, reset
- * the number of resolve failures we have on record for it.
- * This is used when we fail a stream because it won't resolve:
- * otherwise future attempts on that address will only try once.
- */
- void
- client_dns_clear_failures(const char *address)
- {
- addressmap_entry_t *ent = strmap_get(addressmap, address);
- if (ent)
- ent->num_resolve_failures = 0;
- }
- /** Record the fact that <b>address</b> resolved to <b>name</b>.
- * We can now use this in subsequent streams via addressmap_rewrite()
- * so we can more correctly choose an exit that will allow <b>address</b>.
- *
- * If <b>exitname</b> is defined, then append the addresses with
- * ".exitname.exit" before registering the mapping.
- *
- * If <b>ttl</b> is nonnegative, the mapping will be valid for
- * <b>ttl</b>seconds; otherwise, we use the default.
- */
- static void
- client_dns_set_addressmap_impl(const char *address, const char *name,
- const char *exitname,
- int ttl)
- {
- /* <address>.<hex or nickname>.exit or just <address>