17187 lines
582 KiB
Diff
17187 lines
582 KiB
Diff
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"}
|
||
# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
|
||
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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#include <ipset/ipset.h>
|
||
+#include <ctype.h>
|
||
+
|
||
+#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 <unistd.h>
|
||
+#include <stdio.h>
|
||
+
|
||
+#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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#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 <mmgac001@gmail.com>
|
||
+ */
|
||
+
|
||
+#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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+/*
|
||
+ * Original Author: Oliver Lorenz (ol), olli@olorenz.org, https://olorenz.org
|
||
+ * License: This is licensed under the same terms as uthash itself
|
||
+ */
|
||
+
|
||
+#include <errno.h>
|
||
+#include <stdlib.h>
|
||
+
|
||
+#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 <key, value> pair into the cache
|
||
+ *
|
||
+ * @param cache
|
||
+ * The cache object
|
||
+ *
|
||
+ * @param key
|
||
+ * The key that identifies <value>
|
||
+ *
|
||
+ * @param key_len
|
||
+ * The length of key
|
||
+ *
|
||
+ * @param data
|
||
+ * Data associated with <key>
|
||
+ *
|
||
+ * @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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+/*
|
||
+ * 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; /**<The key */
|
||
+ void *data; /**<Payload */
|
||
+ ev_tstamp ts; /**<Timestamp */
|
||
+ UT_hash_handle hh; /**<Hash Handle for uthash */
|
||
+};
|
||
+
|
||
+/**
|
||
+ * A cache object
|
||
+ */
|
||
+struct cache {
|
||
+ size_t max_entries; /**<Amount of entries this cache object can hold */
|
||
+ struct cache_entry *entries; /**<Head pointer for uthash */
|
||
+ void (*free_cb) (void *key, void *element); /**<Callback function to free cache entries */
|
||
+};
|
||
+
|
||
+int cache_create(struct cache **dst, const size_t capacity,
|
||
+ void (*free_cb)(void *key, void *element));
|
||
+int cache_delete(struct cache *cache, int keep_data);
|
||
+int cache_clear(struct cache *cache, ev_tstamp age);
|
||
+int cache_lookup(struct cache *cache, char *key, size_t key_len, void *result);
|
||
+int cache_insert(struct cache *cache, char *key, size_t key_len, void *data);
|
||
+int cache_remove(struct cache *cache, char *key, size_t key_len);
|
||
+int cache_key_exist(struct cache *cache, char *key, size_t key_len);
|
||
+
|
||
+#endif
|
||
diff --git a/server/check.c b/server/check.c
|
||
new file mode 100644
|
||
index 0000000..ad035e0
|
||
--- /dev/null
|
||
+++ b/server/check.c
|
||
@@ -0,0 +1,236 @@
|
||
+#include <stdlib.h>
|
||
+#include <stdio.h>
|
||
+#include <unistd.h>
|
||
+#include <fcntl.h>
|
||
+#include <sys/types.h>
|
||
+#include <sys/socket.h>
|
||
+#include <netdb.h>
|
||
+#include <netinet/in.h>
|
||
+#include <errno.h>
|
||
+#include <time.h>
|
||
+#include<arpa/inet.h>
|
||
+#include <setjmp.h>
|
||
+#include <signal.h>
|
||
+#include <string.h>
|
||
+
|
||
+//#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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#include <stdint.h>
|
||
+
|
||
+#ifdef HAVE_CONFIG_H
|
||
+#include "config.h"
|
||
+#endif
|
||
+
|
||
+#if defined(USE_CRYPTO_OPENSSL)
|
||
+
|
||
+#include <openssl/md5.h>
|
||
+#include <openssl/rand.h>
|
||
+#include <openssl/hmac.h>
|
||
+#include <openssl/aes.h>
|
||
+
|
||
+#elif defined(USE_CRYPTO_POLARSSL)
|
||
+
|
||
+#include <polarssl/md5.h>
|
||
+#include <polarssl/sha1.h>
|
||
+#include <polarssl/aes.h>
|
||
+#include <polarssl/entropy.h>
|
||
+#include <polarssl/ctr_drbg.h>
|
||
+#include <polarssl/version.h>
|
||
+#define CIPHER_UNSUPPORTED "unsupported"
|
||
+
|
||
+#include <time.h>
|
||
+#ifdef _WIN32
|
||
+#include <windows.h>
|
||
+#include <wincrypt.h>
|
||
+#else
|
||
+#include <stdio.h>
|
||
+#endif
|
||
+
|
||
+#elif defined(USE_CRYPTO_MBEDTLS)
|
||
+
|
||
+#include <mbedtls/md5.h>
|
||
+#include <mbedtls/entropy.h>
|
||
+#include <mbedtls/ctr_drbg.h>
|
||
+#include <mbedtls/version.h>
|
||
+#include <mbedtls/aes.h>
|
||
+#define CIPHER_UNSUPPORTED "unsupported"
|
||
+
|
||
+#include <time.h>
|
||
+#ifdef _WIN32
|
||
+#include <windows.h>
|
||
+#include <wincrypt.h>
|
||
+#else
|
||
+#include <stdio.h>
|
||
+#endif
|
||
+
|
||
+#endif
|
||
+
|
||
+#include <sodium.h>
|
||
+
|
||
+#ifndef __MINGW32__
|
||
+#include <arpa/inet.h>
|
||
+#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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#ifndef _ENCRYPT_H
|
||
+#define _ENCRYPT_H
|
||
+
|
||
+#ifndef __MINGW32__
|
||
+#include <sys/socket.h>
|
||
+#else
|
||
+
|
||
+#ifdef max
|
||
+#undef max
|
||
+#endif
|
||
+
|
||
+#ifdef min
|
||
+#undef min
|
||
+#endif
|
||
+
|
||
+#endif
|
||
+
|
||
+#include <string.h>
|
||
+#include <stdlib.h>
|
||
+#include <stdio.h>
|
||
+#include <stdint.h>
|
||
+
|
||
+#if defined(USE_CRYPTO_OPENSSL)
|
||
+
|
||
+#include <openssl/evp.h>
|
||
+#include <openssl/sha.h>
|
||
+#include <openssl/md5.h>
|
||
+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 <polarssl/cipher.h>
|
||
+#include <polarssl/md.h>
|
||
+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 <mbedtls/cipher.h>
|
||
+#include <mbedtls/md.h>
|
||
+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 <CommonCrypto/CommonCrypto.h>
|
||
+
|
||
+#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 <stdint.h>
|
||
+#elif HAVE_INTTYPES_H
|
||
+#include <inttypes.h>
|
||
+#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 <dustin@null-ptr.net>
|
||
+ * 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 <stdio.h>
|
||
+#include <stdlib.h> /* malloc() */
|
||
+#include <string.h> /* strncpy() */
|
||
+#include <strings.h> /* strncasecmp() */
|
||
+#include <ctype.h> /* 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 <CR><LF> 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 <dustin@null-ptr.net>
|
||
+ * 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 <stdio.h>
|
||
+#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 <mmgac001@gmail.com>
|
||
+ */
|
||
+
|
||
+#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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#include <stdlib.h>
|
||
+#include <stdio.h>
|
||
+#include <unistd.h>
|
||
+#include <string.h>
|
||
+#include <time.h>
|
||
+
|
||
+#include "utils.h"
|
||
+#include "jconf.h"
|
||
+#include "json.h"
|
||
+#include "string.h"
|
||
+
|
||
+#include <libcork/core.h>
|
||
+
|
||
+#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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#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 <stdio.h>
|
||
+#include <string.h>
|
||
+#include <ctype.h>
|
||
+#include <math.h>
|
||
+
|
||
+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 <inttypes.h>
|
||
+#define json_int_t int64_t
|
||
+#else
|
||
+#define json_int_t __int64
|
||
+#endif
|
||
+#endif
|
||
+
|
||
+#include <stdlib.h>
|
||
+
|
||
+#ifdef __cplusplus
|
||
+
|
||
+#include <string.h>
|
||
+
|
||
+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 <stdio.h>
|
||
+#include <malloc.h>
|
||
+#include <string.h>
|
||
+
|
||
+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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#include <math.h>
|
||
+
|
||
+#include <libcork/core.h>
|
||
+#include <udns.h>
|
||
+
|
||
+#ifdef HAVE_CONFIG_H
|
||
+#include "config.h"
|
||
+#endif
|
||
+
|
||
+#ifdef __MINGW32__
|
||
+#include "win32.h"
|
||
+#define sleep(n) Sleep(1000 * (n))
|
||
+#else
|
||
+#include <sys/socket.h>
|
||
+#include <netdb.h>
|
||
+#include <netinet/in.h>
|
||
+#include <unistd.h>
|
||
+#endif
|
||
+
|
||
+#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__)
|
||
+#include <net/if.h>
|
||
+#include <sys/ioctl.h>
|
||
+#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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#ifndef _NETUTILS_H
|
||
+#define _NETUTILS_H
|
||
+
|
||
+#if defined(__linux__)
|
||
+#include <netdb.h>
|
||
+#elif !defined(__MINGW32__)
|
||
+#include <netinet/tcp.h>
|
||
+#endif
|
||
+
|
||
+// only enable TCP_FASTOPEN on linux
|
||
+#if defined(__linux__)
|
||
+#include <linux/tcp.h>
|
||
+/* 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 <string.h>
|
||
+#include <stdlib.h>
|
||
+
|
||
+#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 <mmgac001@gmail.com>
|
||
+ */
|
||
+
|
||
+#ifndef _OBFS_H
|
||
+#define _OBFS_H
|
||
+
|
||
+#include <stdint.h>
|
||
+#include <unistd.h>
|
||
+
|
||
+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 <dustin@null-ptr.net>
|
||
+ * 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 <dustin@null-ptr.net>
|
||
+ * 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 <stdio.h>
|
||
+#include <stdlib.h>
|
||
+#include <string.h>
|
||
+#include <fcntl.h>
|
||
+#include <ev.h>
|
||
+#include <udns.h>
|
||
+
|
||
+#ifdef __MINGW32__
|
||
+#include "win32.h"
|
||
+#else
|
||
+#include <sys/socket.h>
|
||
+#include <netinet/in.h>
|
||
+#include <errno.h>
|
||
+#include <unistd.h>
|
||
+#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 <dustin@null-ptr.net>
|
||
+ * 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 <stdint.h>
|
||
+
|
||
+#ifdef __MINGW32__
|
||
+#include "win32.h"
|
||
+#else
|
||
+#include <sys/socket.h>
|
||
+#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 <dustin@null-ptr.net>
|
||
+ * Copyright (c) 2011 Manuel Kasper <mk@neon1.net>
|
||
+ * 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 <stdio.h>
|
||
+#include <string.h>
|
||
+
|
||
+#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 <dustin@null-ptr.net>
|
||
+ * Copyright (c) 2011 Manuel Kasper <mk@neon1.net>
|
||
+ * 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 <libcork/ds.h>
|
||
+
|
||
+#ifdef HAVE_PCRE_H
|
||
+#include <pcre.h>
|
||
+#elif HAVE_PCRE_PCRE_H
|
||
+#include <pcre/pcre.h>
|
||
+#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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#ifdef HAVE_CONFIG_H
|
||
+#include "config.h"
|
||
+#endif
|
||
+
|
||
+#include <sys/stat.h>
|
||
+#include <sys/types.h>
|
||
+#include <fcntl.h>
|
||
+#include <locale.h>
|
||
+#include <signal.h>
|
||
+#include <string.h>
|
||
+#include <strings.h>
|
||
+#include <time.h>
|
||
+#include <unistd.h>
|
||
+#include <getopt.h>
|
||
+#include <math.h>
|
||
+
|
||
+#ifndef __MINGW32__
|
||
+#include <netdb.h>
|
||
+#include <errno.h>
|
||
+#include <arpa/inet.h>
|
||
+#include <netinet/in.h>
|
||
+#include <pthread.h>
|
||
+#include <sys/un.h>
|
||
+#endif
|
||
+
|
||
+#include <libcork/core.h>
|
||
+#include <udns.h>
|
||
+
|
||
+#ifdef __MINGW32__
|
||
+#include "win32.h"
|
||
+#endif
|
||
+
|
||
+#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__)
|
||
+#include <net/if.h>
|
||
+#include <sys/ioctl.h>
|
||
+#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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#ifndef _SERVER_H
|
||
+#define _SERVER_H
|
||
+
|
||
+#include <ev.h>
|
||
+#include <time.h>
|
||
+#include <libcork/ds.h>
|
||
+
|
||
+#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 <dustin@null-ptr.net>
|
||
+ * 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 <stdio.h>
|
||
+#include <stdlib.h> /* malloc() */
|
||
+#include <string.h> /* strncpy() */
|
||
+
|
||
+#ifndef __MINGW32__
|
||
+#include <sys/socket.h>
|
||
+#else
|
||
+#include <win32.h>
|
||
+#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 <dustin@null-ptr.net>
|
||
+ * 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 <mmgac001@gmail.com>
|
||
+ */
|
||
+
|
||
+#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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#include <sys/stat.h>
|
||
+#include <sys/types.h>
|
||
+#include <fcntl.h>
|
||
+#include <locale.h>
|
||
+#include <signal.h>
|
||
+#include <string.h>
|
||
+#include <strings.h>
|
||
+#include <time.h>
|
||
+#include <unistd.h>
|
||
+
|
||
+#ifndef __MINGW32__
|
||
+#include <arpa/inet.h>
|
||
+#include <errno.h>
|
||
+#include <netdb.h>
|
||
+#include <netinet/in.h>
|
||
+#include <pthread.h>
|
||
+#endif
|
||
+
|
||
+#ifdef HAVE_CONFIG_H
|
||
+#include "config.h"
|
||
+#endif
|
||
+
|
||
+#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__)
|
||
+#include <net/if.h>
|
||
+#include <sys/ioctl.h>
|
||
+#define SET_INTERFACE
|
||
+#endif
|
||
+
|
||
+#ifdef __MINGW32__
|
||
+#include "win32.h"
|
||
+#endif
|
||
+
|
||
+#include <libcork/core.h>
|
||
+#include <udns.h>
|
||
+
|
||
+#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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#ifndef _UDPRELAY_H
|
||
+#define _UDPRELAY_H
|
||
+
|
||
+#include <ev.h>
|
||
+#include <time.h>
|
||
+
|
||
+#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 <string.h> /* memcmp,strlen */
|
||
+#include <stddef.h> /* ptrdiff_t */
|
||
+#include <stdlib.h> /* 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 <stdint.h>
|
||
+#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__)
|
||
+#include <stdint.h>
|
||
+#else
|
||
+typedef unsigned int uint32_t;
|
||
+typedef unsigned char uint8_t;
|
||
+#endif
|
||
+#elif defined(__GNUC__) && !defined(__VXWORKS__)
|
||
+#include <stdint.h>
|
||
+#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 <unistd.h> 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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#ifdef HAVE_CONFIG_H
|
||
+#include "config.h"
|
||
+#endif
|
||
+
|
||
+#include <stdlib.h>
|
||
+#include <unistd.h>
|
||
+#include <string.h>
|
||
+#include <errno.h>
|
||
+#include <ctype.h>
|
||
+#ifndef __MINGW32__
|
||
+#include <pwd.h>
|
||
+#include <grp.h>
|
||
+#endif
|
||
+
|
||
+#include <sys/types.h>
|
||
+#include <sys/stat.h>
|
||
+
|
||
+#include "utils.h"
|
||
+
|
||
+#ifdef HAVE_SETRLIMIT
|
||
+#include <sys/time.h>
|
||
+#include <sys/resource.h>
|
||
+#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 <max.c.lv@gmail.com> and Linus Yang <laokongzi@gmail.com>\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 <server_host> Host name or IP address of your remote server.\n");
|
||
+ printf(
|
||
+ " -p <server_port> Port number of your remote server.\n");
|
||
+ printf(
|
||
+ " -l <local_port> Port number of your local server.\n");
|
||
+ printf(
|
||
+ " -k <password> Password of your remote server.\n");
|
||
+ printf(
|
||
+ " -m <encrypt_method> 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 <user>] Run as another user.\n");
|
||
+ printf(
|
||
+ " [-f <pid_file>] The file path to store pid.\n");
|
||
+ printf(
|
||
+ " [-t <timeout>] Socket timeout in seconds.\n");
|
||
+ printf(
|
||
+ " [-c <config_file>] The path to config file.\n");
|
||
+#ifdef HAVE_SETRLIMIT
|
||
+ printf(
|
||
+ " [-n <number>] Max number of open files.\n");
|
||
+#endif
|
||
+#ifndef MODULE_REDIR
|
||
+ printf(
|
||
+ " [-i <interface>] Network interface to bind.\n");
|
||
+#endif
|
||
+ printf(
|
||
+ " [-b <local_address>] 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 <addr>:<port>] Destination server address and port\n");
|
||
+ printf(
|
||
+ " for local port forwarding.\n");
|
||
+#endif
|
||
+#ifdef MODULE_REMOTE
|
||
+ printf(
|
||
+ " [-d <addr>] 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 <acl_file>] Path to ACL (Access Control List).\n");
|
||
+#endif
|
||
+#if defined(MODULE_REMOTE) || defined(MODULE_MANAGER)
|
||
+ printf(
|
||
+ " [--manager-address <addr>] UNIX domain socket address.\n");
|
||
+#endif
|
||
+#ifdef MODULE_MANAGER
|
||
+ printf(
|
||
+ " [--executable <path>] Path to the executable of ss-server.\n");
|
||
+#endif
|
||
+ printf(
|
||
+ " [--mtu <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 <max.c.lv@gmail.com>
|
||
+ *
|
||
+ * 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
|
||
+ * <http://www.gnu.org/licenses/>.
|
||
+ */
|
||
+
|
||
+#if defined(USE_CRYPTO_OPENSSL)
|
||
+
|
||
+#include <openssl/opensslv.h>
|
||
+#define USING_CRYPTO OPENSSL_VERSION_TEXT
|
||
+
|
||
+#elif defined(USE_CRYPTO_POLARSSL)
|
||
+#include <polarssl/version.h>
|
||
+#define USING_CRYPTO POLARSSL_VERSION_STRING_FULL
|
||
+
|
||
+#elif defined(USE_CRYPTO_MBEDTLS)
|
||
+#include <mbedtls/version.h>
|
||
+#define USING_CRYPTO MBEDTLS_VERSION_STRING_FULL
|
||
+
|
||
+#endif
|
||
+
|
||
+#ifndef _UTILS_H
|
||
+#define _UTILS_H
|
||
+
|
||
+#include <stddef.h>
|
||
+#include <stdlib.h>
|
||
+#include <stdio.h>
|
||
+#include <time.h>
|
||
+
|
||
+#define PORTSTRLEN 16
|
||
+#define SS_ADDRSTRLEN (INET6_ADDRSTRLEN + PORTSTRLEN + 1)
|
||
+
|
||
+#ifdef ANDROID
|
||
+
|
||
+#include <android/log.h>
|
||
+
|
||
+#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 <syslog.h>
|
||
+
|
||
+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 <mmgac001@gmail.com>
|
||
+ */
|
||
+
|
||
+#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
|
||
|