Commit 4b30ae15 authored by Nick Mathewson's avatar Nick Mathewson 🦀
Browse files

Add support for HTTP Connect tunnels

parent eda79c2f
Loading
Loading
Loading
Loading
+14 −1
Original line number Diff line number Diff line
@@ -372,6 +372,7 @@ static config_var_t option_vars_[] = {
  V(HTTPProxyAuthenticator,      STRING,   NULL),
  V(HTTPSProxy,                  STRING,   NULL),
  V(HTTPSProxyAuthenticator,     STRING,   NULL),
  VPORT(HTTPTunnelPort),
  V(IPv6Exit,                    BOOL,     "0"),
  VAR("ServerTransportPlugin",   LINELIST, ServerTransportPlugin,  NULL),
  V(ServerTransportListenAddr,   LINELIST, NULL),
@@ -2910,7 +2911,8 @@ options_validate_single_onion(or_options_t *options, char **msg)
  const int client_port_set = (options->SocksPort_set ||
                               options->TransPort_set ||
                               options->NATDPort_set ||
                               options->DNSPort_set);
                               options->DNSPort_set ||
                               options->HTTPTunnelPort_set);
  if (rend_service_non_anonymous_mode_enabled(options) && client_port_set &&
      !options->Tor2webMode) {
    REJECT("HiddenServiceNonAnonymousMode is incompatible with using Tor as "
@@ -6976,6 +6978,15 @@ parse_ports(or_options_t *options, int validate_only,
    *msg = tor_strdup("Invalid NatdPort configuration");
    goto err;
  }
  if (parse_port_config(ports,
                        options->HTTPTunnelPort_lines,
                        "HTTP Tunnel", CONN_TYPE_AP_HTTP_CONNECT_LISTENER,
                        "127.0.0.1", 0,
                        ((validate_only ? 0 : CL_PORT_WARN_NONLOCAL)
                         | CL_PORT_TAKES_HOSTNAMES | gw_flag)) < 0) {
    *msg = tor_strdup("Invalid HTTPTunnelPort configuration");
    goto err;
  }
  {
    unsigned control_port_flags = CL_PORT_NO_STREAM_OPTIONS |
      CL_PORT_WARN_NONLOCAL;
@@ -7053,6 +7064,8 @@ parse_ports(or_options_t *options, int validate_only,
    !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER, 1);
  options->NATDPort_set =
    !! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER, 1);
  options->HTTPTunnelPort_set =
    !! count_real_listeners(ports, CONN_TYPE_AP_HTTP_CONNECT_LISTENER, 1);
  /* Use options->ControlSocket to test if a control socket is set */
  options->ControlPort_set =
    !! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER, 0);
+7 −1
Original line number Diff line number Diff line
@@ -158,7 +158,8 @@ static smartlist_t *outgoing_addrs = NULL;
    case CONN_TYPE_CONTROL_LISTENER: \
    case CONN_TYPE_AP_TRANS_LISTENER: \
    case CONN_TYPE_AP_NATD_LISTENER: \
    case CONN_TYPE_AP_DNS_LISTENER
    case CONN_TYPE_AP_DNS_LISTENER: \
    case CONN_TYPE_AP_HTTP_CONNECT_LISTENER

/**************************************************************/

@@ -185,6 +186,7 @@ conn_type_to_string(int type)
    case CONN_TYPE_CONTROL: return "Control";
    case CONN_TYPE_EXT_OR: return "Extended OR";
    case CONN_TYPE_EXT_OR_LISTENER: return "Extended OR listener";
    case CONN_TYPE_AP_HTTP_CONNECT_LISTENER: return "HTTP tunnel listener";
    default:
      log_warn(LD_BUG, "unknown connection type %d", type);
      tor_snprintf(buf, sizeof(buf), "unknown [%d]", type);
@@ -1702,6 +1704,8 @@ connection_init_accepted_conn(connection_t *conn,
          TO_ENTRY_CONN(conn)->is_transparent_ap = 1;
          conn->state = AP_CONN_STATE_NATD_WAIT;
          break;
        case CONN_TYPE_AP_HTTP_CONNECT_LISTENER:
          conn->state = AP_CONN_STATE_HTTP_CONNECT_WAIT;
      }
      break;
    case CONN_TYPE_DIR:
@@ -3394,6 +3398,7 @@ connection_handle_read_impl(connection_t *conn)
    case CONN_TYPE_AP_LISTENER:
    case CONN_TYPE_AP_TRANS_LISTENER:
    case CONN_TYPE_AP_NATD_LISTENER:
    case CONN_TYPE_AP_HTTP_CONNECT_LISTENER:
      return connection_handle_listener_read(conn, CONN_TYPE_AP);
    case CONN_TYPE_DIR_LISTENER:
      return connection_handle_listener_read(conn, CONN_TYPE_DIR);
@@ -4286,6 +4291,7 @@ connection_is_listener(connection_t *conn)
      conn->type == CONN_TYPE_AP_TRANS_LISTENER ||
      conn->type == CONN_TYPE_AP_DNS_LISTENER ||
      conn->type == CONN_TYPE_AP_NATD_LISTENER ||
      conn->type == CONN_TYPE_AP_HTTP_CONNECT_LISTENER ||
      conn->type == CONN_TYPE_DIR_LISTENER ||
      conn->type == CONN_TYPE_CONTROL_LISTENER)
    return 1;
+104 −1
Original line number Diff line number Diff line
@@ -127,6 +127,7 @@

static int connection_ap_handshake_process_socks(entry_connection_t *conn);
static int connection_ap_process_natd(entry_connection_t *conn);
static int connection_ap_process_http_connect(entry_connection_t *conn);
static int connection_exit_connect_dir(edge_connection_t *exitconn);
static int consider_plaintext_ports(entry_connection_t *conn, uint16_t port);
static int connection_ap_supports_optimistic_data(const entry_connection_t *);
@@ -237,6 +238,11 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
        return -1;
      }
      return 0;
    case AP_CONN_STATE_HTTP_CONNECT_WAIT:
      if (connection_ap_process_http_connect(EDGE_TO_ENTRY_CONN(conn)) < 0) {
        return -1;
      }
      return 0;
    case AP_CONN_STATE_OPEN:
    case EXIT_CONN_STATE_OPEN:
      if (connection_edge_package_raw_inbuf(conn, package_partial, NULL) < 0) {
@@ -486,6 +492,7 @@ connection_edge_finished_flushing(edge_connection_t *conn)
    case AP_CONN_STATE_CONNECT_WAIT:
    case AP_CONN_STATE_CONTROLLER_WAIT:
    case AP_CONN_STATE_RESOLVE_WAIT:
    case AP_CONN_STATE_HTTP_CONNECT_WAIT:
      return 0;
    default:
      log_warn(LD_BUG, "Called in unexpected state %d.",conn->base_.state);
@@ -2349,6 +2356,95 @@ connection_ap_process_natd(entry_connection_t *conn)
  return connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL);
}

/** Called on an HTTP CONNECT entry connection when some bytes have arrived,
 * but we have not yet received a full HTTP CONNECT request.  Try to parse an
 * HTTP CONNECT request from the connection's inbuf.  On success, set up the
 * connection's socks_request field and try to attach the connection.  On
 * failure, send an HTTP reply, and mark the connection.
 */
static int
connection_ap_process_http_connect(entry_connection_t *conn)
{
  if (BUG(ENTRY_TO_CONN(conn)->state != AP_CONN_STATE_HTTP_CONNECT_WAIT))
    return -1;

  char *headers = NULL, *body = NULL;
  char *command = NULL, *addrport = NULL;
  char *addr = NULL;
  size_t bodylen = 0;

  const char *errmsg = NULL;
  int rv = 0;

  const int http_status =
    fetch_from_buf_http(ENTRY_TO_CONN(conn)->inbuf, &headers, 8192,
                        &body, &bodylen, 1024, 0);
  if (http_status < 0) {
    /* Bad http status */
    errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
    goto err;
  } else if (http_status == 0) {
    /* no HTTP request yet. */
    goto done;
  }

  const int cmd_status = parse_http_command(headers, &command, &addrport);
  if (cmd_status < 0) {
    errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
    goto err;
  }
  tor_assert(command);
  tor_assert(addrport);
  if (strcasecmp(command, "connect")) {
    errmsg = "HTTP/1.0 405 Method Not Allowed\r\n\r\n";
    goto err;
  }

  tor_assert(conn->socks_request);
  socks_request_t *socks = conn->socks_request;
  uint16_t port;
  if (tor_addr_port_split(LOG_WARN, addrport, &addr, &port) < 0) {
    errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
    goto err;
  }
  if (strlen(addr) >= MAX_SOCKS_ADDR_LEN) {
    errmsg = "HTTP/1.0 414 Request-URI Too Long\r\n\r\n";
    goto err;
  }

  /* XXXX Look at headers */

  socks->command = SOCKS_COMMAND_CONNECT;
  socks->listener_type = CONN_TYPE_AP_HTTP_CONNECT_LISTENER;
  strlcpy(socks->address, addr, sizeof(socks->address));
  socks->port = port;

  control_event_stream_status(conn, STREAM_EVENT_NEW, 0);

  rv = connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL);

  // XXXX send a "100 Continue" message?

  goto done;

 err:
  if (BUG(errmsg == NULL))
    errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
  log_warn(LD_EDGE, "Saying %s", escaped(errmsg));
  connection_write_to_buf(errmsg, strlen(errmsg), ENTRY_TO_CONN(conn));
  connection_mark_unattached_ap(conn,
                                END_STREAM_REASON_HTTPPROTOCOL|
                                END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);

 done:
  tor_free(headers);
  tor_free(body);
  tor_free(command);
  tor_free(addrport);
  tor_free(addr);
  return rv;
}

/** Iterate over the two bytes of stream_id until we get one that is not
 * already in use; return it. Return 0 if can't get a unique stream_id.
 */
@@ -2972,7 +3068,14 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
    conn->socks_request->has_finished = 1;
    return;
  }
  if (conn->socks_request->socks_version == 4) {
  if (conn->socks_request->listener_type ==
       CONN_TYPE_AP_HTTP_CONNECT_LISTENER) {
    const char *response = end_reason_to_http_connect_response_line(endreason);
    if (!response) {
      response = "HTTP/1.0 400 Bad Request\r\n\r\n";
    }
    connection_write_to_buf(response, strlen(response), ENTRY_TO_CONN(conn));
  } else if (conn->socks_request->socks_version == 4) {
    memset(buf,0,SOCKS4_NETWORK_LEN);
    buf[1] = (status==SOCKS5_SUCCEEDED ? SOCKS4_GRANTED : SOCKS4_REJECT);
    /* leave version, destport, destip zero */
+2 −1
Original line number Diff line number Diff line
@@ -1683,7 +1683,8 @@ any_client_port_set(const or_options_t *options)
          options->TransPort_set ||
          options->NATDPort_set ||
          options->ControlPort_set ||
          options->DNSPort_set);
          options->DNSPort_set ||
          options->HTTPTunnelPort_set);
}

/**
+15 −3
Original line number Diff line number Diff line
@@ -226,8 +226,10 @@ typedef enum {
#define CONN_TYPE_EXT_OR 16
/** Type for sockets listening for Extended ORPort connections. */
#define CONN_TYPE_EXT_OR_LISTENER 17
/** Type for sockets listening for HTTP CONNECT tunnel connections. */
#define CONN_TYPE_AP_HTTP_CONNECT_LISTENER 18

#define CONN_TYPE_MAX_ 17
#define CONN_TYPE_MAX_ 19
/* !!!! If _CONN_TYPE_MAX is ever over 31, we must grow the type field in
 * connection_t. */

@@ -348,7 +350,9 @@ typedef enum {
/** State for a transparent natd connection: waiting for original
 * destination. */
#define AP_CONN_STATE_NATD_WAIT 12
#define AP_CONN_STATE_MAX_ 12
/** State for an HTTP tunnel: waiting for an HTTP CONNECT command. */
#define AP_CONN_STATE_HTTP_CONNECT_WAIT 13
#define AP_CONN_STATE_MAX_ 13

/** True iff the AP_CONN_STATE_* value <b>s</b> means that the corresponding
 * edge connection is not attached to any circuit. */
@@ -645,6 +649,10 @@ typedef enum {
/** The target address is in a private network (like 127.0.0.1 or 10.0.0.1);
 * you don't want to do that over a randomly chosen exit */
#define END_STREAM_REASON_PRIVATE_ADDR 262
/** This is an HTTP tunnel connection and the client used or misused HTTP in a
 * way we can't handle.
 */
#define END_STREAM_REASON_HTTPPROTOCOL 263

/** Bitwise-and this value with endreason to mask out all flags. */
#define END_STREAM_REASON_MASK 511
@@ -3693,6 +3701,8 @@ typedef struct {
  } TransProxyType_parsed;
  config_line_t *NATDPort_lines; /**< Ports to listen on for transparent natd
                            * connections. */
  /** Ports to listen on for HTTP Tunnel connections. */
  config_line_t *HTTPTunnelPort_lines;
  config_line_t *ControlPort_lines; /**< Ports to listen on for control
                               * connections. */
  config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on
@@ -3719,7 +3729,8 @@ typedef struct {
   * configured in one of the _lines options above.
   * For client ports, also true if there is a unix socket configured.
   * If you are checking for client ports, you may want to use:
   *   SocksPort_set || TransPort_set || NATDPort_set || DNSPort_set
   *   SocksPort_set || TransPort_set || NATDPort_set || DNSPort_set ||
   *   HTTPTunnelPort_set
   * rather than SocksPort_set.
   *
   * @{
@@ -3732,6 +3743,7 @@ typedef struct {
  unsigned int DirPort_set : 1;
  unsigned int DNSPort_set : 1;
  unsigned int ExtORPort_set : 1;
  unsigned int HTTPTunnelPort_set : 1;
  /**@}*/

  int AssumeReachable; /**< Whether to publish our descriptor regardless. */
Loading