diff --git a/configure.ac b/configure.ac
index d7cef6b6..a7a17943 100755
--- a/configure.ac
+++ b/configure.ac
@@ -66,7 +66,7 @@ dnl Add library for mingw
case $host in
*-mingw*)
CFLAGS="$CFLAGS -mno-ms-bitfields"
- LIBS="$LIBS -ladvapi32 -lgdi32 -lws2_32 -lcrypt32"
+ LIBS="$LIBS -lws2_32"
;;
*-cygwin*)
CFLAGS="$CFLAGS -mno-ms-bitfields"
@@ -93,6 +93,10 @@ case $host in
os_support=linux
AC_MSG_RESULT(Linux)
;;
+ *-mingw*)
+ os_support=mingw
+ AC_MSG_RESULT(MinGW)
+ ;;
*)
AC_MSG_RESULT(transparent proxy does not support for $host)
;;
@@ -161,6 +165,10 @@ AC_CHECK_HEADERS([net/if.h], [], [],
])
case $host in
+ *-mingw*)
+ AC_DEFINE([CONNECT_IN_PROGRESS], [WSAEWOULDBLOCK], [errno for incomplete non-blocking connect(2)])
+ AC_CHECK_HEADERS([winsock2.h ws2tcpip.h], [], [AC_MSG_ERROR([Missing MinGW headers])], [])
+ ;;
*-linux*)
AC_DEFINE([CONNECT_IN_PROGRESS], [EINPROGRESS], [errno for incomplete non-blocking connect(2)])
dnl Checks for netfilter headers
@@ -219,6 +227,12 @@ AC_CHECK_LIB(socket, connect)
dnl Checks for library functions.
AC_CHECK_FUNCS([malloc memset posix_memalign socket])
+AC_ARG_WITH(ev,
+ AS_HELP_STRING([--with-ev=DIR], [use a specific libev library]),
+ [CFLAGS="$CFLAGS -I$withval/include"
+ CPPFLAGS="$CPPFLAGS -I$withval/include"
+ LDFLAGS="$LDFLAGS -L$withval/lib"]
+)
AC_CHECK_HEADERS([ev.h libev/ev.h], [], [])
AC_CHECK_LIB([ev], [ev_loop_destroy], [LIBS="-lev $LIBS"], [AC_MSG_ERROR([Couldn't find libev. Try installing libev-dev@<:@el@:>@.])])
diff --git a/docker/mingw/Dockerfile b/docker/mingw/Dockerfile
new file mode 100644
index 00000000..050fb847
--- /dev/null
+++ b/docker/mingw/Dockerfile
@@ -0,0 +1,42 @@
+#
+# Dockerfile for building MinGW port
+#
+# This file is part of the shadowsocks-libev.
+#
+# shadowsocks-libev is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# shadowsocks-libev is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with shadowsocks-libev; see the file COPYING. If not, see
+# .
+#
+
+FROM debian:testing
+
+ARG REPO=shadowsocks
+ARG REV=master
+
+ADD prepare.sh /
+
+RUN \
+ /bin/bash -c "source /prepare.sh && dk_prepare" && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /build
+
+RUN /bin/bash -c "source /prepare.sh && dk_download"
+
+ADD deps.sh /
+RUN /bin/bash -c "source /deps.sh && dk_deps"
+
+ADD build.sh /
+
+ARG REBUILD=0
+
+RUN /bin/bash -c "source /build.sh && dk_build && dk_package"
diff --git a/docker/mingw/Makefile b/docker/mingw/Makefile
new file mode 100644
index 00000000..92c0122a
--- /dev/null
+++ b/docker/mingw/Makefile
@@ -0,0 +1,38 @@
+#
+# Makefile for building MinGW port
+#
+# This file is part of the shadowsocks-libev.
+#
+# shadowsocks-libev is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# shadowsocks-libev is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with shadowsocks-libev; see the file COPYING. If not, see
+# .
+#
+
+REPO=shadowsocks
+REV=master
+IMAGE=ss-build-mingw
+DIST=ss-libev-win-dist.tar.gz
+
+all: build
+
+build:
+ docker build --force-rm -t $(IMAGE) \
+ --build-arg REV=$(REV) --build-arg REPO=$(REPO) \
+ --build-arg REBUILD="$$(date +%Y-%m-%d-%H-%M-%S)" .
+ docker run --rm --entrypoint cat $(IMAGE) /bin.tgz > $(DIST)
+
+clean:
+ rm -f $(DIST)
+ docker rmi $(IMAGE) || true
+
+.PHONY: all clean build
diff --git a/docker/mingw/build.sh b/docker/mingw/build.sh
new file mode 100644
index 00000000..da89871b
--- /dev/null
+++ b/docker/mingw/build.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+#
+# Functions for building MinGW port in Docker
+#
+# This file is part of the shadowsocks-libev.
+#
+# shadowsocks-libev is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# shadowsocks-libev is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with shadowsocks-libev; see the file COPYING. If not, see
+# .
+#
+
+# Exit on error
+set -e
+
+. /prepare.sh
+
+build_proj() {
+ arch=$1
+ host=$arch-w64-mingw32
+ prefix=${DIST}/$arch
+ dep=${PREFIX}/$arch
+
+ cd "$SRC"
+ git clone ${PROJ_URL} proj
+ cd proj
+ git checkout ${PROJ_REV}
+ git submodule update --init
+ ./autogen.sh
+ ./configure --host=${host} --prefix=${prefix} \
+ --disable-documentation \
+ --with-ev="$dep" \
+ --with-mbedtls="$dep" \
+ --with-sodium="$dep" \
+ --with-pcre="$dep" \
+ --with-cares="$dep" \
+ CFLAGS="-DCARES_STATICLIB -DPCRE_STATIC"
+ make clean
+ make LDFLAGS="-all-static -L${dep}/lib"
+ make install-strip
+}
+
+dk_build() {
+ for arch in i686 x86_64; do
+ build_proj $arch
+ done
+}
+
+dk_package() {
+ rm -rf "$BASE/pack"
+ mkdir -p "$BASE/pack"
+ cd "$BASE/pack"
+ mkdir -p ss-libev-${PROJ_REV}
+ cd ss-libev-${PROJ_REV}
+ for bin in local server tunnel; do
+ cp ${DIST}/i686/bin/ss-${bin}.exe ss-${bin}-x86.exe
+ cp ${DIST}/x86_64/bin/ss-${bin}.exe ss-${bin}-x64.exe
+ done
+ pushd "$SRC/proj"
+ GIT_REV="$(git rev-parse --short HEAD)"
+ popd
+ echo "SHA1 checksum for build $(date +"%y%m%d")-${GIT_REV}" > checksum
+ for f in *.exe; do
+ echo " $f:" >> checksum
+ echo " $(sha1sum $f | cut -d ' ' -f 1)" >> checksum
+ done
+ sed -e 's/$/\r/' checksum > checksum.txt
+ rm -f checksum
+ cd ..
+ tar zcf /bin.tgz ss-libev-${PROJ_REV}
+}
diff --git a/docker/mingw/deps.sh b/docker/mingw/deps.sh
new file mode 100644
index 00000000..0ee99454
--- /dev/null
+++ b/docker/mingw/deps.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+#
+# Functions for building MinGW port in Docker
+#
+# This file is part of the shadowsocks-libev.
+#
+# shadowsocks-libev is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# shadowsocks-libev is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with shadowsocks-libev; see the file COPYING. If not, see
+# .
+#
+
+# Exit on error
+set -e
+
+. /prepare.sh
+
+build_deps() {
+ arch=$1
+ host=$arch-w64-mingw32
+ prefix=${PREFIX}/$arch
+ args="--host=${host} --prefix=${prefix} --disable-shared --enable-static"
+
+ # libev
+ cd "$SRC/$LIBEV_SRC"
+ ./configure $args
+ make clean
+ make install
+
+ # mbedtls
+ cd "$SRC/$MBEDTLS_SRC"
+ make clean
+ make lib WINDOWS=1 CC="${host}-gcc" AR="${host}-ar"
+ make install DESTDIR="${prefix}"
+
+ # sodium
+ cd "$SRC/$SODIUM_SRC"
+ ./configure $args
+ make clean
+ make install
+
+ # pcre
+ cd "$SRC/$PCRE_SRC"
+ ./configure $args \
+ --enable-jit --disable-cpp \
+ --enable-unicode-properties \
+ --enable-pcre16 --enable-pcre32
+ make clean
+ make install
+
+ # c-ares
+ cd "$SRC/$CARES_SRC"
+ ./configure $args
+ make clean
+ make install
+}
+
+dk_deps() {
+ for arch in i686 x86_64; do
+ build_deps $arch
+ done
+}
diff --git a/docker/mingw/prepare.sh b/docker/mingw/prepare.sh
new file mode 100644
index 00000000..d6b9b143
--- /dev/null
+++ b/docker/mingw/prepare.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+#
+# Functions for building MinGW port in Docker
+#
+# This file is part of the shadowsocks-libev.
+#
+# shadowsocks-libev is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# shadowsocks-libev is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with shadowsocks-libev; see the file COPYING. If not, see
+# .
+#
+
+# Exit on error
+set -e
+
+# Build options
+BASE="/build"
+PREFIX="$BASE/stage"
+SRC="$BASE/src"
+DIST="$BASE/dist"
+
+# Project URL
+PROJ_SITE=$REPO # Change REPO in Makefile
+PROJ_REV=$REV # Change REV in Makefile
+PROJ_URL=https://github.com/${PROJ_SITE}/shadowsocks-libev.git
+
+# Libraries from project
+
+## libev for MinGW
+LIBEV_VER=mingw
+LIBEV_SRC=libev-${LIBEV_VER}
+LIBEV_URL=https://github.com/${PROJ_SITE}/libev/archive/${LIBEV_VER}.tar.gz
+
+## mbedTLS for MinGW
+MBEDTLS_VER=mingw
+MBEDTLS_SRC=mbedtls-${MBEDTLS_VER}
+MBEDTLS_URL=https://github.com/${PROJ_SITE}/mbedtls/archive/${MBEDTLS_VER}.tar.gz
+
+# Public libraries
+
+## Sodium
+SODIUM_VER=1.0.16
+SODIUM_SRC=libsodium-${SODIUM_VER}
+SODIUM_URL=https://download.libsodium.org/libsodium/releases/${SODIUM_SRC}.tar.gz
+
+## PCRE
+PCRE_VER=8.41
+PCRE_SRC=pcre-${PCRE_VER}
+PCRE_URL=https://ftp.pcre.org/pub/pcre/${PCRE_SRC}.tar.gz
+
+## c-ares
+CARES_VER=1.14.0
+CARES_SRC=c-ares-${CARES_VER}
+CARES_URL=https://c-ares.haxx.se/download/${CARES_SRC}.tar.gz
+
+# Build steps
+
+dk_prepare() {
+ apt-get update -y
+ apt-get install --no-install-recommends -y \
+ mingw-w64 aria2 git make automake autoconf libtool ca-certificates
+}
+
+dk_download() {
+ mkdir -p "${SRC}"
+ cd "${SRC}"
+ DOWN="aria2c --file-allocation=trunc -s10 -x10 -j10 -c"
+ for pkg in LIBEV SODIUM MBEDTLS PCRE CARES; do
+ src=${pkg}_SRC
+ url=${pkg}_URL
+ out="${!src}".tar.gz
+ $DOWN ${!url} -o "${out}"
+ echo "Unpacking ${out}..."
+ tar zxf ${out}
+ done
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index dcc5fd9b..4efb8905 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -19,7 +19,10 @@ SS_COMMON_LIBS += -lbloom -lcork -lcorkipset
endif
SS_COMMON_LIBS += -lev -lsodium -lm
-bin_PROGRAMS = ss-local ss-tunnel ss-server ss-manager
+bin_PROGRAMS = ss-local ss-tunnel ss-server
+if !BUILD_WINCOMPAT
+bin_PROGRAMS += ss-manager
+endif
sni_src = http.c \
tls.c
@@ -30,41 +33,37 @@ acl_src = rule.c \
crypto_src = crypto.c \
aead.c \
stream.c \
- ppbloom.c \
+ ppbloom.c \
base64.c
plugin_src = plugin.c
-ss_local_SOURCES = utils.c \
- jconf.c \
- json.c \
- udprelay.c \
- cache.c \
- netutils.c \
- local.c \
+common_src = utils.c \
+ jconf.c \
+ json.c \
+ udprelay.c \
+ cache.c \
+ netutils.c
+
+if BUILD_WINCOMPAT
+common_src += winsock.c
+endif
+
+ss_local_SOURCES = local.c \
+ $(common_src) \
$(crypto_src) \
$(plugin_src) \
$(sni_src) \
$(acl_src)
-ss_tunnel_SOURCES = utils.c \
- jconf.c \
- json.c \
- udprelay.c \
- cache.c \
- netutils.c \
- tunnel.c \
+ss_tunnel_SOURCES = tunnel.c \
+ $(common_src) \
$(crypto_src) \
$(plugin_src)
-ss_server_SOURCES = utils.c \
- netutils.c \
- jconf.c \
- json.c \
- udprelay.c \
- cache.c \
- resolv.c \
+ss_server_SOURCES = resolv.c \
server.c \
+ $(common_src) \
$(crypto_src) \
$(plugin_src) \
$(sni_src) \
diff --git a/src/aead.c b/src/aead.c
index b85d0384..56cf30ad 100644
--- a/src/aead.c
+++ b/src/aead.c
@@ -31,11 +31,14 @@
#include
#include
+#ifndef __MINGW32__
#include
+#endif
#include "ppbloom.h"
#include "aead.h"
#include "utils.h"
+#include "winsock.h"
#define NONE (-1)
#define AES128GCM 0
diff --git a/src/crypto.c b/src/crypto.c
index c783a6f2..ba74db79 100644
--- a/src/crypto.c
+++ b/src/crypto.c
@@ -372,7 +372,7 @@ crypto_parse_key(const char *base64, uint8_t *key, size_t key_len)
rand_bytes(key, key_len);
base64_encode(out_key, out_len, key, key_len);
LOGE("Invalid key for your chosen cipher!");
- LOGE("It requires a %zu-byte key encoded with URL-safe Base64", key_len);
+ LOGE("It requires a " SIZE_FMT "-byte key encoded with URL-safe Base64", key_len);
LOGE("Generating a new random key: %s", out_key);
FATAL("Please use the key above or input a valid key");
return key_len;
diff --git a/src/crypto.h b/src/crypto.h
index 69a46945..c86d40f0 100644
--- a/src/crypto.h
+++ b/src/crypto.h
@@ -23,7 +23,9 @@
#ifndef _CRYPTO_H
#define _CRYPTO_H
+#ifndef __MINGW32__
#include
+#endif
#include
#include
#include
diff --git a/src/json.c b/src/json.c
index 6012bad7..2a919009 100644
--- a/src/json.c
+++ b/src/json.c
@@ -35,6 +35,12 @@
#endif
#endif
+#ifdef __MINGW32__
+#define CONV_PTR (uintptr_t)
+#else
+#define CONV_PTR (unsigned long)
+#endif
+
const struct _json_value json_value_none;
#include
@@ -138,7 +144,7 @@ static int new_value (json_state * state,
values_size = sizeof (*value->u.object.values) * value->u.object.length;
if (! (value->u.object.values = (json_object_entry *) json_alloc
- (state, values_size + ((unsigned long) value->u.object.values), 0)) )
+ (state, values_size + (CONV_PTR value->u.object.values), 0)) )
{
return 0;
}
diff --git a/src/local.c b/src/local.c
index b74caac9..47ff491c 100644
--- a/src/local.c
+++ b/src/local.c
@@ -33,12 +33,12 @@
#include
#include
#include
-
+#ifndef __MINGW32__
#include
#include
#include
#include
-
+#endif
#ifdef LIB_ONLY
#include "shadowsocks.h"
#endif
@@ -59,6 +59,7 @@
#include "tls.h"
#include "plugin.h"
#include "local.h"
+#include "winsock.h"
#ifndef LIB_ONLY
#ifdef __APPLE__
@@ -104,8 +105,10 @@ static int udp_fd = 0;
static struct ev_signal sigint_watcher;
static struct ev_signal sigterm_watcher;
+#ifndef __MINGW32__
static struct ev_signal sigchld_watcher;
static struct ev_signal sigusr1_watcher;
+#endif
#ifdef HAVE_SETRLIMIT
#ifndef LIB_ONLY
@@ -135,6 +138,7 @@ static server_t *new_server(int fd);
static struct cork_dllist connections;
+#ifndef __MINGW32__
int
setnonblocking(int fd)
{
@@ -144,6 +148,7 @@ setnonblocking(int fd)
}
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
+#endif
int
create_and_bind(const char *addr, const char *port)
@@ -1213,18 +1218,22 @@ signal_cb(EV_P_ ev_signal *w, int revents)
{
if (revents & EV_SIGNAL) {
switch (w->signum) {
+#ifndef __MINGW32__
case SIGCHLD:
if (!is_plugin_running())
LOGE("plugin service exit unexpectedly");
else
return;
case SIGUSR1:
+#endif
case SIGINT:
case SIGTERM:
ev_signal_stop(EV_DEFAULT, &sigint_watcher);
ev_signal_stop(EV_DEFAULT, &sigterm_watcher);
+#ifndef __MINGW32__
ev_signal_stop(EV_DEFAULT, &sigchld_watcher);
ev_signal_stop(EV_DEFAULT, &sigusr1_watcher);
+#endif
keep_resolving = 0;
ev_unloop(EV_A_ EVUNLOOP_ALL);
}
@@ -1276,7 +1285,9 @@ main(int argc, char **argv)
char *plugin_opts = NULL;
char *plugin_host = NULL;
char *plugin_port = NULL;
+#ifndef __MINGW32__
char tmp_port[8];
+#endif
srand(time(NULL));
@@ -1501,7 +1512,12 @@ main(int argc, char **argv)
exit(EXIT_FAILURE);
}
+#ifdef __MINGW32__
+ winsock_init();
+#endif
+
if (plugin != NULL) {
+#ifndef __MINGW32__
uint16_t port = get_local_port();
if (port == 0) {
FATAL("failed to find a free port");
@@ -1511,6 +1527,9 @@ main(int argc, char **argv)
plugin_port = tmp_port;
LOGI("plugin \"%s\" enabled", plugin);
+#else
+ FATAL("plugins not implemented in MinGW port");
+#endif
}
if (method == NULL) {
@@ -1560,6 +1579,7 @@ main(int argc, char **argv)
LOGI("resolving hostname to IPv6 address first");
}
+#ifndef __MINGW32__
if (plugin != NULL) {
int len = 0;
size_t buf_size = 256 * remote_num;
@@ -1577,10 +1597,13 @@ main(int argc, char **argv)
FATAL("failed to start the plugin");
}
}
+#endif
+#ifndef __MINGW32__
// ignore SIGPIPE
signal(SIGPIPE, SIG_IGN);
signal(SIGABRT, SIG_IGN);
+#endif
// Setup keys
LOGI("initializing ciphers... %s", method);
@@ -1621,8 +1644,10 @@ main(int argc, char **argv)
ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM);
ev_signal_start(EV_DEFAULT, &sigint_watcher);
ev_signal_start(EV_DEFAULT, &sigterm_watcher);
+#ifndef __MINGW32__
ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD);
ev_signal_start(EV_DEFAULT, &sigchld_watcher);
+#endif
if (strcmp(local_addr, ":") > 0)
LOGI("listening at [%s]:%s", local_addr, local_port);
@@ -1674,6 +1699,7 @@ main(int argc, char **argv)
else
#endif
+#ifndef __MINGW32__
// setuid
if (user != NULL && !run_as(user)) {
FATAL("failed to switch user");
@@ -1682,6 +1708,7 @@ main(int argc, char **argv)
if (geteuid() == 0) {
LOGI("running from root user");
}
+#endif
// Init connections
cork_dllist_init(&connections);
@@ -1694,9 +1721,11 @@ main(int argc, char **argv)
}
// Clean up
+#ifndef __MINGW32__
if (plugin != NULL) {
stop_plugin();
}
+#endif
if (mode != UDP_ONLY) {
ev_io_stop(loop, &listen_ctx.io);
@@ -1711,6 +1740,10 @@ main(int argc, char **argv)
free_udprelay();
}
+#ifdef __MINGW32__
+ winsock_cleanup();
+#endif
+
return 0;
}
@@ -1743,6 +1776,10 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat
sprintf(local_port_str, "%d", local_port);
sprintf(remote_port_str, "%d", remote_port);
+#ifdef __MINGW32__
+ winsock_init();
+#endif
+
USE_LOGFILE(log);
if (profile.acl != NULL) {
@@ -1753,18 +1790,22 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat
local_addr = "127.0.0.1";
}
+#ifndef __MINGW32__
// ignore SIGPIPE
signal(SIGPIPE, SIG_IGN);
signal(SIGABRT, SIG_IGN);
+#endif
ev_signal_init(&sigint_watcher, signal_cb, SIGINT);
ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM);
ev_signal_start(EV_DEFAULT, &sigint_watcher);
ev_signal_start(EV_DEFAULT, &sigterm_watcher);
+#ifndef __MINGW32__
ev_signal_init(&sigusr1_watcher, signal_cb, SIGUSR1);
ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD);
ev_signal_start(EV_DEFAULT, &sigusr1_watcher);
ev_signal_start(EV_DEFAULT, &sigchld_watcher);
+#endif
// Setup keys
LOGI("initializing ciphers... %s", method);
@@ -1848,6 +1889,10 @@ _start_ss_local_server(profile_t profile, ss_local_callback callback, void *udat
free_udprelay();
}
+#ifdef __MINGW32__
+ winsock_cleanup();
+#endif
+
return 0;
}
diff --git a/src/netutils.c b/src/netutils.c
index d4254b71..1c119d67 100644
--- a/src/netutils.c
+++ b/src/netutils.c
@@ -28,10 +28,12 @@
#include "config.h"
#endif
+#ifndef __MINGW32__
#include
#include
#include
#include
+#endif
#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__)
#include
diff --git a/src/netutils.h b/src/netutils.h
index e5c98707..1565dcf2 100644
--- a/src/netutils.h
+++ b/src/netutils.h
@@ -23,7 +23,11 @@
#ifndef _NETUTILS_H
#define _NETUTILS_H
+#ifdef __MINGW32__
+#include "winsock.h"
+#else
#include
+#endif
#ifdef HAVE_LINUX_TCP_H
#include
diff --git a/src/plugin.c b/src/plugin.c
index c3d2f5a5..f20c49ed 100644
--- a/src/plugin.c
+++ b/src/plugin.c
@@ -24,6 +24,8 @@
#include "config.h"
#endif
+#ifndef __MINGW32__
+
#include
#include
#include
@@ -299,3 +301,5 @@ is_plugin_running()
}
return 0;
}
+
+#endif
diff --git a/src/resolv.c b/src/resolv.c
index 3c12c7f5..7e7e42b6 100644
--- a/src/resolv.c
+++ b/src/resolv.c
@@ -32,13 +32,13 @@
#include
#include
#include
-
+#ifndef __MINGW32__
#include
#include
#include
#include
#include
-
+#endif
#include
#ifdef HAVE_LIBEV_EV_H
@@ -52,6 +52,13 @@
#include "resolv.h"
#include "utils.h"
#include "netutils.h"
+#include "winsock.h"
+
+#ifdef __MINGW32__
+#define CONV_STATE_CB (ares_sock_state_cb)
+#else
+#define CONV_STATE_CB
+#endif
/*
* Implement DNS resolution interface using libc-ares
@@ -147,7 +154,7 @@ resolv_init(struct ev_loop *loop, char *nameservers, int ipv6first)
memset(&default_ctx, 0, sizeof(struct resolv_ctx));
default_ctx.options.sock_state_cb_data = &default_ctx;
- default_ctx.options.sock_state_cb = resolv_sock_state_cb;
+ default_ctx.options.sock_state_cb = CONV_STATE_CB resolv_sock_state_cb;
default_ctx.options.timeout = 3000;
default_ctx.options.tries = 2;
diff --git a/src/resolv.h b/src/resolv.h
index 993692d4..e211abc6 100644
--- a/src/resolv.h
+++ b/src/resolv.h
@@ -31,7 +31,9 @@
#endif
#include
+#ifndef __MINGW32__
#include
+#endif
struct resolv_query;
diff --git a/src/server.c b/src/server.c
index 3132c4d4..ab3ce1a0 100644
--- a/src/server.c
+++ b/src/server.c
@@ -35,14 +35,14 @@
#include
#include
#include
-
+#ifndef __MINGW32__
#include
#include
#include
#include
#include
#include
-
+#endif
#include
#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__)
@@ -56,6 +56,7 @@
#include "acl.h"
#include "plugin.h"
#include "server.h"
+#include "winsock.h"
#ifndef EAGAIN
#define EAGAIN EWOULDBLOCK
@@ -134,15 +135,20 @@ static char *manager_addr = NULL;
uint64_t tx = 0;
uint64_t rx = 0;
+#ifndef __MINGW32__
ev_timer stat_update_watcher;
+#endif
ev_timer block_list_watcher;
static struct ev_signal sigint_watcher;
static struct ev_signal sigterm_watcher;
+#ifndef __MINGW32__
static struct ev_signal sigchld_watcher;
+#endif
static struct cork_dllist connections;
+#ifndef __MINGW32__
static void
stat_update_cb(EV_P_ ev_timer *watcher, int revents)
{
@@ -218,6 +224,7 @@ stat_update_cb(EV_P_ ev_timer *watcher, int revents)
close(sfd);
}
+#endif
static void
free_connections(struct ev_loop *loop)
@@ -320,6 +327,7 @@ setfastopen(int fd)
return s;
}
+#ifndef __MINGW32__
int
setnonblocking(int fd)
{
@@ -329,6 +337,7 @@ setnonblocking(int fd)
}
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
+#endif
int
create_and_bind(const char *host, const char *port, int mptcp)
@@ -1409,16 +1418,20 @@ signal_cb(EV_P_ ev_signal *w, int revents)
{
if (revents & EV_SIGNAL) {
switch (w->signum) {
+#ifndef __MINGW32__
case SIGCHLD:
if (!is_plugin_running())
LOGE("plugin service exit unexpectedly");
else
return;
+#endif
case SIGINT:
case SIGTERM:
ev_signal_stop(EV_DEFAULT, &sigint_watcher);
ev_signal_stop(EV_DEFAULT, &sigterm_watcher);
+#ifndef __MINGW32__
ev_signal_stop(EV_DEFAULT, &sigchld_watcher);
+#endif
ev_unloop(EV_A_ EVUNLOOP_ALL);
}
}
@@ -1493,7 +1506,9 @@ main(int argc, char **argv)
char *server_port = NULL;
char *plugin_opts = NULL;
char *plugin_port = NULL;
+#ifndef __MINGW32__
char tmp_port[8];
+#endif
int server_num = 0;
const char *server_host[MAX_REMOTE_NUM];
@@ -1717,7 +1732,12 @@ main(int argc, char **argv)
remote_port = server_port;
+#ifdef __MINGW32__
+ winsock_init();
+#endif
+
if (plugin != NULL) {
+#ifndef __MINGW32__
uint16_t port = get_local_port();
if (port == 0) {
FATAL("failed to find a free port");
@@ -1725,6 +1745,9 @@ main(int argc, char **argv)
snprintf(tmp_port, 8, "%d", port);
plugin_port = server_port;
server_port = tmp_port;
+#else
+ FATAL("plugins not implemented in MinGW port");
+#endif
}
if (method == NULL) {
@@ -1782,16 +1805,20 @@ main(int argc, char **argv)
LOGI("enable TCP no-delay");
}
+#ifndef __MINGW32__
// ignore SIGPIPE
signal(SIGPIPE, SIG_IGN);
signal(SIGABRT, SIG_IGN);
+#endif
ev_signal_init(&sigint_watcher, signal_cb, SIGINT);
ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM);
- ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD);
ev_signal_start(EV_DEFAULT, &sigint_watcher);
ev_signal_start(EV_DEFAULT, &sigterm_watcher);
+#ifndef __MINGW32__
+ ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD);
ev_signal_start(EV_DEFAULT, &sigchld_watcher);
+#endif
// setup keys
LOGI("initializing ciphers... %s", method);
@@ -1808,6 +1835,7 @@ main(int argc, char **argv)
if (nameservers != NULL)
LOGI("using nameserver: %s", nameservers);
+#ifndef __MINGW32__
// Start plugin server
if (plugin != NULL) {
int len = 0;
@@ -1827,6 +1855,7 @@ main(int argc, char **argv)
FATAL("failed to start the plugin");
}
}
+#endif
// initialize listen context
listen_ctx_t listen_ctx_list[server_num];
@@ -1888,14 +1917,17 @@ main(int argc, char **argv)
}
}
+#ifndef __MINGW32__
if (manager_addr != NULL) {
ev_timer_init(&stat_update_watcher, stat_update_cb, UPDATE_INTERVAL, UPDATE_INTERVAL);
ev_timer_start(EV_DEFAULT, &stat_update_watcher);
}
+#endif
ev_timer_init(&block_list_watcher, block_list_clear_cb, UPDATE_INTERVAL, UPDATE_INTERVAL);
ev_timer_start(EV_DEFAULT, &block_list_watcher);
+#ifndef __MINGW32__
// setuid
if (user != NULL && !run_as(user)) {
FATAL("failed to switch user");
@@ -1904,6 +1936,7 @@ main(int argc, char **argv)
if (geteuid() == 0) {
LOGI("running from root user");
}
+#endif
// init block list
init_block_list();
@@ -1921,15 +1954,19 @@ main(int argc, char **argv)
// Free block list
free_block_list();
+#ifndef __MINGW32__
if (manager_addr != NULL) {
ev_timer_stop(EV_DEFAULT, &stat_update_watcher);
}
+#endif
ev_timer_stop(EV_DEFAULT, &block_list_watcher);
+#ifndef __MINGW32__
if (plugin != NULL) {
stop_plugin();
}
+#endif
// Clean up
@@ -1953,5 +1990,9 @@ main(int argc, char **argv)
free_udprelay();
}
+#ifdef __MINGW32__
+ winsock_cleanup();
+#endif
+
return 0;
}
diff --git a/src/stream.h b/src/stream.h
index 97742703..2972a67b 100644
--- a/src/stream.h
+++ b/src/stream.h
@@ -23,7 +23,9 @@
#ifndef _STREAM_H
#define _STREAM_H
+#ifndef __MINGW32__
#include
+#endif
#include
#include
#include
diff --git a/src/tls.c b/src/tls.c
index 0b2f4a4d..863ae9ef 100644
--- a/src/tls.c
+++ b/src/tls.c
@@ -36,7 +36,9 @@
#include
#include /* malloc() */
#include /* strncpy() */
+#ifndef __MINGW32__
#include
+#endif
#include "tls.h"
#include "protocol.h"
diff --git a/src/tunnel.c b/src/tunnel.c
index 2f8b3373..fc9be825 100644
--- a/src/tunnel.c
+++ b/src/tunnel.c
@@ -29,13 +29,13 @@
#include
#include
#include
-
+#ifndef __MINGW32__
#include
#include
#include
#include
#include
-
+#endif
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -52,6 +52,7 @@
#include "utils.h"
#include "plugin.h"
#include "tunnel.h"
+#include "winsock.h"
#ifndef EAGAIN
#define EAGAIN EWOULDBLOCK
@@ -98,8 +99,11 @@ static int no_delay = 0;
static struct ev_signal sigint_watcher;
static struct ev_signal sigterm_watcher;
+#ifndef __MINGW32__
static struct ev_signal sigchld_watcher;
+#endif
+#ifndef __MINGW32__
static int
setnonblocking(int fd)
{
@@ -109,6 +113,7 @@ setnonblocking(int fd)
}
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
+#endif
int
create_and_bind(const char *addr, const char *port)
@@ -738,16 +743,20 @@ signal_cb(EV_P_ ev_signal *w, int revents)
{
if (revents & EV_SIGNAL) {
switch (w->signum) {
+#ifndef __MINGW32__
case SIGCHLD:
if (!is_plugin_running())
LOGE("plugin service exit unexpectedly");
else
return;
+#endif
case SIGINT:
case SIGTERM:
ev_signal_stop(EV_DEFAULT, &sigint_watcher);
ev_signal_stop(EV_DEFAULT, &sigterm_watcher);
+#ifndef __MINGW32__
ev_signal_stop(EV_DEFAULT, &sigchld_watcher);
+#endif
keep_resolving = 0;
ev_unloop(EV_A_ EVUNLOOP_ALL);
}
@@ -778,7 +787,9 @@ main(int argc, char **argv)
char *plugin_opts = NULL;
char *plugin_host = NULL;
char *plugin_port = NULL;
+#ifndef __MINGW32__
char tmp_port[8];
+#endif
int remote_num = 0;
ss_addr_t remote_addr[MAX_REMOTE_NUM];
@@ -993,7 +1004,12 @@ main(int argc, char **argv)
exit(EXIT_FAILURE);
}
+#ifdef __MINGW32__
+ winsock_init();
+#endif
+
if (plugin != NULL) {
+#ifndef __MINGW32__
uint16_t port = get_local_port();
if (port == 0) {
FATAL("failed to find a free port");
@@ -1003,6 +1019,9 @@ main(int argc, char **argv)
plugin_port = tmp_port;
LOGI("plugin \"%s\" enabled", plugin);
+#else
+ FATAL("plugins not implemented in MinGW port");
+#endif
}
if (method == NULL) {
@@ -1046,6 +1065,7 @@ main(int argc, char **argv)
FATAL("tunnel port is not defined");
}
+#ifndef __MINGW32__
if (plugin != NULL) {
int len = 0;
size_t buf_size = 256 * remote_num;
@@ -1062,17 +1082,22 @@ main(int argc, char **argv)
FATAL("failed to start the plugin");
}
}
+#endif
+#ifndef __MINGW32__
// ignore SIGPIPE
signal(SIGPIPE, SIG_IGN);
signal(SIGABRT, SIG_IGN);
+#endif
ev_signal_init(&sigint_watcher, signal_cb, SIGINT);
ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM);
- ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD);
ev_signal_start(EV_DEFAULT, &sigint_watcher);
ev_signal_start(EV_DEFAULT, &sigterm_watcher);
+#ifndef __MINGW32__
+ ev_signal_init(&sigchld_watcher, signal_cb, SIGCHLD);
ev_signal_start(EV_DEFAULT, &sigchld_watcher);
+#endif
// Setup keys
LOGI("initializing ciphers... %s", method);
@@ -1149,6 +1174,7 @@ main(int argc, char **argv)
LOGI("TCP relay disabled");
}
+#ifndef __MINGW32__
// setuid
if (user != NULL && !run_as(user)) {
FATAL("failed to switch user");
@@ -1157,12 +1183,19 @@ main(int argc, char **argv)
if (geteuid() == 0) {
LOGI("running from root user");
}
+#endif
ev_run(loop, 0);
+#ifndef __MINGW32__
if (plugin != NULL) {
stop_plugin();
}
+#endif
+
+#ifdef __MINGW32__
+ winsock_cleanup();
+#endif
return 0;
}
diff --git a/src/udprelay.c b/src/udprelay.c
index 2b774d27..24d55fd2 100644
--- a/src/udprelay.c
+++ b/src/udprelay.c
@@ -29,13 +29,13 @@
#include
#include
#include
-
+#ifndef __MINGW32__
#include
#include
#include
#include
#include
-
+#endif
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -52,6 +52,7 @@
#include "netutils.h"
#include "cache.h"
#include "udprelay.h"
+#include "winsock.h"
#ifdef MODULE_REMOTE
#define MAX_UDP_CONN_NUM 512
@@ -105,6 +106,7 @@ static int buf_size = DEFAULT_PACKET_SIZE * 2;
static int server_num = 0;
static server_ctx_t *server_ctx_list[MAX_REMOTE_NUM] = { NULL };
+#ifndef __MINGW32__
static int
setnonblocking(int fd)
{
@@ -114,6 +116,7 @@ setnonblocking(int fd)
}
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
+#endif
#if defined(MODULE_REMOTE) && defined(SO_BROADCAST)
static int
@@ -724,7 +727,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents)
goto CLEAN_UP;
} else if (r > packet_size) {
if (verbose) {
- LOGI("[udp] remote_recv_recvfrom fragmentation, MTU at least be: %zd", r + PACKET_HEADER_SIZE);
+ LOGI("[udp] remote_recv_recvfrom fragmentation, MTU at least be: " SSIZE_FMT, r + PACKET_HEADER_SIZE);
}
}
@@ -798,7 +801,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents)
if (buf->len > packet_size) {
if (verbose) {
- LOGI("[udp] remote_recv_sendto fragmentation, MTU at least be: %zd", buf->len + PACKET_HEADER_SIZE);
+ LOGI("[udp] remote_recv_sendto fragmentation, MTU at least be: " SSIZE_FMT, buf->len + PACKET_HEADER_SIZE);
}
}
@@ -902,7 +905,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
goto CLEAN_UP;
} else if (buf->len > packet_size) {
if (verbose) {
- LOGI("[udp] UDP server_recv_recvmsg fragmentation, MTU at least be: %zd", buf->len + PACKET_HEADER_SIZE);
+ LOGI("[udp] UDP server_recv_recvmsg fragmentation, MTU at least be: " SSIZE_FMT, buf->len + PACKET_HEADER_SIZE);
}
}
@@ -924,7 +927,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
goto CLEAN_UP;
} else if (r > packet_size) {
if (verbose) {
- LOGI("[udp] server_recv_recvfrom fragmentation, MTU at least be: %zd", r + PACKET_HEADER_SIZE);
+ LOGI("[udp] server_recv_recvfrom fragmentation, MTU at least be: " SSIZE_FMT, r + PACKET_HEADER_SIZE);
}
}
@@ -1208,7 +1211,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
if (buf->len > packet_size) {
if (verbose) {
- LOGI("[udp] server_recv_sendto fragmentation, MTU at least be: %zd", buf->len + PACKET_HEADER_SIZE);
+ LOGI("[udp] server_recv_sendto fragmentation, MTU at least be: " SSIZE_FMT, buf->len + PACKET_HEADER_SIZE);
}
}
@@ -1225,7 +1228,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
if (buf->len - addr_header_len > packet_size) {
if (verbose) {
- LOGI("[udp] server_recv_sendto fragmentation, MTU at least be: %zd", buf->len - addr_header_len + PACKET_HEADER_SIZE);
+ LOGI("[udp] server_recv_sendto fragmentation, MTU at least be: " SSIZE_FMT, buf->len - addr_header_len + PACKET_HEADER_SIZE);
}
}
diff --git a/src/utils.c b/src/utils.c
index b9142e7e..9461afd3 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -25,12 +25,14 @@
#endif
#include
-#include
#include
-#include
#include
+#ifndef __MINGW32__
+#include
+#include
#include
#include
+#endif
#include
#include
@@ -55,12 +57,14 @@ FILE *logfile;
int use_syslog = 0;
#endif
+#ifndef __MINGW32__
void
ERROR(const char *s)
{
char *msg = strerror(errno);
LOGE("%s: %s", s, msg);
}
+#endif
int use_tty = 1;
@@ -102,6 +106,7 @@ ss_isnumeric(const char *s)
int
run_as(const char *user)
{
+#ifndef __MINGW32__
if (user[0]) {
/* Convert user to a long integer if it is a non-negative number.
* -1 means it is a user name. */
@@ -195,6 +200,9 @@ run_as(const char *user)
}
#endif
}
+#else
+ LOGE("run_as(): not implemented in MinGW port");
+#endif
return 1;
}
@@ -400,6 +408,7 @@ usage()
void
daemonize(const char *path)
{
+#ifndef __MINGW32__
/* Our process ID and Session ID */
pid_t pid, sid;
@@ -444,6 +453,9 @@ daemonize(const char *path)
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
+#else
+ LOGE("daemonize(): not implemented in MinGW port");
+#endif
}
#ifdef HAVE_SETRLIMIT
@@ -478,6 +490,7 @@ set_nofile(int nofile)
char *
get_default_conf(void)
{
+#ifndef __MINGW32__
static char sysconf[] = "/etc/shadowsocks-libev/config.json";
static char userconf[PATH_MAX] = { 0 };
char *conf_home;
@@ -498,4 +511,7 @@ get_default_conf(void)
// If not, fall back to the system-wide config.
return sysconf;
+#else
+ return "config.json";
+#endif
}
diff --git a/src/utils.h b/src/utils.h
index 09353b67..3b8167f9 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -87,6 +87,35 @@ extern FILE *logfile;
#else // not LIB_ONLY
+#ifdef __MINGW32__
+
+#define USE_TTY()
+#define USE_SYSLOG(ident, _cond)
+#define USE_LOGFILE(ident)
+#define TIME_FORMAT "%Y-%m-%d %H:%M:%S"
+#define LOGI(format, ...) \
+ do { \
+ time_t now = time(NULL); \
+ char timestr[20]; \
+ strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \
+ fprintf(stdout, " %s INFO: " format "\n", timestr, \
+ ## __VA_ARGS__); \
+ } \
+ while (0)
+
+#define LOGE(format, ...) \
+ do { \
+ time_t now = time(NULL); \
+ char timestr[20]; \
+ strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \
+ fprintf(stdout, " %s ERROR: " format "\n", timestr, \
+ ## __VA_ARGS__); \
+ } \
+ while (0)
+
+
+#else // not __MINGW32__
+
#include
extern int use_tty;
extern int use_syslog;
@@ -146,11 +175,30 @@ extern int use_syslog;
} } \
while (0)
+#endif // if __MINGW32__
+
#endif // if LIB_ONLY
#endif // if __ANDROID__
+// Workaround for "%z" in Windows printf
+#ifdef __MINGW32__
+#define SSIZE_FMT "%Id"
+#define SIZE_FMT "%Iu"
+#else
+#define SSIZE_FMT "%zd"
+#define SIZE_FMT "%zu"
+#endif
+
+#ifdef __MINGW32__
+#ifdef ERROR
+#undef ERROR
+#endif
+#define ERROR(s) ss_error(s)
+void ss_error(const char *s); // Implemented in winsock.c
+#else
void ERROR(const char *s);
+#endif
char *ss_itoa(int i);
int ss_isnumeric(const char *s);
diff --git a/src/winsock.c b/src/winsock.c
new file mode 100644
index 00000000..68a3af16
--- /dev/null
+++ b/src/winsock.c
@@ -0,0 +1,75 @@
+/*
+ * winsock.c - Windows socket compatibility layer
+ *
+ * Copyright (C) 2013 - 2018, Max Lv
+ *
+ * This file is part of the shadowsocks-libev.
+ *
+ * shadowsocks-libev is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * shadowsocks-libev is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with shadowsocks-libev; see the file COPYING. If not, see
+ * .
+ */
+
+#ifdef __MINGW32__
+
+#include "winsock.h"
+#include "utils.h"
+
+void
+winsock_init(void)
+{
+ int ret;
+ WSADATA wsa_data;
+ ret = WSAStartup(MAKEWORD(2, 2), &wsa_data);
+ if (ret != 0) {
+ FATAL("Failed to initialize winsock");
+ }
+}
+
+void
+winsock_cleanup(void)
+{
+ WSACleanup();
+}
+
+int
+setnonblocking(SOCKET socket)
+{
+ u_long arg = 1;
+ return ioctlsocket(socket, FIONBIO, &arg);
+}
+
+void
+ss_error(const char *s)
+{
+ char *msg = NULL;
+ DWORD err = WSAGetLastError();
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&msg, 0, NULL);
+ if (msg != NULL) {
+ // Remove trailing newline character
+ ssize_t len = strlen(msg) - 1;
+ if (len >= 0 && msg[len] == '\n') {
+ msg[len] = '\0';
+ }
+ LOGE("%s: [%ld] %s", s, err, msg);
+ LocalFree(msg);
+ }
+}
+
+#endif // __MINGW32__
diff --git a/src/winsock.h b/src/winsock.h
new file mode 100644
index 00000000..a20259bb
--- /dev/null
+++ b/src/winsock.h
@@ -0,0 +1,86 @@
+/*
+ * winsock.h - Windows socket compatibility layer
+ *
+ * Copyright (C) 2013 - 2018, Max Lv
+ *
+ * This file is part of the shadowsocks-libev.
+ *
+ * shadowsocks-libev is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * shadowsocks-libev is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with shadowsocks-libev; see the file COPYING. If not, see
+ * .
+ */
+
+#ifndef _WINSOCK_H
+#define _WINSOCK_H
+
+#ifdef __MINGW32__
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600
+#undef _WIN32_WINNT
+#endif
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#endif
+
+#include
+#include
+#include
+
+// Override error number
+#ifdef errno
+#undef errno
+#endif
+#define errno WSAGetLastError()
+
+#ifdef EWOULDBLOCK
+#undef EWOULDBLOCK
+#endif
+#define EWOULDBLOCK WSAEWOULDBLOCK
+
+#ifdef CONNECT_IN_PROGRESS
+#undef CONNECT_IN_PROGRESS
+#endif
+#define CONNECT_IN_PROGRESS WSAEWOULDBLOCK
+
+// Override close function
+#define close(fd) closesocket(fd)
+
+// Override MinGW functions
+#define setsockopt(a,b,c,d,e) setsockopt(a,b,c,(const char *)(d),e)
+#define inet_ntop(a,b,c,d) inet_ntop(a,(void *)(b),c,d)
+
+// Override Windows built-in functions
+#ifdef ERROR
+#undef ERROR
+#endif
+#define ERROR(s) ss_error(s)
+#ifndef _UTILS_H
+void ss_error(const char *s);
+#endif
+
+// Missing unistd.h functions
+#define sleep(x) Sleep((x) * 1000)
+
+// Winsock compatibility functions
+int setnonblocking(SOCKET socket);
+void winsock_init(void);
+void winsock_cleanup(void);
+
+#endif // __MINGW32__
+
+#endif // _WINSOCK_H