mirror of https://github.com/lukechilds/node.git
56 changed files with 3019 additions and 347 deletions
@ -0,0 +1,22 @@ |
|||||
|
# 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. |
||||
|
|
||||
|
*.mk |
||||
|
*.Makefile |
@ -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/ |
@ -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. |
@ -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'] |
||||
|
}] |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
} |
@ -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); |
||||
|
} |
@ -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_ */ |
@ -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 */ |
||||
|
} |
@ -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); |
||||
|
} |
@ -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."; |
||||
|
} |
@ -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_ */ |
@ -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); |
||||
|
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; |
||||
|
} |
@ -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); |
||||
|
} |
@ -0,0 +1,77 @@ |
|||||
|
/* Copyright Joyent, Inc. and other Node contributors. 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. |
||||
|
*/ |
||||
|
|
||||
|
#if !defined(_WIN32) |
||||
|
|
||||
|
#include "uv.h" |
||||
|
#include "task.h" |
||||
|
#include <fcntl.h> |
||||
|
#include <unistd.h> |
||||
|
|
||||
|
static unsigned int read_cb_called; |
||||
|
|
||||
|
static void alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) { |
||||
|
static char slab[1]; |
||||
|
buf->base = slab; |
||||
|
buf->len = sizeof(slab); |
||||
|
} |
||||
|
|
||||
|
static void read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) { |
||||
|
switch (++read_cb_called) { |
||||
|
case 1: |
||||
|
ASSERT(nread == 1); |
||||
|
uv_read_stop(handle); |
||||
|
break; |
||||
|
case 2: |
||||
|
ASSERT(nread == UV_EOF); |
||||
|
uv_close((uv_handle_t *) handle, NULL); |
||||
|
break; |
||||
|
default: |
||||
|
ASSERT(!"read_cb_called > 2"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
TEST_IMPL(close_fd) { |
||||
|
uv_pipe_t pipe_handle; |
||||
|
int fd[2]; |
||||
|
|
||||
|
ASSERT(0 == pipe(fd)); |
||||
|
ASSERT(0 == fcntl(fd[0], F_SETFL, O_NONBLOCK)); |
||||
|
ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); |
||||
|
ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); |
||||
|
fd[0] = -1; /* uv_pipe_open() takes ownership of the file descriptor. */ |
||||
|
ASSERT(1 == write(fd[1], "", 1)); |
||||
|
ASSERT(0 == close(fd[1])); |
||||
|
fd[1] = -1; |
||||
|
ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb)); |
||||
|
ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); |
||||
|
ASSERT(1 == read_cb_called); |
||||
|
ASSERT(0 == uv_is_active((const uv_handle_t *) &pipe_handle)); |
||||
|
ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb)); |
||||
|
ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); |
||||
|
ASSERT(2 == read_cb_called); |
||||
|
ASSERT(0 != uv_is_closing((const uv_handle_t *) &pipe_handle)); |
||||
|
|
||||
|
MAKE_VALGRIND_HAPPY(); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
#endif /* !defined(_WIN32) */ |
@ -0,0 +1,183 @@ |
|||||
|
/* Copyright Joyent, Inc. and other Node contributors. 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 "uv.h" |
||||
|
#include "task.h" |
||||
|
|
||||
|
#include <stdio.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
static struct sockaddr_in addr; |
||||
|
static uv_tcp_t tcp_server; |
||||
|
static uv_tcp_t tcp_outgoing[2]; |
||||
|
static uv_tcp_t tcp_incoming[ARRAY_SIZE(tcp_outgoing)]; |
||||
|
static uv_connect_t connect_reqs[ARRAY_SIZE(tcp_outgoing)]; |
||||
|
static uv_tcp_t tcp_check; |
||||
|
static uv_connect_t tcp_check_req; |
||||
|
static uv_write_t write_reqs[ARRAY_SIZE(tcp_outgoing)]; |
||||
|
static unsigned int got_connections; |
||||
|
static unsigned int close_cb_called; |
||||
|
static unsigned int write_cb_called; |
||||
|
static unsigned int read_cb_called; |
||||
|
|
||||
|
static void close_cb(uv_handle_t* handle) { |
||||
|
close_cb_called++; |
||||
|
} |
||||
|
|
||||
|
static void write_cb(uv_write_t* req, int status) { |
||||
|
ASSERT(status == 0); |
||||
|
write_cb_called++; |
||||
|
} |
||||
|
|
||||
|
static void connect_cb(uv_connect_t* req, int status) { |
||||
|
unsigned int i; |
||||
|
uv_buf_t buf; |
||||
|
uv_stream_t* outgoing; |
||||
|
|
||||
|
if (req == &tcp_check_req) { |
||||
|
ASSERT(status != 0); |
||||
|
|
||||
|
/* Close check and incoming[0], time to finish test */ |
||||
|
uv_close((uv_handle_t*) &tcp_incoming[0], close_cb); |
||||
|
uv_close((uv_handle_t*) &tcp_check, close_cb); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ASSERT(status == 0); |
||||
|
ASSERT(connect_reqs <= req); |
||||
|
ASSERT(req <= connect_reqs + ARRAY_SIZE(connect_reqs)); |
||||
|
i = req - connect_reqs; |
||||
|
|
||||
|
buf = uv_buf_init("x", 1); |
||||
|
outgoing = (uv_stream_t*) &tcp_outgoing[i]; |
||||
|
ASSERT(0 == uv_write(&write_reqs[i], outgoing, &buf, 1, write_cb)); |
||||
|
} |
||||
|
|
||||
|
static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { |
||||
|
static char slab[1]; |
||||
|
buf->base = slab; |
||||
|
buf->len = sizeof(slab); |
||||
|
} |
||||
|
|
||||
|
static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { |
||||
|
uv_loop_t* loop; |
||||
|
unsigned int i; |
||||
|
|
||||
|
/* Only first stream should receive read events */ |
||||
|
ASSERT(stream == (uv_stream_t*) &tcp_incoming[0]); |
||||
|
ASSERT(0 == uv_read_stop(stream)); |
||||
|
ASSERT(1 == nread); |
||||
|
|
||||
|
loop = stream->loop; |
||||
|
read_cb_called++; |
||||
|
|
||||
|
/* Close all active incomings, except current one */ |
||||
|
for (i = 1; i < got_connections; i++) |
||||
|
uv_close((uv_handle_t*) &tcp_incoming[i], close_cb); |
||||
|
|
||||
|
/* Create new fd that should be one of the closed incomings */ |
||||
|
ASSERT(0 == uv_tcp_init(loop, &tcp_check)); |
||||
|
ASSERT(0 == uv_tcp_connect(&tcp_check_req, |
||||
|
&tcp_check, |
||||
|
(const struct sockaddr*) &addr, |
||||
|
connect_cb)); |
||||
|
ASSERT(0 == uv_read_start((uv_stream_t*) &tcp_check, alloc_cb, read_cb)); |
||||
|
|
||||
|
/* Close server, so no one will connect to it */ |
||||
|
uv_close((uv_handle_t*) &tcp_server, close_cb); |
||||
|
} |
||||
|
|
||||
|
static void connection_cb(uv_stream_t* server, int status) { |
||||
|
unsigned int i; |
||||
|
uv_tcp_t* incoming; |
||||
|
|
||||
|
ASSERT(server == (uv_stream_t*) &tcp_server); |
||||
|
|
||||
|
/* Ignore tcp_check connection */ |
||||
|
if (got_connections == ARRAY_SIZE(tcp_incoming)) |
||||
|
return; |
||||
|
|
||||
|
/* Accept everyone */ |
||||
|
incoming = &tcp_incoming[got_connections++]; |
||||
|
ASSERT(0 == uv_tcp_init(server->loop, incoming)); |
||||
|
ASSERT(0 == uv_accept(server, (uv_stream_t*) incoming)); |
||||
|
|
||||
|
if (got_connections != ARRAY_SIZE(tcp_incoming)) |
||||
|
return; |
||||
|
|
||||
|
/* Once all clients are accepted - start reading */ |
||||
|
for (i = 0; i < ARRAY_SIZE(tcp_incoming); i++) { |
||||
|
incoming = &tcp_incoming[i]; |
||||
|
ASSERT(0 == uv_read_start((uv_stream_t*) incoming, alloc_cb, read_cb)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
TEST_IMPL(tcp_close_accept) { |
||||
|
unsigned int i; |
||||
|
uv_loop_t* loop; |
||||
|
uv_tcp_t* client; |
||||
|
|
||||
|
/*
|
||||
|
* A little explanation of what goes on below: |
||||
|
* |
||||
|
* We'll create server and connect to it using two clients, each writing one |
||||
|
* byte once connected. |
||||
|
* |
||||
|
* When all clients will be accepted by server - we'll start reading from them |
||||
|
* and, on first client's first byte, will close second client and server. |
||||
|
* After that, we'll immediately initiate new connection to server using |
||||
|
* tcp_check handle (thus, reusing fd from second client). |
||||
|
* |
||||
|
* In this situation uv__io_poll()'s event list should still contain read |
||||
|
* event for second client, and, if not cleaned up properly, `tcp_check` will |
||||
|
* receive stale event of second incoming and invoke `connect_cb` with zero |
||||
|
* status. |
||||
|
*/ |
||||
|
|
||||
|
loop = uv_default_loop(); |
||||
|
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); |
||||
|
|
||||
|
ASSERT(0 == uv_tcp_init(loop, &tcp_server)); |
||||
|
ASSERT(0 == uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr)); |
||||
|
ASSERT(0 == uv_listen((uv_stream_t*) &tcp_server, |
||||
|
ARRAY_SIZE(tcp_outgoing), |
||||
|
connection_cb)); |
||||
|
|
||||
|
for (i = 0; i < ARRAY_SIZE(tcp_outgoing); i++) { |
||||
|
client = tcp_outgoing + i; |
||||
|
|
||||
|
ASSERT(0 == uv_tcp_init(loop, client)); |
||||
|
ASSERT(0 == uv_tcp_connect(&connect_reqs[i], |
||||
|
client, |
||||
|
(const struct sockaddr*) &addr, |
||||
|
connect_cb)); |
||||
|
} |
||||
|
|
||||
|
uv_run(loop, UV_RUN_DEFAULT); |
||||
|
|
||||
|
ASSERT(ARRAY_SIZE(tcp_outgoing) == got_connections); |
||||
|
ASSERT((ARRAY_SIZE(tcp_outgoing) + 2) == close_cb_called); |
||||
|
ASSERT(ARRAY_SIZE(tcp_outgoing) == write_cb_called); |
||||
|
ASSERT(1 == read_cb_called); |
||||
|
|
||||
|
MAKE_VALGRIND_HAPPY(); |
||||
|
return 0; |
||||
|
} |
@ -1,97 +0,0 @@ |
|||||
/* Copyright Joyent, Inc. and other Node contributors. 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 "uv.h" |
|
||||
#include "task.h" |
|
||||
|
|
||||
#include <string.h> |
|
||||
|
|
||||
#define memeq(a, b, c) (memcmp((a), (b), (c)) == 0) |
|
||||
|
|
||||
|
|
||||
TEST_IMPL(strlcpy) { |
|
||||
size_t r; |
|
||||
|
|
||||
{ |
|
||||
char dst[2] = "A"; |
|
||||
r = uv_strlcpy(dst, "", 0); |
|
||||
ASSERT(r == 0); |
|
||||
ASSERT(memeq(dst, "A", 1)); |
|
||||
} |
|
||||
|
|
||||
{ |
|
||||
char dst[2] = "A"; |
|
||||
r = uv_strlcpy(dst, "B", 1); |
|
||||
ASSERT(r == 0); |
|
||||
ASSERT(memeq(dst, "", 1)); |
|
||||
} |
|
||||
|
|
||||
{ |
|
||||
char dst[2] = "A"; |
|
||||
r = uv_strlcpy(dst, "B", 2); |
|
||||
ASSERT(r == 1); |
|
||||
ASSERT(memeq(dst, "B", 2)); |
|
||||
} |
|
||||
|
|
||||
{ |
|
||||
char dst[3] = "AB"; |
|
||||
r = uv_strlcpy(dst, "CD", 3); |
|
||||
ASSERT(r == 2); |
|
||||
ASSERT(memeq(dst, "CD", 3)); |
|
||||
} |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
TEST_IMPL(strlcat) { |
|
||||
size_t r; |
|
||||
|
|
||||
{ |
|
||||
char dst[2] = "A"; |
|
||||
r = uv_strlcat(dst, "B", 1); |
|
||||
ASSERT(r == 1); |
|
||||
ASSERT(memeq(dst, "A", 2)); |
|
||||
} |
|
||||
|
|
||||
{ |
|
||||
char dst[2] = "A"; |
|
||||
r = uv_strlcat(dst, "B", 2); |
|
||||
ASSERT(r == 1); |
|
||||
ASSERT(memeq(dst, "A", 2)); |
|
||||
} |
|
||||
|
|
||||
{ |
|
||||
char dst[3] = "A"; |
|
||||
r = uv_strlcat(dst, "B", 3); |
|
||||
ASSERT(r == 2); |
|
||||
ASSERT(memeq(dst, "AB", 3)); |
|
||||
} |
|
||||
|
|
||||
{ |
|
||||
char dst[5] = "AB"; |
|
||||
r = uv_strlcat(dst, "CD", 5); |
|
||||
ASSERT(r == 4); |
|
||||
ASSERT(memeq(dst, "ABCD", 5)); |
|
||||
} |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
@ -0,0 +1,101 @@ |
|||||
|
/* Copyright Joyent, Inc. and other Node contributors. 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 "uv.h" |
||||
|
#include "task.h" |
||||
|
|
||||
|
#include <string.h> |
||||
|
#include <errno.h> |
||||
|
|
||||
|
/* NOTE: Number should be big enough to trigger this problem */ |
||||
|
static uv_udp_t sockets[2500]; |
||||
|
static uv_udp_send_t reqs[ARRAY_SIZE(sockets)]; |
||||
|
static char slab[1]; |
||||
|
static unsigned int recv_cb_called; |
||||
|
static unsigned int send_cb_called; |
||||
|
static unsigned int close_cb_called; |
||||
|
|
||||
|
static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { |
||||
|
buf->base = slab; |
||||
|
buf->len = sizeof(slab); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void recv_cb(uv_udp_t* handle, |
||||
|
ssize_t nread, |
||||
|
const uv_buf_t* buf, |
||||
|
const struct sockaddr* addr, |
||||
|
unsigned flags) { |
||||
|
recv_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void send_cb(uv_udp_send_t* req, int status) { |
||||
|
send_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void close_cb(uv_handle_t* handle) { |
||||
|
close_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST_IMPL(watcher_cross_stop) { |
||||
|
uv_loop_t* loop = uv_default_loop(); |
||||
|
unsigned int i; |
||||
|
struct sockaddr_in addr; |
||||
|
uv_buf_t buf; |
||||
|
char big_string[1024]; |
||||
|
|
||||
|
TEST_FILE_LIMIT(ARRAY_SIZE(sockets) + 32); |
||||
|
|
||||
|
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); |
||||
|
memset(big_string, 'A', sizeof(big_string)); |
||||
|
buf = uv_buf_init(big_string, sizeof(big_string)); |
||||
|
|
||||
|
for (i = 0; i < ARRAY_SIZE(sockets); i++) { |
||||
|
ASSERT(0 == uv_udp_init(loop, &sockets[i])); |
||||
|
ASSERT(0 == uv_udp_bind(&sockets[i], (const struct sockaddr*) &addr, 0)); |
||||
|
ASSERT(0 == uv_udp_recv_start(&sockets[i], alloc_cb, recv_cb)); |
||||
|
ASSERT(0 == uv_udp_send(&reqs[i], |
||||
|
&sockets[i], |
||||
|
&buf, |
||||
|
1, |
||||
|
(const struct sockaddr*) &addr, |
||||
|
send_cb)); |
||||
|
} |
||||
|
|
||||
|
while (recv_cb_called == 0) |
||||
|
uv_run(loop, UV_RUN_ONCE); |
||||
|
|
||||
|
for (i = 0; i < ARRAY_SIZE(sockets); i++) |
||||
|
uv_close((uv_handle_t*) &sockets[i], close_cb); |
||||
|
|
||||
|
ASSERT(0 < recv_cb_called && recv_cb_called <= ARRAY_SIZE(sockets)); |
||||
|
ASSERT(ARRAY_SIZE(sockets) == send_cb_called); |
||||
|
|
||||
|
uv_run(loop, UV_RUN_DEFAULT); |
||||
|
|
||||
|
ASSERT(ARRAY_SIZE(sockets) == close_cb_called); |
||||
|
|
||||
|
MAKE_VALGRIND_HAPPY(); |
||||
|
return 0; |
||||
|
} |
Loading…
Reference in new issue