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