diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index 4d56a3b0241..a9c63bf6054 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -97,3 +97,5 @@ Luca Bruno Reini Urban Maks Naumov Sean Farrell +Chris Bank +Geert Jansen diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index ef3cfd3c82a..0e5638f887d 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,4 +1,72 @@ -2013.10.30, Version 0.11.14 (Unstable) +2013.11.21, Version 0.11.15 (Unstable) + +Changes since version 0.11.14: + +* fsevents: report errors to user (Fedor Indutny) + +* include: UV_FS_EVENT_RECURSIVE is a flag (Fedor Indutny) + +* linux: use CLOCK_MONOTONIC_COARSE if available (Ben Noordhuis) + +* build: make systemtap probes work with gyp build (Ben Noordhuis) + +* unix: update events from pevents between polls (Fedor Indutny) + +* fsevents: support japaneese characters in path (Chris Bank) + +* linux: don't turn on SO_REUSEPORT socket option (Ben Noordhuis) + +* queue: strengthen type checks (Ben Noordhuis) + +* include: remove uv_strlcat() and uv_strlcpy() (Ben Noordhuis) + +* build: fix windows smp build with gyp (Geert Jansen) + +* unix: return exec errors from uv_spawn, not async (Alex Crichton) + +* fsevents: use native character encoding file paths (Ben Noordhuis) + +* linux: handle EPOLLHUP without EPOLLIN/EPOLLOUT (Ben Noordhuis) + +* windows: use _snwprintf(), not swprintf() (Ben Noordhuis) + +* fsevents: use FlagNoDefer for FSEventStreamCreate (Fedor Indutny) + +* unix: fix reopened fd bug (Fedor Indutny) + +* core: fix fake watcher list and count preservation (Fedor Indutny) + +* unix: set close-on-exec flag on received fds (Ben Noordhuis) + +* netbsd, openbsd: enable futimes() wrapper (Ben Noordhuis) + +* unix: nicer error message when kqueue() fails (Ben Noordhuis) + +* samples: add socks5 proxy sample application (Ben Noordhuis) + + +2013.11.13, Version 0.10.19 (Stable), 33959f7524090b8d2c6c41e2400ca77e31755059 + +Changes since version 0.10.18: + +* darwin: avoid calling GetCurrentProcess (Fedor Indutny) + +* unix: update events from pevents between polls (Fedor Indutny) + +* fsevents: support japaneese characters in path (Chris Bank) + +* linux: don't turn on SO_REUSEPORT socket option (Ben Noordhuis) + +* build: fix windows smp build with gyp (Geert Jansen) + +* linux: handle EPOLLHUP without EPOLLIN/EPOLLOUT (Ben Noordhuis) + +* unix: fix reopened fd bug (Fedor Indutny) + +* core: fix fake watcher list and count preservation (Fedor Indutny) + + +2013.10.30, Version 0.11.14 (Unstable), d7a6482f45c1b4eb4a853dbe1a9ce8090a35633a Changes since version 0.11.13: diff --git a/deps/uv/Makefile.am b/deps/uv/Makefile.am index 9080b940535..8f656f36f4b 100644 --- a/deps/uv/Makefile.am +++ b/deps/uv/Makefile.am @@ -118,6 +118,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-barrier.c \ test/test-callback-order.c \ test/test-callback-stack.c \ + test/test-close-fd.c \ test/test-close-order.c \ test/test-condvar.c \ test/test-connection-fail.c \ @@ -166,6 +167,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-stdio-over-pipes.c \ test/test-tcp-bind-error.c \ test/test-tcp-bind6-error.c \ + test/test-tcp-close-accept.c \ test/test-tcp-close-while-connecting.c \ test/test-tcp-close.c \ test/test-tcp-connect-error-after-write.c \ @@ -193,8 +195,8 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-udp-open.c \ test/test-udp-options.c \ test/test-udp-send-and-recv.c \ - test/test-util.c \ - test/test-walk-handles.c + test/test-walk-handles.c \ + test/test-watcher-cross-stop.c test_run_tests_LDADD = libuv.la if WINNT diff --git a/deps/uv/README.md b/deps/uv/README.md index aab9e802296..ce43f6d99e9 100644 --- a/deps/uv/README.md +++ b/deps/uv/README.md @@ -83,14 +83,14 @@ project tree manually: Run: - $ ./gyp_uv -f make + $ ./gyp_uv.py -f make $ make -C out ### OS X Run: - $ ./gyp_uv -f xcode + $ ./gyp_uv.py -f xcode $ xcodebuild -project uv.xcodeproj -configuration Release -target All ### Android diff --git a/deps/uv/android-configure b/deps/uv/android-configure index d5c937e52a1..56625761fdd 100755 --- a/deps/uv/android-configure +++ b/deps/uv/android-configure @@ -16,5 +16,5 @@ export PLATFORM=android if [ $2 -a $2 == 'gyp' ] then - ./gyp_uv -Dtarget_arch=arm -DOS=android + ./gyp_uv.py -Dtarget_arch=arm -DOS=android fi diff --git a/deps/uv/checksparse.sh b/deps/uv/checksparse.sh index c2e3e887a1c..b1d818702fc 100755 --- a/deps/uv/checksparse.sh +++ b/deps/uv/checksparse.sh @@ -132,6 +132,7 @@ test/test-stdio-over-pipes.c test/test-tcp-bind-error.c test/test-tcp-bind6-error.c test/test-tcp-close-while-connecting.c +test/test-tcp-close-accept.c test/test-tcp-close.c test/test-tcp-connect-error-after-write.c test/test-tcp-connect-error.c @@ -158,8 +159,8 @@ test/test-udp-multicast-ttl.c test/test-udp-open.c test/test-udp-options.c test/test-udp-send-and-recv.c -test/test-util.c test/test-walk-handles.c +test/test-watcher-cross-stop.c " case `uname -s` in diff --git a/deps/uv/configure.ac b/deps/uv/configure.ac index ede6c6f51a9..fda951ee14b 100644 --- a/deps/uv/configure.ac +++ b/deps/uv/configure.ac @@ -13,7 +13,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.57) -AC_INIT([libuv], [0.11.14], [https://github.com/joyent/libuv/issues]) +AC_INIT([libuv], [0.11.15], [https://github.com/joyent/libuv/issues]) AC_CONFIG_MACRO_DIR([m4]) m4_include([m4/libuv-extra-automake-flags.m4]) AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects] UV_EXTRA_AUTOMAKE_FLAGS) diff --git a/deps/uv/gyp_uv b/deps/uv/gyp_uv.py similarity index 100% rename from deps/uv/gyp_uv rename to deps/uv/gyp_uv.py diff --git a/deps/uv/include/uv-darwin.h b/deps/uv/include/uv-darwin.h index dcdd42ba6d5..24bc35b5816 100644 --- a/deps/uv/include/uv-darwin.h +++ b/deps/uv/include/uv-darwin.h @@ -47,10 +47,10 @@ char* realpath; \ int realpath_len; \ int cf_flags; \ - void* cf_event; \ uv_async_t* cf_cb; \ + void* cf_events[2]; \ void* cf_member[2]; \ - uv_sem_t _cf_reserved; \ + int cf_error; \ uv_mutex_t cf_mutex; \ #define UV_STREAM_PRIVATE_PLATFORM_FIELDS \ diff --git a/deps/uv/include/uv-unix.h b/deps/uv/include/uv-unix.h index 965fbafbbc4..45006092be6 100644 --- a/deps/uv/include/uv-unix.h +++ b/deps/uv/include/uv-unix.h @@ -287,7 +287,6 @@ typedef struct { #define UV_PROCESS_PRIVATE_FIELDS \ void* queue[2]; \ - int errorno; \ int status; \ #define UV_FS_PRIVATE_FIELDS \ diff --git a/deps/uv/include/uv-win.h b/deps/uv/include/uv-win.h index 512d7f89f7e..e4e1f839f04 100644 --- a/deps/uv/include/uv-win.h +++ b/deps/uv/include/uv-win.h @@ -533,7 +533,6 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); UV_REQ_FIELDS \ } exit_req; \ BYTE* child_stdio_buffer; \ - int spawn_error; \ int exit_signal; \ HANDLE wait_handle; \ HANDLE process_handle; \ diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 5a1d1d94886..c3ba250237a 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -567,21 +567,6 @@ UV_EXTERN void uv_close(uv_handle_t* handle, uv_close_cb close_cb); UV_EXTERN uv_buf_t uv_buf_init(char* base, unsigned int len); -/* - * Utility function. Copies up to `size` characters from `src` to `dst` - * and ensures that `dst` is properly NUL terminated unless `size` is zero. - */ -UV_EXTERN size_t uv_strlcpy(char* dst, const char* src, size_t size); - -/* - * Utility function. Appends `src` to `dst` and ensures that `dst` is - * properly NUL terminated unless `size` is zero or `dst` does not - * contain a NUL byte. `size` is the total length of `dst` so at most - * `size - strlen(dst) - 1` characters will be copied from `src`. - */ -UV_EXTERN size_t uv_strlcat(char* dst, const char* src, size_t size); - - #define UV_STREAM_FIELDS \ /* number of bytes queued for writing */ \ size_t write_queue_size; \ @@ -1504,7 +1489,17 @@ struct uv_process_s { UV_PROCESS_PRIVATE_FIELDS }; -/* Initializes uv_process_t and starts the process. */ +/* + * Initializes the uv_process_t and starts the process. If the process is + * successfully spawned, then this function will return 0. Otherwise, the + * negative error code corresponding to the reason it couldn't spawn is + * returned. + * + * Possible reasons for failing to spawn would include (but not be limited to) + * the file to execute not existing, not having permissions to use the setuid or + * setgid specified, or not having enough memory to allocate for the new + * process. + */ UV_EXTERN int uv_spawn(uv_loop_t* loop, uv_process_t* handle, const uv_process_options_t* options); @@ -1873,7 +1868,7 @@ enum uv_fs_event_flags { * flag does not affect individual files watched. * This flag is currently not implemented yet on any backend. */ - UV_FS_EVENT_WATCH_ENTRY = 1, + UV_FS_EVENT_WATCH_ENTRY = 1, /* * By default uv_fs_event will try to use a kernel interface such as inotify @@ -1889,7 +1884,7 @@ enum uv_fs_event_flags { * (is ignoring) changes in it's subdirectories. * This flag will override this behaviour on platforms that support it. */ - UV_FS_EVENT_RECURSIVE = 3 + UV_FS_EVENT_RECURSIVE = 4 }; diff --git a/deps/uv/samples/.gitignore b/deps/uv/samples/.gitignore new file mode 100644 index 00000000000..f868091ba32 --- /dev/null +++ b/deps/uv/samples/.gitignore @@ -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 diff --git a/deps/uv/samples/socks5-proxy/.gitignore b/deps/uv/samples/socks5-proxy/.gitignore new file mode 100644 index 00000000000..c177f374510 --- /dev/null +++ b/deps/uv/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/deps/uv/samples/socks5-proxy/LICENSE b/deps/uv/samples/socks5-proxy/LICENSE new file mode 100644 index 00000000000..63c1447fc55 --- /dev/null +++ b/deps/uv/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/deps/uv/samples/socks5-proxy/build.gyp b/deps/uv/samples/socks5-proxy/build.gyp new file mode 100644 index 00000000000..771a1e146db --- /dev/null +++ b/deps/uv/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/deps/uv/samples/socks5-proxy/client.c b/deps/uv/samples/socks5-proxy/client.c new file mode 100644 index 00000000000..ae9913a1c6e --- /dev/null +++ b/deps/uv/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 +#include +#include + +/* 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/deps/uv/samples/socks5-proxy/defs.h b/deps/uv/samples/socks5-proxy/defs.h new file mode 100644 index 00000000000..99ee8160c8a --- /dev/null +++ b/deps/uv/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 +#include /* sockaddr_in, sockaddr_in6 */ +#include /* size_t, ssize_t */ +#include +#include /* 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/deps/uv/samples/socks5-proxy/getopt.c b/deps/uv/samples/socks5-proxy/getopt.c new file mode 100644 index 00000000000..8481b2264f2 --- /dev/null +++ b/deps/uv/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 +#include +#include +#include + +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/deps/uv/samples/socks5-proxy/main.c b/deps/uv/samples/socks5-proxy/main.c new file mode 100644 index 00000000000..04020cbd3ad --- /dev/null +++ b/deps/uv/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 +#include +#include + +#if HAVE_UNISTD_H +#include /* 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
[-h] [-p ]\n" + "\n" + "Options:\n" + "\n" + " -b Bind to this address or hostname.\n" + " Default: \"127.0.0.1\"\n" + " -h Show this help message.\n" + " -p Bind to this port number. Default: 1080\n" + "", + progname); + exit(1); +} diff --git a/deps/uv/samples/socks5-proxy/s5.c b/deps/uv/samples/socks5-proxy/s5.c new file mode 100644 index 00000000000..4f08e345247 --- /dev/null +++ b/deps/uv/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 +#include +#include /* abort() */ +#include /* 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/deps/uv/samples/socks5-proxy/s5.h b/deps/uv/samples/socks5-proxy/s5.h new file mode 100644 index 00000000000..715f322287d --- /dev/null +++ b/deps/uv/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 +#include + +#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/deps/uv/samples/socks5-proxy/server.c b/deps/uv/samples/socks5-proxy/server.c new file mode 100644 index 00000000000..477469fac40 --- /dev/null +++ b/deps/uv/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 /* INET6_ADDRSTRLEN */ +#include +#include + +#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; +} diff --git a/deps/uv/samples/socks5-proxy/util.c b/deps/uv/samples/socks5-proxy/util.c new file mode 100644 index 00000000000..af34f055936 --- /dev/null +++ b/deps/uv/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 +#include +#include + +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); +} diff --git a/deps/uv/src/queue.h b/deps/uv/src/queue.h index aa15837d0f7..fe02b454ead 100644 --- a/deps/uv/src/queue.h +++ b/deps/uv/src/queue.h @@ -19,20 +19,20 @@ typedef void *QUEUE[2]; /* Private macros. */ -#define QUEUE_NEXT(q) ((*(q))[0]) -#define QUEUE_PREV(q) ((*(q))[1]) -#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT((QUEUE *) QUEUE_PREV(q))) -#define QUEUE_NEXT_PREV(q) (QUEUE_PREV((QUEUE *) QUEUE_NEXT(q))) +#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0])) +#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1])) +#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q))) +#define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q))) /* Public macros. */ #define QUEUE_DATA(ptr, type, field) \ ((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field))) #define QUEUE_FOREACH(q, h) \ - for ((q) = (QUEUE *) (*(h))[0]; (q) != (h); (q) = (QUEUE *) (*(q))[0]) + for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q)) #define QUEUE_EMPTY(q) \ - (QUEUE_NEXT(q) == (q)) + ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q)) #define QUEUE_HEAD(q) \ (QUEUE_NEXT(q)) diff --git a/deps/uv/src/unix/aix.c b/deps/uv/src/unix/aix.c index fe0ef421b49..25216813051 100644 --- a/deps/uv/src/unix/aix.c +++ b/deps/uv/src/unix/aix.c @@ -45,7 +45,7 @@ #include #include -uint64_t uv__hrtime(void) { +uint64_t uv__hrtime(uv_clocktype_t type) { uint64_t G = 1000000000; timebasestruct_t t; read_wall_time(&t, TIMEBASE_SZ); diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index b23f6aed116..79813a05df5 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -73,7 +73,7 @@ STATIC_ASSERT(offsetof(uv_buf_t, len) == offsetof(struct iovec, iov_len)); uint64_t uv_hrtime(void) { - return uv__hrtime(); + return uv__hrtime(UV_CLOCK_PRECISE); } @@ -542,6 +542,44 @@ int uv__dup(int fd) { } +ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { + struct cmsghdr* cmsg; + ssize_t rc; + int* pfd; + int* end; +#if defined(__linux__) + static int no_msg_cmsg_cloexec; + if (no_msg_cmsg_cloexec == 0) { + rc = recvmsg(fd, msg, flags | 0x40000000); /* MSG_CMSG_CLOEXEC */ + if (rc != -1) + return rc; + if (errno != EINVAL) + return -errno; + rc = recvmsg(fd, msg, flags); + if (rc == -1) + return -errno; + no_msg_cmsg_cloexec = 1; + } else { + rc = recvmsg(fd, msg, flags); + } +#else + rc = recvmsg(fd, msg, flags); +#endif + if (rc == -1) + return -errno; + if (msg->msg_controllen == 0) + return rc; + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) + if (cmsg->cmsg_type == SCM_RIGHTS) + for (pfd = (int*) CMSG_DATA(cmsg), + end = (int*) ((char*) cmsg + cmsg->cmsg_len); + pfd < end; + pfd += 1) + uv__cloexec(*pfd, 1); + return rc; +} + + int uv_cwd(char* buffer, size_t size) { if (buffer == NULL) return -EINVAL; @@ -604,20 +642,33 @@ static unsigned int next_power_of_two(unsigned int val) { static void maybe_resize(uv_loop_t* loop, unsigned int len) { uv__io_t** watchers; + void* fake_watcher_list; + void* fake_watcher_count; unsigned int nwatchers; unsigned int i; if (len <= loop->nwatchers) return; - nwatchers = next_power_of_two(len); - watchers = realloc(loop->watchers, nwatchers * sizeof(loop->watchers[0])); + /* Preserve fake watcher list and count at the end of the watchers */ + if (loop->watchers != NULL) { + fake_watcher_list = loop->watchers[loop->nwatchers]; + fake_watcher_count = loop->watchers[loop->nwatchers + 1]; + } else { + fake_watcher_list = NULL; + fake_watcher_count = NULL; + } + + nwatchers = next_power_of_two(len + 2) - 2; + watchers = realloc(loop->watchers, + (nwatchers + 2) * sizeof(loop->watchers[0])); if (watchers == NULL) abort(); - for (i = loop->nwatchers; i < nwatchers; i++) watchers[i] = NULL; + watchers[nwatchers] = fake_watcher_list; + watchers[nwatchers + 1] = fake_watcher_count; loop->watchers = watchers; loop->nwatchers = nwatchers; @@ -709,6 +760,9 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { void uv__io_close(uv_loop_t* loop, uv__io_t* w) { uv__io_stop(loop, w, UV__POLLIN | UV__POLLOUT); QUEUE_REMOVE(&w->pending_queue); + + /* Remove stale events for this file descriptor */ + uv__platform_invalidate_fd(loop, w->fd); } diff --git a/deps/uv/src/unix/darwin.c b/deps/uv/src/unix/darwin.c index a03ef2a9e01..c1655994e73 100644 --- a/deps/uv/src/unix/darwin.c +++ b/deps/uv/src/unix/darwin.c @@ -52,7 +52,7 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } -uint64_t uv__hrtime(void) { +uint64_t uv__hrtime(uv_clocktype_t type) { mach_timebase_info_data_t info; if (mach_timebase_info(&info) != KERN_SUCCESS) diff --git a/deps/uv/src/unix/freebsd.c b/deps/uv/src/unix/freebsd.c index bb8fb16e9a3..dcae244bb18 100644 --- a/deps/uv/src/unix/freebsd.c +++ b/deps/uv/src/unix/freebsd.c @@ -67,7 +67,7 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } -uint64_t uv__hrtime(void) { +uint64_t uv__hrtime(uv_clocktype_t type) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec); diff --git a/deps/uv/src/unix/fs.c b/deps/uv/src/unix/fs.c index 6cabf51376a..64517c46522 100644 --- a/deps/uv/src/unix/fs.c +++ b/deps/uv/src/unix/fs.c @@ -170,6 +170,8 @@ static ssize_t uv__fs_futime(uv_fs_t* req) { #elif defined(__APPLE__) \ || defined(__DragonFly__) \ || defined(__FreeBSD__) \ + || defined(__NetBSD__) \ + || defined(__OpenBSD__) \ || defined(__sun) struct timeval tv[2]; tv[0].tv_sec = req->atime; diff --git a/deps/uv/src/unix/fsevents.c b/deps/uv/src/unix/fsevents.c index 305de6def08..3618f46996c 100644 --- a/deps/uv/src/unix/fsevents.c +++ b/deps/uv/src/unix/fsevents.c @@ -73,28 +73,28 @@ typedef struct uv__fsevents_event_s uv__fsevents_event_t; typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t; typedef struct uv__cf_loop_state_s uv__cf_loop_state_t; -struct uv__cf_loop_state_s { - CFRunLoopRef loop; - CFRunLoopSourceRef signal_source; - int fsevent_need_reschedule; - FSEventStreamRef fsevent_stream; - uv_sem_t fsevent_sem; - uv_mutex_t fsevent_mutex; - void* fsevent_handles[2]; - int fsevent_handle_count; -}; - struct uv__cf_loop_signal_s { QUEUE member; uv_fs_event_t* handle; }; struct uv__fsevents_event_s { + QUEUE member; int events; - void* next; char path[1]; }; +struct uv__cf_loop_state_s { + CFRunLoopRef loop; + CFRunLoopSourceRef signal_source; + int fsevent_need_reschedule; + FSEventStreamRef fsevent_stream; + uv_sem_t fsevent_sem; + uv_mutex_t fsevent_mutex; + void* fsevent_handles[2]; + unsigned int fsevent_handle_count; +}; + /* Forward declarations */ static void uv__cf_loop_cb(void* arg); static void* uv__cf_loop_runner(void* arg); @@ -120,9 +120,9 @@ static CFRunLoopSourceRef (*pCFRunLoopSourceCreate)(CFAllocatorRef, static void (*pCFRunLoopSourceSignal)(CFRunLoopSourceRef); static void (*pCFRunLoopStop)(CFRunLoopRef); static void (*pCFRunLoopWakeUp)(CFRunLoopRef); -static CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef, - const char*, - CFStringEncoding); +static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)( + CFAllocatorRef, + const char*); static CFStringEncoding (*pCFStringGetSystemEncoding)(void); static CFStringRef (*pkCFRunLoopDefaultMode); static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef, @@ -143,22 +143,36 @@ static void (*pFSEventStreamStop)(FSEventStreamRef); #define UV__FSEVENTS_PROCESS(handle, block) \ do { \ + QUEUE events; \ + QUEUE* q; \ uv__fsevents_event_t* event; \ - uv__fsevents_event_t* next; \ + int err; \ uv_mutex_lock(&(handle)->cf_mutex); \ - event = (handle)->cf_event; \ - (handle)->cf_event = NULL; \ + /* Split-off all events and empty original queue */ \ + QUEUE_INIT(&events); \ + if (!QUEUE_EMPTY(&(handle)->cf_events)) { \ + q = QUEUE_HEAD(&(handle)->cf_events); \ + QUEUE_SPLIT(&(handle)->cf_events, q, &events); \ + } \ + /* Get error (if any) and zero original one */ \ + err = (handle)->cf_error; \ + (handle)->cf_error = 0; \ uv_mutex_unlock(&(handle)->cf_mutex); \ - while (event != NULL) { \ - /* Invoke callback */ \ - /* Invoke block code, but only if handle wasn't closed */ \ - if (!uv__is_closing((handle))) \ + /* Loop through events, deallocating each after processing */ \ + while (!QUEUE_EMPTY(&events)) { \ + q = QUEUE_HEAD(&events); \ + event = QUEUE_DATA(q, uv__fsevents_event_t, member); \ + QUEUE_REMOVE(q); \ + /* NOTE: Checking uv__is_active() is required here, because handle \ + * callback may close handle and invoking it after it will lead to \ + * incorrect behaviour */ \ + if (!uv__is_closing((handle)) && uv__is_active((handle))) \ block \ /* Free allocated data */ \ - next = event->next; \ free(event); \ - event = next; \ } \ + if (err != 0 && !uv__is_closing((handle)) && uv__is_active((handle))) \ + (handle)->cb((handle), NULL, 0, err); \ } while (0) @@ -169,12 +183,28 @@ static void uv__fsevents_cb(uv_async_t* cb, int status) { handle = cb->data; UV__FSEVENTS_PROCESS(handle, { - if (handle->event_watcher.fd != -1) - handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0); + handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0); }); +} + + +/* Runs in CF thread, pushed event into handle's event list */ +static void uv__fsevents_push_event(uv_fs_event_t* handle, + QUEUE* events, + int err) { + assert(events != NULL || err != 0); + uv_mutex_lock(&handle->cf_mutex); + + /* Concatenate two queues */ + if (events != NULL) + QUEUE_ADD(&handle->cf_events, events); - if (!uv__is_closing(handle) && handle->event_watcher.fd == -1) - uv__fsevents_close(handle); + /* Propagate error */ + if (err != 0) + handle->cf_error = err; + uv_mutex_unlock(&handle->cf_mutex); + + uv_async_send(handle->cf_cb); } @@ -195,7 +225,7 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, uv_loop_t* loop; uv__cf_loop_state_t* state; uv__fsevents_event_t* event; - uv__fsevents_event_t* tail; + QUEUE head; loop = info; state = loop->cf_state; @@ -203,9 +233,10 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, paths = eventPaths; /* For each handle */ + uv_mutex_lock(&state->fsevent_mutex); QUEUE_FOREACH(q, &state->fsevent_handles) { handle = QUEUE_DATA(q, uv_fs_event_t, cf_member); - tail = NULL; + QUEUE_INIT(&head); /* Process and filter out events */ for (i = 0; i < numEvents; i++) { @@ -260,25 +291,18 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, else event->events = UV_RENAME; - if (tail != NULL) - tail->next = event; - tail = event; + QUEUE_INSERT_TAIL(&head, &event->member); } - if (tail != NULL) { - uv_mutex_lock(&handle->cf_mutex); - tail->next = handle->cf_event; - handle->cf_event = tail; - uv_mutex_unlock(&handle->cf_mutex); - - uv_async_send(handle->cf_cb); - } + if (!QUEUE_EMPTY(&head)) + uv__fsevents_push_event(handle, &head, 0); } + uv_mutex_unlock(&state->fsevent_mutex); } /* Runs in CF thread */ -static void uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { +static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { uv__cf_loop_state_t* state; FSEventStreamContext ctx; FSEventStreamRef ref; @@ -292,10 +316,21 @@ static void uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { ctx.release = NULL; ctx.copyDescription = NULL; - latency = 0.15; - - /* Set appropriate flags */ - flags = kFSEventStreamCreateFlagFileEvents; + latency = 0.05; + + /* Explanation of selected flags: + * 1. NoDefer - without this flag, events that are happening continuously + * (i.e. each event is happening after time interval less than `latency`, + * counted from previous event), will be deferred and passed to callback + * once they'll either fill whole OS buffer, or when this continuous stream + * will stop (i.e. there'll be delay between events, bigger than + * `latency`). + * Specifying this flag will invoke callback after `latency` time passed + * since event. + * 2. FileEvents - fire callback for file changes too (by default it is firing + * it only for directory changes). + */ + flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents; /* * NOTE: It might sound like a good idea to remember last seen StreamEventId, @@ -316,10 +351,14 @@ static void uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { pFSEventStreamScheduleWithRunLoop(ref, state->loop, *pkCFRunLoopDefaultMode); - if (!pFSEventStreamStart(ref)) - abort(); + if (!pFSEventStreamStart(ref)) { + pFSEventStreamInvalidate(ref); + pFSEventStreamRelease(ref); + return -EMFILE; + } state->fsevent_stream = ref; + return 0; } @@ -352,10 +391,16 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle) { uv_fs_event_t* curr; CFArrayRef cf_paths; CFStringRef* paths; - int i; - int path_count; + unsigned int i; + int err; + unsigned int path_count; state = handle->loop->cf_state; + paths = NULL; + cf_paths = NULL; + err = 0; + /* NOTE: `i` is used in deallocation loop below */ + i = 0; /* Optimization to prevent O(n^2) time spent when starting to watch * many files simultaneously @@ -371,39 +416,68 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle) { /* Destroy previous FSEventStream */ uv__fsevents_destroy_stream(handle->loop); + /* Any failure below will be a memory failure */ + err = -ENOMEM; + /* Create list of all watched paths */ uv_mutex_lock(&state->fsevent_mutex); path_count = state->fsevent_handle_count; if (path_count != 0) { paths = malloc(sizeof(*paths) * path_count); - if (paths == NULL) - abort(); + if (paths == NULL) { + uv_mutex_unlock(&state->fsevent_mutex); + goto final; + } q = &state->fsevent_handles; - for (i = 0; i < path_count; i++) { + for (; i < path_count; i++) { q = QUEUE_NEXT(q); assert(q != &state->fsevent_handles); curr = QUEUE_DATA(q, uv_fs_event_t, cf_member); assert(curr->realpath != NULL); - paths[i] = pCFStringCreateWithCString(NULL, - curr->realpath, - pCFStringGetSystemEncoding()); - if (paths[i] == NULL) - abort(); + paths[i] = + pCFStringCreateWithFileSystemRepresentation(NULL, curr->realpath); + if (paths[i] == NULL) { + uv_mutex_unlock(&state->fsevent_mutex); + goto final; + } } } uv_mutex_unlock(&state->fsevent_mutex); + err = 0; if (path_count != 0) { /* Create new FSEventStream */ cf_paths = pCFArrayCreate(NULL, (const void**) paths, path_count, NULL); - if (cf_paths == NULL) - abort(); - uv__fsevents_create_stream(handle->loop, cf_paths); + if (cf_paths == NULL) { + err = -ENOMEM; + goto final; + } + err = uv__fsevents_create_stream(handle->loop, cf_paths); } final: + /* Deallocate all paths in case of failure */ + if (err != 0) { + if (cf_paths == NULL) { + while (i != 0) + pCFRelease(paths[--i]); + free(paths); + } else { + /* CFArray takes ownership of both strings and original C-array */ + pCFRelease(cf_paths); + } + + /* Broadcast error to all handles */ + uv_mutex_lock(&state->fsevent_mutex); + QUEUE_FOREACH(q, &state->fsevent_handles) { + curr = QUEUE_DATA(q, uv_fs_event_t, cf_member); + uv__fsevents_push_event(curr, NULL, err); + } + uv_mutex_unlock(&state->fsevent_mutex); + } + /* * Main thread will block until the removal of handle from the list, * we must tell it when we're ready. @@ -464,7 +538,7 @@ static int uv__fsevents_global_init(void) { V(core_foundation_handle, CFRunLoopSourceSignal); V(core_foundation_handle, CFRunLoopStop); V(core_foundation_handle, CFRunLoopWakeUp); - V(core_foundation_handle, CFStringCreateWithCString); + V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation); V(core_foundation_handle, CFStringGetSystemEncoding); V(core_foundation_handle, kCFRunLoopDefaultMode); V(core_services_handle, FSEventStreamCreate); @@ -722,8 +796,9 @@ int uv__fsevents_init(uv_fs_event_t* handle) { return -errno; handle->realpath_len = strlen(handle->realpath); - /* Initialize singly-linked list */ - handle->cf_event = NULL; + /* Initialize event queue */ + QUEUE_INIT(&handle->cf_events); + handle->cf_error = 0; /* * Events will occur in other thread. diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index c050c522294..79e41faae8d 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -128,12 +128,18 @@ enum { UV_TCP_SINGLE_ACCEPT = 0x1000 /* Only accept() when idle. */ }; +typedef enum { + UV_CLOCK_PRECISE = 0, /* Use the highest resolution clock available. */ + UV_CLOCK_FAST = 1 /* Use the fastest clock with <= 1ms granularity. */ +} uv_clocktype_t; + /* core */ int uv__nonblock(int fd, int set); int uv__close(int fd); int uv__cloexec(int fd, int set); int uv__socket(int domain, int type, int protocol); int uv__dup(int fd); +ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags); void uv__make_close_pending(uv_handle_t* handle); void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd); @@ -191,10 +197,11 @@ void uv__work_submit(uv_loop_t* loop, void uv__work_done(uv_async_t* handle, int status); /* platform specific */ -uint64_t uv__hrtime(void); +uint64_t uv__hrtime(uv_clocktype_t type); int uv__kqueue_init(uv_loop_t* loop); int uv__platform_loop_init(uv_loop_t* loop, int default_loop); void uv__platform_loop_delete(uv_loop_t* loop); +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd); /* various */ void uv__async_close(uv_async_t* handle); @@ -236,6 +243,7 @@ void uv__fsevents_loop_delete(uv_loop_t* loop); /* OSX < 10.7 has no file events, polyfill them */ #ifndef MAC_OS_X_VERSION_10_7 +static const int kFSEventStreamCreateFlagNoDefer = 0x00000002; static const int kFSEventStreamCreateFlagFileEvents = 0x00000010; static const int kFSEventStreamEventFlagItemCreated = 0x00000100; static const int kFSEventStreamEventFlagItemRemoved = 0x00000200; @@ -263,7 +271,9 @@ UV_UNUSED(static void uv__req_init(uv_loop_t* loop, uv__req_init((loop), (uv_req_t*)(req), (type)) UV_UNUSED(static void uv__update_time(uv_loop_t* loop)) { - loop->time = uv__hrtime() / 1000000; + /* Use a fast time source if available. We only need millisecond precision. + */ + loop->time = uv__hrtime(UV_CLOCK_FAST) / 1000000; } UV_UNUSED(static char* uv__basename_r(const char* path)) { diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c index 470045d3e1c..70f5d9edb44 100644 --- a/deps/uv/src/unix/kqueue.c +++ b/deps/uv/src/unix/kqueue.c @@ -161,11 +161,18 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nevents = 0; + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = (void*) events; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; for (i = 0; i < nfds; i++) { ev = events + i; fd = ev->ident; w = loop->watchers[fd]; + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + if (w == NULL) { /* File descriptor that we've stopped watching, disarm it. */ /* TODO batch up */ @@ -190,7 +197,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { revents = 0; if (ev->filter == EVFILT_READ) { - if (w->events & UV__POLLIN) { + if (w->pevents & UV__POLLIN) { revents |= UV__POLLIN; w->rcount = ev->data; } else { @@ -204,7 +211,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } if (ev->filter == EVFILT_WRITE) { - if (w->events & UV__POLLOUT) { + if (w->pevents & UV__POLLOUT) { revents |= UV__POLLOUT; w->wcount = ev->data; } else { @@ -226,6 +233,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w->cb(loop, w, revents); nevents++; } + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { @@ -254,6 +263,25 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct kevent* events; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + + events = (struct kevent*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events == NULL) + return; + + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].ident == fd) + events[i].ident = -1; +} + + static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) { uv_fs_event_t* handle; struct kevent ev; diff --git a/deps/uv/src/unix/linux-core.c b/deps/uv/src/unix/linux-core.c index 8bdd53d2680..78b234364ed 100644 --- a/deps/uv/src/unix/linux-core.c +++ b/deps/uv/src/unix/linux-core.c @@ -52,8 +52,10 @@ # include #endif -#undef NANOSEC -#define NANOSEC ((uint64_t) 1e9) +/* Available from 2.6.32 onwards. */ +#ifndef CLOCK_MONOTONIC_COARSE +# define CLOCK_MONOTONIC_COARSE 6 +#endif /* This is rather annoying: CLOCK_BOOTTIME lives in but we can't * include that file because it conflicts with . We'll just have to @@ -103,6 +105,25 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct uv__epoll_event* events; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + + events = (struct uv__epoll_event*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events == NULL) + return; + + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].data == fd) + events[i].data = -1; +} + + void uv__io_poll(uv_loop_t* loop, int timeout) { struct uv__epoll_event events[1024]; struct uv__epoll_event* pe; @@ -195,10 +216,17 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nevents = 0; + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = (void*) events; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; for (i = 0; i < nfds; i++) { pe = events + i; fd = pe->data; + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + assert(fd >= 0); assert((unsigned) fd < loop->nwatchers); @@ -214,9 +242,38 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { continue; } - w->cb(loop, w, pe->events); - nevents++; + /* Give users only events they're interested in. Prevents spurious + * callbacks when previous callback invocation in this loop has stopped + * the current watcher. Also, filters out events that users has not + * requested us to watch. + */ + pe->events &= w->pevents | UV__POLLERR | UV__POLLHUP; + + /* Work around an epoll quirk where it sometimes reports just the + * EPOLLERR or EPOLLHUP event. In order to force the event loop to + * move forward, we merge in the read/write events that the watcher + * is interested in; uv__read() and uv__write() will then deal with + * the error or hangup in the usual fashion. + * + * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user + * reads the available data, calls uv_read_stop(), then sometime later + * calls uv_read_start() again. By then, libuv has forgotten about the + * hangup and the kernel won't report EPOLLIN again because there's + * nothing left to read. If anything, libuv is to blame here. The + * current hack is just a quick bandaid; to properly fix it, libuv + * needs to remember the error/hangup event. We should get that for + * free when we switch over to edge-triggered I/O. + */ + if (pe->events == UV__EPOLLERR || pe->events == UV__EPOLLHUP) + pe->events |= w->pevents & (UV__EPOLLIN | UV__EPOLLOUT); + + if (pe->events != 0) { + w->cb(loop, w, pe->events); + nevents++; + } } + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { @@ -245,10 +302,36 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } -uint64_t uv__hrtime(void) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec); +uint64_t uv__hrtime(uv_clocktype_t type) { + static clock_t fast_clock_id = -1; + struct timespec t; + clock_t clock_id; + + /* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has + * millisecond granularity or better. CLOCK_MONOTONIC_COARSE is + * serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may + * decide to make a costly system call. + */ + /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE + * when it has microsecond granularity or better (unlikely). + */ + if (type == UV_CLOCK_FAST && fast_clock_id == -1) { + if (clock_getres(CLOCK_MONOTONIC_COARSE, &t) == 0 && + t.tv_nsec <= 1 * 1000 * 1000) { + fast_clock_id = CLOCK_MONOTONIC_COARSE; + } else { + fast_clock_id = CLOCK_MONOTONIC; + } + } + + clock_id = CLOCK_MONOTONIC; + if (type == UV_CLOCK_FAST) + clock_id = fast_clock_id; + + if (clock_gettime(clock_id, &t)) + return 0; /* Not really possible. */ + + return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec; } diff --git a/deps/uv/src/unix/loop.c b/deps/uv/src/unix/loop.c index cbe931712c3..94a5c038198 100644 --- a/deps/uv/src/unix/loop.c +++ b/deps/uv/src/unix/loop.c @@ -96,7 +96,7 @@ static int uv__loop_init(uv_loop_t* loop, int default_loop) { QUEUE_INIT(&loop->watcher_queue); loop->closing_handles = NULL; - loop->time = uv__hrtime() / 1000000; + uv__update_time(loop); uv__async_init(&loop->async_watcher); loop->signal_pipefd[0] = -1; loop->signal_pipefd[1] = -1; diff --git a/deps/uv/src/unix/netbsd.c b/deps/uv/src/unix/netbsd.c index 0722a6bf74e..7423a71078f 100644 --- a/deps/uv/src/unix/netbsd.c +++ b/deps/uv/src/unix/netbsd.c @@ -57,7 +57,7 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } -uint64_t uv__hrtime(void) { +uint64_t uv__hrtime(uv_clocktype_t type) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec); diff --git a/deps/uv/src/unix/openbsd.c b/deps/uv/src/unix/openbsd.c index 30f6fbdcb51..0ff9ad64314 100644 --- a/deps/uv/src/unix/openbsd.c +++ b/deps/uv/src/unix/openbsd.c @@ -56,7 +56,7 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } -uint64_t uv__hrtime(void) { +uint64_t uv__hrtime(uv_clocktype_t type) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec); diff --git a/deps/uv/src/unix/pipe.c b/deps/uv/src/unix/pipe.c index 705a973969d..fd4afb63704 100644 --- a/deps/uv/src/unix/pipe.c +++ b/deps/uv/src/unix/pipe.c @@ -72,7 +72,8 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { sockfd = err; memset(&saddr, 0, sizeof saddr); - uv_strlcpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path)); + strncpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path) - 1); + saddr.sun_path[sizeof(saddr.sun_path) - 1] = '\0'; saddr.sun_family = AF_UNIX; if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) { @@ -167,7 +168,8 @@ void uv_pipe_connect(uv_connect_t* req, } memset(&saddr, 0, sizeof saddr); - uv_strlcpy(saddr.sun_path, name, sizeof(saddr.sun_path)); + strncpy(saddr.sun_path, name, sizeof(saddr.sun_path) - 1); + saddr.sun_path[sizeof(saddr.sun_path) - 1] = '\0'; saddr.sun_family = AF_UNIX; do { diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c index 42990ce5f24..6854ac1190c 100644 --- a/deps/uv/src/unix/process.c +++ b/deps/uv/src/unix/process.c @@ -109,9 +109,6 @@ static void uv__chld(uv_signal_t* handle, int signum) { if (WIFSIGNALED(process->status)) term_signal = WTERMSIG(process->status); - if (process->errorno != 0) - exit_status = process->errorno; /* execve() failed */ - process->exit_cb(process, exit_status, term_signal); } } @@ -359,6 +356,7 @@ int uv_spawn(uv_loop_t* loop, ssize_t r; pid_t pid; int err; + int exec_errorno; int i; assert(options->file != NULL); @@ -434,14 +432,14 @@ int uv_spawn(uv_loop_t* loop, uv__close(signal_pipe[1]); process->status = 0; - process->errorno = 0; + exec_errorno = 0; do - r = read(signal_pipe[0], &process->errorno, sizeof(process->errorno)); + r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno)); while (r == -1 && errno == EINTR); if (r == 0) ; /* okay, EOF */ - else if (r == sizeof(process->errorno)) + else if (r == sizeof(exec_errorno)) ; /* okay, read errorno */ else if (r == -1 && errno == EPIPE) ; /* okay, got EPIPE */ @@ -461,15 +459,18 @@ int uv_spawn(uv_loop_t* loop, goto error; } - q = uv__process_queue(loop, pid); - QUEUE_INSERT_TAIL(q, &process->queue); + /* Only activate this handle if exec() happened successfully */ + if (exec_errorno == 0) { + q = uv__process_queue(loop, pid); + QUEUE_INSERT_TAIL(q, &process->queue); + uv__handle_start(process); + } process->pid = pid; process->exit_cb = options->exit_cb; - uv__handle_start(process); free(pipes); - return 0; + return exec_errorno; error: if (pipes != NULL) { diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index d910a68ba3f..abef01ee3f6 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -298,7 +298,7 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { kq = kqueue(); if (kq == -1) { - fprintf(stderr, "(libuv) Failed to create kqueue (%d)\n", errno); + perror("(libuv) kqueue()"); return -errno; } @@ -1007,7 +1007,7 @@ static void uv__read(uv_stream_t* stream) { msg.msg_control = (void*) cmsg_space; do { - nread = recvmsg(uv__stream_fd(stream), &msg, 0); + nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0); } while (nread < 0 && errno == EINTR); } diff --git a/deps/uv/src/unix/sunos.c b/deps/uv/src/unix/sunos.c index fe99d0853cd..f31a23fb3cf 100644 --- a/deps/uv/src/unix/sunos.c +++ b/deps/uv/src/unix/sunos.c @@ -97,6 +97,25 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct port_event* events; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + + events = (struct port_event*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events == NULL) + return; + + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].portev_object == fd) + events[i].portev_object = -1; +} + + void uv__io_poll(uv_loop_t* loop, int timeout) { struct port_event events[1024]; struct port_event* pe; @@ -183,10 +202,17 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nevents = 0; + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = (void*) events; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; for (i = 0; i < nfds; i++) { pe = events + i; fd = pe->portev_object; + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + assert(fd >= 0); assert((unsigned) fd < loop->nwatchers); @@ -206,6 +232,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); } + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { @@ -239,7 +267,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } -uint64_t uv__hrtime(void) { +uint64_t uv__hrtime(uv_clocktype_t type) { return gethrtime(); } diff --git a/deps/uv/src/unix/thread.c b/deps/uv/src/unix/thread.c index 8c38c7f1466..f2ce0828424 100644 --- a/deps/uv/src/unix/thread.c +++ b/deps/uv/src/unix/thread.c @@ -335,7 +335,7 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { ts.tv_nsec = timeout % NANOSEC; r = pthread_cond_timedwait_relative_np(cond, mutex, &ts); #else - timeout += uv__hrtime(); + timeout += uv__hrtime(UV_CLOCK_PRECISE); ts.tv_sec = timeout / NANOSEC; ts.tv_nsec = timeout % NANOSEC; #if defined(__ANDROID__) diff --git a/deps/uv/src/uv-common.c b/deps/uv/src/uv-common.c index 6cfd10852a1..4129a366862 100644 --- a/deps/uv/src/uv-common.c +++ b/deps/uv/src/uv-common.c @@ -66,40 +66,6 @@ size_t uv_req_size(uv_req_type type) { #undef XX -size_t uv_strlcpy(char* dst, const char* src, size_t size) { - size_t n; - - if (size == 0) - return 0; - - for (n = 0; n < (size - 1) && *src != '\0'; n++) - *dst++ = *src++; - - *dst = '\0'; - - return n; -} - - -size_t uv_strlcat(char* dst, const char* src, size_t size) { - size_t n; - - if (size == 0) - return 0; - - for (n = 0; n < size && *dst != '\0'; n++, dst++); - - if (n == size) - return n; - - while (n < (size - 1) && *src != '\0') - n++, *dst++ = *src++; - - *dst = '\0'; - - return n; -} - uv_buf_t uv_buf_init(char* base, unsigned int len) { uv_buf_t buf; diff --git a/deps/uv/src/version.c b/deps/uv/src/version.c index 7ca1ab10840..2170dee3691 100644 --- a/deps/uv/src/version.c +++ b/deps/uv/src/version.c @@ -31,7 +31,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 11 -#define UV_VERSION_PATCH 14 +#define UV_VERSION_PATCH 15 #define UV_VERSION_IS_RELEASE 1 diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index a51f8f013f8..e3e11c77123 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -738,11 +738,7 @@ void fs__readdir(uv_fs_t* req) { uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); } -#ifdef _CRT_NON_CONFORMING_SWPRINTFS - swprintf(path2, fmt, pathw); -#else - swprintf(path2, len + 3, fmt, pathw); -#endif + _snwprintf(path2, len + 3, fmt, pathw); dir = FindFirstFileW(path2, &ent); free(path2); diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c index 218ea8f21ba..a5bb7437706 100644 --- a/deps/uv/src/win/process.c +++ b/deps/uv/src/win/process.c @@ -127,7 +127,6 @@ static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) { uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS); handle->exit_cb = NULL; handle->pid = 0; - handle->spawn_error = 0; handle->exit_signal = 0; handle->wait_handle = INVALID_HANDLE_VALUE; handle->process_handle = INVALID_HANDLE_VALUE; @@ -752,10 +751,7 @@ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { /* callback.*/ uv__handle_stop(handle); - if (handle->spawn_error) { - /* Spawning failed. */ - exit_code = uv_translate_sys_error(handle->spawn_error); - } else if (GetExitCodeProcess(handle->process_handle, &status)) { + if (GetExitCodeProcess(handle->process_handle, &status)) { exit_code = status; } else { /* Unable to to obtain the exit code. This should never happen. */ @@ -1025,25 +1021,20 @@ int uv_spawn(uv_loop_t* loop, free(env); free(path); - process->spawn_error = err; - if (process->child_stdio_buffer != NULL) { /* Clean up child stdio handles. */ uv__stdio_destroy(process->child_stdio_buffer); process->child_stdio_buffer = NULL; } - /* Make the handle active. It will remain active until the exit callback */ - /* is made or the handle is closed, whichever happens first. */ - uv__handle_start(process); - - /* If an error happened, queue the exit req. */ - if (err) { - process->exit_cb_pending = 1; - uv_insert_pending_req(loop, (uv_req_t*) &process->exit_req); + /* Make the handle active, but only if an error didn't happen. It will */ + /* remain active until the exit callback is made or the handle is closed, */ + /* whichever happens first. */ + if (err == 0) { + uv__handle_start(process); } - return 0; + return err; /* This code path is taken when we run into an error that we want to */ /* report immediately. */ diff --git a/deps/uv/test/task.h b/deps/uv/test/task.h index 8e7666ad007..b736c375c7d 100644 --- a/deps/uv/test/task.h +++ b/deps/uv/test/task.h @@ -32,6 +32,11 @@ # include #endif +#if !defined(_WIN32) +# include +# include /* setrlimit() */ +#endif + #define TEST_PORT 9123 #define TEST_PORT_2 9124 @@ -153,6 +158,24 @@ enum test_status { return TEST_SKIP; \ } while (0) +#if !defined(_WIN32) + +# define TEST_FILE_LIMIT(num) \ + do { \ + struct rlimit lim; \ + lim.rlim_cur = (num); \ + lim.rlim_max = lim.rlim_cur; \ + if (setrlimit(RLIMIT_NOFILE, &lim)) \ + RETURN_SKIP("File descriptor limit too low."); \ + } while (0) + +#else /* defined(_WIN32) */ + +# define TEST_FILE_LIMIT(num) do {} while (0) + +#endif + + #if defined _WIN32 && ! defined __GNUC__ #include diff --git a/deps/uv/test/test-close-fd.c b/deps/uv/test/test-close-fd.c new file mode 100644 index 00000000000..0d17f076615 --- /dev/null +++ b/deps/uv/test/test-close-fd.c @@ -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 +#include + +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) */ diff --git a/deps/uv/test/test-fs-event.c b/deps/uv/test/test-fs-event.c index 66132e172f8..3286de51f98 100644 --- a/deps/uv/test/test-fs-event.c +++ b/deps/uv/test/test-fs-event.c @@ -36,11 +36,21 @@ #endif static uv_fs_event_t fs_event; +static const char file_prefix[] = "fsevent-"; static uv_timer_t timer; -static int timer_cb_called = 0; -static int close_cb_called = 0; -static int fs_event_cb_called = 0; -static int timer_cb_touch_called = 0; +static int timer_cb_called; +static int close_cb_called; +static const int fs_event_file_count = 128; +static int fs_event_created; +static int fs_event_cb_called; +#if defined(PATH_MAX) +static char fs_event_filename[PATH_MAX]; +#else +static char fs_event_filename[1024]; +#endif /* defined(PATH_MAX) */ +static int timer_cb_touch_called; + +static void fs_event_unlink_files(uv_timer_t* handle, int status); static void create_dir(uv_loop_t* loop, const char* name) { int r; @@ -107,6 +117,69 @@ static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename, uv_close((uv_handle_t*)handle, close_cb); } +static void fs_event_cb_dir_multi_file(uv_fs_event_t* handle, + const char* filename, + int events, + int status) { + fs_event_cb_called++; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_RENAME); + ASSERT(filename == NULL || + strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0); + + /* Stop watching dir when received events about all files: + * both create and close events */ + if (fs_event_cb_called == 2 * fs_event_file_count) { + ASSERT(0 == uv_fs_event_stop(handle)); + uv_close((uv_handle_t*) handle, close_cb); + } +} + +static const char* fs_event_get_filename(int i) { + snprintf(fs_event_filename, + sizeof(fs_event_filename), + "watch_dir/%s%d", + file_prefix, + i); + return fs_event_filename; +} + +static void fs_event_create_files(uv_timer_t* handle, int status) { + int i; + + /* Already created all files */ + if (fs_event_created == fs_event_file_count) { + uv_close((uv_handle_t*) &timer, close_cb); + return; + } + + /* Create all files */ + for (i = 0; i < 16; i++, fs_event_created++) + create_file(handle->loop, fs_event_get_filename(i)); + + /* And unlink them */ + ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 50, 0)); +} + +void fs_event_unlink_files(uv_timer_t* handle, int status) { + int r; + int i; + + /* NOTE: handle might be NULL if invoked not as timer callback */ + + /* Unlink all files */ + for (i = 0; i < 16; i++) { + r = remove(fs_event_get_filename(i)); + if (handle != NULL) + ASSERT(r == 0); + } + + /* And create them again */ + if (handle != NULL) + ASSERT(0 == uv_timer_start(&timer, fs_event_create_files, 50, 0)); +} + static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename, int events, int status) { ++fs_event_cb_called; @@ -148,12 +221,6 @@ static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, } } -static void timer_cb_dir(uv_timer_t* handle, int status) { - ++timer_cb_called; - create_file(handle->loop, "watch_dir/file1"); - uv_close((uv_handle_t*)handle, close_cb); -} - static void timer_cb_file(uv_timer_t* handle, int status) { ++timer_cb_called; @@ -184,6 +251,7 @@ TEST_IMPL(fs_event_watch_dir) { int r; /* Setup */ + fs_event_unlink_files(NULL, 0); remove("watch_dir/file2"); remove("watch_dir/file1"); remove("watch_dir/"); @@ -191,20 +259,21 @@ TEST_IMPL(fs_event_watch_dir) { r = uv_fs_event_init(loop, &fs_event); ASSERT(r == 0); - r = uv_fs_event_start(&fs_event, fs_event_cb_dir, "watch_dir", 0); + r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file, "watch_dir", 0); ASSERT(r == 0); r = uv_timer_init(loop, &timer); ASSERT(r == 0); - r = uv_timer_start(&timer, timer_cb_dir, 100, 0); + r = uv_timer_start(&timer, fs_event_create_files, 100, 0); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(fs_event_cb_called == 1); - ASSERT(timer_cb_called == 1); + ASSERT(fs_event_cb_called == 2 * fs_event_file_count); + ASSERT(fs_event_created == fs_event_file_count); ASSERT(close_cb_called == 2); /* Cleanup */ + fs_event_unlink_files(NULL, 0); remove("watch_dir/file2"); remove("watch_dir/file1"); remove("watch_dir/"); @@ -556,3 +625,106 @@ TEST_IMPL(fs_event_start_and_close) { MAKE_VALGRIND_HAPPY(); return 0; } + +#if defined(__APPLE__) + +static int fs_event_error_reported; + +static void fs_event_error_report_cb(uv_fs_event_t* handle, + const char* filename, + int events, + int status) { + if (status != 0) + fs_event_error_reported = status; +} + +static void timer_cb_nop(uv_timer_t* handle, int status) { + ++timer_cb_called; + uv_close((uv_handle_t*) handle, close_cb); +} + +static void fs_event_error_report_close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; + + /* handle is allocated on-stack, no need to free it */ +} + + +TEST_IMPL(fs_event_error_reporting) { + unsigned int i; + uv_loop_t* loops[1024]; + uv_fs_event_t events[ARRAY_SIZE(loops)]; + uv_loop_t* loop; + uv_fs_event_t* event; + + TEST_FILE_LIMIT(ARRAY_SIZE(loops) * 3); + + remove("watch_dir/"); + create_dir(uv_default_loop(), "watch_dir"); + + /* Create a lot of loops, and start FSEventStream in each of them. + * Eventually, this should create enough streams to make FSEventStreamStart() + * fail. + */ + for (i = 0; i < ARRAY_SIZE(loops); i++) { + loop = uv_loop_new(); + event = &events[i]; + ASSERT(loop != NULL); + + loops[i] = loop; + timer_cb_called = 0; + close_cb_called = 0; + ASSERT(0 == uv_fs_event_init(loop, event)); + ASSERT(0 == uv_fs_event_start(event, + fs_event_error_report_cb, + "watch_dir", + 0)); + uv_unref((uv_handle_t*) event); + + /* Let loop run for some time */ + ASSERT(0 == uv_timer_init(loop, &timer)); + ASSERT(0 == uv_timer_start(&timer, timer_cb_nop, 2, 0)); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(1 == timer_cb_called); + ASSERT(1 == close_cb_called); + if (fs_event_error_reported != 0) + break; + } + + /* At least one loop should fail */ + ASSERT(fs_event_error_reported == UV_EMFILE); + + /* Stop and close all events, and destroy loops */ + do { + loop = loops[i]; + event = &events[i]; + + ASSERT(0 == uv_fs_event_stop(event)); + uv_ref((uv_handle_t*) event); + uv_close((uv_handle_t*) event, fs_event_error_report_close_cb); + + close_cb_called = 0; + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(close_cb_called == 1); + + uv_loop_delete(loop); + + loops[i] = NULL; + } while (i-- != 0); + + remove("watch_dir/"); + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#else /* !defined(__APPLE__) */ + +TEST_IMPL(fs_event_error_reporting) { + /* No-op, needed only for FSEvents backend */ + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* defined(__APPLE__) */ diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index d963f04cf39..b3f2e0afac3 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -65,6 +65,7 @@ TEST_DECLARE (tcp_connect_error_fault) TEST_DECLARE (tcp_connect_timeout) TEST_DECLARE (tcp_close_while_connecting) TEST_DECLARE (tcp_close) +TEST_DECLARE (tcp_close_accept) TEST_DECLARE (tcp_flags) TEST_DECLARE (tcp_write_to_half_open_connection) TEST_DECLARE (tcp_unexpected_read) @@ -108,6 +109,7 @@ TEST_DECLARE (idle_starvation) TEST_DECLARE (loop_handles) TEST_DECLARE (get_loadavg) TEST_DECLARE (walk_handles) +TEST_DECLARE (watcher_cross_stop) TEST_DECLARE (ref) TEST_DECLARE (idle_ref) TEST_DECLARE (async_ref) @@ -194,6 +196,7 @@ TEST_DECLARE (fs_event_immediate_close) TEST_DECLARE (fs_event_close_with_pending_event) TEST_DECLARE (fs_event_close_in_callback) TEST_DECLARE (fs_event_start_and_close) +TEST_DECLARE (fs_event_error_reporting) TEST_DECLARE (fs_readdir_empty_dir) TEST_DECLARE (fs_readdir_file) TEST_DECLARE (fs_open_dir) @@ -209,8 +212,6 @@ TEST_DECLARE (thread_local_storage) TEST_DECLARE (thread_mutex) TEST_DECLARE (thread_rwlock) TEST_DECLARE (thread_create) -TEST_DECLARE (strlcpy) -TEST_DECLARE (strlcat) TEST_DECLARE (dlerror) TEST_DECLARE (poll_duplex) TEST_DECLARE (poll_unidirectional) @@ -224,6 +225,7 @@ TEST_DECLARE (listen_with_simultaneous_accepts) TEST_DECLARE (listen_no_simultaneous_accepts) TEST_DECLARE (fs_stat_root) #else +TEST_DECLARE (close_fd) TEST_DECLARE (spawn_setuid_setgid) TEST_DECLARE (we_get_signal) TEST_DECLARE (we_get_signals) @@ -306,6 +308,7 @@ TASK_LIST_START TEST_ENTRY (tcp_connect_timeout) TEST_ENTRY (tcp_close_while_connecting) TEST_ENTRY (tcp_close) + TEST_ENTRY (tcp_close_accept) TEST_ENTRY (tcp_flags) TEST_ENTRY (tcp_write_to_half_open_connection) TEST_ENTRY (tcp_unexpected_read) @@ -396,6 +399,8 @@ TASK_LIST_START TEST_ENTRY (loop_handles) TEST_ENTRY (walk_handles) + TEST_ENTRY (watcher_cross_stop) + TEST_ENTRY (active) TEST_ENTRY (embed) @@ -452,6 +457,7 @@ TASK_LIST_START TEST_ENTRY (listen_no_simultaneous_accepts) TEST_ENTRY (fs_stat_root) #else + TEST_ENTRY (close_fd) TEST_ENTRY (spawn_setuid_setgid) TEST_ENTRY (we_get_signal) TEST_ENTRY (we_get_signals) @@ -490,6 +496,7 @@ TASK_LIST_START TEST_ENTRY (fs_event_close_with_pending_event) TEST_ENTRY (fs_event_close_in_callback) TEST_ENTRY (fs_event_start_and_close) + TEST_ENTRY (fs_event_error_reporting) TEST_ENTRY (fs_readdir_empty_dir) TEST_ENTRY (fs_readdir_file) TEST_ENTRY (fs_open_dir) @@ -505,8 +512,6 @@ TASK_LIST_START TEST_ENTRY (thread_mutex) TEST_ENTRY (thread_rwlock) TEST_ENTRY (thread_create) - TEST_ENTRY (strlcpy) - TEST_ENTRY (strlcat) TEST_ENTRY (dlerror) TEST_ENTRY (ip6_addr_link_local) #if 0 diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c index 454c18169d6..0a355af0f62 100644 --- a/deps/uv/test/test-spawn.c +++ b/deps/uv/test/test-spawn.c @@ -51,7 +51,6 @@ static void close_cb(uv_handle_t* handle) { close_cb_called++; } - static void exit_cb(uv_process_t* process, int64_t exit_status, int term_signal) { @@ -63,29 +62,10 @@ static void exit_cb(uv_process_t* process, } -static void expect(uv_process_t* process, - int64_t exit_status, - int term_signal, - int err) { - printf("exit_cb\n"); - exit_cb_called++; - ASSERT(exit_status == err); - ASSERT(term_signal == 0); - uv_close((uv_handle_t*)process, close_cb); -} - - -static void exit_cb_expect_enoent(uv_process_t* process, - int64_t exit_status, - int term_signal) { - expect(process, exit_status, term_signal, UV_ENOENT); -} - - -static void exit_cb_expect_eperm(uv_process_t* process, - int64_t exit_status, - int term_signal) { - expect(process, exit_status, term_signal, UV_EPERM); +static void fail_cb(uv_process_t* process, + int64_t exit_status, + int term_signal) { + ASSERT(0 && "fail_cb called"); } @@ -166,12 +146,12 @@ static void timer_cb(uv_timer_t* handle, int status) { TEST_IMPL(spawn_fails) { - init_process_options("", exit_cb_expect_enoent); + init_process_options("", fail_cb); options.file = options.args[0] = "program-that-had-better-not-exist"; - ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); - ASSERT(1 == uv_is_active((uv_handle_t*) &process)); + + ASSERT(UV_ENOENT == uv_spawn(uv_default_loop(), &process, &options)); + ASSERT(0 == uv_is_active((uv_handle_t*) &process)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(1 == exit_cb_called); MAKE_VALGRIND_HAPPY(); return 0; @@ -851,19 +831,18 @@ TEST_IMPL(spawn_setuid_fails) { ASSERT(0 == setuid(pw->pw_uid)); } - init_process_options("spawn_helper1", exit_cb_expect_eperm); + init_process_options("spawn_helper1", fail_cb); options.flags |= UV_PROCESS_SETUID; options.uid = 0; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT(r == UV_EPERM); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT(close_cb_called == 0); MAKE_VALGRIND_HAPPY(); return 0; @@ -883,19 +862,18 @@ TEST_IMPL(spawn_setgid_fails) { ASSERT(0 == setuid(pw->pw_uid)); } - init_process_options("spawn_helper1", exit_cb_expect_eperm); + init_process_options("spawn_helper1", fail_cb); options.flags |= UV_PROCESS_SETGID; options.gid = 0; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT(r == UV_EPERM); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT(close_cb_called == 0); MAKE_VALGRIND_HAPPY(); return 0; diff --git a/deps/uv/test/test-tcp-close-accept.c b/deps/uv/test/test-tcp-close-accept.c new file mode 100644 index 00000000000..471be53a998 --- /dev/null +++ b/deps/uv/test/test-tcp-close-accept.c @@ -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 +#include + +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; +} diff --git a/deps/uv/test/test-util.c b/deps/uv/test/test-util.c deleted file mode 100644 index d61d3b12856..00000000000 --- a/deps/uv/test/test-util.c +++ /dev/null @@ -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 - -#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; -} diff --git a/deps/uv/test/test-watcher-cross-stop.c b/deps/uv/test/test-watcher-cross-stop.c new file mode 100644 index 00000000000..c701dd2f918 --- /dev/null +++ b/deps/uv/test/test-watcher-cross-stop.c @@ -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 +#include + +/* 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; +} diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index 95095642991..25190b6c820 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -297,12 +297,12 @@ 'test/runner.h', 'test/test-get-loadavg.c', 'test/task.h', - 'test/test-util.c', 'test/test-active.c', 'test/test-async.c', 'test/test-async-null-cb.c', 'test/test-callback-stack.c', 'test/test-callback-order.c', + 'test/test-close-fd.c', 'test/test-close-order.c', 'test/test-connection-fail.c', 'test/test-cwd-and-chdir.c', @@ -324,6 +324,7 @@ 'test/test-loop-handles.c', 'test/test-loop-stop.c', 'test/test-walk-handles.c', + 'test/test-watcher-cross-stop.c', 'test/test-multiple-listen.c', 'test/test-osx-select.c', 'test/test-pass-always.c', @@ -348,6 +349,7 @@ 'test/test-tcp-bind-error.c', 'test/test-tcp-bind6-error.c', 'test/test-tcp-close.c', + 'test/test-tcp-close-accept.c', 'test/test-tcp-close-while-connecting.c', 'test/test-tcp-connect-error-after-write.c', 'test/test-tcp-shutdown-after-write.c', diff --git a/deps/uv/vcbuild.bat b/deps/uv/vcbuild.bat index 1b2f865a61a..0b7ea481117 100644 --- a/deps/uv/vcbuild.bat +++ b/deps/uv/vcbuild.bat @@ -91,7 +91,7 @@ exit /b 1 :have_gyp if not defined PYTHON set PYTHON="python" -%PYTHON% gyp_uv -Dtarget_arch=%target_arch% -Dlibrary=%library% +%PYTHON% gyp_uv.py -Dtarget_arch=%target_arch% -Dlibrary=%library% if errorlevel 1 goto create-msvs-files-failed if not exist uv.sln goto create-msvs-files-failed echo Project files generated.