Back to index

tor  0.2.3.19-rc
Defines | Functions
circuituse.h File Reference

Header file for circuituse.c. More...

This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Defines

#define CIRCLAUNCH_ONEHOP_TUNNEL   (1<<0)
 Flag to set when a circuit should have only a single hop.
#define CIRCLAUNCH_NEED_UPTIME   (1<<1)
 Flag to set when a circuit needs to be built of high-uptime nodes.
#define CIRCLAUNCH_NEED_CAPACITY   (1<<2)
 Flag to set when a circuit needs to be built of high-capacity nodes.
#define CIRCLAUNCH_IS_INTERNAL   (1<<3)
 Flag to set when the last hop of a circuit doesn't need to be an exit node.

Functions

void circuit_expire_building (void)
 Close all circuits that start at us, aren't open, and were born at least CircuitBuildTimeout seconds ago.
void circuit_remove_handled_ports (smartlist_t *needed_ports)
 Remove any elements in needed_ports that are handled by an open or in-progress circuit.
int circuit_stream_is_being_handled (entry_connection_t *conn, uint16_t port, int min)
 Return 1 if at least min general-purpose non-internal circuits will have an acceptable exit node for exit stream conn if it is defined, else for "*:port".
void circuit_build_needed_circs (time_t now)
 This function is called once a second, if router_have_min_dir_info() is true.
void circuit_detach_stream (circuit_t *circ, edge_connection_t *conn)
 If the stream conn is a member of any of the linked lists of circ, then remove it from the list.
void circuit_expire_old_circuits_serverside (time_t now)
 Find each non-origin circuit that has been unused for too long, has no streams on it, used a create_fast, and ends here: mark it for close.
void reset_bandwidth_test (void)
 Reset have_performed_bandwidth_test, so we'll start building testing circuits again so we can exercise our bandwidth.
int circuit_enough_testing_circs (void)
 Return 1 if we've already exercised our bandwidth, or if we have fewer than NUM_PARALLEL_TESTING_CIRCS testing circuits established or on the way.
void circuit_has_opened (origin_circuit_t *circ)
 The circuit circ has just become open.
void circuit_try_attaching_streams (origin_circuit_t *circ)
 Called when a circuit becomes ready for streams to be attached to it.
void circuit_build_failed (origin_circuit_t *circ)
 Called whenever a circuit could not be successfully built.
origin_circuit_tcircuit_launch_by_extend_info (uint8_t purpose, extend_info_t *info, int flags)
 Launch a new circuit with purpose purpose and exit node extend_info (or NULL to select a random exit node).
origin_circuit_tcircuit_launch (uint8_t purpose, int flags)
 Launch a new circuit; see circuit_launch_by_extend_info() for details on arguments.
void circuit_reset_failure_count (int timeout)
 Reset the failure count for opening general circuits.
int connection_ap_handshake_attach_chosen_circuit (entry_connection_t *conn, origin_circuit_t *circ, crypt_path_t *cpath)
 Attempt to attach the connection conn to circ, and send a begin or resolve cell as appropriate.
int connection_ap_handshake_attach_circuit (entry_connection_t *conn)
 Try to find a safe live circuit for CONN_TYPE_AP connection conn.
void circuit_change_purpose (circuit_t *circ, uint8_t new_purpose)
 Change circ's purpose to new_purpose.
int hostname_in_track_host_exits (const or_options_t *options, const char *address)
 Return true iff address is matched by one of the entries in TrackHostExits.

Detailed Description

Header file for circuituse.c.

Definition in file circuituse.h.


Define Documentation

#define CIRCLAUNCH_IS_INTERNAL   (1<<3)

Flag to set when the last hop of a circuit doesn't need to be an exit node.

Definition at line 43 of file circuituse.h.

#define CIRCLAUNCH_NEED_CAPACITY   (1<<2)

Flag to set when a circuit needs to be built of high-capacity nodes.

Definition at line 40 of file circuituse.h.

#define CIRCLAUNCH_NEED_UPTIME   (1<<1)

Flag to set when a circuit needs to be built of high-uptime nodes.

Definition at line 38 of file circuituse.h.

#define CIRCLAUNCH_ONEHOP_TUNNEL   (1<<0)

Flag to set when a circuit should have only a single hop.

Definition at line 36 of file circuituse.h.


Function Documentation

Called whenever a circuit could not be successfully built.

Definition at line 1164 of file circuituse.c.

