diff options
Diffstat (limited to '3rdparty/libuv/samples/socks5-proxy')
-rw-r--r-- | 3rdparty/libuv/samples/socks5-proxy/.gitignore | 21 | ||||
-rw-r--r-- | 3rdparty/libuv/samples/socks5-proxy/LICENSE | 53 | ||||
-rw-r--r-- | 3rdparty/libuv/samples/socks5-proxy/build.gyp | 46 | ||||
-rw-r--r-- | 3rdparty/libuv/samples/socks5-proxy/client.c | 737 | ||||
-rw-r--r-- | 3rdparty/libuv/samples/socks5-proxy/defs.h | 139 | ||||
-rw-r--r-- | 3rdparty/libuv/samples/socks5-proxy/getopt.c | 131 | ||||
-rw-r--r-- | 3rdparty/libuv/samples/socks5-proxy/main.c | 99 | ||||
-rw-r--r-- | 3rdparty/libuv/samples/socks5-proxy/s5.c | 271 | ||||
-rw-r--r-- | 3rdparty/libuv/samples/socks5-proxy/s5.h | 94 | ||||
-rw-r--r-- | 3rdparty/libuv/samples/socks5-proxy/server.c | 241 | ||||
-rw-r--r-- | 3rdparty/libuv/samples/socks5-proxy/util.c | 72 |
11 files changed, 1904 insertions, 0 deletions
diff --git a/3rdparty/libuv/samples/socks5-proxy/.gitignore b/3rdparty/libuv/samples/socks5-proxy/.gitignore new file mode 100644 index 00000000000..c177f374510 --- /dev/null +++ b/3rdparty/libuv/samples/socks5-proxy/.gitignore @@ -0,0 +1,21 @@ +# Copyright StrongLoop, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +/build/ diff --git a/3rdparty/libuv/samples/socks5-proxy/LICENSE b/3rdparty/libuv/samples/socks5-proxy/LICENSE new file mode 100644 index 00000000000..63c1447fc55 --- /dev/null +++ b/3rdparty/libuv/samples/socks5-proxy/LICENSE @@ -0,0 +1,53 @@ +Files: * +======== + +Copyright StrongLoop, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + + +Files: getopt.c +=============== + +Copyright (c) 1987, 1993, 1994 +The Regents of the University of California. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/3rdparty/libuv/samples/socks5-proxy/build.gyp b/3rdparty/libuv/samples/socks5-proxy/build.gyp new file mode 100644 index 00000000000..771a1e146db --- /dev/null +++ b/3rdparty/libuv/samples/socks5-proxy/build.gyp @@ -0,0 +1,46 @@ +# Copyright StrongLoop, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +{ + 'targets': [ + { + 'dependencies': ['../../uv.gyp:libuv'], + 'target_name': 's5-proxy', + 'type': 'executable', + 'sources': [ + 'client.c', + 'defs.h', + 'main.c', + 's5.c', + 's5.h', + 'server.c', + 'util.c', + ], + 'conditions': [ + ['OS=="win"', { + 'defines': ['HAVE_UNISTD_H=0'], + 'sources': ['getopt.c'] + }, { + 'defines': ['HAVE_UNISTD_H=1'] + }] + ] + } + ] +} diff --git a/3rdparty/libuv/samples/socks5-proxy/client.c b/3rdparty/libuv/samples/socks5-proxy/client.c new file mode 100644 index 00000000000..ae9913a1c6e --- /dev/null +++ b/3rdparty/libuv/samples/socks5-proxy/client.c @@ -0,0 +1,737 @@ +/* Copyright StrongLoop, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "defs.h" +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +/* A connection is modeled as an abstraction on top of two simple state + * machines, one for reading and one for writing. Either state machine + * is, when active, in one of three states: busy, done or stop; the fourth + * and final state, dead, is an end state and only relevant when shutting + * down the connection. A short overview: + * + * busy done stop + * ----------|---------------------------|--------------------|------| + * readable | waiting for incoming data | have incoming data | idle | + * writable | busy writing out data | completed write | idle | + * + * We could remove the done state from the writable state machine. For our + * purposes, it's functionally equivalent to the stop state. + * + * When the connection with upstream has been established, the client_ctx + * moves into a state where incoming data from the client is sent upstream + * and vice versa, incoming data from upstream is sent to the client. In + * other words, we're just piping data back and forth. See conn_cycle() + * for details. + * + * An interesting deviation from libuv's I/O model is that reads are discrete + * rather than continuous events. In layman's terms, when a read operation + * completes, the connection stops reading until further notice. + * + * The rationale for this approach is that we have to wait until the data + * has been sent out again before we can reuse the read buffer. + * + * It also pleasingly unifies with the request model that libuv uses for + * writes and everything else; libuv may switch to a request model for + * reads in the future. + */ +enum conn_state { + c_busy, /* Busy; waiting for incoming data or for a write to complete. */ + c_done, /* Done; read incoming data or write finished. */ + c_stop, /* Stopped. */ + c_dead +}; + +/* Session states. */ +enum sess_state { + s_handshake, /* Wait for client handshake. */ + s_handshake_auth, /* Wait for client authentication data. */ + s_req_start, /* Start waiting for request data. */ + s_req_parse, /* Wait for request data. */ + s_req_lookup, /* Wait for upstream hostname DNS lookup to complete. */ + s_req_connect, /* Wait for uv_tcp_connect() to complete. */ + s_proxy_start, /* Connected. Start piping data. */ + s_proxy, /* Connected. Pipe data back and forth. */ + s_kill, /* Tear down session. */ + s_almost_dead_0, /* Waiting for finalizers to complete. */ + s_almost_dead_1, /* Waiting for finalizers to complete. */ + s_almost_dead_2, /* Waiting for finalizers to complete. */ + s_almost_dead_3, /* Waiting for finalizers to complete. */ + s_almost_dead_4, /* Waiting for finalizers to complete. */ + s_dead /* Dead. Safe to free now. */ +}; + +static void do_next(client_ctx *cx); +static int do_handshake(client_ctx *cx); +static int do_handshake_auth(client_ctx *cx); +static int do_req_start(client_ctx *cx); +static int do_req_parse(client_ctx *cx); +static int do_req_lookup(client_ctx *cx); +static int do_req_connect_start(client_ctx *cx); +static int do_req_connect(client_ctx *cx); +static int do_proxy_start(client_ctx *cx); +static int do_proxy(client_ctx *cx); +static int do_kill(client_ctx *cx); +static int do_almost_dead(client_ctx *cx); +static int conn_cycle(const char *who, conn *a, conn *b); +static void conn_timer_reset(conn *c); +static void conn_timer_expire(uv_timer_t *handle, int status); +static void conn_getaddrinfo(conn *c, const char *hostname); +static void conn_getaddrinfo_done(uv_getaddrinfo_t *req, + int status, + struct addrinfo *ai); +static int conn_connect(conn *c); +static void conn_connect_done(uv_connect_t *req, int status); +static void conn_read(conn *c); +static void conn_read_done(uv_stream_t *handle, + ssize_t nread, + const uv_buf_t *buf); +static void conn_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf); +static void conn_write(conn *c, const void *data, unsigned int len); +static void conn_write_done(uv_write_t *req, int status); +static void conn_close(conn *c); +static void conn_close_done(uv_handle_t *handle); + +/* |incoming| has been initialized by server.c when this is called. */ +void client_finish_init(server_ctx *sx, client_ctx *cx) { + conn *incoming; + conn *outgoing; + + cx->sx = sx; + cx->state = s_handshake; + s5_init(&cx->parser); + + incoming = &cx->incoming; + incoming->client = cx; + incoming->result = 0; + incoming->rdstate = c_stop; + incoming->wrstate = c_stop; + incoming->idle_timeout = sx->idle_timeout; + CHECK(0 == uv_timer_init(sx->loop, &incoming->timer_handle)); + + outgoing = &cx->outgoing; + outgoing->client = cx; + outgoing->result = 0; + outgoing->rdstate = c_stop; + outgoing->wrstate = c_stop; + outgoing->idle_timeout = sx->idle_timeout; + CHECK(0 == uv_tcp_init(cx->sx->loop, &outgoing->handle.tcp)); + CHECK(0 == uv_timer_init(cx->sx->loop, &outgoing->timer_handle)); + + /* Wait for the initial packet. */ + conn_read(incoming); +} + +/* This is the core state machine that drives the client <-> upstream proxy. + * We move through the initial handshake and authentication steps first and + * end up (if all goes well) in the proxy state where we're just proxying + * data between the client and upstream. + */ +static void do_next(client_ctx *cx) { + int new_state; + + ASSERT(cx->state != s_dead); + switch (cx->state) { + case s_handshake: + new_state = do_handshake(cx); + break; + case s_handshake_auth: + new_state = do_handshake_auth(cx); + break; + case s_req_start: + new_state = do_req_start(cx); + break; + case s_req_parse: + new_state = do_req_parse(cx); + break; + case s_req_lookup: + new_state = do_req_lookup(cx); + break; + case s_req_connect: + new_state = do_req_connect(cx); + break; + case s_proxy_start: + new_state = do_proxy_start(cx); + break; + case s_proxy: + new_state = do_proxy(cx); + break; + case s_kill: + new_state = do_kill(cx); + break; + case s_almost_dead_0: + case s_almost_dead_1: + case s_almost_dead_2: + case s_almost_dead_3: + case s_almost_dead_4: + new_state = do_almost_dead(cx); + break; + default: + UNREACHABLE(); + } + cx->state = new_state; + + if (cx->state == s_dead) { + if (DEBUG_CHECKS) { + memset(cx, -1, sizeof(*cx)); + } + free(cx); + } +} + +static int do_handshake(client_ctx *cx) { + unsigned int methods; + conn *incoming; + s5_ctx *parser; + uint8_t *data; + size_t size; + int err; + + parser = &cx->parser; + incoming = &cx->incoming; + ASSERT(incoming->rdstate == c_done); + ASSERT(incoming->wrstate == c_stop); + incoming->rdstate = c_stop; + + if (incoming->result < 0) { + pr_err("read error: %s", uv_strerror(incoming->result)); + return do_kill(cx); + } + + data = (uint8_t *) incoming->t.buf; + size = (size_t) incoming->result; + err = s5_parse(parser, &data, &size); + if (err == s5_ok) { + conn_read(incoming); + return s_handshake; /* Need more data. */ + } + + if (size != 0) { + /* Could allow a round-trip saving shortcut here if the requested auth + * method is S5_AUTH_NONE (provided unauthenticated traffic is allowed.) + * Requires client support however. + */ + pr_err("junk in handshake"); + return do_kill(cx); + } + + if (err != s5_auth_select) { + pr_err("handshake error: %s", s5_strerror(err)); + return do_kill(cx); + } + + methods = s5_auth_methods(parser); + if ((methods & S5_AUTH_NONE) && can_auth_none(cx->sx, cx)) { + s5_select_auth(parser, S5_AUTH_NONE); + conn_write(incoming, "\5\0", 2); /* No auth required. */ + return s_req_start; + } + + if ((methods & S5_AUTH_PASSWD) && can_auth_passwd(cx->sx, cx)) { + /* TODO(bnoordhuis) Implement username/password auth. */ + } + + conn_write(incoming, "\5\377", 2); /* No acceptable auth. */ + return s_kill; +} + +/* TODO(bnoordhuis) Implement username/password auth. */ +static int do_handshake_auth(client_ctx *cx) { + UNREACHABLE(); + return do_kill(cx); +} + +static int do_req_start(client_ctx *cx) { + conn *incoming; + + incoming = &cx->incoming; + ASSERT(incoming->rdstate == c_stop); + ASSERT(incoming->wrstate == c_done); + incoming->wrstate = c_stop; + + if (incoming->result < 0) { + pr_err("write error: %s", uv_strerror(incoming->result)); + return do_kill(cx); + } + + conn_read(incoming); + return s_req_parse; +} + +static int do_req_parse(client_ctx *cx) { + conn *incoming; + conn *outgoing; + s5_ctx *parser; + uint8_t *data; + size_t size; + int err; + + parser = &cx->parser; + incoming = &cx->incoming; + outgoing = &cx->outgoing; + ASSERT(incoming->rdstate == c_done); + ASSERT(incoming->wrstate == c_stop); + ASSERT(outgoing->rdstate == c_stop); + ASSERT(outgoing->wrstate == c_stop); + incoming->rdstate = c_stop; + + if (incoming->result < 0) { + pr_err("read error: %s", uv_strerror(incoming->result)); + return do_kill(cx); + } + + data = (uint8_t *) incoming->t.buf; + size = (size_t) incoming->result; + err = s5_parse(parser, &data, &size); + if (err == s5_ok) { + conn_read(incoming); + return s_req_parse; /* Need more data. */ + } + + if (size != 0) { + pr_err("junk in request %u", (unsigned) size); + return do_kill(cx); + } + + if (err != s5_exec_cmd) { + pr_err("request error: %s", s5_strerror(err)); + return do_kill(cx); + } + + if (parser->cmd == s5_cmd_tcp_bind) { + /* Not supported but relatively straightforward to implement. */ + pr_warn("BIND requests are not supported."); + return do_kill(cx); + } + + if (parser->cmd == s5_cmd_udp_assoc) { + /* Not supported. Might be hard to implement because libuv has no + * functionality for detecting the MTU size which the RFC mandates. + */ + pr_warn("UDP ASSOC requests are not supported."); + return do_kill(cx); + } + ASSERT(parser->cmd == s5_cmd_tcp_connect); + + if (parser->atyp == s5_atyp_host) { + conn_getaddrinfo(outgoing, (const char *) parser->daddr); + return s_req_lookup; + } + + if (parser->atyp == s5_atyp_ipv4) { + memset(&outgoing->t.addr4, 0, sizeof(outgoing->t.addr4)); + outgoing->t.addr4.sin_family = AF_INET; + outgoing->t.addr4.sin_port = htons(parser->dport); + memcpy(&outgoing->t.addr4.sin_addr, + parser->daddr, + sizeof(outgoing->t.addr4.sin_addr)); + } else if (parser->atyp == s5_atyp_ipv6) { + memset(&outgoing->t.addr6, 0, sizeof(outgoing->t.addr6)); + outgoing->t.addr6.sin6_family = AF_INET6; + outgoing->t.addr6.sin6_port = htons(parser->dport); + memcpy(&outgoing->t.addr6.sin6_addr, + parser->daddr, + sizeof(outgoing->t.addr6.sin6_addr)); + } else { + UNREACHABLE(); + } + + return do_req_connect_start(cx); +} + +static int do_req_lookup(client_ctx *cx) { + s5_ctx *parser; + conn *incoming; + conn *outgoing; + + parser = &cx->parser; + incoming = &cx->incoming; + outgoing = &cx->outgoing; + ASSERT(incoming->rdstate == c_stop); + ASSERT(incoming->wrstate == c_stop); + ASSERT(outgoing->rdstate == c_stop); + ASSERT(outgoing->wrstate == c_stop); + + if (outgoing->result < 0) { + /* TODO(bnoordhuis) Escape control characters in parser->daddr. */ + pr_err("lookup error for \"%s\": %s", + parser->daddr, + uv_strerror(outgoing->result)); + /* Send back a 'Host unreachable' reply. */ + conn_write(incoming, "\5\4\0\1\0\0\0\0\0\0", 10); + return s_kill; + } + + /* Don't make assumptions about the offset of sin_port/sin6_port. */ + switch (outgoing->t.addr.sa_family) { + case AF_INET: + outgoing->t.addr4.sin_port = htons(parser->dport); + break; + case AF_INET6: + outgoing->t.addr6.sin6_port = htons(parser->dport); + break; + default: + UNREACHABLE(); + } + + return do_req_connect_start(cx); +} + +/* Assumes that cx->outgoing.t.sa contains a valid AF_INET/AF_INET6 address. */ +static int do_req_connect_start(client_ctx *cx) { + conn *incoming; + conn *outgoing; + int err; + + incoming = &cx->incoming; + outgoing = &cx->outgoing; + ASSERT(incoming->rdstate == c_stop); + ASSERT(incoming->wrstate == c_stop); + ASSERT(outgoing->rdstate == c_stop); + ASSERT(outgoing->wrstate == c_stop); + + if (!can_access(cx->sx, cx, &outgoing->t.addr)) { + pr_warn("connection not allowed by ruleset"); + /* Send a 'Connection not allowed by ruleset' reply. */ + conn_write(incoming, "\5\2\0\1\0\0\0\0\0\0", 10); + return s_kill; + } + + err = conn_connect(outgoing); + if (err != 0) { + pr_err("connect error: %s\n", uv_strerror(err)); + return do_kill(cx); + } + + return s_req_connect; +} + +static int do_req_connect(client_ctx *cx) { + const struct sockaddr_in6 *in6; + const struct sockaddr_in *in; + char addr_storage[sizeof(*in6)]; + conn *incoming; + conn *outgoing; + uint8_t *buf; + int addrlen; + + incoming = &cx->incoming; + outgoing = &cx->outgoing; + ASSERT(incoming->rdstate == c_stop); + ASSERT(incoming->wrstate == c_stop); + ASSERT(outgoing->rdstate == c_stop); + ASSERT(outgoing->wrstate == c_stop); + + /* Build and send the reply. Not very pretty but gets the job done. */ + buf = (uint8_t *) incoming->t.buf; + if (outgoing->result == 0) { + /* The RFC mandates that the SOCKS server must include the local port + * and address in the reply. So that's what we do. + */ + addrlen = sizeof(addr_storage); + CHECK(0 == uv_tcp_getsockname(&outgoing->handle.tcp, + (struct sockaddr *) addr_storage, + &addrlen)); + buf[0] = 5; /* Version. */ + buf[1] = 0; /* Success. */ + buf[2] = 0; /* Reserved. */ + if (addrlen == sizeof(*in)) { + buf[3] = 1; /* IPv4. */ + in = (const struct sockaddr_in *) &addr_storage; + memcpy(buf + 4, &in->sin_addr, 4); + memcpy(buf + 8, &in->sin_port, 2); + conn_write(incoming, buf, 10); + } else if (addrlen == sizeof(*in6)) { + buf[3] = 4; /* IPv6. */ + in6 = (const struct sockaddr_in6 *) &addr_storage; + memcpy(buf + 4, &in6->sin6_addr, 16); + memcpy(buf + 20, &in6->sin6_port, 2); + conn_write(incoming, buf, 22); + } else { + UNREACHABLE(); + } + return s_proxy_start; + } else { + pr_err("upstream connection error: %s\n", uv_strerror(outgoing->result)); + /* Send a 'Connection refused' reply. */ + conn_write(incoming, "\5\5\0\1\0\0\0\0\0\0", 10); + return s_kill; + } + + UNREACHABLE(); + return s_kill; +} + +static int do_proxy_start(client_ctx *cx) { + conn *incoming; + conn *outgoing; + + incoming = &cx->incoming; + outgoing = &cx->outgoing; + ASSERT(incoming->rdstate == c_stop); + ASSERT(incoming->wrstate == c_done); + ASSERT(outgoing->rdstate == c_stop); + ASSERT(outgoing->wrstate == c_stop); + incoming->wrstate = c_stop; + + if (incoming->result < 0) { + pr_err("write error: %s", uv_strerror(incoming->result)); + return do_kill(cx); + } + + conn_read(incoming); + conn_read(outgoing); + return s_proxy; +} + +/* Proxy incoming data back and forth. */ +static int do_proxy(client_ctx *cx) { + if (conn_cycle("client", &cx->incoming, &cx->outgoing)) { + return do_kill(cx); + } + + if (conn_cycle("upstream", &cx->outgoing, &cx->incoming)) { + return do_kill(cx); + } + + return s_proxy; +} + +static int do_kill(client_ctx *cx) { + int new_state; + + if (cx->state >= s_almost_dead_0) { + return cx->state; + } + + /* Try to cancel the request. The callback still runs but if the + * cancellation succeeded, it gets called with status=UV_ECANCELED. + */ + new_state = s_almost_dead_1; + if (cx->state == s_req_lookup) { + new_state = s_almost_dead_0; + uv_cancel(&cx->outgoing.t.req); + } + + conn_close(&cx->incoming); + conn_close(&cx->outgoing); + return new_state; +} + +static int do_almost_dead(client_ctx *cx) { + ASSERT(cx->state >= s_almost_dead_0); + return cx->state + 1; /* Another finalizer completed. */ +} + +static int conn_cycle(const char *who, conn *a, conn *b) { + if (a->result < 0) { + if (a->result != UV_EOF) { + pr_err("%s error: %s", who, uv_strerror(a->result)); + } + return -1; + } + + if (b->result < 0) { + return -1; + } + + if (a->wrstate == c_done) { + a->wrstate = c_stop; + } + + /* The logic is as follows: read when we don't write and write when we don't + * read. That gives us back-pressure handling for free because if the peer + * sends data faster than we consume it, TCP congestion control kicks in. + */ + if (a->wrstate == c_stop) { + if (b->rdstate == c_stop) { + conn_read(b); + } else if (b->rdstate == c_done) { + conn_write(a, b->t.buf, b->result); + b->rdstate = c_stop; /* Triggers the call to conn_read() above. */ + } + } + + return 0; +} + +static void conn_timer_reset(conn *c) { + CHECK(0 == uv_timer_start(&c->timer_handle, + conn_timer_expire, + c->idle_timeout, + 0)); +} + +static void conn_timer_expire(uv_timer_t *handle, int status) { + conn *c; + + CHECK(0 == status); + c = CONTAINER_OF(handle, conn, timer_handle); + c->result = UV_ETIMEDOUT; + do_next(c->client); +} + +static void conn_getaddrinfo(conn *c, const char *hostname) { + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + CHECK(0 == uv_getaddrinfo(c->client->sx->loop, + &c->t.addrinfo_req, + conn_getaddrinfo_done, + hostname, + NULL, + &hints)); + conn_timer_reset(c); +} + +static void conn_getaddrinfo_done(uv_getaddrinfo_t *req, + int status, + struct addrinfo *ai) { + conn *c; + + c = CONTAINER_OF(req, conn, t.addrinfo_req); + c->result = status; + + if (status == 0) { + /* FIXME(bnoordhuis) Should try all addresses. */ + if (ai->ai_family == AF_INET) { + c->t.addr4 = *(const struct sockaddr_in *) ai->ai_addr; + } else if (ai->ai_family == AF_INET6) { + c->t.addr6 = *(const struct sockaddr_in6 *) ai->ai_addr; + } else { + UNREACHABLE(); + } + } + + uv_freeaddrinfo(ai); + do_next(c->client); +} + +/* Assumes that c->t.sa contains a valid AF_INET or AF_INET6 address. */ +static int conn_connect(conn *c) { + ASSERT(c->t.addr.sa_family == AF_INET || + c->t.addr.sa_family == AF_INET6); + conn_timer_reset(c); + return uv_tcp_connect(&c->t.connect_req, + &c->handle.tcp, + &c->t.addr, + conn_connect_done); +} + +static void conn_connect_done(uv_connect_t *req, int status) { + conn *c; + + if (status == UV_ECANCELED) { + return; /* Handle has been closed. */ + } + + c = CONTAINER_OF(req, conn, t.connect_req); + c->result = status; + do_next(c->client); +} + +static void conn_read(conn *c) { + ASSERT(c->rdstate == c_stop); + CHECK(0 == uv_read_start(&c->handle.stream, conn_alloc, conn_read_done)); + c->rdstate = c_busy; + conn_timer_reset(c); +} + +static void conn_read_done(uv_stream_t *handle, + ssize_t nread, + const uv_buf_t *buf) { + conn *c; + + c = CONTAINER_OF(handle, conn, handle); + ASSERT(c->t.buf == buf->base); + ASSERT(c->rdstate == c_busy); + c->rdstate = c_done; + c->result = nread; + + uv_read_stop(&c->handle.stream); + do_next(c->client); +} + +static void conn_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf) { + conn *c; + + c = CONTAINER_OF(handle, conn, handle); + ASSERT(c->rdstate == c_busy); + buf->base = c->t.buf; + buf->len = sizeof(c->t.buf); +} + +static void conn_write(conn *c, const void *data, unsigned int len) { + uv_buf_t buf; + + ASSERT(c->wrstate == c_stop || c->wrstate == c_done); + c->wrstate = c_busy; + + /* It's okay to cast away constness here, uv_write() won't modify the + * memory. + */ + buf.base = (char *) data; + buf.len = len; + + CHECK(0 == uv_write(&c->write_req, + &c->handle.stream, + &buf, + 1, + conn_write_done)); + conn_timer_reset(c); +} + +static void conn_write_done(uv_write_t *req, int status) { + conn *c; + + if (status == UV_ECANCELED) { + return; /* Handle has been closed. */ + } + + c = CONTAINER_OF(req, conn, write_req); + ASSERT(c->wrstate == c_busy); + c->wrstate = c_done; + c->result = status; + do_next(c->client); +} + +static void conn_close(conn *c) { + ASSERT(c->rdstate != c_dead); + ASSERT(c->wrstate != c_dead); + c->rdstate = c_dead; + c->wrstate = c_dead; + c->timer_handle.data = c; + c->handle.handle.data = c; + uv_close(&c->handle.handle, conn_close_done); + uv_close((uv_handle_t *) &c->timer_handle, conn_close_done); +} + +static void conn_close_done(uv_handle_t *handle) { + conn *c; + + c = handle->data; + do_next(c->client); +} diff --git a/3rdparty/libuv/samples/socks5-proxy/defs.h b/3rdparty/libuv/samples/socks5-proxy/defs.h new file mode 100644 index 00000000000..99ee8160c8a --- /dev/null +++ b/3rdparty/libuv/samples/socks5-proxy/defs.h @@ -0,0 +1,139 @@ +/* Copyright StrongLoop, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef DEFS_H_ +#define DEFS_H_ + +#include "s5.h" +#include "uv.h" + +#include <assert.h> +#include <netinet/in.h> /* sockaddr_in, sockaddr_in6 */ +#include <stddef.h> /* size_t, ssize_t */ +#include <stdint.h> +#include <sys/socket.h> /* sockaddr */ + +struct client_ctx; + +typedef struct { + const char *bind_host; + unsigned short bind_port; + unsigned int idle_timeout; +} server_config; + +typedef struct { + unsigned int idle_timeout; /* Connection idle timeout in ms. */ + uv_tcp_t tcp_handle; + uv_loop_t *loop; +} server_ctx; + +typedef struct { + unsigned char rdstate; + unsigned char wrstate; + unsigned int idle_timeout; + struct client_ctx *client; /* Backlink to owning client context. */ + ssize_t result; + union { + uv_handle_t handle; + uv_stream_t stream; + uv_tcp_t tcp; + uv_udp_t udp; + } handle; + uv_timer_t timer_handle; /* For detecting timeouts. */ + uv_write_t write_req; + /* We only need one of these at a time so make them share memory. */ + union { + uv_getaddrinfo_t addrinfo_req; + uv_connect_t connect_req; + uv_req_t req; + struct sockaddr_in6 addr6; + struct sockaddr_in addr4; + struct sockaddr addr; + char buf[2048]; /* Scratch space. Used to read data into. */ + } t; +} conn; + +typedef struct client_ctx { + unsigned int state; + server_ctx *sx; /* Backlink to owning server context. */ + s5_ctx parser; /* The SOCKS protocol parser. */ + conn incoming; /* Connection with the SOCKS client. */ + conn outgoing; /* Connection with upstream. */ +} client_ctx; + +/* server.c */ +int server_run(const server_config *cf, uv_loop_t *loop); +int can_auth_none(const server_ctx *sx, const client_ctx *cx); +int can_auth_passwd(const server_ctx *sx, const client_ctx *cx); +int can_access(const server_ctx *sx, + const client_ctx *cx, + const struct sockaddr *addr); + +/* client.c */ +void client_finish_init(server_ctx *sx, client_ctx *cx); + +/* util.c */ +#if defined(__GNUC__) +# define ATTRIBUTE_FORMAT_PRINTF(a, b) __attribute__((format(printf, a, b))) +#else +# define ATTRIBUTE_FORMAT_PRINTF(a, b) +#endif +void pr_info(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); +void pr_warn(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); +void pr_err(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); +void *xmalloc(size_t size); + +/* main.c */ +const char *_getprogname(void); + +/* getopt.c */ +#if !HAVE_UNISTD_H +extern char *optarg; +int getopt(int argc, char **argv, const char *options); +#endif + +/* ASSERT() is for debug checks, CHECK() for run-time sanity checks. + * DEBUG_CHECKS is for expensive debug checks that we only want to + * enable in debug builds but still want type-checked by the compiler + * in release builds. + */ +#if defined(NDEBUG) +# define ASSERT(exp) +# define CHECK(exp) do { if (!(exp)) abort(); } while (0) +# define DEBUG_CHECKS (0) +#else +# define ASSERT(exp) assert(exp) +# define CHECK(exp) assert(exp) +# define DEBUG_CHECKS (1) +#endif + +#define UNREACHABLE() CHECK(!"Unreachable code reached.") + +/* This macro looks complicated but it's not: it calculates the address + * of the embedding struct through the address of the embedded struct. + * In other words, if struct A embeds struct B, then we can obtain + * the address of A by taking the address of B and subtracting the + * field offset of B in A. + */ +#define CONTAINER_OF(ptr, type, field) \ + ((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field))) + +#endif /* DEFS_H_ */ diff --git a/3rdparty/libuv/samples/socks5-proxy/getopt.c b/3rdparty/libuv/samples/socks5-proxy/getopt.c new file mode 100644 index 00000000000..8481b2264f2 --- /dev/null +++ b/3rdparty/libuv/samples/socks5-proxy/getopt.c @@ -0,0 +1,131 @@ +/* $NetBSD: getopt.c,v 1.26 2003/08/07 16:43:40 agc Exp $ */ + +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; +#endif /* LIBC_SCCS and not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +extern const char *_getprogname(void); + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(nargc, nargv, ostr) + int nargc; + char * const nargv[]; + const char *ostr; +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + if (optreset || *place == 0) { /* update scanning pointer */ + optreset = 0; + place = nargv[optind]; + if (optind >= nargc || *place++ != '-') { + /* Argument is absent or is not an option */ + place = EMSG; + return (-1); + } + optopt = *place++; + if (optopt == '-' && *place == 0) { + /* "--" => end of options */ + ++optind; + place = EMSG; + return (-1); + } + if (optopt == 0) { + /* Solitary '-', treat as a '-' option + if the program (eg su) is looking for it. */ + place = EMSG; + if (strchr(ostr, '-') == NULL) + return (-1); + optopt = '-'; + } + } else + optopt = *place++; + + /* See if option letter is one the caller wanted... */ + if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) { + if (*place == 0) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(stderr, + "%s: illegal option -- %c\n", _getprogname(), + optopt); + return (BADCH); + } + + /* Does this option need an argument? */ + if (oli[1] != ':') { + /* don't need argument */ + optarg = NULL; + if (*place == 0) + ++optind; + } else { + /* Option-argument is either the rest of this argument or the + entire next argument. */ + if (*place) + optarg = place; + else if (nargc > ++optind) + optarg = nargv[optind]; + else { + /* option-argument absent */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + _getprogname(), optopt); + return (BADCH); + } + place = EMSG; + ++optind; + } + return (optopt); /* return option letter */ +} diff --git a/3rdparty/libuv/samples/socks5-proxy/main.c b/3rdparty/libuv/samples/socks5-proxy/main.c new file mode 100644 index 00000000000..04020cbd3ad --- /dev/null +++ b/3rdparty/libuv/samples/socks5-proxy/main.c @@ -0,0 +1,99 @@ +/* Copyright StrongLoop, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "defs.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if HAVE_UNISTD_H +#include <unistd.h> /* getopt */ +#endif + +#define DEFAULT_BIND_HOST "127.0.0.1" +#define DEFAULT_BIND_PORT 1080 +#define DEFAULT_IDLE_TIMEOUT (60 * 1000) + +static void parse_opts(server_config *cf, int argc, char **argv); +static void usage(void); + +static const char *progname = __FILE__; /* Reset in main(). */ + +int main(int argc, char **argv) { + server_config config; + int err; + + progname = argv[0]; + memset(&config, 0, sizeof(config)); + config.bind_host = DEFAULT_BIND_HOST; + config.bind_port = DEFAULT_BIND_PORT; + config.idle_timeout = DEFAULT_IDLE_TIMEOUT; + parse_opts(&config, argc, argv); + + err = server_run(&config, uv_default_loop()); + if (err) { + exit(1); + } + + return 0; +} + +const char *_getprogname(void) { + return progname; +} + +static void parse_opts(server_config *cf, int argc, char **argv) { + int opt; + + while (-1 != (opt = getopt(argc, argv, "H:hp:"))) { + switch (opt) { + case 'H': + cf->bind_host = optarg; + break; + + case 'p': + if (1 != sscanf(optarg, "%hu", &cf->bind_port)) { + pr_err("bad port number: %s", optarg); + usage(); + } + break; + + default: + usage(); + } + } +} + +static void usage(void) { + printf("Usage:\n" + "\n" + " %s [-b <address> [-h] [-p <port>]\n" + "\n" + "Options:\n" + "\n" + " -b <hostname|address> Bind to this address or hostname.\n" + " Default: \"127.0.0.1\"\n" + " -h Show this help message.\n" + " -p <port> Bind to this port number. Default: 1080\n" + "", + progname); + exit(1); +} diff --git a/3rdparty/libuv/samples/socks5-proxy/s5.c b/3rdparty/libuv/samples/socks5-proxy/s5.c new file mode 100644 index 00000000000..4f08e345247 --- /dev/null +++ b/3rdparty/libuv/samples/socks5-proxy/s5.c @@ -0,0 +1,271 @@ +/* Copyright StrongLoop, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "s5.h" +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> /* abort() */ +#include <string.h> /* memset() */ + +enum { + s5_version, + s5_nmethods, + s5_methods, + s5_auth_pw_version, + s5_auth_pw_userlen, + s5_auth_pw_username, + s5_auth_pw_passlen, + s5_auth_pw_password, + s5_req_version, + s5_req_cmd, + s5_req_reserved, + s5_req_atyp, + s5_req_atyp_host, + s5_req_daddr, + s5_req_dport0, + s5_req_dport1, + s5_dead +}; + +void s5_init(s5_ctx *cx) { + memset(cx, 0, sizeof(*cx)); + cx->state = s5_version; +} + +s5_err s5_parse(s5_ctx *cx, uint8_t **data, size_t *size) { + s5_err err; + uint8_t *p; + uint8_t c; + size_t i; + size_t n; + + p = *data; + n = *size; + i = 0; + + while (i < n) { + c = p[i]; + i += 1; + switch (cx->state) { + case s5_version: + if (c != 5) { + err = s5_bad_version; + goto out; + } + cx->state = s5_nmethods; + break; + + case s5_nmethods: + cx->arg0 = 0; + cx->arg1 = c; /* Number of bytes to read. */ + cx->state = s5_methods; + break; + + case s5_methods: + if (cx->arg0 < cx->arg1) { + switch (c) { + case 0: + cx->methods |= S5_AUTH_NONE; + break; + case 1: + cx->methods |= S5_AUTH_GSSAPI; + break; + case 2: + cx->methods |= S5_AUTH_PASSWD; + break; + /* Ignore everything we don't understand. */ + } + cx->arg0 += 1; + } + if (cx->arg0 == cx->arg1) { + err = s5_auth_select; + goto out; + } + break; + + case s5_auth_pw_version: + if (c != 1) { + err = s5_bad_version; + goto out; + } + cx->state = s5_auth_pw_userlen; + break; + + case s5_auth_pw_userlen: + cx->arg0 = 0; + cx->userlen = c; + cx->state = s5_auth_pw_username; + break; + + case s5_auth_pw_username: + if (cx->arg0 < cx->userlen) { + cx->username[cx->arg0] = c; + cx->arg0 += 1; + } + if (cx->arg0 == cx->userlen) { + cx->username[cx->userlen] = '\0'; + cx->state = s5_auth_pw_passlen; + } + break; + + case s5_auth_pw_passlen: + cx->arg0 = 0; + cx->passlen = c; + cx->state = s5_auth_pw_password; + break; + + case s5_auth_pw_password: + if (cx->arg0 < cx->passlen) { + cx->password[cx->arg0] = c; + cx->arg0 += 1; + } + if (cx->arg0 == cx->passlen) { + cx->password[cx->passlen] = '\0'; + cx->state = s5_req_version; + err = s5_auth_verify; + goto out; + } + break; + + case s5_req_version: + if (c != 5) { + err = s5_bad_version; + goto out; + } + cx->state = s5_req_cmd; + break; + + case s5_req_cmd: + switch (c) { + case 1: /* TCP connect */ + cx->cmd = s5_cmd_tcp_connect; + break; + case 3: /* UDP associate */ + cx->cmd = s5_cmd_udp_assoc; + break; + default: + err = s5_bad_cmd; + goto out; + } + cx->state = s5_req_reserved; + break; + + case s5_req_reserved: + cx->state = s5_req_atyp; + break; + + case s5_req_atyp: + cx->arg0 = 0; + switch (c) { + case 1: /* IPv4, four octets. */ + cx->state = s5_req_daddr; + cx->atyp = s5_atyp_ipv4; + cx->arg1 = 4; + break; + case 3: /* Hostname. First byte is length. */ + cx->state = s5_req_atyp_host; + cx->atyp = s5_atyp_host; + cx->arg1 = 0; + break; + case 4: /* IPv6, sixteen octets. */ + cx->state = s5_req_daddr; + cx->atyp = s5_atyp_ipv6; + cx->arg1 = 16; + break; + default: + err = s5_bad_atyp; + goto out; + } + break; + + case s5_req_atyp_host: + cx->arg1 = c; + cx->state = s5_req_daddr; + break; + + case s5_req_daddr: + if (cx->arg0 < cx->arg1) { + cx->daddr[cx->arg0] = c; + cx->arg0 += 1; + } + if (cx->arg0 == cx->arg1) { + cx->daddr[cx->arg1] = '\0'; + cx->state = s5_req_dport0; + } + break; + + case s5_req_dport0: + cx->dport = c << 8; + cx->state = s5_req_dport1; + break; + + case s5_req_dport1: + cx->dport |= c; + cx->state = s5_dead; + err = s5_exec_cmd; + goto out; + + case s5_dead: + break; + + default: + abort(); + } + } + err = s5_ok; + +out: + *data = p + i; + *size = n - i; + return err; +} + +unsigned int s5_auth_methods(const s5_ctx *cx) { + return cx->methods; +} + +int s5_select_auth(s5_ctx *cx, s5_auth_method method) { + int err; + + err = 0; + switch (method) { + case S5_AUTH_NONE: + cx->state = s5_req_version; + break; + case S5_AUTH_PASSWD: + cx->state = s5_auth_pw_version; + break; + default: + err = -EINVAL; + } + + return err; +} + +const char *s5_strerror(s5_err err) { +#define S5_ERR_GEN(_, name, errmsg) case s5_ ## name: return errmsg; + switch (err) { + S5_ERR_MAP(S5_ERR_GEN) + default: ; /* Silence s5_max_errors -Wswitch warning. */ + } +#undef S5_ERR_GEN + return "Unknown error."; +} diff --git a/3rdparty/libuv/samples/socks5-proxy/s5.h b/3rdparty/libuv/samples/socks5-proxy/s5.h new file mode 100644 index 00000000000..715f322287d --- /dev/null +++ b/3rdparty/libuv/samples/socks5-proxy/s5.h @@ -0,0 +1,94 @@ +/* Copyright StrongLoop, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef S5_H_ +#define S5_H_ + +#include <stddef.h> +#include <stdint.h> + +#define S5_ERR_MAP(V) \ + V(-1, bad_version, "Bad protocol version.") \ + V(-2, bad_cmd, "Bad protocol command.") \ + V(-3, bad_atyp, "Bad address type.") \ + V(0, ok, "No error.") \ + V(1, auth_select, "Select authentication method.") \ + V(2, auth_verify, "Verify authentication.") \ + V(3, exec_cmd, "Execute command.") \ + +typedef enum { +#define S5_ERR_GEN(code, name, _) s5_ ## name = code, + S5_ERR_MAP(S5_ERR_GEN) +#undef S5_ERR_GEN + s5_max_errors +} s5_err; + +typedef enum { + S5_AUTH_NONE = 1 << 0, + S5_AUTH_GSSAPI = 1 << 1, + S5_AUTH_PASSWD = 1 << 2 +} s5_auth_method; + +typedef enum { + s5_auth_allow, + s5_auth_deny +} s5_auth_result; + +typedef enum { + s5_atyp_ipv4, + s5_atyp_ipv6, + s5_atyp_host +} s5_atyp; + +typedef enum { + s5_cmd_tcp_connect, + s5_cmd_tcp_bind, + s5_cmd_udp_assoc +} s5_cmd; + +typedef struct { + uint32_t arg0; /* Scratch space for the state machine. */ + uint32_t arg1; /* Scratch space for the state machine. */ + uint8_t state; + uint8_t methods; + uint8_t cmd; + uint8_t atyp; + uint8_t userlen; + uint8_t passlen; + uint16_t dport; + uint8_t username[257]; + uint8_t password[257]; + uint8_t daddr[257]; /* TODO(bnoordhuis) Merge with username/password. */ +} s5_ctx; + +void s5_init(s5_ctx *ctx); + +s5_err s5_parse(s5_ctx *cx, uint8_t **data, size_t *size); + +/* Only call after s5_parse() has returned s5_want_auth_method. */ +unsigned int s5_auth_methods(const s5_ctx *cx); + +/* Call after s5_parse() has returned s5_want_auth_method. */ +int s5_select_auth(s5_ctx *cx, s5_auth_method method); + +const char *s5_strerror(s5_err err); + +#endif /* S5_H_ */ diff --git a/3rdparty/libuv/samples/socks5-proxy/server.c b/3rdparty/libuv/samples/socks5-proxy/server.c new file mode 100644 index 00000000000..3f1ba42c9e1 --- /dev/null +++ b/3rdparty/libuv/samples/socks5-proxy/server.c @@ -0,0 +1,241 @@ +/* Copyright StrongLoop, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "defs.h" +#include <netinet/in.h> /* INET6_ADDRSTRLEN */ +#include <stdlib.h> +#include <string.h> + +#ifndef INET6_ADDRSTRLEN +# define INET6_ADDRSTRLEN 63 +#endif + +typedef struct { + uv_getaddrinfo_t getaddrinfo_req; + server_config config; + server_ctx *servers; + uv_loop_t *loop; +} server_state; + +static void do_bind(uv_getaddrinfo_t *req, int status, struct addrinfo *ai); +static void on_connection(uv_stream_t *server, int status); + +int server_run(const server_config *cf, uv_loop_t *loop) { + struct addrinfo hints; + server_state state; + int err; + + memset(&state, 0, sizeof(state)); + state.servers = NULL; + state.config = *cf; + state.loop = loop; + + /* Resolve the address of the interface that we should bind to. + * The getaddrinfo callback starts the server and everything else. + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + err = uv_getaddrinfo(loop, + &state.getaddrinfo_req, + do_bind, + cf->bind_host, + NULL, + &hints); + if (err != 0) { + pr_err("getaddrinfo: %s", uv_strerror(err)); + return err; + } + + /* Start the event loop. Control continues in do_bind(). */ + if (uv_run(loop, UV_RUN_DEFAULT)) { + abort(); + } + + /* Please Valgrind. */ + uv_loop_delete(loop); + free(state.servers); + return 0; +} + +/* Bind a server to each address that getaddrinfo() reported. */ +static void do_bind(uv_getaddrinfo_t *req, int status, struct addrinfo *addrs) { + char addrbuf[INET6_ADDRSTRLEN + 1]; + unsigned int ipv4_naddrs; + unsigned int ipv6_naddrs; + server_state *state; + server_config *cf; + struct addrinfo *ai; + const void *addrv; + const char *what; + uv_loop_t *loop; + server_ctx *sx; + unsigned int n; + int err; + union { + struct sockaddr addr; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + } s; + + state = CONTAINER_OF(req, server_state, getaddrinfo_req); + loop = state->loop; + cf = &state->config; + + if (status < 0) { + pr_err("getaddrinfo(\"%s\"): %s", cf->bind_host, uv_strerror(status)); + uv_freeaddrinfo(addrs); + return; + } + + ipv4_naddrs = 0; + ipv6_naddrs = 0; + for (ai = addrs; ai != NULL; ai = ai->ai_next) { + if (ai->ai_family == AF_INET) { + ipv4_naddrs += 1; + } else if (ai->ai_family == AF_INET6) { + ipv6_naddrs += 1; + } + } + + if (ipv4_naddrs == 0 && ipv6_naddrs == 0) { + pr_err("%s has no IPv4/6 addresses", cf->bind_host); + uv_freeaddrinfo(addrs); + return; + } + + state->servers = + xmalloc((ipv4_naddrs + ipv6_naddrs) * sizeof(state->servers[0])); + + n = 0; + for (ai = addrs; ai != NULL; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) { + continue; + } + + if (ai->ai_family == AF_INET) { + s.addr4 = *(const struct sockaddr_in *) ai->ai_addr; + s.addr4.sin_port = htons(cf->bind_port); + addrv = &s.addr4.sin_addr; + } else if (ai->ai_family == AF_INET6) { + s.addr6 = *(const struct sockaddr_in6 *) ai->ai_addr; + s.addr6.sin6_port = htons(cf->bind_port); + addrv = &s.addr6.sin6_addr; + } else { + UNREACHABLE(); + } + + if (uv_inet_ntop(s.addr.sa_family, addrv, addrbuf, sizeof(addrbuf))) { + UNREACHABLE(); + } + + sx = state->servers + n; + sx->loop = loop; + sx->idle_timeout = state->config.idle_timeout; + CHECK(0 == uv_tcp_init(loop, &sx->tcp_handle)); + + what = "uv_tcp_bind"; + err = uv_tcp_bind(&sx->tcp_handle, &s.addr, 0); + if (err == 0) { + what = "uv_listen"; + err = uv_listen((uv_stream_t *) &sx->tcp_handle, 128, on_connection); + } + + if (err != 0) { + pr_err("%s(\"%s:%hu\"): %s", + what, + addrbuf, + cf->bind_port, + uv_strerror(err)); + while (n > 0) { + n -= 1; + uv_close((uv_handle_t *) (state->servers + n), NULL); + } + break; + } + + pr_info("listening on %s:%hu", addrbuf, cf->bind_port); + n += 1; + } + + uv_freeaddrinfo(addrs); +} + +static void on_connection(uv_stream_t *server, int status) { + server_ctx *sx; + client_ctx *cx; + + CHECK(status == 0); + sx = CONTAINER_OF(server, server_ctx, tcp_handle); + cx = xmalloc(sizeof(*cx)); + CHECK(0 == uv_tcp_init(sx->loop, &cx->incoming.handle.tcp)); + CHECK(0 == uv_accept(server, &cx->incoming.handle.stream)); + client_finish_init(sx, cx); +} + +int can_auth_none(const server_ctx *sx, const client_ctx *cx) { + return 1; +} + +int can_auth_passwd(const server_ctx *sx, const client_ctx *cx) { + return 0; +} + +int can_access(const server_ctx *sx, + const client_ctx *cx, + const struct sockaddr *addr) { + const struct sockaddr_in6 *addr6; + const struct sockaddr_in *addr4; + const uint32_t *p; + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + + /* TODO(bnoordhuis) Implement proper access checks. For now, just reject + * traffic to localhost. + */ + if (addr->sa_family == AF_INET) { + addr4 = (const struct sockaddr_in *) addr; + d = ntohl(addr4->sin_addr.s_addr); + return (d >> 24) != 0x7F; + } + + if (addr->sa_family == AF_INET6) { + addr6 = (const struct sockaddr_in6 *) addr; + p = (const uint32_t *) &addr6->sin6_addr.s6_addr; + a = ntohl(p[0]); + b = ntohl(p[1]); + c = ntohl(p[2]); + d = ntohl(p[3]); + if (a == 0 && b == 0 && c == 0 && d == 1) { + return 0; /* "::1" style address. */ + } + if (a == 0 && b == 0 && c == 0xFFFF && (d >> 24) == 0x7F) { + return 0; /* "::ffff:127.x.x.x" style address. */ + } + return 1; + } + + return 0; +} diff --git a/3rdparty/libuv/samples/socks5-proxy/util.c b/3rdparty/libuv/samples/socks5-proxy/util.c new file mode 100644 index 00000000000..af34f055936 --- /dev/null +++ b/3rdparty/libuv/samples/socks5-proxy/util.c @@ -0,0 +1,72 @@ +/* Copyright StrongLoop, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "defs.h" +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +static void pr_do(FILE *stream, + const char *label, + const char *fmt, + va_list ap); + +void *xmalloc(size_t size) { + void *ptr; + + ptr = malloc(size); + if (ptr == NULL) { + pr_err("out of memory, need %lu bytes", (unsigned long) size); + exit(1); + } + + return ptr; +} + +void pr_info(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + pr_do(stdout, "info", fmt, ap); + va_end(ap); +} + +void pr_warn(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + pr_do(stderr, "warn", fmt, ap); + va_end(ap); +} + +void pr_err(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + pr_do(stderr, "error", fmt, ap); + va_end(ap); +} + +static void pr_do(FILE *stream, + const char *label, + const char *fmt, + va_list ap) { + char fmtbuf[1024]; + vsnprintf(fmtbuf, sizeof(fmtbuf), fmt, ap); + fprintf(stream, "%s:%s: %s\n", _getprogname(), label, fmtbuf); +} |