From 1549187fc5e63928efd0953930d199f62608443d Mon Sep 17 00:00:00 2001 From: CN_SZTL <22235437+1715173329@users.noreply.github.com> Date: Tue, 11 May 2021 21:28:59 +0800 Subject: [PATCH] shadowsocksr-libev: remove package (#6814) This is included by somebody else. Signed-off-by: Tianling Shen --- package/lean/shadowsocksr-libev/Makefile | 70 - .../0001-Add-ss-server-and-ss-check.patch | 17186 ---------------- ...Revert-verify_simple-and-auth_simple.patch | 22 - .../patches/0003-Refine-Usage.patch | 42 - .../990-not-defined-errors-on-gcc10.patch | 24 - ...-Fix-Werror-sizeof-pointer-memaccess.patch | 11 - 6 files changed, 17355 deletions(-) delete mode 100644 package/lean/shadowsocksr-libev/Makefile delete mode 100644 package/lean/shadowsocksr-libev/patches/0001-Add-ss-server-and-ss-check.patch delete mode 100644 package/lean/shadowsocksr-libev/patches/0002-Revert-verify_simple-and-auth_simple.patch delete mode 100644 package/lean/shadowsocksr-libev/patches/0003-Refine-Usage.patch delete mode 100644 package/lean/shadowsocksr-libev/patches/990-not-defined-errors-on-gcc10.patch delete mode 100644 package/lean/shadowsocksr-libev/patches/999-Fix-Werror-sizeof-pointer-memaccess.patch diff --git a/package/lean/shadowsocksr-libev/Makefile b/package/lean/shadowsocksr-libev/Makefile deleted file mode 100644 index 501638e03..000000000 --- a/package/lean/shadowsocksr-libev/Makefile +++ /dev/null @@ -1,70 +0,0 @@ -include $(TOPDIR)/rules.mk - -PKG_NAME:=shadowsocksr-libev -PKG_VERSION:=2.5.6 -PKG_RELEASE:=6 - -PKG_SOURCE_PROTO:=git -PKG_SOURCE_URL:=https://github.com/shadowsocksrr/shadowsocksr-libev -PKG_SOURCE_VERSION:=d63ff863800a5645aca4309d5dd5962bd1e95543 -PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION) -PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.xz - -PKG_LICENSE:=GPLv3 -PKG_LICENSE_FILES:=LICENSE - -PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)/$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION) - -PKG_INSTALL:=1 -PKG_FIXUP:=autoreconf -PKG_USE_MIPS16:=0 -PKG_BUILD_PARALLEL:=1 - -include $(INCLUDE_DIR)/package.mk - -define Package/shadowsocksr-libev - SECTION:=net - CATEGORY:=Network - SUBMENU:=Web Servers/Proxies - TITLE:=Lightweight Secured Socks5 Proxy - URL:=https://github.com/shadowsocksrr/shadowsocksr-libev - DEPENDS:=+libopenssl +libpthread +libpcre +zlib -endef - -Package/shadowsocksr-libev-server = $(Package/shadowsocksr-libev) -Package/shadowsocksr-libev-alt = $(Package/shadowsocksr-libev) -Package/shadowsocksr-libev-ssr-local = $(Package/shadowsocksr-libev) - -CONFIGURE_ARGS += --disable-documentation --disable-ssp --disable-assert -TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed - -define Package/shadowsocksr-libev/install - $(INSTALL_DIR) $(1)/usr/bin - $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/ss-redir $(1)/usr/bin/ssr-redir - $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/ss-local $(1)/usr/bin/ssr-local - $(LN) ssr-local $(1)/usr/bin/ssr-tunnel - $(INSTALL_BIN) $(PKG_BUILD_DIR)/server/ss-check $(1)/usr/bin/ssr-check - $(INSTALL_BIN) $(PKG_BUILD_DIR)/server/ss-server $(1)/usr/bin/ssr-server -endef - -define Package/shadowsocksr-libev-alt/install - $(INSTALL_DIR) $(1)/usr/bin - $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/ss-redir $(1)/usr/bin/ssr-redir - $(INSTALL_BIN) $(PKG_BUILD_DIR)/server/ss-check $(1)/usr/bin/ssr-check -endef - -define Package/shadowsocksr-libev-server/install - $(INSTALL_DIR) $(1)/usr/bin - $(INSTALL_BIN) $(PKG_BUILD_DIR)/server/ss-server $(1)/usr/bin/ssr-server -endef - -define Package/shadowsocksr-libev-ssr-local/install - $(INSTALL_DIR) $(1)/usr/bin - $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/ss-local $(1)/usr/bin/ssr-local - $(LN) ssr-local $(1)/usr/bin/ssr-tunnel -endef - -$(eval $(call BuildPackage,shadowsocksr-libev)) -$(eval $(call BuildPackage,shadowsocksr-libev-alt)) -$(eval $(call BuildPackage,shadowsocksr-libev-server)) -$(eval $(call BuildPackage,shadowsocksr-libev-ssr-local)) diff --git a/package/lean/shadowsocksr-libev/patches/0001-Add-ss-server-and-ss-check.patch b/package/lean/shadowsocksr-libev/patches/0001-Add-ss-server-and-ss-check.patch deleted file mode 100644 index 50a4e6a94..000000000 --- a/package/lean/shadowsocksr-libev/patches/0001-Add-ss-server-and-ss-check.patch +++ /dev/null @@ -1,17186 +0,0 @@ -diff --git a/.gitignore b/.gitignore -index 4eab18e..64b8d9f 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -2,6 +2,7 @@ build/ - .deps/ - /Makefile - src/Makefile -+server/Makefile - libev/Makefile - libudns/Makefile - libcork/Makefile -diff --git a/Makefile.am b/Makefile.am -index 690af43..75e158e 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -1,7 +1,7 @@ - if USE_SYSTEM_SHARED_LIB --SUBDIRS = libcork libipset src -+SUBDIRS = libcork libipset src server - else --SUBDIRS = libsodium libcork libipset libudns libev src -+SUBDIRS = libsodium libcork libipset libudns libev src server - endif - - if ENABLE_DOCUMENTATION -diff --git a/Makefile.in b/Makefile.in -index 4cb3deb..e210bc0 100644 ---- a/Makefile.in -+++ b/Makefile.in -@@ -195,7 +195,7 @@ am__define_uniq_tagged_files = \ - ETAGS = etags - CTAGS = ctags - CSCOPE = cscope --DIST_SUBDIRS = libsodium libcork libipset libudns libev src doc -+DIST_SUBDIRS = libsodium libcork libipset libudns libev src server doc - am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ - $(srcdir)/shadowsocks-libev.pc.in $(top_srcdir)/auto/ar-lib \ - $(top_srcdir)/auto/compile $(top_srcdir)/auto/config.guess \ -@@ -377,8 +377,9 @@ top_build_prefix = @top_build_prefix@ - top_builddir = @top_builddir@ - top_srcdir = @top_srcdir@ - @USE_SYSTEM_SHARED_LIB_FALSE@SUBDIRS = libsodium libcork libipset \ --@USE_SYSTEM_SHARED_LIB_FALSE@ libudns libev src $(am__append_1) --@USE_SYSTEM_SHARED_LIB_TRUE@SUBDIRS = libcork libipset src \ -+@USE_SYSTEM_SHARED_LIB_FALSE@ libudns libev src server \ -+@USE_SYSTEM_SHARED_LIB_FALSE@ $(am__append_1) -+@USE_SYSTEM_SHARED_LIB_TRUE@SUBDIRS = libcork libipset src server \ - @USE_SYSTEM_SHARED_LIB_TRUE@ $(am__append_1) - ACLOCAL_AMFLAGS = -I m4 - pkgconfiglibdir = $(libdir)/pkgconfig -diff --git a/configure b/configure -index 7d854c4..01d66ab 100755 ---- a/configure -+++ b/configure -@@ -649,7 +649,6 @@ PTHREAD_CC - ax_pthread_config - INET_NTOP_LIB - MV --RM - GZIP - XMLTO - ASCIIDOC -@@ -757,6 +756,7 @@ infodir - docdir - oldincludedir - includedir -+runstatedir - localstatedir - sharedstatedir - sysconfdir -@@ -857,6 +857,7 @@ datadir='${datarootdir}' - sysconfdir='${prefix}/etc' - sharedstatedir='${prefix}/com' - localstatedir='${prefix}/var' -+runstatedir='${localstatedir}/run' - includedir='${prefix}/include' - oldincludedir='/usr/include' - docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' -@@ -1109,6 +1110,15 @@ do - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - -+ -runstatedir | --runstatedir | --runstatedi | --runstated \ -+ | --runstate | --runstat | --runsta | --runst | --runs \ -+ | --run | --ru | --r) -+ ac_prev=runstatedir ;; -+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ -+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ -+ | --run=* | --ru=* | --r=*) -+ runstatedir=$ac_optarg ;; -+ - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ -@@ -1246,7 +1256,7 @@ fi - for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ - datadir sysconfdir sharedstatedir localstatedir includedir \ - oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ -- libdir localedir mandir -+ libdir localedir mandir runstatedir - do - eval ac_val=\$$ac_var - # Remove trailing slashes. -@@ -1399,6 +1409,7 @@ Fine tuning of the installation directories: - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] -+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] -@@ -2472,8 +2483,8 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. - - - --# expand $ac_aux_dir to an absolute path --am_aux_dir=`cd $ac_aux_dir && pwd` -+# Expand $ac_aux_dir to an absolute path. -+am_aux_dir=`cd "$ac_aux_dir" && pwd` - - ac_ext=c - ac_cpp='$CPP $CPPFLAGS' -@@ -3783,7 +3794,7 @@ $as_echo "$ac_cv_safe_to_define___extensions__" >&6; } - - - --am__api_version='1.14' -+am__api_version='1.15' - - # Find a good install program. We prefer a C program (faster), - # so one script is as good as another. But avoid the broken or -@@ -3972,7 +3983,7 @@ else - $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} - fi - --if test x"${install_sh}" != xset; then -+if test x"${install_sh+set}" != xset; then - case $am_aux_dir in - *\ * | *\ *) - install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; -@@ -4363,8 +4374,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} - # - mkdir_p='$(MKDIR_P)' - --# We need awk for the "check" target. The system "awk" is bad on --# some platforms. -+# We need awk for the "check" target (and possibly the TAP driver). The -+# system "awk" is bad on some platforms. - # Always define AMTAR for backward compatibility. Yes, it's still used - # in the wild :-( We should find a proper way to deprecate it ... - AMTAR='$${TAR-tar}' -@@ -4549,6 +4560,7 @@ END - as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 - fi - fi -+ - if test -n "$ac_tool_prefix"; then - for ac_prog in ar lib "link -lib" - do -@@ -12494,47 +12506,6 @@ $as_echo "no" >&6; } - fi - - -- # Extract the first word of "rm", so it can be a program name with args. --set dummy rm; ac_word=$2 --{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 --$as_echo_n "checking for $ac_word... " >&6; } --if ${ac_cv_path_RM+:} false; then : -- $as_echo_n "(cached) " >&6 --else -- case $RM in -- [\\/]* | ?:[\\/]*) -- ac_cv_path_RM="$RM" # Let the user override the test with a path. -- ;; -- *) -- as_save_IFS=$IFS; IFS=$PATH_SEPARATOR --for as_dir in $PATH --do -- IFS=$as_save_IFS -- test -z "$as_dir" && as_dir=. -- for ac_exec_ext in '' $ac_executable_extensions; do -- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then -- ac_cv_path_RM="$as_dir/$ac_word$ac_exec_ext" -- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 -- break 2 -- fi --done -- done --IFS=$as_save_IFS -- -- test -z "$ac_cv_path_RM" && ac_cv_path_RM="rm" -- ;; --esac --fi --RM=$ac_cv_path_RM --if test -n "$RM"; then -- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RM" >&5 --$as_echo "$RM" >&6; } --else -- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 --$as_echo "no" >&6; } --fi -- -- - # Extract the first word of "mv", so it can be a program name with args. - set dummy mv; ac_word=$2 - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -@@ -16204,15 +16175,162 @@ $as_echo "#define HAVE_IPv6 1" >>confdefs.h - - - if test -z "$USE_SYSTEM_SHARED_LIB_TRUE"; then : -- else -+ -+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sodium_init in -lsodium" >&5 -+$as_echo_n "checking for sodium_init in -lsodium... " >&6; } -+if ${ac_cv_lib_sodium_sodium_init+:} false; then : -+ $as_echo_n "(cached) " >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-lsodium $LIBS" -+cat confdefs.h - <<_ACEOF >conftest.$ac_ext -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char sodium_init (); -+int -+main () -+{ -+return sodium_init (); -+ ; -+ return 0; -+} -+_ACEOF -+if ac_fn_c_try_link "$LINENO"; then : -+ ac_cv_lib_sodium_sodium_init=yes -+else -+ ac_cv_lib_sodium_sodium_init=no -+fi -+rm -f core conftest.err conftest.$ac_objext \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sodium_sodium_init" >&5 -+$as_echo "$ac_cv_lib_sodium_sodium_init" >&6; } -+if test "x$ac_cv_lib_sodium_sodium_init" = xyes; then : -+ cat >>confdefs.h <<_ACEOF -+#define HAVE_LIBSODIUM 1 -+_ACEOF -+ -+ LIBS="-lsodium $LIBS" -+ -+else -+ -+ as_fn_error $? "Couldn't find libsodium. Try installing libsodium-dev[el]." "$LINENO" 5 -+ -+fi -+ -+ -+else - subdirs="$subdirs libsodium" - - fi - --ac_config_files="$ac_config_files shadowsocks-libev.pc Makefile libcork/Makefile libipset/Makefile src/Makefile" -+ac_config_files="$ac_config_files shadowsocks-libev.pc Makefile libcork/Makefile libipset/Makefile src/Makefile server/Makefile" - - if test -z "$USE_SYSTEM_SHARED_LIB_TRUE"; then : -- else -+ -+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dns_dnlen in -ludns" >&5 -+$as_echo_n "checking for dns_dnlen in -ludns... " >&6; } -+if ${ac_cv_lib_udns_dns_dnlen+:} false; then : -+ $as_echo_n "(cached) " >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-ludns $LIBS" -+cat confdefs.h - <<_ACEOF >conftest.$ac_ext -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char dns_dnlen (); -+int -+main () -+{ -+return dns_dnlen (); -+ ; -+ return 0; -+} -+_ACEOF -+if ac_fn_c_try_link "$LINENO"; then : -+ ac_cv_lib_udns_dns_dnlen=yes -+else -+ ac_cv_lib_udns_dns_dnlen=no -+fi -+rm -f core conftest.err conftest.$ac_objext \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_udns_dns_dnlen" >&5 -+$as_echo "$ac_cv_lib_udns_dns_dnlen" >&6; } -+if test "x$ac_cv_lib_udns_dns_dnlen" = xyes; then : -+ cat >>confdefs.h <<_ACEOF -+#define HAVE_LIBUDNS 1 -+_ACEOF -+ -+ LIBS="-ludns $LIBS" -+ -+else -+ as_fn_error $? "Couldn't find libudns. Try installing libudns-dev or udns-devel." "$LINENO" 5 -+fi -+ -+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ev_loop_destroy in -lev" >&5 -+$as_echo_n "checking for ev_loop_destroy in -lev... " >&6; } -+if ${ac_cv_lib_ev_ev_loop_destroy+:} false; then : -+ $as_echo_n "(cached) " >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-lev $LIBS" -+cat confdefs.h - <<_ACEOF >conftest.$ac_ext -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char ev_loop_destroy (); -+int -+main () -+{ -+return ev_loop_destroy (); -+ ; -+ return 0; -+} -+_ACEOF -+if ac_fn_c_try_link "$LINENO"; then : -+ ac_cv_lib_ev_ev_loop_destroy=yes -+else -+ ac_cv_lib_ev_ev_loop_destroy=no -+fi -+rm -f core conftest.err conftest.$ac_objext \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ev_ev_loop_destroy" >&5 -+$as_echo "$ac_cv_lib_ev_ev_loop_destroy" >&6; } -+if test "x$ac_cv_lib_ev_ev_loop_destroy" = xyes; then : -+ cat >>confdefs.h <<_ACEOF -+#define HAVE_LIBEV 1 -+_ACEOF -+ -+ LIBS="-lev $LIBS" -+ -+else -+ as_fn_error $? "Couldn't find libev. Try installing libev-dev[el]." "$LINENO" 5 -+fi -+ -+ -+else - ac_config_files="$ac_config_files libudns/Makefile libev/Makefile" - - fi -@@ -17258,6 +17376,7 @@ do - "libcork/Makefile") CONFIG_FILES="$CONFIG_FILES libcork/Makefile" ;; - "libipset/Makefile") CONFIG_FILES="$CONFIG_FILES libipset/Makefile" ;; - "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; -+ "server/Makefile") CONFIG_FILES="$CONFIG_FILES server/Makefile" ;; - "libudns/Makefile") CONFIG_FILES="$CONFIG_FILES libudns/Makefile" ;; - "libev/Makefile") CONFIG_FILES="$CONFIG_FILES libev/Makefile" ;; - "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; -@@ -17958,8 +18077,8 @@ $as_echo X"$file" | - fi - - cfgfile="${ofile}T" -- trap "$RM -f \"$cfgfile\"; exit 1" 1 2 15 -- $RM -f "$cfgfile" -+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15 -+ $RM "$cfgfile" - - cat <<_LT_EOF >> "$cfgfile" - #! $SHELL -diff --git a/configure.ac b/configure.ac -index 6586f2b..f9c51ab 100755 ---- a/configure.ac -+++ b/configure.ac -@@ -315,7 +315,8 @@ AC_CONFIG_FILES([ shadowsocks-libev.pc - Makefile - libcork/Makefile - libipset/Makefile -- src/Makefile]) -+ src/Makefile -+ server/Makefile]) - AM_COND_IF([USE_SYSTEM_SHARED_LIB],[ - AC_CHECK_LIB([udns], [dns_dnlen], ,[AC_MSG_ERROR([Couldn't find libudns. Try installing libudns-dev or udns-devel.])]) - AC_CHECK_LIB([ev], [ev_loop_destroy], ,[AC_MSG_ERROR([Couldn't find libev. Try installing libev-dev@<:@el@:>@.])]) -diff --git a/server/Makefile.am b/server/Makefile.am -new file mode 100644 -index 0000000..3ae8bc2 ---- /dev/null -+++ b/server/Makefile.am -@@ -0,0 +1,55 @@ -+VERSION_INFO = 2:0:0 -+ -+AM_CFLAGS = -g -O2 -Wall -Werror -Wno-deprecated-declarations -fno-strict-aliasing -std=gnu99 -D_GNU_SOURCE -+AM_CFLAGS += $(PTHREAD_CFLAGS) -+if !USE_SYSTEM_SHARED_LIB -+AM_CFLAGS += -I$(top_srcdir)/libev -+AM_CFLAGS += -I$(top_srcdir)/libudns -+AM_CFLAGS += -I$(top_srcdir)/libsodium/src/libsodium/include -+endif -+AM_CFLAGS += -I$(top_srcdir)/libipset/include -+AM_CFLAGS += -I$(top_srcdir)/libcork/include -+AM_CFLAGS += $(LIBPCRE_CFLAGS) -+ -+SS_COMMON_LIBS = $(top_builddir)/libipset/libipset.la \ -+ $(top_builddir)/libcork/libcork.la \ -+ $(INET_NTOP_LIB) $(LIBPCRE_LIBS) -+if USE_SYSTEM_SHARED_LIB -+SS_COMMON_LIBS += -lev -lsodium -lm -+else -+SS_COMMON_LIBS += $(top_builddir)/libev/libev.la \ -+ $(top_builddir)/libsodium/src/libsodium/libsodium.la -+endif -+ -+bin_PROGRAMS = ss-server ss-check -+ -+sni_src = http.c \ -+ tls.c \ -+ rule.c -+ -+ss_check_SOURCES = check.c -+ -+ss_server_SOURCES = utils.c \ -+ netutils.c \ -+ jconf.c \ -+ json.c \ -+ encrypt.c \ -+ udprelay.c \ -+ cache.c \ -+ acl.c \ -+ resolv.c \ -+ server.c \ -+ $(sni_src) -+ -+ -+ss_check_LDADD = $(SS_COMMON_LIBS) -+ss_server_LDADD = $(SS_COMMON_LIBS) -+ -+if USE_SYSTEM_SHARED_LIB -+ss_server_LDADD += -ludns -+else -+ss_server_LDADD += $(top_builddir)/libudns/libudns.la -+endif -+ -+ss_check_CFLAGS = $(AM_CFLAGS) -+ss_server_CFLAGS = $(AM_CFLAGS) -DMODULE_REMOTE -diff --git a/server/Makefile.in b/server/Makefile.in -new file mode 100644 -index 0000000..3bfa53e ---- /dev/null -+++ b/server/Makefile.in -@@ -0,0 +1,919 @@ -+# Makefile.in generated by automake 1.15 from Makefile.am. -+# @configure_input@ -+ -+# Copyright (C) 1994-2014 Free Software Foundation, Inc. -+ -+# This Makefile.in is free software; the Free Software Foundation -+# gives unlimited permission to copy and/or distribute it, -+# with or without modifications, as long as this notice is preserved. -+ -+# This program is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -+# PARTICULAR PURPOSE. -+ -+@SET_MAKE@ -+ -+VPATH = @srcdir@ -+am__is_gnu_make = { \ -+ if test -z '$(MAKELEVEL)'; then \ -+ false; \ -+ elif test -n '$(MAKE_HOST)'; then \ -+ true; \ -+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ -+ true; \ -+ else \ -+ false; \ -+ fi; \ -+} -+am__make_running_with_option = \ -+ case $${target_option-} in \ -+ ?) ;; \ -+ *) echo "am__make_running_with_option: internal error: invalid" \ -+ "target option '$${target_option-}' specified" >&2; \ -+ exit 1;; \ -+ esac; \ -+ has_opt=no; \ -+ sane_makeflags=$$MAKEFLAGS; \ -+ if $(am__is_gnu_make); then \ -+ sane_makeflags=$$MFLAGS; \ -+ else \ -+ case $$MAKEFLAGS in \ -+ *\\[\ \ ]*) \ -+ bs=\\; \ -+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ -+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ -+ esac; \ -+ fi; \ -+ skip_next=no; \ -+ strip_trailopt () \ -+ { \ -+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ -+ }; \ -+ for flg in $$sane_makeflags; do \ -+ test $$skip_next = yes && { skip_next=no; continue; }; \ -+ case $$flg in \ -+ *=*|--*) continue;; \ -+ -*I) strip_trailopt 'I'; skip_next=yes;; \ -+ -*I?*) strip_trailopt 'I';; \ -+ -*O) strip_trailopt 'O'; skip_next=yes;; \ -+ -*O?*) strip_trailopt 'O';; \ -+ -*l) strip_trailopt 'l'; skip_next=yes;; \ -+ -*l?*) strip_trailopt 'l';; \ -+ -[dEDm]) skip_next=yes;; \ -+ -[JT]) skip_next=yes;; \ -+ esac; \ -+ case $$flg in \ -+ *$$target_option*) has_opt=yes; break;; \ -+ esac; \ -+ done; \ -+ test $$has_opt = yes -+am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -+am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -+pkgdatadir = $(datadir)/@PACKAGE@ -+pkgincludedir = $(includedir)/@PACKAGE@ -+pkglibdir = $(libdir)/@PACKAGE@ -+pkglibexecdir = $(libexecdir)/@PACKAGE@ -+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -+install_sh_DATA = $(install_sh) -c -m 644 -+install_sh_PROGRAM = $(install_sh) -c -+install_sh_SCRIPT = $(install_sh) -c -+INSTALL_HEADER = $(INSTALL_DATA) -+transform = $(program_transform_name) -+NORMAL_INSTALL = : -+PRE_INSTALL = : -+POST_INSTALL = : -+NORMAL_UNINSTALL = : -+PRE_UNINSTALL = : -+POST_UNINSTALL = : -+build_triplet = @build@ -+host_triplet = @host@ -+@USE_SYSTEM_SHARED_LIB_FALSE@am__append_1 = -I$(top_srcdir)/libev \ -+@USE_SYSTEM_SHARED_LIB_FALSE@ -I$(top_srcdir)/libudns \ -+@USE_SYSTEM_SHARED_LIB_FALSE@ -I$(top_srcdir)/libsodium/src/libsodium/include -+@USE_SYSTEM_SHARED_LIB_TRUE@am__append_2 = -lev -lsodium -lm -+@USE_SYSTEM_SHARED_LIB_FALSE@am__append_3 = $(top_builddir)/libev/libev.la \ -+@USE_SYSTEM_SHARED_LIB_FALSE@ $(top_builddir)/libsodium/src/libsodium/libsodium.la -+ -+bin_PROGRAMS = ss-server$(EXEEXT) ss-check$(EXEEXT) -+@USE_SYSTEM_SHARED_LIB_TRUE@am__append_4 = -ludns -+@USE_SYSTEM_SHARED_LIB_FALSE@am__append_5 = $(top_builddir)/libudns/libudns.la -+subdir = server -+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ -+ $(top_srcdir)/m4/ax_tls.m4 $(top_srcdir)/m4/inet_ntop.m4 \ -+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ -+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ -+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/mbedtls.m4 \ -+ $(top_srcdir)/m4/openssl.m4 $(top_srcdir)/m4/pcre.m4 \ -+ $(top_srcdir)/m4/polarssl.m4 \ -+ $(top_srcdir)/m4/stack-protector.m4 $(top_srcdir)/m4/zlib.m4 \ -+ $(top_srcdir)/libev/libev.m4 $(top_srcdir)/configure.ac -+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ -+ $(ACLOCAL_M4) -+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) -+mkinstalldirs = $(install_sh) -d -+CONFIG_HEADER = $(top_builddir)/config.h -+CONFIG_CLEAN_FILES = -+CONFIG_CLEAN_VPATH_FILES = -+am__installdirs = "$(DESTDIR)$(bindir)" -+PROGRAMS = $(bin_PROGRAMS) -+am_ss_check_OBJECTS = ss_check-check.$(OBJEXT) -+ss_check_OBJECTS = $(am_ss_check_OBJECTS) -+am__DEPENDENCIES_1 = -+am__DEPENDENCIES_2 = $(top_builddir)/libipset/libipset.la \ -+ $(top_builddir)/libcork/libcork.la $(am__DEPENDENCIES_1) \ -+ $(am__DEPENDENCIES_1) $(am__append_3) -+ss_check_DEPENDENCIES = $(am__DEPENDENCIES_2) -+AM_V_lt = $(am__v_lt_@AM_V@) -+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -+am__v_lt_0 = --silent -+am__v_lt_1 = -+ss_check_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ -+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ss_check_CFLAGS) \ -+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -+am__objects_1 = ss_server-http.$(OBJEXT) ss_server-tls.$(OBJEXT) \ -+ ss_server-rule.$(OBJEXT) -+am_ss_server_OBJECTS = ss_server-utils.$(OBJEXT) \ -+ ss_server-netutils.$(OBJEXT) ss_server-jconf.$(OBJEXT) \ -+ ss_server-json.$(OBJEXT) ss_server-encrypt.$(OBJEXT) \ -+ ss_server-udprelay.$(OBJEXT) ss_server-cache.$(OBJEXT) \ -+ ss_server-acl.$(OBJEXT) ss_server-resolv.$(OBJEXT) \ -+ ss_server-server.$(OBJEXT) $(am__objects_1) -+ss_server_OBJECTS = $(am_ss_server_OBJECTS) -+ss_server_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ -+ $(am__append_5) -+ss_server_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ -+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ss_server_CFLAGS) \ -+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -+AM_V_P = $(am__v_P_@AM_V@) -+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -+am__v_P_0 = false -+am__v_P_1 = : -+AM_V_GEN = $(am__v_GEN_@AM_V@) -+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -+am__v_GEN_0 = @echo " GEN " $@; -+am__v_GEN_1 = -+AM_V_at = $(am__v_at_@AM_V@) -+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -+am__v_at_0 = @ -+am__v_at_1 = -+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -+depcomp = $(SHELL) $(top_srcdir)/auto/depcomp -+am__depfiles_maybe = depfiles -+am__mv = mv -f -+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ -+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ -+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ -+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ -+ $(AM_CFLAGS) $(CFLAGS) -+AM_V_CC = $(am__v_CC_@AM_V@) -+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -+am__v_CC_0 = @echo " CC " $@; -+am__v_CC_1 = -+CCLD = $(CC) -+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ -+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ -+ $(AM_LDFLAGS) $(LDFLAGS) -o $@ -+AM_V_CCLD = $(am__v_CCLD_@AM_V@) -+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -+am__v_CCLD_0 = @echo " CCLD " $@; -+am__v_CCLD_1 = -+SOURCES = $(ss_check_SOURCES) $(ss_server_SOURCES) -+DIST_SOURCES = $(ss_check_SOURCES) $(ss_server_SOURCES) -+am__can_run_installinfo = \ -+ case $$AM_UPDATE_INFO_DIR in \ -+ n|no|NO) false;; \ -+ *) (install-info --version) >/dev/null 2>&1;; \ -+ esac -+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -+# Read a list of newline-separated strings from the standard input, -+# and print each of them once, without duplicates. Input order is -+# *not* preserved. -+am__uniquify_input = $(AWK) '\ -+ BEGIN { nonempty = 0; } \ -+ { items[$$0] = 1; nonempty = 1; } \ -+ END { if (nonempty) { for (i in items) print i; }; } \ -+' -+# Make sure the list of sources is unique. This is necessary because, -+# e.g., the same source file might be shared among _SOURCES variables -+# for different programs/libraries. -+am__define_uniq_tagged_files = \ -+ list='$(am__tagged_files)'; \ -+ unique=`for i in $$list; do \ -+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ -+ done | $(am__uniquify_input)` -+ETAGS = etags -+CTAGS = ctags -+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/auto/depcomp -+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -+ACLOCAL = @ACLOCAL@ -+AMTAR = @AMTAR@ -+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -+AR = @AR@ -+ASCIIDOC = @ASCIIDOC@ -+AUTOCONF = @AUTOCONF@ -+AUTOHEADER = @AUTOHEADER@ -+AUTOMAKE = @AUTOMAKE@ -+AWK = @AWK@ -+CC = @CC@ -+CCDEPMODE = @CCDEPMODE@ -+CFLAGS = @CFLAGS@ -+CPP = @CPP@ -+CPPFLAGS = @CPPFLAGS@ -+CYGPATH_W = @CYGPATH_W@ -+DEFS = @DEFS@ -+DEPDIR = @DEPDIR@ -+DLLTOOL = @DLLTOOL@ -+DSYMUTIL = @DSYMUTIL@ -+DUMPBIN = @DUMPBIN@ -+ECHO_C = @ECHO_C@ -+ECHO_N = @ECHO_N@ -+ECHO_T = @ECHO_T@ -+EGREP = @EGREP@ -+EXEEXT = @EXEEXT@ -+FGREP = @FGREP@ -+GREP = @GREP@ -+GZIP = @GZIP@ -+INET_NTOP_LIB = @INET_NTOP_LIB@ -+INSTALL = @INSTALL@ -+INSTALL_DATA = @INSTALL_DATA@ -+INSTALL_PROGRAM = @INSTALL_PROGRAM@ -+INSTALL_SCRIPT = @INSTALL_SCRIPT@ -+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -+LD = @LD@ -+LDFLAGS = @LDFLAGS@ -+LIBOBJS = @LIBOBJS@ -+LIBPCRE = @LIBPCRE@ -+LIBS = @LIBS@ -+LIBTOOL = @LIBTOOL@ -+LIPO = @LIPO@ -+LN_S = @LN_S@ -+LTLIBOBJS = @LTLIBOBJS@ -+MAINT = @MAINT@ -+MAKEINFO = @MAKEINFO@ -+MANIFEST_TOOL = @MANIFEST_TOOL@ -+MKDIR_P = @MKDIR_P@ -+MV = @MV@ -+NM = @NM@ -+NMEDIT = @NMEDIT@ -+OBJDUMP = @OBJDUMP@ -+OBJEXT = @OBJEXT@ -+OTOOL = @OTOOL@ -+OTOOL64 = @OTOOL64@ -+PACKAGE = @PACKAGE@ -+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -+PACKAGE_NAME = @PACKAGE_NAME@ -+PACKAGE_STRING = @PACKAGE_STRING@ -+PACKAGE_TARNAME = @PACKAGE_TARNAME@ -+PACKAGE_URL = @PACKAGE_URL@ -+PACKAGE_VERSION = @PACKAGE_VERSION@ -+PATH_SEPARATOR = @PATH_SEPARATOR@ -+PCRE_CONFIG = @PCRE_CONFIG@ -+PTHREAD_CC = @PTHREAD_CC@ -+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ -+PTHREAD_LIBS = @PTHREAD_LIBS@ -+RANLIB = @RANLIB@ -+SED = @SED@ -+SET_MAKE = @SET_MAKE@ -+SHELL = @SHELL@ -+STRIP = @STRIP@ -+VERSION = @VERSION@ -+XMLTO = @XMLTO@ -+abs_builddir = @abs_builddir@ -+abs_srcdir = @abs_srcdir@ -+abs_top_builddir = @abs_top_builddir@ -+abs_top_srcdir = @abs_top_srcdir@ -+ac_ct_AR = @ac_ct_AR@ -+ac_ct_CC = @ac_ct_CC@ -+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -+am__include = @am__include@ -+am__leading_dot = @am__leading_dot@ -+am__quote = @am__quote@ -+am__tar = @am__tar@ -+am__untar = @am__untar@ -+ax_pthread_config = @ax_pthread_config@ -+bindir = @bindir@ -+build = @build@ -+build_alias = @build_alias@ -+build_cpu = @build_cpu@ -+build_os = @build_os@ -+build_vendor = @build_vendor@ -+builddir = @builddir@ -+datadir = @datadir@ -+datarootdir = @datarootdir@ -+docdir = @docdir@ -+dvidir = @dvidir@ -+exec_prefix = @exec_prefix@ -+host = @host@ -+host_alias = @host_alias@ -+host_cpu = @host_cpu@ -+host_os = @host_os@ -+host_vendor = @host_vendor@ -+htmldir = @htmldir@ -+includedir = @includedir@ -+infodir = @infodir@ -+install_sh = @install_sh@ -+libdir = @libdir@ -+libexecdir = @libexecdir@ -+localedir = @localedir@ -+localstatedir = @localstatedir@ -+mandir = @mandir@ -+mkdir_p = @mkdir_p@ -+oldincludedir = @oldincludedir@ -+pcre_pcreh = @pcre_pcreh@ -+pcreh = @pcreh@ -+pdfdir = @pdfdir@ -+prefix = @prefix@ -+program_transform_name = @program_transform_name@ -+psdir = @psdir@ -+runstatedir = @runstatedir@ -+sbindir = @sbindir@ -+sharedstatedir = @sharedstatedir@ -+srcdir = @srcdir@ -+subdirs = @subdirs@ -+sysconfdir = @sysconfdir@ -+target_alias = @target_alias@ -+top_build_prefix = @top_build_prefix@ -+top_builddir = @top_builddir@ -+top_srcdir = @top_srcdir@ -+VERSION_INFO = 2:0:0 -+AM_CFLAGS = -g -O2 -Wall -Werror -Wno-deprecated-declarations \ -+ -fno-strict-aliasing -std=gnu99 -D_GNU_SOURCE \ -+ $(PTHREAD_CFLAGS) $(am__append_1) \ -+ -I$(top_srcdir)/libipset/include \ -+ -I$(top_srcdir)/libcork/include $(LIBPCRE_CFLAGS) -+SS_COMMON_LIBS = $(top_builddir)/libipset/libipset.la \ -+ $(top_builddir)/libcork/libcork.la $(INET_NTOP_LIB) \ -+ $(LIBPCRE_LIBS) $(am__append_2) $(am__append_3) -+sni_src = http.c \ -+ tls.c \ -+ rule.c -+ -+ss_check_SOURCES = check.c -+ss_server_SOURCES = utils.c \ -+ netutils.c \ -+ jconf.c \ -+ json.c \ -+ encrypt.c \ -+ udprelay.c \ -+ cache.c \ -+ acl.c \ -+ resolv.c \ -+ server.c \ -+ $(sni_src) -+ -+ss_check_LDADD = $(SS_COMMON_LIBS) -+ss_server_LDADD = $(SS_COMMON_LIBS) $(am__append_4) $(am__append_5) -+ss_check_CFLAGS = $(AM_CFLAGS) -+ss_server_CFLAGS = $(AM_CFLAGS) -DMODULE_REMOTE -+all: all-am -+ -+.SUFFIXES: -+.SUFFIXES: .c .lo .o .obj -+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) -+ @for dep in $?; do \ -+ case '$(am__configure_deps)' in \ -+ *$$dep*) \ -+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ -+ && { if test -f $@; then exit 0; else break; fi; }; \ -+ exit 1;; \ -+ esac; \ -+ done; \ -+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign server/Makefile'; \ -+ $(am__cd) $(top_srcdir) && \ -+ $(AUTOMAKE) --foreign server/Makefile -+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status -+ @case '$?' in \ -+ *config.status*) \ -+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ -+ *) \ -+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ -+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ -+ esac; -+ -+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) -+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -+ -+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) -+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) -+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -+$(am__aclocal_m4_deps): -+install-binPROGRAMS: $(bin_PROGRAMS) -+ @$(NORMAL_INSTALL) -+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ -+ if test -n "$$list"; then \ -+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ -+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ -+ fi; \ -+ for p in $$list; do echo "$$p $$p"; done | \ -+ sed 's/$(EXEEXT)$$//' | \ -+ while read p p1; do if test -f $$p \ -+ || test -f $$p1 \ -+ ; then echo "$$p"; echo "$$p"; else :; fi; \ -+ done | \ -+ sed -e 'p;s,.*/,,;n;h' \ -+ -e 's|.*|.|' \ -+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ -+ sed 'N;N;N;s,\n, ,g' | \ -+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ -+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ -+ if ($$2 == $$4) files[d] = files[d] " " $$1; \ -+ else { print "f", $$3 "/" $$4, $$1; } } \ -+ END { for (d in files) print "f", d, files[d] }' | \ -+ while read type dir files; do \ -+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ -+ test -z "$$files" || { \ -+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ -+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ -+ } \ -+ ; done -+ -+uninstall-binPROGRAMS: -+ @$(NORMAL_UNINSTALL) -+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ -+ files=`for p in $$list; do echo "$$p"; done | \ -+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -+ -e 's/$$/$(EXEEXT)/' \ -+ `; \ -+ test -n "$$list" || exit 0; \ -+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ -+ cd "$(DESTDIR)$(bindir)" && rm -f $$files -+ -+clean-binPROGRAMS: -+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ -+ echo " rm -f" $$list; \ -+ rm -f $$list || exit $$?; \ -+ test -n "$(EXEEXT)" || exit 0; \ -+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ -+ echo " rm -f" $$list; \ -+ rm -f $$list -+ -+ss-check$(EXEEXT): $(ss_check_OBJECTS) $(ss_check_DEPENDENCIES) $(EXTRA_ss_check_DEPENDENCIES) -+ @rm -f ss-check$(EXEEXT) -+ $(AM_V_CCLD)$(ss_check_LINK) $(ss_check_OBJECTS) $(ss_check_LDADD) $(LIBS) -+ -+ss-server$(EXEEXT): $(ss_server_OBJECTS) $(ss_server_DEPENDENCIES) $(EXTRA_ss_server_DEPENDENCIES) -+ @rm -f ss-server$(EXEEXT) -+ $(AM_V_CCLD)$(ss_server_LINK) $(ss_server_OBJECTS) $(ss_server_LDADD) $(LIBS) -+ -+mostlyclean-compile: -+ -rm -f *.$(OBJEXT) -+ -+distclean-compile: -+ -rm -f *.tab.c -+ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_check-check.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-acl.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-cache.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-encrypt.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-http.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-jconf.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-json.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-netutils.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-resolv.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-rule.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-server.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-tls.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-udprelay.Po@am__quote@ -+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-utils.Po@am__quote@ -+ -+.c.o: -+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ -+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< -+ -+.c.obj: -+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ -+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ -+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` -+ -+.c.lo: -+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ -+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< -+ -+ss_check-check.o: check.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_check_CFLAGS) $(CFLAGS) -MT ss_check-check.o -MD -MP -MF $(DEPDIR)/ss_check-check.Tpo -c -o ss_check-check.o `test -f 'check.c' || echo '$(srcdir)/'`check.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_check-check.Tpo $(DEPDIR)/ss_check-check.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='check.c' object='ss_check-check.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_check_CFLAGS) $(CFLAGS) -c -o ss_check-check.o `test -f 'check.c' || echo '$(srcdir)/'`check.c -+ -+ss_check-check.obj: check.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_check_CFLAGS) $(CFLAGS) -MT ss_check-check.obj -MD -MP -MF $(DEPDIR)/ss_check-check.Tpo -c -o ss_check-check.obj `if test -f 'check.c'; then $(CYGPATH_W) 'check.c'; else $(CYGPATH_W) '$(srcdir)/check.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_check-check.Tpo $(DEPDIR)/ss_check-check.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='check.c' object='ss_check-check.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_check_CFLAGS) $(CFLAGS) -c -o ss_check-check.obj `if test -f 'check.c'; then $(CYGPATH_W) 'check.c'; else $(CYGPATH_W) '$(srcdir)/check.c'; fi` -+ -+ss_server-utils.o: utils.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-utils.o -MD -MP -MF $(DEPDIR)/ss_server-utils.Tpo -c -o ss_server-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-utils.Tpo $(DEPDIR)/ss_server-utils.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='ss_server-utils.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c -+ -+ss_server-utils.obj: utils.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-utils.obj -MD -MP -MF $(DEPDIR)/ss_server-utils.Tpo -c -o ss_server-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-utils.Tpo $(DEPDIR)/ss_server-utils.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='ss_server-utils.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` -+ -+ss_server-netutils.o: netutils.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-netutils.o -MD -MP -MF $(DEPDIR)/ss_server-netutils.Tpo -c -o ss_server-netutils.o `test -f 'netutils.c' || echo '$(srcdir)/'`netutils.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-netutils.Tpo $(DEPDIR)/ss_server-netutils.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netutils.c' object='ss_server-netutils.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-netutils.o `test -f 'netutils.c' || echo '$(srcdir)/'`netutils.c -+ -+ss_server-netutils.obj: netutils.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-netutils.obj -MD -MP -MF $(DEPDIR)/ss_server-netutils.Tpo -c -o ss_server-netutils.obj `if test -f 'netutils.c'; then $(CYGPATH_W) 'netutils.c'; else $(CYGPATH_W) '$(srcdir)/netutils.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-netutils.Tpo $(DEPDIR)/ss_server-netutils.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netutils.c' object='ss_server-netutils.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-netutils.obj `if test -f 'netutils.c'; then $(CYGPATH_W) 'netutils.c'; else $(CYGPATH_W) '$(srcdir)/netutils.c'; fi` -+ -+ss_server-jconf.o: jconf.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-jconf.o -MD -MP -MF $(DEPDIR)/ss_server-jconf.Tpo -c -o ss_server-jconf.o `test -f 'jconf.c' || echo '$(srcdir)/'`jconf.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-jconf.Tpo $(DEPDIR)/ss_server-jconf.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='jconf.c' object='ss_server-jconf.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-jconf.o `test -f 'jconf.c' || echo '$(srcdir)/'`jconf.c -+ -+ss_server-jconf.obj: jconf.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-jconf.obj -MD -MP -MF $(DEPDIR)/ss_server-jconf.Tpo -c -o ss_server-jconf.obj `if test -f 'jconf.c'; then $(CYGPATH_W) 'jconf.c'; else $(CYGPATH_W) '$(srcdir)/jconf.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-jconf.Tpo $(DEPDIR)/ss_server-jconf.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='jconf.c' object='ss_server-jconf.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-jconf.obj `if test -f 'jconf.c'; then $(CYGPATH_W) 'jconf.c'; else $(CYGPATH_W) '$(srcdir)/jconf.c'; fi` -+ -+ss_server-json.o: json.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-json.o -MD -MP -MF $(DEPDIR)/ss_server-json.Tpo -c -o ss_server-json.o `test -f 'json.c' || echo '$(srcdir)/'`json.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-json.Tpo $(DEPDIR)/ss_server-json.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='json.c' object='ss_server-json.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-json.o `test -f 'json.c' || echo '$(srcdir)/'`json.c -+ -+ss_server-json.obj: json.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-json.obj -MD -MP -MF $(DEPDIR)/ss_server-json.Tpo -c -o ss_server-json.obj `if test -f 'json.c'; then $(CYGPATH_W) 'json.c'; else $(CYGPATH_W) '$(srcdir)/json.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-json.Tpo $(DEPDIR)/ss_server-json.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='json.c' object='ss_server-json.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-json.obj `if test -f 'json.c'; then $(CYGPATH_W) 'json.c'; else $(CYGPATH_W) '$(srcdir)/json.c'; fi` -+ -+ss_server-encrypt.o: encrypt.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-encrypt.o -MD -MP -MF $(DEPDIR)/ss_server-encrypt.Tpo -c -o ss_server-encrypt.o `test -f 'encrypt.c' || echo '$(srcdir)/'`encrypt.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-encrypt.Tpo $(DEPDIR)/ss_server-encrypt.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='encrypt.c' object='ss_server-encrypt.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-encrypt.o `test -f 'encrypt.c' || echo '$(srcdir)/'`encrypt.c -+ -+ss_server-encrypt.obj: encrypt.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-encrypt.obj -MD -MP -MF $(DEPDIR)/ss_server-encrypt.Tpo -c -o ss_server-encrypt.obj `if test -f 'encrypt.c'; then $(CYGPATH_W) 'encrypt.c'; else $(CYGPATH_W) '$(srcdir)/encrypt.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-encrypt.Tpo $(DEPDIR)/ss_server-encrypt.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='encrypt.c' object='ss_server-encrypt.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-encrypt.obj `if test -f 'encrypt.c'; then $(CYGPATH_W) 'encrypt.c'; else $(CYGPATH_W) '$(srcdir)/encrypt.c'; fi` -+ -+ss_server-udprelay.o: udprelay.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-udprelay.o -MD -MP -MF $(DEPDIR)/ss_server-udprelay.Tpo -c -o ss_server-udprelay.o `test -f 'udprelay.c' || echo '$(srcdir)/'`udprelay.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-udprelay.Tpo $(DEPDIR)/ss_server-udprelay.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='udprelay.c' object='ss_server-udprelay.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-udprelay.o `test -f 'udprelay.c' || echo '$(srcdir)/'`udprelay.c -+ -+ss_server-udprelay.obj: udprelay.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-udprelay.obj -MD -MP -MF $(DEPDIR)/ss_server-udprelay.Tpo -c -o ss_server-udprelay.obj `if test -f 'udprelay.c'; then $(CYGPATH_W) 'udprelay.c'; else $(CYGPATH_W) '$(srcdir)/udprelay.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-udprelay.Tpo $(DEPDIR)/ss_server-udprelay.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='udprelay.c' object='ss_server-udprelay.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-udprelay.obj `if test -f 'udprelay.c'; then $(CYGPATH_W) 'udprelay.c'; else $(CYGPATH_W) '$(srcdir)/udprelay.c'; fi` -+ -+ss_server-cache.o: cache.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-cache.o -MD -MP -MF $(DEPDIR)/ss_server-cache.Tpo -c -o ss_server-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-cache.Tpo $(DEPDIR)/ss_server-cache.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cache.c' object='ss_server-cache.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c -+ -+ss_server-cache.obj: cache.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-cache.obj -MD -MP -MF $(DEPDIR)/ss_server-cache.Tpo -c -o ss_server-cache.obj `if test -f 'cache.c'; then $(CYGPATH_W) 'cache.c'; else $(CYGPATH_W) '$(srcdir)/cache.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-cache.Tpo $(DEPDIR)/ss_server-cache.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cache.c' object='ss_server-cache.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-cache.obj `if test -f 'cache.c'; then $(CYGPATH_W) 'cache.c'; else $(CYGPATH_W) '$(srcdir)/cache.c'; fi` -+ -+ss_server-acl.o: acl.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-acl.o -MD -MP -MF $(DEPDIR)/ss_server-acl.Tpo -c -o ss_server-acl.o `test -f 'acl.c' || echo '$(srcdir)/'`acl.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-acl.Tpo $(DEPDIR)/ss_server-acl.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='acl.c' object='ss_server-acl.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-acl.o `test -f 'acl.c' || echo '$(srcdir)/'`acl.c -+ -+ss_server-acl.obj: acl.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-acl.obj -MD -MP -MF $(DEPDIR)/ss_server-acl.Tpo -c -o ss_server-acl.obj `if test -f 'acl.c'; then $(CYGPATH_W) 'acl.c'; else $(CYGPATH_W) '$(srcdir)/acl.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-acl.Tpo $(DEPDIR)/ss_server-acl.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='acl.c' object='ss_server-acl.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-acl.obj `if test -f 'acl.c'; then $(CYGPATH_W) 'acl.c'; else $(CYGPATH_W) '$(srcdir)/acl.c'; fi` -+ -+ss_server-resolv.o: resolv.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-resolv.o -MD -MP -MF $(DEPDIR)/ss_server-resolv.Tpo -c -o ss_server-resolv.o `test -f 'resolv.c' || echo '$(srcdir)/'`resolv.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-resolv.Tpo $(DEPDIR)/ss_server-resolv.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolv.c' object='ss_server-resolv.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-resolv.o `test -f 'resolv.c' || echo '$(srcdir)/'`resolv.c -+ -+ss_server-resolv.obj: resolv.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-resolv.obj -MD -MP -MF $(DEPDIR)/ss_server-resolv.Tpo -c -o ss_server-resolv.obj `if test -f 'resolv.c'; then $(CYGPATH_W) 'resolv.c'; else $(CYGPATH_W) '$(srcdir)/resolv.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-resolv.Tpo $(DEPDIR)/ss_server-resolv.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolv.c' object='ss_server-resolv.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-resolv.obj `if test -f 'resolv.c'; then $(CYGPATH_W) 'resolv.c'; else $(CYGPATH_W) '$(srcdir)/resolv.c'; fi` -+ -+ss_server-server.o: server.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-server.o -MD -MP -MF $(DEPDIR)/ss_server-server.Tpo -c -o ss_server-server.o `test -f 'server.c' || echo '$(srcdir)/'`server.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-server.Tpo $(DEPDIR)/ss_server-server.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server.c' object='ss_server-server.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-server.o `test -f 'server.c' || echo '$(srcdir)/'`server.c -+ -+ss_server-server.obj: server.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-server.obj -MD -MP -MF $(DEPDIR)/ss_server-server.Tpo -c -o ss_server-server.obj `if test -f 'server.c'; then $(CYGPATH_W) 'server.c'; else $(CYGPATH_W) '$(srcdir)/server.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-server.Tpo $(DEPDIR)/ss_server-server.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server.c' object='ss_server-server.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-server.obj `if test -f 'server.c'; then $(CYGPATH_W) 'server.c'; else $(CYGPATH_W) '$(srcdir)/server.c'; fi` -+ -+ss_server-http.o: http.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-http.o -MD -MP -MF $(DEPDIR)/ss_server-http.Tpo -c -o ss_server-http.o `test -f 'http.c' || echo '$(srcdir)/'`http.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-http.Tpo $(DEPDIR)/ss_server-http.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='ss_server-http.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-http.o `test -f 'http.c' || echo '$(srcdir)/'`http.c -+ -+ss_server-http.obj: http.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-http.obj -MD -MP -MF $(DEPDIR)/ss_server-http.Tpo -c -o ss_server-http.obj `if test -f 'http.c'; then $(CYGPATH_W) 'http.c'; else $(CYGPATH_W) '$(srcdir)/http.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-http.Tpo $(DEPDIR)/ss_server-http.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='ss_server-http.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-http.obj `if test -f 'http.c'; then $(CYGPATH_W) 'http.c'; else $(CYGPATH_W) '$(srcdir)/http.c'; fi` -+ -+ss_server-tls.o: tls.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-tls.o -MD -MP -MF $(DEPDIR)/ss_server-tls.Tpo -c -o ss_server-tls.o `test -f 'tls.c' || echo '$(srcdir)/'`tls.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-tls.Tpo $(DEPDIR)/ss_server-tls.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tls.c' object='ss_server-tls.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-tls.o `test -f 'tls.c' || echo '$(srcdir)/'`tls.c -+ -+ss_server-tls.obj: tls.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-tls.obj -MD -MP -MF $(DEPDIR)/ss_server-tls.Tpo -c -o ss_server-tls.obj `if test -f 'tls.c'; then $(CYGPATH_W) 'tls.c'; else $(CYGPATH_W) '$(srcdir)/tls.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-tls.Tpo $(DEPDIR)/ss_server-tls.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tls.c' object='ss_server-tls.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-tls.obj `if test -f 'tls.c'; then $(CYGPATH_W) 'tls.c'; else $(CYGPATH_W) '$(srcdir)/tls.c'; fi` -+ -+ss_server-rule.o: rule.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-rule.o -MD -MP -MF $(DEPDIR)/ss_server-rule.Tpo -c -o ss_server-rule.o `test -f 'rule.c' || echo '$(srcdir)/'`rule.c -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-rule.Tpo $(DEPDIR)/ss_server-rule.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rule.c' object='ss_server-rule.o' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-rule.o `test -f 'rule.c' || echo '$(srcdir)/'`rule.c -+ -+ss_server-rule.obj: rule.c -+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-rule.obj -MD -MP -MF $(DEPDIR)/ss_server-rule.Tpo -c -o ss_server-rule.obj `if test -f 'rule.c'; then $(CYGPATH_W) 'rule.c'; else $(CYGPATH_W) '$(srcdir)/rule.c'; fi` -+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-rule.Tpo $(DEPDIR)/ss_server-rule.Po -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rule.c' object='ss_server-rule.obj' libtool=no @AMDEPBACKSLASH@ -+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-rule.obj `if test -f 'rule.c'; then $(CYGPATH_W) 'rule.c'; else $(CYGPATH_W) '$(srcdir)/rule.c'; fi` -+ -+mostlyclean-libtool: -+ -rm -f *.lo -+ -+clean-libtool: -+ -rm -rf .libs _libs -+ -+ID: $(am__tagged_files) -+ $(am__define_uniq_tagged_files); mkid -fID $$unique -+tags: tags-am -+TAGS: tags -+ -+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) -+ set x; \ -+ here=`pwd`; \ -+ $(am__define_uniq_tagged_files); \ -+ shift; \ -+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ -+ test -n "$$unique" || unique=$$empty_fix; \ -+ if test $$# -gt 0; then \ -+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ -+ "$$@" $$unique; \ -+ else \ -+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ -+ $$unique; \ -+ fi; \ -+ fi -+ctags: ctags-am -+ -+CTAGS: ctags -+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) -+ $(am__define_uniq_tagged_files); \ -+ test -z "$(CTAGS_ARGS)$$unique" \ -+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ -+ $$unique -+ -+GTAGS: -+ here=`$(am__cd) $(top_builddir) && pwd` \ -+ && $(am__cd) $(top_srcdir) \ -+ && gtags -i $(GTAGS_ARGS) "$$here" -+cscopelist: cscopelist-am -+ -+cscopelist-am: $(am__tagged_files) -+ list='$(am__tagged_files)'; \ -+ case "$(srcdir)" in \ -+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ -+ *) sdir=$(subdir)/$(srcdir) ;; \ -+ esac; \ -+ for i in $$list; do \ -+ if test -f "$$i"; then \ -+ echo "$(subdir)/$$i"; \ -+ else \ -+ echo "$$sdir/$$i"; \ -+ fi; \ -+ done >> $(top_builddir)/cscope.files -+ -+distclean-tags: -+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -+ -+distdir: $(DISTFILES) -+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -+ list='$(DISTFILES)'; \ -+ dist_files=`for file in $$list; do echo $$file; done | \ -+ sed -e "s|^$$srcdirstrip/||;t" \ -+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ -+ case $$dist_files in \ -+ */*) $(MKDIR_P) `echo "$$dist_files" | \ -+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ -+ sort -u` ;; \ -+ esac; \ -+ for file in $$dist_files; do \ -+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ -+ if test -d $$d/$$file; then \ -+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ -+ if test -d "$(distdir)/$$file"; then \ -+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ -+ fi; \ -+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ -+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ -+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ -+ fi; \ -+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ -+ else \ -+ test -f "$(distdir)/$$file" \ -+ || cp -p $$d/$$file "$(distdir)/$$file" \ -+ || exit 1; \ -+ fi; \ -+ done -+check-am: all-am -+check: check-am -+all-am: Makefile $(PROGRAMS) -+installdirs: -+ for dir in "$(DESTDIR)$(bindir)"; do \ -+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ -+ done -+install: install-am -+install-exec: install-exec-am -+install-data: install-data-am -+uninstall: uninstall-am -+ -+install-am: all-am -+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am -+ -+installcheck: installcheck-am -+install-strip: -+ if test -z '$(STRIP)'; then \ -+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ -+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ -+ install; \ -+ else \ -+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ -+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ -+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ -+ fi -+mostlyclean-generic: -+ -+clean-generic: -+ -+distclean-generic: -+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -+ -+maintainer-clean-generic: -+ @echo "This command is intended for maintainers to use" -+ @echo "it deletes files that may require special tools to rebuild." -+clean: clean-am -+ -+clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am -+ -+distclean: distclean-am -+ -rm -rf ./$(DEPDIR) -+ -rm -f Makefile -+distclean-am: clean-am distclean-compile distclean-generic \ -+ distclean-tags -+ -+dvi: dvi-am -+ -+dvi-am: -+ -+html: html-am -+ -+html-am: -+ -+info: info-am -+ -+info-am: -+ -+install-data-am: -+ -+install-dvi: install-dvi-am -+ -+install-dvi-am: -+ -+install-exec-am: install-binPROGRAMS -+ -+install-html: install-html-am -+ -+install-html-am: -+ -+install-info: install-info-am -+ -+install-info-am: -+ -+install-man: -+ -+install-pdf: install-pdf-am -+ -+install-pdf-am: -+ -+install-ps: install-ps-am -+ -+install-ps-am: -+ -+installcheck-am: -+ -+maintainer-clean: maintainer-clean-am -+ -rm -rf ./$(DEPDIR) -+ -rm -f Makefile -+maintainer-clean-am: distclean-am maintainer-clean-generic -+ -+mostlyclean: mostlyclean-am -+ -+mostlyclean-am: mostlyclean-compile mostlyclean-generic \ -+ mostlyclean-libtool -+ -+pdf: pdf-am -+ -+pdf-am: -+ -+ps: ps-am -+ -+ps-am: -+ -+uninstall-am: uninstall-binPROGRAMS -+ -+.MAKE: install-am install-strip -+ -+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ -+ clean-binPROGRAMS clean-generic clean-libtool cscopelist-am \ -+ ctags ctags-am distclean distclean-compile distclean-generic \ -+ distclean-libtool distclean-tags distdir dvi dvi-am html \ -+ html-am info info-am install install-am install-binPROGRAMS \ -+ install-data install-data-am install-dvi install-dvi-am \ -+ install-exec install-exec-am install-html install-html-am \ -+ install-info install-info-am install-man install-pdf \ -+ install-pdf-am install-ps install-ps-am install-strip \ -+ installcheck installcheck-am installdirs maintainer-clean \ -+ maintainer-clean-generic mostlyclean mostlyclean-compile \ -+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ -+ tags tags-am uninstall uninstall-am uninstall-binPROGRAMS -+ -+.PRECIOUS: Makefile -+ -+ -+# Tell versions [3.59,3.63) of GNU make to not export all variables. -+# Otherwise a system limit (for SysV at least) may be exceeded. -+.NOEXPORT: -diff --git a/server/README.md b/server/README.md -new file mode 100644 -index 0000000..ef6a20e ---- /dev/null -+++ b/server/README.md -@@ -0,0 +1,3 @@ -+# server -+ -+`ss-server` and `ss-check` from https://github.com/ywb94/shadowsocks-libev -diff --git a/server/acl.c b/server/acl.c -new file mode 100644 -index 0000000..60d4b72 ---- /dev/null -+++ b/server/acl.c -@@ -0,0 +1,597 @@ -+/* -+ * acl.c - Manage the ACL (Access Control List) -+ * -+ * Copyright (C) 2013 - 2016, 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 -+ * . -+ */ -+ -+#include -+#include -+ -+#include "rule.h" -+#include "utils.h" -+#include "cache.h" -+#include "acl.h" -+ -+static struct ip_set white_list_ipv4; -+static struct ip_set white_list_ipv6; -+ -+static struct ip_set black_list_ipv4; -+static struct ip_set black_list_ipv6; -+ -+static struct cork_dllist black_list_rules; -+static struct cork_dllist white_list_rules; -+ -+static int acl_mode = BLACK_LIST; -+ -+static struct cache *block_list; -+ -+static struct ip_set outbound_block_list_ipv4; -+static struct ip_set outbound_block_list_ipv6; -+static struct cork_dllist outbound_block_list_rules; -+ -+#ifdef __linux__ -+ -+#include -+#include -+ -+#define NO_FIREWALL_MODE 0 -+#define IPTABLES_MODE 1 -+#define FIREWALLD_MODE 2 -+ -+static FILE *shell_stdin; -+static int mode = NO_FIREWALL_MODE; -+ -+static char chain_name[64]; -+static char *iptables_init_chain = -+ "iptables -N %s; iptables -F %s; iptables -A OUTPUT -p tcp --tcp-flags RST RST -j %s"; -+static char *iptables_remove_chain = -+ "iptables -D OUTPUT -p tcp --tcp-flags RST RST -j %s; iptables -F %s; iptables -X %s"; -+static char *iptables_add_rule = "iptables -A %s -d %s -j DROP"; -+static char *iptables_remove_rule = "iptables -D %s -d %s -j DROP"; -+ -+static char *ip6tables_init_chain = -+ "ip6tables -N %s; ip6tables -F %s; ip6tables -A OUTPUT -p tcp --tcp-flags RST RST -j %s"; -+static char *ip6tables_remove_chain = -+ "ip6tables -D OUTPUT -p tcp --tcp-flags RST RST -j %s; ip6tables -F %s; ip6tables -X %s"; -+static char *ip6tables_add_rule = "ip6tables -A %s -d %s -j DROP"; -+static char *ip6tables_remove_rule = "ip6tables -D %s -d %s -j DROP"; -+ -+static char *firewalld_init_chain = -+ "firewall-cmd --direct --add-chain ipv4 filter %s; \ -+ firewall-cmd --direct --passthrough ipv4 -F %s; \ -+ firewall-cmd --direct --passthrough ipv4 -A OUTPUT -p tcp --tcp-flags RST RST -j %s"; -+static char *firewalld_remove_chain = -+ "firewall-cmd --direct --passthrough ipv4 -D OUTPUT -p tcp --tcp-flags RST RST -j %s; \ -+ firewall-cmd --direct --passthrough ipv4 -F %s; \ -+ firewall-cmd --direct --remove-chain ipv4 filter %s"; -+static char *firewalld_add_rule = "firewall-cmd --direct --passthrough ipv4 -A %s -d %s -j DROP"; -+static char *firewalld_remove_rule = "firewall-cmd --direct --passthrough ipv4 -D %s -d %s -j DROP"; -+ -+static char *firewalld6_init_chain = -+ "firewall-cmd --direct --add-chain ipv6 filter %s; \ -+ firewall-cmd --direct --passthrough ipv6 -F %s; \ -+ firewall-cmd --direct --passthrough ipv6 -A OUTPUT -p tcp --tcp-flags RST RST -j %s"; -+static char *firewalld6_remove_chain = -+ "firewall-cmd --direct --passthrough ipv6 -D OUTPUT -p tcp --tcp-flags RST RST -j %s; \ -+ firewall-cmd --direct --passthrough ipv6 -F %s; \ -+ firewall-cmd --direct --remove-chain ipv6 filter %s"; -+static char *firewalld6_add_rule = "firewall-cmd --direct --passthrough ipv6 -A %s -d %s -j DROP"; -+static char *firewalld6_remove_rule = "firewall-cmd --direct --passthrough ipv6 -D %s -d %s -j DROP"; -+ -+static int -+run_cmd(const char *cmd) -+{ -+ int ret = 0; -+ char cmdstring[256]; -+ -+ sprintf(cmdstring, "%s\n", cmd); -+ size_t len = strlen(cmdstring); -+ -+ if (shell_stdin != NULL) { -+ ret = fwrite(cmdstring, 1, len, shell_stdin); -+ fflush(shell_stdin); -+ } -+ -+ return ret == len; -+} -+ -+static int -+init_firewall() -+{ -+ int ret = 0; -+ char cli[256]; -+ FILE *fp; -+ -+ if (getuid() != 0) -+ return -1; -+ -+ sprintf(cli, "firewall-cmd --version 2>&1"); -+ fp = popen(cli, "r"); -+ -+ if (fp == NULL) -+ return -1; -+ -+ if (pclose(fp) == 0) { -+ mode = FIREWALLD_MODE; -+ } else { -+ /* Check whether we have permission to operate iptables. -+ * Note that checking `iptables --version` is insufficient: -+ * eg, running within a child user namespace. -+ */ -+ sprintf(cli, "iptables -L 2>&1"); -+ fp = popen(cli, "r"); -+ if (fp == NULL) -+ return -1; -+ if (pclose(fp) == 0) -+ mode = IPTABLES_MODE; -+ } -+ -+ sprintf(chain_name, "SHADOWSOCKS_LIBEV_%d", getpid()); -+ -+ if (mode == FIREWALLD_MODE) { -+ sprintf(cli, firewalld6_init_chain, chain_name, chain_name, chain_name); -+ ret |= system(cli); -+ sprintf(cli, firewalld_init_chain, chain_name, chain_name, chain_name); -+ ret |= system(cli); -+ } else if (mode == IPTABLES_MODE) { -+ sprintf(cli, ip6tables_init_chain, chain_name, chain_name, chain_name); -+ ret |= system(cli); -+ sprintf(cli, iptables_init_chain, chain_name, chain_name, chain_name); -+ ret |= system(cli); -+ } -+ -+ shell_stdin = popen("/bin/sh", "w"); -+ -+ return ret; -+} -+ -+static int -+reset_firewall() -+{ -+ int ret = 0; -+ char cli[256]; -+ -+ if (getuid() != 0) -+ return -1; -+ -+ if (mode == IPTABLES_MODE) { -+ sprintf(cli, ip6tables_remove_chain, chain_name, chain_name, chain_name); -+ ret |= system(cli); -+ sprintf(cli, iptables_remove_chain, chain_name, chain_name, chain_name); -+ ret |= system(cli); -+ } else if (mode == FIREWALLD_MODE) { -+ sprintf(cli, firewalld6_remove_chain, chain_name, chain_name, chain_name); -+ ret |= system(cli); -+ sprintf(cli, firewalld_remove_chain, chain_name, chain_name, chain_name); -+ ret |= system(cli); -+ } -+ -+ if (shell_stdin != NULL) { -+ run_cmd("exit 0"); -+ pclose(shell_stdin); -+ } -+ -+ return ret; -+} -+ -+static int -+set_firewall_rule(char *addr, int add) -+{ -+ char cli[256]; -+ struct cork_ip ip; -+ -+ if (getuid() != 0) -+ return -1; -+ -+ if (cork_ip_init(&ip, addr)) -+ return -1; -+ -+ if (add) { -+ if (mode == IPTABLES_MODE) -+ sprintf(cli, ip.version == 4 ? iptables_add_rule : ip6tables_add_rule, -+ chain_name, addr); -+ else if (mode == FIREWALLD_MODE) -+ sprintf(cli, ip.version == 4 ? firewalld_add_rule : firewalld6_add_rule, -+ chain_name, addr); -+ return run_cmd(cli); -+ } else { -+ if (mode == IPTABLES_MODE) -+ sprintf(cli, ip.version == 4 ? iptables_remove_rule : ip6tables_remove_rule, -+ chain_name, addr); -+ else if (mode == FIREWALLD_MODE) -+ sprintf(cli, ip.version == 4 ? firewalld_remove_rule : firewalld6_remove_rule, -+ chain_name, addr); -+ return run_cmd(cli); -+ } -+ -+ return 0; -+} -+ -+static void -+free_firewall_rule(void *key, void *element) -+{ -+ if (key == NULL) -+ return; -+ char *addr = (char *)key; -+ set_firewall_rule(addr, 0); -+ ss_free(element); -+} -+ -+#endif -+ -+void -+init_block_list(int firewall) -+{ -+ // Initialize cache -+#ifdef __linux__ -+ if (firewall) -+ init_firewall(); -+ else -+ mode = NO_FIREWALL_MODE; -+ cache_create(&block_list, 256, free_firewall_rule); -+#else -+ cache_create(&block_list, 256, NULL); -+#endif -+} -+ -+void -+free_block_list() -+{ -+#ifdef __linux__ -+ if (mode != NO_FIREWALL_MODE) -+ reset_firewall(); -+#endif -+ cache_clear(block_list, 0); // Remove all items -+} -+ -+int -+remove_from_block_list(char *addr) -+{ -+ size_t addr_len = strlen(addr); -+ return cache_remove(block_list, addr, addr_len); -+} -+ -+void -+clear_block_list() -+{ -+ cache_clear(block_list, 3600); // Clear items older than 1 hour -+} -+ -+int -+check_block_list(char *addr) -+{ -+ size_t addr_len = strlen(addr); -+ -+ if (cache_key_exist(block_list, addr, addr_len)) { -+ int *count = NULL; -+ cache_lookup(block_list, addr, addr_len, &count); -+ -+ if (count != NULL && *count > MAX_TRIES) -+ return 1; -+ } -+ -+ return 0; -+} -+ -+int -+update_block_list(char *addr, int err_level) -+{ -+ size_t addr_len = strlen(addr); -+ -+ if (cache_key_exist(block_list, addr, addr_len)) { -+ int *count = NULL; -+ cache_lookup(block_list, addr, addr_len, &count); -+ if (count != NULL) { -+ if (*count > MAX_TRIES) -+ return 1; -+ (*count) += err_level; -+ } -+ } else if (err_level > 0) { -+ int *count = (int *)ss_malloc(sizeof(int)); -+ *count = 1; -+ cache_insert(block_list, addr, addr_len, count); -+#ifdef __linux__ -+ if (mode != NO_FIREWALL_MODE) -+ set_firewall_rule(addr, 1); -+#endif -+ } -+ -+ return 0; -+} -+ -+static void -+parse_addr_cidr(const char *str, char *host, int *cidr) -+{ -+ int ret = -1, n = 0; -+ char *pch; -+ -+ pch = strchr(str, '/'); -+ while (pch != NULL) { -+ n++; -+ ret = pch - str; -+ pch = strchr(pch + 1, '/'); -+ } -+ if (ret == -1) { -+ strcpy(host, str); -+ *cidr = -1; -+ } else { -+ memcpy(host, str, ret); -+ host[ret] = '\0'; -+ *cidr = atoi(str + ret + 1); -+ } -+} -+ -+char * -+trimwhitespace(char *str) -+{ -+ char *end; -+ -+ // Trim leading space -+ while (isspace(*str)) -+ str++; -+ -+ if (*str == 0) // All spaces? -+ return str; -+ -+ // Trim trailing space -+ end = str + strlen(str) - 1; -+ while (end > str && isspace(*end)) -+ end--; -+ -+ // Write new null terminator -+ *(end + 1) = 0; -+ -+ return str; -+} -+ -+int -+init_acl(const char *path) -+{ -+ // initialize ipset -+ ipset_init_library(); -+ -+ ipset_init(&white_list_ipv4); -+ ipset_init(&white_list_ipv6); -+ ipset_init(&black_list_ipv4); -+ ipset_init(&black_list_ipv6); -+ ipset_init(&outbound_block_list_ipv4); -+ ipset_init(&outbound_block_list_ipv6); -+ -+ cork_dllist_init(&black_list_rules); -+ cork_dllist_init(&white_list_rules); -+ cork_dllist_init(&outbound_block_list_rules); -+ -+ struct ip_set *list_ipv4 = &black_list_ipv4; -+ struct ip_set *list_ipv6 = &black_list_ipv6; -+ struct cork_dllist *rules = &black_list_rules; -+ -+ FILE *f = fopen(path, "r"); -+ if (f == NULL) { -+ LOGE("Invalid acl path."); -+ return -1; -+ } -+ -+ char buf[257]; -+ while (!feof(f)) -+ if (fgets(buf, 256, f)) { -+ // Trim the newline -+ int len = strlen(buf); -+ if (len > 0 && buf[len - 1] == '\n') { -+ buf[len - 1] = '\0'; -+ } -+ -+ char *line = trimwhitespace(buf); -+ -+ // Skip comments -+ if (line[0] == '#') { -+ continue; -+ } -+ -+ if (strlen(line) == 0) { -+ continue; -+ } -+ -+ if (strcmp(line, "[outbound_block_list]") == 0) { -+ list_ipv4 = &outbound_block_list_ipv4; -+ list_ipv6 = &outbound_block_list_ipv6; -+ rules = &outbound_block_list_rules; -+ continue; -+ } else if (strcmp(line, "[black_list]") == 0 -+ || strcmp(line, "[bypass_list]") == 0) { -+ list_ipv4 = &black_list_ipv4; -+ list_ipv6 = &black_list_ipv6; -+ rules = &black_list_rules; -+ continue; -+ } else if (strcmp(line, "[white_list]") == 0 -+ || strcmp(line, "[proxy_list]") == 0) { -+ list_ipv4 = &white_list_ipv4; -+ list_ipv6 = &white_list_ipv6; -+ rules = &white_list_rules; -+ continue; -+ } else if (strcmp(line, "[reject_all]") == 0 -+ || strcmp(line, "[bypass_all]") == 0) { -+ acl_mode = WHITE_LIST; -+ continue; -+ } else if (strcmp(line, "[accept_all]") == 0 -+ || strcmp(line, "[proxy_all]") == 0) { -+ acl_mode = BLACK_LIST; -+ continue; -+ } -+ -+ char host[257]; -+ int cidr; -+ parse_addr_cidr(line, host, &cidr); -+ -+ struct cork_ip addr; -+ int err = cork_ip_init(&addr, host); -+ if (!err) { -+ if (addr.version == 4) { -+ if (cidr >= 0) { -+ ipset_ipv4_add_network(list_ipv4, &(addr.ip.v4), cidr); -+ } else { -+ ipset_ipv4_add(list_ipv4, &(addr.ip.v4)); -+ } -+ } else if (addr.version == 6) { -+ if (cidr >= 0) { -+ ipset_ipv6_add_network(list_ipv6, &(addr.ip.v6), cidr); -+ } else { -+ ipset_ipv6_add(list_ipv6, &(addr.ip.v6)); -+ } -+ } -+ } else { -+ rule_t *rule = new_rule(); -+ accept_rule_arg(rule, line); -+ init_rule(rule); -+ add_rule(rules, rule); -+ } -+ } -+ -+ fclose(f); -+ -+ return 0; -+} -+ -+void -+free_rules(struct cork_dllist *rules) -+{ -+ struct cork_dllist_item *iter; -+ while ((iter = cork_dllist_head(rules)) != NULL) { -+ rule_t *rule = cork_container_of(iter, rule_t, entries); -+ remove_rule(rule); -+ } -+} -+ -+void -+free_acl(void) -+{ -+ ipset_done(&black_list_ipv4); -+ ipset_done(&black_list_ipv6); -+ ipset_done(&white_list_ipv4); -+ ipset_done(&white_list_ipv6); -+ -+ free_rules(&black_list_rules); -+ free_rules(&white_list_rules); -+} -+ -+int -+get_acl_mode(void) -+{ -+ return acl_mode; -+} -+ -+/* -+ * Return 0, if not match. -+ * Return 1, if match black list. -+ * Return -1, if match white list. -+ */ -+int -+acl_match_host(const char *host) -+{ -+ struct cork_ip addr; -+ int ret = 0; -+ int err = cork_ip_init(&addr, host); -+ -+ if (err) { -+ int host_len = strlen(host); -+ if (lookup_rule(&black_list_rules, host, host_len) != NULL) -+ ret = 1; -+ else if (lookup_rule(&white_list_rules, host, host_len) != NULL) -+ ret = -1; -+ return ret; -+ } -+ -+ if (addr.version == 4) { -+ if (ipset_contains_ipv4(&black_list_ipv4, &(addr.ip.v4))) -+ ret = 1; -+ else if (ipset_contains_ipv4(&white_list_ipv4, &(addr.ip.v4))) -+ ret = -1; -+ } else if (addr.version == 6) { -+ if (ipset_contains_ipv6(&black_list_ipv6, &(addr.ip.v6))) -+ ret = 1; -+ else if (ipset_contains_ipv6(&white_list_ipv6, &(addr.ip.v6))) -+ ret = -1; -+ } -+ -+ return ret; -+} -+ -+int -+acl_add_ip(const char *ip) -+{ -+ struct cork_ip addr; -+ int err = cork_ip_init(&addr, ip); -+ if (err) { -+ return -1; -+ } -+ -+ if (addr.version == 4) { -+ ipset_ipv4_add(&black_list_ipv4, &(addr.ip.v4)); -+ } else if (addr.version == 6) { -+ ipset_ipv6_add(&black_list_ipv6, &(addr.ip.v6)); -+ } -+ -+ return 0; -+} -+ -+int -+acl_remove_ip(const char *ip) -+{ -+ struct cork_ip addr; -+ int err = cork_ip_init(&addr, ip); -+ if (err) { -+ return -1; -+ } -+ -+ if (addr.version == 4) { -+ ipset_ipv4_remove(&black_list_ipv4, &(addr.ip.v4)); -+ } else if (addr.version == 6) { -+ ipset_ipv6_remove(&black_list_ipv6, &(addr.ip.v6)); -+ } -+ -+ return 0; -+} -+ -+/* -+ * Return 0, if not match. -+ * Return 1, if match black list. -+ */ -+int -+outbound_block_match_host(const char *host) -+{ -+ struct cork_ip addr; -+ int ret = 0; -+ int err = cork_ip_init(&addr, host); -+ -+ if (err) { -+ int host_len = strlen(host); -+ if (lookup_rule(&outbound_block_list_rules, host, host_len) != NULL) -+ ret = 1; -+ return ret; -+ } -+ -+ if (addr.version == 4) { -+ if (ipset_contains_ipv4(&outbound_block_list_ipv4, &(addr.ip.v4))) -+ ret = 1; -+ } else if (addr.version == 6) { -+ if (ipset_contains_ipv6(&outbound_block_list_ipv6, &(addr.ip.v6))) -+ ret = 1; -+ } -+ -+ return ret; -+} -diff --git a/server/acl.h b/server/acl.h -new file mode 100644 -index 0000000..d6f18b8 ---- /dev/null -+++ b/server/acl.h -@@ -0,0 +1,53 @@ -+/* -+ * acl.h - Define the ACL interface -+ * -+ * Copyright (C) 2013 - 2016, 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 _ACL_H -+#define _ACL_H -+ -+#define BLACK_LIST 0 -+#define WHITE_LIST 1 -+ -+#define MAX_TRIES 64 -+#define MALICIOUS 8 -+#define SUSPICIOUS 4 -+#define BAD 2 -+#define MALFORMED 1 -+ -+int init_acl(const char *path); -+void free_acl(void); -+void clear_block_list(void); -+ -+int acl_match_host(const char *ip); -+int acl_add_ip(const char *ip); -+int acl_remove_ip(const char *ip); -+ -+int get_acl_mode(void); -+ -+void init_block_list(int firewall); -+void free_block_list(); -+int check_block_list(char *addr); -+int update_block_list(char *addr, int err_level); -+int remove_from_block_list(char *addr); -+ -+int outbound_block_match_host(const char *host); -+ -+#endif // _ACL_H -diff --git a/server/auth.c b/server/auth.c -new file mode 100644 -index 0000000..a36257a ---- /dev/null -+++ b/server/auth.c -@@ -0,0 +1,993 @@ -+ -+#include "auth.h" -+ -+static int auth_simple_pack_unit_size = 2000; -+typedef int (*hmac_with_key_func)(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len); -+typedef int (*hash_func)(char *auth, char *msg, int msg_len); -+ -+typedef struct auth_simple_global_data { -+ uint8_t local_client_id[8]; -+ uint32_t connection_id; -+}auth_simple_global_data; -+ -+typedef struct auth_simple_local_data { -+ int has_sent_header; -+ char * recv_buffer; -+ int recv_buffer_size; -+ uint32_t recv_id; -+ uint32_t pack_id; -+ char * salt; -+ uint8_t * user_key; -+ char uid[4]; -+ int user_key_len; -+ hmac_with_key_func hmac; -+ hash_func hash; -+ int hash_len; -+}auth_simple_local_data; -+ -+void auth_simple_local_data_init(auth_simple_local_data* local) { -+ local->has_sent_header = 0; -+ local->recv_buffer = (char*)malloc(16384); -+ local->recv_buffer_size = 0; -+ local->recv_id = 1; -+ local->pack_id = 1; -+ local->salt = ""; -+ local->user_key = 0; -+ local->user_key_len = 0; -+ local->hmac = 0; -+ local->hash = 0; -+ local->hash_len = 0; -+ local->salt = ""; -+} -+ -+void * auth_simple_init_data() { -+ auth_simple_global_data *global = (auth_simple_global_data*)malloc(sizeof(auth_simple_global_data)); -+ rand_bytes(global->local_client_id, 8); -+ rand_bytes((uint8_t*)&global->connection_id, 4); -+ global->connection_id &= 0xFFFFFF; -+ return global; -+} -+ -+obfs * auth_simple_new_obfs() { -+ obfs * self = new_obfs(); -+ self->l_data = malloc(sizeof(auth_simple_local_data)); -+ auth_simple_local_data_init((auth_simple_local_data*)self->l_data); -+ return self; -+} -+ -+obfs * auth_aes128_md5_new_obfs() { -+ obfs * self = new_obfs(); -+ self->l_data = malloc(sizeof(auth_simple_local_data)); -+ auth_simple_local_data_init((auth_simple_local_data*)self->l_data); -+ ((auth_simple_local_data*)self->l_data)->hmac = ss_md5_hmac_with_key; -+ ((auth_simple_local_data*)self->l_data)->hash = ss_md5_hash_func; -+ ((auth_simple_local_data*)self->l_data)->hash_len = 16; -+ ((auth_simple_local_data*)self->l_data)->salt = "auth_aes128_md5"; -+ return self; -+} -+ -+obfs * auth_aes128_sha1_new_obfs() { -+ obfs * self = new_obfs(); -+ self->l_data = malloc(sizeof(auth_simple_local_data)); -+ auth_simple_local_data_init((auth_simple_local_data*)self->l_data); -+ ((auth_simple_local_data*)self->l_data)->hmac = ss_sha1_hmac_with_key; -+ ((auth_simple_local_data*)self->l_data)->hash = ss_sha1_hash_func; -+ ((auth_simple_local_data*)self->l_data)->hash_len = 20; -+ ((auth_simple_local_data*)self->l_data)->salt = "auth_aes128_sha1"; -+ return self; -+} -+ -+void auth_simple_dispose(obfs *self) { -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ if (local->recv_buffer != NULL) { -+ free(local->recv_buffer); -+ local->recv_buffer = NULL; -+ } -+ if (local->user_key != NULL) { -+ free(local->user_key); -+ local->user_key = NULL; -+ } -+ free(local); -+ self->l_data = NULL; -+ dispose_obfs(self); -+} -+ -+int auth_simple_pack_data(char *data, int datalength, char *outdata) { -+ unsigned char rand_len = (xorshift128plus() & 0xF) + 1; -+ int out_size = rand_len + datalength + 6; -+ outdata[0] = out_size >> 8; -+ outdata[1] = out_size; -+ outdata[2] = rand_len; -+ memmove(outdata + rand_len + 2, data, datalength); -+ fillcrc32((unsigned char *)outdata, out_size); -+ return out_size; -+} -+ -+void memintcopy_lt(void *mem, uint32_t val) { -+ ((uint8_t *)mem)[0] = val; -+ ((uint8_t *)mem)[1] = val >> 8; -+ ((uint8_t *)mem)[2] = val >> 16; -+ ((uint8_t *)mem)[3] = val >> 24; -+} -+ -+int auth_simple_pack_auth_data(auth_simple_global_data *global, char *data, int datalength, char *outdata) { -+ unsigned char rand_len = (xorshift128plus() & 0xF) + 1; -+ int out_size = rand_len + datalength + 6 + 12; -+ outdata[0] = out_size >> 8; -+ outdata[1] = out_size; -+ outdata[2] = rand_len; -+ ++global->connection_id; -+ if (global->connection_id > 0xFF000000) { -+ rand_bytes(global->local_client_id, 8); -+ rand_bytes((uint8_t*)&global->connection_id, 4); -+ global->connection_id &= 0xFFFFFF; -+ } -+ time_t t = time(NULL); -+ memintcopy_lt(outdata + rand_len + 2, t); -+ memmove(outdata + rand_len + 2 + 4, global->local_client_id, 4); -+ memintcopy_lt(outdata + rand_len + 2 + 8, global->connection_id); -+ memmove(outdata + rand_len + 2 + 12, data, datalength); -+ fillcrc32((unsigned char *)outdata, out_size); -+ return out_size; -+} -+ -+int auth_simple_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { -+ char *plaindata = *pplaindata; -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ char * out_buffer = (char*)malloc(datalength * 2 + 64); -+ char * buffer = out_buffer; -+ char * data = plaindata; -+ int len = datalength; -+ int pack_len; -+ if (len > 0 && local->has_sent_header == 0) { -+ int head_size = get_head_size(plaindata, datalength, 30); -+ if (head_size > datalength) -+ head_size = datalength; -+ pack_len = auth_simple_pack_auth_data((auth_simple_global_data *)self->server.g_data, data, head_size, buffer); -+ buffer += pack_len; -+ data += head_size; -+ len -= head_size; -+ local->has_sent_header = 1; -+ } -+ while ( len > auth_simple_pack_unit_size ) { -+ pack_len = auth_simple_pack_data(data, auth_simple_pack_unit_size, buffer); -+ buffer += pack_len; -+ data += auth_simple_pack_unit_size; -+ len -= auth_simple_pack_unit_size; -+ } -+ if (len > 0) { -+ pack_len = auth_simple_pack_data(data, len, buffer); -+ buffer += pack_len; -+ } -+ len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ free(out_buffer); -+ return len; -+} -+ -+int auth_simple_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { -+ char *plaindata = *pplaindata; -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; -+ if (local->recv_buffer_size + datalength > 16384) -+ return -1; -+ memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); -+ local->recv_buffer_size += datalength; -+ -+ char * out_buffer = (char*)malloc(local->recv_buffer_size); -+ char * buffer = out_buffer; -+ while (local->recv_buffer_size > 2) { -+ int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; -+ if (length >= 8192 || length < 7) { -+ free(out_buffer); -+ local->recv_buffer_size = 0; -+ return -1; -+ } -+ if (length > local->recv_buffer_size) -+ break; -+ -+ int crc = crc32((unsigned char*)recv_buffer, length); -+ if (crc != -1) { -+ free(out_buffer); -+ local->recv_buffer_size = 0; -+ return -1; -+ } -+ int data_size = length - recv_buffer[2] - 6; -+ memmove(buffer, recv_buffer + 2 + recv_buffer[2], data_size); -+ buffer += data_size; -+ memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); -+ } -+ int len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ free(out_buffer); -+ return len; -+} -+ -+ -+int auth_sha1_pack_data(char *data, int datalength, char *outdata) { -+ unsigned char rand_len = (xorshift128plus() & 0xF) + 1; -+ int out_size = rand_len + datalength + 6; -+ outdata[0] = out_size >> 8; -+ outdata[1] = out_size; -+ outdata[2] = rand_len; -+ memmove(outdata + rand_len + 2, data, datalength); -+ filladler32((unsigned char *)outdata, out_size); -+ return out_size; -+} -+ -+int auth_sha1_pack_auth_data(auth_simple_global_data *global, server_info *server, char *data, int datalength, char *outdata) { -+ unsigned char rand_len = (xorshift128plus() & 0x7F) + 1; -+ int data_offset = rand_len + 4 + 2; -+ int out_size = data_offset + datalength + 12 + OBFS_HMAC_SHA1_LEN; -+ fillcrc32to((unsigned char *)server->key, server->key_len, (unsigned char *)outdata); -+ outdata[4] = out_size >> 8; -+ outdata[5] = out_size; -+ outdata[6] = rand_len; -+ ++global->connection_id; -+ if (global->connection_id > 0xFF000000) { -+ rand_bytes(global->local_client_id, 8); -+ rand_bytes((uint8_t*)&global->connection_id, 4); -+ global->connection_id &= 0xFFFFFF; -+ } -+ time_t t = time(NULL); -+ memintcopy_lt(outdata + data_offset, t); -+ memmove(outdata + data_offset + 4, global->local_client_id, 4); -+ memintcopy_lt(outdata + data_offset + 8, global->connection_id); -+ memmove(outdata + data_offset + 12, data, datalength); -+ char hash[ONETIMEAUTH_BYTES * 2]; -+ ss_sha1_hmac(hash, outdata, out_size - OBFS_HMAC_SHA1_LEN, server->iv); -+ memcpy(outdata + out_size - OBFS_HMAC_SHA1_LEN, hash, OBFS_HMAC_SHA1_LEN); -+ return out_size; -+} -+ -+int auth_sha1_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { -+ char *plaindata = *pplaindata; -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ char * out_buffer = (char*)malloc(datalength * 2 + 256); -+ char * buffer = out_buffer; -+ char * data = plaindata; -+ int len = datalength; -+ int pack_len; -+ if (len > 0 && local->has_sent_header == 0) { -+ int head_size = get_head_size(plaindata, datalength, 30); -+ if (head_size > datalength) -+ head_size = datalength; -+ pack_len = auth_sha1_pack_auth_data((auth_simple_global_data *)self->server.g_data, &self->server, data, head_size, buffer); -+ buffer += pack_len; -+ data += head_size; -+ len -= head_size; -+ local->has_sent_header = 1; -+ } -+ while ( len > auth_simple_pack_unit_size ) { -+ pack_len = auth_sha1_pack_data(data, auth_simple_pack_unit_size, buffer); -+ buffer += pack_len; -+ data += auth_simple_pack_unit_size; -+ len -= auth_simple_pack_unit_size; -+ } -+ if (len > 0) { -+ pack_len = auth_sha1_pack_data(data, len, buffer); -+ buffer += pack_len; -+ } -+ len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ free(out_buffer); -+ return len; -+} -+ -+int auth_sha1_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { -+ char *plaindata = *pplaindata; -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; -+ if (local->recv_buffer_size + datalength > 16384) -+ return -1; -+ memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); -+ local->recv_buffer_size += datalength; -+ -+ char * out_buffer = (char*)malloc(local->recv_buffer_size); -+ char * buffer = out_buffer; -+ while (local->recv_buffer_size > 2) { -+ int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; -+ if (length >= 8192 || length < 7) { -+ free(out_buffer); -+ local->recv_buffer_size = 0; -+ return -1; -+ } -+ if (length > local->recv_buffer_size) -+ break; -+ -+ if (checkadler32((unsigned char*)recv_buffer, length) == 0) { -+ free(out_buffer); -+ local->recv_buffer_size = 0; -+ return -1; -+ } -+ int pos = recv_buffer[2] + 2; -+ int data_size = length - pos - 4; -+ memmove(buffer, recv_buffer + pos, data_size); -+ buffer += data_size; -+ memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); -+ } -+ int len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ free(out_buffer); -+ return len; -+} -+ -+int auth_sha1_v2_pack_data(char *data, int datalength, char *outdata) { -+ unsigned int rand_len = (datalength > 1300 ? 0 : datalength > 400 ? (xorshift128plus() & 0x7F) : (xorshift128plus() & 0x3FF)) + 1; -+ int out_size = rand_len + datalength + 6; -+ outdata[0] = out_size >> 8; -+ outdata[1] = out_size; -+ if (rand_len < 128) -+ { -+ outdata[2] = rand_len; -+ } -+ else -+ { -+ outdata[2] = 0xFF; -+ outdata[3] = rand_len >> 8; -+ outdata[4] = rand_len; -+ } -+ memmove(outdata + rand_len + 2, data, datalength); -+ filladler32((unsigned char *)outdata, out_size); -+ return out_size; -+} -+ -+int auth_sha1_v2_pack_auth_data(auth_simple_global_data *global, server_info *server, char *data, int datalength, char *outdata) { -+ unsigned int rand_len = (datalength > 1300 ? 0 : datalength > 400 ? (xorshift128plus() & 0x7F) : (xorshift128plus() & 0x3FF)) + 1; -+ int data_offset = rand_len + 4 + 2; -+ int out_size = data_offset + datalength + 12 + OBFS_HMAC_SHA1_LEN; -+ const char* salt = "auth_sha1_v2"; -+ int salt_len = strlen(salt); -+ unsigned char *crc_salt = (unsigned char*)malloc(salt_len + server->key_len); -+ memcpy(crc_salt, salt, salt_len); -+ memcpy(crc_salt + salt_len, server->key, server->key_len); -+ fillcrc32to(crc_salt, salt_len + server->key_len, (unsigned char *)outdata); -+ free(crc_salt); -+ outdata[4] = out_size >> 8; -+ outdata[5] = out_size; -+ if (rand_len < 128) -+ { -+ outdata[6] = rand_len; -+ } -+ else -+ { -+ outdata[6] = 0xFF; -+ outdata[7] = rand_len >> 8; -+ outdata[8] = rand_len; -+ } -+ ++global->connection_id; -+ if (global->connection_id > 0xFF000000) { -+ rand_bytes(global->local_client_id, 8); -+ rand_bytes((uint8_t*)&global->connection_id, 4); -+ global->connection_id &= 0xFFFFFF; -+ } -+ memmove(outdata + data_offset, global->local_client_id, 8); -+ memintcopy_lt(outdata + data_offset + 8, global->connection_id); -+ memmove(outdata + data_offset + 12, data, datalength); -+ char hash[ONETIMEAUTH_BYTES * 2]; -+ ss_sha1_hmac(hash, outdata, out_size - OBFS_HMAC_SHA1_LEN, server->iv); -+ memcpy(outdata + out_size - OBFS_HMAC_SHA1_LEN, hash, OBFS_HMAC_SHA1_LEN); -+ return out_size; -+} -+ -+int auth_sha1_v2_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { -+ char *plaindata = *pplaindata; -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ char * out_buffer = (char*)malloc(datalength * 2 + 4096); -+ char * buffer = out_buffer; -+ char * data = plaindata; -+ int len = datalength; -+ int pack_len; -+ if (len > 0 && local->has_sent_header == 0) { -+ int head_size = get_head_size(plaindata, datalength, 30); -+ if (head_size > datalength) -+ head_size = datalength; -+ pack_len = auth_sha1_v2_pack_auth_data((auth_simple_global_data *)self->server.g_data, &self->server, data, head_size, buffer); -+ buffer += pack_len; -+ data += head_size; -+ len -= head_size; -+ local->has_sent_header = 1; -+ } -+ while ( len > auth_simple_pack_unit_size ) { -+ pack_len = auth_sha1_v2_pack_data(data, auth_simple_pack_unit_size, buffer); -+ buffer += pack_len; -+ data += auth_simple_pack_unit_size; -+ len -= auth_simple_pack_unit_size; -+ } -+ if (len > 0) { -+ pack_len = auth_sha1_v2_pack_data(data, len, buffer); -+ buffer += pack_len; -+ } -+ len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ free(out_buffer); -+ return len; -+} -+ -+int auth_sha1_v2_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { -+ char *plaindata = *pplaindata; -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; -+ if (local->recv_buffer_size + datalength > 16384) -+ return -1; -+ memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); -+ local->recv_buffer_size += datalength; -+ -+ char * out_buffer = (char*)malloc(local->recv_buffer_size); -+ char * buffer = out_buffer; -+ char error = 0; -+ while (local->recv_buffer_size > 2) { -+ int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; -+ if (length >= 8192 || length < 7) { -+ local->recv_buffer_size = 0; -+ error = 1; -+ break; -+ } -+ if (length > local->recv_buffer_size) -+ break; -+ -+ if (checkadler32((unsigned char*)recv_buffer, length) == 0) { -+ local->recv_buffer_size = 0; -+ error = 1; -+ break; -+ } -+ int pos = recv_buffer[2]; -+ if (pos < 255) -+ { -+ pos += 2; -+ } -+ else -+ { -+ pos = ((recv_buffer[3] << 8) | recv_buffer[4]) + 2; -+ } -+ int data_size = length - pos - 4; -+ memmove(buffer, recv_buffer + pos, data_size); -+ buffer += data_size; -+ memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); -+ } -+ int len; -+ if (error == 0) { -+ len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ } else { -+ len = -1; -+ } -+ free(out_buffer); -+ return len; -+} -+ -+int auth_sha1_v4_pack_data(char *data, int datalength, char *outdata) { -+ unsigned int rand_len = (datalength > 1300 ? 0 : datalength > 400 ? (xorshift128plus() & 0x7F) : (xorshift128plus() & 0x3FF)) + 1; -+ int out_size = rand_len + datalength + 8; -+ outdata[0] = out_size >> 8; -+ outdata[1] = out_size; -+ uint32_t crc_val = crc32((unsigned char*)outdata, 2); -+ outdata[2] = crc_val; -+ outdata[3] = crc_val >> 8; -+ if (rand_len < 128) -+ { -+ outdata[4] = rand_len; -+ } -+ else -+ { -+ outdata[4] = 0xFF; -+ outdata[5] = rand_len >> 8; -+ outdata[6] = rand_len; -+ } -+ memmove(outdata + rand_len + 4, data, datalength); -+ filladler32((unsigned char *)outdata, out_size); -+ return out_size; -+} -+ -+int auth_sha1_v4_pack_auth_data(auth_simple_global_data *global, server_info *server, char *data, int datalength, char *outdata) { -+ unsigned int rand_len = (datalength > 1300 ? 0 : datalength > 400 ? (xorshift128plus() & 0x7F) : (xorshift128plus() & 0x3FF)) + 1; -+ int data_offset = rand_len + 4 + 2; -+ int out_size = data_offset + datalength + 12 + OBFS_HMAC_SHA1_LEN; -+ const char* salt = "auth_sha1_v4"; -+ int salt_len = strlen(salt); -+ unsigned char *crc_salt = (unsigned char*)malloc(salt_len + server->key_len + 2); -+ crc_salt[0] = outdata[0] = out_size >> 8; -+ crc_salt[1] = outdata[1] = out_size; -+ -+ memcpy(crc_salt + 2, salt, salt_len); -+ memcpy(crc_salt + salt_len + 2, server->key, server->key_len); -+ fillcrc32to(crc_salt, salt_len + server->key_len + 2, (unsigned char *)outdata + 2); -+ free(crc_salt); -+ if (rand_len < 128) -+ { -+ outdata[6] = rand_len; -+ } -+ else -+ { -+ outdata[6] = 0xFF; -+ outdata[7] = rand_len >> 8; -+ outdata[8] = rand_len; -+ } -+ ++global->connection_id; -+ if (global->connection_id > 0xFF000000) { -+ rand_bytes(global->local_client_id, 8); -+ rand_bytes((uint8_t*)&global->connection_id, 4); -+ global->connection_id &= 0xFFFFFF; -+ } -+ time_t t = time(NULL); -+ memintcopy_lt(outdata + data_offset, t); -+ memmove(outdata + data_offset + 4, global->local_client_id, 4); -+ memintcopy_lt(outdata + data_offset + 8, global->connection_id); -+ memmove(outdata + data_offset + 12, data, datalength); -+ char hash[ONETIMEAUTH_BYTES * 2]; -+ ss_sha1_hmac(hash, outdata, out_size - OBFS_HMAC_SHA1_LEN, server->iv); -+ memcpy(outdata + out_size - OBFS_HMAC_SHA1_LEN, hash, OBFS_HMAC_SHA1_LEN); -+ return out_size; -+} -+ -+int auth_sha1_v4_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { -+ char *plaindata = *pplaindata; -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ char * out_buffer = (char*)malloc(datalength * 2 + 4096); -+ char * buffer = out_buffer; -+ char * data = plaindata; -+ int len = datalength; -+ int pack_len; -+ if (len > 0 && local->has_sent_header == 0) { -+ int head_size = get_head_size(plaindata, datalength, 30); -+ if (head_size > datalength) -+ head_size = datalength; -+ pack_len = auth_sha1_v4_pack_auth_data((auth_simple_global_data *)self->server.g_data, &self->server, data, head_size, buffer); -+ buffer += pack_len; -+ data += head_size; -+ len -= head_size; -+ local->has_sent_header = 1; -+ } -+ while ( len > auth_simple_pack_unit_size ) { -+ pack_len = auth_sha1_v4_pack_data(data, auth_simple_pack_unit_size, buffer); -+ buffer += pack_len; -+ data += auth_simple_pack_unit_size; -+ len -= auth_simple_pack_unit_size; -+ } -+ if (len > 0) { -+ pack_len = auth_sha1_v4_pack_data(data, len, buffer); -+ buffer += pack_len; -+ } -+ len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ free(out_buffer); -+ return len; -+} -+ -+int auth_sha1_v4_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { -+ char *plaindata = *pplaindata; -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; -+ if (local->recv_buffer_size + datalength > 16384) -+ return -1; -+ memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); -+ local->recv_buffer_size += datalength; -+ -+ char * out_buffer = (char*)malloc(local->recv_buffer_size); -+ char * buffer = out_buffer; -+ char error = 0; -+ while (local->recv_buffer_size > 4) { -+ uint32_t crc_val = crc32((unsigned char*)recv_buffer, 2); -+ if ((((uint32_t)recv_buffer[3] << 8) | recv_buffer[2]) != (crc_val & 0xffff)) { -+ local->recv_buffer_size = 0; -+ error = 1; -+ break; -+ } -+ int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; -+ if (length >= 8192 || length < 7) { -+ local->recv_buffer_size = 0; -+ error = 1; -+ break; -+ } -+ if (length > local->recv_buffer_size) -+ break; -+ -+ if (checkadler32((unsigned char*)recv_buffer, length) == 0) { -+ local->recv_buffer_size = 0; -+ error = 1; -+ break; -+ } -+ int pos = recv_buffer[4]; -+ if (pos < 255) -+ { -+ pos += 4; -+ } -+ else -+ { -+ pos = (((int)recv_buffer[5] << 8) | recv_buffer[6]) + 4; -+ } -+ int data_size = length - pos - 4; -+ memmove(buffer, recv_buffer + pos, data_size); -+ buffer += data_size; -+ memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); -+ } -+ int len; -+ if (error == 0) { -+ len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ } else { -+ len = -1; -+ } -+ free(out_buffer); -+ return len; -+} -+ -+ -+int auth_aes128_sha1_pack_data(char *data, int datalength, char *outdata, auth_simple_local_data *local, server_info *server) { -+ unsigned int rand_len = (datalength > 1200 ? 0 : local->pack_id > 4 ? (xorshift128plus() & 0x20) : datalength > 900 ? (xorshift128plus() & 0x80) : (xorshift128plus() & 0x200)) + 1; -+ int out_size = rand_len + datalength + 8; -+ memcpy(outdata + rand_len + 4, data, datalength); -+ outdata[0] = out_size; -+ outdata[1] = out_size >> 8; -+ uint8_t key_len = local->user_key_len + 4; -+ uint8_t *key = (uint8_t*)malloc(key_len); -+ memcpy(key, local->user_key, local->user_key_len); -+ memintcopy_lt(key + key_len - 4, local->pack_id); -+ -+ { -+ uint8_t rnd_data[rand_len]; -+ rand_bytes(rnd_data, rand_len); -+ memcpy(outdata + 4, rnd_data, rand_len); -+ } -+ -+ { -+ char hash[20]; -+ local->hmac(hash, outdata, 2, key, key_len); -+ memcpy(outdata + 2, hash, 2); -+ } -+ -+ if (rand_len < 128) -+ { -+ outdata[4] = rand_len; -+ } -+ else -+ { -+ outdata[4] = 0xFF; -+ outdata[5] = rand_len; -+ outdata[6] = rand_len >> 8; -+ } -+ ++local->pack_id; -+ -+ { -+ char hash[20]; -+ local->hmac(hash, outdata, out_size - 4, key, key_len); -+ memcpy(outdata + out_size - 4, hash, 4); -+ } -+ free(key); -+ -+ return out_size; -+} -+ -+int auth_aes128_sha1_pack_auth_data(auth_simple_global_data *global, server_info *server, auth_simple_local_data *local, char *data, int datalength, char *outdata) { -+ unsigned int rand_len = (datalength > 400 ? (xorshift128plus() & 0x200) : (xorshift128plus() & 0x400)); -+ int data_offset = rand_len + 16 + 4 + 4 + 7; -+ int out_size = data_offset + datalength + 4; -+ -+ char encrypt[24]; -+ char encrypt_data[16]; -+ -+ uint8_t *key = (uint8_t*)malloc(server->iv_len + server->key_len); -+ uint8_t key_len = server->iv_len + server->key_len; -+ memcpy(key, server->iv, server->iv_len); -+ memcpy(key + server->iv_len, server->key, server->key_len); -+ -+ { -+ uint8_t rnd_data[rand_len]; -+ rand_bytes(rnd_data, rand_len); -+ memcpy(outdata + data_offset - rand_len, rnd_data, rand_len); -+ } -+ -+ ++global->connection_id; -+ if (global->connection_id > 0xFF000000) { -+ rand_bytes(global->local_client_id, 8); -+ rand_bytes((uint8_t*)&global->connection_id, 4); -+ global->connection_id &= 0xFFFFFF; -+ } -+ time_t t = time(NULL); -+ memintcopy_lt(encrypt, t); -+ memcpy(encrypt + 4, global->local_client_id, 4); -+ memintcopy_lt(encrypt + 8, global->connection_id); -+ encrypt[12] = out_size; -+ encrypt[13] = out_size >> 8; -+ encrypt[14] = rand_len; -+ encrypt[15] = rand_len >> 8; -+ -+ { -+ -+ if (local->user_key == NULL) { -+ if(server->param != NULL && server->param[0] != 0) { -+ char *param = server->param; -+ char *delim = strchr(param, ':'); -+ if(delim != NULL) { -+ char uid_str[16] = {}; -+ strncpy(uid_str, param, delim - param); -+ char key_str[128]; -+ strcpy(key_str, delim + 1); -+ long uid_long = strtol(uid_str, NULL, 10); -+ memintcopy_lt(local->uid, uid_long); -+ -+ char hash[21] = {0}; -+ local->hash(hash, key_str, strlen(key_str)); -+ -+ local->user_key_len = local->hash_len; -+ local->user_key = (uint8_t*)malloc(local->user_key_len); -+ memcpy(local->user_key, hash, local->hash_len); -+ } -+ } -+ if (local->user_key == NULL) { -+ rand_bytes((uint8_t *)local->uid, 4); -+ -+ local->user_key_len = server->key_len; -+ local->user_key = (uint8_t*)malloc(local->user_key_len); -+ memcpy(local->user_key, server->key, local->user_key_len); -+ } -+ } -+ -+ char encrypt_key_base64[256] = {0}; -+ unsigned char encrypt_key[local->user_key_len]; -+ memcpy(encrypt_key, local->user_key, local->user_key_len); -+ base64_encode(encrypt_key, local->user_key_len, encrypt_key_base64); -+ -+ int base64_len; -+ base64_len = (local->user_key_len + 2) / 3 * 4; -+ memcpy(encrypt_key_base64 + base64_len, local->salt, strlen(local->salt)); -+ -+ char enc_key[16]; -+ int enc_key_len = base64_len + strlen(local->salt); -+ bytes_to_key_with_size(encrypt_key_base64, enc_key_len, (uint8_t*)enc_key, 16); -+ ss_aes_128_cbc(encrypt, encrypt_data, enc_key); -+ memcpy(encrypt + 4, encrypt_data, 16); -+ memcpy(encrypt, local->uid, 4); -+ } -+ -+ { -+ char hash[20]; -+ local->hmac(hash, encrypt, 20, key, key_len); -+ memcpy(encrypt + 20, hash, 4); -+ } -+ -+ { -+ uint8_t rnd[1]; -+ rand_bytes(rnd, 1); -+ memcpy(outdata, rnd, 1); -+ char hash[20]; -+ local->hmac(hash, (char *)rnd, 1, key, key_len); -+ memcpy(outdata + 1, hash, 6); -+ } -+ -+ memcpy(outdata + 7, encrypt, 24); -+ memcpy(outdata + data_offset, data, datalength); -+ -+ { -+ char hash[20]; -+ local->hmac(hash, outdata, out_size - 4, local->user_key, local->user_key_len); -+ memmove(outdata + out_size - 4, hash, 4); -+ } -+ free(key); -+ -+ return out_size; -+} -+ -+int auth_aes128_sha1_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { -+ char *plaindata = *pplaindata; -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ char * out_buffer = (char*)malloc(datalength * 2 + 4096); -+ char * buffer = out_buffer; -+ char * data = plaindata; -+ int len = datalength; -+ int pack_len; -+ if (len > 0 && local->has_sent_header == 0) { -+ int head_size = 1200; -+ if (head_size > datalength) -+ head_size = datalength; -+ pack_len = auth_aes128_sha1_pack_auth_data((auth_simple_global_data *)self->server.g_data, &self->server, local, data, head_size, buffer); -+ buffer += pack_len; -+ data += head_size; -+ len -= head_size; -+ local->has_sent_header = 1; -+ } -+ while ( len > auth_simple_pack_unit_size ) { -+ pack_len = auth_aes128_sha1_pack_data(data, auth_simple_pack_unit_size, buffer, local, &self->server); -+ buffer += pack_len; -+ data += auth_simple_pack_unit_size; -+ len -= auth_simple_pack_unit_size; -+ } -+ if (len > 0) { -+ pack_len = auth_aes128_sha1_pack_data(data, len, buffer, local, &self->server); -+ buffer += pack_len; -+ } -+ len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ free(out_buffer); -+ return len; -+} -+ -+int auth_aes128_sha1_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { -+ char *plaindata = *pplaindata; -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ //server_info *server = (server_info*)&self->server; -+ uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; -+ if (local->recv_buffer_size + datalength > 16384) -+ return -1; -+ memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); -+ local->recv_buffer_size += datalength; -+ -+ int key_len = local->user_key_len + 4; -+ uint8_t *key = (uint8_t*)malloc(key_len); -+ memcpy(key, local->user_key, local->user_key_len); -+ -+ char * out_buffer = (char*)malloc(local->recv_buffer_size); -+ char * buffer = out_buffer; -+ char error = 0; -+ while (local->recv_buffer_size > 4) { -+ memintcopy_lt(key + key_len - 4, local->recv_id); -+ -+ { -+ char hash[20]; -+ local->hmac(hash, (char*)recv_buffer, 2, key, key_len); -+ -+ if (memcmp(hash, recv_buffer + 2, 2)) { -+ local->recv_buffer_size = 0; -+ error = 1; -+ break; -+ } -+ } -+ -+ int length = ((int)recv_buffer[1] << 8) + recv_buffer[0]; -+ if (length >= 8192 || length < 8) { -+ local->recv_buffer_size = 0; -+ error = 1; -+ break; -+ } -+ if (length > local->recv_buffer_size) -+ break; -+ -+ { -+ char hash[20]; -+ local->hmac(hash, (char *)recv_buffer, length - 4, key, key_len); -+ if (memcmp(hash, recv_buffer + length - 4, 4)) -+ { -+ local->recv_buffer_size = 0; -+ error = 1; -+ break; -+ } -+ } -+ -+ ++local->recv_id; -+ int pos = recv_buffer[4]; -+ if (pos < 255) -+ { -+ pos += 4; -+ } -+ else -+ { -+ pos = (((int)recv_buffer[6] << 8) | recv_buffer[5]) + 4; -+ } -+ int data_size = length - pos - 4; -+ memmove(buffer, recv_buffer + pos, data_size); -+ buffer += data_size; -+ memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); -+ } -+ int len; -+ if (error == 0) { -+ len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ } else { -+ len = -1; -+ } -+ free(out_buffer); -+ free(key); -+ return len; -+} -+ -+int auth_aes128_sha1_client_udp_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { -+ char *plaindata = *pplaindata; -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ char * out_buffer = (char*)malloc(datalength + 8); -+ -+ if (local->user_key == NULL) { -+ if(self->server.param != NULL && self->server.param[0] != 0) { -+ char *param = self->server.param; -+ char *delim = strchr(param, ':'); -+ if(delim != NULL) { -+ char uid_str[16] = {}; -+ strncpy(uid_str, param, delim - param); -+ char key_str[128]; -+ strcpy(key_str, delim + 1); -+ long uid_long = strtol(uid_str, NULL, 10); -+ memintcopy_lt(local->uid, uid_long); -+ -+ char hash[21] = {0}; -+ local->hash(hash, key_str, strlen(key_str)); -+ -+ local->user_key_len = local->hash_len; -+ local->user_key = (uint8_t*)malloc(local->user_key_len); -+ memcpy(local->user_key, hash, local->hash_len); -+ } -+ } -+ if (local->user_key == NULL) { -+ rand_bytes((uint8_t *)local->uid, 4); -+ -+ local->user_key_len = self->server.key_len; -+ local->user_key = (uint8_t*)malloc(local->user_key_len); -+ memcpy(local->user_key, self->server.key, local->user_key_len); -+ } -+ } -+ -+ int outlength = datalength + 8; -+ memmove(out_buffer, plaindata, datalength); -+ memmove(out_buffer + datalength, local->uid, 4); -+ -+ { -+ char hash[20]; -+ local->hmac(hash, out_buffer, outlength - 4, local->user_key, local->user_key_len); -+ memmove(out_buffer + outlength - 4, hash, 4); -+ } -+ -+ if (*capacity < outlength) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = outlength * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, outlength); -+ -+ free(out_buffer); -+ return outlength; -+} -+ -+int auth_aes128_sha1_client_udp_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { -+ if (datalength <= 4) -+ return 0; -+ -+ char *plaindata = *pplaindata; -+ auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; -+ -+ char hash[20]; -+ local->hmac(hash, plaindata, datalength - 4, self->server.key, self->server.key_len); -+ -+ if (memcmp(hash, plaindata + datalength - 4, 4)) -+ { -+ return 0; -+ } -+ -+ return datalength - 4; -+} -diff --git a/server/auth.h b/server/auth.h -new file mode 100644 -index 0000000..f7730df ---- /dev/null -+++ b/server/auth.h -@@ -0,0 +1,30 @@ -+/* -+ * auth.h - Define shadowsocksR server's buffers and callbacks -+ * -+ * Copyright (C) 2015 - 2016, Break Wa11 -+ */ -+ -+#ifndef _AUTH_H -+#define _AUTH_H -+ -+void * auth_simple_init_data(); -+obfs * auth_simple_new_obfs(); -+void auth_simple_dispose(obfs *self); -+ -+int auth_simple_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+int auth_simple_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+ -+ -+int auth_sha1_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+int auth_sha1_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+ -+int auth_sha1_v2_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+int auth_sha1_v2_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+ -+int auth_sha1_v4_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+int auth_sha1_v4_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+ -+int auth_aes128_sha1_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+int auth_aes128_sha1_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+ -+#endif // _AUTH_H -diff --git a/server/base64.c b/server/base64.c -new file mode 100644 -index 0000000..7cf9552 ---- /dev/null -+++ b/server/base64.c -@@ -0,0 +1,119 @@ -+#include "base64.h" -+ -+/* BASE 64 encode table */ -+static const char base64en[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -+ -+#define BASE64_PAD '=' -+ -+#define BASE64DE_FIRST '+' -+#define BASE64DE_LAST 'z' -+ -+/* ASCII order for BASE 64 decode, -1 in unused character */ -+static const signed char base64de[] = { -+ -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, -1, -1, -1, -1, -1, -+ /* '+', ',', '-', '.', '/', */ -+ -1, -1, -1, 62, -1, -1, -1, 63, -+ /* '0', '1', '2', '3', '4', '5', '6', '7', */ -+ 52, 53, 54, 55, 56, 57, 58, 59, -+ /* '8', '9', ':', ';', '<', '=', '>', '?', */ -+ 60, 61, -1, -1, -1, -1, -1, -1, -+ /* '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', */ -+ -1, 0, 1, 2, 3, 4, 5, 6, -+ /* 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', */ -+ 7, 8, 9, 10, 11, 12, 13, 14, -+ /* 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', */ -+ 15, 16, 17, 18, 19, 20, 21, 22, -+ /* 'X', 'Y', 'Z', '[', '\', ']', '^', '_', */ -+ 23, 24, 25, -1, -1, -1, -1, -1, -+ /* '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', */ -+ -1, 26, 27, 28, 29, 30, 31, 32, -+ /* 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', */ -+ 33, 34, 35, 36, 37, 38, 39, 40, -+ /* 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', */ -+ 41, 42, 43, 44, 45, 46, 47, 48, -+ /* 'x', 'y', 'z', */ -+ 49, 50, 51, -+}; -+ -+int -+base64_encode(const unsigned char *in, unsigned int inlen, char *out) -+{ -+ unsigned int i, j; -+ -+ for (i = j = 0; i < inlen; i++) { -+ int s = i % 3; /* from 6/gcd(6, 8) */ -+ -+ switch (s) { -+ case 0: -+ out[j++] = base64en[(in[i] >> 2) & 0x3F]; -+ continue; -+ case 1: -+ out[j++] = base64en[((in[i-1] & 0x3) << 4) + ((in[i] >> 4) & 0xF)]; -+ continue; -+ case 2: -+ out[j++] = base64en[((in[i-1] & 0xF) << 2) + ((in[i] >> 6) & 0x3)]; -+ out[j++] = base64en[in[i] & 0x3F]; -+ } -+ } -+ -+ /* move back */ -+ i -= 1; -+ -+ /* check the last and add padding */ -+ if ((i % 3) == 0) { -+ out[j++] = base64en[(in[i] & 0x3) << 4]; -+ out[j++] = BASE64_PAD; -+ out[j++] = BASE64_PAD; -+ } else if ((i % 3) == 1) { -+ out[j++] = base64en[(in[i] & 0xF) << 2]; -+ out[j++] = BASE64_PAD; -+ } -+ -+ return BASE64_OK; -+} -+ -+int -+base64_decode(const char *in, unsigned int inlen, unsigned char *out) -+{ -+ unsigned int i, j; -+ -+ for (i = j = 0; i < inlen; i++) { -+ int c; -+ int s = i % 4; /* from 8/gcd(6, 8) */ -+ -+ if (in[i] == '=') -+ return BASE64_OK; -+ -+ if (in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST || -+ (c = base64de[(int)in[i]]) == -1) -+ return BASE64_INVALID; -+ -+ switch (s) { -+ case 0: -+ out[j] = ((unsigned int)c << 2) & 0xFF; -+ continue; -+ case 1: -+ out[j++] += ((unsigned int)c >> 4) & 0x3; -+ -+ /* if not last char with padding */ -+ if (i < (inlen - 3) || in[inlen - 2] != '=') -+ out[j] = ((unsigned int)c & 0xF) << 4; -+ continue; -+ case 2: -+ out[j++] += ((unsigned int)c >> 2) & 0xF; -+ -+ /* if not last char with padding */ -+ if (i < (inlen - 2) || in[inlen - 1] != '=') -+ out[j] = ((unsigned int)c & 0x3) << 6; -+ continue; -+ case 3: -+ out[j++] += (unsigned char)c; -+ } -+ } -+ -+ return BASE64_OK; -+} -diff --git a/server/base64.h b/server/base64.h -new file mode 100644 -index 0000000..6432ba3 ---- /dev/null -+++ b/server/base64.h -@@ -0,0 +1,16 @@ -+#ifndef __BASE64_H__ -+#define __BASE64_H__ -+ -+enum {BASE64_OK = 0, BASE64_INVALID}; -+ -+#define BASE64_ENCODE_OUT_SIZE(s) (((s) + 2) / 3 * 4) -+#define BASE64_DECODE_OUT_SIZE(s) (((s)) / 4 * 3) -+ -+int -+base64_encode(const unsigned char *in, unsigned int inlen, char *out); -+ -+int -+base64_decode(const char *in, unsigned int inlen, unsigned char *out); -+ -+ -+#endif /* __BASE64_H__ */ -diff --git a/server/cache.c b/server/cache.c -new file mode 100644 -index 0000000..c1a2995 ---- /dev/null -+++ b/server/cache.c -@@ -0,0 +1,308 @@ -+/* -+ * cache.c - Manage the connection cache for UDPRELAY -+ * -+ * Copyright (C) 2013 - 2016, 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 -+ * . -+ */ -+ -+/* -+ * Original Author: Oliver Lorenz (ol), olli@olorenz.org, https://olorenz.org -+ * License: This is licensed under the same terms as uthash itself -+ */ -+ -+#include -+#include -+ -+#include "cache.h" -+#include "utils.h" -+ -+#ifdef __MINGW32__ -+#include "win32.h" -+#endif -+ -+/** Creates a new cache object -+ * -+ * @param dst -+ * Where the newly allocated cache object will be stored in -+ * -+ * @param capacity -+ * The maximum number of elements this cache object can hold -+ * -+ * @return EINVAL if dst is NULL, ENOMEM if malloc fails, 0 otherwise -+ */ -+int -+cache_create(struct cache **dst, const size_t capacity, -+ void (*free_cb)(void *key, void *element)) -+{ -+ struct cache *new = NULL; -+ -+ if (!dst) { -+ return EINVAL; -+ } -+ -+ if ((new = malloc(sizeof(*new))) == NULL) { -+ return ENOMEM; -+ } -+ -+ new->max_entries = capacity; -+ new->entries = NULL; -+ new->free_cb = free_cb; -+ *dst = new; -+ return 0; -+} -+ -+/** Frees an allocated cache object -+ * -+ * @param cache -+ * The cache object to free -+ * -+ * @param keep_data -+ * Whether to free contained data or just delete references to it -+ * -+ * @return EINVAL if cache is NULL, 0 otherwise -+ */ -+int -+cache_delete(struct cache *cache, int keep_data) -+{ -+ struct cache_entry *entry, *tmp; -+ -+ if (!cache) { -+ return EINVAL; -+ } -+ -+ if (keep_data) { -+ HASH_CLEAR(hh, cache->entries); -+ } else { -+ HASH_ITER(hh, cache->entries, entry, tmp){ -+ HASH_DEL(cache->entries, entry); -+ if (entry->data != NULL) { -+ if (cache->free_cb) { -+ cache->free_cb(entry->key, entry->data); -+ } else { -+ ss_free(entry->data); -+ } -+ } -+ ss_free(entry->key); -+ ss_free(entry); -+ } -+ } -+ -+ ss_free(cache); -+ return 0; -+} -+ -+/** Clear old cache object -+ * -+ * @param cache -+ * The cache object to clear -+ * -+ * @param age -+ * Clear only objects older than the age (sec) -+ * -+ * @return EINVAL if cache is NULL, 0 otherwise -+ */ -+int -+cache_clear(struct cache *cache, ev_tstamp age) -+{ -+ struct cache_entry *entry, *tmp; -+ -+ if (!cache) { -+ return EINVAL; -+ } -+ -+ ev_tstamp now = ev_time(); -+ -+ HASH_ITER(hh, cache->entries, entry, tmp){ -+ if (now - entry->ts > age) { -+ HASH_DEL(cache->entries, entry); -+ if (entry->data != NULL) { -+ if (cache->free_cb) { -+ cache->free_cb(entry->key, entry->data); -+ } else { -+ ss_free(entry->data); -+ } -+ } -+ ss_free(entry->key); -+ ss_free(entry); -+ } -+ } -+ -+ return 0; -+} -+ -+/** Removes a cache entry -+ * -+ * @param cache -+ * The cache object -+ * -+ * @param key -+ * The key of the entry to remove -+ * -+ * @param key_len -+ * The length of key -+ * -+ * @return EINVAL if cache is NULL, 0 otherwise -+ */ -+int -+cache_remove(struct cache *cache, char *key, size_t key_len) -+{ -+ struct cache_entry *tmp; -+ -+ if (!cache || !key) { -+ return EINVAL; -+ } -+ -+ HASH_FIND(hh, cache->entries, key, key_len, tmp); -+ -+ if (tmp) { -+ HASH_DEL(cache->entries, tmp); -+ if (tmp->data != NULL) { -+ if (cache->free_cb) { -+ cache->free_cb(tmp->key, tmp->data); -+ } else { -+ ss_free(tmp->data); -+ } -+ } -+ ss_free(tmp->key); -+ ss_free(tmp); -+ } -+ -+ return 0; -+} -+ -+/** Checks if a given key is in the cache -+ * -+ * @param cache -+ * The cache object -+ * -+ * @param key -+ * The key to look-up -+ * -+ * @param key_len -+ * The length of key -+ * -+ * @param result -+ * Where to store the result if key is found. -+ * -+ * A warning: Even though result is just a pointer, -+ * you have to call this function with a **ptr, -+ * otherwise this will blow up in your face. -+ * -+ * @return EINVAL if cache is NULL, 0 otherwise -+ */ -+int -+cache_lookup(struct cache *cache, char *key, size_t key_len, void *result) -+{ -+ struct cache_entry *tmp = NULL; -+ char **dirty_hack = result; -+ -+ if (!cache || !key || !result) { -+ return EINVAL; -+ } -+ -+ HASH_FIND(hh, cache->entries, key, key_len, tmp); -+ if (tmp) { -+ HASH_DELETE(hh, cache->entries, tmp); -+ tmp->ts = ev_time(); -+ HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp); -+ *dirty_hack = tmp->data; -+ } else { -+ *dirty_hack = result = NULL; -+ } -+ -+ return 0; -+} -+ -+int -+cache_key_exist(struct cache *cache, char *key, size_t key_len) -+{ -+ struct cache_entry *tmp = NULL; -+ -+ if (!cache || !key) { -+ return 0; -+ } -+ -+ HASH_FIND(hh, cache->entries, key, key_len, tmp); -+ if (tmp) { -+ HASH_DELETE(hh, cache->entries, tmp); -+ tmp->ts = ev_time(); -+ HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp); -+ return 1; -+ } else { -+ return 0; -+ } -+ -+ return 0; -+} -+ -+/** Inserts a given pair into the cache -+ * -+ * @param cache -+ * The cache object -+ * -+ * @param key -+ * The key that identifies -+ * -+ * @param key_len -+ * The length of key -+ * -+ * @param data -+ * Data associated with -+ * -+ * @return EINVAL if cache is NULL, ENOMEM if malloc fails, 0 otherwise -+ */ -+int -+cache_insert(struct cache *cache, char *key, size_t key_len, void *data) -+{ -+ struct cache_entry *entry = NULL; -+ struct cache_entry *tmp_entry = NULL; -+ -+ if (!cache) { -+ return EINVAL; -+ } -+ -+ if ((entry = malloc(sizeof(*entry))) == NULL) { -+ return ENOMEM; -+ } -+ -+ entry->key = ss_malloc(key_len + 1); -+ memcpy(entry->key, key, key_len); -+ entry->key[key_len] = 0; -+ -+ entry->data = data; -+ entry->ts = ev_time(); -+ HASH_ADD_KEYPTR(hh, cache->entries, entry->key, key_len, entry); -+ -+ if (HASH_COUNT(cache->entries) >= cache->max_entries) { -+ HASH_ITER(hh, cache->entries, entry, tmp_entry){ -+ HASH_DELETE(hh, cache->entries, entry); -+ if (entry->data != NULL) { -+ if (cache->free_cb) { -+ cache->free_cb(entry->key, entry->data); -+ } else { -+ ss_free(entry->data); -+ } -+ } -+ ss_free(entry->key); -+ ss_free(entry); -+ break; -+ } -+ } -+ -+ return 0; -+} -diff --git a/server/cache.h b/server/cache.h -new file mode 100644 -index 0000000..0ec98f5 ---- /dev/null -+++ b/server/cache.h -@@ -0,0 +1,62 @@ -+/* -+ * cache.h - Define the cache manager interface -+ * -+ * Copyright (C) 2013 - 2016, 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 -+ * . -+ */ -+ -+/* -+ * Original Author: Oliver Lorenz (ol), olli@olorenz.org, https://olorenz.org -+ * License: This is licensed under the same terms as uthash itself -+ */ -+ -+#ifndef _CACHE_ -+#define _CACHE_ -+ -+#include "uthash.h" -+#include "ev.h" -+ -+/** -+ * A cache entry -+ */ -+struct cache_entry { -+ char *key; /** -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+//#define __DEBUG__ -+#ifdef __DEBUG__ -+#define DEBUG(format,...) printf("File: "__FILE__", Line: %05d: "format"/n", __LINE__, ##__VA_ARGS__) -+#else -+#define DEBUG(format,...) -+#endif -+ -+static sigjmp_buf jmpbuf; -+static void alarm_func() -+{ -+ siglongjmp(jmpbuf, 1); -+} -+ -+static struct hostent *timeGethostbyname(const char *domain, int timeout) -+{ -+ struct hostent *ipHostent = NULL; -+ signal(SIGALRM, alarm_func); -+ if(sigsetjmp(jmpbuf, 1) != 0) -+ { -+ alarm(0);//timout -+ signal(SIGALRM, SIG_IGN); -+ return NULL; -+ } -+ alarm(timeout);//setting alarm -+ ipHostent = gethostbyname(domain); -+ signal(SIGALRM, SIG_IGN); -+ return ipHostent; -+} -+ -+ -+#define MY_HTTP_DEFAULT_PORT 80 -+#define BUFFER_SIZE 1024 -+#define HTTP_POST "POST /%s HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*\r\n"\ -+ "Content-Type:application/x-www-form-urlencoded\r\nContent-Length: %d\r\n\r\n%s" -+#define HTTP_GET "GET /%s HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*\r\n\r\n" -+ -+static int http_parse_url(const char *url,char *host,char *file,int *port) -+{ -+ char *ptr1,*ptr2; -+ int len = 0; -+ if(!url || !host || !file || !port){ -+ return 1; -+ } -+ -+ ptr1 = (char *)url; -+ -+ if(!strncmp(ptr1,"http://",strlen("http://"))){ -+ ptr1 += strlen("http://"); -+ }else{ -+ return 1; -+ } -+ -+ ptr2 = strchr(ptr1,'/'); -+ if(ptr2){ -+ len = strlen(ptr1) - strlen(ptr2); -+ memcpy(host,ptr1,len); -+ host[len] = '\0'; -+ if(*(ptr2 + 1)){ -+ memcpy(file,ptr2 + 1,strlen(ptr2) - 1 ); -+ file[strlen(ptr2) - 1] = '\0'; -+ } -+ }else{ -+ memcpy(host,ptr1,strlen(ptr1)); -+ host[strlen(ptr1)] = '\0'; -+ } -+ //get host and ip -+ ptr1 = strchr(host,':'); -+ if(ptr1){ -+ *ptr1++ = '\0'; -+ *port = atoi(ptr1); -+ }else{ -+ *port = MY_HTTP_DEFAULT_PORT; -+ } -+ -+ return 0; -+} -+ -+ -+static int http_tcpclient_recv(int socket,char *lpbuff){ -+ int recvnum = 0; -+ -+ recvnum = recv(socket, lpbuff,BUFFER_SIZE*4,0); -+ -+ return recvnum; -+} -+ -+static int http_tcpclient_send(int socket,char *buff,int size){ -+ int sent=0,tmpres=0; -+ -+ while(sent < size){ -+ tmpres = send(socket,buff+sent,size-sent,0); -+ if(tmpres == -1){ -+ return 1; -+ } -+ sent += tmpres; -+ } -+ return sent; -+} -+ -+ -+ -+ -+ -+int http_get(const char *url,int socket_fd) -+{ -+ char lpbuf[BUFFER_SIZE*4] = {'\0'}; -+ -+ char host_addr[BUFFER_SIZE] = {'\0'}; -+ char file[BUFFER_SIZE] = {'\0'}; -+ int port = 0; -+ -+ -+ if(!url){ -+ DEBUG(" failed!\n"); -+ return 1; -+ } -+ -+ if(http_parse_url(url,host_addr,file,&port)){ -+ DEBUG("http_parse_url failed!\n"); -+ return 1; -+ } -+ DEBUG("url: %s\thost_addr : %s\tfile:%s\t,%d\n",url,host_addr,file,port); -+ -+ -+ if(socket_fd < 0){ -+ DEBUG("http_tcpclient_create failed\n"); -+ return 1; -+ } -+ -+ sprintf(lpbuf,HTTP_GET,file,host_addr,port); -+ -+ if(http_tcpclient_send(socket_fd,lpbuf,strlen(lpbuf)) < 0){ -+ DEBUG("http_tcpclient_send failed..\n"); -+ return 1; -+ } -+ DEBUG("request:\n%s\n",lpbuf); -+ -+ if(http_tcpclient_recv(socket_fd,lpbuf) <= 0){ -+ DEBUG("http_tcpclient_recv failed\n"); -+ close(socket_fd); -+ return 1; -+ } -+ DEBUG("rec:\n%s\n",lpbuf); -+ close(socket_fd); -+ -+ //return http_parse_result(lpbuf); -+return 0; -+} -+ -+ -+ -+int main(int argc, char *argv[]) -+{ -+ int fd,http_flag=0,http_ret=1; -+ struct sockaddr_in addr; -+ struct hostent *host; -+ struct timeval timeo = {3, 0}; -+ socklen_t len = sizeof(timeo); -+ -+ char http_url[100]="http://"; -+ -+ -+ -+ fd = socket(AF_INET, SOCK_STREAM, 0); -+ if (argc >= 4) -+ timeo.tv_sec = atoi(argv[3]); -+ if (argc>=5) -+ http_flag=1; -+ -+ if((host=timeGethostbyname(argv[1],timeo.tv_sec)) == NULL) { -+ DEBUG("gethostbyname err\n"); -+ return 1; -+ } -+ if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len) == -1) -+ { -+ -+ DEBUG("setsockopt send err\n"); -+ return 1; -+ } -+ -+ if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, len) == -1) -+ { -+ -+ DEBUG("setsockopt recv err\n"); -+ return 1; -+ } -+ -+ addr.sin_family = AF_INET; -+ addr.sin_addr = *((struct in_addr *)host->h_addr); -+ //addr.sin_addr.s_addr = inet_addr(argv[1]); -+ addr.sin_port = htons(atoi(argv[2])); -+if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) -+ { -+ if (errno == EINPROGRESS) -+ { -+ DEBUG("timeout err\n"); -+ return 1; -+ } -+ DEBUG("connect err\n"); -+ return 1; -+ } -+if(http_flag==0) -+{ -+ close(fd); -+ return 0; -+} -+strcat(http_url,argv[1]); -+http_ret=http_get(http_url,fd); -+if(http_ret==1) -+{ -+DEBUG("recv err"); -+ return 1; -+} -+else -+{ -+DEBUG("recv ok"); -+ -+ return 0; -+} -+ -+} -\ No newline at end of file -diff --git a/server/common.h b/server/common.h -new file mode 100644 -index 0000000..000f084 ---- /dev/null -+++ b/server/common.h -@@ -0,0 +1,58 @@ -+/* -+ * common.h - Provide global definitions -+ * -+ * Copyright (C) 2013 - 2016, 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 _COMMON_H -+#define _COMMON_H -+ -+#define DEFAULT_CONF_PATH "/etc/shadowsocks-libev/config.json" -+ -+#ifndef SOL_TCP -+#define SOL_TCP IPPROTO_TCP -+#endif -+ -+#if defined(MODULE_TUNNEL) || defined(MODULE_REDIR) -+#define MODULE_LOCAL -+#endif -+ -+int init_udprelay(const char *server_host, const char *server_port, -+#ifdef MODULE_LOCAL -+ const struct sockaddr *remote_addr, const int remote_addr_len, -+#ifdef MODULE_TUNNEL -+ const ss_addr_t tunnel_addr, -+#endif -+#endif -+ int mtu, int method, int auth, int timeout, const char *iface, const char *protocol, const char *protocol_param); -+ -+void free_udprelay(void); -+ -+#ifdef ANDROID -+int protect_socket(int fd); -+int send_traffic_stat(uint64_t tx, uint64_t rx); -+#endif -+ -+#define STAGE_ERROR -1 /* Error detected */ -+#define STAGE_INIT 0 /* Initial stage */ -+#define STAGE_HANDSHAKE 1 /* Handshake with client */ -+#define STAGE_PARSE 2 /* Parse the header */ -+#define STAGE_RESOLVE 4 /* Resolve the hostname */ -+#define STAGE_STREAM 5 /* Stream between client and server */ -+ -+#endif // _COMMON_H -diff --git a/server/crc32.c b/server/crc32.c -new file mode 100644 -index 0000000..6d328d2 ---- /dev/null -+++ b/server/crc32.c -@@ -0,0 +1,97 @@ -+static uint32_t crc32_table[256] = {0}; -+ -+void init_crc32_table(void) { -+ uint32_t c, i, j; -+ if (crc32_table[0] == 0) { -+ for (i = 0; i < 256; i++) { -+ c = i; -+ for (j = 0; j < 8; j++) { -+ if (c & 1) -+ c = 0xedb88320L ^ (c >> 1); -+ else -+ c = c >> 1; -+ } -+ crc32_table[i] = c; -+ } -+ } -+} -+ -+uint32_t crc32(unsigned char *buffer, unsigned int size) { -+ uint32_t crc = 0xFFFFFFFF; -+ unsigned int i; -+ for (i = 0; i < size; i++) { -+ crc = crc32_table[(crc ^ buffer[i]) & 0xFF] ^ (crc >> 8); -+ } -+ return crc ^ 0xFFFFFFFF; -+} -+ -+void fillcrc32to(unsigned char *buffer, unsigned int size, unsigned char *outbuffer) { -+ uint32_t crc = 0xFFFFFFFF; -+ unsigned int i; -+ for (i = 0; i < size; i++) { -+ crc = crc32_table[(crc ^ buffer[i]) & 0xff] ^ (crc >> 8); -+ } -+ crc ^= 0xFFFFFFFF; -+ outbuffer[0] = crc; -+ outbuffer[1] = crc >> 8; -+ outbuffer[2] = crc >> 16; -+ outbuffer[3] = crc >> 24; -+} -+ -+void fillcrc32(unsigned char *buffer, unsigned int size) { -+ uint32_t crc = 0xFFFFFFFF; -+ unsigned int i; -+ size -= 4; -+ for (i = 0; i < size; i++) { -+ crc = crc32_table[(crc ^ buffer[i]) & 0xff] ^ (crc >> 8); -+ } -+ buffer += size; -+ buffer[0] = crc; -+ buffer[1] = crc >> 8; -+ buffer[2] = crc >> 16; -+ buffer[3] = crc >> 24; -+} -+ -+void adler32_short(unsigned char *buffer, unsigned int size, uint32_t *a, uint32_t *b) { -+ for (int i = 0; i < size; i++) { -+ *a += buffer[i]; -+ *b += *a; -+ } -+ *a %= 65521; -+ *b %= 65521; -+} -+ -+#define NMAX 5552 -+uint32_t adler32(unsigned char *buffer, unsigned int size) { -+ uint32_t a = 1; -+ uint32_t b = 0; -+ while ( size >= NMAX ) { -+ adler32_short(buffer, NMAX, &a, &b); -+ buffer += NMAX; -+ size -= NMAX; -+ } -+ adler32_short(buffer, size, &a, &b); -+ return (b << 16) + a; -+} -+#undef NMAX -+ -+void filladler32(unsigned char *buffer, unsigned int size) { -+ size -= 4; -+ uint32_t checksum = adler32(buffer, size); -+ buffer += size; -+ buffer[0] = checksum; -+ buffer[1] = checksum >> 8; -+ buffer[2] = checksum >> 16; -+ buffer[3] = checksum >> 24; -+} -+ -+int checkadler32(unsigned char *buffer, unsigned int size) { -+ size -= 4; -+ uint32_t checksum = adler32(buffer, size); -+ buffer += size; -+ return checksum == (((uint32_t)buffer[3] << 24) -+ | ((uint32_t)buffer[2] << 16) -+ | ((uint32_t)buffer[1] << 8) -+ | (uint32_t)buffer[0]); -+} -+ -diff --git a/server/encrypt.c b/server/encrypt.c -new file mode 100644 -index 0000000..37dd5cd ---- /dev/null -+++ b/server/encrypt.c -@@ -0,0 +1,1645 @@ -+/* -+ * encrypt.c - Manage the global encryptor -+ * -+ * Copyright (C) 2013 - 2016, 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 -+ * . -+ */ -+ -+#include -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ -+#include -+#include -+#include -+#include -+ -+#elif defined(USE_CRYPTO_POLARSSL) -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#define CIPHER_UNSUPPORTED "unsupported" -+ -+#include -+#ifdef _WIN32 -+#include -+#include -+#else -+#include -+#endif -+ -+#elif defined(USE_CRYPTO_MBEDTLS) -+ -+#include -+#include -+#include -+#include -+#include -+#define CIPHER_UNSUPPORTED "unsupported" -+ -+#include -+#ifdef _WIN32 -+#include -+#include -+#else -+#include -+#endif -+ -+#endif -+ -+#include -+ -+#ifndef __MINGW32__ -+#include -+#endif -+ -+#include "cache.h" -+#include "encrypt.h" -+#include "utils.h" -+ -+#define OFFSET_ROL(p, o) ((uint64_t)(*(p + o)) << (8 * o)) -+ -+static uint8_t *enc_table; -+static uint8_t *dec_table; -+static uint8_t enc_key[MAX_KEY_LENGTH]; -+static int enc_key_len; -+static int enc_iv_len; -+static int enc_method; -+ -+static struct cache *iv_cache; -+ -+#ifdef DEBUG -+static void -+dump(char *tag, char *text, int len) -+{ -+ int i; -+ printf("%s: ", tag); -+ for (i = 0; i < len; i++) -+ printf("0x%02x ", (uint8_t)text[i]); -+ printf("\n"); -+} -+ -+#endif -+ -+static const char *supported_ciphers[CIPHER_NUM] = { -+ "table", -+ "rc4", -+ "rc4-md5-6", -+ "rc4-md5", -+ "aes-128-cfb", -+ "aes-192-cfb", -+ "aes-256-cfb", -+ "aes-128-ctr", -+ "aes-192-ctr", -+ "aes-256-ctr", -+ "bf-cfb", -+ "camellia-128-cfb", -+ "camellia-192-cfb", -+ "camellia-256-cfb", -+ "cast5-cfb", -+ "des-cfb", -+ "idea-cfb", -+ "rc2-cfb", -+ "seed-cfb", -+ "salsa20", -+ "chacha20", -+ "chacha20-ietf" -+}; -+ -+#ifdef USE_CRYPTO_POLARSSL -+static const char *supported_ciphers_polarssl[CIPHER_NUM] = { -+ "table", -+ "ARC4-128", -+ "ARC4-128", -+ "ARC4-128", -+ "AES-128-CFB128", -+ "AES-192-CFB128", -+ "AES-256-CFB128", -+ "AES-128-CTR", -+ "AES-192-CTR", -+ "AES-256-CTR", -+ "BLOWFISH-CFB64", -+ "CAMELLIA-128-CFB128", -+ "CAMELLIA-192-CFB128", -+ "CAMELLIA-256-CFB128", -+ CIPHER_UNSUPPORTED, -+ CIPHER_UNSUPPORTED, -+ CIPHER_UNSUPPORTED, -+ CIPHER_UNSUPPORTED, -+ CIPHER_UNSUPPORTED, -+ "salsa20", -+ "chacha20", -+ "chacha20-ietf" -+}; -+#endif -+ -+#ifdef USE_CRYPTO_MBEDTLS -+static const char *supported_ciphers_mbedtls[CIPHER_NUM] = { -+ "table", -+ "ARC4-128", -+ "ARC4-128", -+ "ARC4-128", -+ "AES-128-CFB128", -+ "AES-192-CFB128", -+ "AES-256-CFB128", -+ "AES-128-CTR", -+ "AES-192-CTR", -+ "AES-256-CTR", -+ "BLOWFISH-CFB64", -+ "CAMELLIA-128-CFB128", -+ "CAMELLIA-192-CFB128", -+ "CAMELLIA-256-CFB128", -+ CIPHER_UNSUPPORTED, -+ CIPHER_UNSUPPORTED, -+ CIPHER_UNSUPPORTED, -+ CIPHER_UNSUPPORTED, -+ CIPHER_UNSUPPORTED, -+ "salsa20", -+ "chacha20", -+ "chacha20-ietf" -+}; -+#endif -+ -+#ifdef USE_CRYPTO_APPLECC -+static const CCAlgorithm supported_ciphers_applecc[CIPHER_NUM] = { -+ kCCAlgorithmInvalid, -+ kCCAlgorithmRC4, -+ kCCAlgorithmRC4, -+ kCCAlgorithmRC4, -+ kCCAlgorithmAES, -+ kCCAlgorithmAES, -+ kCCAlgorithmAES, -+ kCCAlgorithmAES, -+ kCCAlgorithmAES, -+ kCCAlgorithmAES, -+ kCCAlgorithmBlowfish, -+ kCCAlgorithmInvalid, -+ kCCAlgorithmInvalid, -+ kCCAlgorithmInvalid, -+ kCCAlgorithmCAST, -+ kCCAlgorithmDES, -+ kCCAlgorithmInvalid, -+ kCCAlgorithmRC2, -+ kCCAlgorithmInvalid, -+ kCCAlgorithmInvalid, -+ kCCAlgorithmInvalid, -+ kCCAlgorithmInvalid -+}; -+ -+static const CCMode supported_modes_applecc[CIPHER_NUM] = { -+ kCCAlgorithmInvalid, -+ kCCAlgorithmInvalid, -+ kCCModeRC4, -+ kCCModeRC4, -+ kCCModeCFB, -+ kCCModeCFB, -+ kCCModeCFB, -+ kCCModeCTR, -+ kCCModeCTR, -+ kCCModeCTR, -+ kCCModeCFB, -+ kCCAlgorithmInvalid, -+ kCCAlgorithmInvalid, -+ kCCAlgorithmInvalid, -+ kCCModeCFB, -+ kCCModeCFB, -+ kCCModeCFB, -+ kCCModeCFB, -+ kCCAlgorithmInvalid, -+ kCCAlgorithmInvalid, -+ kCCAlgorithmInvalid, -+ kCCAlgorithmInvalid -+}; -+#endif -+ -+static const int supported_ciphers_iv_size[CIPHER_NUM] = { -+ 0, 0, 6, 16, 16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 8, 8, 8, 8, 16, 8, 8, 12 -+}; -+ -+static const int supported_ciphers_key_size[CIPHER_NUM] = { -+ 0, 16, 16, 16, 16, 24, 32, 16, 24, 32, 16, 16, 24, 32, 16, 8, 16, 16, 16, 32, 32, 32 -+}; -+ -+static int -+safe_memcmp(const void *s1, const void *s2, size_t n) -+{ -+ const unsigned char *_s1 = (const unsigned char *)s1; -+ const unsigned char *_s2 = (const unsigned char *)s2; -+ int ret = 0; -+ size_t i; -+ for (i = 0; i < n; i++) -+ ret |= _s1[i] ^ _s2[i]; -+ return !!ret; -+} -+ -+int -+balloc(buffer_t *ptr, size_t capacity) -+{ -+ sodium_memzero(ptr, sizeof(buffer_t)); -+ ptr->array = ss_malloc(capacity); -+ ptr->capacity = capacity; -+ return capacity; -+} -+ -+int -+brealloc(buffer_t *ptr, size_t len, size_t capacity) -+{ -+ if (ptr == NULL) -+ return -1; -+ size_t real_capacity = max(len, capacity); -+ if (ptr->capacity < real_capacity) { -+ ptr->array = ss_realloc(ptr->array, real_capacity); -+ ptr->capacity = real_capacity; -+ } -+ return real_capacity; -+} -+ -+void -+bfree(buffer_t *ptr) -+{ -+ if (ptr == NULL) -+ return; -+ ptr->idx = 0; -+ ptr->len = 0; -+ ptr->capacity = 0; -+ if (ptr->array != NULL) { -+ ss_free(ptr->array); -+ } -+} -+ -+static int -+crypto_stream_xor_ic(uint8_t *c, const uint8_t *m, uint64_t mlen, -+ const uint8_t *n, uint64_t ic, const uint8_t *k, -+ int method) -+{ -+ switch (method) { -+ case SALSA20: -+ return crypto_stream_salsa20_xor_ic(c, m, mlen, n, ic, k); -+ case CHACHA20: -+ return crypto_stream_chacha20_xor_ic(c, m, mlen, n, ic, k); -+ case CHACHA20IETF: -+ return crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, n, (uint32_t)ic, k); -+ } -+ // always return 0 -+ return 0; -+} -+ -+static int -+random_compare(const void *_x, const void *_y, uint32_t i, -+ uint64_t a) -+{ -+ uint8_t x = *((uint8_t *)_x); -+ uint8_t y = *((uint8_t *)_y); -+ return a % (x + i) - a % (y + i); -+} -+ -+static void -+merge(uint8_t *left, int llength, uint8_t *right, -+ int rlength, uint32_t salt, uint64_t key) -+{ -+ uint8_t *ltmp = (uint8_t *)malloc(llength * sizeof(uint8_t)); -+ uint8_t *rtmp = (uint8_t *)malloc(rlength * sizeof(uint8_t)); -+ -+ uint8_t *ll = ltmp; -+ uint8_t *rr = rtmp; -+ -+ uint8_t *result = left; -+ -+ memcpy(ltmp, left, llength * sizeof(uint8_t)); -+ memcpy(rtmp, right, rlength * sizeof(uint8_t)); -+ -+ while (llength > 0 && rlength > 0) { -+ if (random_compare(ll, rr, salt, key) <= 0) { -+ *result = *ll; -+ ++ll; -+ --llength; -+ } else { -+ *result = *rr; -+ ++rr; -+ --rlength; -+ } -+ ++result; -+ } -+ -+ if (llength > 0) { -+ while (llength > 0) { -+ *result = *ll; -+ ++result; -+ ++ll; -+ --llength; -+ } -+ } else { -+ while (rlength > 0) { -+ *result = *rr; -+ ++result; -+ ++rr; -+ --rlength; -+ } -+ } -+ -+ ss_free(ltmp); -+ ss_free(rtmp); -+} -+ -+static void -+merge_sort(uint8_t array[], int length, -+ uint32_t salt, uint64_t key) -+{ -+ uint8_t middle; -+ uint8_t *left, *right; -+ int llength; -+ -+ if (length <= 1) { -+ return; -+ } -+ -+ middle = length / 2; -+ -+ llength = length - middle; -+ -+ left = array; -+ right = array + llength; -+ -+ merge_sort(left, llength, salt, key); -+ merge_sort(right, middle, salt, key); -+ merge(left, llength, right, middle, salt, key); -+} -+ -+int -+enc_get_iv_len() -+{ -+ return enc_iv_len; -+} -+ -+uint8_t* enc_get_key() -+{ -+ return enc_key; -+} -+ -+int enc_get_key_len() -+{ -+ return enc_key_len; -+} -+ -+unsigned char *enc_md5(const unsigned char *d, size_t n, unsigned char *md) -+{ -+#if defined(USE_CRYPTO_OPENSSL) -+ return MD5(d, n, md); -+#elif defined(USE_CRYPTO_POLARSSL) -+ static unsigned char m[16]; -+ if (md == NULL) { -+ md = m; -+ } -+ md5(d, n, md); -+ return md; -+#elif defined(USE_CRYPTO_MBEDTLS) -+ static unsigned char m[16]; -+ if (md == NULL) { -+ md = m; -+ } -+ mbedtls_md5(d, n, md); -+ return md; -+#endif -+} -+ -+void -+enc_table_init(const char *pass) -+{ -+ uint32_t i; -+ uint64_t key = 0; -+ uint8_t *digest; -+ -+ enc_table = ss_malloc(256); -+ dec_table = ss_malloc(256); -+ -+ digest = enc_md5((const uint8_t *)pass, strlen(pass), NULL); -+ -+ for (i = 0; i < 8; i++) -+ key += OFFSET_ROL(digest, i); -+ -+ for (i = 0; i < 256; ++i) -+ enc_table[i] = i; -+ for (i = 1; i < 1024; ++i) -+ merge_sort(enc_table, 256, i, key); -+ for (i = 0; i < 256; ++i) -+ // gen decrypt table from encrypt table -+ dec_table[enc_table[i]] = i; -+} -+ -+int -+cipher_iv_size(const cipher_t *cipher) -+{ -+#if defined(USE_CRYPTO_OPENSSL) -+ if (cipher->info == NULL) -+ return cipher->iv_len; -+ else -+ return EVP_CIPHER_iv_length(cipher->info); -+#elif defined(USE_CRYPTO_POLARSSL) || defined(USE_CRYPTO_MBEDTLS) -+ if (cipher == NULL) { -+ return 0; -+ } -+ return cipher->info->iv_size; -+#endif -+} -+ -+int -+cipher_key_size(const cipher_t *cipher) -+{ -+#if defined(USE_CRYPTO_OPENSSL) -+ if (cipher->info == NULL) -+ return cipher->key_len; -+ else -+ return EVP_CIPHER_key_length(cipher->info); -+#elif defined(USE_CRYPTO_POLARSSL) -+ if (cipher == NULL) { -+ return 0; -+ } -+ /* Override PolarSSL 32 bit default key size with sane 128 bit default */ -+ if (cipher->info->base != NULL && POLARSSL_CIPHER_ID_BLOWFISH == -+ cipher->info->base->cipher) { -+ return 128 / 8; -+ } -+ return cipher->info->key_length / 8; -+#elif defined(USE_CRYPTO_MBEDTLS) -+ /* -+ * Semi-API changes (technically public, morally private) -+ * Renamed a few headers to include _internal in the name. Those headers are -+ * not supposed to be included by users. -+ * Changed md_info_t into an opaque structure (use md_get_xxx() accessors). -+ * Changed pk_info_t into an opaque structure. -+ * Changed cipher_base_t into an opaque structure. -+ */ -+ if (cipher == NULL) { -+ return 0; -+ } -+ /* From Version 1.2.7 released 2013-04-13 Default Blowfish keysize is now 128-bits */ -+ return cipher->info->key_bitlen / 8; -+#endif -+} -+ -+void -+bytes_to_key_with_size(const char *pass, size_t len, uint8_t *md, size_t md_size) -+{ -+ uint8_t result[128]; -+ enc_md5((const unsigned char *)pass, len, result); -+ memcpy(md, result, 16); -+ int i = 16; -+ for (; i < md_size; i += 16) { -+ memcpy(result + 16, pass, len); -+ enc_md5(result, 16 + len, result); -+ memcpy(md + i, result, 16); -+ } -+} -+ -+int -+bytes_to_key(const cipher_t *cipher, const digest_type_t *md, -+ const uint8_t *pass, uint8_t *key) -+{ -+ size_t datal; -+ datal = strlen((const char *)pass); -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ -+ MD5_CTX c; -+ unsigned char md_buf[MAX_MD_SIZE]; -+ int nkey; -+ int addmd; -+ unsigned int i, j, mds; -+ -+ mds = 16; -+ nkey = cipher_key_size(cipher); -+ if (pass == NULL) -+ return nkey; -+ memset(&c, 0, sizeof(MD5_CTX)); -+ -+ for (j = 0, addmd = 0; j < nkey; addmd++) { -+ MD5_Init(&c); -+ if (addmd) { -+ MD5_Update(&c, md_buf, mds); -+ } -+ MD5_Update(&c, pass, datal); -+ MD5_Final(md_buf, &c); -+ -+ for (i = 0; i < mds; i++, j++) { -+ if (j >= nkey) -+ break; -+ key[j] = md_buf[i]; -+ } -+ } -+ -+ return nkey; -+ -+#elif defined(USE_CRYPTO_POLARSSL) -+ md_context_t c; -+ unsigned char md_buf[MAX_MD_SIZE]; -+ int nkey; -+ int addmd; -+ unsigned int i, j, mds; -+ -+ nkey = cipher_key_size(cipher); -+ mds = md_get_size(md); -+ memset(&c, 0, sizeof(md_context_t)); -+ -+ if (pass == NULL) -+ return nkey; -+ if (md_init_ctx(&c, md)) -+ return 0; -+ -+ for (j = 0, addmd = 0; j < nkey; addmd++) { -+ md_starts(&c); -+ if (addmd) { -+ md_update(&c, md_buf, mds); -+ } -+ md_update(&c, pass, datal); -+ md_finish(&c, md_buf); -+ -+ for (i = 0; i < mds; i++, j++) { -+ if (j >= nkey) -+ break; -+ key[j] = md_buf[i]; -+ } -+ } -+ -+ md_free_ctx(&c); -+ return nkey; -+ -+#elif defined(USE_CRYPTO_MBEDTLS) -+ -+ mbedtls_md_context_t c; -+ unsigned char md_buf[MAX_MD_SIZE]; -+ int nkey; -+ int addmd; -+ unsigned int i, j, mds; -+ -+ nkey = cipher_key_size(cipher); -+ mds = mbedtls_md_get_size(md); -+ memset(&c, 0, sizeof(mbedtls_md_context_t)); -+ -+ if (pass == NULL) -+ return nkey; -+ if (mbedtls_md_setup(&c, md, 1)) -+ return 0; -+ -+ for (j = 0, addmd = 0; j < nkey; addmd++) { -+ mbedtls_md_starts(&c); -+ if (addmd) { -+ mbedtls_md_update(&c, md_buf, mds); -+ } -+ mbedtls_md_update(&c, pass, datal); -+ mbedtls_md_finish(&c, &(md_buf[0])); -+ -+ for (i = 0; i < mds; i++, j++) { -+ if (j >= nkey) -+ break; -+ key[j] = md_buf[i]; -+ } -+ } -+ -+ mbedtls_md_free(&c); -+ return nkey; -+#endif -+} -+ -+int -+rand_bytes(uint8_t *output, int len) -+{ -+ randombytes_buf(output, len); -+ // always return success -+ return 0; -+} -+ -+const cipher_kt_t * -+get_cipher_type(int method) -+{ -+ if (method <= TABLE || method >= CIPHER_NUM) { -+ LOGE("get_cipher_type(): Illegal method"); -+ return NULL; -+ } -+ -+ if (method == RC4_MD5 || method == RC4_MD5_6) { -+ method = RC4; -+ } -+ -+ if (method >= SALSA20) { -+ return NULL; -+ } -+ -+ const char *ciphername = supported_ciphers[method]; -+#if defined(USE_CRYPTO_OPENSSL) -+ return EVP_get_cipherbyname(ciphername); -+#elif defined(USE_CRYPTO_POLARSSL) -+ const char *polarname = supported_ciphers_polarssl[method]; -+ if (strcmp(polarname, CIPHER_UNSUPPORTED) == 0) { -+ LOGE("Cipher %s currently is not supported by PolarSSL library", -+ ciphername); -+ return NULL; -+ } -+ return cipher_info_from_string(polarname); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ const char *mbedtlsname = supported_ciphers_mbedtls[method]; -+ if (strcmp(mbedtlsname, CIPHER_UNSUPPORTED) == 0) { -+ LOGE("Cipher %s currently is not supported by mbed TLS library", -+ ciphername); -+ return NULL; -+ } -+ return mbedtls_cipher_info_from_string(mbedtlsname); -+#endif -+} -+ -+const digest_type_t * -+get_digest_type(const char *digest) -+{ -+ if (digest == NULL) { -+ LOGE("get_digest_type(): Digest name is null"); -+ return NULL; -+ } -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ return EVP_get_digestbyname(digest); -+#elif defined(USE_CRYPTO_POLARSSL) -+ return md_info_from_string(digest); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ return mbedtls_md_info_from_string(digest); -+#endif -+} -+ -+void -+cipher_context_init(cipher_ctx_t *ctx, int method, int enc) -+{ -+ if (method <= TABLE || method >= CIPHER_NUM) { -+ LOGE("cipher_context_init(): Illegal method"); -+ return; -+ } -+ -+ if (method >= SALSA20) { -+ enc_iv_len = supported_ciphers_iv_size[method]; -+ return; -+ } -+ -+ const char *ciphername = supported_ciphers[method]; -+#if defined(USE_CRYPTO_APPLECC) -+ cipher_cc_t *cc = &ctx->cc; -+ cc->cryptor = NULL; -+ cc->cipher = supported_ciphers_applecc[method]; -+ if (cc->cipher == kCCAlgorithmInvalid) { -+ cc->valid = kCCContextInvalid; -+ } else { -+ cc->valid = kCCContextValid; -+ if (cc->cipher == kCCAlgorithmRC4) { -+ cc->mode = supported_modes_applecc[method]; -+ cc->padding = ccNoPadding; -+ } else { -+ cc->mode = supported_modes_applecc[method]; -+ if (cc->mode == kCCModeCTR) { -+ cc->padding = ccNoPadding; -+ } else { -+ cc->padding = ccPKCS7Padding; -+ } -+ } -+ return; -+ } -+#endif -+ -+ const cipher_kt_t *cipher = get_cipher_type(method); -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ ctx->evp = EVP_CIPHER_CTX_new(); -+ cipher_evp_t *evp = ctx->evp; -+ -+ if (cipher == NULL) { -+ LOGE("Cipher %s not found in OpenSSL library", ciphername); -+ FATAL("Cannot initialize cipher"); -+ } -+ if (!EVP_CipherInit_ex(evp, cipher, NULL, NULL, NULL, enc)) { -+ LOGE("Cannot initialize cipher %s", ciphername); -+ exit(EXIT_FAILURE); -+ } -+ if (!EVP_CIPHER_CTX_set_key_length(evp, enc_key_len)) { -+ EVP_CIPHER_CTX_cleanup(evp); -+ LOGE("Invalid key length: %d", enc_key_len); -+ exit(EXIT_FAILURE); -+ } -+ if (method > RC4_MD5) { -+ EVP_CIPHER_CTX_set_padding(evp, 1); -+ } -+#elif defined(USE_CRYPTO_POLARSSL) -+ ctx->evp = (cipher_evp_t *)ss_malloc(sizeof(cipher_evp_t)); -+ cipher_evp_t *evp = ctx->evp; -+ -+ if (cipher == NULL) { -+ LOGE("Cipher %s not found in PolarSSL library", ciphername); -+ FATAL("Cannot initialize PolarSSL cipher"); -+ } -+ if (cipher_init_ctx(evp, cipher) != 0) { -+ FATAL("Cannot initialize PolarSSL cipher context"); -+ } -+#elif defined(USE_CRYPTO_MBEDTLS) -+ ctx->evp = (cipher_evp_t *)ss_malloc(sizeof(cipher_evp_t)); -+ cipher_evp_t *evp = ctx->evp; -+ -+ if (cipher == NULL) { -+ LOGE("Cipher %s not found in mbed TLS library", ciphername); -+ FATAL("Cannot initialize mbed TLS cipher"); -+ } -+ mbedtls_cipher_init(evp); -+ if (mbedtls_cipher_setup(evp, cipher) != 0) { -+ FATAL("Cannot initialize mbed TLS cipher context"); -+ } -+#endif -+} -+ -+void -+cipher_context_set_iv(cipher_ctx_t *ctx, uint8_t *iv, size_t iv_len, -+ int enc) -+{ -+ const unsigned char *true_key; -+ -+ if (iv == NULL) { -+ LOGE("cipher_context_set_iv(): IV is null"); -+ return; -+ } -+ -+ if (!enc) { -+ memcpy(ctx->iv, iv, iv_len); -+ } -+ -+ if (enc_method >= SALSA20) { -+ return; -+ } -+ -+ if (enc_method == RC4_MD5 || enc_method == RC4_MD5_6) { -+ unsigned char key_iv[32]; -+ memcpy(key_iv, enc_key, 16); -+ memcpy(key_iv + 16, iv, iv_len); -+ true_key = enc_md5(key_iv, 16 + iv_len, NULL); -+ iv_len = 0; -+ } else { -+ true_key = enc_key; -+ } -+ -+#ifdef USE_CRYPTO_APPLECC -+ cipher_cc_t *cc = &ctx->cc; -+ if (cc->valid == kCCContextValid) { -+ memcpy(cc->iv, iv, iv_len); -+ memcpy(cc->key, true_key, enc_key_len); -+ cc->iv_len = iv_len; -+ cc->key_len = enc_key_len; -+ cc->encrypt = enc ? kCCEncrypt : kCCDecrypt; -+ if (cc->cryptor != NULL) { -+ CCCryptorRelease(cc->cryptor); -+ cc->cryptor = NULL; -+ } -+ -+ CCCryptorStatus ret; -+ ret = CCCryptorCreateWithMode( -+ cc->encrypt, -+ cc->mode, -+ cc->cipher, -+ cc->padding, -+ cc->iv, cc->key, cc->key_len, -+ NULL, 0, 0, kCCModeOptionCTR_BE, -+ &cc->cryptor); -+ if (ret != kCCSuccess) { -+ if (cc->cryptor != NULL) { -+ CCCryptorRelease(cc->cryptor); -+ cc->cryptor = NULL; -+ } -+ FATAL("Cannot set CommonCrypto key and IV"); -+ } -+ return; -+ } -+#endif -+ -+ cipher_evp_t *evp = ctx->evp; -+ if (evp == NULL) { -+ LOGE("cipher_context_set_iv(): Cipher context is null"); -+ return; -+ } -+#if defined(USE_CRYPTO_OPENSSL) -+ if (!EVP_CipherInit_ex(evp, NULL, NULL, true_key, iv, enc)) { -+ EVP_CIPHER_CTX_cleanup(evp); -+ FATAL("Cannot set key and IV"); -+ } -+#elif defined(USE_CRYPTO_POLARSSL) -+ // XXX: PolarSSL 1.3.11: cipher_free_ctx deprecated, Use cipher_free() instead. -+ if (cipher_setkey(evp, true_key, enc_key_len * 8, enc) != 0) { -+ cipher_free_ctx(evp); -+ FATAL("Cannot set PolarSSL cipher key"); -+ } -+#if POLARSSL_VERSION_NUMBER >= 0x01030000 -+ if (cipher_set_iv(evp, iv, iv_len) != 0) { -+ cipher_free_ctx(evp); -+ FATAL("Cannot set PolarSSL cipher IV"); -+ } -+ if (cipher_reset(evp) != 0) { -+ cipher_free_ctx(evp); -+ FATAL("Cannot finalize PolarSSL cipher context"); -+ } -+#else -+ if (cipher_reset(evp, iv) != 0) { -+ cipher_free_ctx(evp); -+ FATAL("Cannot set PolarSSL cipher IV"); -+ } -+#endif -+#elif defined(USE_CRYPTO_MBEDTLS) -+ if (mbedtls_cipher_setkey(evp, true_key, enc_key_len * 8, enc) != 0) { -+ mbedtls_cipher_free(evp); -+ FATAL("Cannot set mbed TLS cipher key"); -+ } -+ -+ if (mbedtls_cipher_set_iv(evp, iv, iv_len) != 0) { -+ mbedtls_cipher_free(evp); -+ FATAL("Cannot set mbed TLS cipher IV"); -+ } -+ if (mbedtls_cipher_reset(evp) != 0) { -+ mbedtls_cipher_free(evp); -+ FATAL("Cannot finalize mbed TLS cipher context"); -+ } -+#endif -+ -+#ifdef DEBUG -+ dump("IV", (char *)iv, iv_len); -+#endif -+} -+ -+void -+cipher_context_release(cipher_ctx_t *ctx) -+{ -+ if (enc_method >= SALSA20) { -+ return; -+ } -+ -+#ifdef USE_CRYPTO_APPLECC -+ cipher_cc_t *cc = &ctx->cc; -+ if (cc->cryptor != NULL) { -+ CCCryptorRelease(cc->cryptor); -+ cc->cryptor = NULL; -+ } -+ if (cc->valid == kCCContextValid) { -+ return; -+ } -+#endif -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ EVP_CIPHER_CTX_free(ctx->evp); -+#elif defined(USE_CRYPTO_POLARSSL) -+// NOTE: cipher_free_ctx deprecated in PolarSSL 1.3.11 -+ cipher_free_ctx(ctx->evp); -+ ss_free(ctx->evp); -+#elif defined(USE_CRYPTO_MBEDTLS) -+// NOTE: cipher_free_ctx deprecated -+ mbedtls_cipher_free(ctx->evp); -+ ss_free(ctx->evp); -+#endif -+} -+ -+static int -+cipher_context_update(cipher_ctx_t *ctx, uint8_t *output, size_t *olen, -+ const uint8_t *input, size_t ilen) -+{ -+#ifdef USE_CRYPTO_APPLECC -+ cipher_cc_t *cc = &ctx->cc; -+ if (cc->valid == kCCContextValid) { -+ CCCryptorStatus ret; -+ ret = CCCryptorUpdate(cc->cryptor, input, ilen, output, -+ ilen, olen); -+ return (ret == kCCSuccess) ? 1 : 0; -+ } -+#endif -+ cipher_evp_t *evp = ctx->evp; -+#if defined(USE_CRYPTO_OPENSSL) -+ int err = 0, tlen = *olen; -+ err = EVP_CipherUpdate(evp, (uint8_t *)output, &tlen, -+ (const uint8_t *)input, ilen); -+ *olen = tlen; -+ return err; -+#elif defined(USE_CRYPTO_POLARSSL) -+ return !cipher_update(evp, (const uint8_t *)input, ilen, -+ (uint8_t *)output, olen); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ return !mbedtls_cipher_update(evp, (const uint8_t *)input, ilen, -+ (uint8_t *)output, olen); -+#endif -+} -+int ss_md5_hmac(char *auth, char *msg, int msg_len, uint8_t *iv) -+{ -+ uint8_t hash[MD5_BYTES]; -+ uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; -+ memcpy(auth_key, iv, enc_iv_len); -+ memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ HMAC(EVP_md5(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash, NULL); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); -+#else -+ md5_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); -+#endif -+ -+ memcpy(auth, hash, MD5_BYTES); -+ -+ return 0; -+} -+ -+int ss_md5_hmac_with_key(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len) -+{ -+ uint8_t hash[MD5_BYTES]; -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ HMAC(EVP_md5(), auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash, NULL); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); -+#else -+ md5_hmac(auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); -+#endif -+ -+ memcpy(auth, hash, MD5_BYTES); -+ -+ return 0; -+} -+ -+int ss_md5_hash_func(char *auth, char *msg, int msg_len) -+{ -+ uint8_t hash[MD5_BYTES]; -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ MD5((uint8_t *)msg, msg_len, (uint8_t *)hash); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), (uint8_t *)msg, msg_len, (uint8_t *)hash); -+#else -+ md5((uint8_t *)msg, msg_len, (uint8_t *)hash); -+#endif -+ -+ memcpy(auth, hash, MD5_BYTES); -+ -+ return 0; -+} -+ -+int ss_sha1_hmac(char *auth, char *msg, int msg_len, uint8_t *iv) -+{ -+ uint8_t hash[SHA1_BYTES]; -+ uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; -+ memcpy(auth_key, iv, enc_iv_len); -+ memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ HMAC(EVP_sha1(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash, NULL); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); -+#else -+ sha1_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); -+#endif -+ -+ memcpy(auth, hash, SHA1_BYTES); -+ -+ return 0; -+} -+ -+int ss_sha1_hmac_with_key(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len) -+{ -+ uint8_t hash[SHA1_BYTES]; -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ HMAC(EVP_sha1(), auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash, NULL); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); -+#else -+ sha1_hmac(auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); -+#endif -+ -+ memcpy(auth, hash, SHA1_BYTES); -+ -+ return 0; -+} -+ -+int ss_sha1_hash_func(char *auth, char *msg, int msg_len) -+{ -+ uint8_t hash[SHA1_BYTES]; -+#if defined(USE_CRYPTO_OPENSSL) -+ SHA1((uint8_t *)msg, msg_len, (uint8_t *)hash); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), (uint8_t *)msg, msg_len, (uint8_t *)hash); -+#else -+ sha1((uint8_t *)msg, msg_len, (uint8_t *)hash); -+#endif -+ -+ memcpy(auth, hash, SHA1_BYTES); -+ -+ return 0; -+} -+ -+int ss_aes_128_cbc(char *encrypt, char *out_data, char *key) -+{ -+ unsigned char iv[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ AES_KEY aes; -+ AES_set_encrypt_key((unsigned char*)key, 128, &aes); -+ AES_cbc_encrypt((const unsigned char *)encrypt, (unsigned char *)out_data, 16, &aes, iv, AES_ENCRYPT); -+ -+#elif defined(USE_CRYPTO_MBEDTLS) -+ mbedtls_aes_context aes; -+ -+ unsigned char output[16]; -+ -+ mbedtls_aes_setkey_enc( &aes, (unsigned char *)key, 128 ); -+ mbedtls_aes_crypt_cbc( &aes, MBEDTLS_AES_ENCRYPT, 16, iv, (unsigned char *)encrypt, output ); -+ -+ memcpy(out_data, output, 16); -+#else -+ -+ aes_context aes; -+ -+ unsigned char output[16]; -+ -+ aes_setkey_enc( &aes, (unsigned char *)key, 128 ); -+ aes_crypt_cbc( &aes, AES_ENCRYPT, 16, iv, (unsigned char *)encrypt, output ); -+ -+ memcpy(out_data, output, 16); -+#endif -+ -+ return 0; -+} -+ -+int ss_onetimeauth(buffer_t *buf, uint8_t *iv, size_t capacity) -+{ -+ uint8_t hash[ONETIMEAUTH_BYTES * 2]; -+ uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; -+ memcpy(auth_key, iv, enc_iv_len); -+ memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); -+ -+ brealloc(buf, ONETIMEAUTH_BYTES + buf->len, capacity); -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ HMAC(EVP_sha1(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, buf->len, (uint8_t *)hash, NULL); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ mbedtls_md_hmac(mbedtls_md_info_from_type( -+ MBEDTLS_MD_SHA1), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, buf->len, -+ (uint8_t *)hash); -+#else -+ sha1_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, buf->len, (uint8_t *)hash); -+#endif -+ -+ memcpy(buf->array + buf->len, hash, ONETIMEAUTH_BYTES); -+ buf->len += ONETIMEAUTH_BYTES; -+ -+ return 0; -+} -+ -+int -+ss_onetimeauth_verify(buffer_t *buf, uint8_t *iv) -+{ -+ uint8_t hash[ONETIMEAUTH_BYTES * 2]; -+ uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; -+ memcpy(auth_key, iv, enc_iv_len); -+ memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); -+ size_t len = buf->len - ONETIMEAUTH_BYTES; -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ HMAC(EVP_sha1(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, len, hash, NULL); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ mbedtls_md_hmac(mbedtls_md_info_from_type( -+ MBEDTLS_MD_SHA1), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, len, hash); -+#else -+ sha1_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, len, hash); -+#endif -+ -+ return safe_memcmp(buf->array + len, hash, ONETIMEAUTH_BYTES); -+} -+ -+int -+ss_encrypt_all(buffer_t *plain, int method, int auth, size_t capacity) -+{ -+ if (method > TABLE) { -+ cipher_ctx_t evp; -+ cipher_context_init(&evp, method, 1); -+ -+ size_t iv_len = enc_iv_len; -+ int err = 1; -+ -+ static buffer_t tmp = { 0, 0, 0, NULL }; -+ brealloc(&tmp, iv_len + plain->len, capacity); -+ buffer_t *cipher = &tmp; -+ cipher->len = plain->len; -+ -+ uint8_t iv[MAX_IV_LENGTH]; -+ -+ rand_bytes(iv, iv_len); -+ cipher_context_set_iv(&evp, iv, iv_len, 1); -+ memcpy(cipher->array, iv, iv_len); -+ -+ if (auth) { -+ ss_onetimeauth(plain, iv, capacity); -+ cipher->len = plain->len; -+ } -+ -+ if (method >= SALSA20) { -+ crypto_stream_xor_ic((uint8_t *)(cipher->array + iv_len), -+ (const uint8_t *)plain->array, (uint64_t)(plain->len), -+ (const uint8_t *)iv, -+ 0, enc_key, method); -+ } else { -+ err = cipher_context_update(&evp, (uint8_t *)(cipher->array + iv_len), -+ &cipher->len, (const uint8_t *)plain->array, -+ plain->len); -+ } -+ -+ if (!err) { -+ bfree(plain); -+ cipher_context_release(&evp); -+ return -1; -+ } -+ -+#ifdef DEBUG -+ dump("PLAIN", plain->array, plain->len); -+ dump("CIPHER", cipher->array + iv_len, cipher->len); -+#endif -+ -+ cipher_context_release(&evp); -+ -+ brealloc(plain, iv_len + cipher->len, capacity); -+ memcpy(plain->array, cipher->array, iv_len + cipher->len); -+ plain->len = iv_len + cipher->len; -+ -+ return 0; -+ } else { -+ char *begin = plain->array; -+ char *ptr = plain->array; -+ while (ptr < begin + plain->len) { -+ *ptr = (char)enc_table[(uint8_t)*ptr]; -+ ptr++; -+ } -+ return 0; -+ } -+} -+ -+int -+ss_encrypt(buffer_t *plain, enc_ctx_t *ctx, size_t capacity) -+{ -+ if (ctx != NULL) { -+ static buffer_t tmp = { 0, 0, 0, NULL }; -+ -+ int err = 1; -+ size_t iv_len = 0; -+ if (!ctx->init) { -+ iv_len = enc_iv_len; -+ } -+ -+ brealloc(&tmp, iv_len + plain->len, capacity); -+ buffer_t *cipher = &tmp; -+ cipher->len = plain->len; -+ -+ if (!ctx->init) { -+ cipher_context_set_iv(&ctx->evp, ctx->evp.iv, iv_len, 1); -+ memcpy(cipher->array, ctx->evp.iv, iv_len); -+ ctx->counter = 0; -+ ctx->init = 1; -+ } -+ -+ if (enc_method >= SALSA20) { -+ int padding = ctx->counter % SODIUM_BLOCK_SIZE; -+ brealloc(cipher, iv_len + (padding + cipher->len) * 2, capacity); -+ if (padding) { -+ brealloc(plain, plain->len + padding, capacity); -+ memmove(plain->array + padding, plain->array, plain->len); -+ sodium_memzero(plain->array, padding); -+ } -+ crypto_stream_xor_ic((uint8_t *)(cipher->array + iv_len), -+ (const uint8_t *)plain->array, -+ (uint64_t)(plain->len + padding), -+ (const uint8_t *)ctx->evp.iv, -+ ctx->counter / SODIUM_BLOCK_SIZE, enc_key, -+ enc_method); -+ ctx->counter += plain->len; -+ if (padding) { -+ memmove(cipher->array + iv_len, -+ cipher->array + iv_len + padding, cipher->len); -+ } -+ } else { -+ err = -+ cipher_context_update(&ctx->evp, -+ (uint8_t *)(cipher->array + iv_len), -+ &cipher->len, (const uint8_t *)plain->array, -+ plain->len); -+ if (!err) { -+ return -1; -+ } -+ } -+ -+#ifdef DEBUG -+ dump("PLAIN", plain->array, plain->len); -+ dump("CIPHER", cipher->array + iv_len, cipher->len); -+#endif -+ -+ brealloc(plain, iv_len + cipher->len, capacity); -+ memcpy(plain->array, cipher->array, iv_len + cipher->len); -+ plain->len = iv_len + cipher->len; -+ -+ return 0; -+ } else { -+ char *begin = plain->array; -+ char *ptr = plain->array; -+ while (ptr < begin + plain->len) { -+ *ptr = (char)enc_table[(uint8_t)*ptr]; -+ ptr++; -+ } -+ return 0; -+ } -+} -+ -+int -+ss_decrypt_all(buffer_t *cipher, int method, int auth, size_t capacity) -+{ -+ if (method > TABLE) { -+ size_t iv_len = enc_iv_len; -+ int ret = 1; -+ -+ if (cipher->len <= iv_len) { -+ return -1; -+ } -+ -+ cipher_ctx_t evp; -+ cipher_context_init(&evp, method, 0); -+ -+ static buffer_t tmp = { 0, 0, 0, NULL }; -+ brealloc(&tmp, cipher->len, capacity); -+ buffer_t *plain = &tmp; -+ plain->len = cipher->len - iv_len; -+ -+ uint8_t iv[MAX_IV_LENGTH]; -+ memcpy(iv, cipher->array, iv_len); -+ cipher_context_set_iv(&evp, iv, iv_len, 0); -+ -+ if (method >= SALSA20) { -+ crypto_stream_xor_ic((uint8_t *)plain->array, -+ (const uint8_t *)(cipher->array + iv_len), -+ (uint64_t)(cipher->len - iv_len), -+ (const uint8_t *)iv, 0, enc_key, method); -+ } else { -+ ret = cipher_context_update(&evp, (uint8_t *)plain->array, &plain->len, -+ (const uint8_t *)(cipher->array + iv_len), -+ cipher->len - iv_len); -+ } -+ -+ if (auth || (plain->array[0] & ONETIMEAUTH_FLAG)) { -+ if (plain->len > ONETIMEAUTH_BYTES) { -+ ret = !ss_onetimeauth_verify(plain, iv); -+ if (ret) { -+ plain->len -= ONETIMEAUTH_BYTES; -+ } -+ } else { -+ ret = 0; -+ } -+ } -+ -+ if (!ret) { -+ bfree(cipher); -+ cipher_context_release(&evp); -+ return -1; -+ } -+ -+#ifdef DEBUG -+ dump("PLAIN", plain->array, plain->len); -+ dump("CIPHER", cipher->array + iv_len, cipher->len - iv_len); -+#endif -+ -+ cipher_context_release(&evp); -+ -+ brealloc(cipher, plain->len, capacity); -+ memcpy(cipher->array, plain->array, plain->len); -+ cipher->len = plain->len; -+ -+ return 0; -+ } else { -+ char *begin = cipher->array; -+ char *ptr = cipher->array; -+ while (ptr < begin + cipher->len) { -+ *ptr = (char)dec_table[(uint8_t)*ptr]; -+ ptr++; -+ } -+ return 0; -+ } -+} -+ -+int -+ss_decrypt(buffer_t *cipher, enc_ctx_t *ctx, size_t capacity) -+{ -+ if (ctx != NULL) { -+ static buffer_t tmp = { 0, 0, 0, NULL }; -+ -+ size_t iv_len = 0; -+ int err = 1; -+ -+ brealloc(&tmp, cipher->len, capacity); -+ buffer_t *plain = &tmp; -+ plain->len = cipher->len; -+ -+ if (!ctx->init) { -+ uint8_t iv[MAX_IV_LENGTH]; -+ iv_len = enc_iv_len; -+ plain->len -= iv_len; -+ -+ memcpy(iv, cipher->array, iv_len); -+ cipher_context_set_iv(&ctx->evp, iv, iv_len, 0); -+ ctx->counter = 0; -+ ctx->init = 1; -+ -+ if (enc_method > RC4) { -+ if (cache_key_exist(iv_cache, (char *)iv, iv_len)) { -+ bfree(cipher); -+ return -1; -+ } else { -+ cache_insert(iv_cache, (char *)iv, iv_len, NULL); -+ } -+ } -+ } -+ -+ if (enc_method >= SALSA20) { -+ int padding = ctx->counter % SODIUM_BLOCK_SIZE; -+ brealloc(plain, (plain->len + padding) * 2, capacity); -+ -+ if (padding) { -+ brealloc(cipher, cipher->len + padding, capacity); -+ memmove(cipher->array + iv_len + padding, cipher->array + iv_len, -+ cipher->len - iv_len); -+ sodium_memzero(cipher->array + iv_len, padding); -+ } -+ crypto_stream_xor_ic((uint8_t *)plain->array, -+ (const uint8_t *)(cipher->array + iv_len), -+ (uint64_t)(cipher->len - iv_len + padding), -+ (const uint8_t *)ctx->evp.iv, -+ ctx->counter / SODIUM_BLOCK_SIZE, enc_key, -+ enc_method); -+ ctx->counter += cipher->len - iv_len; -+ if (padding) { -+ memmove(plain->array, plain->array + padding, plain->len); -+ } -+ } else { -+ err = cipher_context_update(&ctx->evp, (uint8_t *)plain->array, &plain->len, -+ (const uint8_t *)(cipher->array + iv_len), -+ cipher->len - iv_len); -+ } -+ -+ if (!err) { -+ bfree(cipher); -+ return -1; -+ } -+ -+#ifdef DEBUG -+ dump("PLAIN", plain->array, plain->len); -+ dump("CIPHER", cipher->array + iv_len, cipher->len - iv_len); -+#endif -+ -+ brealloc(cipher, plain->len, capacity); -+ memcpy(cipher->array, plain->array, plain->len); -+ cipher->len = plain->len; -+ -+ return 0; -+ } else { -+ char *begin = cipher->array; -+ char *ptr = cipher->array; -+ while (ptr < begin + cipher->len) { -+ *ptr = (char)dec_table[(uint8_t)*ptr]; -+ ptr++; -+ } -+ return 0; -+ } -+} -+ -+void -+enc_ctx_init(int method, enc_ctx_t *ctx, int enc) -+{ -+ sodium_memzero(ctx, sizeof(enc_ctx_t)); -+ cipher_context_init(&ctx->evp, method, enc); -+ -+ if (enc) { -+ rand_bytes(ctx->evp.iv, enc_iv_len); -+ } -+} -+ -+void -+enc_key_init(int method, const char *pass) -+{ -+ if (method <= TABLE || method >= CIPHER_NUM) { -+ LOGE("enc_key_init(): Illegal method"); -+ return; -+ } -+ -+ // Initialize cache -+ cache_create(&iv_cache, 256, NULL); -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ OpenSSL_add_all_algorithms(); -+#else -+ cipher_kt_t cipher_info; -+#endif -+ -+ cipher_t cipher; -+ memset(&cipher, 0, sizeof(cipher_t)); -+ -+ // Initialize sodium for random generator -+ if (sodium_init() == -1) { -+ FATAL("Failed to initialize sodium"); -+ } -+ -+ if (method == SALSA20 || method == CHACHA20 || method == CHACHA20IETF) { -+#if defined(USE_CRYPTO_OPENSSL) -+ cipher.info = NULL; -+ cipher.key_len = supported_ciphers_key_size[method]; -+ cipher.iv_len = supported_ciphers_iv_size[method]; -+#endif -+#if defined(USE_CRYPTO_POLARSSL) -+ cipher.info = &cipher_info; -+ cipher.info->base = NULL; -+ cipher.info->key_length = supported_ciphers_key_size[method] * 8; -+ cipher.info->iv_size = supported_ciphers_iv_size[method]; -+#endif -+#if defined(USE_CRYPTO_MBEDTLS) -+ // XXX: key_length changed to key_bitlen in mbed TLS 2.0.0 -+ cipher.info = &cipher_info; -+ cipher.info->base = NULL; -+ cipher.info->key_bitlen = supported_ciphers_key_size[method] * 8; -+ cipher.info->iv_size = supported_ciphers_iv_size[method]; -+#endif -+ } else { -+ cipher.info = (cipher_kt_t *)get_cipher_type(method); -+ } -+ -+ if (cipher.info == NULL && cipher.key_len == 0) { -+ do { -+#if defined(USE_CRYPTO_POLARSSL) && defined(USE_CRYPTO_APPLECC) -+ if (supported_ciphers_applecc[method] != kCCAlgorithmInvalid) { -+ cipher_info.base = NULL; -+ cipher_info.key_length = supported_ciphers_key_size[method] * 8; -+ cipher_info.iv_size = supported_ciphers_iv_size[method]; -+ cipher.info = (cipher_kt_t *)&cipher_info; -+ break; -+ } -+#endif -+#if defined(USE_CRYPTO_MBEDTLS) && defined(USE_CRYPTO_APPLECC) -+ // XXX: key_length changed to key_bitlen in mbed TLS 2.0.0 -+ if (supported_ciphers_applecc[method] != kCCAlgorithmInvalid) { -+ cipher_info.base = NULL; -+ cipher_info.key_bitlen = supported_ciphers_key_size[method] * 8; -+ cipher_info.iv_size = supported_ciphers_iv_size[method]; -+ cipher.info = (cipher_kt_t *)&cipher_info; -+ break; -+ } -+#endif -+ LOGE("Cipher %s not found in crypto library", supported_ciphers[method]); -+ FATAL("Cannot initialize cipher"); -+ } while (0); -+ } -+ -+ const digest_type_t *md = get_digest_type("MD5"); -+ if (md == NULL) { -+ FATAL("MD5 Digest not found in crypto library"); -+ } -+ -+ enc_key_len = bytes_to_key(&cipher, md, (const uint8_t *)pass, enc_key); -+ -+ if (enc_key_len == 0) { -+ FATAL("Cannot generate key and IV"); -+ } -+ if (method == RC4_MD5 || method == RC4_MD5_6) { -+ enc_iv_len = supported_ciphers_iv_size[method]; -+ } else { -+ enc_iv_len = cipher_iv_size(&cipher); -+ } -+ enc_method = method; -+} -+ -+int -+enc_init(const char *pass, const char *method) -+{ -+ int m = TABLE; -+ if (method != NULL) { -+ for (m = TABLE; m < CIPHER_NUM; m++) -+ if (strcmp(method, supported_ciphers[m]) == 0) { -+ break; -+ } -+ if (m >= CIPHER_NUM) { -+ LOGE("Invalid cipher name: %s, use rc4-md5 instead", method); -+ m = RC4_MD5; -+ } -+ } -+ if (m == TABLE) { -+ enc_table_init(pass); -+ } else { -+ enc_key_init(m, pass); -+ } -+ return m; -+} -+ -+int -+ss_check_hash(buffer_t *buf, chunk_t *chunk, enc_ctx_t *ctx, size_t capacity) -+{ -+ int i, j, k; -+ ssize_t blen = buf->len; -+ uint32_t cidx = chunk->idx; -+ -+ brealloc(chunk->buf, chunk->len + blen, capacity); -+ brealloc(buf, chunk->len + blen, capacity); -+ -+ for (i = 0, j = 0, k = 0; i < blen; i++) { -+ chunk->buf->array[cidx++] = buf->array[k++]; -+ -+ if (cidx == CLEN_BYTES) { -+ uint16_t clen = ntohs(*((uint16_t *)chunk->buf->array)); -+ brealloc(chunk->buf, clen + AUTH_BYTES, capacity); -+ chunk->len = clen; -+ } -+ -+ if (cidx == chunk->len + AUTH_BYTES) { -+ // Compare hash -+ uint8_t hash[ONETIMEAUTH_BYTES * 2]; -+ uint8_t key[MAX_IV_LENGTH + sizeof(uint32_t)]; -+ -+ uint32_t c = htonl(chunk->counter); -+ memcpy(key, ctx->evp.iv, enc_iv_len); -+ memcpy(key + enc_iv_len, &c, sizeof(uint32_t)); -+#if defined(USE_CRYPTO_OPENSSL) -+ HMAC(EVP_sha1(), key, enc_iv_len + sizeof(uint32_t), -+ (uint8_t *)chunk->buf->array + AUTH_BYTES, chunk->len, hash, NULL); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), key, enc_iv_len + sizeof(uint32_t), -+ (uint8_t *)chunk->buf->array + AUTH_BYTES, chunk->len, hash); -+#else -+ sha1_hmac(key, enc_iv_len + sizeof(uint32_t), -+ (uint8_t *)chunk->buf->array + AUTH_BYTES, chunk->len, hash); -+#endif -+ -+ if (safe_memcmp(hash, chunk->buf->array + CLEN_BYTES, ONETIMEAUTH_BYTES) != 0) { -+ return 0; -+ } -+ -+ // Copy chunk back to buffer -+ memmove(buf->array + j + chunk->len, buf->array + k, blen - i - 1); -+ memcpy(buf->array + j, chunk->buf->array + AUTH_BYTES, chunk->len); -+ -+ // Reset the base offset -+ j += chunk->len; -+ k = j; -+ cidx = 0; -+ chunk->counter++; -+ } -+ } -+ -+ buf->len = j; -+ chunk->idx = cidx; -+ return 1; -+} -+ -+int -+ss_gen_hash(buffer_t *buf, uint32_t *counter, enc_ctx_t *ctx, size_t capacity) -+{ -+ ssize_t blen = buf->len; -+ uint16_t chunk_len = htons((uint16_t)blen); -+ uint8_t hash[ONETIMEAUTH_BYTES * 2]; -+ uint8_t key[MAX_IV_LENGTH + sizeof(uint32_t)]; -+ uint32_t c = htonl(*counter); -+ -+ brealloc(buf, AUTH_BYTES + blen, capacity); -+ memcpy(key, ctx->evp.iv, enc_iv_len); -+ memcpy(key + enc_iv_len, &c, sizeof(uint32_t)); -+#if defined(USE_CRYPTO_OPENSSL) -+ HMAC(EVP_sha1(), key, enc_iv_len + sizeof(uint32_t), (uint8_t *)buf->array, blen, hash, NULL); -+#elif defined(USE_CRYPTO_MBEDTLS) -+ mbedtls_md_hmac(mbedtls_md_info_from_type( -+ MBEDTLS_MD_SHA1), key, enc_iv_len + sizeof(uint32_t), (uint8_t *)buf->array, blen, hash); -+#else -+ sha1_hmac(key, enc_iv_len + sizeof(uint32_t), (uint8_t *)buf->array, blen, hash); -+#endif -+ -+ memmove(buf->array + AUTH_BYTES, buf->array, blen); -+ memcpy(buf->array + CLEN_BYTES, hash, ONETIMEAUTH_BYTES); -+ memcpy(buf->array, &chunk_len, CLEN_BYTES); -+ -+ *counter = *counter + 1; -+ buf->len = blen + AUTH_BYTES; -+ -+ return 0; -+} -diff --git a/server/encrypt.h b/server/encrypt.h -new file mode 100644 -index 0000000..3bb7940 ---- /dev/null -+++ b/server/encrypt.h -@@ -0,0 +1,222 @@ -+/* -+ * encrypt.h - Define the enryptor's interface -+ * -+ * Copyright (C) 2013 - 2016, 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 _ENCRYPT_H -+#define _ENCRYPT_H -+ -+#ifndef __MINGW32__ -+#include -+#else -+ -+#ifdef max -+#undef max -+#endif -+ -+#ifdef min -+#undef min -+#endif -+ -+#endif -+ -+#include -+#include -+#include -+#include -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ -+#include -+#include -+#include -+typedef EVP_CIPHER cipher_kt_t; -+typedef EVP_CIPHER_CTX cipher_evp_t; -+typedef EVP_MD digest_type_t; -+#define MAX_KEY_LENGTH EVP_MAX_KEY_LENGTH -+#define MAX_IV_LENGTH EVP_MAX_IV_LENGTH -+#define MAX_MD_SIZE EVP_MAX_MD_SIZE -+ -+#elif defined(USE_CRYPTO_POLARSSL) -+ -+#include -+#include -+typedef cipher_info_t cipher_kt_t; -+typedef cipher_context_t cipher_evp_t; -+typedef md_info_t digest_type_t; -+#define MAX_KEY_LENGTH 64 -+#define MAX_IV_LENGTH POLARSSL_MAX_IV_LENGTH -+#define MAX_MD_SIZE POLARSSL_MD_MAX_SIZE -+ -+#elif defined(USE_CRYPTO_MBEDTLS) -+ -+#include -+#include -+typedef mbedtls_cipher_info_t cipher_kt_t; -+typedef mbedtls_cipher_context_t cipher_evp_t; -+typedef mbedtls_md_info_t digest_type_t; -+#define MAX_KEY_LENGTH 64 -+#define MAX_IV_LENGTH MBEDTLS_MAX_IV_LENGTH -+#define MAX_MD_SIZE MBEDTLS_MD_MAX_SIZE -+ -+/* we must have MBEDTLS_CIPHER_MODE_CFB defined */ -+#if !defined(MBEDTLS_CIPHER_MODE_CFB) -+#error Cipher Feedback mode a.k.a CFB not supported by your mbed TLS. -+#endif -+ -+#endif -+ -+#ifdef USE_CRYPTO_APPLECC -+ -+#include -+ -+#define kCCAlgorithmInvalid UINT32_MAX -+#define kCCContextValid 0 -+#define kCCContextInvalid -1 -+ -+typedef struct { -+ CCCryptorRef cryptor; -+ int valid; -+ CCOperation encrypt; -+ CCAlgorithm cipher; -+ CCMode mode; -+ CCPadding padding; -+ uint8_t iv[MAX_IV_LENGTH]; -+ uint8_t key[MAX_KEY_LENGTH]; -+ size_t iv_len; -+ size_t key_len; -+} cipher_cc_t; -+ -+#endif -+ -+typedef struct { -+ cipher_evp_t *evp; -+#ifdef USE_CRYPTO_APPLECC -+ cipher_cc_t cc; -+#endif -+ uint8_t iv[MAX_IV_LENGTH]; -+} cipher_ctx_t; -+ -+typedef struct { -+ cipher_kt_t *info; -+ size_t iv_len; -+ size_t key_len; -+} cipher_t; -+ -+#ifdef HAVE_STDINT_H -+#include -+#elif HAVE_INTTYPES_H -+#include -+#endif -+ -+#define SODIUM_BLOCK_SIZE 64 -+ -+enum crpher_index { -+ NONE = -1, -+ TABLE = 0, -+ RC4, -+ RC4_MD5_6, -+ RC4_MD5, -+ AES_128_CFB, -+ AES_192_CFB, -+ AES_256_CFB, -+ AES_128_CTR, -+ AES_192_CTR, -+ AES_256_CTR, -+ BF_CFB, -+ CAMELLIA_128_CFB, -+ CAMELLIA_192_CFB, -+ CAMELLIA_256_CFB, -+ CAST5_CFB, -+ DES_CFB, -+ IDEA_CFB, -+ RC2_CFB, -+ SEED_CFB, -+ SALSA20, -+ CHACHA20, -+ CHACHA20IETF, -+ CIPHER_NUM, -+}; -+ -+#define ONETIMEAUTH_FLAG 0x10 -+#define ADDRTYPE_MASK 0xEF -+ -+#define ONETIMEAUTH_BYTES 10U -+#define MD5_BYTES 16U -+#define SHA1_BYTES 20U -+#define CLEN_BYTES 2U -+#define AUTH_BYTES (ONETIMEAUTH_BYTES + CLEN_BYTES) -+ -+#define min(a, b) (((a) < (b)) ? (a) : (b)) -+#define max(a, b) (((a) > (b)) ? (a) : (b)) -+ -+typedef struct buffer { -+ size_t idx; -+ size_t len; -+ size_t capacity; -+ char *array; -+} buffer_t; -+ -+typedef struct chunk { -+ uint32_t idx; -+ uint32_t len; -+ uint32_t counter; -+ buffer_t *buf; -+} chunk_t; -+ -+typedef struct enc_ctx { -+ uint8_t init; -+ uint64_t counter; -+ cipher_ctx_t evp; -+} enc_ctx_t; -+ -+void bytes_to_key_with_size(const char *pass, size_t len, uint8_t *md, size_t md_size); -+ -+int ss_encrypt_all(buffer_t *plaintext, int method, int auth, size_t capacity); -+int ss_decrypt_all(buffer_t *ciphertext, int method, int auth, size_t capacity); -+int ss_encrypt(buffer_t *plaintext, enc_ctx_t *ctx, size_t capacity); -+int ss_decrypt(buffer_t *ciphertext, enc_ctx_t *ctx, size_t capacity); -+ -+void enc_ctx_init(int method, enc_ctx_t *ctx, int enc); -+int enc_init(const char *pass, const char *method); -+int enc_get_iv_len(void); -+uint8_t* enc_get_key(void); -+int enc_get_key_len(void); -+void cipher_context_release(cipher_ctx_t *evp); -+unsigned char *enc_md5(const unsigned char *d, size_t n, unsigned char *md); -+ -+int ss_md5_hmac(char *auth, char *msg, int msg_len, uint8_t *iv); -+int ss_md5_hmac_with_key(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len); -+int ss_md5_hash_func(char *auth, char *msg, int msg_len); -+int ss_sha1_hmac(char *auth, char *msg, int msg_len, uint8_t *iv); -+int ss_sha1_hmac_with_key(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len); -+int ss_sha1_hash_func(char *auth, char *msg, int msg_len); -+int ss_aes_128_cbc(char *encrypt, char *out_data, char *key); -+int ss_onetimeauth(buffer_t *buf, uint8_t *iv, size_t capacity); -+int ss_onetimeauth_verify(buffer_t *buf, uint8_t *iv); -+ -+int ss_check_hash(buffer_t *buf, chunk_t *chunk, enc_ctx_t *ctx, size_t capacity); -+int ss_gen_hash(buffer_t *buf, uint32_t *counter, enc_ctx_t *ctx, size_t capacity); -+ -+int balloc(buffer_t *ptr, size_t capacity); -+int brealloc(buffer_t *ptr, size_t len, size_t capacity); -+void bfree(buffer_t *ptr); -+ -+#endif // _ENCRYPT_H -diff --git a/server/http.c b/server/http.c -new file mode 100644 -index 0000000..3bd4a32 ---- /dev/null -+++ b/server/http.c -@@ -0,0 +1,152 @@ -+/* -+ * Copyright (c) 2011 and 2012, Dustin Lundquist -+ * 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. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include -+#include /* malloc() */ -+#include /* strncpy() */ -+#include /* strncasecmp() */ -+#include /* isblank() */ -+ -+#include "http.h" -+#include "protocol.h" -+ -+#define SERVER_NAME_LEN 256 -+ -+static int parse_http_header(const char *, size_t, char **); -+static int get_header(const char *, const char *, int, char **); -+static int next_header(const char **, int *); -+ -+static const protocol_t http_protocol_st = { -+ .default_port = 80, -+ .parse_packet = &parse_http_header, -+}; -+const protocol_t *const http_protocol = &http_protocol_st; -+ -+/* -+ * Parses a HTTP request for the Host: header -+ * -+ * Returns: -+ * >=0 - length of the hostname and updates *hostname -+ * caller is responsible for freeing *hostname -+ * -1 - Incomplete request -+ * -2 - No Host header included in this request -+ * -3 - Invalid hostname pointer -+ * -4 - malloc failure -+ * < -4 - Invalid HTTP request -+ * -+ */ -+static int -+parse_http_header(const char *data, size_t data_len, char **hostname) -+{ -+ int result, i; -+ -+ if (hostname == NULL) -+ return -3; -+ -+ if (data_len == 0) -+ return -1; -+ -+ result = get_header("Host:", data, data_len, hostname); -+ if (result < 0) -+ return result; -+ -+ /* -+ * if the user specifies the port in the request, it is included here. -+ * Host: example.com:80 -+ * so we trim off port portion -+ */ -+ for (i = result - 1; i >= 0; i--) -+ if ((*hostname)[i] == ':') { -+ (*hostname)[i] = '\0'; -+ result = i; -+ break; -+ } -+ -+ return result; -+} -+ -+static int -+get_header(const char *header, const char *data, int data_len, char **value) -+{ -+ int len, header_len; -+ -+ header_len = strlen(header); -+ -+ /* loop through headers stopping at first blank line */ -+ while ((len = next_header(&data, &data_len)) != 0) -+ if (len > header_len && strncasecmp(header, data, header_len) == 0) { -+ /* Eat leading whitespace */ -+ while (header_len < len && isblank(data[header_len])) -+ header_len++; -+ -+ *value = malloc(len - header_len + 1); -+ if (*value == NULL) -+ return -4; -+ -+ strncpy(*value, data + header_len, len - header_len); -+ (*value)[len - header_len] = '\0'; -+ -+ return len - header_len; -+ } -+ -+ /* If there is no data left after reading all the headers then we do not -+ * have a complete HTTP request, there must be a blank line */ -+ if (data_len == 0) -+ return -1; -+ -+ return -2; -+} -+ -+static int -+next_header(const char **data, int *len) -+{ -+ int header_len; -+ -+ /* perhaps we can optimize this to reuse the value of header_len, rather -+ * than scanning twice. -+ * Walk our data stream until the end of the header */ -+ while (*len > 2 && (*data)[0] != '\r' && (*data)[1] != '\n') { -+ (*len)--; -+ (*data)++; -+ } -+ -+ /* advanced past the pair */ -+ *data += 2; -+ *len -= 2; -+ -+ /* Find the length of the next header */ -+ header_len = 0; -+ while (*len > header_len + 1 -+ && (*data)[header_len] != '\r' -+ && (*data)[header_len + 1] != '\n') -+ header_len++; -+ -+ return header_len; -+} -diff --git a/server/http.h b/server/http.h -new file mode 100644 -index 0000000..914815a ---- /dev/null -+++ b/server/http.h -@@ -0,0 +1,34 @@ -+/* -+ * Copyright (c) 2011 and 2012, Dustin Lundquist -+ * 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. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. -+ */ -+#ifndef HTTP_H -+#define HTTP_H -+ -+#include -+#include "protocol.h" -+ -+const protocol_t *const http_protocol; -+ -+#endif -diff --git a/server/http_simple.c b/server/http_simple.c -new file mode 100644 -index 0000000..c1e34ee ---- /dev/null -+++ b/server/http_simple.c -@@ -0,0 +1,625 @@ -+ -+#include "http_simple.h" -+ -+static char* g_useragent[] = { -+ "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0", -+ "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/44.0", -+ "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36", -+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/27.0.1453.93 Chrome/27.0.1453.93 Safari/537.36", -+ "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0", -+ "Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)", -+ "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", -+ "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)", -+ "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko", -+ "Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36", -+ "Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", -+ "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", -+}; -+ -+static int g_useragent_index = -1; -+ -+typedef struct http_simple_local_data { -+ int has_sent_header; -+ int has_recv_header; -+ char *encode_buffer; -+ int host_matched; -+ char *recv_buffer; -+ int recv_buffer_size; -+}http_simple_local_data; -+ -+void http_simple_local_data_init(http_simple_local_data* local) { -+ local->has_sent_header = 0; -+ local->has_recv_header = 0; -+ local->encode_buffer = NULL; -+ -+ local->recv_buffer = malloc(0); -+ local->recv_buffer_size = 0; -+ -+ local->host_matched = 0; -+ -+ if (g_useragent_index == -1) { -+ g_useragent_index = xorshift128plus() % (sizeof(g_useragent) / sizeof(*g_useragent)); -+ } -+} -+ -+obfs * http_simple_new_obfs() { -+ obfs * self = new_obfs(); -+ self->l_data = malloc(sizeof(http_simple_local_data)); -+ http_simple_local_data_init((http_simple_local_data*)self->l_data); -+ return self; -+} -+ -+void http_simple_dispose(obfs *self) { -+ http_simple_local_data *local = (http_simple_local_data*)self->l_data; -+ if (local->encode_buffer != NULL) { -+ free(local->encode_buffer); -+ local->encode_buffer = NULL; -+ } -+ free(local); -+ dispose_obfs(self); -+} -+ -+char http_simple_hex(char c) { -+ if (c < 10) return c + '0'; -+ return c - 10 + 'a'; -+} -+ -+int get_data_from_http_header(char *data, char **outdata) { -+ char *delim = "\r\n"; -+ char *delim_hex = "%"; -+ int outlength = 0; -+ -+ char *buf = *outdata; -+ char *p_line; -+ p_line = strtok(data, delim); -+ -+ //while(p_line) -+ { -+ char *p_hex; -+ -+ p_hex = strtok(p_line, delim_hex); -+ -+ while((p_hex = strtok(NULL, delim_hex))) -+ { -+ char hex = 0; -+ -+ if(strlen(p_hex) <= 0) -+ { -+ continue; -+ } -+ -+ if(strlen(p_hex) > 2) -+ { -+ char *c_hex = (char*)malloc(2); -+ memcpy(c_hex, p_hex, 2); -+ hex = (char)strtol(c_hex, NULL, 16); -+ free(c_hex); -+ } -+ else -+ { -+ hex = (char)strtol(p_hex, NULL, 16); -+ } -+ -+ outlength += 1; -+ buf = (char*)realloc(buf, outlength); -+ buf[outlength - 1] = hex; -+ } -+ -+ //p_line = strtok(p_line, delim); -+ } -+ return outlength; -+} -+ -+void get_host_from_http_header(char *data, char **host) { -+ char* data_begin = strstr(data, "Host: "); -+ -+ if(data_begin == NULL) -+ { -+ return; -+ } -+ -+ data_begin += 6; -+ char* data_end = strstr(data_begin, "\r\n"); -+ char* data_end_port = strstr(data_begin, ":"); -+ -+ int host_length = 0; -+ -+ if(data_end_port != NULL) -+ { -+ host_length = data_end_port - data_begin; -+ } -+ else -+ { -+ host_length = data_end - data_begin; -+ } -+ -+ if(host_length <= 0) -+ { -+ return; -+ } -+ -+ memset(*host, 0x00, 1024); -+ memcpy(*host, data_begin, host_length); -+} -+ -+void http_simple_encode_head(http_simple_local_data *local, char *data, int datalength) { -+ if (local->encode_buffer == NULL) { -+ local->encode_buffer = (char*)malloc(datalength * 3 + 1); -+ } -+ int pos = 0; -+ for (; pos < datalength; ++pos) { -+ local->encode_buffer[pos * 3] = '%'; -+ local->encode_buffer[pos * 3 + 1] = http_simple_hex(((unsigned char)data[pos] >> 4)); -+ local->encode_buffer[pos * 3 + 2] = http_simple_hex(data[pos] & 0xF); -+ } -+ local->encode_buffer[pos * 3] = 0; -+} -+ -+int http_simple_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { -+ char *encryptdata = *pencryptdata; -+ http_simple_local_data *local = (http_simple_local_data*)self->l_data; -+ if (local->has_sent_header) { -+ return datalength; -+ } -+ char hosts[1024]; -+ char * phost[128]; -+ int host_num = 0; -+ int pos; -+ char hostport[128]; -+ int head_size = self->server.head_len + (xorshift128plus() & 0x3F); -+ int outlength; -+ char * out_buffer = (char*)malloc(datalength + 2048); -+ char * body_buffer = NULL; -+ if (head_size > datalength) -+ head_size = datalength; -+ http_simple_encode_head(local, encryptdata, head_size); -+ if (self->server.param && strlen(self->server.param) == 0) -+ self->server.param = NULL; -+ strncpy(hosts, self->server.param ? self->server.param : self->server.host, sizeof hosts); -+ phost[host_num++] = hosts; -+ for (pos = 0; hosts[pos]; ++pos) { -+ if (hosts[pos] == ',') { -+ phost[host_num++] = &hosts[pos + 1]; -+ hosts[pos] = 0; -+ } else if (hosts[pos] == '#') { -+ char * body_pointer = &hosts[pos + 1]; -+ char * p; -+ int trans_char = 0; -+ p = body_buffer = (char*)malloc(2048); -+ for ( ; *body_pointer; ++body_pointer) { -+ if (*body_pointer == '\\') { -+ trans_char = 1; -+ continue; -+ } else if (*body_pointer == '\n') { -+ *p = '\r'; -+ *++p = '\n'; -+ continue; -+ } -+ if (trans_char) { -+ if (*body_pointer == '\\' ) { -+ *p = '\\'; -+ } else if (*body_pointer == 'n' ) { -+ *p = '\r'; -+ *++p = '\n'; -+ } else { -+ *p = '\\'; -+ *p = *body_pointer; -+ } -+ trans_char = 0; -+ } else { -+ *p = *body_pointer; -+ } -+ ++p; -+ } -+ *p = 0; -+ hosts[pos] = 0; -+ break; -+ } -+ } -+ host_num = xorshift128plus() % host_num; -+ if (self->server.port == 80) -+ sprintf(hostport, "%s", phost[host_num]); -+ else -+ sprintf(hostport, "%s:%d", phost[host_num], self->server.port); -+ if (body_buffer) { -+ sprintf(out_buffer, -+ "GET /%s HTTP/1.1\r\n" -+ "Host: %s\r\n" -+ "%s\r\n\r\n", -+ local->encode_buffer, -+ hostport, -+ body_buffer); -+ } else { -+ sprintf(out_buffer, -+ "GET /%s HTTP/1.1\r\n" -+ "Host: %s\r\n" -+ "User-Agent: %s\r\n" -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" -+ "Accept-Language: en-US,en;q=0.8\r\n" -+ "Accept-Encoding: gzip, deflate\r\n" -+ "DNT: 1\r\n" -+ "Connection: keep-alive\r\n" -+ "\r\n", -+ local->encode_buffer, -+ hostport, -+ g_useragent[g_useragent_index] -+ ); -+ } -+ //LOGI("http header: %s", out_buffer); -+ outlength = strlen(out_buffer); -+ memmove(out_buffer + outlength, encryptdata + head_size, datalength - head_size); -+ outlength += datalength - head_size; -+ local->has_sent_header = 1; -+ if (*capacity < outlength) { -+ *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); -+ encryptdata = *pencryptdata; -+ } -+ memmove(encryptdata, out_buffer, outlength); -+ free(out_buffer); -+ if (body_buffer != NULL) -+ free(body_buffer); -+ if (local->encode_buffer != NULL) { -+ free(local->encode_buffer); -+ local->encode_buffer = NULL; -+ } -+ return outlength; -+} -+ -+int http_simple_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { -+ char *encryptdata = *pencryptdata; -+ http_simple_local_data *local = (http_simple_local_data*)self->l_data; -+ if (local->has_sent_header) { -+ return datalength; -+ } -+ int outlength; -+ char * out_buffer = (char*)malloc(datalength + 2048); -+ -+ time_t now; -+ struct tm *tm_now; -+ char datetime[200]; -+ -+ time(&now); -+ tm_now = localtime(&now); -+ strftime(datetime, 200, "%a, %d %b %Y %H:%M:%S GMT", tm_now); -+ -+ sprintf(out_buffer, -+ "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Encoding: gzip\r\nContent-Type: text/html\r\nDate: " -+ "%s" -+ "\r\nServer: nginx\r\nVary: Accept-Encoding\r\n\r\n", -+ datetime); -+ -+ outlength = strlen(out_buffer); -+ memmove(out_buffer + outlength, encryptdata, datalength); -+ outlength += datalength; -+ -+ local->has_sent_header = 1; -+ if (*capacity < outlength) { -+ *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); -+ encryptdata = *pencryptdata; -+ } -+ memmove(encryptdata, out_buffer, outlength); -+ free(out_buffer); -+ return outlength; -+} -+ -+int http_simple_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) { -+ char *encryptdata = *pencryptdata; -+ http_simple_local_data *local = (http_simple_local_data*)self->l_data; -+ *needsendback = 0; -+ if (local->has_recv_header) { -+ return datalength; -+ } -+ char* data_begin = strstr(encryptdata, "\r\n\r\n"); -+ if (data_begin) { -+ int outlength; -+ data_begin += 4; -+ local->has_recv_header = 1; -+ outlength = datalength - (data_begin - encryptdata); -+ memmove(encryptdata, data_begin, outlength); -+ return outlength; -+ } else { -+ return 0; -+ } -+} -+ -+int http_simple_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) { -+ char *encryptdata = *pencryptdata; -+ http_simple_local_data *local = (http_simple_local_data*)self->l_data; -+ *needsendback = 0; -+ if (local->has_recv_header) { -+ return datalength; -+ } -+ -+ if(datalength != 0) -+ { -+ local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size + datalength); -+ memmove(local->recv_buffer + local->recv_buffer_size, encryptdata, datalength); -+ local->recv_buffer_size += datalength; -+ -+ int outlength = local->recv_buffer_size; -+ if (*capacity < outlength) { -+ *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); -+ encryptdata = *pencryptdata; -+ } -+ memcpy(encryptdata, local->recv_buffer, local->recv_buffer_size); -+ } -+ -+ if(local->recv_buffer_size > 10) -+ { -+ if(strstr(local->recv_buffer, "GET /") == local->recv_buffer || strstr(local->recv_buffer, "POST /") == local->recv_buffer) -+ { -+ if(local->recv_buffer_size > 65536) -+ { -+ free(local->recv_buffer); -+ local->recv_buffer = malloc(0); -+ local->recv_buffer_size = 0; -+ local->has_sent_header = 1; -+ local->has_recv_header = 1; -+ LOGE("http_simple: over size"); -+ return -1; -+ } -+ } -+ else -+ { -+ free(local->recv_buffer); -+ local->recv_buffer = malloc(0); -+ local->recv_buffer_size = 0; -+ local->has_sent_header = 1; -+ local->has_recv_header = 1; -+ LOGE("http_simple: not match begin"); -+ return -1; -+ } -+ } -+ else -+ { -+ LOGE("http_simple: too short"); -+ local->has_sent_header = 1; -+ local->has_recv_header = 1; -+ return -1; -+ } -+ -+ char* data_begin = strstr(encryptdata, "\r\n\r\n"); -+ if (data_begin) { -+ int outlength; -+ char *ret_buf = (char*)malloc(*capacity); -+ memset(ret_buf, 0x00, *capacity); -+ int ret_buf_len = 0; -+ ret_buf_len = get_data_from_http_header(encryptdata, &ret_buf); -+ -+ if (self->server.param && strlen(self->server.param) == 0) -+ { -+ self->server.param = NULL; -+ } -+ else -+ { -+ if(local->host_matched == 0) -+ { -+ char *host = (char*)malloc(1024); -+ get_host_from_http_header(local->recv_buffer, &host); -+ char hosts[1024]; -+ char * phost[128]; -+ int host_num = 0; -+ int pos = 0; -+ int is_match = 0; -+ char * body_buffer = NULL; -+ strncpy(hosts, self->server.param, sizeof hosts); -+ phost[host_num++] = hosts; -+ -+ for (pos = 0; hosts[pos]; ++pos) { -+ if (hosts[pos] == ',') { -+ phost[host_num++] = &hosts[pos + 1]; -+ hosts[pos] = 0; -+ } else if (hosts[pos] == '#') { -+ char * body_pointer = &hosts[pos + 1]; -+ char * p; -+ int trans_char = 0; -+ p = body_buffer = (char*)malloc(2048); -+ for ( ; *body_pointer; ++body_pointer) { -+ if (*body_pointer == '\\') { -+ trans_char = 1; -+ continue; -+ } else if (*body_pointer == '\n') { -+ *p = '\r'; -+ *++p = '\n'; -+ continue; -+ } -+ if (trans_char) { -+ if (*body_pointer == '\\' ) { -+ *p = '\\'; -+ } else if (*body_pointer == 'n' ) { -+ *p = '\r'; -+ *++p = '\n'; -+ } else { -+ *p = '\\'; -+ *p = *body_pointer; -+ } -+ trans_char = 0; -+ } else { -+ *p = *body_pointer; -+ } -+ ++p; -+ } -+ *p = 0; -+ hosts[pos] = 0; -+ break; -+ } -+ } -+ -+ -+ for(pos = 0; pos < host_num; pos++) -+ { -+ if(strcmp(phost[pos], host) == 0) -+ { -+ is_match = 1; -+ local->host_matched = 1; -+ } -+ } -+ -+ if(is_match == 0) -+ { -+ free(local->recv_buffer); -+ local->recv_buffer = malloc(0); -+ local->recv_buffer_size = 0; -+ local->has_sent_header = 1; -+ local->has_recv_header = 1; -+ LOGE("http_simple: not match host, host: %s", host); -+ return -1; -+ } -+ -+ free(host); -+ } -+ } -+ -+ if(ret_buf_len <= 0) -+ { -+ return -1; -+ } -+ -+ data_begin += 4; -+ local->has_recv_header = 1; -+ -+ ret_buf = (char*)realloc(ret_buf, ret_buf_len + datalength - (data_begin - encryptdata)); -+ outlength = ret_buf_len + datalength - (data_begin - encryptdata); -+ -+ memcpy(ret_buf + ret_buf_len, data_begin, datalength - (data_begin - encryptdata)); -+ -+ if (*capacity < outlength) { -+ *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); -+ encryptdata = *pencryptdata; -+ } -+ -+ memcpy(encryptdata, ret_buf, outlength); -+ free(ret_buf); -+ return outlength; -+ } else { -+ return 0; -+ } -+} -+ -+void boundary(char result[]) -+{ -+ char *str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; -+ int i,lstr; -+ char ss[3] = {0}; -+ lstr = strlen(str); -+ srand((unsigned int)time((time_t *)NULL)); -+ for(i = 0; i < 32; ++i) -+ { -+ sprintf(ss, "%c", str[(rand()%lstr)]); -+ strcat(result, ss); -+ } -+} -+ -+int http_post_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { -+ char *encryptdata = *pencryptdata; -+ http_simple_local_data *local = (http_simple_local_data*)self->l_data; -+ if (local->has_sent_header) { -+ return datalength; -+ } -+ char hosts[1024]; -+ char * phost[128]; -+ int host_num = 0; -+ int pos; -+ char hostport[128]; -+ int head_size = self->server.head_len + (xorshift128plus() & 0x3F); -+ int outlength; -+ char * out_buffer = (char*)malloc(datalength + 2048); -+ char * body_buffer = NULL; -+ if (head_size > datalength) -+ head_size = datalength; -+ http_simple_encode_head(local, encryptdata, head_size); -+ if (self->server.param && strlen(self->server.param) == 0) -+ self->server.param = NULL; -+ strncpy(hosts, self->server.param ? self->server.param : self->server.host, sizeof hosts); -+ phost[host_num++] = hosts; -+ for (pos = 0; hosts[pos]; ++pos) { -+ if (hosts[pos] == ',') { -+ phost[host_num++] = &hosts[pos + 1]; -+ hosts[pos] = 0; -+ } else if (hosts[pos] == '#') { -+ char * body_pointer = &hosts[pos + 1]; -+ char * p; -+ int trans_char = 0; -+ p = body_buffer = (char*)malloc(2048); -+ for ( ; *body_pointer; ++body_pointer) { -+ if (*body_pointer == '\\') { -+ trans_char = 1; -+ continue; -+ } else if (*body_pointer == '\n') { -+ *p = '\r'; -+ *++p = '\n'; -+ continue; -+ } -+ if (trans_char) { -+ if (*body_pointer == '\\' ) { -+ *p = '\\'; -+ } else if (*body_pointer == 'n' ) { -+ *p = '\r'; -+ *++p = '\n'; -+ } else { -+ *p = '\\'; -+ *p = *body_pointer; -+ } -+ trans_char = 0; -+ } else { -+ *p = *body_pointer; -+ } -+ ++p; -+ } -+ *p = 0; -+ hosts[pos] = 0; -+ break; -+ } -+ } -+ host_num = xorshift128plus() % host_num; -+ if (self->server.port == 80) -+ sprintf(hostport, "%s", phost[host_num]); -+ else -+ sprintf(hostport, "%s:%d", phost[host_num], self->server.port); -+ if (body_buffer) { -+ sprintf(out_buffer, -+ "POST /%s HTTP/1.1\r\n" -+ "Host: %s\r\n" -+ "%s\r\n\r\n", -+ local->encode_buffer, -+ hostport, -+ body_buffer); -+ } else { -+ char result[33] = {0}; -+ boundary(result); -+ sprintf(out_buffer, -+ "POST /%s HTTP/1.1\r\n" -+ "Host: %s\r\n" -+ "User-Agent: %s\r\n" -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" -+ "Accept-Language: en-US,en;q=0.8\r\n" -+ "Accept-Encoding: gzip, deflate\r\n" -+ "Content-Type: multipart/form-data; boundary=%s\r\n" -+ "DNT: 1\r\n" -+ "Connection: keep-alive\r\n" -+ "\r\n", -+ local->encode_buffer, -+ hostport, -+ g_useragent[g_useragent_index], -+ result -+ ); -+ } -+ //LOGI("http header: %s", out_buffer); -+ outlength = strlen(out_buffer); -+ memmove(out_buffer + outlength, encryptdata + head_size, datalength - head_size); -+ outlength += datalength - head_size; -+ local->has_sent_header = 1; -+ if (*capacity < outlength) { -+ *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); -+ encryptdata = *pencryptdata; -+ } -+ memmove(encryptdata, out_buffer, outlength); -+ free(out_buffer); -+ if (body_buffer != NULL) -+ free(body_buffer); -+ if (local->encode_buffer != NULL) { -+ free(local->encode_buffer); -+ local->encode_buffer = NULL; -+ } -+ return outlength; -+} -diff --git a/server/http_simple.h b/server/http_simple.h -new file mode 100644 -index 0000000..cce24cc ---- /dev/null -+++ b/server/http_simple.h -@@ -0,0 +1,21 @@ -+/* -+ * http_simple.h - Define shadowsocksR server's buffers and callbacks -+ * -+ * Copyright (C) 2015 - 2016, Break Wa11 -+ */ -+ -+#ifndef _HTTP_SIMPLE_H -+#define _HTTP_SIMPLE_H -+ -+obfs * http_simple_new_obfs(); -+void http_simple_dispose(obfs *self); -+ -+int http_simple_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); -+int http_simple_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback); -+ -+int http_post_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); -+ -+int http_simple_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); -+int http_simple_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback); -+ -+#endif // _HTTP_SIMPLE_H -diff --git a/server/jconf.c b/server/jconf.c -new file mode 100644 -index 0000000..494aa5f ---- /dev/null -+++ b/server/jconf.c -@@ -0,0 +1,260 @@ -+/* -+ * jconf.c - Parse the JSON format config file -+ * -+ * Copyright (C) 2013 - 2016, 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 -+ * . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "utils.h" -+#include "jconf.h" -+#include "json.h" -+#include "string.h" -+ -+#include -+ -+#define check_json_value_type(value, expected_type, message) \ -+ do { \ -+ if ((value)->type != (expected_type)) \ -+ FATAL((message)); \ -+ } while(0) -+ -+static char * -+to_string(const json_value *value) -+{ -+ if (value->type == json_string) { -+ return ss_strndup(value->u.string.ptr, value->u.string.length); -+ } else if (value->type == json_integer) { -+ return strdup(ss_itoa(value->u.integer)); -+ } else if (value->type == json_null) { -+ return "null"; -+ } else { -+ LOGE("%d", value->type); -+ FATAL("Invalid config format."); -+ } -+ return 0; -+} -+ -+void -+free_addr(ss_addr_t *addr) -+{ -+ ss_free(addr->host); -+ ss_free(addr->port); -+} -+ -+void -+parse_addr(const char *str, ss_addr_t *addr) -+{ -+ int ipv6 = 0, ret = -1, n = 0; -+ char *pch; -+ -+ struct cork_ip ip; -+ if (cork_ip_init(&ip, str) != -1) { -+ addr->host = strdup(str); -+ addr->port = NULL; -+ return; -+ } -+ -+ pch = strchr(str, ':'); -+ while (pch != NULL) { -+ n++; -+ ret = pch - str; -+ pch = strchr(pch + 1, ':'); -+ } -+ if (n > 1) { -+ ipv6 = 1; -+ if (str[ret - 1] != ']') { -+ ret = -1; -+ } -+ } -+ -+ if (ret == -1) { -+ if (ipv6) { -+ addr->host = ss_strndup(str + 1, strlen(str) - 2); -+ } else { -+ addr->host = strdup(str); -+ } -+ addr->port = NULL; -+ } else { -+ if (ipv6) { -+ addr->host = ss_strndup(str + 1, ret - 2); -+ } else { -+ addr->host = ss_strndup(str, ret); -+ } -+ addr->port = strdup(str + ret + 1); -+ } -+} -+ -+jconf_t * -+read_jconf(const char *file) -+{ -+ static jconf_t conf; -+ -+ memset(&conf, 0, sizeof(jconf_t)); -+ -+ char *buf; -+ json_value *obj; -+ -+ FILE *f = fopen(file, "rb"); -+ if (f == NULL) { -+ FATAL("Invalid config path."); -+ } -+ -+ fseek(f, 0, SEEK_END); -+ long pos = ftell(f); -+ fseek(f, 0, SEEK_SET); -+ -+ if (pos >= MAX_CONF_SIZE) { -+ FATAL("Too large config file."); -+ } -+ -+ buf = ss_malloc(pos + 1); -+ if (buf == NULL) { -+ FATAL("No enough memory."); -+ } -+ -+ int nread = fread(buf, pos, 1, f); -+ if (!nread) { -+ FATAL("Failed to read the config file."); -+ } -+ fclose(f); -+ -+ buf[pos] = '\0'; // end of string -+ -+ json_settings settings = { 0UL, 0, NULL, NULL, NULL }; -+ char error_buf[512]; -+ obj = json_parse_ex(&settings, buf, pos, error_buf); -+ -+ if (obj == NULL) { -+ FATAL(error_buf); -+ } -+ -+ if (obj->type == json_object) { -+ unsigned int i, j; -+ for (i = 0; i < obj->u.object.length; i++) { -+ char *name = obj->u.object.values[i].name; -+ json_value *value = obj->u.object.values[i].value; -+ if (strcmp(name, "server") == 0) { -+ if (value->type == json_array) { -+ for (j = 0; j < value->u.array.length; j++) { -+ if (j >= MAX_REMOTE_NUM) { -+ break; -+ } -+ json_value *v = value->u.array.values[j]; -+ char *addr_str = to_string(v); -+ parse_addr(addr_str, conf.remote_addr + j); -+ ss_free(addr_str); -+ conf.remote_num = j + 1; -+ } -+ } else if (value->type == json_string) { -+ conf.remote_addr[0].host = to_string(value); -+ conf.remote_addr[0].port = NULL; -+ conf.remote_num = 1; -+ } -+ } else if (strcmp(name, "port_password") == 0) { -+ if (value->type == json_object) { -+ for (j = 0; j < value->u.object.length; j++) { -+ if (j >= MAX_PORT_NUM) { -+ break; -+ } -+ json_value *v = value->u.object.values[j].value; -+ if (v->type == json_string) { -+ conf.port_password[j].port = ss_strndup(value->u.object.values[j].name, -+ value->u.object.values[j].name_length); -+ conf.port_password[j].password = to_string(v); -+ conf.port_password_num = j + 1; -+ } -+ } -+ } -+ } else if (strcmp(name, "server_port") == 0) { -+ conf.remote_port = to_string(value); -+ } else if (strcmp(name, "local_address") == 0) { -+ conf.local_addr = to_string(value); -+ } else if (strcmp(name, "local_port") == 0) { -+ conf.local_port = to_string(value); -+ } else if (strcmp(name, "password") == 0) { -+ conf.password = to_string(value); -+ } else if (strcmp(name, "protocol") == 0) { // SSR -+ conf.protocol = to_string(value); -+ } else if (strcmp(name, "protocol_param") == 0) { // SSR -+ conf.protocol_param = to_string(value); -+ } else if (strcmp(name, "method") == 0) { -+ conf.method = to_string(value); -+ } else if (strcmp(name, "obfs") == 0) { // SSR -+ conf.obfs = to_string(value); -+ } else if (strcmp(name, "obfs_param") == 0) { // SSR -+ conf.obfs_param = to_string(value); -+ } else if (strcmp(name, "timeout") == 0) { -+ conf.timeout = to_string(value); -+ } else if (strcmp(name, "user") == 0) { -+ conf.user = to_string(value); -+ } else if (strcmp(name, "fast_open") == 0) { -+ check_json_value_type(value, json_boolean, -+ "invalid config file: option 'fast_open' must be a boolean"); -+ conf.fast_open = value->u.boolean; -+ } else if (strcmp(name, "auth") == 0) { -+ check_json_value_type(value, json_boolean, -+ "invalid config file: option 'auth' must be a boolean"); -+ conf.auth = value->u.boolean; -+ } else if (strcmp(name, "nofile") == 0) { -+ check_json_value_type(value, json_integer, -+ "invalid config file: option 'nofile' must be an integer"); -+ conf.nofile = value->u.integer; -+ } else if (strcmp(name, "nameserver") == 0) { -+ conf.nameserver = to_string(value); -+ } else if (strcmp(name, "tunnel_address") == 0) { -+ conf.tunnel_address = to_string(value); -+ } else if (strcmp(name, "mode") == 0) { -+ char *mode_str = to_string(value); -+ -+ if (strcmp(mode_str, "tcp_only") == 0) -+ conf.mode = TCP_ONLY; -+ else if (strcmp(mode_str, "tcp_and_udp") == 0) -+ conf.mode = TCP_AND_UDP; -+ else if (strcmp(mode_str, "udp_only") == 0) -+ conf.mode = UDP_ONLY; -+ else -+ LOGI("ignore unknown mode: %s, use tcp_only as fallback", -+ mode_str); -+ ss_free(mode_str); -+ } else if (strcmp(name, "mtu") == 0) { -+ check_json_value_type(value, json_integer, -+ "invalid config file: option 'mtu' must be an integer"); -+ conf.mtu = value->u.integer; -+ } else if (strcmp(name, "mptcp") == 0) { -+ check_json_value_type(value, json_boolean, -+ "invalid config file: option 'mptcp' must be a boolean"); -+ conf.mptcp = value->u.boolean; -+ } else if (strcmp(name, "ipv6_first") == 0) { -+ check_json_value_type(value, json_boolean, -+ "invalid config file: option 'ipv6_first' must be a boolean"); -+ conf.ipv6_first = value->u.boolean; -+ } -+ } -+ } else { -+ FATAL("Invalid config file"); -+ } -+ -+ ss_free(buf); -+ json_value_free(obj); -+ return &conf; -+} -diff --git a/server/jconf.h b/server/jconf.h -new file mode 100644 -index 0000000..9a7e5e3 ---- /dev/null -+++ b/server/jconf.h -@@ -0,0 +1,78 @@ -+/* -+ * jconf.h - Define the config data structure -+ * -+ * Copyright (C) 2013 - 2016, 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 _JCONF_H -+#define _JCONF_H -+ -+#define MAX_PORT_NUM 1024 -+#define MAX_REMOTE_NUM 10 -+#define MAX_CONF_SIZE 128 * 1024 -+#define MAX_DNS_NUM 4 -+#define MAX_CONNECT_TIMEOUT 10 -+#define MAX_REQUEST_TIMEOUT 60 -+#define MIN_UDP_TIMEOUT 10 -+ -+#define TCP_ONLY 0 -+#define TCP_AND_UDP 1 -+#define UDP_ONLY 3 -+ -+typedef struct { -+ char *host; -+ char *port; -+} ss_addr_t; -+ -+typedef struct { -+ char *port; -+ char *password; -+} ss_port_password_t; -+ -+typedef struct { -+ int remote_num; -+ ss_addr_t remote_addr[MAX_REMOTE_NUM]; -+ int port_password_num; -+ ss_port_password_t port_password[MAX_PORT_NUM]; -+ char *remote_port; -+ char *local_addr; -+ char *local_port; -+ char *password; -+ char *protocol; // SSR -+ char *protocol_param; // SSR -+ char *method; -+ char *obfs; // SSR -+ char *obfs_param; // SSR -+ char *timeout; -+ char *user; -+ int auth; -+ int fast_open; -+ int nofile; -+ char *nameserver; -+ char *tunnel_address; -+ int mode; -+ int mtu; -+ int mptcp; -+ int ipv6_first; -+} jconf_t; -+ -+jconf_t *read_jconf(const char *file); -+void parse_addr(const char *str, ss_addr_t *addr); -+void free_addr(ss_addr_t *addr); -+ -+#endif // _JCONF_H -diff --git a/server/json.c b/server/json.c -new file mode 100644 -index 0000000..18e95ef ---- /dev/null -+++ b/server/json.c -@@ -0,0 +1,1002 @@ -+/* vim: set et ts=3 sw=3 sts=3 ft=c: -+ * -+ * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. -+ * https://github.com/udp/json-parser -+ * -+ * 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. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. -+ */ -+ -+#include "json.h" -+#include "utils.h" -+ -+#ifdef _MSC_VER -+#ifndef _CRT_SECURE_NO_WARNINGS -+#define _CRT_SECURE_NO_WARNINGS -+#endif -+#endif -+ -+#ifdef __cplusplus -+const struct _json_value json_value_none; /* zero-d by ctor */ -+#else -+const struct _json_value json_value_none = { NULL, 0, { 0 }, { NULL } }; -+#endif -+ -+#include -+#include -+#include -+#include -+ -+typedef unsigned short json_uchar; -+ -+static unsigned char -+hex_value(json_char c) -+{ -+ if (isdigit((uint8_t)c)) { -+ return c - '0'; -+ } -+ -+ switch (c) { -+ case 'a': -+ case 'A': -+ return 0x0A; -+ case 'b': -+ case 'B': -+ return 0x0B; -+ case 'c': -+ case 'C': -+ return 0x0C; -+ case 'd': -+ case 'D': -+ return 0x0D; -+ case 'e': -+ case 'E': -+ return 0x0E; -+ case 'f': -+ case 'F': -+ return 0x0F; -+ default: -+ return 0xFF; -+ } -+} -+ -+typedef struct { -+ unsigned long used_memory; -+ -+ unsigned int uint_max; -+ unsigned long ulong_max; -+ -+ json_settings settings; -+ int first_pass; -+} json_state; -+ -+static void * -+default_alloc(size_t size, int zero, void *user_data) -+{ -+ return zero ? calloc(1, size) : ss_malloc(size); -+} -+ -+static void -+default_free(void *ptr, void *user_data) -+{ -+ ss_free(ptr); -+} -+ -+static void * -+json_alloc(json_state *state, unsigned long size, int zero) -+{ -+ if ((state->ulong_max - state->used_memory) < size) { -+ return 0; -+ } -+ -+ if (state->settings.max_memory -+ && (state->used_memory += size) > state->settings.max_memory) { -+ return 0; -+ } -+ -+ return state->settings.mem_alloc(size, zero, state->settings.user_data); -+} -+ -+static int -+new_value(json_state *state, json_value **top, json_value **root, -+ json_value **alloc, json_type type) -+{ -+ json_value *value; -+ int values_size; -+ -+ if (!state->first_pass) { -+ value = *top = *alloc; -+ *alloc = (*alloc)->_reserved.next_alloc; -+ -+ if (!*root) { -+ *root = value; -+ } -+ -+ switch (value->type) { -+ case json_array: -+ -+ if (!(value->u.array.values = (json_value **)json_alloc -+ (state, value->u.array.length * -+ sizeof(json_value *), 0))) { -+ return 0; -+ } -+ -+ value->u.array.length = 0; -+ break; -+ -+ case json_object: -+ -+ values_size = sizeof(*value->u.object.values) * -+ value->u.object.length; -+ -+ if (!((*(void **)&value->u.object.values) = json_alloc -+ (state, -+ values_size + -+ ((size_t)value->u. -+ object.values), -+ 0))) { -+ return 0; -+ } -+ -+ value->_reserved.object_mem = (*(char **)&value->u.object.values) + -+ values_size; -+ -+ value->u.object.length = 0; -+ break; -+ -+ case json_string: -+ -+ if (!(value->u.string.ptr = (json_char *)json_alloc -+ (state, -+ (value->u.string.length + -+ 1) * sizeof(json_char), 0))) { -+ return 0; -+ } -+ -+ value->u.string.length = 0; -+ break; -+ -+ default: -+ break; -+ } -+ -+ return 1; -+ } -+ -+ value = (json_value *)json_alloc(state, sizeof(json_value), 1); -+ -+ if (!value) { -+ return 0; -+ } -+ -+ if (!*root) { -+ *root = value; -+ } -+ -+ value->type = type; -+ value->parent = *top; -+ -+ if (*alloc) { -+ (*alloc)->_reserved.next_alloc = value; -+ } -+ -+ *alloc = *top = value; -+ -+ return 1; -+} -+ -+#define e_off \ -+ ((int)(i - cur_line_begin)) -+ -+#define whitespace \ -+case '\n': \ -+ ++cur_line; cur_line_begin = i; \ -+case ' ': \ -+case '\t': \ -+case '\r' -+ -+#define string_add(b) \ -+ do { if (!state.first_pass) { string[string_length] = b; \ -+ } ++string_length; } while (0) -+ -+static const long -+ flag_next = 1 << 0, -+ flag_reproc = 1 << 1, -+ flag_need_comma = 1 << 2, -+ flag_seek_value = 1 << 3, -+ flag_escaped = 1 << 4, -+ flag_string = 1 << 5, -+ flag_need_colon = 1 << 6, -+ flag_done = 1 << 7, -+ flag_num_negative = 1 << 8, -+ flag_num_zero = 1 << 9, -+ flag_num_e = 1 << 10, -+ flag_num_e_got_sign = 1 << 11, -+ flag_num_e_negative = 1 << 12, -+ flag_line_comment = 1 << 13, -+ flag_block_comment = 1 << 14; -+ -+json_value * -+json_parse_ex(json_settings *settings, -+ const json_char *json, -+ size_t length, -+ char *error_buf) -+{ -+ json_char error[json_error_max]; -+ int cur_line; -+ const json_char *cur_line_begin, *i, *end; -+ json_value *top, *root, *alloc = 0; -+ json_state state = { 0UL, 0U, 0UL, { 0UL, 0, NULL, NULL, NULL }, 0 }; -+ long flags; -+ long num_digits = 0, num_e = 0; -+ json_int_t num_fraction = 0; -+ -+ /* Skip UTF-8 BOM -+ */ -+ if (length >= 3 && ((unsigned char)json[0]) == 0xEF -+ && ((unsigned char)json[1]) == 0xBB -+ && ((unsigned char)json[2]) == 0xBF) { -+ json += 3; -+ length -= 3; -+ } -+ -+ error[0] = '\0'; -+ end = (json + length); -+ -+ memcpy(&state.settings, settings, sizeof(json_settings)); -+ -+ if (!state.settings.mem_alloc) { -+ state.settings.mem_alloc = default_alloc; -+ } -+ -+ if (!state.settings.mem_free) { -+ state.settings.mem_free = default_free; -+ } -+ -+ memset(&state.uint_max, 0xFF, sizeof(state.uint_max)); -+ memset(&state.ulong_max, 0xFF, sizeof(state.ulong_max)); -+ -+ state.uint_max -= 8; /* limit of how much can be added before next check */ -+ state.ulong_max -= 8; -+ -+ for (state.first_pass = 1; state.first_pass >= 0; --state.first_pass) { -+ json_uchar uchar; -+ unsigned char uc_b1, uc_b2, uc_b3, uc_b4; -+ json_char *string = 0; -+ unsigned int string_length = 0; -+ -+ top = root = 0; -+ flags = flag_seek_value; -+ -+ cur_line = 1; -+ cur_line_begin = json; -+ -+ for (i = json;; ++i) { -+ json_char b = (i == end ? 0 : *i); -+ -+ if (flags & flag_string) { -+ if (!b) { -+ sprintf(error, "Unexpected EOF in string (at %d:%d)", -+ cur_line, e_off); -+ goto e_failed; -+ } -+ -+ if (string_length > state.uint_max) { -+ goto e_overflow; -+ } -+ -+ if (flags & flag_escaped) { -+ flags &= ~flag_escaped; -+ -+ switch (b) { -+ case 'b': -+ string_add('\b'); -+ break; -+ case 'f': -+ string_add('\f'); -+ break; -+ case 'n': -+ string_add('\n'); -+ break; -+ case 'r': -+ string_add('\r'); -+ break; -+ case 't': -+ string_add('\t'); -+ break; -+ case 'u': -+ -+ if (end - i < 4 || -+ (uc_b1 = hex_value(*++i)) == 0xFF || -+ (uc_b2 = hex_value(*++i)) == 0xFF -+ || (uc_b3 = hex_value(*++i)) == 0xFF || -+ (uc_b4 = hex_value(*++i)) == 0xFF) { -+ sprintf(error, -+ "Invalid character value `%c` (at %d:%d)", -+ b, cur_line, e_off); -+ goto e_failed; -+ } -+ -+ uc_b1 = uc_b1 * 16 + uc_b2; -+ uc_b2 = uc_b3 * 16 + uc_b4; -+ -+ uchar = ((json_char)uc_b1) * 256 + uc_b2; -+ -+ if (sizeof(json_char) >= sizeof(json_uchar) || -+ (uc_b1 == 0 && uc_b2 <= 0x7F)) { -+ string_add((json_char)uchar); -+ break; -+ } -+ -+ if (uchar <= 0x7FF) { -+ if (state.first_pass) { -+ string_length += 2; -+ } else { -+ string[string_length++] = 0xC0 | -+ ((uc_b2 & -+ 0xC0) >> -+ 6) | -+ ((uc_b1 & 0x7) << 2); -+ string[string_length++] = 0x80 | -+ (uc_b2 & 0x3F); -+ } -+ -+ break; -+ } -+ -+ if (state.first_pass) { -+ string_length += 3; -+ } else { -+ string[string_length++] = 0xE0 | -+ ((uc_b1 & 0xF0) >> 4); -+ string[string_length++] = 0x80 | -+ ((uc_b1 & -+ 0xF) << -+ 2) | -+ ((uc_b2 & 0xC0) >> 6); -+ string[string_length++] = 0x80 | (uc_b2 & 0x3F); -+ } -+ -+ break; -+ -+ default: -+ string_add(b); -+ } -+ -+ continue; -+ } -+ -+ if (b == '\\') { -+ flags |= flag_escaped; -+ continue; -+ } -+ -+ if (b == '"') { -+ if (!state.first_pass) { -+ string[string_length] = 0; -+ } -+ -+ flags &= ~flag_string; -+ string = 0; -+ -+ switch (top->type) { -+ case json_string: -+ -+ top->u.string.length = string_length; -+ flags |= flag_next; -+ -+ break; -+ -+ case json_object: -+ -+ if (state.first_pass) { -+ (*(json_char **)&top->u.object.values) += -+ string_length + 1; -+ } else { -+ top->u.object.values[top->u.object.length].name -+ = (json_char *)top->_reserved.object_mem; -+ -+ top->u.object.values[top->u.object.length]. -+ name_length -+ = string_length; -+ -+ (*(json_char **)&top->_reserved.object_mem) += -+ string_length + 1; -+ } -+ -+ flags |= flag_seek_value | flag_need_colon; -+ continue; -+ -+ default: -+ break; -+ } -+ } else { -+ string_add(b); -+ continue; -+ } -+ } -+ -+ if (state.settings.settings & json_enable_comments) { -+ if (flags & (flag_line_comment | flag_block_comment)) { -+ if (flags & flag_line_comment) { -+ if (b == '\r' || b == '\n' || !b) { -+ flags &= ~flag_line_comment; -+ --i; /* so null can be reproc'd */ -+ } -+ -+ continue; -+ } -+ -+ if (flags & flag_block_comment) { -+ if (!b) { -+ sprintf(error, -+ "%d:%d: Unexpected EOF in block comment", -+ cur_line, e_off); -+ goto e_failed; -+ } -+ -+ if (b == '*' && i < (end - 1) && i[1] == '/') { -+ flags &= ~flag_block_comment; -+ ++i; /* skip closing sequence */ -+ } -+ -+ continue; -+ } -+ } else if (b == '/') { -+ if (!(flags & (flag_seek_value | flag_done)) && top->type != -+ json_object) { -+ sprintf(error, "%d:%d: Comment not allowed here", -+ cur_line, e_off); -+ goto e_failed; -+ } -+ -+ if (++i == end) { -+ sprintf(error, "%d:%d: EOF unexpected", cur_line, -+ e_off); -+ goto e_failed; -+ } -+ -+ switch (b = *i) { -+ case '/': -+ flags |= flag_line_comment; -+ continue; -+ -+ case '*': -+ flags |= flag_block_comment; -+ continue; -+ -+ default: -+ sprintf(error, -+ "%d:%d: Unexpected `%c` in comment opening sequence", cur_line, e_off, -+ b); -+ goto e_failed; -+ } -+ } -+ } -+ -+ if (flags & flag_done) { -+ if (!b) { -+ break; -+ } -+ -+ switch (b) { -+whitespace: -+ continue; -+ -+ default: -+ sprintf(error, "%d:%d: Trailing garbage: `%c`", cur_line, -+ e_off, b); -+ goto e_failed; -+ } -+ } -+ -+ if (flags & flag_seek_value) { -+ switch (b) { -+whitespace: -+ continue; -+ -+ case ']': -+ -+ if (top->type == json_array) { -+ flags = -+ (flags & -+ ~(flag_need_comma | flag_seek_value)) | flag_next; -+ } else { -+ sprintf(error, "%d:%d: Unexpected ]", cur_line, e_off); -+ goto e_failed; -+ } -+ -+ break; -+ -+ default: -+ -+ if (flags & flag_need_comma) { -+ if (b == ',') { -+ flags &= ~flag_need_comma; -+ continue; -+ } else { -+ sprintf(error, "%d:%d: Expected , before %c", -+ cur_line, e_off, b); -+ goto e_failed; -+ } -+ } -+ -+ if (flags & flag_need_colon) { -+ if (b == ':') { -+ flags &= ~flag_need_colon; -+ continue; -+ } else { -+ sprintf(error, "%d:%d: Expected : before %c", -+ cur_line, e_off, b); -+ goto e_failed; -+ } -+ } -+ -+ flags &= ~flag_seek_value; -+ -+ switch (b) { -+ case '{': -+ -+ if (!new_value(&state, &top, &root, &alloc, -+ json_object)) { -+ goto e_alloc_failure; -+ } -+ -+ continue; -+ -+ case '[': -+ -+ if (!new_value(&state, &top, &root, &alloc, -+ json_array)) { -+ goto e_alloc_failure; -+ } -+ -+ flags |= flag_seek_value; -+ continue; -+ -+ case '"': -+ -+ if (!new_value(&state, &top, &root, &alloc, -+ json_string)) { -+ goto e_alloc_failure; -+ } -+ -+ flags |= flag_string; -+ -+ string = top->u.string.ptr; -+ string_length = 0; -+ -+ continue; -+ -+ case 't': -+ -+ if ((end - i) < 3 || *(++i) != 'r' || *(++i) != 'u' || -+ *(++i) != 'e') { -+ goto e_unknown_value; -+ } -+ -+ if (!new_value(&state, &top, &root, &alloc, -+ json_boolean)) { -+ goto e_alloc_failure; -+ } -+ -+ top->u.boolean = 1; -+ -+ flags |= flag_next; -+ break; -+ -+ case 'f': -+ -+ if ((end - i) < 4 || *(++i) != 'a' || *(++i) != 'l' || -+ *(++i) != 's' || *(++i) != 'e') { -+ goto e_unknown_value; -+ } -+ -+ if (!new_value(&state, &top, &root, &alloc, -+ json_boolean)) { -+ goto e_alloc_failure; -+ } -+ -+ flags |= flag_next; -+ break; -+ -+ case 'n': -+ -+ if ((end - i) < 3 || *(++i) != 'u' || *(++i) != 'l' || -+ *(++i) != 'l') { -+ goto e_unknown_value; -+ } -+ -+ if (!new_value(&state, &top, &root, &alloc, -+ json_null)) { -+ goto e_alloc_failure; -+ } -+ -+ flags |= flag_next; -+ break; -+ -+ default: -+ -+ if (isdigit((uint8_t)b) || b == '-') { -+ if (!new_value(&state, &top, &root, &alloc, -+ json_integer)) { -+ goto e_alloc_failure; -+ } -+ -+ if (!state.first_pass) { -+ while (isdigit((uint8_t)b) || b == '+' || b == -+ '-' -+ || b == 'e' || b == 'E' || b == '.') { -+ if ((++i) == end) { -+ b = 0; -+ break; -+ } -+ -+ b = *i; -+ } -+ -+ flags |= flag_next | flag_reproc; -+ break; -+ } -+ -+ flags &= ~(flag_num_negative | flag_num_e | -+ flag_num_e_got_sign | -+ flag_num_e_negative | -+ flag_num_zero); -+ -+ num_digits = 0; -+ num_fraction = 0; -+ num_e = 0; -+ -+ if (b != '-') { -+ flags |= flag_reproc; -+ break; -+ } -+ -+ flags |= flag_num_negative; -+ continue; -+ } else { -+ sprintf(error, -+ "%d:%d: Unexpected %c when seeking value", -+ cur_line, e_off, b); -+ goto e_failed; -+ } -+ } -+ } -+ } else { -+ switch (top->type) { -+ case json_object: -+ -+ switch (b) { -+whitespace: -+ continue; -+ -+ case '"': -+ -+ if (flags & flag_need_comma) { -+ sprintf(error, "%d:%d: Expected , before \"", -+ cur_line, e_off); -+ goto e_failed; -+ } -+ -+ flags |= flag_string; -+ -+ string = (json_char *)top->_reserved.object_mem; -+ string_length = 0; -+ -+ break; -+ -+ case '}': -+ -+ flags = (flags & ~flag_need_comma) | flag_next; -+ break; -+ -+ case ',': -+ -+ if (flags & flag_need_comma) { -+ flags &= ~flag_need_comma; -+ break; -+ } -+ -+ default: -+ -+ sprintf(error, "%d:%d: Unexpected `%c` in object", -+ cur_line, e_off, b); -+ goto e_failed; -+ } -+ -+ break; -+ -+ case json_integer: -+ case json_double: -+ -+ if (isdigit((uint8_t)b)) { -+ ++num_digits; -+ -+ if (top->type == json_integer || flags & flag_num_e) { -+ if (!(flags & flag_num_e)) { -+ if (flags & flag_num_zero) { -+ sprintf(error, -+ "%d:%d: Unexpected `0` before `%c`", -+ cur_line, e_off, b); -+ goto e_failed; -+ } -+ -+ if (num_digits == 1 && b == '0') { -+ flags |= flag_num_zero; -+ } -+ } else { -+ flags |= flag_num_e_got_sign; -+ num_e = (num_e * 10) + (b - '0'); -+ continue; -+ } -+ -+ top->u.integer = (top->u.integer * 10) + (b - '0'); -+ continue; -+ } -+ -+ num_fraction = (num_fraction * 10) + (b - '0'); -+ continue; -+ } -+ -+ if (b == '+' || b == '-') { -+ if ((flags & flag_num_e) && -+ !(flags & flag_num_e_got_sign)) { -+ flags |= flag_num_e_got_sign; -+ -+ if (b == '-') { -+ flags |= flag_num_e_negative; -+ } -+ -+ continue; -+ } -+ } else if (b == '.' && top->type == json_integer) { -+ if (!num_digits) { -+ sprintf(error, "%d:%d: Expected digit before `.`", -+ cur_line, e_off); -+ goto e_failed; -+ } -+ -+ top->type = json_double; -+ top->u.dbl = (double)top->u.integer; -+ -+ num_digits = 0; -+ continue; -+ } -+ -+ if (!(flags & flag_num_e)) { -+ if (top->type == json_double) { -+ if (!num_digits) { -+ sprintf(error, -+ "%d:%d: Expected digit after `.`", -+ cur_line, e_off); -+ goto e_failed; -+ } -+ -+ top->u.dbl += ((double)num_fraction) / -+ (pow(10, (double)num_digits)); -+ } -+ -+ if (b == 'e' || b == 'E') { -+ flags |= flag_num_e; -+ -+ if (top->type == json_integer) { -+ top->type = json_double; -+ top->u.dbl = (double)top->u.integer; -+ } -+ -+ num_digits = 0; -+ flags &= ~flag_num_zero; -+ -+ continue; -+ } -+ } else { -+ if (!num_digits) { -+ sprintf(error, "%d:%d: Expected digit after `e`", -+ cur_line, e_off); -+ goto e_failed; -+ } -+ -+ top->u.dbl *= -+ pow(10, -+ (double)((flags & -+ flag_num_e_negative) ? -num_e : num_e)); -+ } -+ -+ if (flags & flag_num_negative) { -+ if (top->type == json_integer) { -+ top->u.integer = -top->u.integer; -+ } else { -+ top->u.dbl = -top->u.dbl; -+ } -+ } -+ -+ flags |= flag_next | flag_reproc; -+ break; -+ -+ default: -+ break; -+ } -+ } -+ -+ if (flags & flag_reproc) { -+ flags &= ~flag_reproc; -+ --i; -+ } -+ -+ if (flags & flag_next) { -+ flags = (flags & ~flag_next) | flag_need_comma; -+ -+ if (!top->parent) { -+ /* root value done */ -+ -+ flags |= flag_done; -+ continue; -+ } -+ -+ if (top->parent->type == json_array) { -+ flags |= flag_seek_value; -+ } -+ -+ if (!state.first_pass) { -+ json_value *parent = top->parent; -+ -+ switch (parent->type) { -+ case json_object: -+ -+ parent->u.object.values -+ [parent->u.object.length].value = top; -+ -+ break; -+ -+ case json_array: -+ -+ parent->u.array.values -+ [parent->u.array.length] = top; -+ -+ break; -+ -+ default: -+ break; -+ } -+ } -+ -+ if ((++top->parent->u.array.length) > state.uint_max) { -+ goto e_overflow; -+ } -+ -+ top = top->parent; -+ -+ continue; -+ } -+ } -+ -+ alloc = root; -+ } -+ -+ return root; -+ -+e_unknown_value: -+ -+ sprintf(error, "%d:%d: Unknown value", cur_line, e_off); -+ goto e_failed; -+ -+e_alloc_failure: -+ -+ strcpy(error, "Memory allocation failure"); -+ goto e_failed; -+ -+e_overflow: -+ -+ sprintf(error, "%d:%d: Too long (caught overflow)", cur_line, e_off); -+ goto e_failed; -+ -+e_failed: -+ -+ if (error_buf) { -+ if (*error) { -+ strcpy(error_buf, error); -+ } else { -+ strcpy(error_buf, "Unknown error"); -+ } -+ } -+ -+ if (state.first_pass) { -+ alloc = root; -+ } -+ -+ while (alloc) { -+ top = alloc->_reserved.next_alloc; -+ state.settings.mem_free(alloc, state.settings.user_data); -+ alloc = top; -+ } -+ -+ if (!state.first_pass) { -+ json_value_free_ex(&state.settings, root); -+ } -+ -+ return 0; -+} -+ -+json_value * -+json_parse(const json_char *json, size_t length) -+{ -+ json_settings settings = { 0UL, 0, NULL, NULL, NULL }; -+ return json_parse_ex(&settings, json, length, 0); -+} -+ -+void -+json_value_free_ex(json_settings *settings, json_value *value) -+{ -+ json_value *cur_value; -+ -+ if (!value) { -+ return; -+ } -+ -+ value->parent = 0; -+ -+ while (value) { -+ switch (value->type) { -+ case json_array: -+ -+ if (!value->u.array.length) { -+ settings->mem_free(value->u.array.values, settings->user_data); -+ break; -+ } -+ -+ value = value->u.array.values[--value->u.array.length]; -+ continue; -+ -+ case json_object: -+ -+ if (!value->u.object.length) { -+ settings->mem_free(value->u.object.values, settings->user_data); -+ break; -+ } -+ -+ value = value->u.object.values[--value->u.object.length].value; -+ continue; -+ -+ case json_string: -+ -+ settings->mem_free(value->u.string.ptr, settings->user_data); -+ break; -+ -+ default: -+ break; -+ } -+ -+ cur_value = value; -+ value = value->parent; -+ settings->mem_free(cur_value, settings->user_data); -+ } -+} -+ -+void -+json_value_free(json_value *value) -+{ -+ json_settings settings = { 0UL, 0, NULL, NULL, NULL }; -+ settings.mem_free = default_free; -+ json_value_free_ex(&settings, value); -+} -diff --git a/server/json.h b/server/json.h -new file mode 100644 -index 0000000..016fc5a ---- /dev/null -+++ b/server/json.h -@@ -0,0 +1,249 @@ -+/* vim: set et ts=3 sw=3 sts=3 ft=c: -+ * -+ * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. -+ * https://github.com/udp/json-parser -+ * -+ * 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. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. -+ */ -+ -+#ifndef _JSON_H -+#define _JSON_H -+ -+#ifndef json_char -+#define json_char char -+#endif -+ -+#ifndef json_int_t -+#ifndef _MSC_VER -+#include -+#define json_int_t int64_t -+#else -+#define json_int_t __int64 -+#endif -+#endif -+ -+#include -+ -+#ifdef __cplusplus -+ -+#include -+ -+extern "C" -+{ -+#endif -+ -+typedef struct { -+ unsigned long max_memory; -+ int settings; -+ -+ /* Custom allocator support (leave null to use malloc/free) -+ */ -+ -+ void * (*mem_alloc)(size_t, int zero, void *user_data); -+ void (*mem_free)(void *, void *user_data); -+ -+ void *user_data; /* will be passed to mem_alloc and mem_free */ -+} json_settings; -+ -+#define json_enable_comments 0x01 -+ -+typedef enum { -+ json_none, -+ json_object, -+ json_array, -+ json_integer, -+ json_double, -+ json_string, -+ json_boolean, -+ json_null -+} json_type; -+ -+extern const struct _json_value json_value_none; -+ -+typedef struct _json_value { -+ struct _json_value *parent; -+ -+ json_type type; -+ -+ union { -+ int boolean; -+ json_int_t integer; -+ double dbl; -+ -+ struct { -+ unsigned int length; -+ json_char *ptr; /* null terminated */ -+ } string; -+ -+ struct { -+ unsigned int length; -+ -+ struct { -+ json_char *name; -+ unsigned int name_length; -+ -+ struct _json_value *value; -+ } *values; -+ -+#if defined(__cplusplus) && __cplusplus >= 201103L -+ decltype(values) begin() const -+ { -+ return values; -+ } -+ decltype(values) end() const -+ { -+ return values + length; -+ } -+#endif -+ } object; -+ -+ struct { -+ unsigned int length; -+ struct _json_value **values; -+ -+#if defined(__cplusplus) && __cplusplus >= 201103L -+ decltype(values) begin() const -+ { -+ return values; -+ } -+ decltype(values) end() const -+ { -+ return values + length; -+ } -+#endif -+ } array; -+ } u; -+ -+ union { -+ struct _json_value *next_alloc; -+ void *object_mem; -+ } _reserved; -+ -+ /* Some C++ operator sugar */ -+ -+#ifdef __cplusplus -+ -+public: -+ -+ inline _json_value(){ -+ memset(this, 0, sizeof(_json_value)); -+ } -+ -+ inline const struct _json_value &operator [] (int index) const { -+ if (type != json_array || index < 0 -+ || ((unsigned int)index) >= u.array.length) { -+ return json_value_none; -+ } -+ -+ return *u.array.values[index]; -+ } -+ -+ inline const struct _json_value &operator [] (const char *index) const { -+ if (type != json_object) { -+ return json_value_none; -+ } -+ -+ for (unsigned int i = 0; i < u.object.length; ++i) -+ if (!strcmp(u.object.values[i].name, index)) { -+ return *u.object.values[i].value; -+ } -+ -+ return json_value_none; -+ } -+ -+ inline operator const char * () const -+ { -+ switch (type) { -+ case json_string: -+ return u.string.ptr; -+ -+ default: -+ return ""; -+ } -+ } -+ -+ inline operator -+ json_int_t() const -+ { -+ switch (type) { -+ case json_integer: -+ return u.integer; -+ -+ case json_double: -+ return (json_int_t)u.dbl; -+ -+ default: -+ return 0; -+ } -+ } -+ -+ inline operator -+ bool() const -+ { -+ if (type != json_boolean) { -+ return false; -+ } -+ -+ return u.boolean != 0; -+ } -+ -+ inline operator double () const -+ { -+ switch (type) { -+ case json_integer: -+ return (double)u.integer; -+ -+ case json_double: -+ return u.dbl; -+ -+ default: -+ return 0; -+ } -+ } -+ -+#endif -+} json_value; -+ -+json_value *json_parse(const json_char *json, -+ size_t length); -+ -+#define json_error_max 128 -+json_value *json_parse_ex(json_settings *settings, -+ const json_char *json, -+ size_t length, -+ char *error); -+ -+void json_value_free(json_value *); -+ -+/* Not usually necessary, unless you used a custom mem_alloc and now want to -+ * use a custom mem_free. -+ */ -+void json_value_free_ex(json_settings *settings, -+ json_value *); -+ -+#ifdef __cplusplus -+} /* extern "C" */ -+#endif -+ -+#endif -diff --git a/server/list.c b/server/list.c -new file mode 100644 -index 0000000..dde085d ---- /dev/null -+++ b/server/list.c -@@ -0,0 +1,370 @@ -+#include "list.h" -+ -+/// 文件:list_impl.c -+/// 功能:实现链表的基本操作 -+/// 作者:bluewind -+/// 完成时间:2011.5.29 -+/// 修改时间:2011.5.31, 2011.7.2 -+/// 修改备注:在头节点处添加一个空节点,可以优化添加、删除节点代码 -+/// 再次修改,链表增加节点数据data_size,限制数据大小,修改了 -+/// 添加复制数据代码,修正重复添加节点后释放节点的Bug,添加了前 -+/// 插、排序和遍历功能,7.3 添加tail尾指针,改进后插法性能,并改名 -+/// -------------------------------------------------------------- -+ -+void swap_data(Node n1, Node n2); -+ -+/// -------------------------------------------------------------- -+// 函数名:list_init -+// 功能: 链表初始化 -+// 参数: 无 -+// 返回值:已初始化链表指针 -+// 备注: 链表本身动态分配,由list_destroy函数管理释放 -+/// -------------------------------------------------------------- -+List list_init(unsigned int data_size) -+{ -+ List list = (List) malloc(sizeof(struct clist)); -+ if(list != NULL) //内存分配成功 -+ { -+ list->head = (Node) malloc(sizeof(node)); //为头节点分配内存 -+ if(list->head) //内存分配成功 -+ { -+ list->head->data = NULL; //初始化头节点 -+ list->head->next = NULL; -+ list->data_size = data_size; -+ list->tail = list->head; -+ list->size = 0; -+ -+ list->add_back = list_add_back; //初始化成员函数 -+ list->add_front = list_add_front; -+ list->delete_node = list_delete_node; -+ list->delete_at = list_delete_at; -+ list->modify_at = list_modify_at; -+ list->have_same = list_have_same; -+ list->have_same_cmp = list_have_same_cmp; -+ list->foreach = list_foreach; -+ list->clear = list_clear; -+ list->sort = list_sort; -+ list->destroy = list_destroy; -+ } -+ } -+ return list; -+} -+ -+/// -------------------------------------------------------------- -+// 函数名:list_add_back -+// 功能: 添加链表结点 (后插法) -+// 参数: l--链表指针,data--链表数据指针,可为任意类型 -+// 返回值:int型,为1表示添加成功,为0表示添加失败 -+// 备注: 如果链表本身为空或是分配节点内存失败,将返回0 -+/// -------------------------------------------------------------- -+int list_add_back(List l, void *data) -+{ -+ Node new_node = (Node) malloc(sizeof(node)); -+ -+ if(l != NULL && new_node != NULL) //链表本身不为空,且内存申请成功 -+ { -+ new_node->data = malloc(l->data_size); -+ memcpy(new_node->data, data, l->data_size); -+ new_node->next = NULL; -+ -+ l->tail->next = new_node; //添加节点 -+ l->tail = new_node; //记录尾节点位置 -+ l->size ++; //链表元素总数加1 -+ -+ return 1; -+ } -+ -+ return 0; -+} -+ -+/// -------------------------------------------------------------- -+// 函数名:list_add_front -+// 功能: 添加链表结点 (前插法) -+// 参数: l--链表指针,data--链表数据指针,可为任意类型 -+// 返回值:int型,为1表示添加成功,为0表示添加失败 -+// 备注: 如果链表本身为空或是分配节点内存失败,将返回0 -+/// -------------------------------------------------------------- -+int list_add_front(List l, void *data) -+{ -+ Node new_node = (Node) malloc(sizeof(node)); -+ -+ if(l != NULL && new_node != NULL) -+ { -+ new_node->data = malloc(l->data_size); -+ memcpy(new_node->data, data, l->data_size); -+ new_node->next = l->head->next; -+ -+ l->head->next = new_node; -+ if(!l->size) //记录尾指针位置 -+ l->tail = new_node; -+ l->size ++; -+ -+ return 1; -+ } -+ -+ return 0; -+} -+ -+/// -------------------------------------------------------------- -+// 函数名:list_delete_node -+// 功能:删除链表结点 -+// 参数:l--链表指针,data--链表数据指针,可为任意类型 -+// *pfunc为指向一个数据类型比较的函数指针 -+// 返回值:int型,为1表示删除成功,为0表示没有找到匹配数据 -+// 备注:*pfunc函数接口参数ndata为节点数据,data为比较数据,返回为真表示匹配数据 -+/// -------------------------------------------------------------- -+int list_delete_node(List l, void *data, int (*pfunc)(void *ndata, void *data)) -+{ -+ if(l != NULL) -+ { -+ Node prev = l->head; //前一个节点 -+ Node curr = l->head->next; //当前节点 -+ -+ while(curr != NULL) -+ { -+ if(pfunc(curr->data, data)) //如果找到匹配数据 -+ { -+ if(curr == l->tail) //如果是删除尾节点 -+ l->tail = prev; -+ -+ prev->next = prev->next->next; //修改前节点next指针指向下下个节点 -+ -+ free(curr->data); //释放节点数据 -+ free(curr); //释放节点 -+ -+ l->size--; //链表元素总数减1 -+ return 1; //返回真值 -+ } -+ prev = prev->next; //没有找到匹配时移动前节点和当前节点 -+ curr = curr->next; -+ } -+ } -+ -+ return 0; //没有找到匹配数据 -+} -+ -+/// -------------------------------------------------------------- -+// 函数名:list_delete_at -+// 功能: 修改链表节点元素值 -+// 参数: l--链表指针,index--索引值, 范围(0 -- size-1) -+// 返回值:int型,为1表示删除成功,为0表示删除失败 -+// 备注: 如果链表本身为空或是index为非法值,将返回0 -+/// -------------------------------------------------------------- -+int list_delete_at(List l, unsigned int index) -+{ -+ unsigned int cindex = 0; -+ -+ if(l != NULL && index >= 0 && index < l->size) -+ { -+ Node prev = l->head; //前一个节点 -+ Node curr = l->head->next; //当前节点 -+ -+ while(cindex != index) -+ { -+ prev = prev->next; -+ curr = curr->next; -+ cindex ++; -+ } -+ -+ if(index == (l->size) - 1) -+ l->tail = prev; -+ -+ prev->next = prev->next->next; -+ free(curr->data); -+ free(curr); -+ l->size --; -+ -+ return 1; -+ } -+ -+ return 0; -+} -+ -+/// -------------------------------------------------------------- -+// 函数名:list_modify_at -+// 功能: 修改链表节点元素值 -+// 参数: l--链表指针,index--索引值, 范围(0 -- size-1) -+// data--链表数据指针 -+// 返回值:int型,为1表示修改成功,为0表示修改失败 -+// 备注: 如果链表本身为空或是index为非法值,将返回0 -+/// -------------------------------------------------------------- -+int list_modify_at(List l, unsigned int index, void *new_data) -+{ -+ unsigned int cindex = 0; -+ -+ if(l != NULL && index >= 0 && index < l->size ) //非空链表,并且index值合法 -+ { -+ Node curr = l->head->next; -+ while(cindex != index) -+ { -+ curr = curr->next; -+ cindex ++; -+ } -+ memcpy(curr->data, new_data, l->data_size); -+ return 1; -+ } -+ -+ return 0; -+} -+ -+/// -------------------------------------------------------------- -+// 函数名:list_sort -+// 功能: 链表排序 -+// 参数: l--链表指针,*pfunc为指向一个数据类型比较的函数指针 -+// 返回值:无 -+// 备注: 使用简单选择排序法,相比冒泡法每次交换,效率高一点 -+/// -------------------------------------------------------------- -+void list_sort(List l, compare pfunc) -+{ -+ if(l != NULL) -+ { -+ Node min, icurr, jcurr; -+ -+ icurr = l->head->next; -+ while(icurr) -+ { -+ min = icurr; //记录最小值 -+ jcurr = icurr->next; //内循环指向下一个节点 -+ while(jcurr) -+ { -+ if(pfunc(min->data, jcurr->data)) //如果找到n+1到最后一个元素最小值 -+ min = jcurr; //记录下最小值的位置 -+ -+ jcurr = jcurr->next; -+ } -+ -+ if(min != icurr) //当最小值位置和n+1元素位置不相同时 -+ { -+ swap_data(min, icurr); //才进行交换,减少交换次数 -+ } -+ -+ icurr = icurr->next; -+ } -+ } -+} -+ -+void swap_data(Node n1, Node n2) -+{ -+ void *temp; -+ -+ temp = n2->data; -+ n2->data = n1->data; -+ n1->data = temp; -+} -+ -+ -+int list_have_same(List l, void *data, int (*pfunc)(void *ndata, void *data)) -+{ -+ if(l != NULL) -+ { -+ Node curr; -+ -+ for(curr = l->head->next; curr != NULL; curr = curr->next) -+ { -+ if(pfunc(curr->data, data)) -+ { -+ return 1; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+int list_have_same_cmp(List l, void *data) -+{ -+ if(l != NULL) -+ { -+ Node curr; -+ -+ for(curr = l->head->next; curr != NULL; curr = curr->next) -+ { -+ if(memcmp(curr->data, data, l->data_size)) -+ { -+ return 1; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+/// -------------------------------------------------------------- -+// 函数名:list_foreach -+// 功能: 遍历链表元素 -+// 参数: l--链表指针,doit为指向一个处理数据的函数指针 -+// 返回值:无 -+// 备注: doit申明为void (*dofunc)(void *ndata)原型 -+/// -------------------------------------------------------------- -+void list_foreach(List l, dofunc doit) -+{ -+ if(l != NULL) -+ { -+ Node curr; -+ -+ for(curr = l->head->next; curr != NULL; curr = curr->next) -+ { -+ doit(curr->data); -+ } -+ } -+} -+ -+/// -------------------------------------------------------------- -+// 函数名:list_clear -+// 功能: 清空链表元素 -+// 参数: l--链表指针 -+// 返回值:无 -+// 备注: 没有使用先Destroy再Init链表的办法,直接实现 -+/// -------------------------------------------------------------- -+void list_clear(List l) -+{ -+ if(l != NULL) -+ { -+ Node temp; -+ Node curr = l->head->next; -+ -+ while(curr != NULL) -+ { -+ temp = curr->next; -+ -+ free(curr->data); //释放节点和数据 -+ free(curr); -+ -+ curr = temp; -+ } -+ -+ l->size = 0; //重置链表数据 -+ l->head->next = NULL; -+ l->tail = l->head; -+ } -+} -+ -+/// -------------------------------------------------------------- -+// 函数名:list_destroy -+// 功能: 释放链表 -+// 参数: l--链表指针 -+// 返回值:空链表指针 -+/// -------------------------------------------------------------- -+List list_destroy(List l) -+{ -+ if(l != NULL) -+ { -+ Node temp; -+ -+ while(l->head) -+ { -+ temp = l->head->next; -+ -+ if(l->head->data != NULL) //如果是头节点就不释放数据空间 -+ free(l->head->data); //先释放节点数据(但是节点数据里也有指针?) -+ free(l->head); //再释放节点 -+ -+ l->head = temp; -+ } -+ -+ free(l); //释放链表本身占用空间 -+ l = NULL; -+ } -+ -+ return l; -+} -diff --git a/server/list.h b/server/list.h -new file mode 100644 -index 0000000..ab49720 ---- /dev/null -+++ b/server/list.h -@@ -0,0 +1,61 @@ -+#ifndef LIST_H_H -+#define LIST_H_H -+ -+#include -+#include -+#include -+ -+typedef struct clist *List; -+ -+typedef int (*compare)(void *ndata, void *data); -+typedef void (*dofunc)(void *ndata); -+ -+typedef int (*lpf0)(List l, void *data); -+typedef int (*lpf1)(List l, void *data, compare pfunc); -+typedef List (*lpf2)(List l); -+typedef void (*lpf3)(List l); -+typedef void (*lpf4)(List l, dofunc pfunc); -+typedef int (*lpf5)(List l, unsigned int index, void *new_data); -+typedef void (*lpf6)(List l, compare pfunc); -+typedef int (*lpf7)(List l, unsigned int index); -+ -+typedef struct cnode -+{ -+ void *data; -+ struct cnode *next; -+}node, *Node; -+ -+typedef struct clist -+{ -+ Node head; -+ Node tail; -+ unsigned int size; -+ unsigned int data_size; -+ lpf0 add_back; -+ lpf0 add_front; -+ lpf1 delete_node; -+ lpf1 have_same; -+ lpf0 have_same_cmp; -+ lpf4 foreach; -+ lpf3 clear; -+ lpf2 destroy; -+ lpf5 modify_at; -+ lpf6 sort; -+ lpf7 delete_at; -+}list; -+ -+//初始化链表 -+List list_init(unsigned int data_size); -+int list_add_back(List l, void *data); -+int list_add_front(List l, void *data); -+int list_delete_node(List l, void *data, compare pfunc); -+int list_delete_at(List l, unsigned int index); -+int list_modify_at(List l, unsigned int index, void *new_data); -+int list_have_same(List l, void *data, compare pfunc); -+int list_have_same_cmp(List l, void *data); -+void list_foreach(List l, dofunc doit); -+void list_sort(List l, compare pfunc); -+void list_clear(List l); -+//释放链表 -+List list_destroy(List l); -+#endif -diff --git a/server/netutils.c b/server/netutils.c -new file mode 100644 -index 0000000..3a32b4d ---- /dev/null -+++ b/server/netutils.c -@@ -0,0 +1,297 @@ -+/* -+ * netutils.c - Network utilities -+ * -+ * Copyright (C) 2013 - 2016, 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 -+ * . -+ */ -+ -+#include -+ -+#include -+#include -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#ifdef __MINGW32__ -+#include "win32.h" -+#define sleep(n) Sleep(1000 * (n)) -+#else -+#include -+#include -+#include -+#include -+#endif -+ -+#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__) -+#include -+#include -+#define SET_INTERFACE -+#endif -+ -+#include "netutils.h" -+#include "utils.h" -+ -+#ifndef SO_REUSEPORT -+#define SO_REUSEPORT 15 -+#endif -+ -+extern int verbose; -+ -+static const char valid_label_bytes[] = -+ "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; -+ -+#if defined(MODULE_LOCAL) -+extern int keep_resolving; -+#endif -+ -+int -+set_reuseport(int socket) -+{ -+ int opt = 1; -+ return setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); -+} -+ -+size_t -+get_sockaddr_len(struct sockaddr *addr) -+{ -+ if (addr->sa_family == AF_INET) { -+ return sizeof(struct sockaddr_in); -+ } else if (addr->sa_family == AF_INET6) { -+ return sizeof(struct sockaddr_in6); -+ } -+ return 0; -+} -+ -+#ifdef SET_INTERFACE -+int -+setinterface(int socket_fd, const char *interface_name) -+{ -+ struct ifreq interface; -+ memset(&interface, 0, sizeof(struct ifreq)); -+ strncpy(interface.ifr_name, interface_name, IFNAMSIZ); -+ int res = setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &interface, -+ sizeof(struct ifreq)); -+ return res; -+} -+ -+#endif -+ -+int -+bind_to_address(int socket_fd, const char *host) -+{ -+ if (host != NULL) { -+ struct cork_ip ip; -+ struct sockaddr_storage storage; -+ memset(&storage, 0, sizeof(struct sockaddr_storage)); -+ if (cork_ip_init(&ip, host) != -1) { -+ if (ip.version == 4) { -+ struct sockaddr_in *addr = (struct sockaddr_in *)&storage; -+ dns_pton(AF_INET, host, &addr->sin_addr); -+ addr->sin_family = AF_INET; -+ return bind(socket_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); -+ } else if (ip.version == 6) { -+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; -+ dns_pton(AF_INET6, host, &addr->sin6_addr); -+ addr->sin6_family = AF_INET6; -+ return bind(socket_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in6)); -+ } -+ } -+ } -+ return -1; -+} -+ -+ssize_t -+get_sockaddr(char *host, char *port, -+ struct sockaddr_storage *storage, int block, -+ int ipv6first) -+{ -+ struct cork_ip ip; -+ if (cork_ip_init(&ip, host) != -1) { -+ if (ip.version == 4) { -+ struct sockaddr_in *addr = (struct sockaddr_in *)storage; -+ addr->sin_family = AF_INET; -+ dns_pton(AF_INET, host, &(addr->sin_addr)); -+ if (port != NULL) { -+ addr->sin_port = htons(atoi(port)); -+ } -+ } else if (ip.version == 6) { -+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; -+ addr->sin6_family = AF_INET6; -+ dns_pton(AF_INET6, host, &(addr->sin6_addr)); -+ if (port != NULL) { -+ addr->sin6_port = htons(atoi(port)); -+ } -+ } -+ return 0; -+ } else { -+ struct addrinfo hints; -+ struct addrinfo *result, *rp; -+ -+ memset(&hints, 0, sizeof(struct addrinfo)); -+ hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ -+ hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */ -+ -+ int err, i; -+ -+ for (i = 1; i < 8; i++) { -+ err = getaddrinfo(host, port, &hints, &result); -+#if defined(MODULE_LOCAL) -+ if (!keep_resolving) -+ break; -+#endif -+ if ((!block || !err)) { -+ break; -+ } else { -+ sleep(pow(2, i)); -+ LOGE("failed to resolve server name, wait %.0f seconds", pow(2, i)); -+ } -+ } -+ -+ if (err != 0) { -+ LOGE("getaddrinfo: %s", gai_strerror(err)); -+ return -1; -+ } -+ -+ int prefer_af = ipv6first ? AF_INET6 : AF_INET; -+ for (rp = result; rp != NULL; rp = rp->ai_next) -+ if (rp->ai_family == prefer_af) { -+ if (rp->ai_family == AF_INET) -+ memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in)); -+ else if (rp->ai_family == AF_INET6) -+ memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in6)); -+ break; -+ } -+ -+ if (rp == NULL) { -+ for (rp = result; rp != NULL; rp = rp->ai_next) { -+ if (rp->ai_family == AF_INET) -+ memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in)); -+ else if (rp->ai_family == AF_INET6) -+ memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in6)); -+ break; -+ } -+ } -+ -+ if (rp == NULL) { -+ LOGE("failed to resolve remote addr"); -+ return -1; -+ } -+ -+ freeaddrinfo(result); -+ return 0; -+ } -+ -+ return -1; -+} -+ -+int -+sockaddr_cmp(struct sockaddr_storage *addr1, -+ struct sockaddr_storage *addr2, socklen_t len) -+{ -+ struct sockaddr_in *p1_in = (struct sockaddr_in *)addr1; -+ struct sockaddr_in *p2_in = (struct sockaddr_in *)addr2; -+ struct sockaddr_in6 *p1_in6 = (struct sockaddr_in6 *)addr1; -+ struct sockaddr_in6 *p2_in6 = (struct sockaddr_in6 *)addr2; -+ if (p1_in->sin_family < p2_in->sin_family) -+ return -1; -+ if (p1_in->sin_family > p2_in->sin_family) -+ return 1; -+ /* compare ip4 */ -+ if (p1_in->sin_family == AF_INET) { -+ /* just order it, ntohs not required */ -+ if (p1_in->sin_port < p2_in->sin_port) -+ return -1; -+ if (p1_in->sin_port > p2_in->sin_port) -+ return 1; -+ return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, INET_SIZE); -+ } else if (p1_in6->sin6_family == AF_INET6) { -+ /* just order it, ntohs not required */ -+ if (p1_in6->sin6_port < p2_in6->sin6_port) -+ return -1; -+ if (p1_in6->sin6_port > p2_in6->sin6_port) -+ return 1; -+ return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr, -+ INET6_SIZE); -+ } else { -+ /* eek unknown type, perform this comparison for sanity. */ -+ return memcmp(addr1, addr2, len); -+ } -+} -+ -+int -+sockaddr_cmp_addr(struct sockaddr_storage *addr1, -+ struct sockaddr_storage *addr2, socklen_t len) -+{ -+ struct sockaddr_in *p1_in = (struct sockaddr_in *)addr1; -+ struct sockaddr_in *p2_in = (struct sockaddr_in *)addr2; -+ struct sockaddr_in6 *p1_in6 = (struct sockaddr_in6 *)addr1; -+ struct sockaddr_in6 *p2_in6 = (struct sockaddr_in6 *)addr2; -+ if (p1_in->sin_family < p2_in->sin_family) -+ return -1; -+ if (p1_in->sin_family > p2_in->sin_family) -+ return 1; -+ /* compare ip4 */ -+ if (p1_in->sin_family == AF_INET) { -+ return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, INET_SIZE); -+ } else if (p1_in6->sin6_family == AF_INET6) { -+ return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr, -+ INET6_SIZE); -+ } else { -+ /* eek unknown type, perform this comparison for sanity. */ -+ return memcmp(addr1, addr2, len); -+ } -+} -+ -+int -+validate_hostname(const char *hostname, const int hostname_len) -+{ -+ if (hostname == NULL) -+ return 0; -+ -+ if (hostname_len < 1 || hostname_len > 255) -+ return 0; -+ -+ if (hostname[0] == '.') -+ return 0; -+ -+ const char *label = hostname; -+ while (label < hostname + hostname_len) { -+ size_t label_len = hostname_len - (label - hostname); -+ char *next_dot = strchr(label, '.'); -+ if (next_dot != NULL) -+ label_len = next_dot - label; -+ -+ if (label + label_len > hostname + hostname_len) -+ return 0; -+ -+ if (label_len > 63 || label_len < 1) -+ return 0; -+ -+ if (label[0] == '-' || label[label_len - 1] == '-') -+ return 0; -+ -+ if (strspn(label, valid_label_bytes) < label_len) -+ return 0; -+ -+ label += label_len + 1; -+ } -+ -+ return 1; -+} -diff --git a/server/netutils.h b/server/netutils.h -new file mode 100644 -index 0000000..0725592 ---- /dev/null -+++ b/server/netutils.h -@@ -0,0 +1,98 @@ -+/* -+ * netutils.h - Network utilities -+ * -+ * Copyright (C) 2013 - 2016, 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 _NETUTILS_H -+#define _NETUTILS_H -+ -+#if defined(__linux__) -+#include -+#elif !defined(__MINGW32__) -+#include -+#endif -+ -+// only enable TCP_FASTOPEN on linux -+#if defined(__linux__) -+#include -+/* conditional define for TCP_FASTOPEN */ -+#ifndef TCP_FASTOPEN -+#define TCP_FASTOPEN 23 -+#endif -+/* conditional define for MSG_FASTOPEN */ -+#ifndef MSG_FASTOPEN -+#define MSG_FASTOPEN 0x20000000 -+#endif -+#elif !defined(__APPLE__) -+#ifdef TCP_FASTOPEN -+#undef TCP_FASTOPEN -+#endif -+#endif -+ -+/* Backward compatibility for MPTCP_ENABLED between kernel 3 & 4 */ -+#ifndef MPTCP_ENABLED -+#ifdef TCP_CC_INFO -+#define MPTCP_ENABLED 42 -+#else -+#define MPTCP_ENABLED 26 -+#endif -+#endif -+ -+/** byte size of ip4 address */ -+#define INET_SIZE 4 -+/** byte size of ip6 address */ -+#define INET6_SIZE 16 -+ -+size_t get_sockaddr_len(struct sockaddr *addr); -+ssize_t get_sockaddr(char *host, char *port, -+ struct sockaddr_storage *storage, int block, -+ int ipv6first); -+int set_reuseport(int socket); -+ -+#ifdef SET_INTERFACE -+int setinterface(int socket_fd, const char *interface_name); -+#endif -+ -+int bind_to_address(int socket_fd, const char *address); -+ -+/** -+ * Compare two sockaddrs. Imposes an ordering on the addresses. -+ * Compares address and port. -+ * @param addr1: address 1. -+ * @param addr2: address 2. -+ * @param len: lengths of addr. -+ * @return: 0 if addr1 == addr2. -1 if addr1 is smaller, +1 if larger. -+ */ -+int sockaddr_cmp(struct sockaddr_storage *addr1, -+ struct sockaddr_storage *addr2, socklen_t len); -+ -+/** -+ * Compare two sockaddrs. Compares address, not the port. -+ * @param addr1: address 1. -+ * @param addr2: address 2. -+ * @param len: lengths of addr. -+ * @return: 0 if addr1 == addr2. -1 if addr1 is smaller, +1 if larger. -+ */ -+int sockaddr_cmp_addr(struct sockaddr_storage *addr1, -+ struct sockaddr_storage *addr2, socklen_t len); -+ -+int validate_hostname(const char *hostname, const int hostname_len); -+ -+#endif -diff --git a/server/obfs.c b/server/obfs.c -new file mode 100644 -index 0000000..5c885bf ---- /dev/null -+++ b/server/obfs.c -@@ -0,0 +1,205 @@ -+#include -+#include -+ -+#include "utils.h" -+#include "obfs.h" -+ -+int rand_bytes(uint8_t *output, int len); -+#define OBFS_HMAC_SHA1_LEN 10 -+ -+#include "obfsutil.c" -+#include "crc32.c" -+#include "base64.c" -+#include "http_simple.c" -+#include "tls1.2_ticket.c" -+#include "verify.c" -+#include "auth.c" -+ -+void * init_data() { -+ return malloc(1); -+} -+ -+obfs * new_obfs() { -+ obfs * self = (obfs*)malloc(sizeof(obfs)); -+ self->l_data = NULL; -+ return self; -+} -+ -+void set_server_info(obfs *self, server_info *server) { -+ memmove(&self->server, server, sizeof(server_info)); -+} -+ -+void get_server_info(obfs *self, server_info *server) { -+ memmove(server, &self->server, sizeof(server_info)); -+} -+ -+void dispose_obfs(obfs *self) { -+ free(self); -+} -+ -+obfs_class * new_obfs_class(char *plugin_name) -+{ -+ if (plugin_name == NULL) -+ return NULL; -+ if (strcmp(plugin_name, "origin") == 0) -+ return NULL; -+ if (strcmp(plugin_name, "plain") == 0) -+ return NULL; -+ init_crc32_table(); -+ init_shift128plus(); -+ if (strcmp(plugin_name, "http_simple") == 0) { -+ obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); -+ plugin->init_data = init_data; -+ plugin->new_obfs = http_simple_new_obfs; -+ plugin->get_server_info = get_server_info; -+ plugin->set_server_info = set_server_info; -+ plugin->dispose = http_simple_dispose; -+ -+ plugin->client_encode = http_simple_client_encode; -+ plugin->client_decode = http_simple_client_decode; -+ -+ plugin->server_encode = http_simple_server_encode; -+ plugin->server_decode = http_simple_server_decode; -+ -+ return plugin; -+ } else if (strcmp(plugin_name, "http_post") == 0) { -+ obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); -+ plugin->init_data = init_data; -+ plugin->new_obfs = http_simple_new_obfs; -+ plugin->get_server_info = get_server_info; -+ plugin->set_server_info = set_server_info; -+ plugin->dispose = http_simple_dispose; -+ -+ plugin->client_encode = http_post_client_encode; -+ plugin->client_decode = http_simple_client_decode; -+ -+ plugin->server_encode = http_simple_server_encode; -+ plugin->server_decode = http_simple_server_decode; -+ -+ return plugin; -+ } else if (strcmp(plugin_name, "tls1.2_ticket_auth") == 0) { -+ obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); -+ plugin->init_data = tls12_ticket_auth_init_data; -+ plugin->new_obfs = tls12_ticket_auth_new_obfs; -+ plugin->get_server_info = get_server_info; -+ plugin->set_server_info = set_server_info; -+ plugin->dispose = tls12_ticket_auth_dispose; -+ -+ plugin->client_encode = tls12_ticket_auth_client_encode; -+ plugin->client_decode = tls12_ticket_auth_client_decode; -+ -+ plugin->server_encode = tls12_ticket_auth_server_encode; -+ plugin->server_decode = tls12_ticket_auth_server_decode; -+ -+ return plugin; -+ } else if (strcmp(plugin_name, "verify_simple") == 0) { -+ obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); -+ plugin->init_data = init_data; -+ plugin->new_obfs = verify_simple_new_obfs; -+ plugin->get_server_info = get_server_info; -+ plugin->set_server_info = set_server_info; -+ plugin->dispose = verify_simple_dispose; -+ -+ plugin->client_pre_encrypt = verify_simple_client_pre_encrypt; -+ plugin->client_post_decrypt = verify_simple_client_post_decrypt; -+ plugin->client_udp_pre_encrypt = NULL; -+ plugin->client_udp_post_decrypt = NULL; -+ -+ plugin->server_pre_encrypt = verify_simple_server_pre_encrypt; -+ plugin->server_post_decrypt = verify_simple_server_post_decrypt; -+ plugin->server_udp_pre_encrypt = NULL; -+ plugin->server_udp_post_decrypt = NULL; -+ -+ return plugin; -+ } else if (strcmp(plugin_name, "auth_simple") == 0) { -+ obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); -+ plugin->init_data = auth_simple_init_data; -+ plugin->new_obfs = auth_simple_new_obfs; -+ plugin->get_server_info = get_server_info; -+ plugin->set_server_info = set_server_info; -+ plugin->dispose = auth_simple_dispose; -+ -+ plugin->client_pre_encrypt = auth_simple_client_pre_encrypt; -+ plugin->client_post_decrypt = auth_simple_client_post_decrypt; -+ plugin->client_udp_pre_encrypt = NULL; -+ plugin->client_udp_post_decrypt = NULL; -+ -+ return plugin; -+ } else if (strcmp(plugin_name, "auth_sha1") == 0) { -+ obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); -+ plugin->init_data = auth_simple_init_data; -+ plugin->new_obfs = auth_simple_new_obfs; -+ plugin->get_server_info = get_server_info; -+ plugin->set_server_info = set_server_info; -+ plugin->dispose = auth_simple_dispose; -+ -+ plugin->client_pre_encrypt = auth_sha1_client_pre_encrypt; -+ plugin->client_post_decrypt = auth_sha1_client_post_decrypt; -+ plugin->client_udp_pre_encrypt = NULL; -+ plugin->client_udp_post_decrypt = NULL; -+ -+ return plugin; -+ } else if (strcmp(plugin_name, "auth_sha1_v2") == 0) { -+ obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); -+ plugin->init_data = auth_simple_init_data; -+ plugin->new_obfs = auth_simple_new_obfs; -+ plugin->get_server_info = get_server_info; -+ plugin->set_server_info = set_server_info; -+ plugin->dispose = auth_simple_dispose; -+ -+ plugin->client_pre_encrypt = auth_sha1_v2_client_pre_encrypt; -+ plugin->client_post_decrypt = auth_sha1_v2_client_post_decrypt; -+ plugin->client_udp_pre_encrypt = NULL; -+ plugin->client_udp_post_decrypt = NULL; -+ -+ return plugin; -+ } else if (strcmp(plugin_name, "auth_sha1_v4") == 0) { -+ obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); -+ plugin->init_data = auth_simple_init_data; -+ plugin->new_obfs = auth_simple_new_obfs; -+ plugin->get_server_info = get_server_info; -+ plugin->set_server_info = set_server_info; -+ plugin->dispose = auth_simple_dispose; -+ -+ plugin->client_pre_encrypt = auth_sha1_v4_client_pre_encrypt; -+ plugin->client_post_decrypt = auth_sha1_v4_client_post_decrypt; -+ plugin->client_udp_pre_encrypt = NULL; -+ plugin->client_udp_post_decrypt = NULL; -+ -+ return plugin; -+ } else if (strcmp(plugin_name, "auth_aes128_md5") == 0) { -+ obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); -+ plugin->init_data = auth_simple_init_data; -+ plugin->new_obfs = auth_aes128_md5_new_obfs; -+ plugin->get_server_info = get_server_info; -+ plugin->set_server_info = set_server_info; -+ plugin->dispose = auth_simple_dispose; -+ -+ plugin->client_pre_encrypt = auth_aes128_sha1_client_pre_encrypt; -+ plugin->client_post_decrypt = auth_aes128_sha1_client_post_decrypt; -+ plugin->client_udp_pre_encrypt = auth_aes128_sha1_client_udp_pre_encrypt; -+ plugin->client_udp_post_decrypt = auth_aes128_sha1_client_udp_post_decrypt; -+ -+ return plugin; -+ } else if (strcmp(plugin_name, "auth_aes128_sha1") == 0) { -+ obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); -+ plugin->init_data = auth_simple_init_data; -+ plugin->new_obfs = auth_aes128_sha1_new_obfs; -+ plugin->get_server_info = get_server_info; -+ plugin->set_server_info = set_server_info; -+ plugin->dispose = auth_simple_dispose; -+ -+ plugin->client_pre_encrypt = auth_aes128_sha1_client_pre_encrypt; -+ plugin->client_post_decrypt = auth_aes128_sha1_client_post_decrypt; -+ plugin->client_udp_pre_encrypt = auth_aes128_sha1_client_udp_pre_encrypt; -+ plugin->client_udp_post_decrypt = auth_aes128_sha1_client_udp_post_decrypt; -+ -+ return plugin; -+ } -+ LOGE("Load obfs '%s' failed", plugin_name); -+ return NULL; -+} -+ -+void free_obfs_class(obfs_class *plugin) { -+ free(plugin); -+} -diff --git a/server/obfs.h b/server/obfs.h -new file mode 100644 -index 0000000..74c60c9 ---- /dev/null -+++ b/server/obfs.h -@@ -0,0 +1,100 @@ -+/* -+ * obfs.h - Define shadowsocksR server's buffers and callbacks -+ * -+ * Copyright (C) 2015 - 2016, Break Wa11 -+ */ -+ -+#ifndef _OBFS_H -+#define _OBFS_H -+ -+#include -+#include -+ -+typedef struct server_info { -+ char host[64]; -+ uint16_t port; -+ char *param; -+ void *g_data; -+ uint8_t *iv; -+ size_t iv_len; -+ uint8_t *recv_iv; -+ size_t recv_iv_len; -+ uint8_t *key; -+ size_t key_len; -+ int head_len; -+ size_t tcp_mss; -+}server_info; -+ -+typedef struct obfs { -+ server_info server; -+ void *l_data; -+}obfs; -+ -+typedef struct obfs_class { -+ void * (*init_data)(); -+ obfs * (*new_obfs)(); -+ void (*get_server_info)(obfs *self, server_info *server); -+ void (*set_server_info)(obfs *self, server_info *server); -+ void (*dispose)(obfs *self); -+ -+ int (*client_pre_encrypt)(obfs *self, -+ char **pplaindata, -+ int datalength, -+ size_t* capacity); -+ int (*client_encode)(obfs *self, -+ char **pencryptdata, -+ int datalength, -+ size_t* capacity); -+ int (*client_decode)(obfs *self, -+ char **pencryptdata, -+ int datalength, -+ size_t* capacity, -+ int *needsendback); -+ int (*client_post_decrypt)(obfs *self, -+ char **pplaindata, -+ int datalength, -+ size_t* capacity); -+ int (*client_udp_pre_encrypt)(obfs *self, -+ char **pplaindata, -+ int datalength, -+ size_t* capacity); -+ int (*client_udp_post_decrypt)(obfs *self, -+ char **pplaindata, -+ int datalength, -+ size_t* capacity); -+ int (*server_pre_encrypt)(obfs *self, -+ char **pplaindata, -+ int datalength, -+ size_t* capacity); -+ int (*server_post_decrypt)(obfs *self, -+ char **pplaindata, -+ int datalength, -+ size_t* capacity); -+ int (*server_udp_pre_encrypt)(obfs *self, -+ char **pplaindata, -+ int datalength, -+ size_t* capacity); -+ int (*server_udp_post_decrypt)(obfs *self, -+ char **pplaindata, -+ int datalength, -+ size_t* capacity); -+ int (*server_encode)(obfs *self, -+ char **pencryptdata, -+ int datalength, -+ size_t* capacity); -+ int (*server_decode)(obfs *self, -+ char **pencryptdata, -+ int datalength, -+ size_t* capacity, -+ int *needsendback); -+}obfs_class; -+ -+obfs_class * new_obfs_class(char *plugin_name); -+void free_obfs_class(obfs_class *plugin); -+ -+void set_server_info(obfs *self, server_info *server); -+void get_server_info(obfs *self, server_info *server); -+obfs * new_obfs(); -+void dispose_obfs(obfs *self); -+ -+#endif // _OBFS_H -diff --git a/server/obfsutil.c b/server/obfsutil.c -new file mode 100644 -index 0000000..d00959b ---- /dev/null -+++ b/server/obfsutil.c -@@ -0,0 +1,36 @@ -+int get_head_size(char *plaindata, int size, int def_size) { -+ if (plaindata == NULL || size < 2) -+ return def_size; -+ int head_type = plaindata[0] & 0x7; -+ if (head_type == 1) -+ return 7; -+ if (head_type == 4) -+ return 19; -+ if (head_type == 3) -+ return 4 + plaindata[1]; -+ return def_size; -+} -+ -+static int shift128plus_init_flag = 0; -+static uint64_t shift128plus_s[2] = {0x10000000, 0xFFFFFFFF}; -+ -+void init_shift128plus(void) { -+ if (shift128plus_init_flag == 0) { -+ shift128plus_init_flag = 1; -+ uint32_t seed = time(NULL); -+ shift128plus_s[0] = seed | 0x100000000L; -+ shift128plus_s[1] = ((uint64_t)seed << 32) | 0x1; -+ } -+} -+ -+uint64_t xorshift128plus(void) { -+ uint64_t x = shift128plus_s[0]; -+ uint64_t const y = shift128plus_s[1]; -+ shift128plus_s[0] = y; -+ x ^= x << 23; // a -+ x ^= x >> 17; // b -+ x ^= y ^ (y >> 26); // c -+ shift128plus_s[1] = x; -+ return x + y; -+} -+ -diff --git a/server/protocol.h b/server/protocol.h -new file mode 100644 -index 0000000..eaa866e ---- /dev/null -+++ b/server/protocol.h -@@ -0,0 +1,34 @@ -+/* -+ * Copyright (c) 2014, Dustin Lundquist -+ * 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. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. -+ */ -+#ifndef PROTOCOL_H -+#define PROTOCOL_H -+ -+typedef struct protocol { -+ const int default_port; -+ int(*const parse_packet)(const char *, size_t, char **); -+} protocol_t; -+ -+#endif -diff --git a/server/resolv.c b/server/resolv.c -new file mode 100644 -index 0000000..f580d06 ---- /dev/null -+++ b/server/resolv.c -@@ -0,0 +1,444 @@ -+/* -+ * Copyright (c) 2014, Dustin Lundquist -+ * 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. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifdef __MINGW32__ -+#include "win32.h" -+#else -+#include -+#include -+#include -+#include -+#endif -+ -+#include "resolv.h" -+#include "utils.h" -+#include "netutils.h" -+ -+/* -+ * Implement DNS resolution interface using libudns -+ */ -+ -+struct ResolvQuery { -+ void (*client_cb)(struct sockaddr *, void *); -+ void (*client_free_cb)(void *); -+ void *client_cb_data; -+ struct dns_query *queries[2]; -+ size_t response_count; -+ struct sockaddr **responses; -+ uint16_t port; -+}; -+ -+extern int verbose; -+ -+static struct ev_io resolv_io_watcher; -+static struct ev_timer resolv_timeout_watcher; -+static const int MODE_IPV4_ONLY = 0; -+static const int MODE_IPV6_ONLY = 1; -+static const int MODE_IPV4_FIRST = 2; -+static const int MODE_IPV6_FIRST = 3; -+static int resolv_mode = 0; -+ -+static void resolv_sock_cb(struct ev_loop *, struct ev_io *, int); -+static void resolv_timeout_cb(struct ev_loop *, struct ev_timer *, int); -+static void dns_query_v4_cb(struct dns_ctx *, struct dns_rr_a4 *, void *); -+static void dns_query_v6_cb(struct dns_ctx *, struct dns_rr_a6 *, void *); -+static void dns_timer_setup_cb(struct dns_ctx *, int, void *); -+static void process_client_callback(struct ResolvQuery *); -+static inline int all_queries_are_null(struct ResolvQuery *); -+static struct sockaddr *choose_ipv4_first(struct ResolvQuery *); -+static struct sockaddr *choose_ipv6_first(struct ResolvQuery *); -+static struct sockaddr *choose_any(struct ResolvQuery *); -+ -+int -+resolv_init(struct ev_loop *loop, char **nameservers, int nameserver_num, int ipv6first) -+{ -+ if (ipv6first) -+ resolv_mode = MODE_IPV6_FIRST; -+ else -+ resolv_mode = MODE_IPV4_FIRST; -+ -+ struct dns_ctx *ctx = &dns_defctx; -+ if (nameservers == NULL) { -+ /* Nameservers not specified, use system resolver config */ -+ dns_init(ctx, 0); -+ } else { -+ dns_reset(ctx); -+ -+ for (int i = 0; i < nameserver_num; i++) { -+ char *server = nameservers[i]; -+ dns_add_serv(ctx, server); -+ } -+ } -+ -+ int sockfd = dns_open(ctx); -+ if (sockfd < 0) { -+ FATAL("Failed to open DNS resolver socket"); -+ } -+ -+ if (nameserver_num == 1 && nameservers != NULL) { -+ if (strncmp("127.0.0.1", nameservers[0], 9) == 0 -+ || strncmp("::1", nameservers[0], 3) == 0) { -+ if (verbose) { -+ LOGI("bind UDP resolver to %s", nameservers[0]); -+ } -+ if (bind_to_address(sockfd, nameservers[0]) == -1) -+ ERROR("bind_to_address"); -+ } -+ } -+ -+#ifdef __MINGW32__ -+ setnonblocking(sockfd); -+#else -+ int flags = fcntl(sockfd, F_GETFL, 0); -+ fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); -+#endif -+ -+ ev_io_init(&resolv_io_watcher, resolv_sock_cb, sockfd, EV_READ); -+ resolv_io_watcher.data = ctx; -+ -+ ev_io_start(loop, &resolv_io_watcher); -+ -+ ev_timer_init(&resolv_timeout_watcher, resolv_timeout_cb, 0.0, 0.0); -+ resolv_timeout_watcher.data = ctx; -+ -+ dns_set_tmcbck(ctx, dns_timer_setup_cb, loop); -+ -+ return sockfd; -+} -+ -+void -+resolv_shutdown(struct ev_loop *loop) -+{ -+ struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; -+ -+ ev_io_stop(loop, &resolv_io_watcher); -+ -+ if (ev_is_active(&resolv_timeout_watcher)) { -+ ev_timer_stop(loop, &resolv_timeout_watcher); -+ } -+ -+ dns_close(ctx); -+} -+ -+struct ResolvQuery * -+resolv_query(const char *hostname, void (*client_cb)(struct sockaddr *, void *), -+ void (*client_free_cb)(void *), void *client_cb_data, -+ uint16_t port) -+{ -+ struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; -+ -+ /* -+ * Wrap udns's call back in our own -+ */ -+ struct ResolvQuery *cb_data = ss_malloc(sizeof(struct ResolvQuery)); -+ if (cb_data == NULL) { -+ LOGE("Failed to allocate memory for DNS query callback data."); -+ return NULL; -+ } -+ memset(cb_data, 0, sizeof(struct ResolvQuery)); -+ -+ cb_data->client_cb = client_cb; -+ cb_data->client_free_cb = client_free_cb; -+ cb_data->client_cb_data = client_cb_data; -+ memset(cb_data->queries, 0, sizeof(cb_data->queries)); -+ cb_data->response_count = 0; -+ cb_data->responses = NULL; -+ cb_data->port = port; -+ -+ /* Submit A and AAAA queries */ -+ if (resolv_mode != MODE_IPV6_ONLY) { -+ cb_data->queries[0] = dns_submit_a4(ctx, -+ hostname, 0, -+ dns_query_v4_cb, cb_data); -+ if (cb_data->queries[0] == NULL) { -+ LOGE("Failed to submit DNS query: %s", -+ dns_strerror(dns_status(ctx))); -+ } -+ } -+ -+ if (resolv_mode != MODE_IPV4_ONLY) { -+ cb_data->queries[1] = dns_submit_a6(ctx, -+ hostname, 0, -+ dns_query_v6_cb, cb_data); -+ if (cb_data->queries[1] == NULL) { -+ LOGE("Failed to submit DNS query: %s", -+ dns_strerror(dns_status(ctx))); -+ } -+ } -+ -+ if (all_queries_are_null(cb_data)) { -+ if (cb_data->client_free_cb != NULL) { -+ cb_data->client_free_cb(cb_data->client_cb_data); -+ } -+ ss_free(cb_data); -+ } -+ -+ return cb_data; -+} -+ -+void -+resolv_cancel(struct ResolvQuery *query_handle) -+{ -+ struct ResolvQuery *cb_data = (struct ResolvQuery *)query_handle; -+ struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; -+ -+ for (int i = 0; i < sizeof(cb_data->queries) / sizeof(cb_data->queries[0]); -+ i++) -+ if (cb_data->queries[i] != NULL) { -+ dns_cancel(ctx, cb_data->queries[i]); -+ ss_free(cb_data->queries[i]); -+ } -+ -+ if (cb_data->client_free_cb != NULL) { -+ cb_data->client_free_cb(cb_data->client_cb_data); -+ } -+ -+ ss_free(cb_data); -+} -+ -+/* -+ * DNS UDP socket activity callback -+ */ -+static void -+resolv_sock_cb(struct ev_loop *loop, struct ev_io *w, int revents) -+{ -+ struct dns_ctx *ctx = (struct dns_ctx *)w->data; -+ -+ if (revents & EV_READ) { -+ dns_ioevent(ctx, ev_now(loop)); -+ } -+} -+ -+/* -+ * Wrapper for client callback we provide to udns -+ */ -+static void -+dns_query_v4_cb(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data) -+{ -+ struct ResolvQuery *cb_data = (struct ResolvQuery *)data; -+ -+ if (result == NULL) { -+ if (verbose) { -+ LOGI("IPv4 resolv: %s", dns_strerror(dns_status(ctx))); -+ } -+ } else if (result->dnsa4_nrr > 0) { -+ struct sockaddr **new_responses = ss_realloc(cb_data->responses, -+ (cb_data->response_count + -+ result->dnsa4_nrr) * -+ sizeof(struct sockaddr *)); -+ if (new_responses == NULL) { -+ LOGE("Failed to allocate memory for additional DNS responses"); -+ } else { -+ cb_data->responses = new_responses; -+ -+ for (int i = 0; i < result->dnsa4_nrr; i++) { -+ struct sockaddr_in *sa = -+ (struct sockaddr_in *)ss_malloc(sizeof(struct sockaddr_in)); -+ sa->sin_family = AF_INET; -+ sa->sin_port = cb_data->port; -+ sa->sin_addr = result->dnsa4_addr[i]; -+ -+ cb_data->responses[cb_data->response_count] = -+ (struct sockaddr *)sa; -+ if (cb_data->responses[cb_data->response_count] == NULL) { -+ LOGE( -+ "Failed to allocate memory for DNS query result address"); -+ } else { -+ cb_data->response_count++; -+ } -+ } -+ } -+ } -+ -+ ss_free(result); -+ cb_data->queries[0] = NULL; /* mark A query as being completed */ -+ -+ /* Once all queries have completed, call client callback */ -+ if (all_queries_are_null(cb_data)) { -+ return process_client_callback(cb_data); -+ } -+} -+ -+static void -+dns_query_v6_cb(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data) -+{ -+ struct ResolvQuery *cb_data = (struct ResolvQuery *)data; -+ -+ if (result == NULL) { -+ if (verbose) { -+ LOGI("IPv6 resolv: %s", dns_strerror(dns_status(ctx))); -+ } -+ } else if (result->dnsa6_nrr > 0) { -+ struct sockaddr **new_responses = ss_realloc(cb_data->responses, -+ (cb_data->response_count + -+ result->dnsa6_nrr) * -+ sizeof(struct sockaddr *)); -+ if (new_responses == NULL) { -+ LOGE("Failed to allocate memory for additional DNS responses"); -+ } else { -+ cb_data->responses = new_responses; -+ -+ for (int i = 0; i < result->dnsa6_nrr; i++) { -+ struct sockaddr_in6 *sa = -+ (struct sockaddr_in6 *)ss_malloc(sizeof(struct sockaddr_in6)); -+ sa->sin6_family = AF_INET6; -+ sa->sin6_port = cb_data->port; -+ sa->sin6_addr = result->dnsa6_addr[i]; -+ -+ cb_data->responses[cb_data->response_count] = -+ (struct sockaddr *)sa; -+ if (cb_data->responses[cb_data->response_count] == NULL) { -+ LOGE( -+ "Failed to allocate memory for DNS query result address"); -+ } else { -+ cb_data->response_count++; -+ } -+ } -+ } -+ } -+ -+ ss_free(result); -+ cb_data->queries[1] = NULL; /* mark AAAA query as being completed */ -+ -+ /* Once all queries have completed, call client callback */ -+ if (all_queries_are_null(cb_data)) { -+ return process_client_callback(cb_data); -+ } -+} -+ -+/* -+ * Called once all queries have been completed -+ */ -+static void -+process_client_callback(struct ResolvQuery *cb_data) -+{ -+ struct sockaddr *best_address = NULL; -+ -+ if (resolv_mode == MODE_IPV4_FIRST) { -+ best_address = choose_ipv4_first(cb_data); -+ } else if (resolv_mode == MODE_IPV6_FIRST) { -+ best_address = choose_ipv6_first(cb_data); -+ } else { -+ best_address = choose_any(cb_data); -+ } -+ -+ cb_data->client_cb(best_address, cb_data->client_cb_data); -+ -+ for (int i = 0; i < cb_data->response_count; i++) -+ ss_free(cb_data->responses[i]); -+ -+ ss_free(cb_data->responses); -+ if (cb_data->client_free_cb != NULL) { -+ cb_data->client_free_cb(cb_data->client_cb_data); -+ } -+ ss_free(cb_data); -+} -+ -+static struct sockaddr * -+choose_ipv4_first(struct ResolvQuery *cb_data) -+{ -+ for (int i = 0; i < cb_data->response_count; i++) -+ if (cb_data->responses[i]->sa_family == AF_INET) { -+ return cb_data->responses[i]; -+ } -+ -+ return choose_any(cb_data); -+} -+ -+static struct sockaddr * -+choose_ipv6_first(struct ResolvQuery *cb_data) -+{ -+ for (int i = 0; i < cb_data->response_count; i++) -+ if (cb_data->responses[i]->sa_family == AF_INET6) { -+ return cb_data->responses[i]; -+ } -+ -+ return choose_any(cb_data); -+} -+ -+static struct sockaddr * -+choose_any(struct ResolvQuery *cb_data) -+{ -+ if (cb_data->response_count >= 1) { -+ return cb_data->responses[0]; -+ } -+ -+ return NULL; -+} -+ -+/* -+ * DNS timeout callback -+ */ -+static void -+resolv_timeout_cb(struct ev_loop *loop, struct ev_timer *w, int revents) -+{ -+ struct dns_ctx *ctx = (struct dns_ctx *)w->data; -+ -+ if (revents & EV_TIMER) { -+ dns_timeouts(ctx, 30, ev_now(loop)); -+ } -+} -+ -+/* -+ * Callback to setup DNS timeout callback -+ */ -+static void -+dns_timer_setup_cb(struct dns_ctx *ctx, int timeout, void *data) -+{ -+ struct ev_loop *loop = (struct ev_loop *)data; -+ -+ if (ev_is_active(&resolv_timeout_watcher)) { -+ ev_timer_stop(loop, &resolv_timeout_watcher); -+ } -+ -+ if (ctx != NULL && timeout >= 0) { -+ ev_timer_set(&resolv_timeout_watcher, timeout, 0.0); -+ ev_timer_start(loop, &resolv_timeout_watcher); -+ } -+} -+ -+static inline int -+all_queries_are_null(struct ResolvQuery *cb_data) -+{ -+ int result = 1; -+ -+ for (int i = 0; i < sizeof(cb_data->queries) / sizeof(cb_data->queries[0]); -+ i++) -+ result = result && cb_data->queries[i] == NULL; -+ -+ return result; -+} -diff --git a/server/resolv.h b/server/resolv.h -new file mode 100644 -index 0000000..0552922 ---- /dev/null -+++ b/server/resolv.h -@@ -0,0 +1,50 @@ -+/* -+ * Copyright (c) 2014, Dustin Lundquist -+ * 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. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. -+ */ -+#ifndef RESOLV_H -+#define RESOLV_H -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include -+ -+#ifdef __MINGW32__ -+#include "win32.h" -+#else -+#include -+#endif -+ -+struct ResolvQuery; -+ -+int resolv_init(struct ev_loop *, char **, int, int); -+struct ResolvQuery *resolv_query(const char *, void (*)(struct sockaddr *, -+ void *), void (*)( -+ void *), void *, uint16_t); -+void resolv_cancel(struct ResolvQuery *); -+void resolv_shutdown(struct ev_loop *); -+ -+#endif -diff --git a/server/rule.c b/server/rule.c -new file mode 100644 -index 0000000..8aae04e ---- /dev/null -+++ b/server/rule.c -@@ -0,0 +1,137 @@ -+/* -+ * Copyright (c) 2011 and 2012, Dustin Lundquist -+ * Copyright (c) 2011 Manuel Kasper -+ * 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. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include -+#include -+ -+#ifdef __MINGW32__ -+extern void ss_error(const char *s); -+#endif -+ -+#include "rule.h" -+#include "utils.h" -+ -+static void free_rule(rule_t *); -+ -+rule_t * -+new_rule() -+{ -+ rule_t *rule; -+ -+ rule = calloc(1, sizeof(rule_t)); -+ if (rule == NULL) { -+ ERROR("malloc"); -+ return NULL; -+ } -+ -+ return rule; -+} -+ -+int -+accept_rule_arg(rule_t *rule, const char *arg) -+{ -+ if (rule->pattern == NULL) { -+ rule->pattern = strdup(arg); -+ if (rule->pattern == NULL) { -+ ERROR("strdup failed"); -+ return -1; -+ } -+ } else { -+ LOGE("Unexpected table rule argument: %s", arg); -+ return -1; -+ } -+ -+ return 1; -+} -+ -+void -+add_rule(struct cork_dllist *rules, rule_t *rule) -+{ -+ cork_dllist_add(rules, &rule->entries); -+} -+ -+int -+init_rule(rule_t *rule) -+{ -+ if (rule->pattern_re == NULL) { -+ const char *reerr; -+ int reerroffset; -+ -+ rule->pattern_re = -+ pcre_compile(rule->pattern, 0, &reerr, &reerroffset, NULL); -+ if (rule->pattern_re == NULL) { -+ LOGE("Regex compilation of \"%s\" failed: %s, offset %d", -+ rule->pattern, reerr, reerroffset); -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+rule_t * -+lookup_rule(const struct cork_dllist *rules, const char *name, size_t name_len) -+{ -+ struct cork_dllist_item *curr, *next; -+ -+ if (name == NULL) { -+ name = ""; -+ name_len = 0; -+ } -+ -+ cork_dllist_foreach_void(rules, curr, next) { -+ rule_t *rule = cork_container_of(curr, rule_t, entries); -+ if (pcre_exec(rule->pattern_re, NULL, -+ name, name_len, 0, 0, NULL, 0) >= 0) -+ return rule; -+ } -+ -+ return NULL; -+} -+ -+void -+remove_rule(rule_t *rule) -+{ -+ cork_dllist_remove(&rule->entries); -+ free_rule(rule); -+} -+ -+static void -+free_rule(rule_t *rule) -+{ -+ if (rule == NULL) -+ return; -+ -+ ss_free(rule->pattern); -+ if (rule->pattern_re != NULL) -+ pcre_free(rule->pattern_re); -+ ss_free(rule); -+} -diff --git a/server/rule.h b/server/rule.h -new file mode 100644 -index 0000000..015bc42 ---- /dev/null -+++ b/server/rule.h -@@ -0,0 +1,58 @@ -+/* -+ * Copyright (c) 2011 and 2012, Dustin Lundquist -+ * Copyright (c) 2011 Manuel Kasper -+ * 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. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. -+ */ -+#ifndef RULE_H -+#define RULE_H -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include -+ -+#ifdef HAVE_PCRE_H -+#include -+#elif HAVE_PCRE_PCRE_H -+#include -+#endif -+ -+typedef struct rule { -+ char *pattern; -+ -+ /* Runtime fields */ -+ pcre *pattern_re; -+ -+ struct cork_dllist_item entries; -+} rule_t; -+ -+void add_rule(struct cork_dllist *, rule_t *); -+int init_rule(rule_t *); -+rule_t *lookup_rule(const struct cork_dllist *, const char *, size_t); -+void remove_rule(rule_t *); -+rule_t *new_rule(); -+int accept_rule_arg(rule_t *, const char *); -+ -+#endif -diff --git a/server/server.c b/server/server.c -new file mode 100644 -index 0000000..65b0e42 ---- /dev/null -+++ b/server/server.c -@@ -0,0 +1,2209 @@ -+/* -+ * server.c - Provide shadowsocks service -+ * -+ * Copyright (C) 2013 - 2016, 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 HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifndef __MINGW32__ -+#include -+#include -+#include -+#include -+#include -+#include -+#endif -+ -+#include -+#include -+ -+#ifdef __MINGW32__ -+#include "win32.h" -+#endif -+ -+#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__) -+#include -+#include -+#define SET_INTERFACE -+#endif -+ -+#include "netutils.h" -+#include "utils.h" -+#include "acl.h" -+#include "server.h" -+ -+#include "obfs.c" // I don't want to modify makefile -+ -+#ifndef EAGAIN -+#define EAGAIN EWOULDBLOCK -+#endif -+ -+#ifndef EWOULDBLOCK -+#define EWOULDBLOCK EAGAIN -+#endif -+ -+#ifndef BUF_SIZE -+#define BUF_SIZE 2048 -+#endif -+ -+#ifndef SSMAXCONN -+#define SSMAXCONN 1024 -+#endif -+ -+#ifndef UPDATE_INTERVAL -+#define UPDATE_INTERVAL 30 -+#endif -+ -+static void signal_cb(EV_P_ ev_signal *w, int revents); -+static void accept_cb(EV_P_ ev_io *w, int revents); -+static void server_send_cb(EV_P_ ev_io *w, int revents); -+static void server_recv_cb(EV_P_ ev_io *w, int revents); -+static void remote_recv_cb(EV_P_ ev_io *w, int revents); -+static void remote_send_cb(EV_P_ ev_io *w, int revents); -+static void server_timeout_cb(EV_P_ ev_timer *watcher, int revents); -+static void block_list_clear_cb(EV_P_ ev_timer *watcher, int revents); -+ -+static remote_t *new_remote(int fd); -+static server_t *new_server(int fd, listen_ctx_t *listener); -+static remote_t *connect_to_remote(EV_P_ struct addrinfo *res, -+ server_t *server); -+ -+static void free_remote(remote_t *remote); -+static void close_and_free_remote(EV_P_ remote_t *remote); -+static void free_server(server_t *server); -+static void close_and_free_server(EV_P_ server_t *server); -+static void server_resolve_cb(struct sockaddr *addr, void *data); -+static void query_free_cb(void *data); -+ -+static size_t parse_header_len(const char atyp, const char *data, size_t offset); -+static int is_header_complete(const buffer_t *buf); -+ -+int verbose = 0; -+ -+static int acl = 0; -+static int mode = TCP_ONLY; -+static int auth = 0; -+static int ipv6first = 0; -+ -+static int protocol_compatible = 0;//SSR -+static int obfs_compatible = 0;//SSR -+ -+static int fast_open = 0; -+#ifdef HAVE_SETRLIMIT -+static int nofile = 0; -+#endif -+static int remote_conn = 0; -+static int server_conn = 0; -+ -+static char *bind_address = NULL; -+static char *server_port = NULL; -+static char *manager_address = NULL; -+uint64_t tx = 0; -+uint64_t rx = 0; -+ev_timer stat_update_watcher; -+ev_timer block_list_watcher; -+ -+static struct cork_dllist connections; -+ -+static void -+stat_update_cb(EV_P_ ev_timer *watcher, int revents) -+{ -+ struct sockaddr_un svaddr, claddr; -+ int sfd = -1; -+ size_t msgLen; -+ char resp[BUF_SIZE]; -+ -+ if (verbose) { -+ LOGI("update traffic stat: tx: %" PRIu64 " rx: %" PRIu64 "", tx, rx); -+ } -+ -+ snprintf(resp, BUF_SIZE, "stat: {\"%s\":%" PRIu64 "}", server_port, tx + rx); -+ msgLen = strlen(resp) + 1; -+ -+ ss_addr_t ip_addr = { .host = NULL, .port = NULL }; -+ parse_addr(manager_address, &ip_addr); -+ -+ if (ip_addr.host == NULL || ip_addr.port == NULL) { -+ sfd = socket(AF_UNIX, SOCK_DGRAM, 0); -+ if (sfd == -1) { -+ ERROR("stat_socket"); -+ return; -+ } -+ -+ memset(&claddr, 0, sizeof(struct sockaddr_un)); -+ claddr.sun_family = AF_UNIX; -+ snprintf(claddr.sun_path, sizeof(claddr.sun_path), "/tmp/shadowsocks.%s", server_port); -+ -+ unlink(claddr.sun_path); -+ -+ if (bind(sfd, (struct sockaddr *)&claddr, sizeof(struct sockaddr_un)) == -1) { -+ ERROR("stat_bind"); -+ close(sfd); -+ return; -+ } -+ -+ memset(&svaddr, 0, sizeof(struct sockaddr_un)); -+ svaddr.sun_family = AF_UNIX; -+ strncpy(svaddr.sun_path, manager_address, sizeof(svaddr.sun_path) - 1); -+ -+ if (sendto(sfd, resp, strlen(resp) + 1, 0, (struct sockaddr *)&svaddr, -+ sizeof(struct sockaddr_un)) != msgLen) { -+ ERROR("stat_sendto"); -+ close(sfd); -+ return; -+ } -+ -+ unlink(claddr.sun_path); -+ } else { -+ struct sockaddr_storage storage; -+ memset(&storage, 0, sizeof(struct sockaddr_storage)); -+ if (get_sockaddr(ip_addr.host, ip_addr.port, &storage, 0, ipv6first) == -1) { -+ ERROR("failed to parse the manager addr"); -+ return; -+ } -+ -+ sfd = socket(storage.ss_family, SOCK_DGRAM, 0); -+ -+ if (sfd == -1) { -+ ERROR("stat_socket"); -+ return; -+ } -+ -+ size_t addr_len = get_sockaddr_len((struct sockaddr *)&storage); -+ if (sendto(sfd, resp, strlen(resp) + 1, 0, (struct sockaddr *)&storage, -+ addr_len) != msgLen) { -+ ERROR("stat_sendto"); -+ close(sfd); -+ return; -+ } -+ } -+ -+ close(sfd); -+} -+ -+static void -+free_connections(struct ev_loop *loop) -+{ -+ struct cork_dllist_item *curr, *next; -+ cork_dllist_foreach_void(&connections, curr, next) { -+ server_t *server = cork_container_of(curr, server_t, entries); -+ remote_t *remote = server->remote; -+ close_and_free_server(loop, server); -+ close_and_free_remote(loop, remote); -+ } -+} -+ -+static size_t -+parse_header_len(const char atyp, const char *data, size_t offset) -+{ -+ size_t len = 0; -+ if ((atyp & ADDRTYPE_MASK) == 1) { -+ // IP V4 -+ len += sizeof(struct in_addr); -+ } else if ((atyp & ADDRTYPE_MASK) == 3) { -+ // Domain name -+ uint8_t name_len = *(uint8_t *)(data + offset); -+ len += name_len + 1; -+ } else if ((atyp & ADDRTYPE_MASK) == 4) { -+ // IP V6 -+ len += sizeof(struct in6_addr); -+ } else { -+ return 0; -+ } -+ len += 2; -+ return len; -+} -+ -+static int -+is_header_complete(const buffer_t *buf) -+{ -+ size_t header_len = 0; -+ size_t buf_len = buf->len; -+ -+ char atyp = buf->array[header_len]; -+ -+ // 1 byte for atyp -+ header_len++; -+ -+ if ((atyp & ADDRTYPE_MASK) == 1) { -+ // IP V4 -+ header_len += sizeof(struct in_addr); -+ } else if ((atyp & ADDRTYPE_MASK) == 3) { -+ // Domain name -+ // domain len + len of domain -+ if (buf_len < header_len + 1) -+ return 0; -+ uint8_t name_len = *(uint8_t *)(buf->array + header_len); -+ header_len += name_len + 1; -+ } else if ((atyp & ADDRTYPE_MASK) == 4) { -+ // IP V6 -+ header_len += sizeof(struct in6_addr); -+ } else { -+ return -1; -+ } -+ -+ // len of port -+ header_len += 2; -+ -+ // size of ONETIMEAUTH_BYTES -+ if (auth || (atyp & ONETIMEAUTH_FLAG)) { -+ header_len += ONETIMEAUTH_BYTES; -+ } -+ -+ return buf_len >= header_len ? 1 : 0; -+} -+ -+static char * -+get_peer_name(int fd) -+{ -+ static char peer_name[INET6_ADDRSTRLEN] = { 0 }; -+ struct sockaddr_storage addr; -+ socklen_t len = sizeof(struct sockaddr_storage); -+ memset(&addr, 0, len); -+ memset(peer_name, 0, INET6_ADDRSTRLEN); -+ int err = getpeername(fd, (struct sockaddr *)&addr, &len); -+ if (err == 0) { -+ if (addr.ss_family == AF_INET) { -+ struct sockaddr_in *s = (struct sockaddr_in *)&addr; -+ dns_ntop(AF_INET, &s->sin_addr, peer_name, INET_ADDRSTRLEN); -+ } else if (addr.ss_family == AF_INET6) { -+ struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; -+ dns_ntop(AF_INET6, &s->sin6_addr, peer_name, INET6_ADDRSTRLEN); -+ } -+ } else { -+ return NULL; -+ } -+ return peer_name; -+} -+ -+#ifdef __linux__ -+static void -+set_linger(int fd) -+{ -+ struct linger so_linger; -+ memset(&so_linger, 0, sizeof(struct linger)); -+ so_linger.l_onoff = 1; -+ so_linger.l_linger = 0; -+ setsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger); -+} -+#endif -+ -+static void -+reset_addr(int fd) -+{ -+ char *peer_name; -+ peer_name = get_peer_name(fd); -+ if (peer_name != NULL) { -+ remove_from_block_list(peer_name); -+ } -+} -+ -+static void -+report_addr(int fd, int err_level) -+{ -+#ifdef __linux__ -+ set_linger(fd); -+#endif -+ -+ char *peer_name; -+ peer_name = get_peer_name(fd); -+ if (peer_name != NULL) { -+ LOGE("failed to handshake with %s", peer_name); -+ update_block_list(peer_name, err_level); -+ } -+} -+ -+int -+setfastopen(int fd) -+{ -+ int s = 0; -+#ifdef TCP_FASTOPEN -+ if (fast_open) { -+#ifdef __APPLE__ -+ int opt = 1; -+#else -+ int opt = 5; -+#endif -+ s = setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &opt, sizeof(opt)); -+ -+ if (s == -1) { -+ if (errno == EPROTONOSUPPORT || errno == ENOPROTOOPT) { -+ LOGE("fast open is not supported on this platform"); -+ fast_open = 0; -+ } else { -+ ERROR("setsockopt"); -+ } -+ } -+ } -+#endif -+ return s; -+} -+ -+#ifndef __MINGW32__ -+int -+setnonblocking(int fd) -+{ -+ int flags; -+ if (-1 == (flags = fcntl(fd, F_GETFL, 0))) { -+ flags = 0; -+ } -+ return fcntl(fd, F_SETFL, flags | O_NONBLOCK); -+} -+ -+#endif -+ -+int -+create_and_bind(const char *host, const char *port, int mptcp) -+{ -+ struct addrinfo hints; -+ struct addrinfo *result, *rp, *ipv4v6bindall; -+ int s, listen_sock; -+ -+ memset(&hints, 0, sizeof(struct addrinfo)); -+ hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ -+ hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */ -+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* For wildcard IP address */ -+ hints.ai_protocol = IPPROTO_TCP; -+ -+ for (int i = 1; i < 8; i++) { -+ s = getaddrinfo(host, port, &hints, &result); -+ if (s == 0) { -+ break; -+ } else { -+ sleep(pow(2, i)); -+ LOGE("failed to resolve server name, wait %.0f seconds", pow(2, i)); -+ } -+ } -+ -+ if (s != 0) { -+ LOGE("getaddrinfo: %s", gai_strerror(s)); -+ return -1; -+ } -+ -+ rp = result; -+ -+ /* -+ * On Linux, with net.ipv6.bindv6only = 0 (the default), getaddrinfo(NULL) with -+ * AI_PASSIVE returns 0.0.0.0 and :: (in this order). AI_PASSIVE was meant to -+ * return a list of addresses to listen on, but it is impossible to listen on -+ * 0.0.0.0 and :: at the same time, if :: implies dualstack mode. -+ */ -+ if (!host) { -+ ipv4v6bindall = result; -+ -+ /* Loop over all address infos found until a IPV6 address is found. */ -+ while (ipv4v6bindall) { -+ if (ipv4v6bindall->ai_family == AF_INET6) { -+ rp = ipv4v6bindall; /* Take first IPV6 address available */ -+ break; -+ } -+ ipv4v6bindall = ipv4v6bindall->ai_next; /* Get next address info, if any */ -+ } -+ } -+ -+ for (/*rp = result*/; rp != NULL; rp = rp->ai_next) { -+ listen_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); -+ if (listen_sock == -1) { -+ continue; -+ } -+ -+ if (rp->ai_family == AF_INET6) { -+ int ipv6only = host ? 1 : 0; -+ setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)); -+ } -+ -+ int opt = 1; -+ setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); -+#ifdef SO_NOSIGPIPE -+ setsockopt(listen_sock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); -+#endif -+ int err = set_reuseport(listen_sock); -+ if (err == 0) { -+ LOGI("tcp port reuse enabled"); -+ } -+ -+ if (mptcp == 1) { -+ int err = setsockopt(listen_sock, SOL_TCP, MPTCP_ENABLED, &opt, sizeof(opt)); -+ if (err == -1) { -+ ERROR("failed to enable multipath TCP"); -+ } -+ } -+ -+ s = bind(listen_sock, rp->ai_addr, rp->ai_addrlen); -+ if (s == 0) { -+ /* We managed to bind successfully! */ -+ break; -+ } else { -+ ERROR("bind"); -+ } -+ -+ close(listen_sock); -+ } -+ -+ if (rp == NULL) { -+ LOGE("Could not bind"); -+ return -1; -+ } -+ -+ freeaddrinfo(result); -+ -+ return listen_sock; -+} -+ -+static remote_t * -+connect_to_remote(EV_P_ struct addrinfo *res, -+ server_t *server) -+{ -+ int sockfd; -+#ifdef SET_INTERFACE -+ const char *iface = server->listen_ctx->iface; -+#endif -+ -+ if (acl) { -+ char ipstr[INET6_ADDRSTRLEN]; -+ memset(ipstr, 0, INET6_ADDRSTRLEN); -+ -+ if (res->ai_addr->sa_family == AF_INET) { -+ struct sockaddr_in *s = (struct sockaddr_in *)res->ai_addr; -+ dns_ntop(AF_INET, &s->sin_addr, ipstr, INET_ADDRSTRLEN); -+ } else if (res->ai_addr->sa_family == AF_INET6) { -+ struct sockaddr_in6 *s = (struct sockaddr_in6 *)res->ai_addr; -+ dns_ntop(AF_INET6, &s->sin6_addr, ipstr, INET6_ADDRSTRLEN); -+ } -+ -+ if (outbound_block_match_host(ipstr) == 1) { -+ if (verbose) -+ LOGI("outbound blocked %s", ipstr); -+ return NULL; -+ } -+ } -+ -+ // initialize remote socks -+ sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); -+ if (sockfd == -1) { -+ ERROR("socket"); -+ close(sockfd); -+ return NULL; -+ } -+ -+ int opt = 1; -+ setsockopt(sockfd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); -+#ifdef SO_NOSIGPIPE -+ setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); -+#endif -+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); -+ -+ // setup remote socks -+ -+ if (setnonblocking(sockfd) == -1) -+ ERROR("setnonblocking"); -+ -+ if (bind_address != NULL) -+ if (bind_to_address(sockfd, bind_address) == -1) { -+ ERROR("bind_to_address"); -+ close(sockfd); -+ return NULL; -+ } -+ -+#ifdef SET_INTERFACE -+ if (iface) { -+ if (setinterface(sockfd, iface) == -1) { -+ ERROR("setinterface"); -+ close(sockfd); -+ return NULL; -+ } -+ } -+#endif -+ -+ remote_t *remote = new_remote(sockfd); -+ -+#ifdef TCP_FASTOPEN -+ if (fast_open) { -+#ifdef __APPLE__ -+ ((struct sockaddr_in *)(res->ai_addr))->sin_len = sizeof(struct sockaddr_in); -+ sa_endpoints_t endpoints; -+ memset((char *)&endpoints, 0, sizeof(endpoints)); -+ endpoints.sae_dstaddr = res->ai_addr; -+ endpoints.sae_dstaddrlen = res->ai_addrlen; -+ -+ struct iovec iov; -+ iov.iov_base = server->buf->array + server->buf->idx; -+ iov.iov_len = server->buf->len; -+ size_t len; -+ int s = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY, CONNECT_DATA_IDEMPOTENT, -+ &iov, 1, &len, NULL); -+ if (s == 0) { -+ s = len; -+ } -+#else -+ ssize_t s = sendto(sockfd, server->buf->array + server->buf->idx, -+ server->buf->len, MSG_FASTOPEN, res->ai_addr, -+ res->ai_addrlen); -+#endif -+ if (s == -1) { -+ if (errno == CONNECT_IN_PROGRESS || errno == EAGAIN -+ || errno == EWOULDBLOCK) { -+ // The remote server doesn't support tfo or it's the first connection to the server. -+ // It will automatically fall back to conventional TCP. -+ } else if (errno == EOPNOTSUPP || errno == EPROTONOSUPPORT || -+ errno == ENOPROTOOPT) { -+ // Disable fast open as it's not supported -+ fast_open = 0; -+ LOGE("fast open is not supported on this platform"); -+ } else { -+ ERROR("sendto"); -+ } -+ } else if (s <= server->buf->len) { -+ server->buf->idx += s; -+ server->buf->len -= s; -+ } else { -+ server->buf->idx = 0; -+ server->buf->len = 0; -+ } -+ } -+#endif -+ -+ if (!fast_open) { -+ int r = connect(sockfd, res->ai_addr, res->ai_addrlen); -+ -+ if (r == -1 && errno != CONNECT_IN_PROGRESS) { -+ ERROR("connect"); -+ close_and_free_remote(EV_A_ remote); -+ return NULL; -+ } -+ } -+ -+ return remote; -+} -+ -+static void -+server_recv_cb(EV_P_ ev_io *w, int revents) -+{ -+ server_ctx_t *server_recv_ctx = (server_ctx_t *)w; -+ server_t *server = server_recv_ctx->server; -+ remote_t *remote = NULL; -+ -+ int len = server->buf->len; -+ buffer_t *buf = server->buf; -+ -+ if (server->stage > STAGE_PARSE) { -+ remote = server->remote; -+ buf = remote->buf; -+ len = 0; -+ -+ ev_timer_again(EV_A_ & server->recv_ctx->watcher); -+ } -+ -+ if (len > BUF_SIZE) { -+ ERROR("out of recv buffer"); -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ -+ ssize_t r = recv(server->fd, buf->array + len, BUF_SIZE - len, 0); -+ -+ if (r == 0) { -+ // connection closed -+ if (verbose) { -+ LOGI("server_recv close the connection"); -+ } -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } else if (r == -1) { -+ if (errno == EAGAIN || errno == EWOULDBLOCK) { -+ // no data -+ // continue to wait for recv -+ return; -+ } else { -+ ERROR("server recv"); -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ } -+ -+ tx += r; -+ -+ if (server->stage == STAGE_ERROR) { -+ server->buf->len = 0; -+ server->buf->idx = 0; -+ return; -+ } -+ -+ // handle incomplete header part 1 -+ if (server->stage == STAGE_INIT) { -+ buf->len += r; -+ if (buf->len <= enc_get_iv_len() + 1) { -+ // wait for more -+ return; -+ } -+ } else { -+ buf->len = r; -+ } -+ -+ // SSR beg -+ -+ if (server->obfs_plugin) { -+ obfs_class *obfs_plugin = server->obfs_plugin; -+ if (obfs_plugin->server_decode) { -+ int needsendback = 0; -+ -+ if(obfs_compatible == 1) -+ { -+ char *back_buf = (char*)malloc(sizeof(buffer_t)); -+ memcpy(back_buf, buf, sizeof(buffer_t)); -+ buf->len = obfs_plugin->server_decode(server->obfs, &buf->array, buf->len, &buf->capacity, &needsendback); -+ -+ if ((int)buf->len < 0) -+ { -+ LOGE("obfs_compatible"); -+ memcpy(buf, back_buf, sizeof(buffer_t)); -+ free(back_buf); -+ server->obfs_compatible_state = 1; -+ } -+ } -+ else -+ { -+ buf->len = obfs_plugin->server_decode(server->obfs, &buf->array, buf->len, &buf->capacity, &needsendback); -+ if ((int)buf->len < 0) { -+ LOGE("server_decode"); -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ } -+ -+ if (needsendback) { -+ size_t capacity = BUF_SIZE; -+ char *sendback_buf = (char*)malloc(capacity); -+ obfs_class *obfs_plugin = server->obfs_plugin; -+ if (obfs_plugin->server_encode) { -+ int len = obfs_plugin->server_encode(server->obfs, &sendback_buf, 0, &capacity); -+ send(server->fd, sendback_buf, len, 0); -+ } -+ free(sendback_buf); -+ return; -+ } -+ } -+ } -+ -+ int err = ss_decrypt(buf, server->d_ctx, BUF_SIZE); -+ -+ if (err) { -+ report_addr(server->fd, MALICIOUS); -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ -+ if (server->protocol_plugin) { -+ obfs_class *protocol_plugin = server->protocol_plugin; -+ if (protocol_plugin->server_post_decrypt) { -+ -+ if(protocol_compatible == 1) -+ { -+ char *back_buf = (char*)malloc(sizeof(buffer_t)); -+ memcpy(back_buf, buf, sizeof(buffer_t)); -+ buf->len = protocol_plugin->server_post_decrypt(server->protocol, &buf->array, buf->len, &buf->capacity); -+ -+ if ((int)buf->len < 0) { -+ LOGE("protocol_compatible"); -+ memcpy(buf, back_buf, sizeof(buffer_t)); -+ free(back_buf); -+ server->protocol_compatible_state = 1; -+ } -+ if ( buf->len == 0 ) -+ { -+ LOGE("protocol_compatible"); -+ memcpy(buf, back_buf, sizeof(buffer_t)); -+ free(back_buf); -+ server->protocol_compatible_state = 1; -+ } -+ } -+ else -+ { -+ buf->len = protocol_plugin->server_post_decrypt(server->protocol, &buf->array, buf->len, &buf->capacity); -+ if ((int)buf->len < 0) { -+ LOGE("server_post_decrypt"); -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ if ( buf->len == 0 ) -+ { -+ LOGE("server_post_decrypt"); -+ return; -+ } -+ } -+ } -+ } -+ // SSR end -+ -+ // handle incomplete header part 2 -+ if (server->stage == STAGE_INIT) { -+ int ret = is_header_complete(server->buf); -+ if (ret == 1) { -+ bfree(server->header_buf); -+ ss_free(server->header_buf); -+ server->stage = STAGE_PARSE; -+ } else if (ret == -1) { -+ server->stage = STAGE_ERROR; -+ report_addr(server->fd, MALFORMED); -+ server->buf->len = 0; -+ server->buf->idx = 0; -+ return; -+ } else { -+ server->stage = STAGE_HANDSHAKE; -+ } -+ } -+ -+ if (server->stage == STAGE_HANDSHAKE) { -+ size_t header_len = server->header_buf->len; -+ brealloc(server->header_buf, server->buf->len + header_len, BUF_SIZE); -+ memcpy(server->header_buf->array + header_len, -+ server->buf->array, server->buf->len); -+ server->header_buf->len = server->buf->len + header_len; -+ -+ int ret = is_header_complete(server->buf); -+ -+ if (ret == 1) { -+ brealloc(server->buf, server->header_buf->len, BUF_SIZE); -+ memcpy(server->buf->array, server->header_buf->array, server->header_buf->len); -+ server->buf->len = server->header_buf->len; -+ bfree(server->header_buf); -+ ss_free(server->header_buf); -+ server->stage = STAGE_PARSE; -+ } else { -+ if (ret == -1) -+ server->stage = STAGE_ERROR; -+ server->buf->len = 0; -+ server->buf->idx = 0; -+ return; -+ } -+ } -+ -+ // handshake and transmit data -+ if (server->stage == STAGE_STREAM) { -+ if (server->auth && !ss_check_hash(remote->buf, server->chunk, server->d_ctx, BUF_SIZE)) { -+ LOGE("hash error"); -+ report_addr(server->fd, BAD); -+ close_and_free_server(EV_A_ server); -+ close_and_free_remote(EV_A_ remote); -+ return; -+ } -+ -+ int s = send(remote->fd, remote->buf->array, remote->buf->len, 0); -+ if (s == -1) { -+ if (errno == EAGAIN || errno == EWOULDBLOCK) { -+ // no data, wait for send -+ remote->buf->idx = 0; -+ ev_io_stop(EV_A_ & server_recv_ctx->io); -+ ev_io_start(EV_A_ & remote->send_ctx->io); -+ } else { -+ ERROR("server_recv_send"); -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ } -+ } else if (s < remote->buf->len) { -+ remote->buf->len -= s; -+ remote->buf->idx = s; -+ ev_io_stop(EV_A_ & server_recv_ctx->io); -+ ev_io_start(EV_A_ & remote->send_ctx->io); -+ } -+ return; -+ } else if (server->stage == STAGE_PARSE) { -+ /* -+ * Shadowsocks TCP Relay Header: -+ * -+ * +------+----------+----------+----------------+ -+ * | ATYP | DST.ADDR | DST.PORT | HMAC-SHA1 | -+ * +------+----------+----------+----------------+ -+ * | 1 | Variable | 2 | 10 | -+ * +------+----------+----------+----------------+ -+ * -+ * If ATYP & ONETIMEAUTH_FLAG(0x10) != 0, Authentication (HMAC-SHA1) is enabled. -+ * -+ * The key of HMAC-SHA1 is (IV + KEY) and the input is the whole header. -+ * The output of HMAC-SHA is truncated to 10 bytes (leftmost bits). -+ */ -+ -+ /* -+ * Shadowsocks Request's Chunk Authentication for TCP Relay's payload -+ * (No chunk authentication for response's payload): -+ * -+ * +------+-----------+-------------+------+ -+ * | LEN | HMAC-SHA1 | DATA | ... -+ * +------+-----------+-------------+------+ -+ * | 2 | 10 | Variable | ... -+ * +------+-----------+-------------+------+ -+ * -+ * The key of HMAC-SHA1 is (IV + CHUNK ID) -+ * The output of HMAC-SHA is truncated to 10 bytes (leftmost bits). -+ */ -+ -+ int offset = 0; -+ int need_query = 0; -+ char atyp = server->buf->array[offset++]; -+ char host[257] = { 0 }; -+ uint16_t port = 0; -+ struct addrinfo info; -+ struct sockaddr_storage storage; -+ memset(&info, 0, sizeof(struct addrinfo)); -+ memset(&storage, 0, sizeof(struct sockaddr_storage)); -+ -+ if (auth || (atyp & ONETIMEAUTH_FLAG)) { -+ size_t header_len = parse_header_len(atyp, server->buf->array, offset); -+ size_t len = server->buf->len; -+ -+ if (header_len == 0 || len < offset + header_len + ONETIMEAUTH_BYTES) { -+ report_addr(server->fd, MALFORMED); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ -+ server->buf->len = offset + header_len + ONETIMEAUTH_BYTES; -+ if (ss_onetimeauth_verify(server->buf, server->d_ctx->evp.iv)) { -+ report_addr(server->fd, BAD); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ -+ server->buf->len = len; -+ server->auth = 1; -+ } -+ -+ // get remote addr and port -+ if ((atyp & ADDRTYPE_MASK) == 1) { -+ // IP V4 -+ struct sockaddr_in *addr = (struct sockaddr_in *)&storage; -+ size_t in_addr_len = sizeof(struct in_addr); -+ addr->sin_family = AF_INET; -+ if (server->buf->len >= in_addr_len + 3) { -+ addr->sin_addr = *(struct in_addr *)(server->buf->array + offset); -+ dns_ntop(AF_INET, (const void *)(server->buf->array + offset), -+ host, INET_ADDRSTRLEN); -+ offset += in_addr_len; -+ } else { -+ LOGE("invalid header with addr type %d", atyp); -+ report_addr(server->fd, MALFORMED); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ addr->sin_port = *(uint16_t *)(server->buf->array + offset); -+ info.ai_family = AF_INET; -+ info.ai_socktype = SOCK_STREAM; -+ info.ai_protocol = IPPROTO_TCP; -+ info.ai_addrlen = sizeof(struct sockaddr_in); -+ info.ai_addr = (struct sockaddr *)addr; -+ } else if ((atyp & ADDRTYPE_MASK) == 3) { -+ // Domain name -+ uint8_t name_len = *(uint8_t *)(server->buf->array + offset); -+ if (name_len + 4 <= server->buf->len) { -+ memcpy(host, server->buf->array + offset + 1, name_len); -+ offset += name_len + 1; -+ } else { -+ LOGE("invalid name length: %d", name_len); -+ report_addr(server->fd, MALFORMED); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ if (acl && outbound_block_match_host(host) == 1) { -+ if (verbose) -+ LOGI("outbound blocked %s", host); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ struct cork_ip ip; -+ if (cork_ip_init(&ip, host) != -1) { -+ info.ai_socktype = SOCK_STREAM; -+ info.ai_protocol = IPPROTO_TCP; -+ if (ip.version == 4) { -+ struct sockaddr_in *addr = (struct sockaddr_in *)&storage; -+ dns_pton(AF_INET, host, &(addr->sin_addr)); -+ addr->sin_port = *(uint16_t *)(server->buf->array + offset); -+ addr->sin_family = AF_INET; -+ info.ai_family = AF_INET; -+ info.ai_addrlen = sizeof(struct sockaddr_in); -+ info.ai_addr = (struct sockaddr *)addr; -+ } else if (ip.version == 6) { -+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; -+ dns_pton(AF_INET6, host, &(addr->sin6_addr)); -+ addr->sin6_port = *(uint16_t *)(server->buf->array + offset); -+ addr->sin6_family = AF_INET6; -+ info.ai_family = AF_INET6; -+ info.ai_addrlen = sizeof(struct sockaddr_in6); -+ info.ai_addr = (struct sockaddr *)addr; -+ } -+ } else { -+ if (!validate_hostname(host, name_len)) { -+ LOGE("invalid host name"); -+ report_addr(server->fd, MALFORMED); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ need_query = 1; -+ } -+ } else if ((atyp & ADDRTYPE_MASK) == 4) { -+ // IP V6 -+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; -+ size_t in6_addr_len = sizeof(struct in6_addr); -+ addr->sin6_family = AF_INET6; -+ if (server->buf->len >= in6_addr_len + 3) { -+ addr->sin6_addr = *(struct in6_addr *)(server->buf->array + offset); -+ dns_ntop(AF_INET6, (const void *)(server->buf->array + offset), -+ host, INET6_ADDRSTRLEN); -+ offset += in6_addr_len; -+ } else { -+ LOGE("invalid header with addr type %d", atyp); -+ report_addr(server->fd, MALFORMED); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ addr->sin6_port = *(uint16_t *)(server->buf->array + offset); -+ info.ai_family = AF_INET6; -+ info.ai_socktype = SOCK_STREAM; -+ info.ai_protocol = IPPROTO_TCP; -+ info.ai_addrlen = sizeof(struct sockaddr_in6); -+ info.ai_addr = (struct sockaddr *)addr; -+ } -+ -+ if (offset == 1) { -+ LOGE("invalid header with addr type %d", atyp); -+ report_addr(server->fd, MALFORMED); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ -+ port = (*(uint16_t *)(server->buf->array + offset)); -+ -+ offset += 2; -+ -+ if (server->auth) { -+ offset += ONETIMEAUTH_BYTES; -+ } -+ -+ if (server->buf->len < offset) { -+ report_addr(server->fd, MALFORMED); -+ close_and_free_server(EV_A_ server); -+ return; -+ } else { -+ server->buf->len -= offset; -+ memmove(server->buf->array, server->buf->array + offset, server->buf->len); -+ } -+ -+ if (verbose) { -+ if ((atyp & ADDRTYPE_MASK) == 4) -+ LOGI("connect to [%s]:%d", host, ntohs(port)); -+ else -+ LOGI("connect to %s:%d", host, ntohs(port)); -+ } -+ -+ if (server->auth && !ss_check_hash(server->buf, server->chunk, server->d_ctx, BUF_SIZE)) { -+ LOGE("hash error"); -+ report_addr(server->fd, BAD); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ -+ -+ if (!need_query) { -+ remote_t *remote = connect_to_remote(EV_A_ &info, server); -+ -+ if (remote == NULL) { -+ LOGE("connect error"); -+ close_and_free_server(EV_A_ server); -+ return; -+ } else { -+ server->remote = remote; -+ remote->server = server; -+ -+ // XXX: should handle buffer carefully -+ if (server->buf->len > 0) { -+ memcpy(remote->buf->array, server->buf->array, server->buf->len); -+ remote->buf->len = server->buf->len; -+ remote->buf->idx = 0; -+ server->buf->len = 0; -+ server->buf->idx = 0; -+ } -+ -+ // waiting on remote connected event -+ ev_io_stop(EV_A_ & server_recv_ctx->io); -+ ev_io_start(EV_A_ & remote->send_ctx->io); -+ } -+ } else { -+ query_t *query = (query_t *)ss_malloc(sizeof(query_t)); -+ query->server = server; -+ snprintf(query->hostname, 256, "%s", host); -+ -+ server->stage = STAGE_RESOLVE; -+ server->query = resolv_query(host, server_resolve_cb, -+ query_free_cb, query, port); -+ -+ ev_io_stop(EV_A_ & server_recv_ctx->io); -+ } -+ -+ return; -+ } -+ // should not reach here -+ FATAL("server context error"); -+} -+ -+static void -+server_send_cb(EV_P_ ev_io *w, int revents) -+{ -+ server_ctx_t *server_send_ctx = (server_ctx_t *)w; -+ server_t *server = server_send_ctx->server; -+ remote_t *remote = server->remote; -+ -+ if (remote == NULL) { -+ LOGE("invalid server"); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ -+ if (server->buf->len == 0) { -+ // close and free -+ if (verbose) { -+ LOGI("server_send close the connection"); -+ } -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } else { -+ // has data to send -+ ssize_t s = send(server->fd, server->buf->array + server->buf->idx, -+ server->buf->len, 0); -+ if (s == -1) { -+ if (errno != EAGAIN && errno != EWOULDBLOCK) { -+ ERROR("server_send_send"); -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ } -+ return; -+ } else if (s < server->buf->len) { -+ // partly sent, move memory, wait for the next time to send -+ server->buf->len -= s; -+ server->buf->idx += s; -+ return; -+ } else { -+ // all sent out, wait for reading -+ server->buf->len = 0; -+ server->buf->idx = 0; -+ ev_io_stop(EV_A_ & server_send_ctx->io); -+ if (remote != NULL) { -+ ev_io_start(EV_A_ & remote->recv_ctx->io); -+ return; -+ } else { -+ LOGE("invalid remote"); -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ } -+ } -+} -+ -+static void -+block_list_clear_cb(EV_P_ ev_timer *watcher, int revents) -+{ -+ clear_block_list(); -+} -+ -+static void -+server_timeout_cb(EV_P_ ev_timer *watcher, int revents) -+{ -+ server_ctx_t *server_ctx -+ = cork_container_of(watcher, server_ctx_t, watcher); -+ server_t *server = server_ctx->server; -+ remote_t *remote = server->remote; -+ -+ if (verbose) { -+ LOGI("TCP connection timeout"); -+ } -+ -+ if (server->stage < STAGE_PARSE) { -+ if (verbose) { -+ size_t len = server->stage ? -+ server->header_buf->len : server->buf->len; -+#ifdef __MINGW32__ -+ LOGI("incomplete header: %u", len); -+#else -+ LOGI("incomplete header: %zu", len); -+#endif -+ } -+ report_addr(server->fd, SUSPICIOUS); -+ } -+ -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+} -+ -+static void -+query_free_cb(void *data) -+{ -+ if (data != NULL) { -+ ss_free(data); -+ } -+} -+ -+static void -+server_resolve_cb(struct sockaddr *addr, void *data) -+{ -+ query_t *query = (query_t *)data; -+ server_t *server = query->server; -+ struct ev_loop *loop = server->listen_ctx->loop; -+ -+ server->query = NULL; -+ -+ if (addr == NULL) { -+ LOGE("unable to resolve %s", query->hostname); -+ close_and_free_server(EV_A_ server); -+ } else { -+ if (verbose) { -+ LOGI("successfully resolved %s", query->hostname); -+ } -+ -+ struct addrinfo info; -+ memset(&info, 0, sizeof(struct addrinfo)); -+ info.ai_socktype = SOCK_STREAM; -+ info.ai_protocol = IPPROTO_TCP; -+ info.ai_addr = addr; -+ -+ if (addr->sa_family == AF_INET) { -+ info.ai_family = AF_INET; -+ info.ai_addrlen = sizeof(struct sockaddr_in); -+ } else if (addr->sa_family == AF_INET6) { -+ info.ai_family = AF_INET6; -+ info.ai_addrlen = sizeof(struct sockaddr_in6); -+ } -+ -+ remote_t *remote = connect_to_remote(EV_A_ &info, server); -+ -+ if (remote == NULL) { -+ close_and_free_server(EV_A_ server); -+ } else { -+ server->remote = remote; -+ remote->server = server; -+ -+ // XXX: should handle buffer carefully -+ if (server->buf->len > 0) { -+ memcpy(remote->buf->array, server->buf->array + server->buf->idx, -+ server->buf->len); -+ remote->buf->len = server->buf->len; -+ remote->buf->idx = 0; -+ server->buf->len = 0; -+ server->buf->idx = 0; -+ } -+ -+ // listen to remote connected event -+ ev_io_start(EV_A_ & remote->send_ctx->io); -+ } -+ } -+} -+ -+static void -+remote_recv_cb(EV_P_ ev_io *w, int revents) -+{ -+ remote_ctx_t *remote_recv_ctx = (remote_ctx_t *)w; -+ remote_t *remote = remote_recv_ctx->remote; -+ server_t *server = remote->server; -+ -+ if (server == NULL) { -+ LOGE("invalid server"); -+ close_and_free_remote(EV_A_ remote); -+ return; -+ } -+ -+ ev_timer_again(EV_A_ & server->recv_ctx->watcher); -+ -+ ssize_t r = recv(remote->fd, server->buf->array, BUF_SIZE, 0); -+ -+ if (r == 0) { -+ // connection closed -+ if (verbose) { -+ LOGI("remote_recv close the connection"); -+ } -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } else if (r == -1) { -+ if (errno == EAGAIN || errno == EWOULDBLOCK) { -+ // no data -+ // continue to wait for recv -+ return; -+ } else { -+ ERROR("remote recv"); -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ } -+ -+ rx += r; -+ -+ server->buf->len = r; -+ -+ // SSR beg -+ server_info _server_info; -+ if (server->obfs_plugin) { -+ server->obfs_plugin->get_server_info(server->obfs, &_server_info); -+ _server_info.head_len = get_head_size(server->buf->array, server->buf->len, 30); -+ server->obfs_plugin->set_server_info(server->obfs, &_server_info); -+ } -+ -+ if (server->protocol_plugin && server->obfs_compatible_state == 0) { -+ obfs_class *protocol_plugin = server->protocol_plugin; -+ if (protocol_plugin->server_pre_encrypt) { -+ server->buf->len = protocol_plugin->server_pre_encrypt(server->protocol, &server->buf->array, server->buf->len, &server->buf->capacity); -+ } -+ } -+ -+ int err = ss_encrypt(server->buf, server->e_ctx, BUF_SIZE); -+ -+ if (err) { -+ LOGE("invalid password or cipher"); -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ -+ if (server->obfs_plugin && server->obfs_compatible_state == 0) { -+ obfs_class *obfs_plugin = server->obfs_plugin; -+ if (obfs_plugin->server_encode) { -+ server->buf->len = obfs_plugin->server_encode(server->obfs, &server->buf->array, server->buf->len, &server->buf->capacity); -+ } -+ } -+ // SSR end -+ -+ int s = send(server->fd, server->buf->array, server->buf->len, 0); -+ -+ if (s == -1) { -+ if (errno == EAGAIN || errno == EWOULDBLOCK) { -+ // no data, wait for send -+ server->buf->idx = 0; -+ ev_io_stop(EV_A_ & remote_recv_ctx->io); -+ ev_io_start(EV_A_ & server->send_ctx->io); -+ } else { -+ ERROR("remote_recv_send"); -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ } else if (s < server->buf->len) { -+ server->buf->len -= s; -+ server->buf->idx = s; -+ ev_io_stop(EV_A_ & remote_recv_ctx->io); -+ ev_io_start(EV_A_ & server->send_ctx->io); -+ } -+ -+ // Disable TCP_NODELAY after the first response are sent -+ if (!remote->recv_ctx->connected) { -+ int opt = 0; -+ setsockopt(server->fd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); -+ setsockopt(remote->fd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); -+ remote->recv_ctx->connected = 1; -+ } -+} -+ -+static void -+remote_send_cb(EV_P_ ev_io *w, int revents) -+{ -+ remote_ctx_t *remote_send_ctx = (remote_ctx_t *)w; -+ remote_t *remote = remote_send_ctx->remote; -+ server_t *server = remote->server; -+ -+ if (server == NULL) { -+ LOGE("invalid server"); -+ close_and_free_remote(EV_A_ remote); -+ return; -+ } -+ -+ if (!remote_send_ctx->connected) { -+ struct sockaddr_storage addr; -+ socklen_t len = sizeof(struct sockaddr_storage); -+ memset(&addr, 0, len); -+ int r = getpeername(remote->fd, (struct sockaddr *)&addr, &len); -+ if (r == 0) { -+ if (verbose) { -+ LOGI("remote connected"); -+ } -+ remote_send_ctx->connected = 1; -+ -+ // Clear the state of this address in the block list -+ reset_addr(server->fd); -+ -+ if (remote->buf->len == 0) { -+ server->stage = STAGE_STREAM; -+ ev_io_stop(EV_A_ & remote_send_ctx->io); -+ ev_io_start(EV_A_ & server->recv_ctx->io); -+ ev_io_start(EV_A_ & remote->recv_ctx->io); -+ return; -+ } -+ } else { -+ ERROR("getpeername"); -+ // not connected -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } -+ } -+ -+ if (remote->buf->len == 0) { -+ // close and free -+ if (verbose) { -+ LOGI("remote_send close the connection"); -+ } -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ return; -+ } else { -+ // has data to send -+ ssize_t s = send(remote->fd, remote->buf->array + remote->buf->idx, -+ remote->buf->len, 0); -+ if (s == -1) { -+ if (errno != EAGAIN && errno != EWOULDBLOCK) { -+ ERROR("remote_send_send"); -+ // close and free -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ } -+ return; -+ } else if (s < remote->buf->len) { -+ // partly sent, move memory, wait for the next time to send -+ remote->buf->len -= s; -+ remote->buf->idx += s; -+ return; -+ } else { -+ // all sent out, wait for reading -+ remote->buf->len = 0; -+ remote->buf->idx = 0; -+ ev_io_stop(EV_A_ & remote_send_ctx->io); -+ if (server != NULL) { -+ ev_io_start(EV_A_ & server->recv_ctx->io); -+ if (server->stage != STAGE_STREAM) { -+ server->stage = STAGE_STREAM; -+ ev_io_start(EV_A_ & remote->recv_ctx->io); -+ } -+ } else { -+ LOGE("invalid server"); -+ close_and_free_remote(EV_A_ remote); -+ close_and_free_server(EV_A_ server); -+ } -+ return; -+ } -+ } -+} -+ -+static remote_t * -+new_remote(int fd) -+{ -+ if (verbose) { -+ remote_conn++; -+ } -+ -+ remote_t *remote; -+ -+ remote = ss_malloc(sizeof(remote_t)); -+ remote->recv_ctx = ss_malloc(sizeof(remote_ctx_t)); -+ remote->send_ctx = ss_malloc(sizeof(remote_ctx_t)); -+ remote->buf = ss_malloc(sizeof(buffer_t)); -+ remote->fd = fd; -+ remote->recv_ctx->remote = remote; -+ remote->recv_ctx->connected = 0; -+ remote->send_ctx->remote = remote; -+ remote->send_ctx->connected = 0; -+ remote->server = NULL; -+ -+ ev_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, EV_READ); -+ ev_io_init(&remote->send_ctx->io, remote_send_cb, fd, EV_WRITE); -+ -+ balloc(remote->buf, BUF_SIZE); -+ -+ return remote; -+} -+ -+static void -+free_remote(remote_t *remote) -+{ -+ if (remote->server != NULL) { -+ remote->server->remote = NULL; -+ } -+ if (remote->buf != NULL) { -+ bfree(remote->buf); -+ ss_free(remote->buf); -+ } -+ ss_free(remote->recv_ctx); -+ ss_free(remote->send_ctx); -+ ss_free(remote); -+} -+ -+static void -+close_and_free_remote(EV_P_ remote_t *remote) -+{ -+ if (remote != NULL) { -+ ev_io_stop(EV_A_ & remote->send_ctx->io); -+ ev_io_stop(EV_A_ & remote->recv_ctx->io); -+ close(remote->fd); -+ free_remote(remote); -+ if (verbose) { -+ remote_conn--; -+ LOGI("current remote connection: %d", remote_conn); -+ } -+ } -+} -+ -+static server_t * -+new_server(int fd, listen_ctx_t *listener) -+{ -+ if (verbose) { -+ server_conn++; -+ } -+ -+ server_t *server; -+ server = ss_malloc(sizeof(server_t)); -+ -+ memset(server, 0, sizeof(server_t)); -+ -+ server->recv_ctx = ss_malloc(sizeof(server_ctx_t)); -+ server->send_ctx = ss_malloc(sizeof(server_ctx_t)); -+ server->buf = ss_malloc(sizeof(buffer_t)); -+ server->header_buf = ss_malloc(sizeof(buffer_t)); -+ server->fd = fd; -+ server->recv_ctx->server = server; -+ server->recv_ctx->connected = 0; -+ server->send_ctx->server = server; -+ server->send_ctx->connected = 0; -+ server->stage = STAGE_INIT; -+ server->query = NULL; -+ server->listen_ctx = listener; -+ server->remote = NULL; -+ -+ if (listener->method) { -+ server->e_ctx = ss_malloc(sizeof(enc_ctx_t)); -+ server->d_ctx = ss_malloc(sizeof(enc_ctx_t)); -+ enc_ctx_init(listener->method, server->e_ctx, 1); -+ enc_ctx_init(listener->method, server->d_ctx, 0); -+ } else { -+ server->e_ctx = NULL; -+ server->d_ctx = NULL; -+ } -+ -+ int request_timeout = min(MAX_REQUEST_TIMEOUT, listener->timeout) -+ + rand() % MAX_REQUEST_TIMEOUT; -+ -+ ev_io_init(&server->recv_ctx->io, server_recv_cb, fd, EV_READ); -+ ev_io_init(&server->send_ctx->io, server_send_cb, fd, EV_WRITE); -+ ev_timer_init(&server->recv_ctx->watcher, server_timeout_cb, -+ request_timeout, listener->timeout); -+ -+ balloc(server->buf, BUF_SIZE); -+ balloc(server->header_buf, BUF_SIZE); -+ -+ server->chunk = (chunk_t *)malloc(sizeof(chunk_t)); -+ memset(server->chunk, 0, sizeof(chunk_t)); -+ server->chunk->buf = ss_malloc(sizeof(buffer_t)); -+ memset(server->chunk->buf, 0, sizeof(buffer_t)); -+ -+ cork_dllist_add(&connections, &server->entries); -+ -+ return server; -+} -+ -+static void -+free_server(server_t *server) -+{ -+ cork_dllist_remove(&server->entries); -+ -+ if (server->chunk != NULL) { -+ if (server->chunk->buf != NULL) { -+ bfree(server->chunk->buf); -+ ss_free(server->chunk->buf); -+ } -+ ss_free(server->chunk); -+ } -+ if (server->remote != NULL) { -+ server->remote->server = NULL; -+ } -+ if (server->e_ctx != NULL) { -+ cipher_context_release(&server->e_ctx->evp); -+ ss_free(server->e_ctx); -+ } -+ if (server->d_ctx != NULL) { -+ cipher_context_release(&server->d_ctx->evp); -+ ss_free(server->d_ctx); -+ } -+ if (server->buf != NULL) { -+ bfree(server->buf); -+ ss_free(server->buf); -+ } -+ if (server->header_buf != NULL) { -+ bfree(server->header_buf); -+ ss_free(server->header_buf); -+ } -+ -+ ss_free(server->recv_ctx); -+ ss_free(server->send_ctx); -+ ss_free(server); -+} -+ -+static void -+close_and_free_server(EV_P_ server_t *server) -+{ -+ if (server != NULL) { -+ if (server->query != NULL) { -+ resolv_cancel(server->query); -+ server->query = NULL; -+ } -+ ev_io_stop(EV_A_ & server->send_ctx->io); -+ ev_io_stop(EV_A_ & server->recv_ctx->io); -+ ev_timer_stop(EV_A_ & server->recv_ctx->watcher); -+ close(server->fd); -+ free_server(server); -+ if (verbose) { -+ server_conn--; -+ LOGI("current server connection: %d", server_conn); -+ } -+ } -+} -+ -+static void -+signal_cb(EV_P_ ev_signal *w, int revents) -+{ -+ if (revents & EV_SIGNAL) { -+ switch (w->signum) { -+ case SIGINT: -+ case SIGTERM: -+ ev_unloop(EV_A_ EVUNLOOP_ALL); -+ } -+ } -+} -+ -+static void -+accept_cb(EV_P_ ev_io *w, int revents) -+{ -+ listen_ctx_t *listener = (listen_ctx_t *)w; -+ int serverfd = accept(listener->fd, NULL, NULL); -+ if (serverfd == -1) { -+ ERROR("accept"); -+ return; -+ } -+ -+ char *peer_name = get_peer_name(serverfd); -+ if (peer_name != NULL) { -+ int in_white_list = 0; -+ if (acl) { -+ if ((get_acl_mode() == BLACK_LIST && acl_match_host(peer_name) == 1) -+ || (get_acl_mode() == WHITE_LIST && acl_match_host(peer_name) >= 0)) { -+ LOGE("Access denied from %s", peer_name); -+ close(serverfd); -+ return; -+ } else if (acl_match_host(peer_name) == -1) { -+ in_white_list = 1; -+ } -+ } -+ if (!in_white_list && check_block_list(peer_name)) { -+ LOGE("block all requests from %s", peer_name); -+#ifdef __linux__ -+ set_linger(serverfd); -+#endif -+ close(serverfd); -+ return; -+ } -+ } -+ -+ int opt = 1; -+ setsockopt(serverfd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); -+#ifdef SO_NOSIGPIPE -+ setsockopt(serverfd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); -+#endif -+ setnonblocking(serverfd); -+ -+ if (verbose) { -+ LOGI("accept a connection"); -+ } -+ -+ server_t *server = new_server(serverfd, listener); -+ -+ // SSR beg -+ server->obfs_plugin = new_obfs_class(server->listen_ctx->obfs_name); -+ if (server->obfs_plugin) { -+ server->obfs = server->obfs_plugin->new_obfs(); -+ server->obfs_compatible_state = 0; -+ } -+ server->protocol_plugin = new_obfs_class(server->listen_ctx->protocol_name); -+ if (server->protocol_plugin) { -+ server->protocol = server->protocol_plugin->new_obfs(); -+ server->protocol_compatible_state = 0; -+ } -+ server_info _server_info; -+ memset(&_server_info, 0, sizeof(server_info)); -+ _server_info.param = server->listen_ctx->obfs_param; -+ if(server->obfs_plugin) -+ _server_info.g_data = server->obfs_plugin->init_data(); -+ _server_info.head_len = 7; -+ _server_info.iv = server->e_ctx->evp.iv; -+ _server_info.iv_len = enc_get_iv_len(); -+ _server_info.key = enc_get_key(); -+ _server_info.key_len = enc_get_key_len(); -+ _server_info.tcp_mss = 1460; -+ -+ if (server->obfs_plugin) -+ server->obfs_plugin->set_server_info(server->obfs, &_server_info); -+ -+ _server_info.param = server->listen_ctx->protocol_param; -+ if (server->protocol_plugin) -+ _server_info.g_data = server->protocol_plugin->init_data(); -+ -+ if (server->protocol_plugin) -+ server->protocol_plugin->set_server_info(server->protocol, &_server_info); -+ // SSR end -+ -+ ev_io_start(EV_A_ & server->recv_ctx->io); -+ ev_timer_start(EV_A_ & server->recv_ctx->watcher); -+} -+ -+int -+main(int argc, char **argv) -+{ -+ int i, c; -+ int pid_flags = 0; -+ int mptcp = 0; -+ int firewall = 0; -+ int mtu = 0; -+ char *user = NULL; -+ char *password = NULL; -+ char *timeout = NULL; -+ char *protocol = NULL; // SSR -+ char *protocol_param = NULL; // SSR -+ char *method = NULL; -+ char *obfs = NULL; // SSR -+ char *obfs_param = NULL; // SSR -+ char *pid_path = NULL; -+ char *conf_path = NULL; -+ char *iface = NULL; -+ -+ int server_num = 0; -+ const char *server_host[MAX_REMOTE_NUM]; -+ -+ char *nameservers[MAX_DNS_NUM + 1]; -+ int nameserver_num = 0; -+ -+ int option_index = 0; -+ static struct option long_options[] = { -+ { "fast-open", no_argument, 0, 0 }, -+ { "acl", required_argument, 0, 0 }, -+ { "manager-address", required_argument, 0, 0 }, -+ { "mtu", required_argument, 0, 0 }, -+ { "help", no_argument, 0, 0 }, -+#ifdef __linux__ -+ { "mptcp", no_argument, 0, 0 }, -+ { "firewall", no_argument, 0, 0 }, -+#endif -+ { 0, 0, 0, 0 } -+ }; -+ -+ opterr = 0; -+ -+ USE_TTY(); -+ -+ while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:b:c:i:d:a:n:O:o:G:g:huUvA6", -+ long_options, &option_index)) != -1) { -+ switch (c) { -+ case 0: -+ if (option_index == 0) { -+ fast_open = 1; -+ } else if (option_index == 1) { -+ LOGI("initializing acl..."); -+ acl = !init_acl(optarg); -+ } else if (option_index == 2) { -+ manager_address = optarg; -+ } else if (option_index == 3) { -+ mtu = atoi(optarg); -+ LOGI("set MTU to %d", mtu); -+ } else if (option_index == 4) { -+ usage(); -+ exit(EXIT_SUCCESS); -+ } else if (option_index == 5) { -+ mptcp = 1; -+ LOGI("enable multipath TCP"); -+ } else if (option_index == 6) { -+ firewall = 1; -+ LOGI("enable firewall rules"); -+ } -+ break; -+ case 's': -+ if (server_num < MAX_REMOTE_NUM) { -+ server_host[server_num++] = optarg; -+ } -+ break; -+ case 'b': -+ bind_address = optarg; -+ break; -+ case 'p': -+ server_port = optarg; -+ break; -+ case 'k': -+ password = optarg; -+ break; -+ case 'f': -+ pid_flags = 1; -+ pid_path = optarg; -+ break; -+ case 't': -+ timeout = optarg; -+ break; -+ // SSR beg -+ case 'O': -+ protocol = optarg; -+ break; -+ case 'm': -+ method = optarg; -+ break; -+ case 'o': -+ obfs = optarg; -+ break; -+ case 'G': -+ protocol_param = optarg; -+ break; -+ case 'g': -+ obfs_param = optarg; -+ break; -+ // SSR end -+ case 'c': -+ conf_path = optarg; -+ break; -+ case 'i': -+ iface = optarg; -+ break; -+ case 'd': -+ if (nameserver_num < MAX_DNS_NUM) { -+ nameservers[nameserver_num++] = optarg; -+ } -+ break; -+ case 'a': -+ user = optarg; -+ break; -+#ifdef HAVE_SETRLIMIT -+ case 'n': -+ nofile = atoi(optarg); -+ break; -+#endif -+ case 'u': -+ mode = TCP_AND_UDP; -+ break; -+ case 'U': -+ mode = UDP_ONLY; -+ break; -+ case 'v': -+ verbose = 1; -+ break; -+ case 'h': -+ usage(); -+ exit(EXIT_SUCCESS); -+ case 'A': -+ auth = 1; -+ break; -+ case '6': -+ ipv6first = 1; -+ break; -+ case '?': -+ // The option character is not recognized. -+ LOGE("Unrecognized option: %s", optarg); -+ opterr = 1; -+ break; -+ } -+ } -+ -+ if (opterr) { -+ usage(); -+ exit(EXIT_FAILURE); -+ } -+ -+ if (argc == 1) { -+ if (conf_path == NULL) { -+ conf_path = DEFAULT_CONF_PATH; -+ } -+ } -+ -+ if (conf_path != NULL) { -+ jconf_t *conf = read_jconf(conf_path); -+ if (server_num == 0) { -+ server_num = conf->remote_num; -+ for (i = 0; i < server_num; i++) -+ server_host[i] = conf->remote_addr[i].host; -+ } -+ if (server_port == NULL) { -+ server_port = conf->remote_port; -+ } -+ if (password == NULL) { -+ password = conf->password; -+ } -+ // SSR beg -+ if (protocol == NULL) { -+ protocol = conf->protocol; -+ LOGI("protocol %s", protocol); -+ } -+ if (protocol_param == NULL) { -+ protocol_param = conf->protocol_param; -+ LOGI("protocol_param %s", obfs_param); -+ } -+ if (method == NULL) { -+ method = conf->method; -+ LOGI("method %s", method); -+ } -+ if (obfs == NULL) { -+ obfs = conf->obfs; -+ LOGI("obfs %s", obfs); -+ } -+ if (obfs_param == NULL) { -+ obfs_param = conf->obfs_param; -+ LOGI("obfs_param %s", obfs_param); -+ } -+ // SSR end -+ if (timeout == NULL) { -+ timeout = conf->timeout; -+ } -+ if (user == NULL) { -+ user = conf->user; -+ } -+ if (auth == 0) { -+ auth = conf->auth; -+ } -+ if (mode == TCP_ONLY) { -+ mode = conf->mode; -+ } -+ if (mtu == 0) { -+ mtu = conf->mtu; -+ } -+ if (mptcp == 0) { -+ mptcp = conf->mptcp; -+ } -+#ifdef TCP_FASTOPEN -+ if (fast_open == 0) { -+ fast_open = conf->fast_open; -+ } -+#endif -+#ifdef HAVE_SETRLIMIT -+ if (nofile == 0) { -+ nofile = conf->nofile; -+ } -+#endif -+ if (conf->nameserver != NULL) { -+ nameservers[nameserver_num++] = conf->nameserver; -+ } -+ if (ipv6first == 0) { -+ ipv6first = conf->ipv6_first; -+ } -+ } -+ -+ //_compatible -+ if(strlen(protocol)>11) -+ { -+ char *text; -+ text = (char*)malloc(12); -+ memcpy(text, protocol + strlen(protocol) - 11, 12); -+ -+ if(strcmp(text, "_compatible") == 0) -+ { -+ free(text); -+ text = (char*)malloc(strlen(protocol) - 11); -+ memcpy(text, protocol, strlen(protocol) - 11); -+ int length = strlen(protocol) - 11; -+ free(protocol); -+ obfs = (char*)malloc(length); -+ memset(protocol, 0x00, length); -+ memcpy(protocol, text, length); -+ LOGI("protocol compatible enable, %s", protocol); -+ free(text); -+ protocol_compatible = 1; -+ } -+ } -+ -+ if(strlen(obfs)>11) -+ { -+ char *text; -+ text = (char*)malloc(12); -+ memcpy(text, obfs + strlen(obfs) - 11, 12); -+ -+ if(strcmp(text, "_compatible") == 0) -+ { -+ free(text); -+ text = (char*)malloc(strlen(obfs) - 11); -+ memcpy(text, obfs, strlen(obfs) - 11); -+ int length = strlen(obfs) - 11; -+ free(obfs); -+ obfs = (char*)malloc(length); -+ memset(obfs, 0x00, length); -+ memcpy(obfs, text, length); -+ LOGI("obfs compatible enable, %s", obfs); -+ free(text); -+ obfs_compatible = 1; -+ } -+ } -+ -+ -+ if (server_num == 0) { -+ server_host[server_num++] = NULL; -+ } -+ -+ if (server_num == 0 || server_port == NULL || password == NULL) { -+ usage(); -+ exit(EXIT_FAILURE); -+ } -+ -+ if (protocol && strcmp(protocol, "verify_sha1") == 0) { -+ auth = 1; -+ protocol = NULL; -+ } -+ -+ if (method == NULL) { -+ method = "rc4-md5"; -+ } -+ -+ if (timeout == NULL) { -+ timeout = "60"; -+ } -+ -+#ifdef HAVE_SETRLIMIT -+ /* -+ * no need to check the return value here since we will show -+ * the user an error message if setrlimit(2) fails -+ */ -+ if (nofile > 1024) { -+ if (verbose) { -+ LOGI("setting NOFILE to %d", nofile); -+ } -+ set_nofile(nofile); -+ } -+#endif -+ -+ if (pid_flags) { -+ USE_SYSLOG(argv[0]); -+ daemonize(pid_path); -+ } -+ -+ if (ipv6first) { -+ LOGI("resolving hostname to IPv6 address first"); -+ } -+ -+ if (fast_open == 1) { -+#ifdef TCP_FASTOPEN -+ LOGI("using tcp fast open"); -+#else -+ LOGE("tcp fast open is not supported by this environment"); -+ fast_open = 0; -+#endif -+ } -+ -+ if (auth) { -+ LOGI("onetime authentication enabled"); -+ } -+ -+ if (mode != TCP_ONLY) { -+ LOGI("UDP relay enabled"); -+ } -+ -+ if (mode == UDP_ONLY) { -+ LOGI("TCP relay disabled"); -+ } -+ -+#ifdef __MINGW32__ -+ winsock_init(); -+#else -+ // ignore SIGPIPE -+ signal(SIGPIPE, SIG_IGN); -+ signal(SIGCHLD, SIG_IGN); -+ signal(SIGABRT, SIG_IGN); -+#endif -+ -+ struct ev_signal sigint_watcher; -+ struct ev_signal sigterm_watcher; -+ 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); -+ -+ // setup keys -+ LOGI("initializing ciphers... %s", method); -+ int m = enc_init(password, method); -+ -+ // initialize ev loop -+ struct ev_loop *loop = EV_DEFAULT; -+ -+ // setup udns -+ if (nameserver_num == 0) { -+#ifdef __MINGW32__ -+ nameservers[nameserver_num++] = "8.8.8.8"; -+ resolv_init(loop, nameservers, nameserver_num, ipv6first); -+#else -+ resolv_init(loop, NULL, 0, ipv6first); -+#endif -+ } else { -+ resolv_init(loop, nameservers, nameserver_num, ipv6first); -+ } -+ -+ for (int i = 0; i < nameserver_num; i++) -+ LOGI("using nameserver: %s", nameservers[i]); -+ -+ // initialize listen context -+ listen_ctx_t listen_ctx_list[server_num]; -+ -+ // bind to each interface -+ while (server_num > 0) { -+ int index = --server_num; -+ const char *host = server_host[index]; -+ -+ if (mode != UDP_ONLY) { -+ // Bind to port -+ int listenfd; -+ listenfd = create_and_bind(host, server_port, mptcp); -+ if (listenfd == -1) { -+ FATAL("bind() error"); -+ } -+ if (listen(listenfd, SSMAXCONN) == -1) { -+ FATAL("listen() error"); -+ } -+ setfastopen(listenfd); -+ setnonblocking(listenfd); -+ listen_ctx_t *listen_ctx = &listen_ctx_list[index]; -+ -+ // Setup proxy context -+ listen_ctx->timeout = atoi(timeout); -+ listen_ctx->fd = listenfd; -+ listen_ctx->method = m; -+ listen_ctx->iface = iface; -+ -+ // SSR beg -+ listen_ctx->protocol_name = protocol; -+ listen_ctx->protocol_param = protocol_param; -+ listen_ctx->method = m; -+ listen_ctx->obfs_name = obfs; -+ listen_ctx->obfs_param = obfs_param; -+ listen_ctx->list_protocol_global = malloc(sizeof(void *)); -+ listen_ctx->list_obfs_global = malloc(sizeof(void *)); -+ memset(listen_ctx->list_protocol_global, 0, sizeof(void *)); -+ memset(listen_ctx->list_obfs_global, 0, sizeof(void *)); -+ // SSR end -+ -+ listen_ctx->loop = loop; -+ -+ ev_io_init(&listen_ctx->io, accept_cb, listenfd, EV_READ); -+ ev_io_start(loop, &listen_ctx->io); -+ } -+ -+ // Setup UDP -+ if (mode != TCP_ONLY) { -+ init_udprelay(server_host[index], server_port, mtu, m, -+ auth, atoi(timeout), iface, protocol, protocol_param); -+ } -+ -+ if (host && strcmp(host, ":") > 0) -+ LOGI("listening at [%s]:%s", host, server_port); -+ else -+ LOGI("listening at %s:%s", host ? host : "*", server_port); -+ } -+ -+ if (manager_address != NULL) { -+ ev_timer_init(&stat_update_watcher, stat_update_cb, UPDATE_INTERVAL, UPDATE_INTERVAL); -+ ev_timer_start(EV_DEFAULT, &stat_update_watcher); -+ } -+ -+ ev_timer_init(&block_list_watcher, block_list_clear_cb, UPDATE_INTERVAL, UPDATE_INTERVAL); -+ ev_timer_start(EV_DEFAULT, &block_list_watcher); -+ -+ // setuid -+ if (user != NULL && ! run_as(user)) { -+ FATAL("failed to switch user"); -+ } -+ -+#ifndef __MINGW32__ -+ if (geteuid() == 0){ -+ LOGI("running from root user"); -+ } else if (firewall) { -+ LOGE("firewall setup requires running from root user"); -+ exit(-1); -+ } -+#endif -+ -+ // init block list -+ init_block_list(firewall); -+ -+ // Init connections -+ cork_dllist_init(&connections); -+ -+ // start ev loop -+ ev_run(loop, 0); -+ -+ if (verbose) { -+ LOGI("closed gracefully"); -+ } -+ -+ // Free block list -+ free_block_list(); -+ -+ if (manager_address != NULL) { -+ ev_timer_stop(EV_DEFAULT, &stat_update_watcher); -+ } -+ ev_timer_stop(EV_DEFAULT, &block_list_watcher); -+ -+ // Clean up -+ for (int i = 0; i <= server_num; i++) { -+ listen_ctx_t *listen_ctx = &listen_ctx_list[i]; -+ if (mode != UDP_ONLY) { -+ ev_io_stop(loop, &listen_ctx->io); -+ close(listen_ctx->fd); -+ } -+ } -+ -+ if (mode != UDP_ONLY) { -+ free_connections(loop); -+ } -+ -+ if (mode != TCP_ONLY) { -+ free_udprelay(); -+ } -+ -+ resolv_shutdown(loop); -+ -+#ifdef __MINGW32__ -+ winsock_cleanup(); -+#endif -+ -+ ev_signal_stop(EV_DEFAULT, &sigint_watcher); -+ ev_signal_stop(EV_DEFAULT, &sigterm_watcher); -+ -+ return 0; -+} -diff --git a/server/server.h b/server/server.h -new file mode 100644 -index 0000000..4cd3cf6 ---- /dev/null -+++ b/server/server.h -@@ -0,0 +1,115 @@ -+/* -+ * server.h - Define shadowsocks server's buffers and callbacks -+ * -+ * Copyright (C) 2013 - 2016, 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 _SERVER_H -+#define _SERVER_H -+ -+#include -+#include -+#include -+ -+#include "encrypt.h" -+#include "jconf.h" -+#include "resolv.h" -+#include "obfs.h" -+#include "protocol.h" -+ -+#include "common.h" -+ -+typedef struct listen_ctx { -+ ev_io io; -+ int fd; -+ int timeout; -+ int method; -+ char *iface; -+ struct ev_loop *loop; -+ -+ // SSR -+ char *protocol_name; -+ char *protocol_param; -+ char *obfs_name; -+ char *obfs_param; -+ void **list_protocol_global; -+ void **list_obfs_global; -+} listen_ctx_t; -+ -+typedef struct server_ctx { -+ ev_io io; -+ ev_timer watcher; -+ int connected; -+ struct server *server; -+} server_ctx_t; -+ -+typedef struct server { -+ int fd; -+ int stage; -+ buffer_t *buf; -+ ssize_t buf_capacity; -+ buffer_t *header_buf; -+ -+ int auth; -+ struct chunk *chunk; -+ -+ struct enc_ctx *e_ctx; -+ struct enc_ctx *d_ctx; -+ struct server_ctx *recv_ctx; -+ struct server_ctx *send_ctx; -+ struct listen_ctx *listen_ctx; -+ struct remote *remote; -+ -+ struct ResolvQuery *query; -+ -+ struct cork_dllist_item entries; -+ -+ // SSR -+ obfs *protocol; -+ obfs *obfs; -+ obfs_class *protocol_plugin; -+ obfs_class *obfs_plugin; -+ int obfs_compatible_state; -+ int protocol_compatible_state; -+} server_t; -+ -+typedef struct query { -+ server_t *server; -+ char hostname[257]; -+} query_t; -+ -+typedef struct remote_ctx { -+ ev_io io; -+ int connected; -+ struct remote *remote; -+} remote_ctx_t; -+ -+typedef struct remote { -+ int fd; -+ buffer_t *buf; -+ ssize_t buf_capacity; -+ struct remote_ctx *recv_ctx; -+ struct remote_ctx *send_ctx; -+ struct server *server; -+ -+ // SSR -+ int remote_index; -+} remote_t; -+ -+#endif // _SERVER_H -diff --git a/server/tls.c b/server/tls.c -new file mode 100644 -index 0000000..5c42216 ---- /dev/null -+++ b/server/tls.c -@@ -0,0 +1,263 @@ -+/* -+ * Copyright (c) 2011 and 2012, Dustin Lundquist -+ * 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. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. -+ */ -+/* -+ * This is a minimal TLS implementation intended only to parse the server name -+ * extension. This was created based primarily on Wireshark dissection of a -+ * TLS handshake and RFC4366. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include -+#include /* malloc() */ -+#include /* strncpy() */ -+ -+#ifndef __MINGW32__ -+#include -+#else -+#include -+#endif -+ -+#include "tls.h" -+#include "protocol.h" -+#include "utils.h" -+ -+#define SERVER_NAME_LEN 256 -+#define TLS_HEADER_LEN 5 -+#define TLS_HANDSHAKE_CONTENT_TYPE 0x16 -+#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 -+ -+#ifndef MIN -+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) -+#endif -+ -+extern int verbose; -+ -+static int parse_tls_header(const char *, size_t, char **); -+static int parse_extensions(const char *, size_t, char **); -+static int parse_server_name_extension(const char *, size_t, char **); -+ -+static const protocol_t tls_protocol_st = { -+ .default_port = 443, -+ .parse_packet = &parse_tls_header, -+}; -+const protocol_t *const tls_protocol = &tls_protocol_st; -+ -+/* Parse a TLS packet for the Server Name Indication extension in the client -+ * hello handshake, returning the first servername found (pointer to static -+ * array) -+ * -+ * Returns: -+ * >=0 - length of the hostname and updates *hostname -+ * caller is responsible for freeing *hostname -+ * -1 - Incomplete request -+ * -2 - No Host header included in this request -+ * -3 - Invalid hostname pointer -+ * -4 - malloc failure -+ * < -4 - Invalid TLS client hello -+ */ -+static int -+parse_tls_header(const char *data, size_t data_len, char **hostname) -+{ -+ char tls_content_type; -+ char tls_version_major; -+ char tls_version_minor; -+ size_t pos = TLS_HEADER_LEN; -+ size_t len; -+ -+ if (hostname == NULL) -+ return -3; -+ -+ /* Check that our TCP payload is at least large enough for a TLS header */ -+ if (data_len < TLS_HEADER_LEN) -+ return -1; -+ -+ /* SSL 2.0 compatible Client Hello -+ * -+ * High bit of first byte (length) and content type is Client Hello -+ * -+ * See RFC5246 Appendix E.2 -+ */ -+ if (data[0] & 0x80 && data[2] == 1) { -+ if (verbose) -+ LOGI("Received SSL 2.0 Client Hello which can not support SNI."); -+ return -2; -+ } -+ -+ tls_content_type = data[0]; -+ if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { -+ if (verbose) -+ LOGI("Request did not begin with TLS handshake."); -+ return -5; -+ } -+ -+ tls_version_major = data[1]; -+ tls_version_minor = data[2]; -+ if (tls_version_major < 3) { -+ if (verbose) -+ LOGI("Received SSL %d.%d handshake which can not support SNI.", -+ tls_version_major, tls_version_minor); -+ -+ return -2; -+ } -+ -+ /* TLS record length */ -+ len = ((unsigned char)data[3] << 8) + -+ (unsigned char)data[4] + TLS_HEADER_LEN; -+ data_len = MIN(data_len, len); -+ -+ /* Check we received entire TLS record length */ -+ if (data_len < len) -+ return -1; -+ -+ /* -+ * Handshake -+ */ -+ if (pos + 1 > data_len) { -+ return -5; -+ } -+ if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { -+ if (verbose) -+ LOGI("Not a client hello"); -+ -+ return -5; -+ } -+ -+ /* Skip past fixed length records: -+ * 1 Handshake Type -+ * 3 Length -+ * 2 Version (again) -+ * 32 Random -+ * to Session ID Length -+ */ -+ pos += 38; -+ -+ /* Session ID */ -+ if (pos + 1 > data_len) -+ return -5; -+ len = (unsigned char)data[pos]; -+ pos += 1 + len; -+ -+ /* Cipher Suites */ -+ if (pos + 2 > data_len) -+ return -5; -+ len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; -+ pos += 2 + len; -+ -+ /* Compression Methods */ -+ if (pos + 1 > data_len) -+ return -5; -+ len = (unsigned char)data[pos]; -+ pos += 1 + len; -+ -+ if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) { -+ if (verbose) -+ LOGI("Received SSL 3.0 handshake without extensions"); -+ return -2; -+ } -+ -+ /* Extensions */ -+ if (pos + 2 > data_len) -+ return -5; -+ len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; -+ pos += 2; -+ -+ if (pos + len > data_len) -+ return -5; -+ return parse_extensions(data + pos, len, hostname); -+} -+ -+static int -+parse_extensions(const char *data, size_t data_len, char **hostname) -+{ -+ size_t pos = 0; -+ size_t len; -+ -+ /* Parse each 4 bytes for the extension header */ -+ while (pos + 4 <= data_len) { -+ /* Extension Length */ -+ len = ((unsigned char)data[pos + 2] << 8) + -+ (unsigned char)data[pos + 3]; -+ -+ /* Check if it's a server name extension */ -+ if (data[pos] == 0x00 && data[pos + 1] == 0x00) { -+ /* There can be only one extension of each type, so we break -+ * our state and move p to beinnging of the extension here */ -+ if (pos + 4 + len > data_len) -+ return -5; -+ return parse_server_name_extension(data + pos + 4, len, hostname); -+ } -+ pos += 4 + len; /* Advance to the next extension header */ -+ } -+ /* Check we ended where we expected to */ -+ if (pos != data_len) -+ return -5; -+ -+ return -2; -+} -+ -+static int -+parse_server_name_extension(const char *data, size_t data_len, -+ char **hostname) -+{ -+ size_t pos = 2; /* skip server name list length */ -+ size_t len; -+ -+ while (pos + 3 < data_len) { -+ len = ((unsigned char)data[pos + 1] << 8) + -+ (unsigned char)data[pos + 2]; -+ -+ if (pos + 3 + len > data_len) -+ return -5; -+ -+ switch (data[pos]) { /* name type */ -+ case 0x00: /* host_name */ -+ *hostname = malloc(len + 1); -+ if (*hostname == NULL) { -+ ERROR("malloc() failure"); -+ return -4; -+ } -+ -+ strncpy(*hostname, data + pos + 3, len); -+ -+ (*hostname)[len] = '\0'; -+ -+ return len; -+ default: -+ if (verbose) -+ LOGI("Unknown server name extension name type: %d", -+ data[pos]); -+ } -+ pos += 3 + len; -+ } -+ /* Check we ended where we expected to */ -+ if (pos != data_len) -+ return -5; -+ -+ return -2; -+} -diff --git a/server/tls.h b/server/tls.h -new file mode 100644 -index 0000000..3998913 ---- /dev/null -+++ b/server/tls.h -@@ -0,0 +1,33 @@ -+/* -+ * Copyright (c) 2011 and 2012, Dustin Lundquist -+ * 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. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. -+ */ -+#ifndef TLS_H -+#define TLS_H -+ -+#include "protocol.h" -+ -+const protocol_t *const tls_protocol; -+ -+#endif -diff --git a/server/tls1.2_ticket.c b/server/tls1.2_ticket.c -new file mode 100644 -index 0000000..88970c0 ---- /dev/null -+++ b/server/tls1.2_ticket.c -@@ -0,0 +1,609 @@ -+ -+#include "tls1.2_ticket.h" -+#include "list.c" -+ -+typedef struct tls12_ticket_auth_global_data { -+ uint8_t local_client_id[32]; -+ List client_data; -+ time_t startup_time; -+}tls12_ticket_auth_global_data; -+ -+typedef struct tls12_ticket_auth_local_data { -+ int handshake_status; -+ char *send_buffer; -+ int send_buffer_size; -+ char *recv_buffer; -+ int recv_buffer_size; -+}tls12_ticket_auth_local_data; -+ -+void tls12_ticket_auth_local_data_init(tls12_ticket_auth_local_data* local) { -+ local->handshake_status = 0; -+ local->send_buffer = malloc(0); -+ local->send_buffer_size = 0; -+ local->recv_buffer = malloc(0); -+ local->recv_buffer_size = 0; -+} -+ -+void * tls12_ticket_auth_init_data() { -+ tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)malloc(sizeof(tls12_ticket_auth_global_data)); -+ rand_bytes(global->local_client_id, 32); -+ global->client_data = list_init(22); -+ global->startup_time = time(NULL); -+ return global; -+} -+ -+obfs * tls12_ticket_auth_new_obfs() { -+ obfs * self = new_obfs(); -+ self->l_data = malloc(sizeof(tls12_ticket_auth_local_data)); -+ tls12_ticket_auth_local_data_init((tls12_ticket_auth_local_data*)self->l_data); -+ return self; -+} -+ -+void tls12_ticket_auth_dispose(obfs *self) { -+ tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; -+ if (local->send_buffer != NULL) { -+ free(local->send_buffer); -+ local->send_buffer = NULL; -+ } -+ if (local->recv_buffer != NULL) { -+ free(local->recv_buffer); -+ local->recv_buffer = NULL; -+ } -+ free(local); -+ dispose_obfs(self); -+} -+ -+int tls12_ticket_pack_auth_data(tls12_ticket_auth_global_data *global, server_info *server, char *outdata) { -+ int out_size = 32; -+ time_t t = time(NULL); -+ outdata[0] = t >> 24; -+ outdata[1] = t >> 16; -+ outdata[2] = t >> 8; -+ outdata[3] = t; -+ rand_bytes((uint8_t*)outdata + 4, 18); -+ -+ uint8_t *key = (uint8_t*)malloc(server->key_len + 32); -+ char hash[ONETIMEAUTH_BYTES * 2]; -+ memcpy(key, server->key, server->key_len); -+ memcpy(key + server->key_len, global->local_client_id, 32); -+ ss_sha1_hmac_with_key(hash, outdata, out_size - OBFS_HMAC_SHA1_LEN, key, server->key_len + 32); -+ free(key); -+ memcpy(outdata + out_size - OBFS_HMAC_SHA1_LEN, hash, OBFS_HMAC_SHA1_LEN); -+ return out_size; -+} -+ -+void tls12_ticket_auth_pack_data(char *encryptdata, int datalength, int start, int len, char *out_buffer, int outlength) { -+ out_buffer[outlength] = 0x17; -+ out_buffer[outlength + 1] = 0x3; -+ out_buffer[outlength + 2] = 0x3; -+ out_buffer[outlength + 3] = len >> 8; -+ out_buffer[outlength + 4] = len; -+ memcpy(out_buffer + outlength + 5, encryptdata + start, len); -+} -+ -+int tls12_ticket_auth_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { -+ char *encryptdata = *pencryptdata; -+ tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; -+ tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)self->server.g_data; -+ char * out_buffer = NULL; -+ -+ if (local->handshake_status == 8) { -+ if (datalength < 1024) { -+ if (*capacity < datalength + 5) { -+ *pencryptdata = (char*)realloc(*pencryptdata, *capacity = (datalength + 5) * 2); -+ encryptdata = *pencryptdata; -+ } -+ memmove(encryptdata + 5, encryptdata, datalength); -+ encryptdata[0] = 0x17; -+ encryptdata[1] = 0x3; -+ encryptdata[2] = 0x3; -+ encryptdata[3] = datalength >> 8; -+ encryptdata[4] = datalength; -+ return datalength + 5; -+ } else { -+ out_buffer = (char*)malloc(datalength + 2048); -+ int start = 0; -+ int outlength = 0; -+ int len; -+ while (datalength - start > 2048) { -+ len = xorshift128plus() % 4096 + 100; -+ if (len > datalength - start) -+ len = datalength - start; -+ tls12_ticket_auth_pack_data(encryptdata, datalength, start, len, out_buffer, outlength); -+ outlength += len + 5; -+ start += len; -+ } -+ if (datalength - start > 0) { -+ len = datalength - start; -+ tls12_ticket_auth_pack_data(encryptdata, datalength, start, len, out_buffer, outlength); -+ outlength += len + 5; -+ } -+ if (*capacity < outlength) { -+ *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); -+ encryptdata = *pencryptdata; -+ } -+ memcpy(encryptdata, out_buffer, outlength); -+ free(out_buffer); -+ return outlength; -+ } -+ } -+ local->send_buffer = (char*)realloc(local->send_buffer, local->send_buffer_size + datalength + 5); -+ memcpy(local->send_buffer + local->send_buffer_size + 5, encryptdata, datalength); -+ local->send_buffer[local->send_buffer_size] = 0x17; -+ local->send_buffer[local->send_buffer_size + 1] = 0x3; -+ local->send_buffer[local->send_buffer_size + 2] = 0x3; -+ local->send_buffer[local->send_buffer_size + 3] = datalength >> 8; -+ local->send_buffer[local->send_buffer_size + 4] = datalength; -+ local->send_buffer_size += datalength + 5; -+ -+ if (local->handshake_status == 0) { -+#define CSTR_DECL(name, len, str) const char* name = str; const int len = sizeof(str) - 1; -+ CSTR_DECL(tls_data0, tls_data0_len, "\x00\x1c\xc0\x2b\xc0\x2f\xcc\xa9\xcc\xa8\xcc\x14\xcc\x13\xc0\x0a\xc0\x14\xc0\x09\xc0\x13\x00\x9c\x00\x35\x00\x2f\x00\x0a\x01\x00" -+ ); -+ CSTR_DECL(tls_data1, tls_data1_len, "\xff\x01\x00\x01\x00" -+ ); -+ CSTR_DECL(tls_data2, tls_data2_len, "\x00\x17\x00\x00\x00\x23\x00\xd0"); -+ CSTR_DECL(tls_data3, tls_data3_len, "\x00\x0d\x00\x16\x00\x14\x06\x01\x06\x03\x05\x01\x05\x03\x04\x01\x04\x03\x03\x01\x03\x03\x02\x01\x02\x03\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x12\x00\x00\x75\x50\x00\x00\x00\x0b\x00\x02\x01\x00\x00\x0a\x00\x06\x00\x04\x00\x17\x00\x18" -+ //"00150066000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" // padding -+ ); -+ uint8_t tls_data[2048]; -+ int tls_data_len = 0; -+ memcpy(tls_data, tls_data1, tls_data1_len); -+ tls_data_len += tls_data1_len; -+ -+ char hosts[1024]; -+ char * phost[128]; -+ int host_num = 0; -+ int pos; -+ -+ char sni[256] = {0}; -+ if (self->server.param && strlen(self->server.param) == 0) -+ self->server.param = NULL; -+ strncpy(hosts, self->server.param ? self->server.param : self->server.host, sizeof hosts); -+ phost[host_num++] = hosts; -+ for (pos = 0; hosts[pos]; ++pos) { -+ if (hosts[pos] == ',') { -+ phost[host_num++] = &hosts[pos + 1]; -+ } -+ } -+ host_num = xorshift128plus() % host_num; -+ -+ sprintf(sni, "%s", phost[host_num]); -+ int sni_len = strlen(sni); -+ if (sni_len > 0 && sni[sni_len - 1] >= '0' && sni[sni_len - 1] <= '9') -+ sni_len = 0; -+ tls_data[tls_data_len] = '\0'; -+ tls_data[tls_data_len + 1] = '\0'; -+ tls_data[tls_data_len + 2] = (sni_len + 5) >> 8; -+ tls_data[tls_data_len + 3] = (sni_len + 5); -+ tls_data[tls_data_len + 4] = (sni_len + 3) >> 8; -+ tls_data[tls_data_len + 5] = (sni_len + 3); -+ tls_data[tls_data_len + 6] = '\0'; -+ tls_data[tls_data_len + 7] = sni_len >> 8; -+ tls_data[tls_data_len + 8] = sni_len; -+ memcpy(tls_data + tls_data_len + 9, sni, sni_len); -+ tls_data_len += 9 + sni_len; -+ memcpy(tls_data + tls_data_len, tls_data2, tls_data2_len); -+ tls_data_len += tls_data2_len; -+ rand_bytes(tls_data + tls_data_len, 208); -+ tls_data_len += 208; -+ memcpy(tls_data + tls_data_len, tls_data3, tls_data3_len); -+ tls_data_len += tls_data3_len; -+ -+ datalength = 11 + 32 + 1 + 32 + tls_data0_len + 2 + tls_data_len; -+ out_buffer = (char*)malloc(datalength); -+ char *pdata = out_buffer + datalength - tls_data_len; -+ int len = tls_data_len; -+ memcpy(pdata, tls_data, tls_data_len); -+ pdata[-1] = tls_data_len; -+ pdata[-2] = tls_data_len >> 8; -+ pdata -= 2; len += 2; -+ memcpy(pdata - tls_data0_len, tls_data0, tls_data0_len); -+ pdata -= tls_data0_len; len += tls_data0_len; -+ memcpy(pdata - 32, global->local_client_id, 32); -+ pdata -= 32; len += 32; -+ pdata[-1] = 0x20; -+ pdata -= 1; len += 1; -+ tls12_ticket_pack_auth_data(global, &self->server, pdata - 32); -+ pdata -= 32; len += 32; -+ pdata[-1] = 0x3; -+ pdata[-2] = 0x3; // tls version -+ pdata -= 2; len += 2; -+ pdata[-1] = len; -+ pdata[-2] = len >> 8; -+ pdata[-3] = 0; -+ pdata[-4] = 1; -+ pdata -= 4; len += 4; -+ -+ pdata[-1] = len; -+ pdata[-2] = len >> 8; -+ pdata -= 2; len += 2; -+ pdata[-1] = 0x1; -+ pdata[-2] = 0x3; // tls version -+ pdata -= 2; len += 2; -+ pdata[-1] = 0x16; // tls handshake -+ pdata -= 1; len += 1; -+ -+ local->handshake_status = 1; -+ } else if (datalength == 0) { -+ datalength = local->send_buffer_size + 43; -+ out_buffer = (char*)malloc(datalength); -+ char *pdata = out_buffer; -+ memcpy(pdata, "\x14\x03\x03\x00\x01\x01", 6); -+ pdata += 6; -+ memcpy(pdata, "\x16\x03\x03\x00\x20", 5); -+ pdata += 5; -+ rand_bytes((uint8_t*)pdata, 22); -+ pdata += 22; -+ -+ uint8_t *key = (uint8_t*)malloc(self->server.key_len + 32); -+ char hash[ONETIMEAUTH_BYTES * 2]; -+ memcpy(key, self->server.key, self->server.key_len); -+ memcpy(key + self->server.key_len, global->local_client_id, 32); -+ ss_sha1_hmac_with_key(hash, out_buffer, pdata - out_buffer, key, self->server.key_len + 32); -+ free(key); -+ memcpy(pdata, hash, OBFS_HMAC_SHA1_LEN); -+ -+ pdata += OBFS_HMAC_SHA1_LEN; -+ memcpy(pdata, local->send_buffer, local->send_buffer_size); -+ free(local->send_buffer); -+ local->send_buffer = NULL; -+ -+ local->handshake_status = 8; -+ } else { -+ return 0; -+ } -+ if (*capacity < datalength) { -+ *pencryptdata = (char*)realloc(*pencryptdata, *capacity = datalength * 2); -+ encryptdata = *pencryptdata; -+ } -+ memmove(encryptdata, out_buffer, datalength); -+ free(out_buffer); -+ return datalength; -+} -+ -+int tls12_ticket_auth_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { -+ char *encryptdata = *pencryptdata; -+ tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; -+ tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)self->server.g_data; -+ char * out_buffer = NULL; -+ -+ if (local->handshake_status == 8) { -+ if (datalength < 1024) { -+ if (*capacity < datalength + 5) { -+ *pencryptdata = (char*)realloc(*pencryptdata, *capacity = (datalength + 5) * 2); -+ encryptdata = *pencryptdata; -+ } -+ memmove(encryptdata + 5, encryptdata, datalength); -+ encryptdata[0] = 0x17; -+ encryptdata[1] = 0x3; -+ encryptdata[2] = 0x3; -+ encryptdata[3] = datalength >> 8; -+ encryptdata[4] = datalength; -+ return datalength + 5; -+ } else { -+ out_buffer = (char*)malloc(datalength + 2048); -+ int start = 0; -+ int outlength = 0; -+ int len; -+ while (datalength - start > 2048) { -+ len = xorshift128plus() % 4096 + 100; -+ if (len > datalength - start) -+ len = datalength - start; -+ tls12_ticket_auth_pack_data(encryptdata, datalength, start, len, out_buffer, outlength); -+ outlength += len + 5; -+ start += len; -+ } -+ if (datalength - start > 0) { -+ len = datalength - start; -+ tls12_ticket_auth_pack_data(encryptdata, datalength, start, len, out_buffer, outlength); -+ outlength += len + 5; -+ } -+ if (*capacity < outlength) { -+ *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); -+ encryptdata = *pencryptdata; -+ } -+ memcpy(encryptdata, out_buffer, outlength); -+ free(out_buffer); -+ return outlength; -+ } -+ } -+ -+ local->handshake_status = 3; -+ -+ out_buffer = (char*)malloc(43 + 86); -+ int data_len = 0; -+ char *p_data = out_buffer + 86; -+ -+ memcpy(p_data - 10, "\xc0\x2f\x00\x00\x05\xff\x01\x00\x01\x00", 10); -+ p_data -= 10;data_len += 10; -+ -+ memcpy(p_data - 32, global->local_client_id, 32); -+ p_data -= 32;data_len += 32; -+ -+ p_data[-1] = 0x20; -+ p_data -= 1;data_len += 1; -+ -+ tls12_ticket_pack_auth_data(global, &self->server, p_data - 32); -+ p_data -= 32;data_len += 32; -+ -+ p_data[-1] = 0x3; -+ p_data[-2] = 0x3; // tls version -+ p_data -= 2;data_len += 2; -+ -+ p_data[-1] = data_len; -+ p_data[-2] = data_len >> 8; -+ p_data[-3] = 0x00; -+ p_data[-4] = 0x02; -+ p_data -= 4; data_len += 4; -+ -+ p_data[-1] = data_len; -+ p_data[-2] = data_len >> 8; -+ p_data[-3] = 0x03; -+ p_data[-4] = 0x03; -+ p_data[-5] = 0x16; -+ p_data -= 5; data_len += 5; -+ -+ memcpy(out_buffer, p_data, data_len); -+ char *pdata = out_buffer + 86; -+ -+ memcpy(pdata, "\x14\x03\x03\x00\x01\x01", 6); -+ pdata += 6; -+ memcpy(pdata, "\x16\x03\x03\x00\x20", 5); -+ pdata += 5; -+ rand_bytes((uint8_t*)pdata, 22); -+ pdata += 22; -+ -+ uint8_t *key = (uint8_t*)malloc(self->server.key_len + 32); -+ char hash[ONETIMEAUTH_BYTES * 2]; -+ memcpy(key, self->server.key, self->server.key_len); -+ memcpy(key + self->server.key_len, global->local_client_id, 32); -+ ss_sha1_hmac_with_key(hash, out_buffer, 43 + 86, key, self->server.key_len + 32); -+ free(key); -+ memcpy(pdata, hash, OBFS_HMAC_SHA1_LEN); -+ -+ memmove(encryptdata, out_buffer, 43 + 86); -+ free(out_buffer); -+ return 43 + 86; -+} -+ -+int tls12_ticket_auth_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) { -+ char *encryptdata = *pencryptdata; -+ tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; -+ tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)self->server.g_data; -+ -+ *needsendback = 0; -+ -+ if (local->handshake_status == 8) { -+ local->recv_buffer_size += datalength; -+ local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size); -+ memcpy(local->recv_buffer + local->recv_buffer_size - datalength, encryptdata, datalength); -+ datalength = 0; -+ while (local->recv_buffer_size > 5) { -+ if (local->recv_buffer[0] != 0x17) -+ return -1; -+ int size = ((int)(unsigned char)local->recv_buffer[3] << 8) + (unsigned char)local->recv_buffer[4]; -+ if (size + 5 > local->recv_buffer_size) -+ break; -+ if (*capacity < datalength + size) { -+ *pencryptdata = (char*)realloc(*pencryptdata, *capacity = (datalength + size) * 2); -+ encryptdata = *pencryptdata; -+ } -+ memcpy(encryptdata + datalength, local->recv_buffer + 5, size); -+ datalength += size; -+ local->recv_buffer_size -= 5 + size; -+ memmove(local->recv_buffer, local->recv_buffer + 5 + size, local->recv_buffer_size); -+ } -+ return datalength; -+ } -+ if (datalength < 11 + 32 + 1 + 32) { -+ return -1; -+ } -+ -+ uint8_t *key = (uint8_t*)malloc(self->server.key_len + 32); -+ char hash[ONETIMEAUTH_BYTES * 2]; -+ memcpy(key, self->server.key, self->server.key_len); -+ memcpy(key + self->server.key_len, global->local_client_id, 32); -+ ss_sha1_hmac_with_key(hash, encryptdata + 11, 22, key, self->server.key_len + 32); -+ free(key); -+ -+ if (memcmp(encryptdata + 33, hash, OBFS_HMAC_SHA1_LEN)) { -+ return -1; -+ } -+ -+ *needsendback = 1; -+ return 0; -+} -+ -+int tls12_ticket_auth_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) { -+ char *encryptdata = *pencryptdata; -+ tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; -+ tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)self->server.g_data; -+ -+ *needsendback = 0; -+ -+ if (local->handshake_status == 8) { -+ if(datalength != 0) -+ { -+ local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size + datalength); -+ memmove(local->recv_buffer + local->recv_buffer_size, encryptdata, datalength); -+ local->recv_buffer_size += datalength; -+ } -+ datalength = 0; -+ -+ while (local->recv_buffer_size > 5) { -+ if (local->recv_buffer[0] != 0x17 || local->recv_buffer[1] != 0x03 || local->recv_buffer[2] != 0x03) -+ { -+ LOGE("server_decode data error, wrong tls version 3"); -+ return -1; -+ } -+ int size = ((int)(unsigned char)local->recv_buffer[3] << 8) + (unsigned char)local->recv_buffer[4]; -+ if (size + 5 > local->recv_buffer_size) -+ break; -+ if (*capacity < local->recv_buffer_size + size) { -+ *pencryptdata = (char*)realloc(*pencryptdata, *capacity = (local->recv_buffer_size + size) * 2); -+ encryptdata = *pencryptdata; -+ } -+ memcpy(encryptdata + datalength, local->recv_buffer + 5, size); -+ datalength += size; -+ local->recv_buffer_size -= 5 + size; -+ memmove(local->recv_buffer, local->recv_buffer + 5 + size, local->recv_buffer_size); -+ } -+ return datalength; -+ } -+ -+ if (local->handshake_status == 3) { -+ -+ char *verify = encryptdata; -+ -+ if(datalength < 43) -+ { -+ LOGE("server_decode data error, too short:%d", (int)datalength); -+ return -1; -+ } -+ -+ if(encryptdata[0] != 0x14 || encryptdata[1] != 0x03 || encryptdata[2] != 0x03 || encryptdata[3] != 0x00 || encryptdata[4] != 0x01 || encryptdata[5] != 0x01) -+ { -+ LOGE("server_decode data error, wrong tls version"); -+ return -1; -+ } -+ -+ encryptdata += 6; -+ -+ if(encryptdata[0] != 0x16 || encryptdata[1] != 0x03 || encryptdata[2] != 0x03 || encryptdata[3] != 0x00 || encryptdata[4] != 0x20) -+ { -+ LOGE("server_decode data error, wrong tls version 2"); -+ return -1; -+ } -+ -+ uint8_t *key = (uint8_t*)malloc(self->server.key_len + 32); -+ char hash[ONETIMEAUTH_BYTES * 2]; -+ memcpy(key, self->server.key, self->server.key_len); -+ memcpy(key + self->server.key_len, global->local_client_id, 32); -+ ss_sha1_hmac_with_key(hash, verify, 33, key, self->server.key_len + 32); -+ free(key); -+ -+ if (memcmp(verify + 33, hash, OBFS_HMAC_SHA1_LEN) != 0) { -+ LOGE("server_decode data error, hash Mismatch %d",(int)memcmp(verify + 33, hash, OBFS_HMAC_SHA1_LEN)); -+ return -1; -+ } -+ -+ local->recv_buffer_size = datalength - 43; -+ local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size); -+ memmove(local->recv_buffer, encryptdata + 37, datalength - 43); -+ -+ local->handshake_status = 8; -+ return tls12_ticket_auth_server_decode(self, pencryptdata, 0, capacity, needsendback); -+ } -+ -+ local->handshake_status = 2; -+ if(encryptdata[0] != 0x16 || encryptdata[1] != 0x03 || encryptdata[2] != 0x01) -+ { -+ return -1; -+ } -+ -+ encryptdata += 3; -+ -+ { -+ int size = ((int)(unsigned char)encryptdata[0] << 8) + (unsigned char)encryptdata[1]; -+ if(size != datalength - 5) -+ { -+ LOGE("tls_auth wrong tls head size"); -+ return -1; -+ } -+ } -+ -+ encryptdata += 2; -+ -+ if(encryptdata[0] != 0x01 || encryptdata[1] != 0x00) -+ { -+ LOGE("tls_auth not client hello message"); -+ return -1; -+ } -+ -+ encryptdata += 2; -+ -+ { -+ int size = ((int)(unsigned char)encryptdata[0] << 8) + (unsigned char)encryptdata[1]; -+ if(size != datalength - 9) -+ { -+ LOGE("tls_auth wrong message size"); -+ return -1; -+ } -+ } -+ -+ encryptdata += 2; -+ -+ if(encryptdata[0] != 0x03 || encryptdata[1] != 0x03) -+ { -+ LOGE("tls_auth wrong tls version"); -+ return -1; -+ } -+ -+ encryptdata += 2; -+ -+ char *verifyid = encryptdata; -+ -+ encryptdata += 32; -+ -+ int sessionid_len = encryptdata[0]; -+ if(sessionid_len < 32) -+ { -+ LOGE("tls_auth wrong sessionid_len"); -+ return -1; -+ } -+ -+ char *sessionid = encryptdata + 1; -+ memcpy(global->local_client_id , sessionid, sessionid_len); -+ -+ uint8_t *key = (uint8_t*)malloc(self->server.key_len + sessionid_len); -+ char hash[ONETIMEAUTH_BYTES * 2]; -+ memcpy(key, self->server.key, self->server.key_len); -+ memcpy(key + self->server.key_len, global->local_client_id, sessionid_len); -+ ss_sha1_hmac_with_key(hash, verifyid, 22, key, self->server.key_len + sessionid_len); -+ free(key); -+ -+ encryptdata += (sessionid_len + 1); -+ -+ long utc_time = ((int)(unsigned char)verifyid[0] << 24) + ((int)(unsigned char)verifyid[1] << 16) + ((int)(unsigned char)verifyid[2] << 8) + (unsigned char)verifyid[3]; -+ time_t t = time(NULL); -+ -+ -+ if (self->server.param && strlen(self->server.param) == 0) -+ { -+ self->server.param = NULL; -+ } -+ -+ int max_time_dif = 0; -+ int time_dif = utc_time - t; -+ if(self->server.param) -+ { -+ max_time_dif = atoi(self->server.param); -+ } -+ -+ if(max_time_dif > 0 && (time_dif < -max_time_dif || time_dif > max_time_dif || utc_time - global->startup_time < -max_time_dif / 2)) -+ { -+ LOGE("tls_auth wrong time"); -+ return -1; -+ } -+ -+ if (memcmp(verifyid + 22, hash, OBFS_HMAC_SHA1_LEN)) { -+ LOGE("tls_auth wrong sha1"); -+ return -1; -+ } -+ -+ int search_result = global->client_data->have_same_cmp(global->client_data, verifyid); -+ if(search_result != 0) -+ { -+ LOGE("replay attack detect!"); -+ return -1; -+ } -+ -+ global->client_data->add_back(global->client_data, verifyid); -+ -+ encryptdata += 48; -+ -+ *needsendback = 1; -+ -+ return 0; -+} -diff --git a/server/tls1.2_ticket.h b/server/tls1.2_ticket.h -new file mode 100644 -index 0000000..10a57c9 ---- /dev/null -+++ b/server/tls1.2_ticket.h -@@ -0,0 +1,20 @@ -+/* -+ * http_simple.h - Define shadowsocksR server's buffers and callbacks -+ * -+ * Copyright (C) 2015 - 2016, Break Wa11 -+ */ -+ -+#ifndef _TLS1_2_TICKET_H -+#define _TLS1_2_TICKET_H -+ -+void * tls12_ticket_auth_init_data(); -+obfs * tls12_ticket_auth_new_obfs(); -+void tls12_ticket_auth_dispose(obfs *self); -+ -+int tls12_ticket_auth_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); -+int tls12_ticket_auth_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback); -+ -+int tls12_ticket_auth_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); -+int tls12_ticket_auth_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback); -+ -+#endif // _TLS1_2_TICKET_H -diff --git a/server/udprelay.c b/server/udprelay.c -new file mode 100644 -index 0000000..d9251ee ---- /dev/null -+++ b/server/udprelay.c -@@ -0,0 +1,1452 @@ -+/* -+ * udprelay.c - Setup UDP relay for both client and server -+ * -+ * Copyright (C) 2013 - 2016, 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 -+ * . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifndef __MINGW32__ -+#include -+#include -+#include -+#include -+#include -+#endif -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__) -+#include -+#include -+#define SET_INTERFACE -+#endif -+ -+#ifdef __MINGW32__ -+#include "win32.h" -+#endif -+ -+#include -+#include -+ -+#include "utils.h" -+#include "netutils.h" -+#include "cache.h" -+#include "udprelay.h" -+ -+#ifdef MODULE_REMOTE -+#define MAX_UDP_CONN_NUM 512 -+#else -+#define MAX_UDP_CONN_NUM 256 -+#endif -+ -+#ifdef MODULE_REMOTE -+#ifdef MODULE_ -+#error "MODULE_REMOTE and MODULE_LOCAL should not be both defined" -+#endif -+#endif -+ -+#ifndef EAGAIN -+#define EAGAIN EWOULDBLOCK -+#endif -+ -+#ifndef EWOULDBLOCK -+#define EWOULDBLOCK EAGAIN -+#endif -+ -+static void server_recv_cb(EV_P_ ev_io *w, int revents); -+static void remote_recv_cb(EV_P_ ev_io *w, int revents); -+static void remote_timeout_cb(EV_P_ ev_timer *watcher, int revents); -+ -+static char *hash_key(const int af, const struct sockaddr_storage *addr); -+#ifdef MODULE_REMOTE -+static void query_resolve_cb(struct sockaddr *addr, void *data); -+#endif -+static void close_and_free_remote(EV_P_ remote_ctx_t *ctx); -+static remote_ctx_t *new_remote(int fd, server_ctx_t *server_ctx); -+ -+#ifdef ANDROID -+extern uint64_t tx; -+extern uint64_t rx; -+extern int vpn; -+#endif -+ -+extern int verbose; -+#ifdef MODULE_REMOTE -+extern uint64_t tx; -+extern uint64_t rx; -+#endif -+ -+static int packet_size = DEFAULT_PACKET_SIZE; -+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) -+{ -+ int flags; -+ if (-1 == (flags = fcntl(fd, F_GETFL, 0))) { -+ flags = 0; -+ } -+ return fcntl(fd, F_SETFL, flags | O_NONBLOCK); -+} -+ -+#endif -+ -+#if defined(MODULE_REMOTE) && defined(SO_BROADCAST) -+static int -+set_broadcast(int socket_fd) -+{ -+ int opt = 1; -+ return setsockopt(socket_fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)); -+} -+ -+#endif -+ -+#ifdef SO_NOSIGPIPE -+static int -+set_nosigpipe(int socket_fd) -+{ -+ int opt = 1; -+ return setsockopt(socket_fd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); -+} -+ -+#endif -+ -+#ifdef MODULE_REDIR -+ -+#ifndef IP_TRANSPARENT -+#define IP_TRANSPARENT 19 -+#endif -+ -+#ifndef IP_RECVORIGDSTADDR -+#define IP_RECVORIGDSTADDR 20 -+#endif -+ -+static int -+get_dstaddr(struct msghdr *msg, struct sockaddr_storage *dstaddr) -+{ -+ struct cmsghdr *cmsg; -+ -+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { -+ if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVORIGDSTADDR) { -+ memcpy(dstaddr, CMSG_DATA(cmsg), sizeof(struct sockaddr_in)); -+ dstaddr->ss_family = AF_INET; -+ return 0; -+ } else if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IP_RECVORIGDSTADDR) { -+ memcpy(dstaddr, CMSG_DATA(cmsg), sizeof(struct sockaddr_in6)); -+ dstaddr->ss_family = AF_INET6; -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+#endif -+ -+#define HASH_KEY_LEN sizeof(struct sockaddr_storage) + sizeof(int) -+static char * -+hash_key(const int af, const struct sockaddr_storage *addr) -+{ -+ size_t addr_len = sizeof(struct sockaddr_storage); -+ static char key[HASH_KEY_LEN]; -+ -+ memset(key, 0, HASH_KEY_LEN); -+ memcpy(key, &af, sizeof(int)); -+ memcpy(key + sizeof(int), (const uint8_t *)addr, addr_len); -+ -+ return key; -+} -+ -+#if defined(MODULE_REDIR) || defined(MODULE_REMOTE) -+static int -+construct_udprealy_header(const struct sockaddr_storage *in_addr, -+ char *addr_header) -+{ -+ int addr_header_len = 0; -+ if (in_addr->ss_family == AF_INET) { -+ struct sockaddr_in *addr = (struct sockaddr_in *)in_addr; -+ size_t addr_len = sizeof(struct in_addr); -+ addr_header[addr_header_len++] = 1; -+ memcpy(addr_header + addr_header_len, &addr->sin_addr, addr_len); -+ addr_header_len += addr_len; -+ memcpy(addr_header + addr_header_len, &addr->sin_port, 2); -+ addr_header_len += 2; -+ } else if (in_addr->ss_family == AF_INET6) { -+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)in_addr; -+ size_t addr_len = sizeof(struct in6_addr); -+ addr_header[addr_header_len++] = 4; -+ memcpy(addr_header + addr_header_len, &addr->sin6_addr, addr_len); -+ addr_header_len += addr_len; -+ memcpy(addr_header + addr_header_len, &addr->sin6_port, 2); -+ addr_header_len += 2; -+ } else { -+ return 0; -+ } -+ return addr_header_len; -+} -+ -+#endif -+ -+static int -+parse_udprealy_header(const char *buf, const size_t buf_len, -+ char *host, char *port, struct sockaddr_storage *storage) -+{ -+ const uint8_t atyp = *(uint8_t *)buf; -+ int offset = 1; -+ -+ // get remote addr and port -+ if ((atyp & ADDRTYPE_MASK) == 1) { -+ // IP V4 -+ size_t in_addr_len = sizeof(struct in_addr); -+ if (buf_len >= in_addr_len + 3) { -+ if (storage != NULL) { -+ struct sockaddr_in *addr = (struct sockaddr_in *)storage; -+ addr->sin_family = AF_INET; -+ addr->sin_addr = *(struct in_addr *)(buf + offset); -+ addr->sin_port = *(uint16_t *)(buf + offset + in_addr_len); -+ } -+ if (host != NULL) { -+ dns_ntop(AF_INET, (const void *)(buf + offset), -+ host, INET_ADDRSTRLEN); -+ } -+ offset += in_addr_len; -+ } -+ } else if ((atyp & ADDRTYPE_MASK) == 3) { -+ // Domain name -+ uint8_t name_len = *(uint8_t *)(buf + offset); -+ if (name_len + 4 <= buf_len) { -+ if (storage != NULL) { -+ char tmp[257] = { 0 }; -+ struct cork_ip ip; -+ memcpy(tmp, buf + offset + 1, name_len); -+ if (cork_ip_init(&ip, tmp) != -1) { -+ if (ip.version == 4) { -+ struct sockaddr_in *addr = (struct sockaddr_in *)storage; -+ dns_pton(AF_INET, tmp, &(addr->sin_addr)); -+ addr->sin_port = *(uint16_t *)(buf + offset + 1 + name_len); -+ addr->sin_family = AF_INET; -+ } else if (ip.version == 6) { -+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; -+ dns_pton(AF_INET, tmp, &(addr->sin6_addr)); -+ addr->sin6_port = *(uint16_t *)(buf + offset + 1 + name_len); -+ addr->sin6_family = AF_INET6; -+ } -+ } -+ } -+ if (host != NULL) { -+ memcpy(host, buf + offset + 1, name_len); -+ } -+ offset += 1 + name_len; -+ } -+ } else if ((atyp & ADDRTYPE_MASK) == 4) { -+ // IP V6 -+ size_t in6_addr_len = sizeof(struct in6_addr); -+ if (buf_len >= in6_addr_len + 3) { -+ if (storage != NULL) { -+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; -+ addr->sin6_family = AF_INET6; -+ addr->sin6_addr = *(struct in6_addr *)(buf + offset); -+ addr->sin6_port = *(uint16_t *)(buf + offset + in6_addr_len); -+ } -+ if (host != NULL) { -+ dns_ntop(AF_INET6, (const void *)(buf + offset), -+ host, INET6_ADDRSTRLEN); -+ } -+ offset += in6_addr_len; -+ } -+ } -+ -+ if (offset == 1) { -+ LOGE("[udp] invalid header with addr type %d", atyp); -+ return 0; -+ } -+ -+ if (port != NULL) { -+ sprintf(port, "%d", ntohs(*(uint16_t *)(buf + offset))); -+ } -+ offset += 2; -+ -+ return offset; -+} -+ -+static char * -+get_addr_str(const struct sockaddr *sa) -+{ -+ static char s[SS_ADDRSTRLEN]; -+ memset(s, 0, SS_ADDRSTRLEN); -+ char addr[INET6_ADDRSTRLEN] = { 0 }; -+ char port[PORTSTRLEN] = { 0 }; -+ uint16_t p; -+ -+ switch (sa->sa_family) { -+ case AF_INET: -+ dns_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), -+ addr, INET_ADDRSTRLEN); -+ p = ntohs(((struct sockaddr_in *)sa)->sin_port); -+ sprintf(port, "%d", p); -+ break; -+ -+ case AF_INET6: -+ dns_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), -+ addr, INET6_ADDRSTRLEN); -+ p = ntohs(((struct sockaddr_in *)sa)->sin_port); -+ sprintf(port, "%d", p); -+ break; -+ -+ default: -+ strncpy(s, "Unknown AF", SS_ADDRSTRLEN); -+ } -+ -+ int addr_len = strlen(addr); -+ int port_len = strlen(port); -+ memcpy(s, addr, addr_len); -+ memcpy(s + addr_len + 1, port, port_len); -+ s[addr_len] = ':'; -+ -+ return s; -+} -+ -+int -+create_remote_socket(int ipv6) -+{ -+ int remote_sock; -+ -+ if (ipv6) { -+ // Try to bind IPv6 first -+ struct sockaddr_in6 addr; -+ memset(&addr, 0, sizeof(struct sockaddr_in6)); -+ addr.sin6_family = AF_INET6; -+ addr.sin6_addr = in6addr_any; -+ addr.sin6_port = 0; -+ remote_sock = socket(AF_INET6, SOCK_DGRAM, 0); -+ if (remote_sock == -1) { -+ ERROR("[udp] cannot create socket"); -+ return -1; -+ } -+ if (bind(remote_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { -+ FATAL("[udp] cannot bind remote"); -+ return -1; -+ } -+ } else { -+ // Or else bind to IPv4 -+ struct sockaddr_in addr; -+ memset(&addr, 0, sizeof(struct sockaddr_in)); -+ addr.sin_family = AF_INET; -+ addr.sin_addr.s_addr = INADDR_ANY; -+ addr.sin_port = 0; -+ remote_sock = socket(AF_INET, SOCK_DGRAM, 0); -+ if (remote_sock == -1) { -+ ERROR("[udp] cannot create socket"); -+ return -1; -+ } -+ -+ if (bind(remote_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { -+ FATAL("[udp] cannot bind remote"); -+ return -1; -+ } -+ } -+ return remote_sock; -+} -+ -+int -+create_server_socket(const char *host, const char *port) -+{ -+ struct addrinfo hints; -+ struct addrinfo *result, *rp, *ipv4v6bindall; -+ int s, server_sock; -+ -+ memset(&hints, 0, sizeof(struct addrinfo)); -+ hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ -+ hints.ai_socktype = SOCK_DGRAM; /* We want a UDP socket */ -+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* For wildcard IP address */ -+ hints.ai_protocol = IPPROTO_UDP; -+ -+ s = getaddrinfo(host, port, &hints, &result); -+ if (s != 0) { -+ LOGE("[udp] getaddrinfo: %s", gai_strerror(s)); -+ return -1; -+ } -+ -+ rp = result; -+ -+ /* -+ * On Linux, with net.ipv6.bindv6only = 0 (the default), getaddrinfo(NULL) with -+ * AI_PASSIVE returns 0.0.0.0 and :: (in this order). AI_PASSIVE was meant to -+ * return a list of addresses to listen on, but it is impossible to listen on -+ * 0.0.0.0 and :: at the same time, if :: implies dualstack mode. -+ */ -+ if (!host) { -+ ipv4v6bindall = result; -+ -+ /* Loop over all address infos found until a IPV6 address is found. */ -+ while (ipv4v6bindall) { -+ if (ipv4v6bindall->ai_family == AF_INET6) { -+ rp = ipv4v6bindall; /* Take first IPV6 address available */ -+ break; -+ } -+ ipv4v6bindall = ipv4v6bindall->ai_next; /* Get next address info, if any */ -+ } -+ } -+ -+ for (/*rp = result*/; rp != NULL; rp = rp->ai_next) { -+ server_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); -+ if (server_sock == -1) { -+ continue; -+ } -+ -+ if (rp->ai_family == AF_INET6) { -+ int ipv6only = host ? 1 : 0; -+ setsockopt(server_sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)); -+ } -+ -+ int opt = 1; -+ setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); -+#ifdef SO_NOSIGPIPE -+ set_nosigpipe(server_sock); -+#endif -+ int err = set_reuseport(server_sock); -+ if (err == 0) { -+ LOGI("udp port reuse enabled"); -+ } -+#ifdef IP_TOS -+ // Set QoS flag -+ int tos = 46; -+ setsockopt(server_sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); -+#endif -+ -+#ifdef MODULE_REDIR -+ if (setsockopt(server_sock, SOL_IP, IP_TRANSPARENT, &opt, sizeof(opt))) { -+ ERROR("[udp] setsockopt IP_TRANSPARENT"); -+ exit(EXIT_FAILURE); -+ } -+ if (setsockopt(server_sock, IPPROTO_IP, IP_RECVORIGDSTADDR, &opt, sizeof(opt))) { -+ FATAL("[udp] setsockopt IP_RECVORIGDSTADDR"); -+ } -+#endif -+ -+ s = bind(server_sock, rp->ai_addr, rp->ai_addrlen); -+ if (s == 0) { -+ /* We managed to bind successfully! */ -+ break; -+ } else { -+ ERROR("[udp] bind"); -+ } -+ -+ close(server_sock); -+ } -+ -+ if (rp == NULL) { -+ LOGE("[udp] cannot bind"); -+ return -1; -+ } -+ -+ freeaddrinfo(result); -+ -+ return server_sock; -+} -+ -+remote_ctx_t * -+new_remote(int fd, server_ctx_t *server_ctx) -+{ -+ remote_ctx_t *ctx = ss_malloc(sizeof(remote_ctx_t)); -+ memset(ctx, 0, sizeof(remote_ctx_t)); -+ -+ ctx->fd = fd; -+ ctx->server_ctx = server_ctx; -+ -+ ev_io_init(&ctx->io, remote_recv_cb, fd, EV_READ); -+ ev_timer_init(&ctx->watcher, remote_timeout_cb, server_ctx->timeout, -+ server_ctx->timeout); -+ -+ return ctx; -+} -+ -+server_ctx_t * -+new_server_ctx(int fd) -+{ -+ server_ctx_t *ctx = ss_malloc(sizeof(server_ctx_t)); -+ memset(ctx, 0, sizeof(server_ctx_t)); -+ -+ ctx->fd = fd; -+ -+ ev_io_init(&ctx->io, server_recv_cb, fd, EV_READ); -+ -+ return ctx; -+} -+ -+#ifdef MODULE_REMOTE -+struct query_ctx * -+new_query_ctx(char *buf, size_t len) -+{ -+ struct query_ctx *ctx = ss_malloc(sizeof(struct query_ctx)); -+ memset(ctx, 0, sizeof(struct query_ctx)); -+ ctx->buf = ss_malloc(sizeof(buffer_t)); -+ balloc(ctx->buf, len); -+ memcpy(ctx->buf->array, buf, len); -+ ctx->buf->len = len; -+ return ctx; -+} -+ -+void -+close_and_free_query(EV_P_ struct query_ctx *ctx) -+{ -+ if (ctx != NULL) { -+ if (ctx->query != NULL) { -+ resolv_cancel(ctx->query); -+ ctx->query = NULL; -+ } -+ if (ctx->buf != NULL) { -+ bfree(ctx->buf); -+ ss_free(ctx->buf); -+ } -+ ss_free(ctx); -+ } -+} -+ -+#endif -+ -+void -+close_and_free_remote(EV_P_ remote_ctx_t *ctx) -+{ -+ if (ctx != NULL) { -+ ev_timer_stop(EV_A_ & ctx->watcher); -+ ev_io_stop(EV_A_ & ctx->io); -+ close(ctx->fd); -+ ss_free(ctx); -+ } -+} -+ -+static void -+remote_timeout_cb(EV_P_ ev_timer *watcher, int revents) -+{ -+ remote_ctx_t *remote_ctx -+ = cork_container_of(watcher, remote_ctx_t, watcher); -+ -+ if (verbose) { -+ LOGI("[udp] connection timeout"); -+ } -+ -+ char *key = hash_key(remote_ctx->af, &remote_ctx->src_addr); -+ cache_remove(remote_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN); -+} -+ -+#ifdef MODULE_REMOTE -+static void -+query_resolve_cb(struct sockaddr *addr, void *data) -+{ -+ struct query_ctx *query_ctx = (struct query_ctx *)data; -+ struct ev_loop *loop = query_ctx->server_ctx->loop; -+ -+ if (verbose) { -+ LOGI("[udp] udns resolved"); -+ } -+ -+ query_ctx->query = NULL; -+ -+ if (addr == NULL) { -+ LOGE("[udp] udns returned an error"); -+ } else { -+ remote_ctx_t *remote_ctx = query_ctx->remote_ctx; -+ int cache_hit = 0; -+ -+ // Lookup in the conn cache -+ if (remote_ctx == NULL) { -+ char *key = hash_key(AF_UNSPEC, &query_ctx->src_addr); -+ cache_lookup(query_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)&remote_ctx); -+ } -+ -+ if (remote_ctx == NULL) { -+ int remotefd = create_remote_socket(addr->sa_family == AF_INET6); -+ if (remotefd != -1) { -+ setnonblocking(remotefd); -+#ifdef SO_BROADCAST -+ set_broadcast(remotefd); -+#endif -+#ifdef SO_NOSIGPIPE -+ set_nosigpipe(remotefd); -+#endif -+#ifdef IP_TOS -+ // Set QoS flag -+ int tos = 46; -+ setsockopt(remotefd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); -+#endif -+#ifdef SET_INTERFACE -+ if (query_ctx->server_ctx->iface) { -+ if (setinterface(remotefd, query_ctx->server_ctx->iface) == -1) -+ ERROR("setinterface"); -+ } -+#endif -+ remote_ctx = new_remote(remotefd, query_ctx->server_ctx); -+ remote_ctx->src_addr = query_ctx->src_addr; -+ remote_ctx->server_ctx = query_ctx->server_ctx; -+ remote_ctx->addr_header_len = query_ctx->addr_header_len; -+ memcpy(remote_ctx->addr_header, query_ctx->addr_header, -+ query_ctx->addr_header_len); -+ } else { -+ ERROR("[udp] bind() error"); -+ } -+ } else { -+ cache_hit = 1; -+ } -+ -+ if (remote_ctx != NULL) { -+ memcpy(&remote_ctx->dst_addr, addr, sizeof(struct sockaddr_storage)); -+ -+ size_t addr_len = get_sockaddr_len(addr); -+ int s = sendto(remote_ctx->fd, query_ctx->buf->array, query_ctx->buf->len, -+ 0, addr, addr_len); -+ -+ if (s == -1) { -+ ERROR("[udp] sendto_remote"); -+ if (!cache_hit) { -+ close_and_free_remote(EV_A_ remote_ctx); -+ } -+ } else { -+ if (!cache_hit) { -+ // Add to conn cache -+ char *key = hash_key(AF_UNSPEC, &remote_ctx->src_addr); -+ cache_insert(query_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); -+ ev_io_start(EV_A_ & remote_ctx->io); -+ ev_timer_start(EV_A_ & remote_ctx->watcher); -+ } -+ } -+ } -+ } -+ -+ // clean up -+ close_and_free_query(EV_A_ query_ctx); -+} -+ -+#endif -+ -+static void -+remote_recv_cb(EV_P_ ev_io *w, int revents) -+{ -+ ssize_t r; -+ remote_ctx_t *remote_ctx = (remote_ctx_t *)w; -+ server_ctx_t *server_ctx = remote_ctx->server_ctx; -+ -+ // server has been closed -+ if (server_ctx == NULL) { -+ LOGE("[udp] invalid server"); -+ close_and_free_remote(EV_A_ remote_ctx); -+ return; -+ } -+ -+ struct sockaddr_storage src_addr; -+ socklen_t src_addr_len = sizeof(struct sockaddr_storage); -+ memset(&src_addr, 0, src_addr_len); -+ -+ buffer_t *buf = ss_malloc(sizeof(buffer_t)); -+ balloc(buf, buf_size); -+ -+ // recv -+ r = recvfrom(remote_ctx->fd, buf->array, buf_size, 0, (struct sockaddr *)&src_addr, &src_addr_len); -+ -+ if (r == -1) { -+ // error on recv -+ // simply drop that packet -+ ERROR("[udp] remote_recv_recvfrom"); -+ goto CLEAN_UP; -+ } else if (r > packet_size) { -+ LOGE("[udp] remote_recv_recvfrom fragmentation"); -+ goto CLEAN_UP; -+ } -+ -+ buf->len = r; -+ -+#ifdef MODULE_LOCAL -+ int err = ss_decrypt_all(buf, server_ctx->method, 0, buf_size); -+ if (err) { -+ // drop the packet silently -+ goto CLEAN_UP; -+ } -+ -+ //SSR beg -+ if (server_ctx->protocol_plugin) { -+ obfs_class *protocol_plugin = server_ctx->protocol_plugin; -+ if (protocol_plugin->client_udp_post_decrypt) { -+ buf->len = protocol_plugin->client_udp_post_decrypt(server_ctx->protocol, &buf->array, buf->len, &buf->capacity); -+ if ((int)buf->len < 0) { -+ LOGE("client_udp_post_decrypt"); -+ close_and_free_remote(EV_A_ remote_ctx); -+ return; -+ } -+ if ( buf->len == 0 ) -+ return; -+ } -+ } -+ // SSR end -+ -+#ifdef MODULE_REDIR -+ struct sockaddr_storage dst_addr; -+ memset(&dst_addr, 0, sizeof(struct sockaddr_storage)); -+ int len = parse_udprealy_header(buf->array, buf->len, NULL, NULL, &dst_addr); -+ -+ if (dst_addr.ss_family != AF_INET && dst_addr.ss_family != AF_INET6) { -+ LOGI("[udp] ss-redir does not support domain name"); -+ goto CLEAN_UP; -+ } -+ -+ if (verbose) { -+ char src[SS_ADDRSTRLEN]; -+ char dst[SS_ADDRSTRLEN]; -+ strcpy(src, get_addr_str((struct sockaddr *)&src_addr)); -+ strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr)); -+ LOGI("[udp] recv %s via %s", dst, src); -+ } -+#else -+ int len = parse_udprealy_header(buf->array, buf->len, NULL, NULL, NULL); -+#endif -+ -+ if (len == 0) { -+ LOGI("[udp] error in parse header"); -+ // error in parse header -+ goto CLEAN_UP; -+ } -+ -+ // server may return using a different address type other than the type we -+ // have used during sending -+#if defined(MODULE_TUNNEL) || defined(MODULE_REDIR) -+ // Construct packet -+ buf->len -= len; -+ memmove(buf->array, buf->array + len, buf->len); -+#else -+#ifdef ANDROID -+ rx += buf->len; -+#endif -+ // Construct packet -+ brealloc(buf, buf->len + 3, buf_size); -+ memmove(buf->array + 3, buf->array, buf->len); -+ memset(buf->array, 0, 3); -+ buf->len += 3; -+#endif -+ -+#endif -+ -+#ifdef MODULE_REMOTE -+ -+ rx += buf->len; -+ -+ char addr_header_buf[512]; -+ char *addr_header = remote_ctx->addr_header; -+ int addr_header_len = remote_ctx->addr_header_len; -+ -+ if (remote_ctx->af == AF_INET || remote_ctx->af == AF_INET6) { -+ addr_header_len = construct_udprealy_header(&src_addr, addr_header_buf); -+ addr_header = addr_header_buf; -+ } -+ -+ // Construct packet -+ brealloc(buf, buf->len + addr_header_len, buf_size); -+ memmove(buf->array + addr_header_len, buf->array, buf->len); -+ memcpy(buf->array, addr_header, addr_header_len); -+ buf->len += addr_header_len; -+ -+ int err = ss_encrypt_all(buf, server_ctx->method, 0, buf_size); -+ if (err) { -+ // drop the packet silently -+ goto CLEAN_UP; -+ } -+ -+#endif -+ -+ if (buf->len > packet_size) { -+ LOGE("[udp] remote_recv_sendto fragmentation"); -+ goto CLEAN_UP; -+ } -+ -+ size_t remote_src_addr_len = get_sockaddr_len((struct sockaddr *)&remote_ctx->src_addr); -+ -+#ifdef MODULE_REDIR -+ -+ size_t remote_dst_addr_len = get_sockaddr_len((struct sockaddr *)&dst_addr); -+ -+ int src_fd = socket(remote_ctx->src_addr.ss_family, SOCK_DGRAM, 0); -+ if (src_fd < 0) { -+ ERROR("[udp] remote_recv_socket"); -+ goto CLEAN_UP; -+ } -+ int opt = 1; -+ if (setsockopt(src_fd, SOL_IP, IP_TRANSPARENT, &opt, sizeof(opt))) { -+ ERROR("[udp] remote_recv_setsockopt"); -+ close(src_fd); -+ goto CLEAN_UP; -+ } -+ if (setsockopt(src_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { -+ ERROR("[udp] remote_recv_setsockopt"); -+ close(src_fd); -+ goto CLEAN_UP; -+ } -+#ifdef IP_TOS -+ // Set QoS flag -+ int tos = 46; -+ setsockopt(src_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); -+#endif -+ if (bind(src_fd, (struct sockaddr *)&dst_addr, remote_dst_addr_len) != 0) { -+ ERROR("[udp] remote_recv_bind"); -+ close(src_fd); -+ goto CLEAN_UP; -+ } -+ -+ int s = sendto(src_fd, buf->array, buf->len, 0, -+ (struct sockaddr *)&remote_ctx->src_addr, remote_src_addr_len); -+ if (s == -1) { -+ ERROR("[udp] remote_recv_sendto"); -+ close(src_fd); -+ goto CLEAN_UP; -+ } -+ close(src_fd); -+ -+#else -+ -+ int s = sendto(server_ctx->fd, buf->array, buf->len, 0, -+ (struct sockaddr *)&remote_ctx->src_addr, remote_src_addr_len); -+ if (s == -1) { -+ ERROR("[udp] remote_recv_sendto"); -+ goto CLEAN_UP; -+ } -+ -+#endif -+ -+ // handle the UDP packet successfully, -+ // triger the timer -+ ev_timer_again(EV_A_ & remote_ctx->watcher); -+ -+CLEAN_UP: -+ -+ bfree(buf); -+ ss_free(buf); -+} -+ -+static void -+server_recv_cb(EV_P_ ev_io *w, int revents) -+{ -+ server_ctx_t *server_ctx = (server_ctx_t *)w; -+ struct sockaddr_storage src_addr; -+ memset(&src_addr, 0, sizeof(struct sockaddr_storage)); -+ -+ buffer_t *buf = ss_malloc(sizeof(buffer_t)); -+ balloc(buf, buf_size); -+ -+ socklen_t src_addr_len = sizeof(struct sockaddr_storage); -+ unsigned int offset = 0; -+ -+#ifdef MODULE_REDIR -+ char control_buffer[64] = { 0 }; -+ struct msghdr msg; -+ memset(&msg, 0, sizeof(struct msghdr)); -+ struct iovec iov[1]; -+ struct sockaddr_storage dst_addr; -+ memset(&dst_addr, 0, sizeof(struct sockaddr_storage)); -+ -+ msg.msg_name = &src_addr; -+ msg.msg_namelen = src_addr_len; -+ msg.msg_control = control_buffer; -+ msg.msg_controllen = sizeof(control_buffer); -+ -+ iov[0].iov_base = buf->array; -+ iov[0].iov_len = buf_size; -+ msg.msg_iov = iov; -+ msg.msg_iovlen = 1; -+ -+ buf->len = recvmsg(server_ctx->fd, &msg, 0); -+ if (buf->len == -1) { -+ ERROR("[udp] server_recvmsg"); -+ goto CLEAN_UP; -+ } else if (buf->len > packet_size) { -+ ERROR("[udp] UDP server_recv_recvmsg fragmentation"); -+ goto CLEAN_UP; -+ } -+ -+ if (get_dstaddr(&msg, &dst_addr)) { -+ LOGE("[udp] unable to get dest addr"); -+ goto CLEAN_UP; -+ } -+ -+ src_addr_len = msg.msg_namelen; -+#else -+ ssize_t r; -+ r = recvfrom(server_ctx->fd, buf->array, buf_size, -+ 0, (struct sockaddr *)&src_addr, &src_addr_len); -+ -+ if (r == -1) { -+ // error on recv -+ // simply drop that packet -+ ERROR("[udp] server_recv_recvfrom"); -+ goto CLEAN_UP; -+ } else if (r > packet_size) { -+ ERROR("[udp] server_recv_recvfrom fragmentation"); -+ goto CLEAN_UP; -+ } -+ -+ buf->len = r; -+#endif -+ -+#ifdef MODULE_REMOTE -+ tx += buf->len; -+ -+ int err = ss_decrypt_all(buf, server_ctx->method, server_ctx->auth, buf_size); -+ if (err) { -+ // drop the packet silently -+ goto CLEAN_UP; -+ } -+#endif -+ -+#ifdef MODULE_LOCAL -+#if !defined(MODULE_TUNNEL) && !defined(MODULE_REDIR) -+#ifdef ANDROID -+ tx += buf->len; -+#endif -+ uint8_t frag = *(uint8_t *)(buf->array + 2); -+ offset += 3; -+#endif -+#endif -+ -+ /* -+ * -+ * SOCKS5 UDP Request -+ * +----+------+------+----------+----------+----------+ -+ * |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | -+ * +----+------+------+----------+----------+----------+ -+ * | 2 | 1 | 1 | Variable | 2 | Variable | -+ * +----+------+------+----------+----------+----------+ -+ * -+ * SOCKS5 UDP Response -+ * +----+------+------+----------+----------+----------+ -+ * |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | -+ * +----+------+------+----------+----------+----------+ -+ * | 2 | 1 | 1 | Variable | 2 | Variable | -+ * +----+------+------+----------+----------+----------+ -+ * -+ * shadowsocks UDP Request (before encrypted) -+ * +------+----------+----------+----------+-------------+ -+ * | ATYP | DST.ADDR | DST.PORT | DATA | HMAC-SHA1 | -+ * +------+----------+----------+----------+-------------+ -+ * | 1 | Variable | 2 | Variable | 10 | -+ * +------+----------+----------+----------+-------------+ -+ * -+ * If ATYP & ONETIMEAUTH_FLAG(0x10) != 0, Authentication (HMAC-SHA1) is enabled. -+ * -+ * The key of HMAC-SHA1 is (IV + KEY) and the input is the whole packet. -+ * The output of HMAC-SHA is truncated to 10 bytes (leftmost bits). -+ * -+ * shadowsocks UDP Response (before encrypted) -+ * +------+----------+----------+----------+ -+ * | ATYP | DST.ADDR | DST.PORT | DATA | -+ * +------+----------+----------+----------+ -+ * | 1 | Variable | 2 | Variable | -+ * +------+----------+----------+----------+ -+ * -+ * shadowsocks UDP Request and Response (after encrypted) -+ * +-------+--------------+ -+ * | IV | PAYLOAD | -+ * +-------+--------------+ -+ * | Fixed | Variable | -+ * +-------+--------------+ -+ * -+ */ -+ -+#ifdef MODULE_REDIR -+ if (verbose) { -+ char src[SS_ADDRSTRLEN]; -+ char dst[SS_ADDRSTRLEN]; -+ strcpy(src, get_addr_str((struct sockaddr *)&src_addr)); -+ strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr)); -+ LOGI("[udp] redir to %s from %s", dst, src); -+ } -+ -+ char addr_header[512] = { 0 }; -+ int addr_header_len = construct_udprealy_header(&dst_addr, addr_header); -+ -+ if (addr_header_len == 0) { -+ LOGE("[udp] failed to parse tproxy addr"); -+ goto CLEAN_UP; -+ } -+ -+ // reconstruct the buffer -+ brealloc(buf, buf->len + addr_header_len, buf_size); -+ memmove(buf->array + addr_header_len, buf->array, buf->len); -+ memcpy(buf->array, addr_header, addr_header_len); -+ buf->len += addr_header_len; -+ -+#elif MODULE_TUNNEL -+ -+ char addr_header[512] = { 0 }; -+ char *host = server_ctx->tunnel_addr.host; -+ char *port = server_ctx->tunnel_addr.port; -+ uint16_t port_num = (uint16_t)atoi(port); -+ uint16_t port_net_num = htons(port_num); -+ int addr_header_len = 0; -+ -+ struct cork_ip ip; -+ if (cork_ip_init(&ip, host) != -1) { -+ if (ip.version == 4) { -+ // send as IPv4 -+ struct in_addr host_addr; -+ memset(&host_addr, 0, sizeof(struct in_addr)); -+ int host_len = sizeof(struct in_addr); -+ -+ if (dns_pton(AF_INET, host, &host_addr) == -1) { -+ FATAL("IP parser error"); -+ } -+ addr_header[addr_header_len++] = 1; -+ memcpy(addr_header + addr_header_len, &host_addr, host_len); -+ addr_header_len += host_len; -+ } else if (ip.version == 6) { -+ // send as IPv6 -+ struct in6_addr host_addr; -+ memset(&host_addr, 0, sizeof(struct in6_addr)); -+ int host_len = sizeof(struct in6_addr); -+ -+ if (dns_pton(AF_INET6, host, &host_addr) == -1) { -+ FATAL("IP parser error"); -+ } -+ addr_header[addr_header_len++] = 4; -+ memcpy(addr_header + addr_header_len, &host_addr, host_len); -+ addr_header_len += host_len; -+ } else { -+ FATAL("IP parser error"); -+ } -+ } else { -+ // send as domain -+ int host_len = strlen(host); -+ -+ addr_header[addr_header_len++] = 3; -+ addr_header[addr_header_len++] = host_len; -+ memcpy(addr_header + addr_header_len, host, host_len); -+ addr_header_len += host_len; -+ } -+ memcpy(addr_header + addr_header_len, &port_net_num, 2); -+ addr_header_len += 2; -+ -+ // reconstruct the buffer -+ brealloc(buf, buf->len + addr_header_len, buf_size); -+ memmove(buf->array + addr_header_len, buf->array, buf->len); -+ memcpy(buf->array, addr_header, addr_header_len); -+ buf->len += addr_header_len; -+ -+#else -+ -+ char host[257] = { 0 }; -+ char port[64] = { 0 }; -+ struct sockaddr_storage dst_addr; -+ memset(&dst_addr, 0, sizeof(struct sockaddr_storage)); -+ -+ int addr_header_len = parse_udprealy_header(buf->array + offset, buf->len - offset, -+ host, port, &dst_addr); -+ if (addr_header_len == 0) { -+ // error in parse header -+ goto CLEAN_UP; -+ } -+ -+ char *addr_header = buf->array + offset; -+#endif -+ -+#ifdef MODULE_LOCAL -+ char *key = hash_key(server_ctx->remote_addr->sa_family, &src_addr); -+#else -+ char *key = hash_key(dst_addr.ss_family, &src_addr); -+#endif -+ -+ struct cache *conn_cache = server_ctx->conn_cache; -+ -+ remote_ctx_t *remote_ctx = NULL; -+ cache_lookup(conn_cache, key, HASH_KEY_LEN, (void *)&remote_ctx); -+ -+ if (remote_ctx != NULL) { -+ if (sockaddr_cmp(&src_addr, &remote_ctx->src_addr, sizeof(src_addr))) { -+ remote_ctx = NULL; -+ } -+ } -+ -+ // reset the timer -+ if (remote_ctx != NULL) { -+ ev_timer_again(EV_A_ & remote_ctx->watcher); -+ } -+ -+ if (remote_ctx == NULL) { -+ if (verbose) { -+#ifdef MODULE_REDIR -+ char src[SS_ADDRSTRLEN]; -+ char dst[SS_ADDRSTRLEN]; -+ strcpy(src, get_addr_str((struct sockaddr *)&src_addr)); -+ strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr)); -+ LOGI("[udp] cache miss: %s <-> %s", dst, src); -+#else -+ LOGI("[udp] cache miss: %s:%s <-> %s", host, port, -+ get_addr_str((struct sockaddr *)&src_addr)); -+#endif -+ } -+ } else { -+ if (verbose) { -+#ifdef MODULE_REDIR -+ char src[SS_ADDRSTRLEN]; -+ char dst[SS_ADDRSTRLEN]; -+ strcpy(src, get_addr_str((struct sockaddr *)&src_addr)); -+ strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr)); -+ LOGI("[udp] cache hit: %s <-> %s", dst, src); -+#else -+ LOGI("[udp] cache hit: %s:%s <-> %s", host, port, -+ get_addr_str((struct sockaddr *)&src_addr)); -+#endif -+ } -+ } -+ -+#ifdef MODULE_LOCAL -+ -+#if !defined(MODULE_TUNNEL) && !defined(MODULE_REDIR) -+ if (frag) { -+ LOGE("[udp] drop a message since frag is not 0, but %d", frag); -+ goto CLEAN_UP; -+ } -+#endif -+ -+ const struct sockaddr *remote_addr = server_ctx->remote_addr; -+ const int remote_addr_len = server_ctx->remote_addr_len; -+ -+ if (remote_ctx == NULL) { -+ // Bind to any port -+ int remotefd = create_remote_socket(remote_addr->sa_family == AF_INET6); -+ if (remotefd < 0) { -+ ERROR("[udp] udprelay bind() error"); -+ goto CLEAN_UP; -+ } -+ setnonblocking(remotefd); -+ -+#ifdef SO_NOSIGPIPE -+ set_nosigpipe(remotefd); -+#endif -+#ifdef IP_TOS -+ // Set QoS flag -+ int tos = 46; -+ setsockopt(remotefd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); -+#endif -+#ifdef SET_INTERFACE -+ if (server_ctx->iface) { -+ if (setinterface(remotefd, server_ctx->iface) == -1) -+ ERROR("setinterface"); -+ } -+#endif -+ -+#ifdef ANDROID -+ if (vpn) { -+ if (protect_socket(remotefd) == -1) { -+ ERROR("protect_socket"); -+ close(remotefd); -+ goto CLEAN_UP; -+ } -+ } -+#endif -+ -+ // Init remote_ctx -+ remote_ctx = new_remote(remotefd, server_ctx); -+ remote_ctx->src_addr = src_addr; -+ remote_ctx->af = remote_addr->sa_family; -+ remote_ctx->addr_header_len = addr_header_len; -+ memcpy(remote_ctx->addr_header, addr_header, addr_header_len); -+ -+ // Add to conn cache -+ cache_insert(conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); -+ -+ // Start remote io -+ ev_io_start(EV_A_ & remote_ctx->io); -+ ev_timer_start(EV_A_ & remote_ctx->watcher); -+ } -+ -+ if (offset > 0) { -+ buf->len -= offset; -+ memmove(buf->array, buf->array + offset, buf->len); -+ } -+ -+ if (server_ctx->auth) { -+ buf->array[0] |= ONETIMEAUTH_FLAG; -+ } -+ -+ // SSR beg -+ if (server_ctx->protocol_plugin) { -+ obfs_class *protocol_plugin = server_ctx->protocol_plugin; -+ if (protocol_plugin->client_udp_pre_encrypt) { -+ buf->len = protocol_plugin->client_udp_pre_encrypt(server_ctx->protocol, &buf->array, buf->len, &buf->capacity); -+ } -+ } -+ //SSR end -+ -+ int err = ss_encrypt_all(buf, server_ctx->method, server_ctx->auth, buf->len); -+ -+ if (err) { -+ // drop the packet silently -+ goto CLEAN_UP; -+ } -+ -+ if (buf->len > packet_size) { -+ LOGE("[udp] server_recv_sendto fragmentation"); -+ goto CLEAN_UP; -+ } -+ -+ int s = sendto(remote_ctx->fd, buf->array, buf->len, 0, remote_addr, remote_addr_len); -+ -+ if (s == -1) { -+ ERROR("[udp] server_recv_sendto"); -+ } -+ -+#else -+ -+ int cache_hit = 0; -+ int need_query = 0; -+ -+ if (buf->len - addr_header_len > packet_size) { -+ LOGE("[udp] server_recv_sendto fragmentation"); -+ goto CLEAN_UP; -+ } -+ -+ if (remote_ctx != NULL) { -+ cache_hit = 1; -+ // detect destination mismatch -+ if (remote_ctx->addr_header_len != addr_header_len -+ || memcmp(addr_header, remote_ctx->addr_header, addr_header_len) != 0) { -+ if (dst_addr.ss_family != AF_INET && dst_addr.ss_family != AF_INET6) { -+ need_query = 1; -+ } -+ } else { -+ memcpy(&dst_addr, &remote_ctx->dst_addr, sizeof(struct sockaddr_storage)); -+ } -+ } else { -+ if (dst_addr.ss_family == AF_INET || dst_addr.ss_family == AF_INET6) { -+ int remotefd = create_remote_socket(dst_addr.ss_family == AF_INET6); -+ if (remotefd != -1) { -+ setnonblocking(remotefd); -+#ifdef SO_BROADCAST -+ set_broadcast(remotefd); -+#endif -+#ifdef SO_NOSIGPIPE -+ set_nosigpipe(remotefd); -+#endif -+#ifdef IP_TOS -+ // Set QoS flag -+ int tos = 46; -+ setsockopt(remotefd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); -+#endif -+#ifdef SET_INTERFACE -+ if (server_ctx->iface) { -+ if (setinterface(remotefd, server_ctx->iface) == -1) -+ ERROR("setinterface"); -+ } -+#endif -+ remote_ctx = new_remote(remotefd, server_ctx); -+ remote_ctx->src_addr = src_addr; -+ remote_ctx->server_ctx = server_ctx; -+ remote_ctx->addr_header_len = addr_header_len; -+ memcpy(remote_ctx->addr_header, addr_header, addr_header_len); -+ memcpy(&remote_ctx->dst_addr, &dst_addr, sizeof(struct sockaddr_storage)); -+ } else { -+ ERROR("[udp] bind() error"); -+ goto CLEAN_UP; -+ } -+ } -+ } -+ -+ if (remote_ctx != NULL && !need_query) { -+ size_t addr_len = get_sockaddr_len((struct sockaddr *)&dst_addr); -+ int s = sendto(remote_ctx->fd, buf->array + addr_header_len, -+ buf->len - addr_header_len, 0, -+ (struct sockaddr *)&dst_addr, addr_len); -+ -+ if (s == -1) { -+ ERROR("[udp] sendto_remote"); -+ if (!cache_hit) { -+ close_and_free_remote(EV_A_ remote_ctx); -+ } -+ } else { -+ if (!cache_hit) { -+ // Add to conn cache -+ remote_ctx->af = dst_addr.ss_family; -+ char *key = hash_key(remote_ctx->af, &remote_ctx->src_addr); -+ cache_insert(server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); -+ -+ ev_io_start(EV_A_ & remote_ctx->io); -+ ev_timer_start(EV_A_ & remote_ctx->watcher); -+ } -+ } -+ } else { -+ struct addrinfo hints; -+ memset(&hints, 0, sizeof(struct addrinfo)); -+ hints.ai_family = AF_UNSPEC; -+ hints.ai_socktype = SOCK_DGRAM; -+ hints.ai_protocol = IPPROTO_UDP; -+ -+ struct query_ctx *query_ctx = new_query_ctx(buf->array + addr_header_len, -+ buf->len - addr_header_len); -+ query_ctx->server_ctx = server_ctx; -+ query_ctx->addr_header_len = addr_header_len; -+ query_ctx->src_addr = src_addr; -+ memcpy(query_ctx->addr_header, addr_header, addr_header_len); -+ -+ if (need_query) { -+ query_ctx->remote_ctx = remote_ctx; -+ } -+ -+ struct ResolvQuery *query = resolv_query(host, query_resolve_cb, -+ NULL, query_ctx, htons(atoi(port))); -+ if (query == NULL) { -+ ERROR("[udp] unable to create DNS query"); -+ close_and_free_query(EV_A_ query_ctx); -+ goto CLEAN_UP; -+ } -+ query_ctx->query = query; -+ } -+#endif -+ -+CLEAN_UP: -+ bfree(buf); -+ ss_free(buf); -+} -+ -+void -+free_cb(void *key, void *element) -+{ -+ remote_ctx_t *remote_ctx = (remote_ctx_t *)element; -+ -+ if (verbose) { -+ LOGI("[udp] one connection freed"); -+ } -+ -+ close_and_free_remote(EV_DEFAULT, remote_ctx); -+} -+ -+int -+init_udprelay(const char *server_host, const char *server_port, -+#ifdef MODULE_LOCAL -+ const struct sockaddr *remote_addr, const int remote_addr_len, -+#ifdef MODULE_TUNNEL -+ const ss_addr_t tunnel_addr, -+#endif -+#endif -+ int mtu, int method, int auth, int timeout, const char *iface, const char *protocol, const char *protocol_param) -+{ -+ // Initialize ev loop -+ struct ev_loop *loop = EV_DEFAULT; -+ -+ // Initialize MTU -+ if (mtu > 0) { -+ packet_size = mtu - 1 - 28 - 2 - 64; -+ buf_size = packet_size * 2; -+ } -+ -+ // Initialize cache -+ struct cache *conn_cache; -+ cache_create(&conn_cache, MAX_UDP_CONN_NUM, free_cb); -+ -+ // //////////////////////////////////////////////// -+ // Setup server context -+ -+ // Bind to port -+ int serverfd = create_server_socket(server_host, server_port); -+ if (serverfd < 0) { -+ FATAL("[udp] bind() error"); -+ } -+ setnonblocking(serverfd); -+ if (protocol != NULL && strcmp(protocol, "verify_sha1") == 0) { -+ auth = 1; -+ protocol = NULL; -+ } -+ -+ server_ctx_t *server_ctx = new_server_ctx(serverfd); -+#ifdef MODULE_REMOTE -+ server_ctx->loop = loop; -+#endif -+ server_ctx->auth = auth; -+ server_ctx->timeout = max(timeout, MIN_UDP_TIMEOUT); -+ server_ctx->method = method; -+ server_ctx->iface = iface; -+ server_ctx->conn_cache = conn_cache; -+#ifdef MODULE_LOCAL -+ server_ctx->remote_addr = remote_addr; -+ server_ctx->remote_addr_len = remote_addr_len; -+ //SSR beg -+ server_ctx->protocol_plugin = new_obfs_class((char *)protocol); -+ if (server_ctx->protocol_plugin) { -+ server_ctx->protocol = server_ctx->protocol_plugin->new_obfs(); -+ server_ctx->protocol_global = server_ctx->protocol_plugin->init_data(); -+ } -+ -+ server_info _server_info; -+ memset(&_server_info, 0, sizeof(server_info)); -+ strcpy(_server_info.host, inet_ntoa(((struct sockaddr_in*)remote_addr)->sin_addr)); -+ _server_info.port = ((struct sockaddr_in*)remote_addr)->sin_port; -+ _server_info.port = _server_info.port >> 8 | _server_info.port << 8; -+ _server_info.g_data = server_ctx->protocol_global; -+ _server_info.param = (char *)protocol_param; -+ _server_info.key = enc_get_key(); -+ _server_info.key_len = enc_get_key_len(); -+ -+ if (server_ctx->protocol_plugin) -+ server_ctx->protocol_plugin->set_server_info(server_ctx->protocol, &_server_info); -+ //SSR end -+#ifdef MODULE_TUNNEL -+ server_ctx->tunnel_addr = tunnel_addr; -+#endif -+#endif -+ -+ ev_io_start(loop, &server_ctx->io); -+ -+ server_ctx_list[server_num++] = server_ctx; -+ -+ return 0; -+} -+ -+void -+free_udprelay() -+{ -+ struct ev_loop *loop = EV_DEFAULT; -+ while (server_num-- > 0) { -+ server_ctx_t *server_ctx = server_ctx_list[server_num]; -+ -+#ifdef MODULE_LOCAL -+ //SSR beg -+ if (server_ctx->protocol_plugin) { -+ server_ctx->protocol_plugin->dispose(server_ctx->protocol); -+ server_ctx->protocol = NULL; -+ free_obfs_class(server_ctx->protocol_plugin); -+ server_ctx->protocol_plugin = NULL; -+ } -+ //SSR end -+#endif -+ -+ ev_io_stop(loop, &server_ctx->io); -+ close(server_ctx->fd); -+ cache_delete(server_ctx->conn_cache, 0); -+ ss_free(server_ctx); -+ server_ctx_list[server_num] = NULL; -+ } -+} -diff --git a/server/udprelay.h b/server/udprelay.h -new file mode 100644 -index 0000000..89876d4 ---- /dev/null -+++ b/server/udprelay.h -@@ -0,0 +1,95 @@ -+/* -+ * udprelay.h - Define UDP relay's buffers and callbacks -+ * -+ * Copyright (C) 2013 - 2016, 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 _UDPRELAY_H -+#define _UDPRELAY_H -+ -+#include -+#include -+ -+#include "encrypt.h" -+#include "jconf.h" -+#include "obfs.h" -+ -+#ifdef MODULE_REMOTE -+#include "resolv.h" -+#endif -+ -+#include "cache.h" -+ -+#include "common.h" -+ -+#define MAX_UDP_PACKET_SIZE (65507) -+ -+#define DEFAULT_PACKET_SIZE 1397 // 1492 - 1 - 28 - 2 - 64 = 1397, the default MTU for UDP relay -+ -+typedef struct server_ctx { -+ ev_io io; -+ int fd; -+ int method; -+ int auth; -+ int timeout; -+ const char *iface; -+ struct cache *conn_cache; -+#ifdef MODULE_LOCAL -+ const struct sockaddr *remote_addr; -+ int remote_addr_len; -+#ifdef MODULE_TUNNEL -+ ss_addr_t tunnel_addr; -+#endif -+#endif -+#ifdef MODULE_REMOTE -+ struct ev_loop *loop; -+#endif -+ // SSR -+ obfs *protocol; -+ obfs_class *protocol_plugin; -+ void *protocol_global; -+} server_ctx_t; -+ -+#ifdef MODULE_REMOTE -+typedef struct query_ctx { -+ struct ResolvQuery *query; -+ struct sockaddr_storage src_addr; -+ buffer_t *buf; -+ int addr_header_len; -+ char addr_header[384]; -+ struct server_ctx *server_ctx; -+ struct remote_ctx *remote_ctx; -+} query_ctx_t; -+#endif -+ -+typedef struct remote_ctx { -+ ev_io io; -+ ev_timer watcher; -+ int af; -+ int fd; -+ int addr_header_len; -+ char addr_header[384]; -+ struct sockaddr_storage src_addr; -+#ifdef MODULE_REMOTE -+ struct sockaddr_storage dst_addr; -+#endif -+ struct server_ctx *server_ctx; -+} remote_ctx_t; -+ -+#endif // _UDPRELAY_H -diff --git a/server/uthash.h b/server/uthash.h -new file mode 100644 -index 0000000..45d1f9f ---- /dev/null -+++ b/server/uthash.h -@@ -0,0 +1,1074 @@ -+/* -+Copyright (c) 2003-2016, Troy D. Hanson http://troydhanson.github.com/uthash/ -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER -+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. -+*/ -+ -+#ifndef UTHASH_H -+#define UTHASH_H -+ -+#define UTHASH_VERSION 2.0.1 -+ -+#include /* memcmp,strlen */ -+#include /* ptrdiff_t */ -+#include /* exit() */ -+ -+/* These macros use decltype or the earlier __typeof GNU extension. -+ As decltype is only available in newer compilers (VS2010 or gcc 4.3+ -+ when compiling c++ source) this code uses whatever method is needed -+ or, for VS2008 where neither is available, uses casting workarounds. */ -+#if defined(_MSC_VER) /* MS compiler */ -+#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ -+#define DECLTYPE(x) (decltype(x)) -+#else /* VS2008 or older (or VS2010 in C mode) */ -+#define NO_DECLTYPE -+#define DECLTYPE(x) -+#endif -+#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__) -+#define NO_DECLTYPE -+#define DECLTYPE(x) -+#else /* GNU, Sun and other compilers */ -+#define DECLTYPE(x) (__typeof(x)) -+#endif -+ -+#ifdef NO_DECLTYPE -+#define DECLTYPE_ASSIGN(dst,src) \ -+do { \ -+ char **_da_dst = (char**)(&(dst)); \ -+ *_da_dst = (char*)(src); \ -+} while (0) -+#else -+#define DECLTYPE_ASSIGN(dst,src) \ -+do { \ -+ (dst) = DECLTYPE(dst)(src); \ -+} while (0) -+#endif -+ -+/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ -+#if defined(_WIN32) -+#if defined(_MSC_VER) && _MSC_VER >= 1600 -+#include -+#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__) -+#include -+#else -+typedef unsigned int uint32_t; -+typedef unsigned char uint8_t; -+#endif -+#elif defined(__GNUC__) && !defined(__VXWORKS__) -+#include -+#else -+typedef unsigned int uint32_t; -+typedef unsigned char uint8_t; -+#endif -+ -+#ifndef uthash_fatal -+#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ -+#endif -+#ifndef uthash_malloc -+#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ -+#endif -+#ifndef uthash_free -+#define uthash_free(ptr,sz) free(ptr) /* free fcn */ -+#endif -+#ifndef uthash_strlen -+#define uthash_strlen(s) strlen(s) -+#endif -+#ifndef uthash_memcmp -+#define uthash_memcmp(a,b,n) memcmp(a,b,n) -+#endif -+ -+#ifndef uthash_noexpand_fyi -+#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ -+#endif -+#ifndef uthash_expand_fyi -+#define uthash_expand_fyi(tbl) /* can be defined to log expands */ -+#endif -+ -+/* initial number of buckets */ -+#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ -+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ -+#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ -+ -+/* calculate the element whose hash handle address is hhp */ -+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) -+/* calculate the hash handle from element address elp */ -+#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho))) -+ -+#define HASH_VALUE(keyptr,keylen,hashv) \ -+do { \ -+ HASH_FCN(keyptr, keylen, hashv); \ -+} while (0) -+ -+#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ -+do { \ -+ (out) = NULL; \ -+ if (head) { \ -+ unsigned _hf_bkt; \ -+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ -+ if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ -+ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ -+ } \ -+ } \ -+} while (0) -+ -+#define HASH_FIND(hh,head,keyptr,keylen,out) \ -+do { \ -+ unsigned _hf_hashv; \ -+ HASH_VALUE(keyptr, keylen, _hf_hashv); \ -+ HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ -+} while (0) -+ -+#ifdef HASH_BLOOM -+#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) -+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) -+#define HASH_BLOOM_MAKE(tbl) \ -+do { \ -+ (tbl)->bloom_nbits = HASH_BLOOM; \ -+ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ -+ if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ -+ memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ -+ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ -+} while (0) -+ -+#define HASH_BLOOM_FREE(tbl) \ -+do { \ -+ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ -+} while (0) -+ -+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) -+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) -+ -+#define HASH_BLOOM_ADD(tbl,hashv) \ -+ HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) -+ -+#define HASH_BLOOM_TEST(tbl,hashv) \ -+ HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) -+ -+#else -+#define HASH_BLOOM_MAKE(tbl) -+#define HASH_BLOOM_FREE(tbl) -+#define HASH_BLOOM_ADD(tbl,hashv) -+#define HASH_BLOOM_TEST(tbl,hashv) (1) -+#define HASH_BLOOM_BYTELEN 0U -+#endif -+ -+#define HASH_MAKE_TABLE(hh,head) \ -+do { \ -+ (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ -+ sizeof(UT_hash_table)); \ -+ if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ -+ memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ -+ (head)->hh.tbl->tail = &((head)->hh); \ -+ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ -+ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ -+ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ -+ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ -+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ -+ if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ -+ memset((head)->hh.tbl->buckets, 0, \ -+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ -+ HASH_BLOOM_MAKE((head)->hh.tbl); \ -+ (head)->hh.tbl->signature = HASH_SIGNATURE; \ -+} while (0) -+ -+#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ -+do { \ -+ (replaced) = NULL; \ -+ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ -+ if (replaced) { \ -+ HASH_DELETE(hh, head, replaced); \ -+ } \ -+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ -+} while (0) -+ -+#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ -+do { \ -+ (replaced) = NULL; \ -+ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ -+ if (replaced) { \ -+ HASH_DELETE(hh, head, replaced); \ -+ } \ -+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ -+} while (0) -+ -+#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ -+do { \ -+ unsigned _hr_hashv; \ -+ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ -+ HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ -+} while (0) -+ -+#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ -+do { \ -+ unsigned _hr_hashv; \ -+ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ -+ HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ -+} while (0) -+ -+#define HASH_APPEND_LIST(hh, head, add) \ -+do { \ -+ (add)->hh.next = NULL; \ -+ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ -+ (head)->hh.tbl->tail->next = (add); \ -+ (head)->hh.tbl->tail = &((add)->hh); \ -+} while (0) -+ -+#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ -+do { \ -+ unsigned _ha_bkt; \ -+ (add)->hh.hashv = (hashval); \ -+ (add)->hh.key = (char*) (keyptr); \ -+ (add)->hh.keylen = (unsigned) (keylen_in); \ -+ if (!(head)) { \ -+ (add)->hh.next = NULL; \ -+ (add)->hh.prev = NULL; \ -+ (head) = (add); \ -+ HASH_MAKE_TABLE(hh, head); \ -+ } else { \ -+ struct UT_hash_handle *_hs_iter = &(head)->hh; \ -+ (add)->hh.tbl = (head)->hh.tbl; \ -+ do { \ -+ if (cmpfcn(DECLTYPE(head) ELMT_FROM_HH((head)->hh.tbl, _hs_iter), add) > 0) \ -+ break; \ -+ } while ((_hs_iter = _hs_iter->next)); \ -+ if (_hs_iter) { \ -+ (add)->hh.next = _hs_iter; \ -+ if (((add)->hh.prev = _hs_iter->prev)) { \ -+ HH_FROM_ELMT((head)->hh.tbl, _hs_iter->prev)->next = (add); \ -+ } else { \ -+ (head) = (add); \ -+ } \ -+ _hs_iter->prev = (add); \ -+ } else { \ -+ HASH_APPEND_LIST(hh, head, add); \ -+ } \ -+ } \ -+ (head)->hh.tbl->num_items++; \ -+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ -+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \ -+ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ -+ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ -+ HASH_FSCK(hh, head); \ -+} while (0) -+ -+#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ -+do { \ -+ unsigned _hs_hashv; \ -+ HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ -+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ -+} while (0) -+ -+#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ -+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) -+ -+#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ -+ HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) -+ -+#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ -+do { \ -+ unsigned _ha_bkt; \ -+ (add)->hh.hashv = (hashval); \ -+ (add)->hh.key = (char*) (keyptr); \ -+ (add)->hh.keylen = (unsigned) (keylen_in); \ -+ if (!(head)) { \ -+ (add)->hh.next = NULL; \ -+ (add)->hh.prev = NULL; \ -+ (head) = (add); \ -+ HASH_MAKE_TABLE(hh, head); \ -+ } else { \ -+ (add)->hh.tbl = (head)->hh.tbl; \ -+ HASH_APPEND_LIST(hh, head, add); \ -+ } \ -+ (head)->hh.tbl->num_items++; \ -+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ -+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \ -+ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ -+ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ -+ HASH_FSCK(hh, head); \ -+} while (0) -+ -+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ -+do { \ -+ unsigned _ha_hashv; \ -+ HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ -+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ -+} while (0) -+ -+#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ -+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) -+ -+#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ -+ HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) -+ -+#define HASH_TO_BKT(hashv,num_bkts,bkt) \ -+do { \ -+ bkt = ((hashv) & ((num_bkts) - 1U)); \ -+} while (0) -+ -+/* delete "delptr" from the hash table. -+ * "the usual" patch-up process for the app-order doubly-linked-list. -+ * The use of _hd_hh_del below deserves special explanation. -+ * These used to be expressed using (delptr) but that led to a bug -+ * if someone used the same symbol for the head and deletee, like -+ * HASH_DELETE(hh,users,users); -+ * We want that to work, but by changing the head (users) below -+ * we were forfeiting our ability to further refer to the deletee (users) -+ * in the patch-up process. Solution: use scratch space to -+ * copy the deletee pointer, then the latter references are via that -+ * scratch pointer rather than through the repointed (users) symbol. -+ */ -+#define HASH_DELETE(hh,head,delptr) \ -+do { \ -+ struct UT_hash_handle *_hd_hh_del; \ -+ if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ -+ uthash_free((head)->hh.tbl->buckets, \ -+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ -+ HASH_BLOOM_FREE((head)->hh.tbl); \ -+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ -+ head = NULL; \ -+ } else { \ -+ unsigned _hd_bkt; \ -+ _hd_hh_del = &((delptr)->hh); \ -+ if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ -+ (head)->hh.tbl->tail = \ -+ (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ -+ (head)->hh.tbl->hho); \ -+ } \ -+ if ((delptr)->hh.prev != NULL) { \ -+ ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ -+ (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ -+ } else { \ -+ DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ -+ } \ -+ if (_hd_hh_del->next != NULL) { \ -+ ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ -+ (head)->hh.tbl->hho))->prev = \ -+ _hd_hh_del->prev; \ -+ } \ -+ HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ -+ HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ -+ (head)->hh.tbl->num_items--; \ -+ } \ -+ HASH_FSCK(hh,head); \ -+} while (0) -+ -+ -+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ -+#define HASH_FIND_STR(head,findstr,out) \ -+ HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out) -+#define HASH_ADD_STR(head,strfield,add) \ -+ HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add) -+#define HASH_REPLACE_STR(head,strfield,add,replaced) \ -+ HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced) -+#define HASH_FIND_INT(head,findint,out) \ -+ HASH_FIND(hh,head,findint,sizeof(int),out) -+#define HASH_ADD_INT(head,intfield,add) \ -+ HASH_ADD(hh,head,intfield,sizeof(int),add) -+#define HASH_REPLACE_INT(head,intfield,add,replaced) \ -+ HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) -+#define HASH_FIND_PTR(head,findptr,out) \ -+ HASH_FIND(hh,head,findptr,sizeof(void *),out) -+#define HASH_ADD_PTR(head,ptrfield,add) \ -+ HASH_ADD(hh,head,ptrfield,sizeof(void *),add) -+#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ -+ HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) -+#define HASH_DEL(head,delptr) \ -+ HASH_DELETE(hh,head,delptr) -+ -+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. -+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. -+ */ -+#ifdef HASH_DEBUG -+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) -+#define HASH_FSCK(hh,head) \ -+do { \ -+ struct UT_hash_handle *_thh; \ -+ if (head) { \ -+ unsigned _bkt_i; \ -+ unsigned _count; \ -+ char *_prev; \ -+ _count = 0; \ -+ for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ -+ unsigned _bkt_count = 0; \ -+ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ -+ _prev = NULL; \ -+ while (_thh) { \ -+ if (_prev != (char*)(_thh->hh_prev)) { \ -+ HASH_OOPS("invalid hh_prev %p, actual %p\n", \ -+ _thh->hh_prev, _prev ); \ -+ } \ -+ _bkt_count++; \ -+ _prev = (char*)(_thh); \ -+ _thh = _thh->hh_next; \ -+ } \ -+ _count += _bkt_count; \ -+ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ -+ HASH_OOPS("invalid bucket count %u, actual %u\n", \ -+ (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ -+ } \ -+ } \ -+ if (_count != (head)->hh.tbl->num_items) { \ -+ HASH_OOPS("invalid hh item count %u, actual %u\n", \ -+ (head)->hh.tbl->num_items, _count ); \ -+ } \ -+ /* traverse hh in app order; check next/prev integrity, count */ \ -+ _count = 0; \ -+ _prev = NULL; \ -+ _thh = &(head)->hh; \ -+ while (_thh) { \ -+ _count++; \ -+ if (_prev !=(char*)(_thh->prev)) { \ -+ HASH_OOPS("invalid prev %p, actual %p\n", \ -+ _thh->prev, _prev ); \ -+ } \ -+ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ -+ _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ -+ (head)->hh.tbl->hho) : NULL ); \ -+ } \ -+ if (_count != (head)->hh.tbl->num_items) { \ -+ HASH_OOPS("invalid app item count %u, actual %u\n", \ -+ (head)->hh.tbl->num_items, _count ); \ -+ } \ -+ } \ -+} while (0) -+#else -+#define HASH_FSCK(hh,head) -+#endif -+ -+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to -+ * the descriptor to which this macro is defined for tuning the hash function. -+ * The app can #include to get the prototype for write(2). */ -+#ifdef HASH_EMIT_KEYS -+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ -+do { \ -+ unsigned _klen = fieldlen; \ -+ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ -+ write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ -+} while (0) -+#else -+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) -+#endif -+ -+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ -+#ifdef HASH_FUNCTION -+#define HASH_FCN HASH_FUNCTION -+#else -+#define HASH_FCN HASH_JEN -+#endif -+ -+/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ -+#define HASH_BER(key,keylen,hashv) \ -+do { \ -+ unsigned _hb_keylen=(unsigned)keylen; \ -+ const unsigned char *_hb_key=(const unsigned char*)(key); \ -+ (hashv) = 0; \ -+ while (_hb_keylen-- != 0U) { \ -+ (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ -+ } \ -+} while (0) -+ -+ -+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at -+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ -+#define HASH_SAX(key,keylen,hashv) \ -+do { \ -+ unsigned _sx_i; \ -+ const unsigned char *_hs_key=(const unsigned char*)(key); \ -+ hashv = 0; \ -+ for(_sx_i=0; _sx_i < keylen; _sx_i++) { \ -+ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ -+ } \ -+} while (0) -+/* FNV-1a variation */ -+#define HASH_FNV(key,keylen,hashv) \ -+do { \ -+ unsigned _fn_i; \ -+ const unsigned char *_hf_key=(const unsigned char*)(key); \ -+ hashv = 2166136261U; \ -+ for(_fn_i=0; _fn_i < keylen; _fn_i++) { \ -+ hashv = hashv ^ _hf_key[_fn_i]; \ -+ hashv = hashv * 16777619U; \ -+ } \ -+} while (0) -+ -+#define HASH_OAT(key,keylen,hashv) \ -+do { \ -+ unsigned _ho_i; \ -+ const unsigned char *_ho_key=(const unsigned char*)(key); \ -+ hashv = 0; \ -+ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ -+ hashv += _ho_key[_ho_i]; \ -+ hashv += (hashv << 10); \ -+ hashv ^= (hashv >> 6); \ -+ } \ -+ hashv += (hashv << 3); \ -+ hashv ^= (hashv >> 11); \ -+ hashv += (hashv << 15); \ -+} while (0) -+ -+#define HASH_JEN_MIX(a,b,c) \ -+do { \ -+ a -= b; a -= c; a ^= ( c >> 13 ); \ -+ b -= c; b -= a; b ^= ( a << 8 ); \ -+ c -= a; c -= b; c ^= ( b >> 13 ); \ -+ a -= b; a -= c; a ^= ( c >> 12 ); \ -+ b -= c; b -= a; b ^= ( a << 16 ); \ -+ c -= a; c -= b; c ^= ( b >> 5 ); \ -+ a -= b; a -= c; a ^= ( c >> 3 ); \ -+ b -= c; b -= a; b ^= ( a << 10 ); \ -+ c -= a; c -= b; c ^= ( b >> 15 ); \ -+} while (0) -+ -+#define HASH_JEN(key,keylen,hashv) \ -+do { \ -+ unsigned _hj_i,_hj_j,_hj_k; \ -+ unsigned const char *_hj_key=(unsigned const char*)(key); \ -+ hashv = 0xfeedbeefu; \ -+ _hj_i = _hj_j = 0x9e3779b9u; \ -+ _hj_k = (unsigned)(keylen); \ -+ while (_hj_k >= 12U) { \ -+ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ -+ + ( (unsigned)_hj_key[2] << 16 ) \ -+ + ( (unsigned)_hj_key[3] << 24 ) ); \ -+ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ -+ + ( (unsigned)_hj_key[6] << 16 ) \ -+ + ( (unsigned)_hj_key[7] << 24 ) ); \ -+ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ -+ + ( (unsigned)_hj_key[10] << 16 ) \ -+ + ( (unsigned)_hj_key[11] << 24 ) ); \ -+ \ -+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ -+ \ -+ _hj_key += 12; \ -+ _hj_k -= 12U; \ -+ } \ -+ hashv += (unsigned)(keylen); \ -+ switch ( _hj_k ) { \ -+ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ -+ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ -+ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ -+ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ -+ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ -+ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ -+ case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ -+ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ -+ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ -+ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ -+ case 1: _hj_i += _hj_key[0]; \ -+ } \ -+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ -+} while (0) -+ -+/* The Paul Hsieh hash function */ -+#undef get16bits -+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ -+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -+#define get16bits(d) (*((const uint16_t *) (d))) -+#endif -+ -+#if !defined (get16bits) -+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ -+ +(uint32_t)(((const uint8_t *)(d))[0]) ) -+#endif -+#define HASH_SFH(key,keylen,hashv) \ -+do { \ -+ unsigned const char *_sfh_key=(unsigned const char*)(key); \ -+ uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ -+ \ -+ unsigned _sfh_rem = _sfh_len & 3U; \ -+ _sfh_len >>= 2; \ -+ hashv = 0xcafebabeu; \ -+ \ -+ /* Main loop */ \ -+ for (;_sfh_len > 0U; _sfh_len--) { \ -+ hashv += get16bits (_sfh_key); \ -+ _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ -+ hashv = (hashv << 16) ^ _sfh_tmp; \ -+ _sfh_key += 2U*sizeof (uint16_t); \ -+ hashv += hashv >> 11; \ -+ } \ -+ \ -+ /* Handle end cases */ \ -+ switch (_sfh_rem) { \ -+ case 3: hashv += get16bits (_sfh_key); \ -+ hashv ^= hashv << 16; \ -+ hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ -+ hashv += hashv >> 11; \ -+ break; \ -+ case 2: hashv += get16bits (_sfh_key); \ -+ hashv ^= hashv << 11; \ -+ hashv += hashv >> 17; \ -+ break; \ -+ case 1: hashv += *_sfh_key; \ -+ hashv ^= hashv << 10; \ -+ hashv += hashv >> 1; \ -+ } \ -+ \ -+ /* Force "avalanching" of final 127 bits */ \ -+ hashv ^= hashv << 3; \ -+ hashv += hashv >> 5; \ -+ hashv ^= hashv << 4; \ -+ hashv += hashv >> 17; \ -+ hashv ^= hashv << 25; \ -+ hashv += hashv >> 6; \ -+} while (0) -+ -+#ifdef HASH_USING_NO_STRICT_ALIASING -+/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. -+ * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. -+ * MurmurHash uses the faster approach only on CPU's where we know it's safe. -+ * -+ * Note the preprocessor built-in defines can be emitted using: -+ * -+ * gcc -m64 -dM -E - < /dev/null (on gcc) -+ * cc -## a.c (where a.c is a simple test file) (Sun Studio) -+ */ -+#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) -+#define MUR_GETBLOCK(p,i) p[i] -+#else /* non intel */ -+#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL) -+#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL) -+#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL) -+#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL) -+#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) -+#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) -+#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) -+#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) -+#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) -+#else /* assume little endian non-intel */ -+#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) -+#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) -+#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) -+#endif -+#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ -+ (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ -+ (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ -+ MUR_ONE_THREE(p)))) -+#endif -+#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) -+#define MUR_FMIX(_h) \ -+do { \ -+ _h ^= _h >> 16; \ -+ _h *= 0x85ebca6bu; \ -+ _h ^= _h >> 13; \ -+ _h *= 0xc2b2ae35u; \ -+ _h ^= _h >> 16; \ -+} while (0) -+ -+#define HASH_MUR(key,keylen,hashv) \ -+do { \ -+ const uint8_t *_mur_data = (const uint8_t*)(key); \ -+ const int _mur_nblocks = (int)(keylen) / 4; \ -+ uint32_t _mur_h1 = 0xf88D5353u; \ -+ uint32_t _mur_c1 = 0xcc9e2d51u; \ -+ uint32_t _mur_c2 = 0x1b873593u; \ -+ uint32_t _mur_k1 = 0; \ -+ const uint8_t *_mur_tail; \ -+ const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ -+ int _mur_i; \ -+ for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) { \ -+ _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ -+ _mur_k1 *= _mur_c1; \ -+ _mur_k1 = MUR_ROTL32(_mur_k1,15); \ -+ _mur_k1 *= _mur_c2; \ -+ \ -+ _mur_h1 ^= _mur_k1; \ -+ _mur_h1 = MUR_ROTL32(_mur_h1,13); \ -+ _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \ -+ } \ -+ _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ -+ _mur_k1=0; \ -+ switch((keylen) & 3U) { \ -+ case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ -+ case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ -+ case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ -+ _mur_k1 *= _mur_c1; \ -+ _mur_k1 = MUR_ROTL32(_mur_k1,15); \ -+ _mur_k1 *= _mur_c2; \ -+ _mur_h1 ^= _mur_k1; \ -+ } \ -+ _mur_h1 ^= (uint32_t)(keylen); \ -+ MUR_FMIX(_mur_h1); \ -+ hashv = _mur_h1; \ -+} while (0) -+#endif /* HASH_USING_NO_STRICT_ALIASING */ -+ -+/* iterate over items in a known bucket to find desired item */ -+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ -+do { \ -+ if ((head).hh_head != NULL) { \ -+ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ -+ } else { \ -+ (out) = NULL; \ -+ } \ -+ while ((out) != NULL) { \ -+ if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ -+ if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) { \ -+ break; \ -+ } \ -+ } \ -+ if ((out)->hh.hh_next != NULL) { \ -+ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ -+ } else { \ -+ (out) = NULL; \ -+ } \ -+ } \ -+} while (0) -+ -+/* add an item to a bucket */ -+#define HASH_ADD_TO_BKT(head,addhh) \ -+do { \ -+ head.count++; \ -+ (addhh)->hh_next = head.hh_head; \ -+ (addhh)->hh_prev = NULL; \ -+ if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); } \ -+ (head).hh_head=addhh; \ -+ if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH)) \ -+ && ((addhh)->tbl->noexpand != 1U)) { \ -+ HASH_EXPAND_BUCKETS((addhh)->tbl); \ -+ } \ -+} while (0) -+ -+/* remove an item from a given bucket */ -+#define HASH_DEL_IN_BKT(hh,head,hh_del) \ -+ (head).count--; \ -+ if ((head).hh_head == hh_del) { \ -+ (head).hh_head = hh_del->hh_next; \ -+ } \ -+ if (hh_del->hh_prev) { \ -+ hh_del->hh_prev->hh_next = hh_del->hh_next; \ -+ } \ -+ if (hh_del->hh_next) { \ -+ hh_del->hh_next->hh_prev = hh_del->hh_prev; \ -+ } -+ -+/* Bucket expansion has the effect of doubling the number of buckets -+ * and redistributing the items into the new buckets. Ideally the -+ * items will distribute more or less evenly into the new buckets -+ * (the extent to which this is true is a measure of the quality of -+ * the hash function as it applies to the key domain). -+ * -+ * With the items distributed into more buckets, the chain length -+ * (item count) in each bucket is reduced. Thus by expanding buckets -+ * the hash keeps a bound on the chain length. This bounded chain -+ * length is the essence of how a hash provides constant time lookup. -+ * -+ * The calculation of tbl->ideal_chain_maxlen below deserves some -+ * explanation. First, keep in mind that we're calculating the ideal -+ * maximum chain length based on the *new* (doubled) bucket count. -+ * In fractions this is just n/b (n=number of items,b=new num buckets). -+ * Since the ideal chain length is an integer, we want to calculate -+ * ceil(n/b). We don't depend on floating point arithmetic in this -+ * hash, so to calculate ceil(n/b) with integers we could write -+ * -+ * ceil(n/b) = (n/b) + ((n%b)?1:0) -+ * -+ * and in fact a previous version of this hash did just that. -+ * But now we have improved things a bit by recognizing that b is -+ * always a power of two. We keep its base 2 log handy (call it lb), -+ * so now we can write this with a bit shift and logical AND: -+ * -+ * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) -+ * -+ */ -+#define HASH_EXPAND_BUCKETS(tbl) \ -+do { \ -+ unsigned _he_bkt; \ -+ unsigned _he_bkt_i; \ -+ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ -+ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ -+ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ -+ 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ -+ if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ -+ memset(_he_new_buckets, 0, \ -+ 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ -+ tbl->ideal_chain_maxlen = \ -+ (tbl->num_items >> (tbl->log2_num_buckets+1U)) + \ -+ (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ -+ tbl->nonideal_items = 0; \ -+ for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ -+ { \ -+ _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ -+ while (_he_thh != NULL) { \ -+ _he_hh_nxt = _he_thh->hh_next; \ -+ HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt); \ -+ _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ -+ if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ -+ tbl->nonideal_items++; \ -+ _he_newbkt->expand_mult = _he_newbkt->count / \ -+ tbl->ideal_chain_maxlen; \ -+ } \ -+ _he_thh->hh_prev = NULL; \ -+ _he_thh->hh_next = _he_newbkt->hh_head; \ -+ if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev = \ -+ _he_thh; } \ -+ _he_newbkt->hh_head = _he_thh; \ -+ _he_thh = _he_hh_nxt; \ -+ } \ -+ } \ -+ uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ -+ tbl->num_buckets *= 2U; \ -+ tbl->log2_num_buckets++; \ -+ tbl->buckets = _he_new_buckets; \ -+ tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ -+ (tbl->ineff_expands+1U) : 0U; \ -+ if (tbl->ineff_expands > 1U) { \ -+ tbl->noexpand=1; \ -+ uthash_noexpand_fyi(tbl); \ -+ } \ -+ uthash_expand_fyi(tbl); \ -+} while (0) -+ -+ -+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ -+/* Note that HASH_SORT assumes the hash handle name to be hh. -+ * HASH_SRT was added to allow the hash handle name to be passed in. */ -+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) -+#define HASH_SRT(hh,head,cmpfcn) \ -+do { \ -+ unsigned _hs_i; \ -+ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ -+ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ -+ if (head != NULL) { \ -+ _hs_insize = 1; \ -+ _hs_looping = 1; \ -+ _hs_list = &((head)->hh); \ -+ while (_hs_looping != 0U) { \ -+ _hs_p = _hs_list; \ -+ _hs_list = NULL; \ -+ _hs_tail = NULL; \ -+ _hs_nmerges = 0; \ -+ while (_hs_p != NULL) { \ -+ _hs_nmerges++; \ -+ _hs_q = _hs_p; \ -+ _hs_psize = 0; \ -+ for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ -+ _hs_psize++; \ -+ _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ -+ ((void*)((char*)(_hs_q->next) + \ -+ (head)->hh.tbl->hho)) : NULL); \ -+ if (! (_hs_q) ) { break; } \ -+ } \ -+ _hs_qsize = _hs_insize; \ -+ while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\ -+ if (_hs_psize == 0U) { \ -+ _hs_e = _hs_q; \ -+ _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ -+ ((void*)((char*)(_hs_q->next) + \ -+ (head)->hh.tbl->hho)) : NULL); \ -+ _hs_qsize--; \ -+ } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) { \ -+ _hs_e = _hs_p; \ -+ if (_hs_p != NULL){ \ -+ _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ -+ ((void*)((char*)(_hs_p->next) + \ -+ (head)->hh.tbl->hho)) : NULL); \ -+ } \ -+ _hs_psize--; \ -+ } else if (( \ -+ cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ -+ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ -+ ) <= 0) { \ -+ _hs_e = _hs_p; \ -+ if (_hs_p != NULL){ \ -+ _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ -+ ((void*)((char*)(_hs_p->next) + \ -+ (head)->hh.tbl->hho)) : NULL); \ -+ } \ -+ _hs_psize--; \ -+ } else { \ -+ _hs_e = _hs_q; \ -+ _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ -+ ((void*)((char*)(_hs_q->next) + \ -+ (head)->hh.tbl->hho)) : NULL); \ -+ _hs_qsize--; \ -+ } \ -+ if ( _hs_tail != NULL ) { \ -+ _hs_tail->next = ((_hs_e != NULL) ? \ -+ ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ -+ } else { \ -+ _hs_list = _hs_e; \ -+ } \ -+ if (_hs_e != NULL) { \ -+ _hs_e->prev = ((_hs_tail != NULL) ? \ -+ ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ -+ } \ -+ _hs_tail = _hs_e; \ -+ } \ -+ _hs_p = _hs_q; \ -+ } \ -+ if (_hs_tail != NULL){ \ -+ _hs_tail->next = NULL; \ -+ } \ -+ if ( _hs_nmerges <= 1U ) { \ -+ _hs_looping=0; \ -+ (head)->hh.tbl->tail = _hs_tail; \ -+ DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ -+ } \ -+ _hs_insize *= 2U; \ -+ } \ -+ HASH_FSCK(hh,head); \ -+ } \ -+} while (0) -+ -+/* This function selects items from one hash into another hash. -+ * The end result is that the selected items have dual presence -+ * in both hashes. There is no copy of the items made; rather -+ * they are added into the new hash through a secondary hash -+ * hash handle that must be present in the structure. */ -+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ -+do { \ -+ unsigned _src_bkt, _dst_bkt; \ -+ void *_last_elt=NULL, *_elt; \ -+ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ -+ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ -+ if (src != NULL) { \ -+ for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ -+ for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ -+ _src_hh != NULL; \ -+ _src_hh = _src_hh->hh_next) { \ -+ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ -+ if (cond(_elt)) { \ -+ _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ -+ _dst_hh->key = _src_hh->key; \ -+ _dst_hh->keylen = _src_hh->keylen; \ -+ _dst_hh->hashv = _src_hh->hashv; \ -+ _dst_hh->prev = _last_elt; \ -+ _dst_hh->next = NULL; \ -+ if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; } \ -+ if (dst == NULL) { \ -+ DECLTYPE_ASSIGN(dst,_elt); \ -+ HASH_MAKE_TABLE(hh_dst,dst); \ -+ } else { \ -+ _dst_hh->tbl = (dst)->hh_dst.tbl; \ -+ } \ -+ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ -+ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ -+ (dst)->hh_dst.tbl->num_items++; \ -+ _last_elt = _elt; \ -+ _last_elt_hh = _dst_hh; \ -+ } \ -+ } \ -+ } \ -+ } \ -+ HASH_FSCK(hh_dst,dst); \ -+} while (0) -+ -+#define HASH_CLEAR(hh,head) \ -+do { \ -+ if (head != NULL) { \ -+ uthash_free((head)->hh.tbl->buckets, \ -+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ -+ HASH_BLOOM_FREE((head)->hh.tbl); \ -+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ -+ (head)=NULL; \ -+ } \ -+} while (0) -+ -+#define HASH_OVERHEAD(hh,head) \ -+ ((head != NULL) ? ( \ -+ (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ -+ ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ -+ sizeof(UT_hash_table) + \ -+ (HASH_BLOOM_BYTELEN))) : 0U) -+ -+#ifdef NO_DECLTYPE -+#define HASH_ITER(hh,head,el,tmp) \ -+for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ -+ (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) -+#else -+#define HASH_ITER(hh,head,el,tmp) \ -+for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ -+ (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) -+#endif -+ -+/* obtain a count of items in the hash */ -+#define HASH_COUNT(head) HASH_CNT(hh,head) -+#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) -+ -+typedef struct UT_hash_bucket { -+ struct UT_hash_handle *hh_head; -+ unsigned count; -+ -+ /* expand_mult is normally set to 0. In this situation, the max chain length -+ * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If -+ * the bucket's chain exceeds this length, bucket expansion is triggered). -+ * However, setting expand_mult to a non-zero value delays bucket expansion -+ * (that would be triggered by additions to this particular bucket) -+ * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. -+ * (The multiplier is simply expand_mult+1). The whole idea of this -+ * multiplier is to reduce bucket expansions, since they are expensive, in -+ * situations where we know that a particular bucket tends to be overused. -+ * It is better to let its chain length grow to a longer yet-still-bounded -+ * value, than to do an O(n) bucket expansion too often. -+ */ -+ unsigned expand_mult; -+ -+} UT_hash_bucket; -+ -+/* random signature used only to find hash tables in external analysis */ -+#define HASH_SIGNATURE 0xa0111fe1u -+#define HASH_BLOOM_SIGNATURE 0xb12220f2u -+ -+typedef struct UT_hash_table { -+ UT_hash_bucket *buckets; -+ unsigned num_buckets, log2_num_buckets; -+ unsigned num_items; -+ struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ -+ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ -+ -+ /* in an ideal situation (all buckets used equally), no bucket would have -+ * more than ceil(#items/#buckets) items. that's the ideal chain length. */ -+ unsigned ideal_chain_maxlen; -+ -+ /* nonideal_items is the number of items in the hash whose chain position -+ * exceeds the ideal chain maxlen. these items pay the penalty for an uneven -+ * hash distribution; reaching them in a chain traversal takes >ideal steps */ -+ unsigned nonideal_items; -+ -+ /* ineffective expands occur when a bucket doubling was performed, but -+ * afterward, more than half the items in the hash had nonideal chain -+ * positions. If this happens on two consecutive expansions we inhibit any -+ * further expansion, as it's not helping; this happens when the hash -+ * function isn't a good fit for the key domain. When expansion is inhibited -+ * the hash will still work, albeit no longer in constant time. */ -+ unsigned ineff_expands, noexpand; -+ -+ uint32_t signature; /* used only to find hash tables in external analysis */ -+#ifdef HASH_BLOOM -+ uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ -+ uint8_t *bloom_bv; -+ uint8_t bloom_nbits; -+#endif -+ -+} UT_hash_table; -+ -+typedef struct UT_hash_handle { -+ struct UT_hash_table *tbl; -+ void *prev; /* prev element in app order */ -+ void *next; /* next element in app order */ -+ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ -+ struct UT_hash_handle *hh_next; /* next hh in bucket order */ -+ void *key; /* ptr to enclosing struct's key */ -+ unsigned keylen; /* enclosing struct's key len */ -+ unsigned hashv; /* result of hash-fcn(key) */ -+} UT_hash_handle; -+ -+#endif /* UTHASH_H */ -diff --git a/server/utils.c b/server/utils.c -new file mode 100644 -index 0000000..14a60c7 ---- /dev/null -+++ b/server/utils.c -@@ -0,0 +1,448 @@ -+/* -+ * utils.c - Misc utilities -+ * -+ * Copyright (C) 2013 - 2016, 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 HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#ifndef __MINGW32__ -+#include -+#include -+#endif -+ -+#include -+#include -+ -+#include "utils.h" -+ -+#ifdef HAVE_SETRLIMIT -+#include -+#include -+#endif -+ -+#define INT_DIGITS 19 /* enough for 64 bit integer */ -+ -+#ifdef LIB_ONLY -+FILE *logfile; -+#endif -+ -+#ifdef HAS_SYSLOG -+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; -+ -+char * -+ss_itoa(int i) -+{ -+ /* Room for INT_DIGITS digits, - and '\0' */ -+ static char buf[INT_DIGITS + 2]; -+ char *p = buf + INT_DIGITS + 1; /* points to terminating '\0' */ -+ if (i >= 0) { -+ do { -+ *--p = '0' + (i % 10); -+ i /= 10; -+ } while (i != 0); -+ return p; -+ } else { /* i < 0 */ -+ do { -+ *--p = '0' - (i % 10); -+ i /= 10; -+ } while (i != 0); -+ *--p = '-'; -+ } -+ return p; -+} -+ -+int -+ss_isnumeric(const char *s) { -+ if (!s || !*s) -+ return 0; -+ while (isdigit(*s)) -+ ++s; -+ return *s == '\0'; -+} -+ -+/* -+ * setuid() and setgid() for a specified user. -+ */ -+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. */ -+ long uid = -1; -+ if (ss_isnumeric(user)) { -+ errno = 0; -+ char *endptr; -+ uid = strtol(user, &endptr, 10); -+ if (errno || endptr == user) -+ uid = -1; -+ } -+ -+#ifdef HAVE_GETPWNAM_R -+ struct passwd pwdbuf, *pwd; -+ memset(&pwdbuf, 0, sizeof(struct passwd)); -+ size_t buflen; -+ int err; -+ -+ for (buflen = 128;; buflen *= 2) { -+ char buf[buflen]; /* variable length array */ -+ -+ /* Note that we use getpwnam_r() instead of getpwnam(), -+ * which returns its result in a statically allocated buffer and -+ * cannot be considered thread safe. */ -+ err = uid >= 0 ? getpwuid_r((uid_t)uid, &pwdbuf, buf, buflen, &pwd) -+ : getpwnam_r(user, &pwdbuf, buf, buflen, &pwd); -+ -+ if (err == 0 && pwd) { -+ /* setgid first, because we may not be allowed to do it anymore after setuid */ -+ if (setgid(pwd->pw_gid) != 0) { -+ LOGE( -+ "Could not change group id to that of run_as user '%s': %s", -+ pwd->pw_name, strerror(errno)); -+ return 0; -+ } -+ -+ if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { -+ LOGE("Could not change supplementary groups for user '%s'.", pwd->pw_name); -+ return 0; -+ } -+ -+ if (setuid(pwd->pw_uid) != 0) { -+ LOGE( -+ "Could not change user id to that of run_as user '%s': %s", -+ pwd->pw_name, strerror(errno)); -+ return 0; -+ } -+ break; -+ } else if (err != ERANGE) { -+ if (err) { -+ LOGE("run_as user '%s' could not be found: %s", user, -+ strerror(err)); -+ } else { -+ LOGE("run_as user '%s' could not be found.", user); -+ } -+ return 0; -+ } else if (buflen >= 16 * 1024) { -+ /* If getpwnam_r() seems defective, call it quits rather than -+ * keep on allocating ever larger buffers until we crash. */ -+ LOGE( -+ "getpwnam_r() requires more than %u bytes of buffer space.", -+ (unsigned)buflen); -+ return 0; -+ } -+ /* Else try again with larger buffer. */ -+ } -+#else -+ /* No getpwnam_r() :-( We'll use getpwnam() and hope for the best. */ -+ struct passwd *pwd; -+ -+ if (!(pwd = uid >=0 ? getpwuid((uid_t)uid) : getpwnam(user))) { -+ LOGE("run_as user %s could not be found.", user); -+ return 0; -+ } -+ /* setgid first, because we may not allowed to do it anymore after setuid */ -+ if (setgid(pwd->pw_gid) != 0) { -+ LOGE("Could not change group id to that of run_as user '%s': %s", -+ pwd->pw_name, strerror(errno)); -+ return 0; -+ } -+ if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { -+ LOGE("Could not change supplementary groups for user '%s'.", pwd->pw_name); -+ return 0; -+ } -+ if (setuid(pwd->pw_uid) != 0) { -+ LOGE("Could not change user id to that of run_as user '%s': %s", -+ pwd->pw_name, strerror(errno)); -+ return 0; -+ } -+#endif -+ } -+ -+#endif // __MINGW32__ -+ return 1; -+} -+ -+char * -+ss_strndup(const char *s, size_t n) -+{ -+ size_t len = strlen(s); -+ char *ret; -+ -+ if (len <= n) { -+ return strdup(s); -+ } -+ -+ ret = ss_malloc(n + 1); -+ strncpy(ret, s, n); -+ ret[n] = '\0'; -+ return ret; -+} -+ -+void -+FATAL(const char *msg) -+{ -+ LOGE("%s", msg); -+ exit(-1); -+} -+ -+void * -+ss_malloc(size_t size) -+{ -+ void *tmp = malloc(size); -+ if (tmp == NULL) -+ exit(EXIT_FAILURE); -+ return tmp; -+} -+ -+void * -+ss_realloc(void *ptr, size_t new_size) -+{ -+ void *new = realloc(ptr, new_size); -+ if (new == NULL) { -+ free(ptr); -+ ptr = NULL; -+ exit(EXIT_FAILURE); -+ } -+ return new; -+} -+ -+void -+usage() -+{ -+ printf("\n"); -+ printf("shadowsocks-libev %s with %s\n\n", VERSION, USING_CRYPTO); -+ printf( -+ " maintained by Max Lv and Linus Yang \n\n"); -+ printf(" usage:\n\n"); -+#ifdef MODULE_LOCAL -+ printf(" ss-local\n"); -+#elif MODULE_REMOTE -+ printf(" ss-server\n"); -+#elif MODULE_TUNNEL -+ printf(" ss-tunnel\n"); -+#elif MODULE_REDIR -+ printf(" ss-redir\n"); -+#elif MODULE_MANAGER -+ printf(" ss-manager\n"); -+#endif -+ printf("\n"); -+ printf( -+ " -s Host name or IP address of your remote server.\n"); -+ printf( -+ " -p Port number of your remote server.\n"); -+ printf( -+ " -l Port number of your local server.\n"); -+ printf( -+ " -k Password of your remote server.\n"); -+ printf( -+ " -m Encrypt method: table, rc4, rc4-md5,\n"); -+ printf( -+ " aes-128-cfb, aes-192-cfb, aes-256-cfb,\n"); -+ printf( -+ " aes-128-ctr, aes-192-ctr, aes-256-ctr,\n"); -+ printf( -+ " bf-cfb, camellia-128-cfb, camellia-192-cfb,\n"); -+ printf( -+ " camellia-256-cfb, cast5-cfb, des-cfb,\n"); -+ printf( -+ " idea-cfb, rc2-cfb, seed-cfb, salsa20,\n"); -+ printf( -+ " chacha20 and chacha20-ietf.\n"); -+ printf( -+ " The default cipher is rc4-md5.\n"); -+ printf("\n"); -+ printf( -+ " [-a ] Run as another user.\n"); -+ printf( -+ " [-f ] The file path to store pid.\n"); -+ printf( -+ " [-t ] Socket timeout in seconds.\n"); -+ printf( -+ " [-c ] The path to config file.\n"); -+#ifdef HAVE_SETRLIMIT -+ printf( -+ " [-n ] Max number of open files.\n"); -+#endif -+#ifndef MODULE_REDIR -+ printf( -+ " [-i ] Network interface to bind.\n"); -+#endif -+ printf( -+ " [-b ] Local address to bind.\n"); -+ printf("\n"); -+ printf( -+ " [-u] Enable UDP relay.\n"); -+#ifdef MODULE_REDIR -+ printf( -+ " TPROXY is required in redir mode.\n"); -+#endif -+ printf( -+ " [-U] Enable UDP relay and disable TCP relay.\n"); -+ printf( -+ " [-A] Enable onetime authentication.\n"); -+#ifdef MODULE_REMOTE -+ printf( -+ " [-6] Resovle hostname to IPv6 address first.\n"); -+#endif -+ printf("\n"); -+#ifdef MODULE_TUNNEL -+ printf( -+ " [-L :] Destination server address and port\n"); -+ printf( -+ " for local port forwarding.\n"); -+#endif -+#ifdef MODULE_REMOTE -+ printf( -+ " [-d ] Name servers for internal DNS resolver.\n"); -+#endif -+#if defined(MODULE_REMOTE) || defined(MODULE_LOCAL) -+ printf( -+ " [--fast-open] Enable TCP fast open.\n"); -+ printf( -+ " with Linux kernel > 3.7.0.\n"); -+ printf( -+ " [--acl ] Path to ACL (Access Control List).\n"); -+#endif -+#if defined(MODULE_REMOTE) || defined(MODULE_MANAGER) -+ printf( -+ " [--manager-address ] UNIX domain socket address.\n"); -+#endif -+#ifdef MODULE_MANAGER -+ printf( -+ " [--executable ] Path to the executable of ss-server.\n"); -+#endif -+ printf( -+ " [--mtu ] MTU of your network interface.\n"); -+#ifdef __linux__ -+ printf( -+ " [--mptcp] Enable Multipath TCP on MPTCP Kernel.\n"); -+#ifdef MODULE_REMOTE -+ printf( -+ " [--firewall] Setup firewall rules for auto blocking.\n"); -+#endif -+#endif -+ printf("\n"); -+ printf( -+ " [-v] Verbose mode.\n"); -+ printf( -+ " [-h, --help] Print this message.\n"); -+ printf("\n"); -+} -+ -+void -+daemonize(const char *path) -+{ -+#ifndef __MINGW32__ -+ /* Our process ID and Session ID */ -+ pid_t pid, sid; -+ -+ /* Fork off the parent process */ -+ pid = fork(); -+ if (pid < 0) { -+ exit(EXIT_FAILURE); -+ } -+ -+ /* If we got a good PID, then -+ * we can exit the parent process. */ -+ if (pid > 0) { -+ FILE *file = fopen(path, "w"); -+ if (file == NULL) { -+ FATAL("Invalid pid file\n"); -+ } -+ -+ fprintf(file, "%d", (int)pid); -+ fclose(file); -+ exit(EXIT_SUCCESS); -+ } -+ -+ /* Change the file mode mask */ -+ umask(0); -+ -+ /* Open any logs here */ -+ -+ /* Create a new SID for the child process */ -+ sid = setsid(); -+ if (sid < 0) { -+ /* Log the failure */ -+ exit(EXIT_FAILURE); -+ } -+ -+ /* Change the current working directory */ -+ if ((chdir("/")) < 0) { -+ /* Log the failure */ -+ exit(EXIT_FAILURE); -+ } -+ -+ /* Close out the standard file descriptors */ -+ close(STDIN_FILENO); -+ close(STDOUT_FILENO); -+ close(STDERR_FILENO); -+#endif -+} -+ -+#ifdef HAVE_SETRLIMIT -+int -+set_nofile(int nofile) -+{ -+ struct rlimit limit = { nofile, nofile }; /* set both soft and hard limit */ -+ -+ if (nofile <= 0) { -+ FATAL("nofile must be greater than 0\n"); -+ } -+ -+ if (setrlimit(RLIMIT_NOFILE, &limit) < 0) { -+ if (errno == EPERM) { -+ LOGE( -+ "insufficient permission to change NOFILE, not starting as root?"); -+ return -1; -+ } else if (errno == EINVAL) { -+ LOGE("invalid nofile, decrease nofile and try again"); -+ return -1; -+ } else { -+ LOGE("setrlimit failed: %s", strerror(errno)); -+ return -1; -+ } -+ } -+ -+ return 0; -+} -+ -+#endif -diff --git a/server/utils.h b/server/utils.h -new file mode 100644 -index 0000000..0fb7f5a ---- /dev/null -+++ b/server/utils.h -@@ -0,0 +1,232 @@ -+/* -+ * utils.h - Misc utilities -+ * -+ * Copyright (C) 2013 - 2016, 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 -+ * . -+ */ -+ -+#if defined(USE_CRYPTO_OPENSSL) -+ -+#include -+#define USING_CRYPTO OPENSSL_VERSION_TEXT -+ -+#elif defined(USE_CRYPTO_POLARSSL) -+#include -+#define USING_CRYPTO POLARSSL_VERSION_STRING_FULL -+ -+#elif defined(USE_CRYPTO_MBEDTLS) -+#include -+#define USING_CRYPTO MBEDTLS_VERSION_STRING_FULL -+ -+#endif -+ -+#ifndef _UTILS_H -+#define _UTILS_H -+ -+#include -+#include -+#include -+#include -+ -+#define PORTSTRLEN 16 -+#define SS_ADDRSTRLEN (INET6_ADDRSTRLEN + PORTSTRLEN + 1) -+ -+#ifdef ANDROID -+ -+#include -+ -+#define USE_TTY() -+#define USE_SYSLOG(ident) -+#define LOGI(...) \ -+ ((void)__android_log_print(ANDROID_LOG_DEBUG, "shadowsocks", \ -+ __VA_ARGS__)) -+#define LOGE(...) \ -+ ((void)__android_log_print(ANDROID_LOG_ERROR, "shadowsocks", \ -+ __VA_ARGS__)) -+ -+#else -+ -+#define STR(x) # x -+#define TOSTR(x) STR(x) -+ -+#ifdef LIB_ONLY -+ -+extern FILE *logfile; -+ -+#define TIME_FORMAT "%Y-%m-%d %H:%M:%S" -+ -+#define USE_TTY() -+ -+#define USE_SYSLOG(ident) -+ -+#define USE_LOGFILE(ident) \ -+ do { \ -+ if (ident != NULL) { logfile = fopen(ident, "w+"); } } \ -+ while (0) -+ -+#define CLOSE_LOGFILE \ -+ do { \ -+ if (logfile != NULL) { fclose(logfile); } } \ -+ while (0) -+ -+#define LOGI(format, ...) \ -+ do { \ -+ if (logfile != NULL) { \ -+ time_t now = time(NULL); \ -+ char timestr[20]; \ -+ strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ -+ fprintf(logfile, " %s INFO: " format "\n", timestr, ## __VA_ARGS__); \ -+ fflush(logfile); } \ -+ } \ -+ while (0) -+ -+#define LOGE(format, ...) \ -+ do { \ -+ if (logfile != NULL) { \ -+ time_t now = time(NULL); \ -+ char timestr[20]; \ -+ strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ -+ fprintf(logfile, " %s ERROR: " format "\n", timestr, \ -+ ## __VA_ARGS__); \ -+ fflush(logfile); } \ -+ } \ -+ while (0) -+ -+#elif defined(_WIN32) -+ -+#define TIME_FORMAT "%Y-%m-%d %H:%M:%S" -+ -+#define USE_TTY() -+ -+#define USE_SYSLOG(ident) -+ -+#define LOGI(format, ...) \ -+ do { \ -+ time_t now = time(NULL); \ -+ char timestr[20]; \ -+ strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ -+ fprintf(stderr, " %s INFO: " format "\n", timestr, ## __VA_ARGS__); \ -+ fflush(stderr); } \ -+ while (0) -+ -+#define LOGE(format, ...) \ -+ do { \ -+ time_t now = time(NULL); \ -+ char timestr[20]; \ -+ strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ -+ fprintf(stderr, " %s ERROR: " format "\n", timestr, ## __VA_ARGS__); \ -+ fflush(stderr); } \ -+ while (0) -+ -+#else -+ -+#include -+ -+extern int use_tty; -+#define USE_TTY() \ -+ do { \ -+ use_tty = isatty(STDERR_FILENO); \ -+ } while (0) \ -+ -+#define HAS_SYSLOG -+extern int use_syslog; -+ -+#define TIME_FORMAT "%F %T" -+ -+#define USE_SYSLOG(ident) \ -+ do { \ -+ use_syslog = 1; \ -+ openlog((ident), LOG_CONS | LOG_PID, 0); } \ -+ while (0) -+ -+#define LOGI(format, ...) \ -+ do { \ -+ if (use_syslog) { \ -+ syslog(LOG_INFO, format, ## __VA_ARGS__); \ -+ } else { \ -+ time_t now = time(NULL); \ -+ char timestr[20]; \ -+ strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ -+ if (use_tty) { \ -+ fprintf(stderr, "\e[01;32m %s INFO: \e[0m" format "\n", timestr, \ -+ ## __VA_ARGS__); \ -+ } else { \ -+ fprintf(stderr, " %s INFO: " format "\n", timestr, \ -+ ## __VA_ARGS__); \ -+ } \ -+ } \ -+ } \ -+ while (0) -+ -+#define LOGE(format, ...) \ -+ do { \ -+ if (use_syslog) { \ -+ syslog(LOG_ERR, format, ## __VA_ARGS__); \ -+ } else { \ -+ time_t now = time(NULL); \ -+ char timestr[20]; \ -+ strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ -+ if (use_tty) { \ -+ fprintf(stderr, "\e[01;35m %s ERROR: \e[0m" format "\n", timestr, \ -+ ## __VA_ARGS__); \ -+ } else { \ -+ fprintf(stderr, " %s ERROR: " format "\n", timestr, \ -+ ## __VA_ARGS__); \ -+ } \ -+ } } \ -+ while (0) -+ -+#endif -+/* _WIN32 */ -+ -+#endif -+ -+#ifdef __MINGW32__ -+ -+#ifdef ERROR -+#undef ERROR -+#endif -+#define ERROR(s) ss_error(s) -+ -+#else -+ -+void ERROR(const char *s); -+ -+#endif -+ -+char *ss_itoa(int i); -+int ss_isnumeric(const char *s); -+int run_as(const char *user); -+void FATAL(const char *msg); -+void usage(void); -+void daemonize(const char *path); -+char *ss_strndup(const char *s, size_t n); -+#ifdef HAVE_SETRLIMIT -+int set_nofile(int nofile); -+#endif -+ -+void *ss_malloc(size_t size); -+void *ss_realloc(void *ptr, size_t new_size); -+ -+#define ss_free(ptr) \ -+ do { \ -+ free(ptr); \ -+ ptr = NULL; \ -+ } while (0) -+ -+#endif // _UTILS_H -diff --git a/server/verify.c b/server/verify.c -new file mode 100644 -index 0000000..9e7393d ---- /dev/null -+++ b/server/verify.c -@@ -0,0 +1,188 @@ -+ -+#include "verify.h" -+ -+static int verify_simple_pack_unit_size = 2000; -+ -+typedef struct verify_simple_local_data { -+ char * recv_buffer; -+ int recv_buffer_size; -+}verify_simple_local_data; -+ -+void verify_simple_local_data_init(verify_simple_local_data* local) { -+ local->recv_buffer = (char*)malloc(16384); -+ local->recv_buffer_size = 0; -+} -+ -+obfs * verify_simple_new_obfs() { -+ obfs * self = new_obfs(); -+ self->l_data = malloc(sizeof(verify_simple_local_data)); -+ verify_simple_local_data_init((verify_simple_local_data*)self->l_data); -+ return self; -+} -+ -+void verify_simple_dispose(obfs *self) { -+ verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; -+ if (local->recv_buffer != NULL) { -+ free(local->recv_buffer); -+ local->recv_buffer = NULL; -+ } -+ free(local); -+ self->l_data = NULL; -+ dispose_obfs(self); -+} -+ -+int verify_simple_pack_data(char *data, int datalength, char *outdata) { -+ unsigned char rand_len = (xorshift128plus() & 0xF) + 1; -+ int out_size = rand_len + datalength + 6; -+ outdata[0] = out_size >> 8; -+ outdata[1] = out_size; -+ outdata[2] = rand_len; -+ memmove(outdata + rand_len + 2, data, datalength); -+ fillcrc32((unsigned char *)outdata, out_size); -+ return out_size; -+} -+ -+int verify_simple_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t *capacity) { -+ char *plaindata = *pplaindata; -+ //verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; -+ char * out_buffer = (char*)malloc(datalength * 2 + 32); -+ char * buffer = out_buffer; -+ char * data = plaindata; -+ int len = datalength; -+ int pack_len; -+ while ( len > verify_simple_pack_unit_size ) { -+ pack_len = verify_simple_pack_data(data, verify_simple_pack_unit_size, buffer); -+ buffer += pack_len; -+ data += verify_simple_pack_unit_size; -+ len -= verify_simple_pack_unit_size; -+ } -+ if (len > 0) { -+ pack_len = verify_simple_pack_data(data, len, buffer); -+ buffer += pack_len; -+ } -+ len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ free(out_buffer); -+ return len; -+} -+ -+int verify_simple_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t *capacity) { -+ char *plaindata = *pplaindata; -+ verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; -+ uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; -+ if (local->recv_buffer_size + datalength > 16384) -+ return -1; -+ memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); -+ local->recv_buffer_size += datalength; -+ -+ char * out_buffer = (char*)malloc(local->recv_buffer_size); -+ char * buffer = out_buffer; -+ while (local->recv_buffer_size > 2) { -+ int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; -+ if (length >= 8192 || length < 7) { -+ free(out_buffer); -+ local->recv_buffer_size = 0; -+ return -1; -+ } -+ if (length > local->recv_buffer_size) -+ break; -+ -+ int crc = crc32((unsigned char*)recv_buffer, length); -+ if (crc != -1) { -+ free(out_buffer); -+ local->recv_buffer_size = 0; -+ return -1; -+ } -+ int data_size = length - recv_buffer[2] - 6; -+ memmove(buffer, recv_buffer + 2 + recv_buffer[2], data_size); -+ buffer += data_size; -+ memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); -+ } -+ int len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ free(out_buffer); -+ return len; -+} -+ -+int verify_simple_server_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t *capacity) { -+ char *plaindata = *pplaindata; -+ //verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; -+ char * out_buffer = (char*)malloc(datalength * 2 + 32); -+ char * buffer = out_buffer; -+ char * data = plaindata; -+ int len = datalength; -+ int pack_len; -+ while ( len > verify_simple_pack_unit_size ) { -+ pack_len = verify_simple_pack_data(data, verify_simple_pack_unit_size, buffer); -+ buffer += pack_len; -+ data += verify_simple_pack_unit_size; -+ len -= verify_simple_pack_unit_size; -+ } -+ if (len > 0) { -+ pack_len = verify_simple_pack_data(data, len, buffer); -+ buffer += pack_len; -+ } -+ len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ free(out_buffer); -+ return len; -+} -+ -+int verify_simple_server_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t *capacity) { -+ char *plaindata = *pplaindata; -+ verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; -+ uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; -+ if (local->recv_buffer_size + datalength > 16384) -+ { -+ LOGE("verify_simple: wrong buf length %d", local->recv_buffer_size + datalength); -+ return -1; -+ } -+ memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); -+ local->recv_buffer_size += datalength; -+ -+ char * out_buffer = (char*)malloc(local->recv_buffer_size); -+ char * buffer = out_buffer; -+ while (local->recv_buffer_size > 2) { -+ int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; -+ if (length >= 8192 || length < 7) { -+ free(out_buffer); -+ local->recv_buffer_size = 0; -+ LOGE("verify_simple: wrong length %d", length); -+ return -1; -+ } -+ if (length > local->recv_buffer_size) -+ break; -+ -+ int crc = crc32((unsigned char*)recv_buffer, length); -+ if (crc != -1) { -+ free(out_buffer); -+ local->recv_buffer_size = 0; -+ LOGE("verify_simple: wrong crc"); -+ return -1; -+ } -+ int data_size = length - recv_buffer[2] - 6; -+ memmove(buffer, recv_buffer + 2 + recv_buffer[2], data_size); -+ buffer += data_size; -+ memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); -+ } -+ int len = buffer - out_buffer; -+ if (*capacity < len) { -+ *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); -+ plaindata = *pplaindata; -+ } -+ memmove(plaindata, out_buffer, len); -+ free(out_buffer); -+ return len; -+} -diff --git a/server/verify.h b/server/verify.h -new file mode 100644 -index 0000000..57c6ff9 ---- /dev/null -+++ b/server/verify.h -@@ -0,0 +1,19 @@ -+/* -+ * verify.h - Define shadowsocksR server's buffers and callbacks -+ * -+ * Copyright (C) 2015 - 2016, Break Wa11 -+ */ -+ -+#ifndef _VERIFY_H -+#define _VERIFY_H -+ -+obfs * verify_simple_new_obfs(); -+void verify_simple_dispose(obfs *self); -+ -+int verify_simple_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+int verify_simple_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+ -+int verify_simple_server_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+int verify_simple_server_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); -+ -+#endif // _VERIFY_H --- -2.19.1 - diff --git a/package/lean/shadowsocksr-libev/patches/0002-Revert-verify_simple-and-auth_simple.patch b/package/lean/shadowsocksr-libev/patches/0002-Revert-verify_simple-and-auth_simple.patch deleted file mode 100644 index 72ee3337d..000000000 --- a/package/lean/shadowsocksr-libev/patches/0002-Revert-verify_simple-and-auth_simple.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/src/obfs/obfs.c b/src/obfs/obfs.c -index 463359f..4cd750a 100644 ---- a/src/obfs/obfs.c -+++ b/src/obfs/obfs.c -@@ -88,7 +88,7 @@ - plugin->client_decode = tls12_ticket_auth_client_decode; - - return plugin; -- /*} else if (strcmp(plugin_name, "verify_simple") == 0) { -+ } else if (strcmp(plugin_name, "verify_simple") == 0) { - obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs_class)); - plugin->init_data = init_data; - plugin->new_obfs = verify_simple_new_obfs; -@@ -115,7 +115,7 @@ - plugin->client_udp_pre_encrypt = NULL; - plugin->client_udp_post_decrypt = NULL; - -- return plugin;*/ -+ return plugin; - } else if (strcmp(plugin_name, "auth_sha1") == 0) { - obfs_class *plugin = (obfs_class *) malloc(sizeof(obfs_class)); - plugin->init_data = auth_simple_init_data; \ No newline at end of file diff --git a/package/lean/shadowsocksr-libev/patches/0003-Refine-Usage.patch b/package/lean/shadowsocksr-libev/patches/0003-Refine-Usage.patch deleted file mode 100644 index 85aa0f134..000000000 --- a/package/lean/shadowsocksr-libev/patches/0003-Refine-Usage.patch +++ /dev/null @@ -1,42 +0,0 @@ -diff --git a/src/utils.c b/src/utils.c -index 94f8a83..01bcbac 100644 ---- a/src/utils.c -+++ b/src/utils.c -@@ -258,8 +258,6 @@ usage() - { - printf("\n"); - printf("shadowsocks-libev %s with %s\n\n", VERSION, USING_CRYPTO); -- printf( -- " maintained by Max Lv and Linus Yang \n\n"); - printf(" usage:\n\n"); - #ifdef MODULE_LOCAL - printf(" ss-local\n"); -@@ -298,6 +296,25 @@ usage() - printf( - " The default cipher is rc4-md5.\n"); - printf("\n"); -+ printf( -+ " -o Obfs of your remote server: plain,\n"); -+ printf( -+ " http_simple, http_post and tls1.2_ticket_auth.\n"); -+ printf( -+ " -g Obfs-Param of your remote server.\n"); -+ printf( -+ " -O Protocol of your remote server: orgin,\n"); -+ printf( -+ " auth_sha1, auth_sha1_v2, auth_sha1_v4,\n"); -+ printf( -+ " auth_aes128_md5, auth_aes128_sha1,\n"); -+ printf( -+ " auth_chain_a, auth_chain_b, auth_chain_c,\n"); -+ printf( -+ " auth_chain_d, auth_chain_e and auth_chain_f.\n"); -+ printf( -+ " -G Protocol-Param of your remote server.\n"); -+ printf("\n"); - printf( - " [-a ] Run as another user.\n"); - printf( --- -2.19.1 - diff --git a/package/lean/shadowsocksr-libev/patches/990-not-defined-errors-on-gcc10.patch b/package/lean/shadowsocksr-libev/patches/990-not-defined-errors-on-gcc10.patch deleted file mode 100644 index 7a480f12b..000000000 --- a/package/lean/shadowsocksr-libev/patches/990-not-defined-errors-on-gcc10.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/src/http.h b/src/http.h -index 914815a..e312dd3 100644 ---- a/src/http.h -+++ b/src/http.h -@@ -29,6 +29,6 @@ - #include - #include "protocol.h" - --const protocol_t *const http_protocol; -+extern const protocol_t *const http_protocol; - - #endif -diff --git a/src/tls.h b/src/tls.h -index 3998913..ddbee11 100644 ---- a/src/tls.h -+++ b/src/tls.h -@@ -28,6 +28,6 @@ - - #include "protocol.h" - --const protocol_t *const tls_protocol; -+extern const protocol_t *const tls_protocol; - - #endif diff --git a/package/lean/shadowsocksr-libev/patches/999-Fix-Werror-sizeof-pointer-memaccess.patch b/package/lean/shadowsocksr-libev/patches/999-Fix-Werror-sizeof-pointer-memaccess.patch deleted file mode 100644 index 9da0c52b9..000000000 --- a/package/lean/shadowsocksr-libev/patches/999-Fix-Werror-sizeof-pointer-memaccess.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/src/local.c -+++ b/src/local.c -@@ -718,7 +718,7 @@ - - ss_free(hostname); - } else { -- strncpy(host, ip, sizeof(ip)); -+ strncpy(host, ip, INET6_ADDRSTRLEN); - } - } -