{
  /* we should examine circ and see if it failed because of
   * the last hop or an earlier hop. then use this info below.
   */
  int failed_at_last_hop = 0;
  /* If the last hop isn't open, and the second-to-last is, we failed
   * at the last hop. */
  if (circ->cpath &&
      circ->cpath->prev->state != CPATH_STATE_OPEN &&
      circ->cpath->prev->prev->state == CPATH_STATE_OPEN) {
    failed_at_last_hop = 1;
  }
  if (circ->cpath &&
      circ->cpath->state != CPATH_STATE_OPEN) {
    /* We failed at the first hop. If there's an OR connection
     * to blame, blame it. Also, avoid this relay for a while, and
     * fail any one-hop directory fetches destined for it. */
    const char *n_conn_id = circ->cpath->extend_info->identity_digest;
    int already_marked = 0;
    if (circ->_base.n_conn) {
      or_connection_t *n_conn = circ->_base.n_conn;
      if (n_conn->is_bad_for_new_circs) {
        /* We only want to blame this router when a fresh healthy
         * connection fails. So don't mark this router as newly failed,
         * since maybe this was just an old circuit attempt that's
         * finally timing out now. Also, there's no need to blow away
         * circuits/streams/etc, since the failure of an unhealthy conn
         * doesn't tell us much about whether a healthy conn would
         * succeed. */
        already_marked = 1;
      }
      log_info(LD_OR,
               "Our circuit failed to get a response from the first hop "
               "(%s:%d). I'm going to try to rotate to a better connection.",
               n_conn->_base.address, n_conn->_base.port);
      n_conn->is_bad_for_new_circs = 1;
    } else {
      log_info(LD_OR,
               "Our circuit died before the first hop with no connection");
    }
    if (n_conn_id && !already_marked) {
      entry_guard_register_connect_status(n_conn_id, 0, 1, time(NULL));
      /* if there are any one-hop streams waiting on this circuit, fail
       * them now so they can retry elsewhere. */
      connection_ap_fail_onehop(n_conn_id, circ->build_state);
    }
  }

  switch (circ->_base.purpose) {
    case CIRCUIT_PURPOSE_C_GENERAL:
      /* If we never built the circuit, note it as a failure. */
      circuit_increment_failure_count();
      if (failed_at_last_hop) {
        /* Make sure any streams that demand our last hop as their exit
         * know that it's unlikely to happen. */
        circuit_discard_optional_exit_enclaves(circ->cpath->prev->extend_info);
      }
      break;
    case CIRCUIT_PURPOSE_TESTING:
      circuit_testing_failed(circ, failed_at_last_hop);
      break;
    case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
      /* at Bob, waiting for introductions */
      if (circ->_base.state != CIRCUIT_STATE_OPEN) {
        circuit_increment_failure_count();
      }
      /* no need to care here, because bob will rebuild intro
       * points periodically. */
      break;
    case CIRCUIT_PURPOSE_C_INTRODUCING:
      /* at Alice, connecting to intro point */
      /* Don't increment failure count, since Bob may have picked
       * the introduction point maliciously */
      /* Alice will pick a new intro point when this one dies, if
       * the stream in question still cares. No need to act here. */
      break;
    case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
      /* at Alice, waiting for Bob */
      circuit_increment_failure_count();
      /* Alice will pick a new rend point when this one dies, if
       * the stream in question still cares. No need to act here. */
      break;
    case CIRCUIT_PURPOSE_S_CONNECT_REND:
      /* at Bob, connecting to rend point */
      /* Don't increment failure count, since Alice may have picked
       * the rendezvous point maliciously */
      log_info(LD_REND,
               "Couldn't connect to Alice's chosen rend point %s "
               "(%s hop failed).",
               escaped(build_state_get_exit_nickname(circ->build_state)),
               failed_at_last_hop?"last":"non-last");
      rend_service_relaunch_rendezvous(circ);
      break;
    /* default:
     * This won't happen in normal operation, but might happen if the
     * controller did it. Just let it slide. */
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

void circuit_build_needed_circs ( time_t  now)

This function is called once a second, if router_have_min_dir_info() is true.

Its job is to make sure all services we offer have enough circuits available. Some services just want enough circuits for current tasks, whereas others want a minimum set of idle circuits hanging around.

Definition at line 768 of file circuituse.c.

{
  static time_t time_to_new_circuit = 0;
  const or_options_t *options = get_options();

  /* launch a new circ for any pending streams that need one */
  connection_ap_attach_pending();

  /* make sure any hidden services have enough intro points */
  rend_services_introduce();

  if (time_to_new_circuit < now) {
    circuit_reset_failure_count(1);
    time_to_new_circuit = now + options->NewCircuitPeriod;
    if (proxy_mode(get_options()))
      addressmap_clean(now);
    circuit_expire_old_circuits_clientside();

#if 0 /* disable for now, until predict-and-launch-new can cull leftovers */
    circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL);
    if (get_options()->RunTesting &&
        circ &&
        circ->timestamp_created.tv_sec + TESTING_CIRCUIT_INTERVAL < now) {
      log_fn(LOG_INFO,"Creating a new testing circuit.");
      circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, 0);
    }
#endif
  }
  if (!options->DisablePredictedCircuits)
    circuit_predict_and_launch_new();
}

Here is the call graph for this function:

Here is the caller graph for this function:

void circuit_change_purpose ( circuit_t circ,
uint8_t  new_purpose 
)

Change circ's purpose to new_purpose.

Definition at line 2047 of file circuituse.c.

