uv: upgrade to v0.11.15
authorTimothy J Fontaine <tjfontaine@gmail.com>
Wed, 20 Nov 2013 16:25:24 +0000 (08:25 -0800)
committerTimothy J Fontaine <tjfontaine@gmail.com>
Wed, 20 Nov 2013 17:35:08 +0000 (09:35 -0800)
56 files changed:
deps/uv/AUTHORS
deps/uv/ChangeLog
deps/uv/Makefile.am
deps/uv/README.md
deps/uv/android-configure
deps/uv/checksparse.sh
deps/uv/configure.ac
deps/uv/gyp_uv.py [moved from deps/uv/gyp_uv with 100% similarity]
deps/uv/include/uv-darwin.h
deps/uv/include/uv-unix.h
deps/uv/include/uv-win.h
deps/uv/include/uv.h
deps/uv/samples/.gitignore [new file with mode: 0644]
deps/uv/samples/socks5-proxy/.gitignore [new file with mode: 0644]
deps/uv/samples/socks5-proxy/LICENSE [new file with mode: 0644]
deps/uv/samples/socks5-proxy/build.gyp [new file with mode: 0644]
deps/uv/samples/socks5-proxy/client.c [new file with mode: 0644]
deps/uv/samples/socks5-proxy/defs.h [new file with mode: 0644]
deps/uv/samples/socks5-proxy/getopt.c [new file with mode: 0644]
deps/uv/samples/socks5-proxy/main.c [new file with mode: 0644]
deps/uv/samples/socks5-proxy/s5.c [new file with mode: 0644]
deps/uv/samples/socks5-proxy/s5.h [new file with mode: 0644]
deps/uv/samples/socks5-proxy/server.c [new file with mode: 0644]
deps/uv/samples/socks5-proxy/util.c [new file with mode: 0644]
deps/uv/src/queue.h
deps/uv/src/unix/aix.c
deps/uv/src/unix/core.c
deps/uv/src/unix/darwin.c
deps/uv/src/unix/freebsd.c
deps/uv/src/unix/fs.c
deps/uv/src/unix/fsevents.c
deps/uv/src/unix/internal.h
deps/uv/src/unix/kqueue.c
deps/uv/src/unix/linux-core.c
deps/uv/src/unix/loop.c
deps/uv/src/unix/netbsd.c
deps/uv/src/unix/openbsd.c
deps/uv/src/unix/pipe.c
deps/uv/src/unix/process.c
deps/uv/src/unix/stream.c
deps/uv/src/unix/sunos.c
deps/uv/src/unix/thread.c
deps/uv/src/uv-common.c
deps/uv/src/version.c
deps/uv/src/win/fs.c
deps/uv/src/win/process.c
deps/uv/test/task.h
deps/uv/test/test-close-fd.c [new file with mode: 0644]
deps/uv/test/test-fs-event.c
deps/uv/test/test-list.h
deps/uv/test/test-spawn.c
deps/uv/test/test-tcp-close-accept.c [new file with mode: 0644]
deps/uv/test/test-util.c [deleted file]
deps/uv/test/test-watcher-cross-stop.c [new file with mode: 0644]
deps/uv/uv.gyp
deps/uv/vcbuild.bat

index 4d56a3b..a9c63bf 100644 (file)
@@ -97,3 +97,5 @@ Luca Bruno <lucab@debian.org>
 Reini Urban <rurban@cpanel.net>
 Maks Naumov <maksqwe1@ukr.net>
 Sean Farrell <sean.farrell@rioki.org>
+Chris Bank <cbank@adobe.com>
+Geert Jansen <geertj@gmail.com>
index ef3cfd3..0e5638f 100644 (file)
@@ -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:
 
index 9080b94..8f656f3 100644 (file)
@@ -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
index aab9e80..ce43f6d 100644 (file)
@@ -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
index d5c937e..5662576 100755 (executable)
@@ -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
index c2e3e88..b1d8187 100755 (executable)
@@ -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
index ede6c6f..fda951e 100644 (file)
@@ -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)
similarity index 100%
rename from deps/uv/gyp_uv
rename to deps/uv/gyp_uv.py
index dcdd42b..24bc35b 100644 (file)
   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                                     \
index 965fbaf..4500609 100644 (file)
@@ -287,7 +287,6 @@ typedef struct {
 
 #define UV_PROCESS_PRIVATE_FIELDS                                             \
   void* queue[2];                                                             \
-  int errorno;                                                                \
   int status;                                                                 \
 
 #define UV_FS_PRIVATE_FIELDS                                                  \
index 512d7f8..e4e1f83 100644 (file)
@@ -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;                                                      \
index 5a1d1d9..c3ba250 100644 (file)
@@ -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 (file)
index 0000000..f868091
--- /dev/null
@@ -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 (file)
index 0000000..c177f37
--- /dev/null
@@ -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 (file)
index 0000000..63c1447
--- /dev/null
@@ -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 (file)
index 0000000..771a1e1
--- /dev/null
@@ -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 (file)
index 0000000..ae9913a
--- /dev/null
@@ -0,0 +1,737 @@
+/* Copyright StrongLoop, Inc. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "defs.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* A connection is modeled as an abstraction on top of two simple state
+ * machines, one for reading and one for writing.  Either state machine
+ * is, when active, in one of three states: busy, done or stop; the fourth
+ * and final state, dead, is an end state and only relevant when shutting
+ * down the connection.  A short overview:
+ *
+ *                          busy                  done           stop
+ *  ----------|---------------------------|--------------------|------|
+ *  readable  | waiting for incoming data | have incoming data | idle |
+ *  writable  | busy writing out data     | completed write    | idle |
+ *
+ * We could remove the done state from the writable state machine. For our
+ * purposes, it's functionally equivalent to the stop state.
+ *
+ * When the connection with upstream has been established, the client_ctx
+ * moves into a state where incoming data from the client is sent upstream
+ * and vice versa, incoming data from upstream is sent to the client.  In
+ * other words, we're just piping data back and forth.  See conn_cycle()
+ * for details.
+ *
+ * An interesting deviation from libuv's I/O model is that reads are discrete
+ * rather than continuous events.  In layman's terms, when a read operation
+ * completes, the connection stops reading until further notice.
+ *
+ * The rationale for this approach is that we have to wait until the data
+ * has been sent out again before we can reuse the read buffer.
+ *
+ * It also pleasingly unifies with the request model that libuv uses for
+ * writes and everything else; libuv may switch to a request model for
+ * reads in the future.
+ */
+enum conn_state {
+  c_busy,  /* Busy; waiting for incoming data or for a write to complete. */
+  c_done,  /* Done; read incoming data or write finished. */
+  c_stop,  /* Stopped. */
+  c_dead
+};
+
+/* Session states. */
+enum sess_state {
+  s_handshake,        /* Wait for client handshake. */
+  s_handshake_auth,   /* Wait for client authentication data. */
+  s_req_start,        /* Start waiting for request data. */
+  s_req_parse,        /* Wait for request data. */
+  s_req_lookup,       /* Wait for upstream hostname DNS lookup to complete. */
+  s_req_connect,      /* Wait for uv_tcp_connect() to complete. */
+  s_proxy_start,      /* Connected. Start piping data. */
+  s_proxy,            /* Connected. Pipe data back and forth. */
+  s_kill,             /* Tear down session. */
+  s_almost_dead_0,    /* Waiting for finalizers to complete. */
+  s_almost_dead_1,    /* Waiting for finalizers to complete. */
+  s_almost_dead_2,    /* Waiting for finalizers to complete. */
+  s_almost_dead_3,    /* Waiting for finalizers to complete. */
+  s_almost_dead_4,    /* Waiting for finalizers to complete. */
+  s_dead              /* Dead. Safe to free now. */
+};
+
+static void do_next(client_ctx *cx);
+static int do_handshake(client_ctx *cx);
+static int do_handshake_auth(client_ctx *cx);
+static int do_req_start(client_ctx *cx);
+static int do_req_parse(client_ctx *cx);
+static int do_req_lookup(client_ctx *cx);
+static int do_req_connect_start(client_ctx *cx);
+static int do_req_connect(client_ctx *cx);
+static int do_proxy_start(client_ctx *cx);
+static int do_proxy(client_ctx *cx);
+static int do_kill(client_ctx *cx);
+static int do_almost_dead(client_ctx *cx);
+static int conn_cycle(const char *who, conn *a, conn *b);
+static void conn_timer_reset(conn *c);
+static void conn_timer_expire(uv_timer_t *handle, int status);
+static void conn_getaddrinfo(conn *c, const char *hostname);
+static void conn_getaddrinfo_done(uv_getaddrinfo_t *req,
+                                  int status,
+                                  struct addrinfo *ai);
+static int conn_connect(conn *c);
+static void conn_connect_done(uv_connect_t *req, int status);
+static void conn_read(conn *c);
+static void conn_read_done(uv_stream_t *handle,
+                           ssize_t nread,
+                           const uv_buf_t *buf);
+static void conn_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf);
+static void conn_write(conn *c, const void *data, unsigned int len);
+static void conn_write_done(uv_write_t *req, int status);
+static void conn_close(conn *c);
+static void conn_close_done(uv_handle_t *handle);
+
+/* |incoming| has been initialized by server.c when this is called. */
+void client_finish_init(server_ctx *sx, client_ctx *cx) {
+  conn *incoming;
+  conn *outgoing;
+
+  cx->sx = sx;
+  cx->state = s_handshake;
+  s5_init(&cx->parser);
+
+  incoming = &cx->incoming;
+  incoming->client = cx;
+  incoming->result = 0;
+  incoming->rdstate = c_stop;
+  incoming->wrstate = c_stop;
+  incoming->idle_timeout = sx->idle_timeout;
+  CHECK(0 == uv_timer_init(sx->loop, &incoming->timer_handle));
+
+  outgoing = &cx->outgoing;
+  outgoing->client = cx;
+  outgoing->result = 0;
+  outgoing->rdstate = c_stop;
+  outgoing->wrstate = c_stop;
+  outgoing->idle_timeout = sx->idle_timeout;
+  CHECK(0 == uv_tcp_init(cx->sx->loop, &outgoing->handle.tcp));
+  CHECK(0 == uv_timer_init(cx->sx->loop, &outgoing->timer_handle));
+
+  /* Wait for the initial packet. */
+  conn_read(incoming);
+}
+
+/* This is the core state machine that drives the client <-> upstream proxy.
+ * We move through the initial handshake and authentication steps first and
+ * end up (if all goes well) in the proxy state where we're just proxying
+ * data between the client and upstream.
+ */
+static void do_next(client_ctx *cx) {
+  int new_state;
+
+  ASSERT(cx->state != s_dead);
+  switch (cx->state) {
+    case s_handshake:
+      new_state = do_handshake(cx);
+      break;
+    case s_handshake_auth:
+      new_state = do_handshake_auth(cx);
+      break;
+    case s_req_start:
+      new_state = do_req_start(cx);
+      break;
+    case s_req_parse:
+      new_state = do_req_parse(cx);
+      break;
+    case s_req_lookup:
+      new_state = do_req_lookup(cx);
+      break;
+    case s_req_connect:
+      new_state = do_req_connect(cx);
+      break;
+    case s_proxy_start:
+      new_state = do_proxy_start(cx);
+      break;
+    case s_proxy:
+      new_state = do_proxy(cx);
+      break;
+    case s_kill:
+      new_state = do_kill(cx);
+      break;
+    case s_almost_dead_0:
+    case s_almost_dead_1:
+    case s_almost_dead_2:
+    case s_almost_dead_3:
+    case s_almost_dead_4:
+      new_state = do_almost_dead(cx);
+      break;
+    default:
+      UNREACHABLE();
+  }
+  cx->state = new_state;
+
+  if (cx->state == s_dead) {
+    if (DEBUG_CHECKS) {
+      memset(cx, -1, sizeof(*cx));
+    }
+    free(cx);
+  }
+}
+
+static int do_handshake(client_ctx *cx) {
+  unsigned int methods;
+  conn *incoming;
+  s5_ctx *parser;
+  uint8_t *data;
+  size_t size;
+  int err;
+
+  parser = &cx->parser;
+  incoming = &cx->incoming;
+  ASSERT(incoming->rdstate == c_done);
+  ASSERT(incoming->wrstate == c_stop);
+  incoming->rdstate = c_stop;
+
+  if (incoming->result < 0) {
+    pr_err("read error: %s", uv_strerror(incoming->result));
+    return do_kill(cx);
+  }
+
+  data = (uint8_t *) incoming->t.buf;
+  size = (size_t) incoming->result;
+  err = s5_parse(parser, &data, &size);
+  if (err == s5_ok) {
+    conn_read(incoming);
+    return s_handshake;  /* Need more data. */
+  }
+
+  if (size != 0) {
+    /* Could allow a round-trip saving shortcut here if the requested auth
+     * method is S5_AUTH_NONE (provided unauthenticated traffic is allowed.)
+     * Requires client support however.
+     */
+    pr_err("junk in handshake");
+    return do_kill(cx);
+  }
+
+  if (err != s5_auth_select) {
+    pr_err("handshake error: %s", s5_strerror(err));
+    return do_kill(cx);
+  }
+
+  methods = s5_auth_methods(parser);
+  if ((methods & S5_AUTH_NONE) && can_auth_none(cx->sx, cx)) {
+    s5_select_auth(parser, S5_AUTH_NONE);
+    conn_write(incoming, "\5\0", 2);  /* No auth required. */
+    return s_req_start;
+  }
+
+  if ((methods & S5_AUTH_PASSWD) && can_auth_passwd(cx->sx, cx)) {
+    /* TODO(bnoordhuis) Implement username/password auth. */
+  }
+
+  conn_write(incoming, "\5\377", 2);  /* No acceptable auth. */
+  return s_kill;
+}
+
+/* TODO(bnoordhuis) Implement username/password auth. */
+static int do_handshake_auth(client_ctx *cx) {
+  UNREACHABLE();
+  return do_kill(cx);
+}
+
+static int do_req_start(client_ctx *cx) {
+  conn *incoming;
+
+  incoming = &cx->incoming;
+  ASSERT(incoming->rdstate == c_stop);
+  ASSERT(incoming->wrstate == c_done);
+  incoming->wrstate = c_stop;
+
+  if (incoming->result < 0) {
+    pr_err("write error: %s", uv_strerror(incoming->result));
+    return do_kill(cx);
+  }
+
+  conn_read(incoming);
+  return s_req_parse;
+}
+
+static int do_req_parse(client_ctx *cx) {
+  conn *incoming;
+  conn *outgoing;
+  s5_ctx *parser;
+  uint8_t *data;
+  size_t size;
+  int err;
+
+  parser = &cx->parser;
+  incoming = &cx->incoming;
+  outgoing = &cx->outgoing;
+  ASSERT(incoming->rdstate == c_done);
+  ASSERT(incoming->wrstate == c_stop);
+  ASSERT(outgoing->rdstate == c_stop);
+  ASSERT(outgoing->wrstate == c_stop);
+  incoming->rdstate = c_stop;
+
+  if (incoming->result < 0) {
+    pr_err("read error: %s", uv_strerror(incoming->result));
+    return do_kill(cx);
+  }
+
+  data = (uint8_t *) incoming->t.buf;
+  size = (size_t) incoming->result;
+  err = s5_parse(parser, &data, &size);
+  if (err == s5_ok) {
+    conn_read(incoming);
+    return s_req_parse;  /* Need more data. */
+  }
+
+  if (size != 0) {
+    pr_err("junk in request %u", (unsigned) size);
+    return do_kill(cx);
+  }
+
+  if (err != s5_exec_cmd) {
+    pr_err("request error: %s", s5_strerror(err));
+    return do_kill(cx);
+  }
+
+  if (parser->cmd == s5_cmd_tcp_bind) {
+    /* Not supported but relatively straightforward to implement. */
+    pr_warn("BIND requests are not supported.");
+    return do_kill(cx);
+  }
+
+  if (parser->cmd == s5_cmd_udp_assoc) {
+    /* Not supported.  Might be hard to implement because libuv has no
+     * functionality for detecting the MTU size which the RFC mandates.
+     */
+    pr_warn("UDP ASSOC requests are not supported.");
+    return do_kill(cx);
+  }
+  ASSERT(parser->cmd == s5_cmd_tcp_connect);
+
+  if (parser->atyp == s5_atyp_host) {
+    conn_getaddrinfo(outgoing, (const char *) parser->daddr);
+    return s_req_lookup;
+  }
+
+  if (parser->atyp == s5_atyp_ipv4) {
+    memset(&outgoing->t.addr4, 0, sizeof(outgoing->t.addr4));
+    outgoing->t.addr4.sin_family = AF_INET;
+    outgoing->t.addr4.sin_port = htons(parser->dport);
+    memcpy(&outgoing->t.addr4.sin_addr,
+           parser->daddr,
+           sizeof(outgoing->t.addr4.sin_addr));
+  } else if (parser->atyp == s5_atyp_ipv6) {
+    memset(&outgoing->t.addr6, 0, sizeof(outgoing->t.addr6));
+    outgoing->t.addr6.sin6_family = AF_INET6;
+    outgoing->t.addr6.sin6_port = htons(parser->dport);
+    memcpy(&outgoing->t.addr6.sin6_addr,
+           parser->daddr,
+           sizeof(outgoing->t.addr6.sin6_addr));
+  } else {
+    UNREACHABLE();
+  }
+
+  return do_req_connect_start(cx);
+}
+
+static int do_req_lookup(client_ctx *cx) {
+  s5_ctx *parser;
+  conn *incoming;
+  conn *outgoing;
+
+  parser = &cx->parser;
+  incoming = &cx->incoming;
+  outgoing = &cx->outgoing;
+  ASSERT(incoming->rdstate == c_stop);
+  ASSERT(incoming->wrstate == c_stop);
+  ASSERT(outgoing->rdstate == c_stop);
+  ASSERT(outgoing->wrstate == c_stop);
+
+  if (outgoing->result < 0) {
+    /* TODO(bnoordhuis) Escape control characters in parser->daddr. */
+    pr_err("lookup error for \"%s\": %s",
+           parser->daddr,
+           uv_strerror(outgoing->result));
+    /* Send back a 'Host unreachable' reply. */
+    conn_write(incoming, "\5\4\0\1\0\0\0\0\0\0", 10);
+    return s_kill;
+  }
+
+  /* Don't make assumptions about the offset of sin_port/sin6_port. */
+  switch (outgoing->t.addr.sa_family) {
+    case AF_INET:
+      outgoing->t.addr4.sin_port = htons(parser->dport);
+      break;
+    case AF_INET6:
+      outgoing->t.addr6.sin6_port = htons(parser->dport);
+      break;
+    default:
+      UNREACHABLE();
+  }
+
+  return do_req_connect_start(cx);
+}
+
+/* Assumes that cx->outgoing.t.sa contains a valid AF_INET/AF_INET6 address. */
+static int do_req_connect_start(client_ctx *cx) {
+  conn *incoming;
+  conn *outgoing;
+  int err;
+
+  incoming = &cx->incoming;
+  outgoing = &cx->outgoing;
+  ASSERT(incoming->rdstate == c_stop);
+  ASSERT(incoming->wrstate == c_stop);
+  ASSERT(outgoing->rdstate == c_stop);
+  ASSERT(outgoing->wrstate == c_stop);
+
+  if (!can_access(cx->sx, cx, &outgoing->t.addr)) {
+    pr_warn("connection not allowed by ruleset");
+    /* Send a 'Connection not allowed by ruleset' reply. */
+    conn_write(incoming, "\5\2\0\1\0\0\0\0\0\0", 10);
+    return s_kill;
+  }
+
+  err = conn_connect(outgoing);
+  if (err != 0) {
+    pr_err("connect error: %s\n", uv_strerror(err));
+    return do_kill(cx);
+  }
+
+  return s_req_connect;
+}
+
+static int do_req_connect(client_ctx *cx) {
+  const struct sockaddr_in6 *in6;
+  const struct sockaddr_in *in;
+  char addr_storage[sizeof(*in6)];
+  conn *incoming;
+  conn *outgoing;
+  uint8_t *buf;
+  int addrlen;
+
+  incoming = &cx->incoming;
+  outgoing = &cx->outgoing;
+  ASSERT(incoming->rdstate == c_stop);
+  ASSERT(incoming->wrstate == c_stop);
+  ASSERT(outgoing->rdstate == c_stop);
+  ASSERT(outgoing->wrstate == c_stop);
+
+  /* Build and send the reply.  Not very pretty but gets the job done. */
+  buf = (uint8_t *) incoming->t.buf;
+  if (outgoing->result == 0) {
+    /* The RFC mandates that the SOCKS server must include the local port
+     * and address in the reply.  So that's what we do.
+     */
+    addrlen = sizeof(addr_storage);
+    CHECK(0 == uv_tcp_getsockname(&outgoing->handle.tcp,
+                                  (struct sockaddr *) addr_storage,
+                                  &addrlen));
+    buf[0] = 5;  /* Version. */
+    buf[1] = 0;  /* Success. */
+    buf[2] = 0;  /* Reserved. */
+    if (addrlen == sizeof(*in)) {
+      buf[3] = 1;  /* IPv4. */
+      in = (const struct sockaddr_in *) &addr_storage;
+      memcpy(buf + 4, &in->sin_addr, 4);
+      memcpy(buf + 8, &in->sin_port, 2);
+      conn_write(incoming, buf, 10);
+    } else if (addrlen == sizeof(*in6)) {
+      buf[3] = 4;  /* IPv6. */
+      in6 = (const struct sockaddr_in6 *) &addr_storage;
+      memcpy(buf + 4, &in6->sin6_addr, 16);
+      memcpy(buf + 20, &in6->sin6_port, 2);
+      conn_write(incoming, buf, 22);
+    } else {
+      UNREACHABLE();
+    }
+    return s_proxy_start;
+  } else {
+    pr_err("upstream connection error: %s\n", uv_strerror(outgoing->result));
+    /* Send a 'Connection refused' reply. */
+    conn_write(incoming, "\5\5\0\1\0\0\0\0\0\0", 10);
+    return s_kill;
+  }
+
+  UNREACHABLE();
+  return s_kill;
+}
+
+static int do_proxy_start(client_ctx *cx) {
+  conn *incoming;
+  conn *outgoing;
+
+  incoming = &cx->incoming;
+  outgoing = &cx->outgoing;
+  ASSERT(incoming->rdstate == c_stop);
+  ASSERT(incoming->wrstate == c_done);
+  ASSERT(outgoing->rdstate == c_stop);
+  ASSERT(outgoing->wrstate == c_stop);
+  incoming->wrstate = c_stop;
+
+  if (incoming->result < 0) {
+    pr_err("write error: %s", uv_strerror(incoming->result));
+    return do_kill(cx);
+  }
+
+  conn_read(incoming);
+  conn_read(outgoing);
+  return s_proxy;
+}
+
+/* Proxy incoming data back and forth. */
+static int do_proxy(client_ctx *cx) {
+  if (conn_cycle("client", &cx->incoming, &cx->outgoing)) {
+    return do_kill(cx);
+  }
+
+  if (conn_cycle("upstream", &cx->outgoing, &cx->incoming)) {
+    return do_kill(cx);
+  }
+
+  return s_proxy;
+}
+
+static int do_kill(client_ctx *cx) {
+  int new_state;
+
+  if (cx->state >= s_almost_dead_0) {
+    return cx->state;
+  }
+
+  /* Try to cancel the request. The callback still runs but if the
+   * cancellation succeeded, it gets called with status=UV_ECANCELED.
+   */
+  new_state = s_almost_dead_1;
+  if (cx->state == s_req_lookup) {
+    new_state = s_almost_dead_0;
+    uv_cancel(&cx->outgoing.t.req);
+  }
+
+  conn_close(&cx->incoming);
+  conn_close(&cx->outgoing);
+  return new_state;
+}
+
+static int do_almost_dead(client_ctx *cx) {
+  ASSERT(cx->state >= s_almost_dead_0);
+  return cx->state + 1;  /* Another finalizer completed. */
+}
+
+static int conn_cycle(const char *who, conn *a, conn *b) {
+  if (a->result < 0) {
+    if (a->result != UV_EOF) {
+      pr_err("%s error: %s", who, uv_strerror(a->result));
+    }
+    return -1;
+  }
+
+  if (b->result < 0) {
+    return -1;
+  }
+
+  if (a->wrstate == c_done) {
+    a->wrstate = c_stop;
+  }
+
+  /* The logic is as follows: read when we don't write and write when we don't
+   * read.  That gives us back-pressure handling for free because if the peer
+   * sends data faster than we consume it, TCP congestion control kicks in.
+   */
+  if (a->wrstate == c_stop) {
+    if (b->rdstate == c_stop) {
+      conn_read(b);
+    } else if (b->rdstate == c_done) {
+      conn_write(a, b->t.buf, b->result);
+      b->rdstate = c_stop;  /* Triggers the call to conn_read() above. */
+    }
+  }
+
+  return 0;
+}
+
+static void conn_timer_reset(conn *c) {
+  CHECK(0 == uv_timer_start(&c->timer_handle,
+                            conn_timer_expire,
+                            c->idle_timeout,
+                            0));
+}
+
+static void conn_timer_expire(uv_timer_t *handle, int status) {
+  conn *c;
+
+  CHECK(0 == status);
+  c = CONTAINER_OF(handle, conn, timer_handle);
+  c->result = UV_ETIMEDOUT;
+  do_next(c->client);
+}
+
+static void conn_getaddrinfo(conn *c, const char *hostname) {
+  struct addrinfo hints;
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = IPPROTO_TCP;
+  CHECK(0 == uv_getaddrinfo(c->client->sx->loop,
+                            &c->t.addrinfo_req,
+                            conn_getaddrinfo_done,
+                            hostname,
+                            NULL,
+                            &hints));
+  conn_timer_reset(c);
+}
+
+static void conn_getaddrinfo_done(uv_getaddrinfo_t *req,
+                                  int status,
+                                  struct addrinfo *ai) {
+  conn *c;
+
+  c = CONTAINER_OF(req, conn, t.addrinfo_req);
+  c->result = status;
+
+  if (status == 0) {
+    /* FIXME(bnoordhuis) Should try all addresses. */
+    if (ai->ai_family == AF_INET) {
+      c->t.addr4 = *(const struct sockaddr_in *) ai->ai_addr;
+    } else if (ai->ai_family == AF_INET6) {
+      c->t.addr6 = *(const struct sockaddr_in6 *) ai->ai_addr;
+    } else {
+      UNREACHABLE();
+    }
+  }
+
+  uv_freeaddrinfo(ai);
+  do_next(c->client);
+}
+
+/* Assumes that c->t.sa contains a valid AF_INET or AF_INET6 address. */
+static int conn_connect(conn *c) {
+  ASSERT(c->t.addr.sa_family == AF_INET ||
+         c->t.addr.sa_family == AF_INET6);
+  conn_timer_reset(c);
+  return uv_tcp_connect(&c->t.connect_req,
+                        &c->handle.tcp,
+                        &c->t.addr,
+                        conn_connect_done);
+}
+
+static void conn_connect_done(uv_connect_t *req, int status) {
+  conn *c;
+
+  if (status == UV_ECANCELED) {
+    return;  /* Handle has been closed. */
+  }
+
+  c = CONTAINER_OF(req, conn, t.connect_req);
+  c->result = status;
+  do_next(c->client);
+}
+
+static void conn_read(conn *c) {
+  ASSERT(c->rdstate == c_stop);
+  CHECK(0 == uv_read_start(&c->handle.stream, conn_alloc, conn_read_done));
+  c->rdstate = c_busy;
+  conn_timer_reset(c);
+}
+
+static void conn_read_done(uv_stream_t *handle,
+                           ssize_t nread,
+                           const uv_buf_t *buf) {
+  conn *c;
+
+  c = CONTAINER_OF(handle, conn, handle);
+  ASSERT(c->t.buf == buf->base);
+  ASSERT(c->rdstate == c_busy);
+  c->rdstate = c_done;
+  c->result = nread;
+
+  uv_read_stop(&c->handle.stream);
+  do_next(c->client);
+}
+
+static void conn_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf) {
+  conn *c;
+
+  c = CONTAINER_OF(handle, conn, handle);
+  ASSERT(c->rdstate == c_busy);
+  buf->base = c->t.buf;
+  buf->len = sizeof(c->t.buf);
+}
+
+static void conn_write(conn *c, const void *data, unsigned int len) {
+  uv_buf_t buf;
+
+  ASSERT(c->wrstate == c_stop || c->wrstate == c_done);
+  c->wrstate = c_busy;
+
+  /* It's okay to cast away constness here, uv_write() won't modify the
+   * memory.
+   */
+  buf.base = (char *) data;
+  buf.len = len;
+
+  CHECK(0 == uv_write(&c->write_req,
+                      &c->handle.stream,
+                      &buf,
+                      1,
+                      conn_write_done));
+  conn_timer_reset(c);
+}
+
+static void conn_write_done(uv_write_t *req, int status) {
+  conn *c;
+
+  if (status == UV_ECANCELED) {
+    return;  /* Handle has been closed. */
+  }
+
+  c = CONTAINER_OF(req, conn, write_req);
+  ASSERT(c->wrstate == c_busy);
+  c->wrstate = c_done;
+  c->result = status;
+  do_next(c->client);
+}
+
+static void conn_close(conn *c) {
+  ASSERT(c->rdstate != c_dead);
+  ASSERT(c->wrstate != c_dead);
+  c->rdstate = c_dead;
+  c->wrstate = c_dead;
+  c->timer_handle.data = c;
+  c->handle.handle.data = c;
+  uv_close(&c->handle.handle, conn_close_done);
+  uv_close((uv_handle_t *) &c->timer_handle, conn_close_done);
+}
+
+static void conn_close_done(uv_handle_t *handle) {
+  conn *c;
+
+  c = handle->data;
+  do_next(c->client);
+}
diff --git a/deps/uv/samples/socks5-proxy/defs.h b/deps/uv/samples/socks5-proxy/defs.h
new file mode 100644 (file)
index 0000000..99ee816
--- /dev/null
@@ -0,0 +1,139 @@
+/* Copyright StrongLoop, Inc. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef DEFS_H_
+#define DEFS_H_
+
+#include "s5.h"
+#include "uv.h"
+
+#include <assert.h>
+#include <netinet/in.h>  /* sockaddr_in, sockaddr_in6 */
+#include <stddef.h>      /* size_t, ssize_t */
+#include <stdint.h>
+#include <sys/socket.h>  /* sockaddr */
+
+struct client_ctx;
+
+typedef struct {
+  const char *bind_host;
+  unsigned short bind_port;
+  unsigned int idle_timeout;
+} server_config;
+
+typedef struct {
+  unsigned int idle_timeout;  /* Connection idle timeout in ms. */
+  uv_tcp_t tcp_handle;
+  uv_loop_t *loop;
+} server_ctx;
+
+typedef struct {
+  unsigned char rdstate;
+  unsigned char wrstate;
+  unsigned int idle_timeout;
+  struct client_ctx *client;  /* Backlink to owning client context. */
+  ssize_t result;
+  union {
+    uv_handle_t handle;
+    uv_stream_t stream;
+    uv_tcp_t tcp;
+    uv_udp_t udp;
+  } handle;
+  uv_timer_t timer_handle;  /* For detecting timeouts. */
+  uv_write_t write_req;
+  /* We only need one of these at a time so make them share memory. */
+  union {
+    uv_getaddrinfo_t addrinfo_req;
+    uv_connect_t connect_req;
+    uv_req_t req;
+    struct sockaddr_in6 addr6;
+    struct sockaddr_in addr4;
+    struct sockaddr addr;
+    char buf[2048];  /* Scratch space. Used to read data into. */
+  } t;
+} conn;
+
+typedef struct client_ctx {
+  unsigned int state;
+  server_ctx *sx;  /* Backlink to owning server context. */
+  s5_ctx parser;  /* The SOCKS protocol parser. */
+  conn incoming;  /* Connection with the SOCKS client. */
+  conn outgoing;  /* Connection with upstream. */
+} client_ctx;
+
+/* server.c */
+int server_run(const server_config *cf, uv_loop_t *loop);
+int can_auth_none(const server_ctx *sx, const client_ctx *cx);
+int can_auth_passwd(const server_ctx *sx, const client_ctx *cx);
+int can_access(const server_ctx *sx,
+               const client_ctx *cx,
+               const struct sockaddr *addr);
+
+/* client.c */
+void client_finish_init(server_ctx *sx, client_ctx *cx);
+
+/* util.c */
+#if defined(__GNUC__)
+# define ATTRIBUTE_FORMAT_PRINTF(a, b) __attribute__((format(printf, a, b)))
+#else
+# define ATTRIBUTE_FORMAT_PRINTF(a, b)
+#endif
+void pr_info(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2);
+void pr_warn(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2);
+void pr_err(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2);
+void *xmalloc(size_t size);
+
+/* main.c */
+const char *_getprogname(void);
+
+/* getopt.c */
+#if !HAVE_UNISTD_H
+extern char *optarg;
+int getopt(int argc, char **argv, const char *options);
+#endif
+
+/* ASSERT() is for debug checks, CHECK() for run-time sanity checks.
+ * DEBUG_CHECKS is for expensive debug checks that we only want to
+ * enable in debug builds but still want type-checked by the compiler
+ * in release builds.
+ */
+#if defined(NDEBUG)
+# define ASSERT(exp)
+# define CHECK(exp)   do { if (!(exp)) abort(); } while (0)
+# define DEBUG_CHECKS (0)
+#else
+# define ASSERT(exp)  assert(exp)
+# define CHECK(exp)   assert(exp)
+# define DEBUG_CHECKS (1)
+#endif
+
+#define UNREACHABLE() CHECK(!"Unreachable code reached.")
+
+/* This macro looks complicated but it's not: it calculates the address
+ * of the embedding struct through the address of the embedded struct.
+ * In other words, if struct A embeds struct B, then we can obtain
+ * the address of A by taking the address of B and subtracting the
+ * field offset of B in A.
+ */
+#define CONTAINER_OF(ptr, type, field)                                        \
+  ((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field)))
+
+#endif  /* DEFS_H_ */
diff --git a/deps/uv/samples/socks5-proxy/getopt.c b/deps/uv/samples/socks5-proxy/getopt.c
new file mode 100644 (file)
index 0000000..8481b22
--- /dev/null
@@ -0,0 +1,131 @@
+/*     $NetBSD: getopt.c,v 1.26 2003/08/07 16:43:40 agc Exp $  */
+
+/*
+ * Copyright (c) 1987, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)getopt.c   8.3 (Berkeley) 4/27/95";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+extern const char *_getprogname(void);
+
+int    opterr = 1,             /* if error message should be printed */
+       optind = 1,             /* index into parent argv vector */
+       optopt,                 /* character checked for validity */
+       optreset;               /* reset getopt */
+char   *optarg;                /* argument associated with option */
+
+#define        BADCH   (int)'?'
+#define        BADARG  (int)':'
+#define        EMSG    ""
+
+/*
+ * getopt --
+ *     Parse argc/argv argument vector.
+ */
+int
+getopt(nargc, nargv, ostr)
+       int nargc;
+       char * const nargv[];
+       const char *ostr;
+{
+       static char *place = EMSG;              /* option letter processing */
+       char *oli;                              /* option letter list index */
+
+       if (optreset || *place == 0) {          /* update scanning pointer */
+               optreset = 0;
+               place = nargv[optind];
+               if (optind >= nargc || *place++ != '-') {
+                       /* Argument is absent or is not an option */
+                       place = EMSG;
+                       return (-1);
+               }
+               optopt = *place++;
+               if (optopt == '-' && *place == 0) {
+                       /* "--" => end of options */
+                       ++optind;
+                       place = EMSG;
+                       return (-1);
+               }
+               if (optopt == 0) {
+                       /* Solitary '-', treat as a '-' option
+                          if the program (eg su) is looking for it. */
+                       place = EMSG;
+                       if (strchr(ostr, '-') == NULL)
+                               return (-1);
+                       optopt = '-';
+               }
+       } else
+               optopt = *place++;
+
+       /* See if option letter is one the caller wanted... */
+       if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) {
+               if (*place == 0)
+                       ++optind;
+               if (opterr && *ostr != ':')
+                       (void)fprintf(stderr,
+                           "%s: illegal option -- %c\n", _getprogname(),
+                           optopt);
+               return (BADCH);
+       }
+
+       /* Does this option need an argument? */
+       if (oli[1] != ':') {
+               /* don't need argument */
+               optarg = NULL;
+               if (*place == 0)
+                       ++optind;
+       } else {
+               /* Option-argument is either the rest of this argument or the
+                  entire next argument. */
+               if (*place)
+                       optarg = place;
+               else if (nargc > ++optind)
+                       optarg = nargv[optind];
+               else {
+                       /* option-argument absent */
+                       place = EMSG;
+                       if (*ostr == ':')
+                               return (BADARG);
+                       if (opterr)
+                               (void)fprintf(stderr,
+                                   "%s: option requires an argument -- %c\n",
+                                   _getprogname(), optopt);
+                       return (BADCH);
+               }
+               place = EMSG;
+               ++optind;
+       }
+       return (optopt);                        /* return option letter */
+}
diff --git a/deps/uv/samples/socks5-proxy/main.c b/deps/uv/samples/socks5-proxy/main.c
new file mode 100644 (file)
index 0000000..04020cb
--- /dev/null
@@ -0,0 +1,99 @@
+/* Copyright StrongLoop, Inc. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "defs.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>  /* getopt */
+#endif
+
+#define DEFAULT_BIND_HOST     "127.0.0.1"
+#define DEFAULT_BIND_PORT     1080
+#define DEFAULT_IDLE_TIMEOUT  (60 * 1000)
+
+static void parse_opts(server_config *cf, int argc, char **argv);
+static void usage(void);
+
+static const char *progname = __FILE__;  /* Reset in main(). */
+
+int main(int argc, char **argv) {
+  server_config config;
+  int err;
+
+  progname = argv[0];
+  memset(&config, 0, sizeof(config));
+  config.bind_host = DEFAULT_BIND_HOST;
+  config.bind_port = DEFAULT_BIND_PORT;
+  config.idle_timeout = DEFAULT_IDLE_TIMEOUT;
+  parse_opts(&config, argc, argv);
+
+  err = server_run(&config, uv_default_loop());
+  if (err) {
+    exit(1);
+  }
+
+  return 0;
+}
+
+const char *_getprogname(void) {
+  return progname;
+}
+
+static void parse_opts(server_config *cf, int argc, char **argv) {
+  int opt;
+
+  while (-1 != (opt = getopt(argc, argv, "H:hp:"))) {
+    switch (opt) {
+      case 'H':
+        cf->bind_host = optarg;
+        break;
+
+      case 'p':
+        if (1 != sscanf(optarg, "%hu", &cf->bind_port)) {
+          pr_err("bad port number: %s", optarg);
+          usage();
+        }
+        break;
+
+      default:
+        usage();
+    }
+  }
+}
+
+static void usage(void) {
+  printf("Usage:\n"
+         "\n"
+         "  %s [-b <address> [-h] [-p <port>]\n"
+         "\n"
+         "Options:\n"
+         "\n"
+         "  -b <hostname|address>  Bind to this address or hostname.\n"
+         "                         Default: \"127.0.0.1\"\n"
+         "  -h                     Show this help message.\n"
+         "  -p <port>              Bind to this port number.  Default: 1080\n"
+         "",
+         progname);
+  exit(1);
+}
diff --git a/deps/uv/samples/socks5-proxy/s5.c b/deps/uv/samples/socks5-proxy/s5.c
new file mode 100644 (file)
index 0000000..4f08e34
--- /dev/null
@@ -0,0 +1,271 @@
+/* Copyright StrongLoop, Inc. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "s5.h"
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>  /* abort() */
+#include <string.h>  /* memset() */
+
+enum {
+  s5_version,
+  s5_nmethods,
+  s5_methods,
+  s5_auth_pw_version,
+  s5_auth_pw_userlen,
+  s5_auth_pw_username,
+  s5_auth_pw_passlen,
+  s5_auth_pw_password,
+  s5_req_version,
+  s5_req_cmd,
+  s5_req_reserved,
+  s5_req_atyp,
+  s5_req_atyp_host,
+  s5_req_daddr,
+  s5_req_dport0,
+  s5_req_dport1,
+  s5_dead
+};
+
+void s5_init(s5_ctx *cx) {
+  memset(cx, 0, sizeof(*cx));
+  cx->state = s5_version;
+}
+
+s5_err s5_parse(s5_ctx *cx, uint8_t **data, size_t *size) {
+  s5_err err;
+  uint8_t *p;
+  uint8_t c;
+  size_t i;
+  size_t n;
+
+  p = *data;
+  n = *size;
+  i = 0;
+
+  while (i < n) {
+    c = p[i];
+    i += 1;
+    switch (cx->state) {
+      case s5_version:
+        if (c != 5) {
+          err = s5_bad_version;
+          goto out;
+        }
+        cx->state = s5_nmethods;
+        break;
+
+      case s5_nmethods:
+        cx->arg0 = 0;
+        cx->arg1 = c;  /* Number of bytes to read. */
+        cx->state = s5_methods;
+        break;
+
+      case s5_methods:
+        if (cx->arg0 < cx->arg1) {
+          switch (c) {
+            case 0:
+              cx->methods |= S5_AUTH_NONE;
+              break;
+            case 1:
+              cx->methods |= S5_AUTH_GSSAPI;
+              break;
+            case 2:
+              cx->methods |= S5_AUTH_PASSWD;
+              break;
+            /* Ignore everything we don't understand. */
+          }
+          cx->arg0 += 1;
+        }
+        if (cx->arg0 == cx->arg1) {
+          err = s5_auth_select;
+          goto out;
+        }
+        break;
+
+      case s5_auth_pw_version:
+        if (c != 1) {
+          err = s5_bad_version;
+          goto out;
+        }
+        cx->state = s5_auth_pw_userlen;
+        break;
+
+      case s5_auth_pw_userlen:
+        cx->arg0 = 0;
+        cx->userlen = c;
+        cx->state = s5_auth_pw_username;
+        break;
+
+      case s5_auth_pw_username:
+        if (cx->arg0 < cx->userlen) {
+          cx->username[cx->arg0] = c;
+          cx->arg0 += 1;
+        }
+        if (cx->arg0 == cx->userlen) {
+          cx->username[cx->userlen] = '\0';
+          cx->state = s5_auth_pw_passlen;
+        }
+        break;
+
+      case s5_auth_pw_passlen:
+        cx->arg0 = 0;
+        cx->passlen = c;
+        cx->state = s5_auth_pw_password;
+        break;
+
+      case s5_auth_pw_password:
+        if (cx->arg0 < cx->passlen) {
+          cx->password[cx->arg0] = c;
+          cx->arg0 += 1;
+        }
+        if (cx->arg0 == cx->passlen) {
+          cx->password[cx->passlen] = '\0';
+          cx->state = s5_req_version;
+          err = s5_auth_verify;
+          goto out;
+        }
+        break;
+
+      case s5_req_version:
+        if (c != 5) {
+          err = s5_bad_version;
+          goto out;
+        }
+        cx->state = s5_req_cmd;
+        break;
+
+      case s5_req_cmd:
+        switch (c) {
+          case 1:  /* TCP connect */
+            cx->cmd = s5_cmd_tcp_connect;
+            break;
+          case 3:  /* UDP associate */
+            cx->cmd = s5_cmd_udp_assoc;
+            break;
+          default:
+            err = s5_bad_cmd;
+            goto out;
+        }
+        cx->state = s5_req_reserved;
+        break;
+
+      case s5_req_reserved:
+        cx->state = s5_req_atyp;
+        break;
+
+      case s5_req_atyp:
+        cx->arg0 = 0;
+        switch (c) {
+          case 1:  /* IPv4, four octets. */
+            cx->state = s5_req_daddr;
+            cx->atyp = s5_atyp_ipv4;
+            cx->arg1 = 4;
+            break;
+          case 3:  /* Hostname.  First byte is length. */
+            cx->state = s5_req_atyp_host;
+            cx->atyp = s5_atyp_host;
+            cx->arg1 = 0;
+            break;
+          case 4:  /* IPv6, sixteen octets. */
+            cx->state = s5_req_daddr;
+            cx->atyp = s5_atyp_ipv6;
+            cx->arg1 = 16;
+            break;
+          default:
+            err = s5_bad_atyp;
+            goto out;
+        }
+        break;
+
+      case s5_req_atyp_host:
+        cx->arg1 = c;
+        cx->state = s5_req_daddr;
+        break;
+
+      case s5_req_daddr:
+        if (cx->arg0 < cx->arg1) {
+          cx->daddr[cx->arg0] = c;
+          cx->arg0 += 1;
+        }
+        if (cx->arg0 == cx->arg1) {
+          cx->daddr[cx->arg1] = '\0';
+          cx->state = s5_req_dport0;
+        }
+        break;
+
+      case s5_req_dport0:
+        cx->dport = c << 8;
+        cx->state = s5_req_dport1;
+        break;
+
+      case s5_req_dport1:
+        cx->dport |= c;
+        cx->state = s5_dead;
+        err = s5_exec_cmd;
+        goto out;
+
+      case s5_dead:
+        break;
+
+      default:
+        abort();
+    }
+  }
+  err = s5_ok;
+
+out:
+  *data = p + i;
+  *size = n - i;
+  return err;
+}
+
+unsigned int s5_auth_methods(const s5_ctx *cx) {
+  return cx->methods;
+}
+
+int s5_select_auth(s5_ctx *cx, s5_auth_method method) {
+  int err;
+
+  err = 0;
+  switch (method) {
+    case S5_AUTH_NONE:
+      cx->state = s5_req_version;
+      break;
+    case S5_AUTH_PASSWD:
+      cx->state = s5_auth_pw_version;
+      break;
+    default:
+      err = -EINVAL;
+  }
+
+  return err;
+}
+
+const char *s5_strerror(s5_err err) {
+#define S5_ERR_GEN(_, name, errmsg) case s5_ ## name: return errmsg;
+  switch (err) {
+    S5_ERR_MAP(S5_ERR_GEN)
+    default: ;  /* Silence s5_max_errors -Wswitch warning. */
+  }
+#undef S5_ERR_GEN
+  return "Unknown error.";
+}
diff --git a/deps/uv/samples/socks5-proxy/s5.h b/deps/uv/samples/socks5-proxy/s5.h
new file mode 100644 (file)
index 0000000..715f322
--- /dev/null
@@ -0,0 +1,94 @@
+/* Copyright StrongLoop, Inc. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef S5_H_
+#define S5_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define S5_ERR_MAP(V)                                                         \
+  V(-1, bad_version, "Bad protocol version.")                                 \
+  V(-2, bad_cmd, "Bad protocol command.")                                     \
+  V(-3, bad_atyp, "Bad address type.")                                        \
+  V(0, ok, "No error.")                                                       \
+  V(1, auth_select, "Select authentication method.")                          \
+  V(2, auth_verify, "Verify authentication.")                                 \
+  V(3, exec_cmd, "Execute command.")                                          \
+
+typedef enum {
+#define S5_ERR_GEN(code, name, _) s5_ ## name = code,
+  S5_ERR_MAP(S5_ERR_GEN)
+#undef S5_ERR_GEN
+  s5_max_errors
+} s5_err;
+
+typedef enum {
+  S5_AUTH_NONE = 1 << 0,
+  S5_AUTH_GSSAPI = 1 << 1,
+  S5_AUTH_PASSWD = 1 << 2
+} s5_auth_method;
+
+typedef enum {
+  s5_auth_allow,
+  s5_auth_deny
+} s5_auth_result;
+
+typedef enum {
+  s5_atyp_ipv4,
+  s5_atyp_ipv6,
+  s5_atyp_host
+} s5_atyp;
+
+typedef enum {
+  s5_cmd_tcp_connect,
+  s5_cmd_tcp_bind,
+  s5_cmd_udp_assoc
+} s5_cmd;
+
+typedef struct {
+  uint32_t arg0;  /* Scratch space for the state machine. */
+  uint32_t arg1;  /* Scratch space for the state machine. */
+  uint8_t state;
+  uint8_t methods;
+  uint8_t cmd;
+  uint8_t atyp;
+  uint8_t userlen;
+  uint8_t passlen;
+  uint16_t dport;
+  uint8_t username[257];
+  uint8_t password[257];
+  uint8_t daddr[257];  /* TODO(bnoordhuis) Merge with username/password. */
+} s5_ctx;
+
+void s5_init(s5_ctx *ctx);
+
+s5_err s5_parse(s5_ctx *cx, uint8_t **data, size_t *size);
+
+/* Only call after s5_parse() has returned s5_want_auth_method. */
+unsigned int s5_auth_methods(const s5_ctx *cx);
+
+/* Call after s5_parse() has returned s5_want_auth_method. */
+int s5_select_auth(s5_ctx *cx, s5_auth_method method);
+
+const char *s5_strerror(s5_err err);
+
+#endif  /* S5_H_ */
diff --git a/deps/uv/samples/socks5-proxy/server.c b/deps/uv/samples/socks5-proxy/server.c
new file mode 100644 (file)
index 0000000..477469f
--- /dev/null
@@ -0,0 +1,241 @@
+/* Copyright StrongLoop, Inc. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "defs.h"
+#include <netinet/in.h>  /* INET6_ADDRSTRLEN */
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef INET6_ADDRSTRLEN
+# define INET6_ADDRSTRLEN 63
+#endif
+
+typedef struct {
+  uv_getaddrinfo_t getaddrinfo_req;
+  server_config config;
+  server_ctx *servers;
+  uv_loop_t *loop;
+} server_state;
+
+static void do_bind(uv_getaddrinfo_t *req, int status, struct addrinfo *ai);
+static void on_connection(uv_stream_t *server, int status);
+
+int server_run(const server_config *cf, uv_loop_t *loop) {
+  struct addrinfo hints;
+  server_state state;
+  int err;
+
+  memset(&state, 0, sizeof(state));
+  state.servers = NULL;
+  state.config = *cf;
+  state.loop = loop;
+
+  /* Resolve the address of the interface that we should bind to.
+   * The getaddrinfo callback starts the server and everything else.
+   */
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = IPPROTO_TCP;
+
+  err = uv_getaddrinfo(loop,
+                       &state.getaddrinfo_req,
+                       do_bind,
+                       cf->bind_host,
+                       NULL,
+                       &hints);
+  if (err != 0) {
+    pr_err("getaddrinfo: %s", uv_strerror(err));
+    return err;
+  }
+
+  /* Start the event loop.  Control continues in do_bind(). */
+  if (uv_run(loop, UV_RUN_DEFAULT)) {
+    abort();
+  }
+
+  /* Please Valgrind. */
+  uv_loop_delete(loop);
+  free(state.servers);
+  return 0;
+}
+
+/* Bind a server to each address that getaddrinfo() reported. */
+static void do_bind(uv_getaddrinfo_t *req, int status, struct addrinfo *addrs) {
+  char addrbuf[INET6_ADDRSTRLEN + 1];
+  unsigned int ipv4_naddrs;
+  unsigned int ipv6_naddrs;
+  server_state *state;
+  server_config *cf;
+  struct addrinfo *ai;
+  const void *addrv;
+  const char *what;
+  uv_loop_t *loop;
+  server_ctx *sx;
+  unsigned int n;
+  int err;
+  union {
+    struct sockaddr addr;
+    struct sockaddr_in addr4;
+    struct sockaddr_in6 addr6;
+  } s;
+
+  state = CONTAINER_OF(req, server_state, getaddrinfo_req);
+  loop = state->loop;
+  cf = &state->config;
+
+  if (status < 0) {
+    pr_err("getaddrinfo(\"%s\"): %s", cf->bind_host, uv_strerror(status));
+    uv_freeaddrinfo(addrs);
+    return;
+  }
+
+  ipv4_naddrs = 0;
+  ipv6_naddrs = 0;
+  for (ai = addrs; ai != NULL; ai = ai->ai_next) {
+    if (ai->ai_family == AF_INET) {
+      ipv4_naddrs += 1;
+    } else if (ai->ai_family == AF_INET6) {
+      ipv6_naddrs += 1;
+    }
+  }
+
+  if (ipv4_naddrs == 0 && ipv6_naddrs == 0) {
+    pr_err("%s has no IPv4/6 addresses", cf->bind_host);
+    uv_freeaddrinfo(addrs);
+    return;
+  }
+
+  state->servers =
+      xmalloc((ipv4_naddrs + ipv6_naddrs) * sizeof(state->servers[0]));
+
+  n = 0;
+  for (ai = addrs; ai != NULL; ai = ai->ai_next) {
+    if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
+      continue;
+    }
+
+    if (ai->ai_family == AF_INET) {
+      s.addr4 = *(const struct sockaddr_in *) ai->ai_addr;
+      s.addr4.sin_port = htons(cf->bind_port);
+      addrv = &s.addr4.sin_addr;
+    } else if (ai->ai_family == AF_INET6) {
+      s.addr6 = *(const struct sockaddr_in6 *) ai->ai_addr;
+      s.addr6.sin6_port = htons(cf->bind_port);
+      addrv = &s.addr6.sin6_addr;
+    } else {
+      UNREACHABLE();
+    }
+
+    if (uv_inet_ntop(s.addr.sa_family, addrv, addrbuf, sizeof(addrbuf))) {
+      UNREACHABLE();
+    }
+
+    sx = state->servers + n;
+    sx->loop = loop;
+    sx->idle_timeout = state->config.idle_timeout;
+    CHECK(0 == uv_tcp_init(loop, &sx->tcp_handle));
+
+    what = "uv_tcp_bind";
+    err = uv_tcp_bind(&sx->tcp_handle, &s.addr);
+    if (err == 0) {
+      what = "uv_listen";
+      err = uv_listen((uv_stream_t *) &sx->tcp_handle, 128, on_connection);
+    }
+
+    if (err != 0) {
+      pr_err("%s(\"%s:%hu\"): %s",
+             what,
+             addrbuf,
+             cf->bind_port,
+             uv_strerror(err));
+      while (n > 0) {
+        n -= 1;
+        uv_close((uv_handle_t *) (state->servers + n), NULL);
+      }
+      break;
+    }
+
+    pr_info("listening on %s:%hu", addrbuf, cf->bind_port);
+    n += 1;
+  }
+
+  uv_freeaddrinfo(addrs);
+}
+
+static void on_connection(uv_stream_t *server, int status) {
+  server_ctx *sx;
+  client_ctx *cx;
+
+  CHECK(status == 0);
+  sx = CONTAINER_OF(server, server_ctx, tcp_handle);
+  cx = xmalloc(sizeof(*cx));
+  CHECK(0 == uv_tcp_init(sx->loop, &cx->incoming.handle.tcp));
+  CHECK(0 == uv_accept(server, &cx->incoming.handle.stream));
+  client_finish_init(sx, cx);
+}
+
+int can_auth_none(const server_ctx *sx, const client_ctx *cx) {
+  return 1;
+}
+
+int can_auth_passwd(const server_ctx *sx, const client_ctx *cx) {
+  return 0;
+}
+
+int can_access(const server_ctx *sx,
+               const client_ctx *cx,
+               const struct sockaddr *addr) {
+  const struct sockaddr_in6 *addr6;
+  const struct sockaddr_in *addr4;
+  const uint32_t *p;
+  uint32_t a;
+  uint32_t b;
+  uint32_t c;
+  uint32_t d;
+
+  /* TODO(bnoordhuis) Implement proper access checks.  For now, just reject
+   * traffic to localhost.
+   */
+  if (addr->sa_family == AF_INET) {
+    addr4 = (const struct sockaddr_in *) addr;
+    d = ntohl(addr4->sin_addr.s_addr);
+    return (d >> 24) != 0x7F;
+  }
+
+  if (addr->sa_family == AF_INET6) {
+    addr6 = (const struct sockaddr_in6 *) addr;
+    p = (const uint32_t *) &addr6->sin6_addr.s6_addr;
+    a = ntohl(p[0]);
+    b = ntohl(p[1]);
+    c = ntohl(p[2]);
+    d = ntohl(p[3]);
+    if (a == 0 && b == 0 && c == 0 && d == 1) {
+      return 0;  /* "::1" style address. */
+    }
+    if (a == 0 && b == 0 && c == 0xFFFF && (d >> 24) == 0x7F) {
+      return 0;  /* "::ffff:127.x.x.x" style address. */
+    }
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/deps/uv/samples/socks5-proxy/util.c b/deps/uv/samples/socks5-proxy/util.c
new file mode 100644 (file)
index 0000000..af34f05
--- /dev/null
@@ -0,0 +1,72 @@
+/* Copyright StrongLoop, Inc. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "defs.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static void pr_do(FILE *stream,
+                  const char *label,
+                  const char *fmt,
+                  va_list ap);
+
+void *xmalloc(size_t size) {
+  void *ptr;
+
+  ptr = malloc(size);
+  if (ptr == NULL) {
+    pr_err("out of memory, need %lu bytes", (unsigned long) size);
+    exit(1);
+  }
+
+  return ptr;
+}
+
+void pr_info(const char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  pr_do(stdout, "info", fmt, ap);
+  va_end(ap);
+}
+
+void pr_warn(const char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  pr_do(stderr, "warn", fmt, ap);
+  va_end(ap);
+}
+
+void pr_err(const char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  pr_do(stderr, "error", fmt, ap);
+  va_end(ap);
+}
+
+static void pr_do(FILE *stream,
+                  const char *label,
+                  const char *fmt,
+                  va_list ap) {
+  char fmtbuf[1024];
+  vsnprintf(fmtbuf, sizeof(fmtbuf), fmt, ap);
+  fprintf(stream, "%s:%s: %s\n", _getprogname(), label, fmtbuf);
+}
index aa15837..fe02b45 100644 (file)
 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))
index fe0ef42..2521681 100644 (file)
@@ -45,7 +45,7 @@
 #include <sys/proc.h>
 #include <sys/procfs.h>
 
-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);
index b23f6ae..79813a0 100644 (file)
@@ -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);
 }
 
 
index a03ef2a..c165599 100644 (file)
@@ -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)
index bb8fb16..dcae244 100644 (file)
@@ -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);
index 6cabf51..64517c4 100644 (file)
@@ -170,6 +170,8 @@ skip:
 #elif defined(__APPLE__)                                                      \
     || defined(__DragonFly__)                                                 \
     || defined(__FreeBSD__)                                                   \
+    || defined(__NetBSD__)                                                    \
+    || defined(__OpenBSD__)                                                   \
     || defined(__sun)
   struct timeval tv[2];
   tv[0].tv_sec  = req->atime;
index 305de6d..3618f46 100644 (file)
@@ -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.
index c050c52..79e41fa 100644 (file)
@@ -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)) {
index 470045d..70f5d9e 100644 (file)
@@ -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 @@ update_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;
index 8bdd53d..78b2343 100644 (file)
 # include <linux/if_packet.h>
 #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 <linux/time.h> but we can't
  * include that file because it conflicts with <time.h>. 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 @@ update_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;
 }
 
 
index cbe9317..94a5c03 100644 (file)
@@ -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;
index 0722a6b..7423a71 100644 (file)
@@ -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);
index 30f6fbd..0ff9ad6 100644 (file)
@@ -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);
index 705a973..fd4afb6 100644 (file)
@@ -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 {
index 42990ce..6854ac1 100644 (file)
@@ -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) {
index d910a68..abef01e 100644 (file)
@@ -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);
     }
index fe99d08..f31a23f 100644 (file)
@@ -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 @@ update_timeout:
 }
 
 
-uint64_t uv__hrtime(void) {
+uint64_t uv__hrtime(uv_clocktype_t type) {
   return gethrtime();
 }
 
index 8c38c7f..f2ce082 100644 (file)
@@ -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__)
index 6cfd108..4129a36 100644 (file)
@@ -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;
index 7ca1ab1..2170dee 100644 (file)
@@ -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
 
 
index a51f8f0..e3e11c7 100644 (file)
@@ -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);
 
index 218ea8f..a5bb743 100644 (file)
@@ -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. */
index 8e7666a..b736c37 100644 (file)
 # include <stdint.h>
 #endif
 
+#if !defined(_WIN32)
+# include <sys/time.h>
+# include <sys/resource.h>  /* 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 <stdarg.h>
diff --git a/deps/uv/test/test-close-fd.c b/deps/uv/test/test-close-fd.c
new file mode 100644 (file)
index 0000000..0d17f07
--- /dev/null
@@ -0,0 +1,77 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#if !defined(_WIN32)
+
+#include "uv.h"
+#include "task.h"
+#include <fcntl.h>
+#include <unistd.h>
+
+static unsigned int read_cb_called;
+
+static void alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) {
+  static char slab[1];
+  buf->base = slab;
+  buf->len = sizeof(slab);
+}
+
+static void read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) {
+  switch (++read_cb_called) {
+  case 1:
+    ASSERT(nread == 1);
+    uv_read_stop(handle);
+    break;
+  case 2:
+    ASSERT(nread == UV_EOF);
+    uv_close((uv_handle_t *) handle, NULL);
+    break;
+  default:
+    ASSERT(!"read_cb_called > 2");
+  }
+}
+
+TEST_IMPL(close_fd) {
+  uv_pipe_t pipe_handle;
+  int fd[2];
+
+  ASSERT(0 == pipe(fd));
+  ASSERT(0 == fcntl(fd[0], F_SETFL, O_NONBLOCK));
+  ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0));
+  ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0]));
+  fd[0] = -1;  /* uv_pipe_open() takes ownership of the file descriptor. */
+  ASSERT(1 == write(fd[1], "", 1));
+  ASSERT(0 == close(fd[1]));
+  fd[1] = -1;
+  ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb));
+  ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+  ASSERT(1 == read_cb_called);
+  ASSERT(0 == uv_is_active((const uv_handle_t *) &pipe_handle));
+  ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb));
+  ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+  ASSERT(2 == read_cb_called);
+  ASSERT(0 != uv_is_closing((const uv_handle_t *) &pipe_handle));
+
+  MAKE_VALGRIND_HAPPY();
+  return 0;
+}
+
+#endif  /* !defined(_WIN32) */
index 66132e1..3286de5 100644 (file)
 #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__) */
index d963f04..b3f2e0a 100644 (file)
@@ -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
index 454c181..0a355af 100644 (file)
@@ -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 (file)
index 0000000..471be53
--- /dev/null
@@ -0,0 +1,183 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "task.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static struct sockaddr_in addr;
+static uv_tcp_t tcp_server;
+static uv_tcp_t tcp_outgoing[2];
+static uv_tcp_t tcp_incoming[ARRAY_SIZE(tcp_outgoing)];
+static uv_connect_t connect_reqs[ARRAY_SIZE(tcp_outgoing)];
+static uv_tcp_t tcp_check;
+static uv_connect_t tcp_check_req;
+static uv_write_t write_reqs[ARRAY_SIZE(tcp_outgoing)];
+static unsigned int got_connections;
+static unsigned int close_cb_called;
+static unsigned int write_cb_called;
+static unsigned int read_cb_called;
+
+static void close_cb(uv_handle_t* handle) {
+  close_cb_called++;
+}
+
+static void write_cb(uv_write_t* req, int status) {
+  ASSERT(status == 0);
+  write_cb_called++;
+}
+
+static void connect_cb(uv_connect_t* req, int status) {
+  unsigned int i;
+  uv_buf_t buf;
+  uv_stream_t* outgoing;
+
+  if (req == &tcp_check_req) {
+    ASSERT(status != 0);
+
+    /* Close check and incoming[0], time to finish test */
+    uv_close((uv_handle_t*) &tcp_incoming[0], close_cb);
+    uv_close((uv_handle_t*) &tcp_check, close_cb);
+    return;
+  }
+
+  ASSERT(status == 0);
+  ASSERT(connect_reqs <= req);
+  ASSERT(req <= connect_reqs + ARRAY_SIZE(connect_reqs));
+  i = req - connect_reqs;
+
+  buf = uv_buf_init("x", 1);
+  outgoing = (uv_stream_t*) &tcp_outgoing[i];
+  ASSERT(0 == uv_write(&write_reqs[i], outgoing, &buf, 1, write_cb));
+}
+
+static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
+  static char slab[1];
+  buf->base = slab;
+  buf->len = sizeof(slab);
+}
+
+static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
+  uv_loop_t* loop;
+  unsigned int i;
+
+  /* Only first stream should receive read events */
+  ASSERT(stream == (uv_stream_t*) &tcp_incoming[0]);
+  ASSERT(0 == uv_read_stop(stream));
+  ASSERT(1 == nread);
+
+  loop = stream->loop;
+  read_cb_called++;
+
+  /* Close all active incomings, except current one */
+  for (i = 1; i < got_connections; i++)
+    uv_close((uv_handle_t*) &tcp_incoming[i], close_cb);
+
+  /* Create new fd that should be one of the closed incomings */
+  ASSERT(0 == uv_tcp_init(loop, &tcp_check));
+  ASSERT(0 == uv_tcp_connect(&tcp_check_req,
+                             &tcp_check,
+                             (const struct sockaddr*) &addr,
+                             connect_cb));
+  ASSERT(0 == uv_read_start((uv_stream_t*) &tcp_check, alloc_cb, read_cb));
+
+  /* Close server, so no one will connect to it */
+  uv_close((uv_handle_t*) &tcp_server, close_cb);
+}
+
+static void connection_cb(uv_stream_t* server, int status) {
+  unsigned int i;
+  uv_tcp_t* incoming;
+
+  ASSERT(server == (uv_stream_t*) &tcp_server);
+
+  /* Ignore tcp_check connection */
+  if (got_connections == ARRAY_SIZE(tcp_incoming))
+    return;
+
+  /* Accept everyone */
+  incoming = &tcp_incoming[got_connections++];
+  ASSERT(0 == uv_tcp_init(server->loop, incoming));
+  ASSERT(0 == uv_accept(server, (uv_stream_t*) incoming));
+
+  if (got_connections != ARRAY_SIZE(tcp_incoming))
+    return;
+
+  /* Once all clients are accepted - start reading */
+  for (i = 0; i < ARRAY_SIZE(tcp_incoming); i++) {
+    incoming = &tcp_incoming[i];
+    ASSERT(0 == uv_read_start((uv_stream_t*) incoming, alloc_cb, read_cb));
+  }
+}
+
+TEST_IMPL(tcp_close_accept) {
+  unsigned int i;
+  uv_loop_t* loop;
+  uv_tcp_t* client;
+
+  /*
+   * A little explanation of what goes on below:
+   *
+   * We'll create server and connect to it using two clients, each writing one
+   * byte once connected.
+   *
+   * When all clients will be accepted by server - we'll start reading from them
+   * and, on first client's first byte, will close second client and server.
+   * After that, we'll immediately initiate new connection to server using
+   * tcp_check handle (thus, reusing fd from second client).
+   *
+   * In this situation uv__io_poll()'s event list should still contain read
+   * event for second client, and, if not cleaned up properly, `tcp_check` will
+   * receive stale event of second incoming and invoke `connect_cb` with zero
+   * status.
+   */
+
+  loop = uv_default_loop();
+  ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
+
+  ASSERT(0 == uv_tcp_init(loop, &tcp_server));
+  ASSERT(0 == uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr));
+  ASSERT(0 == uv_listen((uv_stream_t*) &tcp_server,
+                        ARRAY_SIZE(tcp_outgoing),
+                        connection_cb));
+
+  for (i = 0; i < ARRAY_SIZE(tcp_outgoing); i++) {
+    client = tcp_outgoing + i;
+
+    ASSERT(0 == uv_tcp_init(loop, client));
+    ASSERT(0 == uv_tcp_connect(&connect_reqs[i],
+                               client,
+                               (const struct sockaddr*) &addr,
+                               connect_cb));
+  }
+
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  ASSERT(ARRAY_SIZE(tcp_outgoing) == got_connections);
+  ASSERT((ARRAY_SIZE(tcp_outgoing) + 2) == close_cb_called);
+  ASSERT(ARRAY_SIZE(tcp_outgoing) == write_cb_called);
+  ASSERT(1 == read_cb_called);
+
+  MAKE_VALGRIND_HAPPY();
+  return 0;
+}
diff --git a/deps/uv/test/test-util.c b/deps/uv/test/test-util.c
deleted file mode 100644 (file)
index d61d3b1..0000000
+++ /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 <string.h>
-
-#define memeq(a, b, c) (memcmp((a), (b), (c)) == 0)
-
-
-TEST_IMPL(strlcpy) {
-  size_t r;
-
-  {
-    char dst[2] = "A";
-    r = uv_strlcpy(dst, "", 0);
-    ASSERT(r == 0);
-    ASSERT(memeq(dst, "A", 1));
-  }
-
-  {
-    char dst[2] = "A";
-    r = uv_strlcpy(dst, "B", 1);
-    ASSERT(r == 0);
-    ASSERT(memeq(dst, "", 1));
-  }
-
-  {
-    char dst[2] = "A";
-    r = uv_strlcpy(dst, "B", 2);
-    ASSERT(r == 1);
-    ASSERT(memeq(dst, "B", 2));
-  }
-
-  {
-    char dst[3] = "AB";
-    r = uv_strlcpy(dst, "CD", 3);
-    ASSERT(r == 2);
-    ASSERT(memeq(dst, "CD", 3));
-  }
-
-  return 0;
-}
-
-
-TEST_IMPL(strlcat) {
-  size_t r;
-
-  {
-    char dst[2] = "A";
-    r = uv_strlcat(dst, "B", 1);
-    ASSERT(r == 1);
-    ASSERT(memeq(dst, "A", 2));
-  }
-
-  {
-    char dst[2] = "A";
-    r = uv_strlcat(dst, "B", 2);
-    ASSERT(r == 1);
-    ASSERT(memeq(dst, "A", 2));
-  }
-
-  {
-    char dst[3] = "A";
-    r = uv_strlcat(dst, "B", 3);
-    ASSERT(r == 2);
-    ASSERT(memeq(dst, "AB", 3));
-  }
-
-  {
-    char dst[5] = "AB";
-    r = uv_strlcat(dst, "CD", 5);
-    ASSERT(r == 4);
-    ASSERT(memeq(dst, "ABCD", 5));
-  }
-
-  return 0;
-}
diff --git a/deps/uv/test/test-watcher-cross-stop.c b/deps/uv/test/test-watcher-cross-stop.c
new file mode 100644 (file)
index 0000000..c701dd2
--- /dev/null
@@ -0,0 +1,101 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "task.h"
+
+#include <string.h>
+#include <errno.h>
+
+/* NOTE: Number should be big enough to trigger this problem */
+static uv_udp_t sockets[2500];
+static uv_udp_send_t reqs[ARRAY_SIZE(sockets)];
+static char slab[1];
+static unsigned int recv_cb_called;
+static unsigned int send_cb_called;
+static unsigned int close_cb_called;
+
+static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
+  buf->base = slab;
+  buf->len = sizeof(slab);
+}
+
+
+static void recv_cb(uv_udp_t* handle,
+                    ssize_t nread,
+                    const uv_buf_t* buf,
+                    const struct sockaddr* addr,
+                    unsigned flags) {
+  recv_cb_called++;
+}
+
+
+static void send_cb(uv_udp_send_t* req, int status) {
+  send_cb_called++;
+}
+
+
+static void close_cb(uv_handle_t* handle) {
+  close_cb_called++;
+}
+
+
+TEST_IMPL(watcher_cross_stop) {
+  uv_loop_t* loop = uv_default_loop();
+  unsigned int i;
+  struct sockaddr_in addr;
+  uv_buf_t buf;
+  char big_string[1024];
+
+  TEST_FILE_LIMIT(ARRAY_SIZE(sockets) + 32);
+
+  ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
+  memset(big_string, 'A', sizeof(big_string));
+  buf = uv_buf_init(big_string, sizeof(big_string));
+
+  for (i = 0; i < ARRAY_SIZE(sockets); i++) {
+    ASSERT(0 == uv_udp_init(loop, &sockets[i]));
+    ASSERT(0 == uv_udp_bind(&sockets[i], (const struct sockaddr*) &addr, 0));
+    ASSERT(0 == uv_udp_recv_start(&sockets[i], alloc_cb, recv_cb));
+    ASSERT(0 == uv_udp_send(&reqs[i],
+                            &sockets[i],
+                            &buf,
+                            1,
+                            (const struct sockaddr*) &addr,
+                            send_cb));
+  }
+
+  while (recv_cb_called == 0)
+    uv_run(loop, UV_RUN_ONCE);
+
+  for (i = 0; i < ARRAY_SIZE(sockets); i++)
+    uv_close((uv_handle_t*) &sockets[i], close_cb);
+
+  ASSERT(0 < recv_cb_called && recv_cb_called <= ARRAY_SIZE(sockets));
+  ASSERT(ARRAY_SIZE(sockets) == send_cb_called);
+
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  ASSERT(ARRAY_SIZE(sockets) == close_cb_called);
+
+  MAKE_VALGRIND_HAPPY();
+  return 0;
+}
index 9509564..25190b6 100644 (file)
         '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',
         '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',
         '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',
index 1b2f865..0b7ea48 100644 (file)
@@ -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.