diff --git a/changes/UserspaceIOCPBuffers b/changes/UserspaceIOCPBuffers
new file mode 100644
index 0000000000..7115b5fe55
--- /dev/null
+++ b/changes/UserspaceIOCPBuffers
@@ -0,0 +1,7 @@
+ o Minor features:
+ - Experimental support for running on Windows with IOCP and no
+ kernel-space socket buffers. This feature is controlled by a new
+ UserspaceIOCPBuffers feature (off by default), which has no
+ effect unless Tor has been built with support for bufferevents,
+ is running on Windows, and has enabled IOCP. This may, in the
+ long run, help solve or mitigate bug 98.
diff --git a/src/or/config.c b/src/or/config.c
index 1b9f9fb475..4710b5590b 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -403,6 +403,7 @@ static config_var_t _option_vars[] = {
V(UseEntryGuards, BOOL, "1"),
V(UseMicrodescriptors, AUTOBOOL, "auto"),
V(User, STRING, NULL),
+ V(UserspaceIOCPBuffers, BOOL, "0"),
VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"),
VAR("V2AuthoritativeDirectory",BOOL, V2AuthoritativeDir, "0"),
VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"),
diff --git a/src/or/main.c b/src/or/main.c
index c1a7015e68..9f6d307a3e 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -196,6 +196,26 @@ free_old_inbuf(connection_t *conn)
}
#endif
+#ifdef MS_WINDOWS
+/** Remove the kernel-space send and receive buffers for s. For use
+ * with IOCP only. */
+static int
+set_buffer_lengths_to_zero(tor_socket_t s)
+{
+ int zero = 0;
+ int r = 0;
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero, sizeof(zero))) {
+ log_warn(LD_NET, "Unable to clear SO_SNDBUF");
+ r = -1;
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero, sizeof(zero))) {
+ log_warn(LD_NET, "Unable to clear SO_RCVBUF");
+ r = -1;
+ }
+ return r;
+}
+#endif
+
/** Add conn to the array of connections that we can poll on. The
* connection's socket must be set; the connection starts out
* non-reading and non-writing.
@@ -216,6 +236,14 @@ connection_add_impl(connection_t *conn, int is_connecting)
#ifdef USE_BUFFEREVENTS
if (connection_type_uses_bufferevent(conn)) {
if (SOCKET_OK(conn->s) && !conn->linked) {
+
+#ifdef MS_WINDOWS
+ if (tor_libevent_using_iocp_bufferevents() &&
+ get_options()->UserspaceIOCPBuffers) {
+ set_buffer_lengths_to_zero(conn->s);
+ }
+#endif
+
conn->bufev = bufferevent_socket_new(
tor_libevent_get_base(),
conn->s,
diff --git a/src/or/or.h b/src/or/or.h
index e4f9b9b2b6..7fb7e9cb22 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -3424,6 +3424,11 @@ typedef struct {
* never use it. If -1, we do what the consensus says. */
int OptimisticData;
+ /** If 1, and we are using IOCP, we set the kernel socket SNDBUF and RCVBUF
+ * to 0 to try to save kernel memory and avoid the dread "Out of buffers"
+ * issue. */
+ int UserspaceIOCPBuffers;
+
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */