Back to index

tor  0.2.3.18-rc
Defines | Functions
rendclient.h File Reference

Header file for rendclient.c. More...

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

Go to the source code of this file.

Defines

#define INTRO_POINT_FAILURE_GENERIC   0
#define INTRO_POINT_FAILURE_TIMEOUT   1
#define INTRO_POINT_FAILURE_UNREACHABLE   2

Functions

void rend_client_purge_state (void)
 Purge all potentially remotely-detectable state held in the hidden service client code.
void rend_client_introcirc_has_opened (origin_circuit_t *circ)
 Called when we've established a circuit to an introduction point: send the introduction request.
void rend_client_rendcirc_has_opened (origin_circuit_t *circ)
 Called when a rendezvous circuit is open; sends a establish rendezvous circuit as appropriate.
int rend_client_introduction_acked (origin_circuit_t *circ, const uint8_t *request, size_t request_len)
 Called when get an ACK or a NAK for a REND_INTRODUCE1 cell.
void rend_client_refetch_v2_renddesc (const rend_data_t *rend_query)
 Unless we already have a descriptor for rend_query with at least one (possibly) working introduction point in it, start a connection to a hidden service directory to fetch a v2 rendezvous service descriptor.
void rend_client_cancel_descriptor_fetches (void)
 Cancel all rendezvous descriptor fetches currently in progress.
void rend_client_purge_last_hid_serv_requests (void)
 Purge the history of request times to hidden service directories, so that future lookups of an HS descriptor will not fail because we accessed all of the HSDir relays responsible for the descriptor recently.
int rend_client_report_intro_point_failure (extend_info_t *failed_intro, const rend_data_t *rend_query, unsigned int failure_type)
 Mark failed_intro as a failed introduction point for the hidden service specified by rend_query.
int rend_client_rendezvous_acked (origin_circuit_t *circ, const uint8_t *request, size_t request_len)
 Called when we receive a RENDEZVOUS_ESTABLISHED cell; changes the state of the circuit to C_REND_READY.
int rend_client_receive_rendezvous (origin_circuit_t *circ, const uint8_t *request, size_t request_len)
 Bob sent us a rendezvous cell; join the circuits.
void rend_client_desc_trynow (const char *query)
 Find all the apconns in state AP_CONN_STATE_RENDDESC_WAIT that are waiting on query.
void rend_client_note_connection_attempt_ended (const char *onion_address)
 Clear temporary state used only during an attempt to connect to the hidden service named onion_address.
extend_info_trend_client_get_random_intro (const rend_data_t *rend_query)
 Return a newly allocated extend_info_t* for a randomly chosen introduction point for the named hidden service.
int rend_client_any_intro_points_usable (const rend_cache_entry_t *entry)
 Return true iff any introduction points still listed in entry are usable.
int rend_client_send_introduction (origin_circuit_t *introcirc, origin_circuit_t *rendcirc)
 Called when we're trying to connect an ap conn; sends an INTRODUCE1 cell down introcirc if possible.
int rend_parse_service_authorization (const or_options_t *options, int validate_only)
 Parse config_line as a client-side authorization for a hidden service and add it to the local map of hidden service authorizations.
rend_service_authorization_trend_client_lookup_service_authorization (const char *onion_address)
 Look up the client-side authorization for the hidden service with onion_address.
void rend_service_authorization_free_all (void)
 Release all the storage held in auth_hid_servs.
rend_data_trend_data_dup (const rend_data_t *request)
 Allocate and return a new rend_data_t with the same contents as query.

Detailed Description

Header file for rendclient.c.

Definition in file rendclient.h.


Define Documentation

Definition at line 26 of file rendclient.h.

Definition at line 27 of file rendclient.h.

Definition at line 28 of file rendclient.h.


Function Documentation

Return true iff any introduction points still listed in entry are usable.

Definition at line 1111 of file rendclient.c.

{
  extend_info_t *extend_info =
    rend_client_get_random_intro_impl(entry, get_options()->StrictNodes, 0);

  int rv = (extend_info != NULL);

  extend_info_free(extend_info);
  return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Cancel all rendezvous descriptor fetches currently in progress.

Definition at line 679 of file rendclient.c.

{
  smartlist_t *connection_array = get_connection_array();

  SMARTLIST_FOREACH_BEGIN(connection_array, connection_t *, conn) {
    if (conn->type == CONN_TYPE_DIR &&
        (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC ||
         conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2)) {
      /* It's a rendezvous descriptor fetch in progress -- cancel it
       * by marking the connection for close.
       *
       * Even if this connection has already reached EOF, this is
       * enough to make sure that if the descriptor hasn't been
       * processed yet, it won't be.  See the end of
       * connection_handle_read; connection_reached_eof (indirectly)
       * processes whatever response the connection received. */

      const rend_data_t *rd = (TO_DIR_CONN(conn))->rend_data;
      if (!rd) {
        log_warn(LD_BUG | LD_REND,
                 "Marking for close dir conn fetching rendezvous "
                 "descriptor for unknown service!");
      } else {
        log_debug(LD_REND, "Marking for close dir conn fetching "
                  "rendezvous descriptor for service %s",
                  safe_str(rd->onion_address));
      }
      connection_mark_for_close(conn);
    }
  } SMARTLIST_FOREACH_END(conn);
}

Here is the call graph for this function:

Here is the caller graph for this function:

void rend_client_desc_trynow ( const char *  query)

Find all the apconns in state AP_CONN_STATE_RENDDESC_WAIT that are waiting on query.

If there's a working cache entry here with at least one intro point, move them to the next state.

Definition at line 923 of file rendclient.c.

{
  entry_connection_t *conn;
  rend_cache_entry_t *entry;
  const rend_data_t *rend_data;
  time_t now = time(NULL);

  smartlist_t *conns = get_connection_array();
  SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
    if (base_conn->type != CONN_TYPE_AP ||
        base_conn->state != AP_CONN_STATE_RENDDESC_WAIT ||
        base_conn->marked_for_close)
      continue;
    conn = TO_ENTRY_CONN(base_conn);
    rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data;
    if (!rend_data)
      continue;
    if (rend_cmp_service_ids(query, rend_data->onion_address))
      continue;
    assert_connection_ok(base_conn, now);
    if (rend_cache_lookup_entry(rend_data->onion_address, -1,
                                &entry) == 1 &&
        rend_client_any_intro_points_usable(entry)) {
      /* either this fetch worked, or it failed but there was a
       * valid entry from before which we should reuse */
      log_info(LD_REND,"Rend desc is usable. Launching circuits.");
      base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;

      /* restart their timeout values, so they get a fair shake at
       * connecting to the hidden service. */
      base_conn->timestamp_created = now;
      base_conn->timestamp_lastread = now;
      base_conn->timestamp_lastwritten = now;

      if (connection_ap_handshake_attach_circuit(conn) < 0) {
        /* it will never work */
        log_warn(LD_REND,"Rendezvous attempt failed. Closing.");
        if (!base_conn->marked_for_close)
          connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
      }
    } else { /* 404, or fetch didn't get that far */
      log_notice(LD_REND,"Closing stream for '%s.onion': hidden service is "
                 "unavailable (try again later).",
                 safe_str_client(query));
      connection_mark_unattached_ap(conn, END_STREAM_REASON_RESOLVEFAILED);
      rend_client_note_connection_attempt_ended(query);
    }
  } SMARTLIST_FOREACH_END(base_conn);
}

Here is the call graph for this function:

Here is the caller graph for this function:

Return a newly allocated extend_info_t* for a randomly chosen introduction point for the named hidden service.

Return NULL if all introduction points have been tried and failed.

Definition at line 1003 of file rendclient.c.

{
  extend_info_t *result;
  rend_cache_entry_t *entry;

  if (rend_cache_lookup_entry(rend_query->onion_address, -1, &entry) < 1) {
      log_warn(LD_REND,
               "Query '%s' didn't have valid rend desc in cache. Failing.",
               safe_str_client(rend_query->onion_address));
    return NULL;
  }

  /* See if we can get a node that complies with ExcludeNodes */
  if ((result = rend_client_get_random_intro_impl(entry, 1, 1)))
    return result;
  /* If not, and StrictNodes is not set, see if we can return any old node
   */
  if (!get_options()->StrictNodes)
    return rend_client_get_random_intro_impl(entry, 0, 1);
  return NULL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Called when we've established a circuit to an introduction point: send the introduction request.

Definition at line 44 of file rendclient.c.

Here is the call graph for this function:

Here is the caller graph for this function:

int rend_client_introduction_acked ( origin_circuit_t circ,
const uint8_t *  request,
size_t  request_len 
)

Called when get an ACK or a NAK for a REND_INTRODUCE1 cell.

Definition at line 328 of file rendclient.c.

{
  origin_circuit_t *rendcirc;
  (void) request; // XXXX Use this.

  if (circ->_base.purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
    log_warn(LD_PROTOCOL,
             "Received REND_INTRODUCE_ACK on unexpected circuit %d.",
             circ->_base.n_circ_id);
    circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
    return -1;
  }

  tor_assert(circ->build_state->chosen_exit);
#ifndef NON_ANONYMOUS_MODE_ENABLED
  tor_assert(!(circ->build_state->onehop_tunnel));
#endif
  tor_assert(circ->rend_data);

  if (request_len == 0) {
    /* It's an ACK; the introduction point relayed our introduction request. */
    /* Locate the rend circ which is waiting to hear about this ack,
     * and tell it.
     */
    log_info(LD_REND,"Received ack. Telling rend circ...");
    rendcirc = circuit_get_ready_rend_circ_by_rend_data(circ->rend_data);
    if (rendcirc) { /* remember the ack */
#ifndef NON_ANONYMOUS_MODE_ENABLED
      tor_assert(!(rendcirc->build_state->onehop_tunnel));
#endif
      circuit_change_purpose(TO_CIRCUIT(rendcirc),
                             CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED);
      /* Set timestamp_dirty, because circuit_expire_building expects
       * it to specify when a circuit entered the
       * _C_REND_READY_INTRO_ACKED state. */
      rendcirc->_base.timestamp_dirty = time(NULL);
    } else {
      log_info(LD_REND,"...Found no rend circ. Dropping on the floor.");
    }
    /* close the circuit: we won't need it anymore. */
    circuit_change_purpose(TO_CIRCUIT(circ),
                           CIRCUIT_PURPOSE_C_INTRODUCE_ACKED);
    circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED);
  } else {
    /* It's a NAK; the introduction point didn't relay our request. */
    circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_INTRODUCING);
    /* Remove this intro point from the set of viable introduction
     * points. If any remain, extend to a new one and try again.
     * If none remain, refetch the service descriptor.
     */
    log_info(LD_REND, "Got nack for %s from %s...",
        safe_str_client(circ->rend_data->onion_address),
        safe_str_client(extend_info_describe(circ->build_state->chosen_exit)));
    if (rend_client_report_intro_point_failure(circ->build_state->chosen_exit,
                                             circ->rend_data,
                                             INTRO_POINT_FAILURE_GENERIC)>0) {
      /* There are introduction points left. Re-extend the circuit to
       * another intro point and try again. */
      int result = rend_client_reextend_intro_circuit(circ);
      /* XXXX If that call failed, should we close the rend circuit,
       * too? */
      return result;
    }
  }
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Look up the client-side authorization for the hidden service with onion_address.

Return NULL if no authorization is available for that address.

Definition at line 1130 of file rendclient.c.

{
  tor_assert(onion_address);
  if (!auth_hid_servs) return NULL;
  return strmap_get(auth_hid_servs, onion_address);
}

Here is the call graph for this function:

void rend_client_note_connection_attempt_ended ( const char *  onion_address)

Clear temporary state used only during an attempt to connect to the hidden service named onion_address.

Called when a connection attempt has ended; may be called occasionally at other times, and should be reasonably harmless.

Definition at line 978 of file rendclient.c.

{
  rend_cache_entry_t *cache_entry = NULL;
  rend_cache_lookup_entry(onion_address, -1, &cache_entry);

  log_info(LD_REND, "Connection attempt for %s has ended; "
           "cleaning up temporary state.",
           safe_str_client(onion_address));

  /* Clear the timed_out flag on all remaining intro points for this HS. */
  if (cache_entry != NULL) {
    SMARTLIST_FOREACH(cache_entry->parsed->intro_nodes,
                      rend_intro_point_t *, ip,
                      ip->timed_out = 0; );
  }

  /* Remove the HS's entries in last_hid_serv_requests. */
  purge_hid_serv_from_last_hid_serv_requests(onion_address);
}

Here is the call graph for this function:

Here is the caller graph for this function:

Purge the history of request times to hidden service directories, so that future lookups of an HS descriptor will not fail because we accessed all of the HSDir relays responsible for the descriptor recently.

Definition at line 517 of file rendclient.c.

{
  /* Don't create the table if it doesn't exist yet (and it may very
   * well not exist if the user hasn't accessed any HSes)... */
  strmap_t *old_last_hid_serv_requests = last_hid_serv_requests_;
  /* ... and let get_last_hid_serv_requests re-create it for us if
   * necessary. */
  last_hid_serv_requests_ = NULL;

  if (old_last_hid_serv_requests != NULL) {
    log_info(LD_REND, "Purging client last-HS-desc-request-time table");
    strmap_free(old_last_hid_serv_requests, _tor_free);
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

void rend_client_purge_state ( void  )

Purge all potentially remotely-detectable state held in the hidden service client code.

Called on SIGNAL NEWNYM.

Definition at line 34 of file rendclient.c.

Here is the call graph for this function:

Here is the caller graph for this function:

int rend_client_receive_rendezvous ( origin_circuit_t circ,
const uint8_t *  request,
size_t  request_len 
)

Bob sent us a rendezvous cell; join the circuits.

Definition at line 844 of file rendclient.c.

{
  crypt_path_t *hop;
  char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];

  if ((circ->_base.purpose != CIRCUIT_PURPOSE_C_REND_READY &&
       circ->_base.purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED)
      || !circ->build_state->pending_final_cpath) {
    log_warn(LD_PROTOCOL,"Got rendezvous2 cell from hidden service, but not "
             "expecting it. Closing.");
    circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
    return -1;
  }

  if (request_len != DH_KEY_LEN+DIGEST_LEN) {
    log_warn(LD_PROTOCOL,"Incorrect length (%d) on RENDEZVOUS2 cell.",
             (int)request_len);
    goto err;
  }

  log_info(LD_REND,"Got RENDEZVOUS2 cell from hidden service.");

  /* first DH_KEY_LEN bytes are g^y from bob. Finish the dh handshake...*/
  tor_assert(circ->build_state);
  tor_assert(circ->build_state->pending_final_cpath);
  hop = circ->build_state->pending_final_cpath;
  tor_assert(hop->dh_handshake_state);
  if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN,
                               hop->dh_handshake_state, (char*)request,
                               DH_KEY_LEN,
                               keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
    log_warn(LD_GENERAL, "Couldn't complete DH handshake.");
    goto err;
  }
  /* ... and set up cpath. */
  if (circuit_init_cpath_crypto(hop, keys+DIGEST_LEN, 0)<0)
    goto err;

  /* Check whether the digest is right... */
  if (tor_memneq(keys, request+DH_KEY_LEN, DIGEST_LEN)) {
    log_warn(LD_PROTOCOL, "Incorrect digest of key material.");
    goto err;
  }

  crypto_dh_free(hop->dh_handshake_state);
  hop->dh_handshake_state = NULL;

  /* All is well. Extend the circuit. */
  circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_REND_JOINED);
  hop->state = CPATH_STATE_OPEN;
  /* set the windows to default. these are the windows
   * that alice thinks bob has.
   */
  hop->package_window = circuit_initial_package_window();
  hop->deliver_window = CIRCWINDOW_START;

  /* Now that this circuit has finished connecting to its destination,
   * make sure circuit_get_open_circ_or_launch is willing to return it
   * so we can actually use it. */
  circ->hs_circ_has_timed_out = 0;

  onion_append_to_cpath(&circ->cpath, hop);
  circ->build_state->pending_final_cpath = NULL; /* prevent double-free */

  circuit_try_attaching_streams(circ);

  memset(keys, 0, sizeof(keys));
  return 0;
 err:
  memset(keys, 0, sizeof(keys));
  circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
  return -1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void rend_client_refetch_v2_renddesc ( const rend_data_t rend_query)

Unless we already have a descriptor for rend_query with at least one (possibly) working introduction point in it, start a connection to a hidden service directory to fetch a v2 rendezvous service descriptor.

Definition at line 624 of file rendclient.c.

{
  char descriptor_id[DIGEST_LEN];
  int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS];
  int i, tries_left;
  rend_cache_entry_t *e = NULL;
  tor_assert(rend_query);
  /* Are we configured to fetch descriptors? */
  if (!get_options()->FetchHidServDescriptors) {
    log_warn(LD_REND, "We received an onion address for a v2 rendezvous "
        "service descriptor, but are not fetching service descriptors.");
    return;
  }
  /* Before fetching, check if we already have a usable descriptor here. */
  if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) > 0 &&
      rend_client_any_intro_points_usable(e)) {
    log_info(LD_REND, "We would fetch a v2 rendezvous descriptor, but we "
                      "already have a usable descriptor here. Not fetching.");
    return;
  }
  log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s",
            safe_str_client(rend_query->onion_address));
  /* Randomly iterate over the replicas until a descriptor can be fetched
   * from one of the consecutive nodes, or no options are left. */
  tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
  for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++)
    replicas_left_to_try[i] = i;
  while (tries_left > 0) {
    int rand = crypto_rand_int(tries_left);
    int chosen_replica = replicas_left_to_try[rand];
    replicas_left_to_try[rand] = replicas_left_to_try[--tries_left];

    if (rend_compute_v2_desc_id(descriptor_id, rend_query->onion_address,
                                rend_query->auth_type == REND_STEALTH_AUTH ?
                                    rend_query->descriptor_cookie : NULL,
                                time(NULL), chosen_replica) < 0) {
      log_warn(LD_REND, "Internal error: Computing v2 rendezvous "
                        "descriptor ID did not succeed.");
      return;
    }
    if (directory_get_from_hs_dir(descriptor_id, rend_query) != 0)
      return; /* either success or failure, but we're done */
  }
  /* If we come here, there are no hidden service directories left. */
  log_info(LD_REND, "Could not pick one of the responsible hidden "
                    "service directories to fetch descriptors, because "
                    "we already tried them all unsuccessfully.");
  /* Close pending connections. */
  rend_client_desc_trynow(rend_query->onion_address);
  return;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Called when a rendezvous circuit is open; sends a establish rendezvous circuit as appropriate.

Definition at line 313 of file rendclient.c.

{
  tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);

  log_info(LD_REND,"rendcirc is open");

  /* generate a rendezvous cookie, store it in circ */
  if (rend_client_send_establish_rendezvous(circ) < 0) {
    return;
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

int rend_client_rendezvous_acked ( origin_circuit_t circ,
const uint8_t *  request,
size_t  request_len 
)

Called when we receive a RENDEZVOUS_ESTABLISHED cell; changes the state of the circuit to C_REND_READY.

Definition at line 815 of file rendclient.c.

{
  (void) request;
  (void) request_len;
  /* we just got an ack for our establish-rendezvous. switch purposes. */
  if (circ->_base.purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND) {
    log_warn(LD_PROTOCOL,"Got a rendezvous ack when we weren't expecting one. "
             "Closing circ.");
    circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
    return -1;
  }
  log_info(LD_REND,"Got rendezvous ack. This circuit is now ready for "
           "rendezvous.");
  circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_REND_READY);
  /* Set timestamp_dirty, because circuit_expire_building expects it
   * to specify when a circuit entered the _C_REND_READY state. */
  circ->_base.timestamp_dirty = time(NULL);
  /* XXXX This is a pretty brute-force approach. It'd be better to
   * attach only the connections that are waiting on this circuit, rather
   * than trying to attach them all. See comments bug 743. */
  /* If we already have the introduction circuit built, make sure we send
   * the INTRODUCE cell _now_ */
  connection_ap_attach_pending();
  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int rend_client_report_intro_point_failure ( extend_info_t failed_intro,
const rend_data_t rend_query,
unsigned int  failure_type 
)

Mark failed_intro as a failed introduction point for the hidden service specified by rend_query.

If the HS now has no usable intro points, or we do not have an HS descriptor for it, then launch a new renddesc fetch.

If failure_type is INTRO_POINT_FAILURE_GENERIC, remove the intro point from (our parsed copy of) the HS descriptor.

If failure_type is INTRO_POINT_FAILURE_TIMEOUT, mark the intro point as 'timed out'; it will not be retried until the current hidden service connection attempt has ended or it has appeared in a newly fetched rendezvous descriptor.

If failure_type is INTRO_POINT_FAILURE_UNREACHABLE, increment the intro point's reachability-failure count; if it has now failed MAX_INTRO_POINT_REACHABILITY_FAILURES or more times, remove the intro point from (our parsed copy of) the HS descriptor.

Return -1 if error, 0 if no usable intro points remain or service unrecognized, 1 if recognized and some intro points remain.

Definition at line 733 of file rendclient.c.

{
  int i, r;
  rend_cache_entry_t *ent;
  connection_t *conn;

  r = rend_cache_lookup_entry(rend_query->onion_address, -1, &ent);
  if (r<0) {
    log_warn(LD_BUG, "Malformed service ID %s.",
             escaped_safe_str_client(rend_query->onion_address));
    return -1;
  }
  if (r==0) {
    log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.",
             escaped_safe_str_client(rend_query->onion_address));
    rend_client_refetch_v2_renddesc(rend_query);
    return 0;
  }

  for (i = 0; i < smartlist_len(ent->parsed->intro_nodes); i++) {
    rend_intro_point_t *intro = smartlist_get(ent->parsed->intro_nodes, i);
    if (tor_memeq(failed_intro->identity_digest,
                intro->extend_info->identity_digest, DIGEST_LEN)) {
      switch (failure_type) {
      default:
        log_warn(LD_BUG, "Unknown failure type %u. Removing intro point.",
                 failure_type);
        tor_fragile_assert();
        /* fall through */
      case INTRO_POINT_FAILURE_GENERIC:
        rend_intro_point_free(intro);
        smartlist_del(ent->parsed->intro_nodes, i);
        break;
      case INTRO_POINT_FAILURE_TIMEOUT:
        intro->timed_out = 1;
        break;
      case INTRO_POINT_FAILURE_UNREACHABLE:
        ++(intro->unreachable_count);
        {
          int zap_intro_point =
            intro->unreachable_count >= MAX_INTRO_POINT_REACHABILITY_FAILURES;
          log_info(LD_REND, "Failed to reach this intro point %u times.%s",
                   intro->unreachable_count,
                   zap_intro_point ? " Removing from descriptor.": "");
          if (zap_intro_point) {
            rend_intro_point_free(intro);
            smartlist_del(ent->parsed->intro_nodes, i);
          }
        }
        break;
      }
      break;
    }
  }

  if (! rend_client_any_intro_points_usable(ent)) {
    log_info(LD_REND,
             "No more intro points remain for %s. Re-fetching descriptor.",
             escaped_safe_str_client(rend_query->onion_address));
    rend_client_refetch_v2_renddesc(rend_query);

    /* move all pending streams back to renddesc_wait */
    while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
                                   AP_CONN_STATE_CIRCUIT_WAIT,
                                   rend_query->onion_address))) {
      conn->state = AP_CONN_STATE_RENDDESC_WAIT;
    }

    return 0;
  }
  log_info(LD_REND,"%d options left for %s.",
           smartlist_len(ent->parsed->intro_nodes),
           escaped_safe_str_client(rend_query->onion_address));
  return 1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int rend_client_send_introduction ( origin_circuit_t introcirc,
origin_circuit_t rendcirc 
)

Called when we're trying to connect an ap conn; sends an INTRODUCE1 cell down introcirc if possible.

Definition at line 124 of file rendclient.c.

{
  size_t payload_len;
  int r, v3_shift = 0;
  char payload[RELAY_PAYLOAD_SIZE];
  char tmp[RELAY_PAYLOAD_SIZE];
  rend_cache_entry_t *entry;
  crypt_path_t *cpath;
  off_t dh_offset;
  crypto_pk_t *intro_key = NULL;

  tor_assert(introcirc->_base.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
  tor_assert(rendcirc->_base.purpose == CIRCUIT_PURPOSE_C_REND_READY);
  tor_assert(introcirc->rend_data);
  tor_assert(rendcirc->rend_data);
  tor_assert(!rend_cmp_service_ids(introcirc->rend_data->onion_address,
                                   rendcirc->rend_data->onion_address));
#ifndef NON_ANONYMOUS_MODE_ENABLED
  tor_assert(!(introcirc->build_state->onehop_tunnel));
  tor_assert(!(rendcirc->build_state->onehop_tunnel));
#endif

  if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1,
                              &entry) < 1) {
    log_info(LD_REND,
             "query %s didn't have valid rend desc in cache. "
             "Refetching descriptor.",
             safe_str_client(introcirc->rend_data->onion_address));
    rend_client_refetch_v2_renddesc(introcirc->rend_data);
    {
      connection_t *conn;

      while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
                       AP_CONN_STATE_CIRCUIT_WAIT,
                       introcirc->rend_data->onion_address))) {
        conn->state = AP_CONN_STATE_RENDDESC_WAIT;
      }
    }

    return -1;
  }

  /* first 20 bytes of payload are the hash of Bob's pk */
  intro_key = NULL;
  SMARTLIST_FOREACH(entry->parsed->intro_nodes, rend_intro_point_t *,
                    intro, {
    if (tor_memeq(introcirc->build_state->chosen_exit->identity_digest,
                intro->extend_info->identity_digest, DIGEST_LEN)) {
      intro_key = intro->intro_key;
      break;
    }
  });
  if (!intro_key) {
    log_info(LD_REND, "Could not find intro key for %s at %s; we "
             "have a v2 rend desc with %d intro points. "
             "Trying a different intro point...",
             safe_str_client(introcirc->rend_data->onion_address),
             safe_str_client(extend_info_describe(
                                   introcirc->build_state->chosen_exit)),
             smartlist_len(entry->parsed->intro_nodes));

    if (rend_client_reextend_intro_circuit(introcirc)) {
      goto perm_err;
    } else {
      return -1;
    }
  }
  if (crypto_pk_get_digest(intro_key, payload)<0) {
    log_warn(LD_BUG, "Internal error: couldn't hash public key.");
    goto perm_err;
  }

  /* Initialize the pending_final_cpath and start the DH handshake. */
  cpath = rendcirc->build_state->pending_final_cpath;
  if (!cpath) {
    cpath = rendcirc->build_state->pending_final_cpath =
      tor_malloc_zero(sizeof(crypt_path_t));
    cpath->magic = CRYPT_PATH_MAGIC;
    if (!(cpath->dh_handshake_state = crypto_dh_new(DH_TYPE_REND))) {
      log_warn(LD_BUG, "Internal error: couldn't allocate DH.");
      goto perm_err;
    }
    if (crypto_dh_generate_public(cpath->dh_handshake_state)<0) {
      log_warn(LD_BUG, "Internal error: couldn't generate g^x.");
      goto perm_err;
    }
  }

  /* If version is 3, write (optional) auth data and timestamp. */
  if (entry->parsed->protocols & (1<<3)) {
    tmp[0] = 3; /* version 3 of the cell format */
    tmp[1] = (uint8_t)introcirc->rend_data->auth_type; /* auth type, if any */
    v3_shift = 1;
    if (introcirc->rend_data->auth_type != REND_NO_AUTH) {
      set_uint16(tmp+2, htons(REND_DESC_COOKIE_LEN));
      memcpy(tmp+4, introcirc->rend_data->descriptor_cookie,
             REND_DESC_COOKIE_LEN);
      v3_shift += 2+REND_DESC_COOKIE_LEN;
    }
    set_uint32(tmp+v3_shift+1, htonl((uint32_t)time(NULL)));
    v3_shift += 4;
  } /* if version 2 only write version number */
  else if (entry->parsed->protocols & (1<<2)) {
    tmp[0] = 2; /* version 2 of the cell format */
  }

  /* write the remaining items into tmp */
  if (entry->parsed->protocols & (1<<3) || entry->parsed->protocols & (1<<2)) {
    /* version 2 format */
    extend_info_t *extend_info = rendcirc->build_state->chosen_exit;
    int klen;
    /* nul pads */
    set_uint32(tmp+v3_shift+1, tor_addr_to_ipv4h(&extend_info->addr));
    set_uint16(tmp+v3_shift+5, htons(extend_info->port));
    memcpy(tmp+v3_shift+7, extend_info->identity_digest, DIGEST_LEN);
    klen = crypto_pk_asn1_encode(extend_info->onion_key,
                                 tmp+v3_shift+7+DIGEST_LEN+2,
                                 sizeof(tmp)-(v3_shift+7+DIGEST_LEN+2));
    set_uint16(tmp+v3_shift+7+DIGEST_LEN, htons(klen));
    memcpy(tmp+v3_shift+7+DIGEST_LEN+2+klen, rendcirc->rend_data->rend_cookie,
           REND_COOKIE_LEN);
    dh_offset = v3_shift+7+DIGEST_LEN+2+klen+REND_COOKIE_LEN;
  } else {
    /* Version 0. */
    strncpy(tmp, rendcirc->build_state->chosen_exit->nickname,
            (MAX_NICKNAME_LEN+1)); /* nul pads */
    memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_data->rend_cookie,
           REND_COOKIE_LEN);
    dh_offset = MAX_NICKNAME_LEN+1+REND_COOKIE_LEN;
  }

  if (crypto_dh_get_public(cpath->dh_handshake_state, tmp+dh_offset,
                           DH_KEY_LEN)<0) {
    log_warn(LD_BUG, "Internal error: couldn't extract g^x.");
    goto perm_err;
  }

  note_crypto_pk_op(REND_CLIENT);
  /*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
   * to avoid buffer overflows? */
  r = crypto_pk_public_hybrid_encrypt(intro_key, payload+DIGEST_LEN,
                                      sizeof(payload)-DIGEST_LEN,
                                      tmp,
                                      (int)(dh_offset+DH_KEY_LEN),
                                      PK_PKCS1_OAEP_PADDING, 0);
  if (r<0) {
    log_warn(LD_BUG,"Internal error: hybrid pk encrypt failed.");
    goto perm_err;
  }

  payload_len = DIGEST_LEN + r;
  tor_assert(payload_len <= RELAY_PAYLOAD_SIZE); /* we overran something */

  /* Copy the rendezvous cookie from rendcirc to introcirc, so that
   * when introcirc gets an ack, we can change the state of the right
   * rendezvous circuit. */
  memcpy(introcirc->rend_data->rend_cookie, rendcirc->rend_data->rend_cookie,
         REND_COOKIE_LEN);

  log_info(LD_REND, "Sending an INTRODUCE1 cell");
  if (relay_send_command_from_edge(0, TO_CIRCUIT(introcirc),
                                   RELAY_COMMAND_INTRODUCE1,
                                   payload, payload_len,
                                   introcirc->cpath->prev)<0) {
    /* introcirc is already marked for close. leave rendcirc alone. */
    log_warn(LD_BUG, "Couldn't send INTRODUCE1 cell");
    return -2;
  }

  /* Now, we wait for an ACK or NAK on this circuit. */
  circuit_change_purpose(TO_CIRCUIT(introcirc),
                         CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT);
  /* Set timestamp_dirty, because circuit_expire_building expects it
   * to specify when a circuit entered the _C_INTRODUCE_ACK_WAIT
   * state. */
  introcirc->_base.timestamp_dirty = time(NULL);

  return 0;
 perm_err:
  if (!introcirc->_base.marked_for_close)
    circuit_mark_for_close(TO_CIRCUIT(introcirc), END_CIRC_REASON_INTERNAL);
  circuit_mark_for_close(TO_CIRCUIT(rendcirc), END_CIRC_REASON_INTERNAL);
  return -2;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Allocate and return a new rend_data_t with the same contents as query.

Definition at line 1465 of file rendcommon.c.

{
  tor_assert(data);
  return tor_memdup(data, sizeof(rend_data_t));
}

Here is the caller graph for this function:

int rend_parse_service_authorization ( const or_options_t options,
int  validate_only 
)

Parse config_line as a client-side authorization for a hidden service and add it to the local map of hidden service authorizations.

Return 0 for success and -1 for failure.

Definition at line 1167 of file rendclient.c.

{
  config_line_t *line;
  int res = -1;
  strmap_t *parsed = strmap_new();
  smartlist_t *sl = smartlist_new();
  rend_service_authorization_t *auth = NULL;

  for (line = options->HidServAuth; line; line = line->next) {
    char *onion_address, *descriptor_cookie;
    char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2];
    char descriptor_cookie_base64ext[REND_DESC_COOKIE_LEN_BASE64+2+1];
    int auth_type_val = 0;
    auth = NULL;
    SMARTLIST_FOREACH(sl, char *, c, tor_free(c););
    smartlist_clear(sl);
    smartlist_split_string(sl, line->value, " ",
                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
    if (smartlist_len(sl) < 2) {
      log_warn(LD_CONFIG, "Configuration line does not consist of "
               "\"onion-address authorization-cookie [service-name]\": "
               "'%s'", line->value);
      goto err;
    }
    auth = tor_malloc_zero(sizeof(rend_service_authorization_t));
    /* Parse onion address. */
    onion_address = smartlist_get(sl, 0);
    if (strlen(onion_address) != REND_SERVICE_ADDRESS_LEN ||
        strcmpend(onion_address, ".onion")) {
      log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
               onion_address);
      goto err;
    }
    strlcpy(auth->onion_address, onion_address, REND_SERVICE_ID_LEN_BASE32+1);
    if (!rend_valid_service_id(auth->onion_address)) {
      log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
               onion_address);
      goto err;
    }
    /* Parse descriptor cookie. */
    descriptor_cookie = smartlist_get(sl, 1);
    if (strlen(descriptor_cookie) != REND_DESC_COOKIE_LEN_BASE64) {
      log_warn(LD_CONFIG, "Authorization cookie has wrong length: '%s'",
               descriptor_cookie);
      goto err;
    }
    /* Add trailing zero bytes (AA) to make base64-decoding happy. */
    tor_snprintf(descriptor_cookie_base64ext,
                 REND_DESC_COOKIE_LEN_BASE64+2+1,
                 "%sAA", descriptor_cookie);
    if (base64_decode(descriptor_cookie_tmp, sizeof(descriptor_cookie_tmp),
                      descriptor_cookie_base64ext,
                      strlen(descriptor_cookie_base64ext)) < 0) {
      log_warn(LD_CONFIG, "Decoding authorization cookie failed: '%s'",
               descriptor_cookie);
      goto err;
    }
    auth_type_val = (descriptor_cookie_tmp[16] >> 4) + 1;
    if (auth_type_val < 1 || auth_type_val > 2) {
      log_warn(LD_CONFIG, "Authorization cookie has unknown authorization "
                          "type encoded.");
      goto err;
    }
    auth->auth_type = auth_type_val == 1 ? REND_BASIC_AUTH : REND_STEALTH_AUTH;
    memcpy(auth->descriptor_cookie, descriptor_cookie_tmp,
           REND_DESC_COOKIE_LEN);
    if (strmap_get(parsed, auth->onion_address)) {
      log_warn(LD_CONFIG, "Duplicate authorization for the same hidden "
                          "service.");
      goto err;
    }
    strmap_set(parsed, auth->onion_address, auth);
    auth = NULL;
  }
  res = 0;
  goto done;
 err:
  res = -1;
 done:
  rend_service_authorization_free(auth);
  SMARTLIST_FOREACH(sl, char *, c, tor_free(c););
  smartlist_free(sl);
  if (!validate_only && res == 0) {
    rend_service_authorization_free_all();
    auth_hid_servs = parsed;
  } else {
    strmap_free(parsed, rend_service_authorization_strmap_item_free);
  }
  return res;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Release all the storage held in auth_hid_servs.

Definition at line 1154 of file rendclient.c.

Here is the call graph for this function:

Here is the caller graph for this function: