Blame samples/socks5-proxy/client.c

Packit b5b901
/* Copyright StrongLoop, Inc. All rights reserved.
Packit b5b901
 *
Packit b5b901
 * Permission is hereby granted, free of charge, to any person obtaining a copy
Packit b5b901
 * of this software and associated documentation files (the "Software"), to
Packit b5b901
 * deal in the Software without restriction, including without limitation the
Packit b5b901
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
Packit b5b901
 * sell copies of the Software, and to permit persons to whom the Software is
Packit b5b901
 * furnished to do so, subject to the following conditions:
Packit b5b901
 *
Packit b5b901
 * The above copyright notice and this permission notice shall be included in
Packit b5b901
 * all copies or substantial portions of the Software.
Packit b5b901
 *
Packit b5b901
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Packit b5b901
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Packit b5b901
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Packit b5b901
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Packit b5b901
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
Packit b5b901
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
Packit b5b901
 * IN THE SOFTWARE.
Packit b5b901
 */
Packit b5b901
Packit b5b901
#include "defs.h"
Packit b5b901
#include <errno.h>
Packit b5b901
#include <stdlib.h>
Packit b5b901
#include <string.h>
Packit b5b901
Packit b5b901
/* A connection is modeled as an abstraction on top of two simple state
Packit b5b901
 * machines, one for reading and one for writing.  Either state machine
Packit b5b901
 * is, when active, in one of three states: busy, done or stop; the fourth
Packit b5b901
 * and final state, dead, is an end state and only relevant when shutting
Packit b5b901
 * down the connection.  A short overview:
Packit b5b901
 *
Packit b5b901
 *                          busy                  done           stop
Packit b5b901
 *  ----------|---------------------------|--------------------|------|
Packit b5b901
 *  readable  | waiting for incoming data | have incoming data | idle |
Packit b5b901
 *  writable  | busy writing out data     | completed write    | idle |
Packit b5b901
 *
Packit b5b901
 * We could remove the done state from the writable state machine. For our
Packit b5b901
 * purposes, it's functionally equivalent to the stop state.
Packit b5b901
 *
Packit b5b901
 * When the connection with upstream has been established, the client_ctx
Packit b5b901
 * moves into a state where incoming data from the client is sent upstream
Packit b5b901
 * and vice versa, incoming data from upstream is sent to the client.  In
Packit b5b901
 * other words, we're just piping data back and forth.  See conn_cycle()
Packit b5b901
 * for details.
Packit b5b901
 *
Packit b5b901
 * An interesting deviation from libuv's I/O model is that reads are discrete
Packit b5b901
 * rather than continuous events.  In layman's terms, when a read operation
Packit b5b901
 * completes, the connection stops reading until further notice.
Packit b5b901
 *
Packit b5b901
 * The rationale for this approach is that we have to wait until the data
Packit b5b901
 * has been sent out again before we can reuse the read buffer.
Packit b5b901
 *
Packit b5b901
 * It also pleasingly unifies with the request model that libuv uses for
Packit b5b901
 * writes and everything else; libuv may switch to a request model for
Packit b5b901
 * reads in the future.
Packit b5b901
 */
Packit b5b901
enum conn_state {
Packit b5b901
  c_busy,  /* Busy; waiting for incoming data or for a write to complete. */
Packit b5b901
  c_done,  /* Done; read incoming data or write finished. */
Packit b5b901
  c_stop,  /* Stopped. */
Packit b5b901
  c_dead
Packit b5b901
};
Packit b5b901
Packit b5b901
/* Session states. */
Packit b5b901
enum sess_state {
Packit b5b901
  s_handshake,        /* Wait for client handshake. */
Packit b5b901
  s_handshake_auth,   /* Wait for client authentication data. */
Packit b5b901
  s_req_start,        /* Start waiting for request data. */
Packit b5b901
  s_req_parse,        /* Wait for request data. */
Packit b5b901
  s_req_lookup,       /* Wait for upstream hostname DNS lookup to complete. */
Packit b5b901
  s_req_connect,      /* Wait for uv_tcp_connect() to complete. */
Packit b5b901
  s_proxy_start,      /* Connected. Start piping data. */
Packit b5b901
  s_proxy,            /* Connected. Pipe data back and forth. */
Packit b5b901
  s_kill,             /* Tear down session. */
Packit b5b901
  s_almost_dead_0,    /* Waiting for finalizers to complete. */
Packit b5b901
  s_almost_dead_1,    /* Waiting for finalizers to complete. */
Packit b5b901
  s_almost_dead_2,    /* Waiting for finalizers to complete. */
Packit b5b901
  s_almost_dead_3,    /* Waiting for finalizers to complete. */
Packit b5b901
  s_almost_dead_4,    /* Waiting for finalizers to complete. */
Packit b5b901
  s_dead              /* Dead. Safe to free now. */
Packit b5b901
};
Packit b5b901
Packit b5b901
static void do_next(client_ctx *cx);
Packit b5b901
static int do_handshake(client_ctx *cx);
Packit b5b901
static int do_handshake_auth(client_ctx *cx);
Packit b5b901
static int do_req_start(client_ctx *cx);
Packit b5b901
static int do_req_parse(client_ctx *cx);
Packit b5b901
static int do_req_lookup(client_ctx *cx);
Packit b5b901
static int do_req_connect_start(client_ctx *cx);
Packit b5b901
static int do_req_connect(client_ctx *cx);
Packit b5b901
static int do_proxy_start(client_ctx *cx);
Packit b5b901
static int do_proxy(client_ctx *cx);
Packit b5b901
static int do_kill(client_ctx *cx);
Packit b5b901
static int do_almost_dead(client_ctx *cx);
Packit b5b901
static int conn_cycle(const char *who, conn *a, conn *b);
Packit b5b901
static void conn_timer_reset(conn *c);
Packit b5b901
static void conn_timer_expire(uv_timer_t *handle);
Packit b5b901
static void conn_getaddrinfo(conn *c, const char *hostname);
Packit b5b901
static void conn_getaddrinfo_done(uv_getaddrinfo_t *req,
Packit b5b901
                                  int status,
Packit b5b901
                                  struct addrinfo *ai);
Packit b5b901
static int conn_connect(conn *c);
Packit b5b901
static void conn_connect_done(uv_connect_t *req, int status);
Packit b5b901
static void conn_read(conn *c);
Packit b5b901
static void conn_read_done(uv_stream_t *handle,
Packit b5b901
                           ssize_t nread,
Packit b5b901
                           const uv_buf_t *buf);
Packit b5b901
static void conn_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf);
Packit b5b901
static void conn_write(conn *c, const void *data, unsigned int len);
Packit b5b901
static void conn_write_done(uv_write_t *req, int status);
Packit b5b901
static void conn_close(conn *c);
Packit b5b901
static void conn_close_done(uv_handle_t *handle);
Packit b5b901
Packit b5b901
/* |incoming| has been initialized by server.c when this is called. */
Packit b5b901
void client_finish_init(server_ctx *sx, client_ctx *cx) {
Packit b5b901
  conn *incoming;
Packit b5b901
  conn *outgoing;
Packit b5b901
Packit b5b901
  cx->sx = sx;
Packit b5b901
  cx->state = s_handshake;
Packit b5b901
  s5_init(&cx->parser);
Packit b5b901
Packit b5b901
  incoming = &cx->incoming;
Packit b5b901
  incoming->client = cx;
Packit b5b901
  incoming->result = 0;
Packit b5b901
  incoming->rdstate = c_stop;
Packit b5b901
  incoming->wrstate = c_stop;
Packit b5b901
  incoming->idle_timeout = sx->idle_timeout;
Packit b5b901
  CHECK(0 == uv_timer_init(sx->loop, &incoming->timer_handle));
Packit b5b901
Packit b5b901
  outgoing = &cx->outgoing;
Packit b5b901
  outgoing->client = cx;
Packit b5b901
  outgoing->result = 0;
Packit b5b901
  outgoing->rdstate = c_stop;
Packit b5b901
  outgoing->wrstate = c_stop;
Packit b5b901
  outgoing->idle_timeout = sx->idle_timeout;
Packit b5b901
  CHECK(0 == uv_tcp_init(cx->sx->loop, &outgoing->handle.tcp));
Packit b5b901
  CHECK(0 == uv_timer_init(cx->sx->loop, &outgoing->timer_handle));
Packit b5b901
Packit b5b901
  /* Wait for the initial packet. */
Packit b5b901
  conn_read(incoming);
Packit b5b901
}
Packit b5b901
Packit b5b901
/* This is the core state machine that drives the client <-> upstream proxy.
Packit b5b901
 * We move through the initial handshake and authentication steps first and
Packit b5b901
 * end up (if all goes well) in the proxy state where we're just proxying
Packit b5b901
 * data between the client and upstream.
Packit b5b901
 */
Packit b5b901
static void do_next(client_ctx *cx) {
Packit b5b901
  int new_state;
Packit b5b901
Packit b5b901
  ASSERT(cx->state != s_dead);
Packit b5b901
  switch (cx->state) {
Packit b5b901
    case s_handshake:
Packit b5b901
      new_state = do_handshake(cx);
Packit b5b901
      break;
Packit b5b901
    case s_handshake_auth:
Packit b5b901
      new_state = do_handshake_auth(cx);
Packit b5b901
      break;
Packit b5b901
    case s_req_start:
Packit b5b901
      new_state = do_req_start(cx);
Packit b5b901
      break;
Packit b5b901
    case s_req_parse:
Packit b5b901
      new_state = do_req_parse(cx);
Packit b5b901
      break;
Packit b5b901
    case s_req_lookup:
Packit b5b901
      new_state = do_req_lookup(cx);
Packit b5b901
      break;
Packit b5b901
    case s_req_connect:
Packit b5b901
      new_state = do_req_connect(cx);
Packit b5b901
      break;
Packit b5b901
    case s_proxy_start:
Packit b5b901
      new_state = do_proxy_start(cx);
Packit b5b901
      break;
Packit b5b901
    case s_proxy:
Packit b5b901
      new_state = do_proxy(cx);
Packit b5b901
      break;
Packit b5b901
    case s_kill:
Packit b5b901
      new_state = do_kill(cx);
Packit b5b901
      break;
Packit b5b901
    case s_almost_dead_0:
Packit b5b901
    case s_almost_dead_1:
Packit b5b901
    case s_almost_dead_2:
Packit b5b901
    case s_almost_dead_3:
Packit b5b901
    case s_almost_dead_4:
Packit b5b901
      new_state = do_almost_dead(cx);
Packit b5b901
      break;
Packit b5b901
    default:
Packit b5b901
      UNREACHABLE();
Packit b5b901
  }
Packit b5b901
  cx->state = new_state;
Packit b5b901
Packit b5b901
  if (cx->state == s_dead) {
Packit b5b901
    if (DEBUG_CHECKS) {
Packit b5b901
      memset(cx, -1, sizeof(*cx));
Packit b5b901
    }
Packit b5b901
    free(cx);
Packit b5b901
  }
Packit b5b901
}
Packit b5b901
Packit b5b901
static int do_handshake(client_ctx *cx) {
Packit b5b901
  unsigned int methods;
Packit b5b901
  conn *incoming;
Packit b5b901
  s5_ctx *parser;
Packit b5b901
  uint8_t *data;
Packit b5b901
  size_t size;
Packit b5b901
  int err;
Packit b5b901
Packit b5b901
  parser = &cx->parser;
Packit b5b901
  incoming = &cx->incoming;
Packit b5b901
  ASSERT(incoming->rdstate == c_done);
Packit b5b901
  ASSERT(incoming->wrstate == c_stop);
Packit b5b901
  incoming->rdstate = c_stop;
Packit b5b901
Packit b5b901
  if (incoming->result < 0) {
Packit b5b901
    pr_err("read error: %s", uv_strerror(incoming->result));
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  data = (uint8_t *) incoming->t.buf;
Packit b5b901
  size = (size_t) incoming->result;
Packit b5b901
  err = s5_parse(parser, &data, &size);
Packit b5b901
  if (err == s5_ok) {
Packit b5b901
    conn_read(incoming);
Packit b5b901
    return s_handshake;  /* Need more data. */
Packit b5b901
  }
Packit b5b901
Packit b5b901
  if (size != 0) {
Packit b5b901
    /* Could allow a round-trip saving shortcut here if the requested auth
Packit b5b901
     * method is S5_AUTH_NONE (provided unauthenticated traffic is allowed.)
Packit b5b901
     * Requires client support however.
Packit b5b901
     */
Packit b5b901
    pr_err("junk in handshake");
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  if (err != s5_auth_select) {
Packit b5b901
    pr_err("handshake error: %s", s5_strerror(err));
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  methods = s5_auth_methods(parser);
Packit b5b901
  if ((methods & S5_AUTH_NONE) && can_auth_none(cx->sx, cx)) {
Packit b5b901
    s5_select_auth(parser, S5_AUTH_NONE);
Packit b5b901
    conn_write(incoming, "\5\0", 2);  /* No auth required. */
Packit b5b901
    return s_req_start;
Packit b5b901
  }
Packit b5b901
Packit b5b901
  if ((methods & S5_AUTH_PASSWD) && can_auth_passwd(cx->sx, cx)) {
Packit b5b901
    /* TODO(bnoordhuis) Implement username/password auth. */
Packit b5b901
  }
Packit b5b901
Packit b5b901
  conn_write(incoming, "\5\377", 2);  /* No acceptable auth. */
Packit b5b901
  return s_kill;
Packit b5b901
}
Packit b5b901
Packit b5b901
/* TODO(bnoordhuis) Implement username/password auth. */
Packit b5b901
static int do_handshake_auth(client_ctx *cx) {
Packit b5b901
  UNREACHABLE();
Packit b5b901
  return do_kill(cx);
Packit b5b901
}
Packit b5b901
Packit b5b901
static int do_req_start(client_ctx *cx) {
Packit b5b901
  conn *incoming;
Packit b5b901
Packit b5b901
  incoming = &cx->incoming;
Packit b5b901
  ASSERT(incoming->rdstate == c_stop);
Packit b5b901
  ASSERT(incoming->wrstate == c_done);
Packit b5b901
  incoming->wrstate = c_stop;
Packit b5b901
Packit b5b901
  if (incoming->result < 0) {
Packit b5b901
    pr_err("write error: %s", uv_strerror(incoming->result));
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  conn_read(incoming);
Packit b5b901
  return s_req_parse;
Packit b5b901
}
Packit b5b901
Packit b5b901
static int do_req_parse(client_ctx *cx) {
Packit b5b901
  conn *incoming;
Packit b5b901
  conn *outgoing;
Packit b5b901
  s5_ctx *parser;
Packit b5b901
  uint8_t *data;
Packit b5b901
  size_t size;
Packit b5b901
  int err;
Packit b5b901
Packit b5b901
  parser = &cx->parser;
Packit b5b901
  incoming = &cx->incoming;
Packit b5b901
  outgoing = &cx->outgoing;
Packit b5b901
  ASSERT(incoming->rdstate == c_done);
Packit b5b901
  ASSERT(incoming->wrstate == c_stop);
Packit b5b901
  ASSERT(outgoing->rdstate == c_stop);
Packit b5b901
  ASSERT(outgoing->wrstate == c_stop);
Packit b5b901
  incoming->rdstate = c_stop;
Packit b5b901
Packit b5b901
  if (incoming->result < 0) {
Packit b5b901
    pr_err("read error: %s", uv_strerror(incoming->result));
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  data = (uint8_t *) incoming->t.buf;
Packit b5b901
  size = (size_t) incoming->result;
Packit b5b901
  err = s5_parse(parser, &data, &size);
Packit b5b901
  if (err == s5_ok) {
Packit b5b901
    conn_read(incoming);
Packit b5b901
    return s_req_parse;  /* Need more data. */
Packit b5b901
  }
Packit b5b901
Packit b5b901
  if (size != 0) {
Packit b5b901
    pr_err("junk in request %u", (unsigned) size);
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  if (err != s5_exec_cmd) {
Packit b5b901
    pr_err("request error: %s", s5_strerror(err));
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  if (parser->cmd == s5_cmd_tcp_bind) {
Packit b5b901
    /* Not supported but relatively straightforward to implement. */
Packit b5b901
    pr_warn("BIND requests are not supported.");
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  if (parser->cmd == s5_cmd_udp_assoc) {
Packit b5b901
    /* Not supported.  Might be hard to implement because libuv has no
Packit b5b901
     * functionality for detecting the MTU size which the RFC mandates.
Packit b5b901
     */
Packit b5b901
    pr_warn("UDP ASSOC requests are not supported.");
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
  ASSERT(parser->cmd == s5_cmd_tcp_connect);
Packit b5b901
Packit b5b901
  if (parser->atyp == s5_atyp_host) {
Packit b5b901
    conn_getaddrinfo(outgoing, (const char *) parser->daddr);
Packit b5b901
    return s_req_lookup;
Packit b5b901
  }
Packit b5b901
Packit b5b901
  if (parser->atyp == s5_atyp_ipv4) {
Packit b5b901
    memset(&outgoing->t.addr4, 0, sizeof(outgoing->t.addr4));
Packit b5b901
    outgoing->t.addr4.sin_family = AF_INET;
Packit b5b901
    outgoing->t.addr4.sin_port = htons(parser->dport);
Packit b5b901
    memcpy(&outgoing->t.addr4.sin_addr,
Packit b5b901
           parser->daddr,
Packit b5b901
           sizeof(outgoing->t.addr4.sin_addr));
Packit b5b901
  } else if (parser->atyp == s5_atyp_ipv6) {
Packit b5b901
    memset(&outgoing->t.addr6, 0, sizeof(outgoing->t.addr6));
Packit b5b901
    outgoing->t.addr6.sin6_family = AF_INET6;
Packit b5b901
    outgoing->t.addr6.sin6_port = htons(parser->dport);
Packit b5b901
    memcpy(&outgoing->t.addr6.sin6_addr,
Packit b5b901
           parser->daddr,
Packit b5b901
           sizeof(outgoing->t.addr6.sin6_addr));
Packit b5b901
  } else {
Packit b5b901
    UNREACHABLE();
Packit b5b901
  }
Packit b5b901
Packit b5b901
  return do_req_connect_start(cx);
Packit b5b901
}
Packit b5b901
Packit b5b901
static int do_req_lookup(client_ctx *cx) {
Packit b5b901
  s5_ctx *parser;
Packit b5b901
  conn *incoming;
Packit b5b901
  conn *outgoing;
Packit b5b901
Packit b5b901
  parser = &cx->parser;
Packit b5b901
  incoming = &cx->incoming;
Packit b5b901
  outgoing = &cx->outgoing;
Packit b5b901
  ASSERT(incoming->rdstate == c_stop);
Packit b5b901
  ASSERT(incoming->wrstate == c_stop);
Packit b5b901
  ASSERT(outgoing->rdstate == c_stop);
Packit b5b901
  ASSERT(outgoing->wrstate == c_stop);
Packit b5b901
Packit b5b901
  if (outgoing->result < 0) {
Packit b5b901
    /* TODO(bnoordhuis) Escape control characters in parser->daddr. */
Packit b5b901
    pr_err("lookup error for \"%s\": %s",
Packit b5b901
           parser->daddr,
Packit b5b901
           uv_strerror(outgoing->result));
Packit b5b901
    /* Send back a 'Host unreachable' reply. */
Packit b5b901
    conn_write(incoming, "\5\4\0\1\0\0\0\0\0\0", 10);
Packit b5b901
    return s_kill;
Packit b5b901
  }
Packit b5b901
Packit b5b901
  /* Don't make assumptions about the offset of sin_port/sin6_port. */
Packit b5b901
  switch (outgoing->t.addr.sa_family) {
Packit b5b901
    case AF_INET:
Packit b5b901
      outgoing->t.addr4.sin_port = htons(parser->dport);
Packit b5b901
      break;
Packit b5b901
    case AF_INET6:
Packit b5b901
      outgoing->t.addr6.sin6_port = htons(parser->dport);
Packit b5b901
      break;
Packit b5b901
    default:
Packit b5b901
      UNREACHABLE();
Packit b5b901
  }
Packit b5b901
Packit b5b901
  return do_req_connect_start(cx);
Packit b5b901
}
Packit b5b901
Packit b5b901
/* Assumes that cx->outgoing.t.sa contains a valid AF_INET/AF_INET6 address. */
Packit b5b901
static int do_req_connect_start(client_ctx *cx) {
Packit b5b901
  conn *incoming;
Packit b5b901
  conn *outgoing;
Packit b5b901
  int err;
Packit b5b901
Packit b5b901
  incoming = &cx->incoming;
Packit b5b901
  outgoing = &cx->outgoing;
Packit b5b901
  ASSERT(incoming->rdstate == c_stop);
Packit b5b901
  ASSERT(incoming->wrstate == c_stop);
Packit b5b901
  ASSERT(outgoing->rdstate == c_stop);
Packit b5b901
  ASSERT(outgoing->wrstate == c_stop);
Packit b5b901
Packit b5b901
  if (!can_access(cx->sx, cx, &outgoing->t.addr)) {
Packit b5b901
    pr_warn("connection not allowed by ruleset");
Packit b5b901
    /* Send a 'Connection not allowed by ruleset' reply. */
Packit b5b901
    conn_write(incoming, "\5\2\0\1\0\0\0\0\0\0", 10);
Packit b5b901
    return s_kill;
Packit b5b901
  }
Packit b5b901
Packit b5b901
  err = conn_connect(outgoing);
Packit b5b901
  if (err != 0) {
Packit b5b901
    pr_err("connect error: %s\n", uv_strerror(err));
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  return s_req_connect;
Packit b5b901
}
Packit b5b901
Packit b5b901
static int do_req_connect(client_ctx *cx) {
Packit b5b901
  const struct sockaddr_in6 *in6;
Packit b5b901
  const struct sockaddr_in *in;
Packit b5b901
  char addr_storage[sizeof(*in6)];
Packit b5b901
  conn *incoming;
Packit b5b901
  conn *outgoing;
Packit b5b901
  uint8_t *buf;
Packit b5b901
  int addrlen;
Packit b5b901
Packit b5b901
  incoming = &cx->incoming;
Packit b5b901
  outgoing = &cx->outgoing;
Packit b5b901
  ASSERT(incoming->rdstate == c_stop);
Packit b5b901
  ASSERT(incoming->wrstate == c_stop);
Packit b5b901
  ASSERT(outgoing->rdstate == c_stop);
Packit b5b901
  ASSERT(outgoing->wrstate == c_stop);
Packit b5b901
Packit b5b901
  /* Build and send the reply.  Not very pretty but gets the job done. */
Packit b5b901
  buf = (uint8_t *) incoming->t.buf;
Packit b5b901
  if (outgoing->result == 0) {
Packit b5b901
    /* The RFC mandates that the SOCKS server must include the local port
Packit b5b901
     * and address in the reply.  So that's what we do.
Packit b5b901
     */
Packit b5b901
    addrlen = sizeof(addr_storage);
Packit b5b901
    CHECK(0 == uv_tcp_getsockname(&outgoing->handle.tcp,
Packit b5b901
                                  (struct sockaddr *) addr_storage,
Packit b5b901
                                  &addrlen));
Packit b5b901
    buf[0] = 5;  /* Version. */
Packit b5b901
    buf[1] = 0;  /* Success. */
Packit b5b901
    buf[2] = 0;  /* Reserved. */
Packit b5b901
    if (addrlen == sizeof(*in)) {
Packit b5b901
      buf[3] = 1;  /* IPv4. */
Packit b5b901
      in = (const struct sockaddr_in *) &addr_storage;
Packit b5b901
      memcpy(buf + 4, &in->sin_addr, 4);
Packit b5b901
      memcpy(buf + 8, &in->sin_port, 2);
Packit b5b901
      conn_write(incoming, buf, 10);
Packit b5b901
    } else if (addrlen == sizeof(*in6)) {
Packit b5b901
      buf[3] = 4;  /* IPv6. */
Packit b5b901
      in6 = (const struct sockaddr_in6 *) &addr_storage;
Packit b5b901
      memcpy(buf + 4, &in6->sin6_addr, 16);
Packit b5b901
      memcpy(buf + 20, &in6->sin6_port, 2);
Packit b5b901
      conn_write(incoming, buf, 22);
Packit b5b901
    } else {
Packit b5b901
      UNREACHABLE();
Packit b5b901
    }
Packit b5b901
    return s_proxy_start;
Packit b5b901
  } else {
Packit b5b901
    pr_err("upstream connection error: %s\n", uv_strerror(outgoing->result));
Packit b5b901
    /* Send a 'Connection refused' reply. */
Packit b5b901
    conn_write(incoming, "\5\5\0\1\0\0\0\0\0\0", 10);
Packit b5b901
    return s_kill;
Packit b5b901
  }
Packit b5b901
Packit b5b901
  UNREACHABLE();
Packit b5b901
  return s_kill;
Packit b5b901
}
Packit b5b901
Packit b5b901
static int do_proxy_start(client_ctx *cx) {
Packit b5b901
  conn *incoming;
Packit b5b901
  conn *outgoing;
Packit b5b901
Packit b5b901
  incoming = &cx->incoming;
Packit b5b901
  outgoing = &cx->outgoing;
Packit b5b901
  ASSERT(incoming->rdstate == c_stop);
Packit b5b901
  ASSERT(incoming->wrstate == c_done);
Packit b5b901
  ASSERT(outgoing->rdstate == c_stop);
Packit b5b901
  ASSERT(outgoing->wrstate == c_stop);
Packit b5b901
  incoming->wrstate = c_stop;
Packit b5b901
Packit b5b901
  if (incoming->result < 0) {
Packit b5b901
    pr_err("write error: %s", uv_strerror(incoming->result));
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  conn_read(incoming);
Packit b5b901
  conn_read(outgoing);
Packit b5b901
  return s_proxy;
Packit b5b901
}
Packit b5b901
Packit b5b901
/* Proxy incoming data back and forth. */
Packit b5b901
static int do_proxy(client_ctx *cx) {
Packit b5b901
  if (conn_cycle("client", &cx->incoming, &cx->outgoing)) {
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  if (conn_cycle("upstream", &cx->outgoing, &cx->incoming)) {
Packit b5b901
    return do_kill(cx);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  return s_proxy;
Packit b5b901
}
Packit b5b901
Packit b5b901
static int do_kill(client_ctx *cx) {
Packit b5b901
  int new_state;
Packit b5b901
Packit b5b901
  if (cx->state >= s_almost_dead_0) {
Packit b5b901
    return cx->state;
Packit b5b901
  }
Packit b5b901
Packit b5b901
  /* Try to cancel the request. The callback still runs but if the
Packit b5b901
   * cancellation succeeded, it gets called with status=UV_ECANCELED.
Packit b5b901
   */
Packit b5b901
  new_state = s_almost_dead_1;
Packit b5b901
  if (cx->state == s_req_lookup) {
Packit b5b901
    new_state = s_almost_dead_0;
Packit b5b901
    uv_cancel(&cx->outgoing.t.req);
Packit b5b901
  }
Packit b5b901
Packit b5b901
  conn_close(&cx->incoming);
Packit b5b901
  conn_close(&cx->outgoing);
Packit b5b901
  return new_state;
Packit b5b901
}
Packit b5b901
Packit b5b901
static int do_almost_dead(client_ctx *cx) {
Packit b5b901
  ASSERT(cx->state >= s_almost_dead_0);
Packit b5b901
  return cx->state + 1;  /* Another finalizer completed. */
Packit b5b901
}
Packit b5b901
Packit b5b901
static int conn_cycle(const char *who, conn *a, conn *b) {
Packit b5b901
  if (a->result < 0) {
Packit b5b901
    if (a->result != UV_EOF) {
Packit b5b901
      pr_err("%s error: %s", who, uv_strerror(a->result));
Packit b5b901
    }
Packit b5b901
    return -1;
Packit b5b901
  }
Packit b5b901
Packit b5b901
  if (b->result < 0) {
Packit b5b901
    return -1;
Packit b5b901
  }
Packit b5b901
Packit b5b901
  if (a->wrstate == c_done) {
Packit b5b901
    a->wrstate = c_stop;
Packit b5b901
  }
Packit b5b901
Packit b5b901
  /* The logic is as follows: read when we don't write and write when we don't
Packit b5b901
   * read.  That gives us back-pressure handling for free because if the peer
Packit b5b901
   * sends data faster than we consume it, TCP congestion control kicks in.
Packit b5b901
   */
Packit b5b901
  if (a->wrstate == c_stop) {
Packit b5b901
    if (b->rdstate == c_stop) {
Packit b5b901
      conn_read(b);
Packit b5b901
    } else if (b->rdstate == c_done) {
Packit b5b901
      conn_write(a, b->t.buf, b->result);
Packit b5b901
      b->rdstate = c_stop;  /* Triggers the call to conn_read() above. */
Packit b5b901
    }
Packit b5b901
  }
Packit b5b901
Packit b5b901
  return 0;
Packit b5b901
}
Packit b5b901
Packit b5b901
static void conn_timer_reset(conn *c) {
Packit b5b901
  CHECK(0 == uv_timer_start(&c->timer_handle,
Packit b5b901
                            conn_timer_expire,
Packit b5b901
                            c->idle_timeout,
Packit b5b901
                            0));
Packit b5b901
}
Packit b5b901
Packit b5b901
static void conn_timer_expire(uv_timer_t *handle) {
Packit b5b901
  conn *c;
Packit b5b901
Packit b5b901
  c = CONTAINER_OF(handle, conn, timer_handle);
Packit b5b901
  c->result = UV_ETIMEDOUT;
Packit b5b901
  do_next(c->client);
Packit b5b901
}
Packit b5b901
Packit b5b901
static void conn_getaddrinfo(conn *c, const char *hostname) {
Packit b5b901
  struct addrinfo hints;
Packit b5b901
Packit b5b901
  memset(&hints, 0, sizeof(hints));
Packit b5b901
  hints.ai_family = AF_UNSPEC;
Packit b5b901
  hints.ai_socktype = SOCK_STREAM;
Packit b5b901
  hints.ai_protocol = IPPROTO_TCP;
Packit b5b901
  CHECK(0 == uv_getaddrinfo(c->client->sx->loop,
Packit b5b901
                            &c->t.addrinfo_req,
Packit b5b901
                            conn_getaddrinfo_done,
Packit b5b901
                            hostname,
Packit b5b901
                            NULL,
Packit b5b901
                            &hints));
Packit b5b901
  conn_timer_reset(c);
Packit b5b901
}
Packit b5b901
Packit b5b901
static void conn_getaddrinfo_done(uv_getaddrinfo_t *req,
Packit b5b901
                                  int status,
Packit b5b901
                                  struct addrinfo *ai) {
Packit b5b901
  conn *c;
Packit b5b901
Packit b5b901
  c = CONTAINER_OF(req, conn, t.addrinfo_req);
Packit b5b901
  c->result = status;
Packit b5b901
Packit b5b901
  if (status == 0) {
Packit b5b901
    /* FIXME(bnoordhuis) Should try all addresses. */
Packit b5b901
    if (ai->ai_family == AF_INET) {
Packit b5b901
      c->t.addr4 = *(const struct sockaddr_in *) ai->ai_addr;
Packit b5b901
    } else if (ai->ai_family == AF_INET6) {
Packit b5b901
      c->t.addr6 = *(const struct sockaddr_in6 *) ai->ai_addr;
Packit b5b901
    } else {
Packit b5b901
      UNREACHABLE();
Packit b5b901
    }
Packit b5b901
  }
Packit b5b901
Packit b5b901
  uv_freeaddrinfo(ai);
Packit b5b901
  do_next(c->client);
Packit b5b901
}
Packit b5b901
Packit b5b901
/* Assumes that c->t.sa contains a valid AF_INET or AF_INET6 address. */
Packit b5b901
static int conn_connect(conn *c) {
Packit b5b901
  ASSERT(c->t.addr.sa_family == AF_INET ||
Packit b5b901
         c->t.addr.sa_family == AF_INET6);
Packit b5b901
  conn_timer_reset(c);
Packit b5b901
  return uv_tcp_connect(&c->t.connect_req,
Packit b5b901
                        &c->handle.tcp,
Packit b5b901
                        &c->t.addr,
Packit b5b901
                        conn_connect_done);
Packit b5b901
}
Packit b5b901
Packit b5b901
static void conn_connect_done(uv_connect_t *req, int status) {
Packit b5b901
  conn *c;
Packit b5b901
Packit b5b901
  if (status == UV_ECANCELED) {
Packit b5b901
    return;  /* Handle has been closed. */
Packit b5b901
  }
Packit b5b901
Packit b5b901
  c = CONTAINER_OF(req, conn, t.connect_req);
Packit b5b901
  c->result = status;
Packit b5b901
  do_next(c->client);
Packit b5b901
}
Packit b5b901
Packit b5b901
static void conn_read(conn *c) {
Packit b5b901
  ASSERT(c->rdstate == c_stop);
Packit b5b901
  CHECK(0 == uv_read_start(&c->handle.stream, conn_alloc, conn_read_done));
Packit b5b901
  c->rdstate = c_busy;
Packit b5b901
  conn_timer_reset(c);
Packit b5b901
}
Packit b5b901
Packit b5b901
static void conn_read_done(uv_stream_t *handle,
Packit b5b901
                           ssize_t nread,
Packit b5b901
                           const uv_buf_t *buf) {
Packit b5b901
  conn *c;
Packit b5b901
Packit b5b901
  c = CONTAINER_OF(handle, conn, handle);
Packit b5b901
  ASSERT(c->t.buf == buf->base);
Packit b5b901
  ASSERT(c->rdstate == c_busy);
Packit b5b901
  c->rdstate = c_done;
Packit b5b901
  c->result = nread;
Packit b5b901
Packit b5b901
  uv_read_stop(&c->handle.stream);
Packit b5b901
  do_next(c->client);
Packit b5b901
}
Packit b5b901
Packit b5b901
static void conn_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf) {
Packit b5b901
  conn *c;
Packit b5b901
Packit b5b901
  c = CONTAINER_OF(handle, conn, handle);
Packit b5b901
  ASSERT(c->rdstate == c_busy);
Packit b5b901
  buf->base = c->t.buf;
Packit b5b901
  buf->len = sizeof(c->t.buf);
Packit b5b901
}
Packit b5b901
Packit b5b901
static void conn_write(conn *c, const void *data, unsigned int len) {
Packit b5b901
  uv_buf_t buf;
Packit b5b901
Packit b5b901
  ASSERT(c->wrstate == c_stop || c->wrstate == c_done);
Packit b5b901
  c->wrstate = c_busy;
Packit b5b901
Packit b5b901
  /* It's okay to cast away constness here, uv_write() won't modify the
Packit b5b901
   * memory.
Packit b5b901
   */
Packit b5b901
  buf.base = (char *) data;
Packit b5b901
  buf.len = len;
Packit b5b901
Packit b5b901
  CHECK(0 == uv_write(&c->write_req,
Packit b5b901
                      &c->handle.stream,
Packit b5b901
                      &buf,
Packit b5b901
                      1,
Packit b5b901
                      conn_write_done));
Packit b5b901
  conn_timer_reset(c);
Packit b5b901
}
Packit b5b901
Packit b5b901
static void conn_write_done(uv_write_t *req, int status) {
Packit b5b901
  conn *c;
Packit b5b901
Packit b5b901
  if (status == UV_ECANCELED) {
Packit b5b901
    return;  /* Handle has been closed. */
Packit b5b901
  }
Packit b5b901
Packit b5b901
  c = CONTAINER_OF(req, conn, write_req);
Packit b5b901
  ASSERT(c->wrstate == c_busy);
Packit b5b901
  c->wrstate = c_done;
Packit b5b901
  c->result = status;
Packit b5b901
  do_next(c->client);
Packit b5b901
}
Packit b5b901
Packit b5b901
static void conn_close(conn *c) {
Packit b5b901
  ASSERT(c->rdstate != c_dead);
Packit b5b901
  ASSERT(c->wrstate != c_dead);
Packit b5b901
  c->rdstate = c_dead;
Packit b5b901
  c->wrstate = c_dead;
Packit b5b901
  c->timer_handle.data = c;
Packit b5b901
  c->handle.handle.data = c;
Packit b5b901
  uv_close(&c->handle.handle, conn_close_done);
Packit b5b901
  uv_close((uv_handle_t *) &c->timer_handle, conn_close_done);
Packit b5b901
}
Packit b5b901
Packit b5b901
static void conn_close_done(uv_handle_t *handle) {
Packit b5b901
  conn *c;
Packit b5b901
Packit b5b901
  c = handle->data;
Packit b5b901
  do_next(c->client);
Packit b5b901
}