{
  uint8_t old_purpose;
  /* Don't allow an OR circ to become an origin circ or vice versa. */
  tor_assert(!!(CIRCUIT_IS_ORIGIN(circ)) ==
             !!(CIRCUIT_PURPOSE_IS_ORIGIN(new_purpose)));

  if (circ->purpose == new_purpose) return;

  if (CIRCUIT_IS_ORIGIN(circ)) {
    char old_purpose_desc[80] = "";

    strncpy(old_purpose_desc, circuit_purpose_to_string(circ->purpose), 80-1);
    old_purpose_desc[80-1] = '\0';

    log_debug(LD_CIRC,
              "changing purpose of origin circ %d "
              "from \"%s\" (%d) to \"%s\" (%d)",
              TO_ORIGIN_CIRCUIT(circ)->global_identifier,
              old_purpose_desc,
              circ->purpose,
              circuit_purpose_to_string(new_purpose),
              new_purpose);
  }

  old_purpose = circ->purpose;
  circ->purpose = new_purpose;

  if (CIRCUIT_IS_ORIGIN(circ)) {
    control_event_circuit_purpose_changed(TO_ORIGIN_CIRCUIT(circ),
                                          old_purpose);
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

void circuit_detach_stream ( circuit_t circ,
edge_connection_t conn 
)

If the stream conn is a member of any of the linked lists of circ, then remove it from the list.

Definition at line 804 of file circuituse.c.

{
  edge_connection_t *prevconn;

  tor_assert(circ);
  tor_assert(conn);

  if (conn->_base.type == CONN_TYPE_AP) {
    entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
    entry_conn->may_use_optimistic_data = 0;
  }
  conn->cpath_layer = NULL; /* don't keep a stale pointer */
  conn->on_circuit = NULL;

  if (CIRCUIT_IS_ORIGIN(circ)) {
    origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
    if (conn == origin_circ->p_streams) {
      origin_circ->p_streams = conn->next_stream;
      return;
    }

    for (prevconn = origin_circ->p_streams;
         prevconn && prevconn->next_stream && prevconn->next_stream != conn;
         prevconn = prevconn->next_stream)
      ;
    if (prevconn && prevconn->next_stream) {
      prevconn->next_stream = conn->next_stream;
      return;
    }
  } else {
    or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
    if (conn == or_circ->n_streams) {
      or_circ->n_streams = conn->next_stream;
      return;
    }
    if (conn == or_circ->resolving_streams) {
      or_circ->resolving_streams = conn->next_stream;
      return;
    }

    for (prevconn = or_circ->n_streams;
         prevconn && prevconn->next_stream && prevconn->next_stream != conn;
         prevconn = prevconn->next_stream)
      ;
    if (prevconn && prevconn->next_stream) {
      prevconn->next_stream = conn->next_stream;
      return;
    }

    for (prevconn = or_circ->resolving_streams;
         prevconn && prevconn->next_stream && prevconn->next_stream != conn;
         prevconn = prevconn->next_stream)
      ;
    if (prevconn && prevconn->next_stream) {
      prevconn->next_stream = conn->next_stream;
      return;
    }
  }

  log_warn(LD_BUG,"Edge connection not in circuit's list.");
  /* Don't give an error here; it's harmless. */
  tor_fragile_assert();
}

Here is the call graph for this function:

Here is the caller graph for this function:

Return 1 if we've already exercised our bandwidth, or if we have fewer than NUM_PARALLEL_TESTING_CIRCS testing circuits established or on the way.

Else return 0.

Definition at line 1012 of file circuituse.c.

{
  circuit_t *circ;
  int num = 0;

  if (have_performed_bandwidth_test)
    return 1;

  for (circ = global_circuitlist; circ; circ = circ->next) {
    if (!circ->marked_for_close && CIRCUIT_IS_ORIGIN(circ) &&
        circ->purpose == CIRCUIT_PURPOSE_TESTING &&
        circ->state == CIRCUIT_STATE_OPEN)
      num++;
  }
  return num >= NUM_PARALLEL_TESTING_CIRCS;
}

Here is the caller graph for this function:

void circuit_expire_building ( void  )

Close all circuits that start at us, aren't open, and were born at least CircuitBuildTimeout seconds ago.

Definition at line 352 of file circuituse.c.

{
  circuit_t *victim, *next_circ = global_circuitlist;
  /* circ_times.timeout_ms and circ_times.close_ms are from
   * circuit_build_times_get_initial_timeout() if we haven't computed
   * custom timeouts yet */
  struct timeval general_cutoff, begindir_cutoff, fourhop_cutoff,
    cannibalize_cutoff, close_cutoff, extremely_old_cutoff,
    hs_extremely_old_cutoff;
  const or_options_t *options = get_options();
  struct timeval now;
  cpath_build_state_t *build_state;

  tor_gettimeofday(&now);
#define SET_CUTOFF(target, msec) do {                       \
    long ms = tor_lround(msec);                             \
    struct timeval diff;                                    \
    diff.tv_sec = ms / 1000;                                \
    diff.tv_usec = (int)((ms % 1000) * 1000);               \
    timersub(&now, &diff, &target);                         \
  } while (0)

  SET_CUTOFF(general_cutoff, circ_times.timeout_ms);
  SET_CUTOFF(begindir_cutoff, circ_times.timeout_ms / 2.0);
  SET_CUTOFF(fourhop_cutoff, circ_times.timeout_ms * (4/3.0));
  SET_CUTOFF(cannibalize_cutoff, circ_times.timeout_ms / 2.0);
  SET_CUTOFF(close_cutoff, circ_times.close_ms);
  SET_CUTOFF(extremely_old_cutoff, circ_times.close_ms*2 + 1000);

  SET_CUTOFF(hs_extremely_old_cutoff,
             MAX(circ_times.close_ms*2 + 1000,
                 options->SocksTimeout * 1000));

  while (next_circ) {
    struct timeval cutoff;
    victim = next_circ;
    next_circ = next_circ->next;
    if (!CIRCUIT_IS_ORIGIN(victim) || /* didn't originate here */
        victim->marked_for_close) /* don't mess with marked circs */
      continue;

    build_state = TO_ORIGIN_CIRCUIT(victim)->build_state;
    if (build_state && build_state->onehop_tunnel)
      cutoff = begindir_cutoff;
    else if (build_state && build_state->desired_path_len == 4
             && !TO_ORIGIN_CIRCUIT(victim)->has_opened)
      cutoff = fourhop_cutoff;
    else if (TO_ORIGIN_CIRCUIT(victim)->has_opened)
      cutoff = cannibalize_cutoff;
    else if (victim->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT)
      cutoff = close_cutoff;
    else
      cutoff = general_cutoff;

    if (TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out)
      cutoff = hs_extremely_old_cutoff;

    if (timercmp(&victim->timestamp_created, &cutoff, >))
      continue; /* it's still young, leave it alone */

#if 0
    /* some debug logs, to help track bugs */
    if (victim->purpose >= CIRCUIT_PURPOSE_C_INTRODUCING &&
        victim->purpose <= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) {
      if (!victim->timestamp_dirty)
        log_fn(LOG_DEBUG,"Considering %sopen purpose %d to %s (circid %d)."
               "(clean).",
               victim->state == CIRCUIT_STATE_OPEN ? "" : "non",
               victim->purpose, victim->build_state->chosen_exit_name,
               victim->n_circ_id);
      else
        log_fn(LOG_DEBUG,"Considering %sopen purpose %d to %s (circid %d). "
               "%d secs since dirty.",
               victim->state == CIRCUIT_STATE_OPEN ? "" : "non",
               victim->purpose, victim->build_state->chosen_exit_name,
               victim->n_circ_id,
               (int)(now - victim->timestamp_dirty));
    }
#endif

    /* if circ is !open, or if it's open but purpose is a non-finished
     * intro or rend, then mark it for close */
    if (victim->state == CIRCUIT_STATE_OPEN) {
      switch (victim->purpose) {
        default: /* most open circuits can be left alone. */
          continue; /* yes, continue inside a switch refers to the nearest
                     * enclosing loop. C is smart. */
        case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
        case CIRCUIT_PURPOSE_C_INTRODUCING:
        case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
          break; /* too old, need to die */
        case CIRCUIT_PURPOSE_C_REND_READY:
          /* it's a rend_ready circ -- has it already picked a query? */
          /* c_rend_ready circs measure age since timestamp_dirty,
           * because that's set when they switch purposes
           */
          if (TO_ORIGIN_CIRCUIT(victim)->rend_data ||
              victim->timestamp_dirty > cutoff.tv_sec)
            continue;
          break;
        case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
        case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
          /* rend and intro circs become dirty each time they
           * make an introduction attempt. so timestamp_dirty
           * will reflect the time since the last attempt.
           */
          if (victim->timestamp_dirty > cutoff.tv_sec)
            continue;
          break;
      }
    } else { /* circuit not open, consider recording failure as timeout */
      int first_hop_succeeded = TO_ORIGIN_CIRCUIT(victim)->cpath &&
            TO_ORIGIN_CIRCUIT(victim)->cpath->state == CPATH_STATE_OPEN;

      if (TO_ORIGIN_CIRCUIT(victim)->p_streams != NULL) {
        log_warn(LD_BUG, "Circuit %d (purpose %d, %s) has timed out, "
                 "yet has attached streams!",
                 TO_ORIGIN_CIRCUIT(victim)->global_identifier,
                 victim->purpose,
                 circuit_purpose_to_string(victim->purpose));
        tor_fragile_assert();
        continue;
      }

      if (circuit_timeout_want_to_count_circ(TO_ORIGIN_CIRCUIT(victim)) &&
          circuit_build_times_enough_to_compute(&circ_times)) {
        /* Circuits are allowed to last longer for measurement.
         * Switch their purpose and wait. */
        if (victim->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
          control_event_circuit_status(TO_ORIGIN_CIRCUIT(victim),
                                       CIRC_EVENT_FAILED,
                                       END_CIRC_REASON_TIMEOUT);
          circuit_change_purpose(victim, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT);
          /* Record this failure to check for too many timeouts
           * in a row. This function does not record a time value yet
           * (we do that later); it only counts the fact that we did
           * have a timeout. */
          circuit_build_times_count_timeout(&circ_times,
                                            first_hop_succeeded);
          continue;
        }

        /*
         * If the circuit build time is much greater than we would have cut
         * it off at, we probably had a suspend event along this codepath,
         * and we should discard the value.
         */
        if (timercmp(&victim->timestamp_created, &extremely_old_cutoff, <)) {
          log_notice(LD_CIRC,
                     "Extremely large value for circuit build timeout: %lds. "
                     "Assuming clock jump. Purpose %d (%s)",
                     (long)(now.tv_sec - victim->timestamp_created.tv_sec),
                     victim->purpose,
                     circuit_purpose_to_string(victim->purpose));
        } else if (circuit_build_times_count_close(&circ_times,
                                         first_hop_succeeded,
                                         victim->timestamp_created.tv_sec)) {
          circuit_build_times_set_timeout(&circ_times);
        }
      }
    }

    /* If this is a hidden service client circuit which is far enough
     * along in connecting to its destination, and we haven't already
     * flagged it as 'timed out', and the user has not told us to
     * close such circs immediately on timeout, flag it as 'timed out'
     * so we'll launch another intro or rend circ, but don't mark it
     * for close yet.
     *
     * (Circs flagged as 'timed out' are given a much longer timeout
     * period above, so we won't close them in the next call to
     * circuit_expire_building.) */
    if (!(options->CloseHSClientCircuitsImmediatelyOnTimeout) &&
        !(TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out)) {
      switch (victim->purpose) {
      case CIRCUIT_PURPOSE_C_REND_READY:
        /* We only want to spare a rend circ if it has been specified in
         * an INTRODUCE1 cell sent to a hidden service.  A circ's
         * pending_final_cpath field is non-NULL iff it is a rend circ
         * and we have tried to send an INTRODUCE1 cell specifying it.
         * Thus, if the pending_final_cpath field *is* NULL, then we
         * want to not spare it. */
        if (TO_ORIGIN_CIRCUIT(victim)->build_state->pending_final_cpath ==
            NULL)
          break;
      case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
      case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
        /* If we have reached this line, we want to spare the circ for now. */
        log_info(LD_CIRC,"Marking circ %d (state %d:%s, purpose %d) "
                 "as timed-out HS circ",
                 victim->n_circ_id,
                 victim->state, circuit_state_to_string(victim->state),
                 victim->purpose);
        TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out = 1;
        continue;
      default:
        break;
      }
    }

    /* If this is a service-side rendezvous circuit which is far
     * enough along in connecting to its destination, consider sparing
     * it. */
    if (!(options->CloseHSServiceRendCircuitsImmediatelyOnTimeout) &&
        !(TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out) &&
        victim->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) {
      log_info(LD_CIRC,"Marking circ %d (state %d:%s, purpose %d) "
               "as timed-out HS circ; relaunching rendezvous attempt.",
               victim->n_circ_id,
               victim->state, circuit_state_to_string(victim->state),
               victim->purpose);
      TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out = 1;
      rend_service_relaunch_rendezvous(TO_ORIGIN_CIRCUIT(victim));
      continue;
    }

    if (victim->n_conn)
      log_info(LD_CIRC,"Abandoning circ %s:%d:%d (state %d:%s, purpose %d)",
               victim->n_conn->_base.address, victim->n_conn->_base.port,
               victim->n_circ_id,
               victim->state, circuit_state_to_string(victim->state),
               victim->purpose);
    else
      log_info(LD_CIRC,"Abandoning circ %d (state %d:%s, purpose %d)",
               victim->n_circ_id, victim->state,
               circuit_state_to_string(victim->state), victim->purpose);

    circuit_log_path(LOG_INFO,LD_CIRC,TO_ORIGIN_CIRCUIT(victim));
    if (victim->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT)
      circuit_mark_for_close(victim, END_CIRC_REASON_MEASUREMENT_EXPIRED);
    else
      circuit_mark_for_close(victim, END_CIRC_REASON_TIMEOUT);
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

Find each non-origin circuit that has been unused for too long, has no streams on it, used a create_fast, and ends here: mark it for close.

Definition at line 967 of file circuituse.c.

{
  circuit_t *circ;
  or_circuit_t *or_circ;
  time_t cutoff = now - IDLE_ONE_HOP_CIRC_TIMEOUT;

  for (circ = global_circuitlist; circ; circ = circ->next) {
    if (circ->marked_for_close || CIRCUIT_IS_ORIGIN(circ))
      continue;
    or_circ = TO_OR_CIRCUIT(circ);
    /* If the circuit has been idle for too long, and there are no streams
     * on it, and it ends here, and it used a create_fast, mark it for close.
     */
    if (or_circ->is_first_hop && !circ->n_conn &&
        !or_circ->n_streams && !or_circ->resolving_streams &&
        or_circ->p_conn &&
        or_circ->p_conn->timestamp_last_added_nonpadding <= cutoff) {
      log_info(LD_CIRC, "Closing circ_id %d (empty %d secs ago)",
               or_circ->p_circ_id,
               (int)(now - or_circ->p_conn->timestamp_last_added_nonpadding));
      circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
    }
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

The circuit circ has just become open.

Take the next step: for rendezvous circuits, we pass circ to the appropriate function in rendclient or rendservice. For general circuits, we call connection_ap_attach_pending, which looks for pending streams that could use circ.

Definition at line 1075 of file circuituse.c.

{
  control_event_circuit_status(circ, CIRC_EVENT_BUILT, 0);

  /* Remember that this circuit has finished building. Now if we start
   * it building again later (e.g. by extending it), we will know not
   * to consider its build time. */
  circ->has_opened = 1;

  switch (TO_CIRCUIT(circ)->purpose) {
    case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
      rend_client_rendcirc_has_opened(circ);
      /* Start building an intro circ if we don't have one yet. */
      connection_ap_attach_pending();
      /* This isn't a call to circuit_try_attaching_streams because a
       * circuit in _C_ESTABLISH_REND state isn't connected to its
       * hidden service yet, thus we can't attach streams to it yet,
       * thus circuit_try_attaching_streams would always clear the
       * circuit's isolation state.  circuit_try_attaching_streams is
       * called later, when the rend circ enters _C_REND_JOINED
       * state. */
      break;
    case CIRCUIT_PURPOSE_C_INTRODUCING:
      rend_client_introcirc_has_opened(circ);
      break;
    case CIRCUIT_PURPOSE_C_GENERAL:
      /* Tell any AP connections that have been waiting for a new
       * circuit that one is ready. */
      circuit_try_attaching_streams(circ);
      break;
    case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
      /* at Bob, waiting for introductions */
      rend_service_intro_has_opened(circ);
      break;
    case CIRCUIT_PURPOSE_S_CONNECT_REND:
      /* at Bob, connecting to rend point */
      rend_service_rendezvous_has_opened(circ);
      break;
    case CIRCUIT_PURPOSE_TESTING:
      circuit_testing_opened(circ);
      break;
    /* default:
     * This won't happen in normal operation, but might happen if the
     * controller did it. Just let it slide. */
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

origin_circuit_t* circuit_launch ( uint8_t  purpose,
int  flags 
)

Launch a new circuit; see circuit_launch_by_extend_info() for details on arguments.

Definition at line 1279 of file circuituse.c.

{
  return circuit_launch_by_extend_info(purpose, NULL, flags);
}

Here is the call graph for this function:

Here is the caller graph for this function:

origin_circuit_t* circuit_launch_by_extend_info ( uint8_t  purpose,
extend_info_t extend_info,
int  flags 
)

Launch a new circuit with purpose purpose and exit node extend_info (or NULL to select a random exit node).

If flags contains CIRCLAUNCH_NEED_UPTIME, choose among routers with high uptime. If CIRCLAUNCH_NEED_CAPACITY is set, choose among routers with high bandwidth. If CIRCLAUNCH_IS_INTERNAL is true, the last hop need not be an exit node. If CIRCLAUNCH_ONEHOP_TUNNEL is set, the circuit will have only one hop. Return the newly allocated circuit on success, or NULL on failure.

Definition at line 1292 of file circuituse.c.

{
  origin_circuit_t *circ;
  int onehop_tunnel = (flags & CIRCLAUNCH_ONEHOP_TUNNEL) != 0;

  if (!onehop_tunnel && !router_have_minimum_dir_info()) {
    log_debug(LD_CIRC,"Haven't fetched enough directory info yet; canceling "
              "circuit launch.");
    return NULL;
  }

  if ((extend_info || purpose != CIRCUIT_PURPOSE_C_GENERAL) &&
      purpose != CIRCUIT_PURPOSE_TESTING && !onehop_tunnel) {
    /* see if there are appropriate circs available to cannibalize. */
    /* XXX if we're planning to add a hop, perhaps we want to look for
     * internal circs rather than exit circs? -RD */
    circ = circuit_find_to_cannibalize(purpose, extend_info, flags);
    if (circ) {
      uint8_t old_purpose = circ->_base.purpose;
      struct timeval old_timestamp_created;

      log_info(LD_CIRC,"Cannibalizing circ '%s' for purpose %d (%s)",
               build_state_get_exit_nickname(circ->build_state), purpose,
               circuit_purpose_to_string(purpose));

      circuit_change_purpose(TO_CIRCUIT(circ), purpose);
      /* reset the birth date of this circ, else expire_building
       * will see it and think it's been trying to build since it
       * began. */
      tor_gettimeofday(&circ->_base.timestamp_created);

      control_event_circuit_cannibalized(circ, old_purpose,
                                         &old_timestamp_created);

      switch (purpose) {
        case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
        case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
          /* it's ready right now */
          break;
        case CIRCUIT_PURPOSE_C_INTRODUCING:
        case CIRCUIT_PURPOSE_S_CONNECT_REND:
        case CIRCUIT_PURPOSE_C_GENERAL:
          /* need to add a new hop */
          tor_assert(extend_info);
          if (circuit_extend_to_new_exit(circ, extend_info) < 0)
            return NULL;
          break;
        default:
          log_warn(LD_BUG,
                   "unexpected purpose %d when cannibalizing a circ.",
                   purpose);
          tor_fragile_assert();
          return NULL;
      }
      return circ;
    }
  }

  if (did_circs_fail_last_period &&
      n_circuit_failures > MAX_CIRCUIT_FAILURES) {
    /* too many failed circs in a row. don't try. */
//    log_fn(LOG_INFO,"%d failures so far, not trying.",n_circuit_failures);
    return NULL;
  }

  /* try a circ. if it fails, circuit_mark_for_close will increment
   * n_circuit_failures */
  return circuit_establish_circuit(purpose, extend_info, flags);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void circuit_remove_handled_ports ( smartlist_t needed_ports)

Remove any elements in needed_ports that are handled by an open or in-progress circuit.

Definition at line 591 of file circuituse.c.

{
  int i;
  uint16_t *port;

  for (i = 0; i < smartlist_len(needed_ports); ++i) {
    port = smartlist_get(needed_ports, i);
    tor_assert(*port);
    if (circuit_stream_is_being_handled(NULL, *port,
                                        MIN_CIRCUITS_HANDLING_STREAM)) {
//      log_debug(LD_CIRC,"Port %d is already being handled; removing.", port);
      smartlist_del(needed_ports, i--);
      tor_free(port);
    } else {
      log_debug(LD_CIRC,"Port %d is not handled.", *port);
    }
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

void circuit_reset_failure_count ( int  timeout)

Reset the failure count for opening general circuits.

This means we will try MAX_CIRCUIT_FAILURES times more (if necessary) before stopping again.

Definition at line 1379 of file circuituse.c.

Here is the caller graph for this function:

int circuit_stream_is_being_handled ( entry_connection_t conn,
uint16_t  port,
int  min 
)

Return 1 if at least min general-purpose non-internal circuits will have an acceptable exit node for exit stream conn if it is defined, else for "*:port".

Else return 0.

Definition at line 616 of file circuituse.c.

{
  circuit_t *circ;
  const node_t *exitnode;
  int num=0;
  time_t now = time(NULL);
  int need_uptime = smartlist_string_num_isin(get_options()->LongLivedPorts,
                                   conn ? conn->socks_request->port : port);

  for (circ=global_circuitlist;circ;circ = circ->next) {
    if (CIRCUIT_IS_ORIGIN(circ) &&
        !circ->marked_for_close &&
        circ->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
        (!circ->timestamp_dirty ||
         circ->timestamp_dirty + get_options()->MaxCircuitDirtiness > now)) {
      cpath_build_state_t *build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
      if (build_state->is_internal || build_state->onehop_tunnel)
        continue;

      exitnode = build_state_get_exit_node(build_state);
      if (exitnode && (!need_uptime || build_state->need_uptime)) {
        int ok;
        if (conn) {
          ok = connection_ap_can_use_exit(conn, exitnode);
        } else {
          addr_policy_result_t r;
          r = compare_tor_addr_to_node_policy(NULL, port, exitnode);
          ok = r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED;
        }
        if (ok) {
          if (++num >= min)
            return 1;
        }
      }
    }
  }
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Called when a circuit becomes ready for streams to be attached to it.

Definition at line 1147 of file circuituse.c.

{
  /* Attach streams to this circuit if we can. */
  connection_ap_attach_pending();

  /* The call to circuit_try_clearing_isolation_state here will do
   * nothing and return 0 if we didn't attach any streams to circ
   * above. */
  if (circuit_try_clearing_isolation_state(circ)) {
    /* Maybe *now* we can attach some streams to this circuit. */
    connection_ap_attach_pending();
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

Attempt to attach the connection conn to circ, and send a begin or resolve cell as appropriate.

Return values are as for connection_ap_handshake_attach_circuit. The stream will exit from the hop indicated by cpath, or from the last hop in circ's cpath if cpath is NULL.

Definition at line 1813 of file circuituse.c.

{
  connection_t *base_conn = ENTRY_TO_CONN(conn);
  tor_assert(conn);
  tor_assert(base_conn->state == AP_CONN_STATE_CIRCUIT_WAIT ||
             base_conn->state == AP_CONN_STATE_CONTROLLER_WAIT);
  tor_assert(conn->socks_request);
  tor_assert(circ);
  tor_assert(circ->_base.state == CIRCUIT_STATE_OPEN);

  base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;

  if (!circ->_base.timestamp_dirty)
    circ->_base.timestamp_dirty = time(NULL);

  link_apconn_to_circ(conn, circ, cpath);
  tor_assert(conn->socks_request);
  if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
    if (!conn->use_begindir)
      consider_recording_trackhost(conn, circ);
    if (connection_ap_handshake_send_begin(conn) < 0)
      return -1;
  } else {
    if (connection_ap_handshake_send_resolve(conn) < 0)
      return -1;
  }

  return 1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Try to find a safe live circuit for CONN_TYPE_AP connection conn.

If we don't find one: if conn cannot be handled by any known nodes, warn and return -1 (conn needs to die, and is maybe already marked); else launch new circuit (if necessary) and return 0. Otherwise, associate conn with a safe live circuit, do the right next step, and return 1.

Definition at line 1855 of file circuituse.c.

{
  connection_t *base_conn = ENTRY_TO_CONN(conn);
  int retval;
  int conn_age;
  int want_onehop;

  tor_assert(conn);
  tor_assert(base_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
  tor_assert(conn->socks_request);
  want_onehop = conn->want_onehop;

  conn_age = (int)(time(NULL) - base_conn->timestamp_created);

  if (conn_age >= get_options()->SocksTimeout) {
    int severity = (tor_addr_is_null(&base_conn->addr) && !base_conn->port) ?
      LOG_INFO : LOG_NOTICE;
    log_fn(severity, LD_APP,
           "Tried for %d seconds to get a connection to %s:%d. Giving up.",
           conn_age, safe_str_client(conn->socks_request->address),
           conn->socks_request->port);
    return -1;
  }

  if (!connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(conn))) {
    /* we're a general conn */
    origin_circuit_t *circ=NULL;

    if (conn->chosen_exit_name) {
      const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
      int opt = conn->chosen_exit_optional;
      if (!node && !want_onehop) {
        /* We ran into this warning when trying to extend a circuit to a
         * hidden service directory for which we didn't have a router
         * descriptor. See flyspray task 767 for more details. We should
         * keep this in mind when deciding to use BEGIN_DIR cells for other
         * directory requests as well. -KL*/
        log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
               "Requested exit point '%s' is not known. %s.",
               conn->chosen_exit_name, opt ? "Trying others" : "Closing");
        if (opt) {
          conn->chosen_exit_optional = 0;
          tor_free(conn->chosen_exit_name);
          return 0;
        }
        return -1;
      }
      if (node && !connection_ap_can_use_exit(conn, node)) {
        log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
               "Requested exit point '%s' is excluded or "
               "would refuse request. %s.",
               conn->chosen_exit_name, opt ? "Trying others" : "Closing");
        if (opt) {
          conn->chosen_exit_optional = 0;
          tor_free(conn->chosen_exit_name);
          return 0;
        }
        return -1;
      }
    }

    /* find the circuit that we should use, if there is one. */
    retval = circuit_get_open_circ_or_launch(
        conn, CIRCUIT_PURPOSE_C_GENERAL, &circ);
    if (retval < 1) // XXX023 if we totally fail, this still returns 0 -RD
      return retval;

    log_debug(LD_APP|LD_CIRC,
              "Attaching apconn to circ %d (stream %d sec old).",
              circ->_base.n_circ_id, conn_age);
    /* print the circ's path, so people can figure out which circs are
     * sucking. */
    circuit_log_path(LOG_INFO,LD_APP|LD_CIRC,circ);

    /* We have found a suitable circuit for our conn. Hurray. */
    return connection_ap_handshake_attach_chosen_circuit(conn, circ, NULL);

  } else { /* we're a rendezvous conn */
    origin_circuit_t *rendcirc=NULL, *introcirc=NULL;

    tor_assert(!ENTRY_TO_EDGE_CONN(conn)->cpath_layer);

    /* start by finding a rendezvous circuit for us */

    retval = circuit_get_open_circ_or_launch(
       conn, CIRCUIT_PURPOSE_C_REND_JOINED, &rendcirc);
    if (retval < 0) return -1; /* failed */

    if (retval > 0) {
      tor_assert(rendcirc);
      /* one is already established, attach */
      log_info(LD_REND,
               "rend joined circ %d already here. attaching. "
               "(stream %d sec old)",
               rendcirc->_base.n_circ_id, conn_age);
      /* Mark rendezvous circuits as 'newly dirty' every time you use
       * them, since the process of rebuilding a rendezvous circ is so
       * expensive. There is a tradeoff between linkability and
       * feasibility, at this point.
       */
      rendcirc->_base.timestamp_dirty = time(NULL);
      link_apconn_to_circ(conn, rendcirc, NULL);
      if (connection_ap_handshake_send_begin(conn) < 0)
        return 0; /* already marked, let them fade away */
      return 1;
    }

    if (rendcirc && (rendcirc->_base.purpose ==
                     CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED)) {
      log_info(LD_REND,
               "pending-join circ %d already here, with intro ack. "
               "Stalling. (stream %d sec old)",
                rendcirc->_base.n_circ_id, conn_age);
      return 0;
    }

    /* it's on its way. find an intro circ. */
    retval = circuit_get_open_circ_or_launch(
      conn, CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT, &introcirc);
    if (retval < 0) return -1; /* failed */

    if (retval > 0) {
      /* one has already sent the intro. keep waiting. */
      circuit_t *c = NULL;
      tor_assert(introcirc);
      log_info(LD_REND, "Intro circ %d present and awaiting ack (rend %d). "
               "Stalling. (stream %d sec old)",
               introcirc->_base.n_circ_id,
               rendcirc ? rendcirc->_base.n_circ_id : 0,
               conn_age);
      /* abort parallel intro circs, if any */
      for (c = global_circuitlist; c; c = c->next) {
        if (c->purpose == CIRCUIT_PURPOSE_C_INTRODUCING &&
            !c->marked_for_close && CIRCUIT_IS_ORIGIN(c)) {
          origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(c);
          if (oc->rend_data &&
              !rend_cmp_service_ids(
                            ENTRY_TO_EDGE_CONN(conn)->rend_data->onion_address,
                            oc->rend_data->onion_address)) {
            log_info(LD_REND|LD_CIRC, "Closing introduction circuit that we "
                     "built in parallel.");
            circuit_mark_for_close(c, END_CIRC_REASON_TIMEOUT);
          }
        }
      }
      return 0;
    }

    /* now rendcirc and introcirc are each either undefined or not finished */

    if (rendcirc && introcirc &&
        rendcirc->_base.purpose == CIRCUIT_PURPOSE_C_REND_READY) {
      log_info(LD_REND,
               "ready rend circ %d already here (no intro-ack yet on "
               "intro %d). (stream %d sec old)",
               rendcirc->_base.n_circ_id,
               introcirc->_base.n_circ_id, conn_age);

      tor_assert(introcirc->_base.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
      if (introcirc->_base.state == CIRCUIT_STATE_OPEN) {
        log_info(LD_REND,"found open intro circ %d (rend %d); sending "
                 "introduction. (stream %d sec old)",
                 introcirc->_base.n_circ_id, rendcirc->_base.n_circ_id,
                 conn_age);
        switch (rend_client_send_introduction(introcirc, rendcirc)) {
        case 0: /* success */
          rendcirc->_base.timestamp_dirty = time(NULL);
          introcirc->_base.timestamp_dirty = time(NULL);
          assert_circuit_ok(TO_CIRCUIT(rendcirc));
          assert_circuit_ok(TO_CIRCUIT(introcirc));
          return 0;
        case -1: /* transient error */
          return 0;
        case -2: /* permanent error */
          return -1;
        default: /* oops */
          tor_fragile_assert();
          return -1;
        }
      }
    }

    log_info(LD_REND, "Intro (%d) and rend (%d) circs are not both ready. "
             "Stalling conn. (%d sec old)",
             introcirc ? introcirc->_base.n_circ_id : 0,
             rendcirc ? rendcirc->_base.n_circ_id : 0, conn_age);
    return 0;
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

int hostname_in_track_host_exits ( const or_options_t options,
const char *  address 
)

Return true iff address is matched by one of the entries in TrackHostExits.

Definition at line 1753 of file circuituse.c.

{
  if (!options->TrackHostExits)
    return 0;
  SMARTLIST_FOREACH_BEGIN(options->TrackHostExits, const char *, cp) {
    if (cp[0] == '.') { /* match end */
      if (cp[1] == '\0' ||
          !strcasecmpend(address, cp) ||
          !strcasecmp(address, &cp[1]))
        return 1;
    } else if (strcasecmp(cp, address) == 0) {
      return 1;
    }
  } SMARTLIST_FOREACH_END(cp);
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void reset_bandwidth_test ( void  )

Reset have_performed_bandwidth_test, so we'll start building testing circuits again so we can exercise our bandwidth.

Definition at line 1002 of file circuituse.c.

Here is the caller graph for this function: