Example #1
0
static void
test_halfstream_wrap(void *arg)
{
  origin_circuit_t *circ =
      helper_create_origin_circuit(CIRCUIT_PURPOSE_C_GENERAL, 0);
  edge_connection_t *edgeconn;
  entry_connection_t *entryconn;

  circ->cpath->state = CPATH_STATE_AWAITING_KEYS;
  circ->cpath->deliver_window = CIRCWINDOW_START;

  entryconn = fake_entry_conn(circ, 23);
  edgeconn = ENTRY_TO_EDGE_CONN(entryconn);

  (void)arg;

  /* Suppress the WARN message we generate in this test */
  setup_full_capture_of_logs(LOG_WARN);
  MOCK(connection_mark_for_close_internal_, mock_mark_for_close);

  /* Verify that get_unique_stream_id_by_circ() can wrap uint16_t */
  circ->next_stream_id = 65530;
  halfstream_insert(circ, edgeconn, NULL, 7, 0);
  tt_int_op(circ->next_stream_id, OP_EQ, 2);
  tt_int_op(smartlist_len(circ->half_streams), OP_EQ, 7);

  /* Insert full-1 */
  halfstream_insert(circ, edgeconn, NULL,
                    65534-smartlist_len(circ->half_streams), 0);
  tt_int_op(smartlist_len(circ->half_streams), OP_EQ, 65534);

  /* Verify that we can get_unique_stream_id_by_circ() successfully */
  edgeconn->stream_id = get_unique_stream_id_by_circ(circ);
  tt_int_op(edgeconn->stream_id, OP_NE, 0); /* 0 is failure */

  /* Insert an opened stream on the circ with that id */
  ENTRY_TO_CONN(entryconn)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn)->outbuf_flushlen = 0;
  edgeconn->base_.state = AP_CONN_STATE_CONNECT_WAIT;
  circ->p_streams = edgeconn;

  /* Verify that get_unique_stream_id_by_circ() fails */
  tt_int_op(get_unique_stream_id_by_circ(circ), OP_EQ, 0); /* 0 is failure */

  /* eof the one opened stream. Verify it is now in half-closed */
  tt_int_op(smartlist_len(circ->half_streams), OP_EQ, 65534);
  connection_edge_reached_eof(edgeconn);
  tt_int_op(smartlist_len(circ->half_streams), OP_EQ, 65535);

  /* Verify get_unique_stream_id_by_circ() fails due to full half-closed */
  circ->p_streams = NULL;
  tt_int_op(get_unique_stream_id_by_circ(circ), OP_EQ, 0); /* 0 is failure */

 done:
  circuit_free_(TO_CIRCUIT(circ));
  connection_free_minimal(ENTRY_TO_CONN(entryconn));
  UNMOCK(connection_mark_for_close_internal_);
}
Example #2
0
/** Helper function: called whenever the client sends a resolve request to our
 * controller.  We need to eventually answer the request <b>req</b>.
 * Returns 0 if the controller will be getting (or has gotten) an event in
 * response; -1 if we couldn't launch the request.
 */
int
dnsserv_launch_request(const char *name, int reverse)
{
  entry_connection_t *entry_conn;
  edge_connection_t *conn;
  char *q_name;

  /* Make a new dummy AP connection, and attach the request to it. */
  entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
  conn = ENTRY_TO_EDGE_CONN(entry_conn);
  conn->base_.state = AP_CONN_STATE_RESOLVE_WAIT;

  if (reverse)
    entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
  else
    entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE;

  conn->is_dns_request = 1;

  strlcpy(entry_conn->socks_request->address, name,
          sizeof(entry_conn->socks_request->address));

  entry_conn->socks_request->listener_type = CONN_TYPE_CONTROL_LISTENER;
  entry_conn->original_dest_address = tor_strdup(name);
  entry_conn->session_group = SESSION_GROUP_CONTROL_RESOLVE;
  entry_conn->nym_epoch = get_signewnym_epoch();
  entry_conn->isolation_flags = ISO_DEFAULT;

  if (connection_add(TO_CONN(conn))<0) {
    log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request");
    connection_free(TO_CONN(conn));
    return -1;
  }

  /* Now, unless a controller asked us to leave streams unattached,
  * throw the connection over to get rewritten (which will
  * answer it immediately if it's in the cache, or completely bogus, or
  * automapped), and then attached to a circuit. */
  log_info(LD_APP, "Passing request for %s to rewrite_and_attach.",
           escaped_safe_str_client(name));
  q_name = tor_strdup(name); /* q could be freed in rewrite_and_attach */
  connection_ap_rewrite_and_attach_if_allowed(entry_conn, NULL, NULL);
  /* Now, the connection is marked if it was bad. */

  log_info(LD_APP, "Passed request for %s to rewrite_and_attach_if_allowed.",
           escaped_safe_str_client(q_name));
  tor_free(q_name);
  return 0;
}
Example #3
0
static entry_connection_t *
fake_entry_conn(origin_circuit_t *oncirc, streamid_t id)
{
  edge_connection_t *edgeconn;
  entry_connection_t *entryconn;

  entryconn = entry_connection_new(CONN_TYPE_AP, AF_INET);
  edgeconn = ENTRY_TO_EDGE_CONN(entryconn);
  edgeconn->base_.state = AP_CONN_STATE_CONNECT_WAIT;
  edgeconn->deliver_window = STREAMWINDOW_START;
  edgeconn->package_window = STREAMWINDOW_START;

  edgeconn->stream_id = id;
  edgeconn->on_circuit = TO_CIRCUIT(oncirc);
  edgeconn->cpath_layer = oncirc->cpath;

  return entryconn;
}
Example #4
0
static edge_connection_t *
dummy_edge_conn_new(circuit_t *circ,
                    int type, size_t in_bytes, size_t out_bytes)
{
  edge_connection_t *conn;
  generic_buffer_t *inbuf, *outbuf;

  if (type == CONN_TYPE_EXIT)
    conn = edge_connection_new(type, AF_INET);
  else
    conn = ENTRY_TO_EDGE_CONN(entry_connection_new(type, AF_INET));

#ifdef USE_BUFFEREVENTS
  inbuf = bufferevent_get_input(TO_CONN(conn)->bufev);
  outbuf = bufferevent_get_output(TO_CONN(conn)->bufev);
#else
  inbuf = TO_CONN(conn)->inbuf;
  outbuf = TO_CONN(conn)->outbuf;
#endif

  /* We add these bytes directly to the buffers, to avoid all the
   * edge connection read/write machinery. */
  add_bytes_to_buf(inbuf, in_bytes);
  add_bytes_to_buf(outbuf, out_bytes);

  conn->on_circuit = circ;
  if (type == CONN_TYPE_EXIT) {
    or_circuit_t *oc  = TO_OR_CIRCUIT(circ);
    conn->next_stream = oc->n_streams;
    oc->n_streams = conn;
  } else {
    origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
    conn->next_stream = oc->p_streams;
    oc->p_streams = conn;
  }

  return conn;
}
Example #5
0
/** Helper function: called by evdns whenever the client sends a request to our
 * DNSPort.  We need to eventually answer the request <b>req</b>.
 */
static void
evdns_server_callback(struct evdns_server_request *req, void *data_)
{
  const listener_connection_t *listener = data_;
  entry_connection_t *entry_conn;
  edge_connection_t *conn;
  int i = 0;
  struct evdns_server_question *q = NULL, *supported_q = NULL;
  struct sockaddr_storage addr;
  struct sockaddr *sa;
  int addrlen;
  tor_addr_t tor_addr;
  uint16_t port;
  int err = DNS_ERR_NONE;
  char *q_name;

  tor_assert(req);

  log_info(LD_APP, "Got a new DNS request!");

  req->flags |= 0x80; /* set RA */

  /* First, check whether the requesting address matches our SOCKSPolicy. */
  if ((addrlen = evdns_server_request_get_requesting_addr(req,
                      (struct sockaddr*)&addr, (socklen_t)sizeof(addr))) < 0) {
    log_warn(LD_APP, "Couldn't get requesting address.");
    evdns_server_request_respond(req, DNS_ERR_SERVERFAILED);
    return;
  }
  (void) addrlen;
  sa = (struct sockaddr*) &addr;
  if (tor_addr_from_sockaddr(&tor_addr, sa, &port)<0) {
    log_warn(LD_APP, "Requesting address wasn't recognized.");
    evdns_server_request_respond(req, DNS_ERR_SERVERFAILED);
    return;
  }

  if (!socks_policy_permits_address(&tor_addr)) {
    log_warn(LD_APP, "Rejecting DNS request from disallowed IP.");
    evdns_server_request_respond(req, DNS_ERR_REFUSED);
    return;
  }

  /* Now, let's find the first actual question of a type we can answer in this
   * DNS request.  It makes us a little noncompliant to act like this; we
   * should fix that eventually if it turns out to make a difference for
   * anybody. */
  if (req->nquestions == 0) {
    log_info(LD_APP, "No questions in DNS request; sending back nil reply.");
    evdns_server_request_respond(req, 0);
    return;
  }
  if (req->nquestions > 1) {
    log_info(LD_APP, "Got a DNS request with more than one question; I only "
             "handle one question at a time for now.  Skipping the extras.");
  }
  for (i = 0; i < req->nquestions; ++i) {
    if (req->questions[i]->dns_question_class != EVDNS_CLASS_INET)
      continue;
    switch (req->questions[i]->type) {
      case EVDNS_TYPE_A:
      case EVDNS_TYPE_AAAA:
      case EVDNS_TYPE_PTR:
        /* We always pick the first one of these questions, if there is
           one. */
        if (! supported_q)
          supported_q = req->questions[i];
        break;
      default:
        break;
      }
  }
  if (supported_q)
    q = supported_q;
  if (!q) {
    log_info(LD_APP, "None of the questions we got were ones we're willing "
             "to support. Sending NOTIMPL.");
    evdns_server_request_respond(req, DNS_ERR_NOTIMPL);
    return;
  }

  /* Make sure the name isn't too long: This should be impossible, I think. */
  if (err == DNS_ERR_NONE && strlen(q->name) > MAX_SOCKS_ADDR_LEN-1)
    err = DNS_ERR_FORMAT;

  if (err != DNS_ERR_NONE || !supported_q) {
    /* We got an error?  There's no question we're willing to answer? Then
     * send back an answer immediately; we're done. */
    evdns_server_request_respond(req, err);
    return;
  }

  /* Make a new dummy AP connection, and attach the request to it. */
  entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
  conn = ENTRY_TO_EDGE_CONN(entry_conn);
  CONNECTION_AP_EXPECT_NONPENDING(entry_conn);
  TO_CONN(conn)->state = AP_CONN_STATE_RESOLVE_WAIT;
  conn->is_dns_request = 1;

  tor_addr_copy(&TO_CONN(conn)->addr, &tor_addr);
  TO_CONN(conn)->port = port;
  TO_CONN(conn)->address = tor_addr_to_str_dup(&tor_addr);

  if (q->type == EVDNS_TYPE_A || q->type == EVDNS_TYPE_AAAA ||
      q->type == EVDNS_QTYPE_ALL) {
    entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE;
  } else {
    tor_assert(q->type == EVDNS_TYPE_PTR);
    entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
  }

  /* This serves our DNS port so enable DNS request by default. */
  entry_conn->entry_cfg.dns_request = 1;
  if (q->type == EVDNS_TYPE_A || q->type == EVDNS_QTYPE_ALL) {
    entry_conn->entry_cfg.ipv4_traffic = 1;
    entry_conn->entry_cfg.ipv6_traffic = 0;
    entry_conn->entry_cfg.prefer_ipv6 = 0;
  } else if (q->type == EVDNS_TYPE_AAAA) {
    entry_conn->entry_cfg.ipv4_traffic = 0;
    entry_conn->entry_cfg.ipv6_traffic = 1;
    entry_conn->entry_cfg.prefer_ipv6 = 1;
  }

  strlcpy(entry_conn->socks_request->address, q->name,
          sizeof(entry_conn->socks_request->address));

  entry_conn->socks_request->listener_type = listener->base_.type;
  entry_conn->dns_server_request = req;
  entry_conn->entry_cfg.isolation_flags = listener->entry_cfg.isolation_flags;
  entry_conn->entry_cfg.session_group = listener->entry_cfg.session_group;
  entry_conn->nym_epoch = get_signewnym_epoch();

  if (connection_add(ENTRY_TO_CONN(entry_conn)) < 0) {
    log_warn(LD_APP, "Couldn't register dummy connection for DNS request");
    evdns_server_request_respond(req, DNS_ERR_SERVERFAILED);
    connection_free(ENTRY_TO_CONN(entry_conn));
    return;
  }

  control_event_stream_status(entry_conn, STREAM_EVENT_NEW_RESOLVE, 0);

  /* Now, unless a controller asked us to leave streams unattached,
  * throw the connection over to get rewritten (which will
  * answer it immediately if it's in the cache, or completely bogus, or
  * automapped), and then attached to a circuit. */
  log_info(LD_APP, "Passing request for %s to rewrite_and_attach.",
           escaped_safe_str_client(q->name));
  q_name = tor_strdup(q->name); /* q could be freed in rewrite_and_attach */
  connection_ap_rewrite_and_attach_if_allowed(entry_conn, NULL, NULL);
  /* Now, the connection is marked if it was bad. */

  log_info(LD_APP, "Passed request for %s to rewrite_and_attach_if_allowed.",
           escaped_safe_str_client(q_name));
  tor_free(q_name);
}
Example #6
0
/** Helper function: called whenever the client sends a resolve request to our
 * controller.  We need to eventually answer the request <b>req</b>.
 * Returns 0 if the controller will be getting (or has gotten) an event in
 * response; -1 if we couldn't launch the request.
 */
int
dnsserv_launch_request(const char *name, int reverse,
                       control_connection_t *control_conn)
{
  entry_connection_t *entry_conn;
  edge_connection_t *conn;
  char *q_name;

  /* Make a new dummy AP connection, and attach the request to it. */
  entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
  conn = ENTRY_TO_EDGE_CONN(entry_conn);
  CONNECTION_AP_EXPECT_NONPENDING(entry_conn);
  conn->base_.state = AP_CONN_STATE_RESOLVE_WAIT;

  tor_addr_copy(&TO_CONN(conn)->addr, &control_conn->base_.addr);
#ifdef AF_UNIX
  /*
   * The control connection can be AF_UNIX and if so tor_addr_to_str_dup will
   * unhelpfully say "<unknown address type>"; say "(Tor_internal)"
   * instead.
   */
  if (control_conn->base_.socket_family == AF_UNIX) {
    TO_CONN(conn)->port = 0;
    TO_CONN(conn)->address = tor_strdup("(Tor_internal)");
  } else {
    TO_CONN(conn)->port = control_conn->base_.port;
    TO_CONN(conn)->address = tor_addr_to_str_dup(&control_conn->base_.addr);
  }
#else
  TO_CONN(conn)->port = control_conn->base_.port;
  TO_CONN(conn)->address = tor_addr_to_str_dup(&control_conn->base_.addr);
#endif

  if (reverse)
    entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
  else
    entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE;

  conn->is_dns_request = 1;

  strlcpy(entry_conn->socks_request->address, name,
          sizeof(entry_conn->socks_request->address));

  entry_conn->socks_request->listener_type = CONN_TYPE_CONTROL_LISTENER;
  entry_conn->original_dest_address = tor_strdup(name);
  entry_conn->entry_cfg.session_group = SESSION_GROUP_CONTROL_RESOLVE;
  entry_conn->nym_epoch = get_signewnym_epoch();
  entry_conn->entry_cfg.isolation_flags = ISO_DEFAULT;

  if (connection_add(TO_CONN(conn))<0) {
    log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request");
    connection_free(TO_CONN(conn));
    return -1;
  }

  control_event_stream_status(entry_conn, STREAM_EVENT_NEW_RESOLVE, 0);

  /* Now, unless a controller asked us to leave streams unattached,
  * throw the connection over to get rewritten (which will
  * answer it immediately if it's in the cache, or completely bogus, or
  * automapped), and then attached to a circuit. */
  log_info(LD_APP, "Passing request for %s to rewrite_and_attach.",
           escaped_safe_str_client(name));
  q_name = tor_strdup(name); /* q could be freed in rewrite_and_attach */
  connection_ap_rewrite_and_attach_if_allowed(entry_conn, NULL, NULL);
  /* Now, the connection is marked if it was bad. */

  log_info(LD_APP, "Passed request for %s to rewrite_and_attach_if_allowed.",
           escaped_safe_str_client(q_name));
  tor_free(q_name);
  return 0;
}
Example #7
0
/* Tests for connection_edge_process_resolved_cell().

   The point of ..process_resolved_cell() is to handle an incoming cell
   on an entry connection, and call connection_mark_unattached_ap() and/or
   connection_ap_handshake_socks_resolved().
 */
static void
test_relaycell_resolved(void *arg)
{
  entry_connection_t *entryconn;
  edge_connection_t *edgeconn;
  cell_t cell;
  relay_header_t rh;
  int r;
  or_options_t *options = get_options_mutable();

#define SET_CELL(s) do {                                                \
    memset(&cell, 0, sizeof(cell));                                     \
    memset(&rh, 0, sizeof(rh));                                         \
    memcpy(cell.payload + RELAY_HEADER_SIZE, (s), sizeof((s))-1);       \
    rh.length = sizeof((s))-1;                                          \
    rh.command = RELAY_COMMAND_RESOLVED;                                \
  } while (0)
#define MOCK_RESET() do {                       \
    srm_ncalls = mum_ncalls = 0;                \
  } while (0)
#define ASSERT_MARK_CALLED(reason) do {         \
    tt_int_op(mum_ncalls, OP_EQ, 1);               \
    tt_ptr_op(mum_conn, OP_EQ, entryconn);         \
    tt_int_op(mum_endreason, OP_EQ, (reason));     \
  } while (0)
#define ASSERT_RESOLVED_CALLED(atype, answer, ttl, expires) do {  \
    tt_int_op(srm_ncalls, OP_EQ, 1);                                 \
    tt_ptr_op(srm_conn, OP_EQ, entryconn);                           \
    tt_int_op(srm_atype, OP_EQ, (atype));                            \
    if ((answer) != NULL) {                                          \
      tt_int_op(srm_alen, OP_EQ, sizeof(answer)-1);                  \
      tt_int_op(srm_alen, OP_LT, 512);                                \
      tt_int_op(srm_answer_is_set, OP_EQ, 1);                        \
      tt_mem_op(srm_answer, OP_EQ, answer, sizeof(answer)-1);        \
    } else {                                                      \
      tt_int_op(srm_answer_is_set, OP_EQ, 0);                        \
    }                                                             \
    tt_int_op(srm_ttl, OP_EQ, ttl);                                  \
    tt_i64_op(srm_expires, OP_EQ, expires);                          \
  } while (0)

  (void)arg;

  MOCK(connection_mark_unattached_ap_, mark_unattached_mock);
  MOCK(connection_ap_handshake_socks_resolved, socks_resolved_mock);

  options->ClientDNSRejectInternalAddresses = 0;

  SET_CELL(/* IPv4: 127.0.1.2, ttl 256 */
           "\x04\x04\x7f\x00\x01\x02\x00\x00\x01\x00"
           /* IPv4: 18.0.0.1, ttl 512 */
           "\x04\x04\x12\x00\x00\x01\x00\x00\x02\x00"
           /* IPv6: 2003::3, ttl 1024 */
           "\x06\x10"
           "\x20\x02\x00\x00\x00\x00\x00\x00"
           "\x00\x00\x00\x00\x00\x00\x00\x03"
           "\x00\x00\x04\x00");

  entryconn = entry_connection_new(CONN_TYPE_AP, AF_INET);
  edgeconn = ENTRY_TO_EDGE_CONN(entryconn);

  /* Try with connection in non-RESOLVE_WAIT state: cell gets ignored */
  MOCK_RESET();
  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
  tt_int_op(r, OP_EQ, 0);
  tt_int_op(srm_ncalls, OP_EQ, 0);
  tt_int_op(mum_ncalls, OP_EQ, 0);

  /* Now put it in the right state. */
  ENTRY_TO_CONN(entryconn)->state = AP_CONN_STATE_RESOLVE_WAIT;
  entryconn->socks_request->command = SOCKS_COMMAND_RESOLVE;
  entryconn->entry_cfg.ipv4_traffic = 1;
  entryconn->entry_cfg.ipv6_traffic = 1;
  entryconn->entry_cfg.prefer_ipv6 = 0;

  /* We prefer ipv4, so we should get the first ipv4 answer */
  MOCK_RESET();
  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
  tt_int_op(r, OP_EQ, 0);
  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_IPV4, "\x7f\x00\x01\x02", 256, -1);

  /* But we may be discarding private answers. */
  MOCK_RESET();
  options->ClientDNSRejectInternalAddresses = 1;
  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
  tt_int_op(r, OP_EQ, 0);
  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_IPV4, "\x12\x00\x00\x01", 512, -1);

  /* now prefer ipv6, and get the first ipv6 answer */
  entryconn->entry_cfg.prefer_ipv6 = 1;
  MOCK_RESET();
  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
  tt_int_op(r, OP_EQ, 0);
  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_IPV6,
                         "\x20\x02\x00\x00\x00\x00\x00\x00"
                         "\x00\x00\x00\x00\x00\x00\x00\x03",
                         1024, -1);

  /* With a cell that only has IPv4, we report IPv4 even if we prefer IPv6 */
  MOCK_RESET();
  SET_CELL("\x04\x04\x12\x00\x00\x01\x00\x00\x02\x00");
  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
  tt_int_op(r, OP_EQ, 0);
  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_IPV4, "\x12\x00\x00\x01", 512, -1);

  /* But if we don't allow IPv4, we report nothing if the cell contains only
   * ipv4 */
  MOCK_RESET();
  entryconn->entry_cfg.ipv4_traffic = 0;
  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
  tt_int_op(r, OP_EQ, 0);
  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_ERROR, NULL, -1, -1);

  /* If we wanted hostnames, we report nothing, since we only had IPs. */
  MOCK_RESET();
  entryconn->entry_cfg.ipv4_traffic = 1;
  entryconn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
  tt_int_op(r, OP_EQ, 0);
  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_ERROR, NULL, -1, -1);

  /* A hostname cell is fine though. */
  MOCK_RESET();
  SET_CELL("\x00\x0fwww.example.com\x00\x01\x00\x00");
  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
  tt_int_op(r, OP_EQ, 0);
  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_HOSTNAME, "www.example.com", 65536, -1);

  /* error on malformed cell */
  MOCK_RESET();
  entryconn->socks_request->command = SOCKS_COMMAND_RESOLVE;
  SET_CELL("\x04\x04\x01\x02\x03\x04"); /* no ttl */
  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
  tt_int_op(r, OP_EQ, 0);
  ASSERT_MARK_CALLED(END_STREAM_REASON_TORPROTOCOL);
  tt_int_op(srm_ncalls, OP_EQ, 0);

  /* error on all addresses private */
  MOCK_RESET();
  SET_CELL(/* IPv4: 127.0.1.2, ttl 256 */
           "\x04\x04\x7f\x00\x01\x02\x00\x00\x01\x00"
           /* IPv4: 192.168.1.1, ttl 256 */
           "\x04\x04\xc0\xa8\x01\x01\x00\x00\x01\x00");
  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
  tt_int_op(r, OP_EQ, 0);
  ASSERT_MARK_CALLED(END_STREAM_REASON_TORPROTOCOL);
  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_ERROR_TRANSIENT, NULL, 0, TIME_MAX);

  /* Legit error code */
  MOCK_RESET();
  SET_CELL("\xf0\x15" "quiet and meaningless" "\x00\x00\x0f\xff");
  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
  tt_int_op(r, OP_EQ, 0);
  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_ERROR_TRANSIENT, NULL, -1, -1);

 done:
  UNMOCK(connection_mark_unattached_ap_);
  UNMOCK(connection_ap_handshake_socks_resolved);
}
Example #8
0
static void
test_circbw_relay(void *arg)
{
  cell_t cell;
  relay_header_t rh;
  tor_addr_t addr;
  edge_connection_t *edgeconn;
  entry_connection_t *entryconn1=NULL;
  origin_circuit_t *circ;
  int delivered = 0;
  int overhead = 0;

  (void)arg;

  MOCK(connection_mark_unattached_ap_, mock_connection_mark_unattached_ap_);
  MOCK(connection_start_reading, mock_start_reading);
  MOCK(connection_mark_for_close_internal_, mock_mark_for_close);
  MOCK(relay_send_command_from_edge_, mock_send_command);
  MOCK(circuit_mark_for_close_, mock_mark_circ_for_close);

  circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_C_GENERAL, 0);
  circ->cpath->state = CPATH_STATE_AWAITING_KEYS;
  circ->cpath->deliver_window = CIRCWINDOW_START;

  entryconn1 = fake_entry_conn(circ, 1);
  edgeconn = ENTRY_TO_EDGE_CONN(entryconn1);

  /* Stream id 0: Not counted */
  PACK_CELL(0, RELAY_COMMAND_END, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Stream id 1: Counted */
  PACK_CELL(1, RELAY_COMMAND_END, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_COUNTED_BW();

  /* Properly formatted connect cell: counted */
  PACK_CELL(1, RELAY_COMMAND_CONNECTED, "Data1234");
  tor_addr_parse(&addr, "30.40.50.60");
  rh.length = connected_cell_format_payload(cell.payload+RELAY_HEADER_SIZE,
                                            &addr, 1024);
  relay_header_pack((uint8_t*)&cell.payload, &rh);                    \
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_COUNTED_BW();

  /* Properly formatted resolved cell in correct state: counted */
  edgeconn->base_.state = AP_CONN_STATE_RESOLVE_WAIT;
  entryconn1->socks_request->command = SOCKS_COMMAND_RESOLVE;
  edgeconn->on_circuit = TO_CIRCUIT(circ);
  PACK_CELL(1, RELAY_COMMAND_RESOLVED,
            "\x04\x04\x12\x00\x00\x01\x00\x00\x02\x00");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_COUNTED_BW();

  edgeconn->base_.state = AP_CONN_STATE_OPEN;
  entryconn1->socks_request->has_finished = 1;

  /* Connected cell after open: not counted */
  PACK_CELL(1, RELAY_COMMAND_CONNECTED, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Resolved cell after open: not counted */
  PACK_CELL(1, RELAY_COMMAND_RESOLVED, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Drop cell: not counted */
  PACK_CELL(1, RELAY_COMMAND_DROP, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Data cell on stream 0: not counted */
  PACK_CELL(0, RELAY_COMMAND_DATA, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Data cell on open connection: counted */
  ENTRY_TO_CONN(entryconn1)->marked_for_close = 0;
  PACK_CELL(1, RELAY_COMMAND_DATA, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_COUNTED_BW();

  /* Empty Data cell on open connection: not counted */
  ENTRY_TO_CONN(entryconn1)->marked_for_close = 0;
  PACK_CELL(1, RELAY_COMMAND_DATA, "");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Sendme on valid stream: counted */
  edgeconn->package_window -= STREAMWINDOW_INCREMENT;
  ENTRY_TO_CONN(entryconn1)->outbuf_flushlen = 0;
  PACK_CELL(1, RELAY_COMMAND_SENDME, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_COUNTED_BW();

  /* Sendme on valid stream with full window: not counted */
  ENTRY_TO_CONN(entryconn1)->outbuf_flushlen = 0;
  PACK_CELL(1, RELAY_COMMAND_SENDME, "Data1234");
  edgeconn->package_window = STREAMWINDOW_START;
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Sendme on unknown stream: not counted */
  ENTRY_TO_CONN(entryconn1)->outbuf_flushlen = 0;
  PACK_CELL(1, RELAY_COMMAND_SENDME, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Sendme on circuit with full window: not counted */
  PACK_CELL(0, RELAY_COMMAND_SENDME, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Sendme on circuit with non-full window: counted */
  PACK_CELL(0, RELAY_COMMAND_SENDME, "Data1234");
  circ->cpath->package_window = 900;
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_COUNTED_BW();

  /* Invalid extended cell: not counted */
  PACK_CELL(1, RELAY_COMMAND_EXTENDED2, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Invalid extended cell: not counted */
  PACK_CELL(1, RELAY_COMMAND_EXTENDED, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Invalid HS cell: not counted */
  PACK_CELL(1, RELAY_COMMAND_ESTABLISH_INTRO, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* "Valid" HS cell in expected state: counted */
  TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND;
  PACK_CELL(1, RELAY_COMMAND_RENDEZVOUS_ESTABLISHED, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_COUNTED_BW();

  /* End cell on non-closed connection: counted */
  PACK_CELL(1, RELAY_COMMAND_END, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  ASSERT_COUNTED_BW();

  /* End cell on connection that already got one: not counted */
  PACK_CELL(1, RELAY_COMMAND_END, "Data1234");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Simulate closed stream on entryconn, then test: */
  if (!subtest_circbw_halfclosed(circ, 2))
    goto done;

  circ->base_.purpose = CIRCUIT_PURPOSE_PATH_BIAS_TESTING;
  if (!subtest_circbw_halfclosed(circ, 6))
    goto done;

  /* Path bias: truncated */
  tt_int_op(circ->base_.marked_for_close, OP_EQ, 0);
  PACK_CELL(0, RELAY_COMMAND_TRUNCATED, "Data1234");
  pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  tt_int_op(circ->base_.marked_for_close, OP_EQ, 1);

 done:
  UNMOCK(connection_start_reading);
  UNMOCK(connection_mark_unattached_ap_);
  UNMOCK(connection_mark_for_close_internal_);
  UNMOCK(relay_send_command_from_edge_);
  UNMOCK(circuit_mark_for_close_);
  circuit_free_(TO_CIRCUIT(circ));
  connection_free_minimal(ENTRY_TO_CONN(entryconn1));
}
Example #9
0
static void
subtest_halfstream_insertremove(int num)
{
  origin_circuit_t *circ =
      helper_create_origin_circuit(CIRCUIT_PURPOSE_C_GENERAL, 0);
  edge_connection_t *edgeconn;
  entry_connection_t *entryconn;
  streamid_t *streams = tor_malloc_zero(num*sizeof(streamid_t));
  int i = 0;

  circ->cpath->state = CPATH_STATE_AWAITING_KEYS;
  circ->cpath->deliver_window = CIRCWINDOW_START;

  entryconn = fake_entry_conn(circ, 23);
  edgeconn = ENTRY_TO_EDGE_CONN(entryconn);

  /* Explicity test all operations on an absent stream list */
  tt_int_op(connection_half_edge_is_valid_data(circ->half_streams,
            23), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_connected(circ->half_streams,
            23), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_sendme(circ->half_streams,
            23), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_resolved(circ->half_streams,
            23), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_end(circ->half_streams,
            23), OP_EQ, 0);

  /* Insert a duplicate element; verify that other elements absent;
   * ensure removing it once works */
  edgeconn->stream_id = 23;
  connection_half_edge_add(edgeconn, circ);
  connection_half_edge_add(edgeconn, circ);
  connection_half_edge_add(edgeconn, circ);

  /* Verify that other elements absent */
  tt_int_op(connection_half_edge_is_valid_data(circ->half_streams,
            22), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_connected(circ->half_streams,
            22), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_sendme(circ->half_streams,
            22), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_resolved(circ->half_streams,
            22), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_end(circ->half_streams,
            22), OP_EQ, 0);

  tt_int_op(connection_half_edge_is_valid_data(circ->half_streams,
            24), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_connected(circ->half_streams,
            24), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_sendme(circ->half_streams,
            24), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_resolved(circ->half_streams,
            24), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_end(circ->half_streams,
            24), OP_EQ, 0);

  /* Verify we only remove it once */
  tt_int_op(connection_half_edge_is_valid_end(circ->half_streams,
            23), OP_EQ, 1);
  tt_int_op(connection_half_edge_is_valid_end(circ->half_streams,
            23), OP_EQ, 0);

  halfstream_insert(circ, edgeconn, streams, num, 1);

  /* Remove half of them */
  for (i = 0; i < num/2; i++) {
    tt_int_op(connection_half_edge_is_valid_end(circ->half_streams,
                                                streams[i]),
              OP_EQ, 1);
  }

  /* Verify first half of list is gone */
  for (i = 0; i < num/2; i++) {
    tt_ptr_op(connection_half_edge_find_stream_id(circ->half_streams,
                                                  streams[i]),
              OP_EQ, NULL);
  }

  /* Verify second half of list is present */
  for (; i < num; i++) {
    tt_ptr_op(connection_half_edge_find_stream_id(circ->half_streams,
                                                  streams[i]),
              OP_NE, NULL);
  }

  /* Remove other half. Verify list is empty. */
  for (i = num/2; i < num; i++) {
    tt_int_op(connection_half_edge_is_valid_end(circ->half_streams,
                                                streams[i]),
              OP_EQ, 1);
  }
  tt_int_op(smartlist_len(circ->half_streams), OP_EQ, 0);

  /* Explicity test all operations on an empty stream list */
  tt_int_op(connection_half_edge_is_valid_data(circ->half_streams,
            23), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_connected(circ->half_streams,
            23), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_sendme(circ->half_streams,
            23), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_resolved(circ->half_streams,
            23), OP_EQ, 0);
  tt_int_op(connection_half_edge_is_valid_end(circ->half_streams,
            23), OP_EQ, 0);

  /* For valgrind, leave some around then free the circ */
  halfstream_insert(circ, edgeconn, NULL, 10, 0);

 done:
  tor_free(streams);
  circuit_free_(TO_CIRCUIT(circ));
  connection_free_minimal(ENTRY_TO_CONN(entryconn));
}
Example #10
0
static int
subtest_circbw_halfclosed(origin_circuit_t *circ, streamid_t init_id)
{
  cell_t cell;
  relay_header_t rh;
  edge_connection_t *edgeconn;
  entry_connection_t *entryconn2=NULL;
  entry_connection_t *entryconn3=NULL;
  entry_connection_t *entryconn4=NULL;
  int delivered = circ->n_delivered_read_circ_bw;
  int overhead = circ->n_overhead_read_circ_bw;

  /* Make new entryconns */
  entryconn2 = fake_entry_conn(circ, init_id);
  entryconn2->socks_request->has_finished = 1;
  entryconn3 = fake_entry_conn(circ, init_id+1);
  entryconn3->socks_request->has_finished = 1;
  entryconn4 = fake_entry_conn(circ, init_id+2);
  entryconn4->socks_request->has_finished = 1;
  edgeconn = ENTRY_TO_EDGE_CONN(entryconn2);
  edgeconn->package_window = 23;
  edgeconn->base_.state = AP_CONN_STATE_OPEN;

  int data_cells = edgeconn->deliver_window;
  int sendme_cells = (STREAMWINDOW_START-edgeconn->package_window)
                             /STREAMWINDOW_INCREMENT;
  ENTRY_TO_CONN(entryconn2)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn2)->outbuf_flushlen = 0;
  connection_edge_reached_eof(edgeconn);

  /* Data cell not in the half-opened list */
  PACK_CELL(4000, RELAY_COMMAND_DATA, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                       circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Sendme cell not in the half-opened list */
  PACK_CELL(4000, RELAY_COMMAND_SENDME, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Connected cell not in the half-opened list */
  PACK_CELL(4000, RELAY_COMMAND_CONNECTED, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Resolved cell not in the half-opened list */
  PACK_CELL(4000, RELAY_COMMAND_RESOLVED, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Connected cell: not counted -- we were open */
  edgeconn = ENTRY_TO_EDGE_CONN(entryconn2);
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_CONNECTED, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* DATA cells up to limit */
  while (data_cells > 0) {
    ENTRY_TO_CONN(entryconn2)->marked_for_close = 0;
    ENTRY_TO_CONN(entryconn2)->outbuf_flushlen = 0;
    PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_DATA, "Data1234");
    if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
      pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
    else
      connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                       circ->cpath);
    ASSERT_COUNTED_BW();
    data_cells--;
  }
  ENTRY_TO_CONN(entryconn2)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn2)->outbuf_flushlen = 0;
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_DATA, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* SENDME cells up to limit */
  while (sendme_cells > 0) {
    ENTRY_TO_CONN(entryconn2)->marked_for_close = 0;
    ENTRY_TO_CONN(entryconn2)->outbuf_flushlen = 0;
    PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_SENDME, "Data1234");
    if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
      pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
    else
      connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                       circ->cpath);
    ASSERT_COUNTED_BW();
    sendme_cells--;
  }
  ENTRY_TO_CONN(entryconn2)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn2)->outbuf_flushlen = 0;
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_SENDME, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Only one END cell */
  ENTRY_TO_CONN(entryconn2)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn2)->outbuf_flushlen = 0;
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_END, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_COUNTED_BW();

  ENTRY_TO_CONN(entryconn2)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn2)->outbuf_flushlen = 0;
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_END, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  edgeconn = ENTRY_TO_EDGE_CONN(entryconn3);
  edgeconn->base_.state = AP_CONN_STATE_OPEN;
  ENTRY_TO_CONN(entryconn3)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn3)->outbuf_flushlen = 0;
  /* sendme cell on open entryconn with full window */
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_SENDME, "Data1234");
  int ret =
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
                                     circ->cpath);
  tt_int_op(ret, OP_EQ, -END_CIRC_REASON_TORPROTOCOL);
  ASSERT_UNCOUNTED_BW();

  /* connected cell on a after EOF */
  ENTRY_TO_CONN(entryconn3)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn3)->outbuf_flushlen = 0;
  edgeconn->base_.state = AP_CONN_STATE_CONNECT_WAIT;
  connection_edge_reached_eof(edgeconn);
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_CONNECTED, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ),  NULL,
                                     circ->cpath);
  ASSERT_COUNTED_BW();

  ENTRY_TO_CONN(entryconn3)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn3)->outbuf_flushlen = 0;
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_CONNECTED, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ),  NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* DATA and SENDME after END cell */
  ENTRY_TO_CONN(entryconn3)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn3)->outbuf_flushlen = 0;
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_END, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ),  NULL,
                                     circ->cpath);
  ASSERT_COUNTED_BW();

  ENTRY_TO_CONN(entryconn3)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn3)->outbuf_flushlen = 0;
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_SENDME, "Data1234");
  ret =
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  tt_int_op(ret, OP_NE, -END_CIRC_REASON_TORPROTOCOL);
  ASSERT_UNCOUNTED_BW();

  ENTRY_TO_CONN(entryconn3)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn3)->outbuf_flushlen = 0;
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_DATA, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Resolved: 1 counted, more not */
  edgeconn = ENTRY_TO_EDGE_CONN(entryconn4);
  entryconn4->socks_request->command = SOCKS_COMMAND_RESOLVE;
  edgeconn->base_.state = AP_CONN_STATE_RESOLVE_WAIT;
  edgeconn->on_circuit = TO_CIRCUIT(circ);
  ENTRY_TO_CONN(entryconn4)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn4)->outbuf_flushlen = 0;
  connection_edge_reached_eof(edgeconn);

  ENTRY_TO_CONN(entryconn4)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn4)->outbuf_flushlen = 0;
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_RESOLVED,
            "\x04\x04\x12\x00\x00\x01\x00\x00\x02\x00");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_COUNTED_BW();

  ENTRY_TO_CONN(entryconn4)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn4)->outbuf_flushlen = 0;
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_RESOLVED,
            "\x04\x04\x12\x00\x00\x01\x00\x00\x02\x00");
  connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* Data not counted after resolved */
  ENTRY_TO_CONN(entryconn4)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn4)->outbuf_flushlen = 0;
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_DATA, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  /* End not counted after resolved */
  ENTRY_TO_CONN(entryconn4)->marked_for_close = 0;
  ENTRY_TO_CONN(entryconn4)->outbuf_flushlen = 0;
  PACK_CELL(edgeconn->stream_id, RELAY_COMMAND_END, "Data1234");
  if (circ->base_.purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
    pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell);
  else
    connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
                                     circ->cpath);
  ASSERT_UNCOUNTED_BW();

  connection_free_minimal(ENTRY_TO_CONN(entryconn2));
  connection_free_minimal(ENTRY_TO_CONN(entryconn3));
  connection_free_minimal(ENTRY_TO_CONN(entryconn4));
  return 1;
 done:
  connection_free_minimal(ENTRY_TO_CONN(entryconn2));
  connection_free_minimal(ENTRY_TO_CONN(entryconn3));
  connection_free_minimal(ENTRY_TO_CONN(entryconn4));
  return 0;
}