diff --git a/.gitignore b/.gitignore index 4eab18e..64b8d9f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ build/ .deps/ /Makefile src/Makefile +server/Makefile libev/Makefile libudns/Makefile libcork/Makefile diff --git a/Makefile.am b/Makefile.am index 690af43..75e158e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ if USE_SYSTEM_SHARED_LIB -SUBDIRS = libcork libipset src +SUBDIRS = libcork libipset src server else -SUBDIRS = libsodium libcork libipset libudns libev src +SUBDIRS = libsodium libcork libipset libudns libev src server endif if ENABLE_DOCUMENTATION diff --git a/Makefile.in b/Makefile.in index 4cb3deb..e210bc0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -195,7 +195,7 @@ am__define_uniq_tagged_files = \ ETAGS = etags CTAGS = ctags CSCOPE = cscope -DIST_SUBDIRS = libsodium libcork libipset libudns libev src doc +DIST_SUBDIRS = libsodium libcork libipset libudns libev src server doc am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ $(srcdir)/shadowsocks-libev.pc.in $(top_srcdir)/auto/ar-lib \ $(top_srcdir)/auto/compile $(top_srcdir)/auto/config.guess \ @@ -377,8 +377,9 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @USE_SYSTEM_SHARED_LIB_FALSE@SUBDIRS = libsodium libcork libipset \ -@USE_SYSTEM_SHARED_LIB_FALSE@ libudns libev src $(am__append_1) -@USE_SYSTEM_SHARED_LIB_TRUE@SUBDIRS = libcork libipset src \ +@USE_SYSTEM_SHARED_LIB_FALSE@ libudns libev src server \ +@USE_SYSTEM_SHARED_LIB_FALSE@ $(am__append_1) +@USE_SYSTEM_SHARED_LIB_TRUE@SUBDIRS = libcork libipset src server \ @USE_SYSTEM_SHARED_LIB_TRUE@ $(am__append_1) ACLOCAL_AMFLAGS = -I m4 pkgconfiglibdir = $(libdir)/pkgconfig diff --git a/configure b/configure index 7d854c4..01d66ab 100755 --- a/configure +++ b/configure @@ -649,7 +649,6 @@ PTHREAD_CC ax_pthread_config INET_NTOP_LIB MV -RM GZIP XMLTO ASCIIDOC @@ -757,6 +756,7 @@ infodir docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -857,6 +857,7 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1109,6 +1110,15 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1246,7 +1256,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1399,6 +1409,7 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -2472,8 +2483,8 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. -# expand $ac_aux_dir to an absolute path -am_aux_dir=`cd $ac_aux_dir && pwd` +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -3783,7 +3794,7 @@ $as_echo "$ac_cv_safe_to_define___extensions__" >&6; } -am__api_version='1.14' +am__api_version='1.15' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or @@ -3972,7 +3983,7 @@ else $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi -if test x"${install_sh}" != xset; then +if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; @@ -4363,8 +4374,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # mkdir_p='$(MKDIR_P)' -# We need awk for the "check" target. The system "awk" is bad on -# some platforms. +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' @@ -4549,6 +4560,7 @@ END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi + if test -n "$ac_tool_prefix"; then for ac_prog in ar lib "link -lib" do @@ -12494,47 +12506,6 @@ $as_echo "no" >&6; } fi - # Extract the first word of "rm", so it can be a program name with args. -set dummy rm; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_RM+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $RM in - [\\/]* | ?:[\\/]*) - ac_cv_path_RM="$RM" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_RM="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - test -z "$ac_cv_path_RM" && ac_cv_path_RM="rm" - ;; -esac -fi -RM=$ac_cv_path_RM -if test -n "$RM"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RM" >&5 -$as_echo "$RM" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - # Extract the first word of "mv", so it can be a program name with args. set dummy mv; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 @@ -16204,15 +16175,162 @@ $as_echo "#define HAVE_IPv6 1" >>confdefs.h if test -z "$USE_SYSTEM_SHARED_LIB_TRUE"; then : - else + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sodium_init in -lsodium" >&5 +$as_echo_n "checking for sodium_init in -lsodium... " >&6; } +if ${ac_cv_lib_sodium_sodium_init+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsodium $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char sodium_init (); +int +main () +{ +return sodium_init (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_sodium_sodium_init=yes +else + ac_cv_lib_sodium_sodium_init=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sodium_sodium_init" >&5 +$as_echo "$ac_cv_lib_sodium_sodium_init" >&6; } +if test "x$ac_cv_lib_sodium_sodium_init" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBSODIUM 1 +_ACEOF + + LIBS="-lsodium $LIBS" + +else + + as_fn_error $? "Couldn't find libsodium. Try installing libsodium-dev[el]." "$LINENO" 5 + +fi + + +else subdirs="$subdirs libsodium" fi -ac_config_files="$ac_config_files shadowsocks-libev.pc Makefile libcork/Makefile libipset/Makefile src/Makefile" +ac_config_files="$ac_config_files shadowsocks-libev.pc Makefile libcork/Makefile libipset/Makefile src/Makefile server/Makefile" if test -z "$USE_SYSTEM_SHARED_LIB_TRUE"; then : - else + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dns_dnlen in -ludns" >&5 +$as_echo_n "checking for dns_dnlen in -ludns... " >&6; } +if ${ac_cv_lib_udns_dns_dnlen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ludns $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dns_dnlen (); +int +main () +{ +return dns_dnlen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_udns_dns_dnlen=yes +else + ac_cv_lib_udns_dns_dnlen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_udns_dns_dnlen" >&5 +$as_echo "$ac_cv_lib_udns_dns_dnlen" >&6; } +if test "x$ac_cv_lib_udns_dns_dnlen" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBUDNS 1 +_ACEOF + + LIBS="-ludns $LIBS" + +else + as_fn_error $? "Couldn't find libudns. Try installing libudns-dev or udns-devel." "$LINENO" 5 +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ev_loop_destroy in -lev" >&5 +$as_echo_n "checking for ev_loop_destroy in -lev... " >&6; } +if ${ac_cv_lib_ev_ev_loop_destroy+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lev $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ev_loop_destroy (); +int +main () +{ +return ev_loop_destroy (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ev_ev_loop_destroy=yes +else + ac_cv_lib_ev_ev_loop_destroy=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ev_ev_loop_destroy" >&5 +$as_echo "$ac_cv_lib_ev_ev_loop_destroy" >&6; } +if test "x$ac_cv_lib_ev_ev_loop_destroy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBEV 1 +_ACEOF + + LIBS="-lev $LIBS" + +else + as_fn_error $? "Couldn't find libev. Try installing libev-dev[el]." "$LINENO" 5 +fi + + +else ac_config_files="$ac_config_files libudns/Makefile libev/Makefile" fi @@ -17258,6 +17376,7 @@ do "libcork/Makefile") CONFIG_FILES="$CONFIG_FILES libcork/Makefile" ;; "libipset/Makefile") CONFIG_FILES="$CONFIG_FILES libipset/Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "server/Makefile") CONFIG_FILES="$CONFIG_FILES server/Makefile" ;; "libudns/Makefile") CONFIG_FILES="$CONFIG_FILES libudns/Makefile" ;; "libev/Makefile") CONFIG_FILES="$CONFIG_FILES libev/Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; @@ -17958,8 +18077,8 @@ $as_echo X"$file" | fi cfgfile="${ofile}T" - trap "$RM -f \"$cfgfile\"; exit 1" 1 2 15 - $RM -f "$cfgfile" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL diff --git a/configure.ac b/configure.ac index 6586f2b..f9c51ab 100755 --- a/configure.ac +++ b/configure.ac @@ -315,7 +315,8 @@ AC_CONFIG_FILES([ shadowsocks-libev.pc Makefile libcork/Makefile libipset/Makefile - src/Makefile]) + src/Makefile + server/Makefile]) AM_COND_IF([USE_SYSTEM_SHARED_LIB],[ AC_CHECK_LIB([udns], [dns_dnlen], ,[AC_MSG_ERROR([Couldn't find libudns. Try installing libudns-dev or udns-devel.])]) AC_CHECK_LIB([ev], [ev_loop_destroy], ,[AC_MSG_ERROR([Couldn't find libev. Try installing libev-dev@<:@el@:>@.])]) diff --git a/server/Makefile.am b/server/Makefile.am new file mode 100644 index 0000000..3ae8bc2 --- /dev/null +++ b/server/Makefile.am @@ -0,0 +1,55 @@ +VERSION_INFO = 2:0:0 + +AM_CFLAGS = -g -O2 -Wall -Werror -Wno-deprecated-declarations -fno-strict-aliasing -std=gnu99 -D_GNU_SOURCE +AM_CFLAGS += $(PTHREAD_CFLAGS) +if !USE_SYSTEM_SHARED_LIB +AM_CFLAGS += -I$(top_srcdir)/libev +AM_CFLAGS += -I$(top_srcdir)/libudns +AM_CFLAGS += -I$(top_srcdir)/libsodium/src/libsodium/include +endif +AM_CFLAGS += -I$(top_srcdir)/libipset/include +AM_CFLAGS += -I$(top_srcdir)/libcork/include +AM_CFLAGS += $(LIBPCRE_CFLAGS) + +SS_COMMON_LIBS = $(top_builddir)/libipset/libipset.la \ + $(top_builddir)/libcork/libcork.la \ + $(INET_NTOP_LIB) $(LIBPCRE_LIBS) +if USE_SYSTEM_SHARED_LIB +SS_COMMON_LIBS += -lev -lsodium -lm +else +SS_COMMON_LIBS += $(top_builddir)/libev/libev.la \ + $(top_builddir)/libsodium/src/libsodium/libsodium.la +endif + +bin_PROGRAMS = ss-server ss-check + +sni_src = http.c \ + tls.c \ + rule.c + +ss_check_SOURCES = check.c + +ss_server_SOURCES = utils.c \ + netutils.c \ + jconf.c \ + json.c \ + encrypt.c \ + udprelay.c \ + cache.c \ + acl.c \ + resolv.c \ + server.c \ + $(sni_src) + + +ss_check_LDADD = $(SS_COMMON_LIBS) +ss_server_LDADD = $(SS_COMMON_LIBS) + +if USE_SYSTEM_SHARED_LIB +ss_server_LDADD += -ludns +else +ss_server_LDADD += $(top_builddir)/libudns/libudns.la +endif + +ss_check_CFLAGS = $(AM_CFLAGS) +ss_server_CFLAGS = $(AM_CFLAGS) -DMODULE_REMOTE diff --git a/server/Makefile.in b/server/Makefile.in new file mode 100644 index 0000000..3bfa53e --- /dev/null +++ b/server/Makefile.in @@ -0,0 +1,919 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@USE_SYSTEM_SHARED_LIB_FALSE@am__append_1 = -I$(top_srcdir)/libev \ +@USE_SYSTEM_SHARED_LIB_FALSE@ -I$(top_srcdir)/libudns \ +@USE_SYSTEM_SHARED_LIB_FALSE@ -I$(top_srcdir)/libsodium/src/libsodium/include +@USE_SYSTEM_SHARED_LIB_TRUE@am__append_2 = -lev -lsodium -lm +@USE_SYSTEM_SHARED_LIB_FALSE@am__append_3 = $(top_builddir)/libev/libev.la \ +@USE_SYSTEM_SHARED_LIB_FALSE@ $(top_builddir)/libsodium/src/libsodium/libsodium.la + +bin_PROGRAMS = ss-server$(EXEEXT) ss-check$(EXEEXT) +@USE_SYSTEM_SHARED_LIB_TRUE@am__append_4 = -ludns +@USE_SYSTEM_SHARED_LIB_FALSE@am__append_5 = $(top_builddir)/libudns/libudns.la +subdir = server +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_tls.m4 $(top_srcdir)/m4/inet_ntop.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/mbedtls.m4 \ + $(top_srcdir)/m4/openssl.m4 $(top_srcdir)/m4/pcre.m4 \ + $(top_srcdir)/m4/polarssl.m4 \ + $(top_srcdir)/m4/stack-protector.m4 $(top_srcdir)/m4/zlib.m4 \ + $(top_srcdir)/libev/libev.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am_ss_check_OBJECTS = ss_check-check.$(OBJEXT) +ss_check_OBJECTS = $(am_ss_check_OBJECTS) +am__DEPENDENCIES_1 = +am__DEPENDENCIES_2 = $(top_builddir)/libipset/libipset.la \ + $(top_builddir)/libcork/libcork.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__append_3) +ss_check_DEPENDENCIES = $(am__DEPENDENCIES_2) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +ss_check_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ss_check_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am__objects_1 = ss_server-http.$(OBJEXT) ss_server-tls.$(OBJEXT) \ + ss_server-rule.$(OBJEXT) +am_ss_server_OBJECTS = ss_server-utils.$(OBJEXT) \ + ss_server-netutils.$(OBJEXT) ss_server-jconf.$(OBJEXT) \ + ss_server-json.$(OBJEXT) ss_server-encrypt.$(OBJEXT) \ + ss_server-udprelay.$(OBJEXT) ss_server-cache.$(OBJEXT) \ + ss_server-acl.$(OBJEXT) ss_server-resolv.$(OBJEXT) \ + ss_server-server.$(OBJEXT) $(am__objects_1) +ss_server_OBJECTS = $(am_ss_server_OBJECTS) +ss_server_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ + $(am__append_5) +ss_server_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ss_server_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/auto/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(ss_check_SOURCES) $(ss_server_SOURCES) +DIST_SOURCES = $(ss_check_SOURCES) $(ss_server_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/auto/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +GZIP = @GZIP@ +INET_NTOP_LIB = @INET_NTOP_LIB@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBPCRE = @LIBPCRE@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MV = @MV@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PCRE_CONFIG = @PCRE_CONFIG@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +XMLTO = @XMLTO@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pcre_pcreh = @pcre_pcreh@ +pcreh = @pcreh@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +VERSION_INFO = 2:0:0 +AM_CFLAGS = -g -O2 -Wall -Werror -Wno-deprecated-declarations \ + -fno-strict-aliasing -std=gnu99 -D_GNU_SOURCE \ + $(PTHREAD_CFLAGS) $(am__append_1) \ + -I$(top_srcdir)/libipset/include \ + -I$(top_srcdir)/libcork/include $(LIBPCRE_CFLAGS) +SS_COMMON_LIBS = $(top_builddir)/libipset/libipset.la \ + $(top_builddir)/libcork/libcork.la $(INET_NTOP_LIB) \ + $(LIBPCRE_LIBS) $(am__append_2) $(am__append_3) +sni_src = http.c \ + tls.c \ + rule.c + +ss_check_SOURCES = check.c +ss_server_SOURCES = utils.c \ + netutils.c \ + jconf.c \ + json.c \ + encrypt.c \ + udprelay.c \ + cache.c \ + acl.c \ + resolv.c \ + server.c \ + $(sni_src) + +ss_check_LDADD = $(SS_COMMON_LIBS) +ss_server_LDADD = $(SS_COMMON_LIBS) $(am__append_4) $(am__append_5) +ss_check_CFLAGS = $(AM_CFLAGS) +ss_server_CFLAGS = $(AM_CFLAGS) -DMODULE_REMOTE +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign server/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign server/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +ss-check$(EXEEXT): $(ss_check_OBJECTS) $(ss_check_DEPENDENCIES) $(EXTRA_ss_check_DEPENDENCIES) + @rm -f ss-check$(EXEEXT) + $(AM_V_CCLD)$(ss_check_LINK) $(ss_check_OBJECTS) $(ss_check_LDADD) $(LIBS) + +ss-server$(EXEEXT): $(ss_server_OBJECTS) $(ss_server_DEPENDENCIES) $(EXTRA_ss_server_DEPENDENCIES) + @rm -f ss-server$(EXEEXT) + $(AM_V_CCLD)$(ss_server_LINK) $(ss_server_OBJECTS) $(ss_server_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_check-check.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-acl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-cache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-encrypt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-http.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-jconf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-json.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-netutils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-resolv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-rule.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-tls.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-udprelay.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-utils.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +ss_check-check.o: check.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_check_CFLAGS) $(CFLAGS) -MT ss_check-check.o -MD -MP -MF $(DEPDIR)/ss_check-check.Tpo -c -o ss_check-check.o `test -f 'check.c' || echo '$(srcdir)/'`check.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_check-check.Tpo $(DEPDIR)/ss_check-check.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='check.c' object='ss_check-check.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_check_CFLAGS) $(CFLAGS) -c -o ss_check-check.o `test -f 'check.c' || echo '$(srcdir)/'`check.c + +ss_check-check.obj: check.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_check_CFLAGS) $(CFLAGS) -MT ss_check-check.obj -MD -MP -MF $(DEPDIR)/ss_check-check.Tpo -c -o ss_check-check.obj `if test -f 'check.c'; then $(CYGPATH_W) 'check.c'; else $(CYGPATH_W) '$(srcdir)/check.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_check-check.Tpo $(DEPDIR)/ss_check-check.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='check.c' object='ss_check-check.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_check_CFLAGS) $(CFLAGS) -c -o ss_check-check.obj `if test -f 'check.c'; then $(CYGPATH_W) 'check.c'; else $(CYGPATH_W) '$(srcdir)/check.c'; fi` + +ss_server-utils.o: utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-utils.o -MD -MP -MF $(DEPDIR)/ss_server-utils.Tpo -c -o ss_server-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-utils.Tpo $(DEPDIR)/ss_server-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='ss_server-utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c + +ss_server-utils.obj: utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-utils.obj -MD -MP -MF $(DEPDIR)/ss_server-utils.Tpo -c -o ss_server-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-utils.Tpo $(DEPDIR)/ss_server-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='ss_server-utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` + +ss_server-netutils.o: netutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-netutils.o -MD -MP -MF $(DEPDIR)/ss_server-netutils.Tpo -c -o ss_server-netutils.o `test -f 'netutils.c' || echo '$(srcdir)/'`netutils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-netutils.Tpo $(DEPDIR)/ss_server-netutils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netutils.c' object='ss_server-netutils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-netutils.o `test -f 'netutils.c' || echo '$(srcdir)/'`netutils.c + +ss_server-netutils.obj: netutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-netutils.obj -MD -MP -MF $(DEPDIR)/ss_server-netutils.Tpo -c -o ss_server-netutils.obj `if test -f 'netutils.c'; then $(CYGPATH_W) 'netutils.c'; else $(CYGPATH_W) '$(srcdir)/netutils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-netutils.Tpo $(DEPDIR)/ss_server-netutils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netutils.c' object='ss_server-netutils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-netutils.obj `if test -f 'netutils.c'; then $(CYGPATH_W) 'netutils.c'; else $(CYGPATH_W) '$(srcdir)/netutils.c'; fi` + +ss_server-jconf.o: jconf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-jconf.o -MD -MP -MF $(DEPDIR)/ss_server-jconf.Tpo -c -o ss_server-jconf.o `test -f 'jconf.c' || echo '$(srcdir)/'`jconf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-jconf.Tpo $(DEPDIR)/ss_server-jconf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='jconf.c' object='ss_server-jconf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-jconf.o `test -f 'jconf.c' || echo '$(srcdir)/'`jconf.c + +ss_server-jconf.obj: jconf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-jconf.obj -MD -MP -MF $(DEPDIR)/ss_server-jconf.Tpo -c -o ss_server-jconf.obj `if test -f 'jconf.c'; then $(CYGPATH_W) 'jconf.c'; else $(CYGPATH_W) '$(srcdir)/jconf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-jconf.Tpo $(DEPDIR)/ss_server-jconf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='jconf.c' object='ss_server-jconf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-jconf.obj `if test -f 'jconf.c'; then $(CYGPATH_W) 'jconf.c'; else $(CYGPATH_W) '$(srcdir)/jconf.c'; fi` + +ss_server-json.o: json.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-json.o -MD -MP -MF $(DEPDIR)/ss_server-json.Tpo -c -o ss_server-json.o `test -f 'json.c' || echo '$(srcdir)/'`json.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-json.Tpo $(DEPDIR)/ss_server-json.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='json.c' object='ss_server-json.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-json.o `test -f 'json.c' || echo '$(srcdir)/'`json.c + +ss_server-json.obj: json.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-json.obj -MD -MP -MF $(DEPDIR)/ss_server-json.Tpo -c -o ss_server-json.obj `if test -f 'json.c'; then $(CYGPATH_W) 'json.c'; else $(CYGPATH_W) '$(srcdir)/json.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-json.Tpo $(DEPDIR)/ss_server-json.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='json.c' object='ss_server-json.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-json.obj `if test -f 'json.c'; then $(CYGPATH_W) 'json.c'; else $(CYGPATH_W) '$(srcdir)/json.c'; fi` + +ss_server-encrypt.o: encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-encrypt.o -MD -MP -MF $(DEPDIR)/ss_server-encrypt.Tpo -c -o ss_server-encrypt.o `test -f 'encrypt.c' || echo '$(srcdir)/'`encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-encrypt.Tpo $(DEPDIR)/ss_server-encrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='encrypt.c' object='ss_server-encrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-encrypt.o `test -f 'encrypt.c' || echo '$(srcdir)/'`encrypt.c + +ss_server-encrypt.obj: encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-encrypt.obj -MD -MP -MF $(DEPDIR)/ss_server-encrypt.Tpo -c -o ss_server-encrypt.obj `if test -f 'encrypt.c'; then $(CYGPATH_W) 'encrypt.c'; else $(CYGPATH_W) '$(srcdir)/encrypt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-encrypt.Tpo $(DEPDIR)/ss_server-encrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='encrypt.c' object='ss_server-encrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-encrypt.obj `if test -f 'encrypt.c'; then $(CYGPATH_W) 'encrypt.c'; else $(CYGPATH_W) '$(srcdir)/encrypt.c'; fi` + +ss_server-udprelay.o: udprelay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-udprelay.o -MD -MP -MF $(DEPDIR)/ss_server-udprelay.Tpo -c -o ss_server-udprelay.o `test -f 'udprelay.c' || echo '$(srcdir)/'`udprelay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-udprelay.Tpo $(DEPDIR)/ss_server-udprelay.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='udprelay.c' object='ss_server-udprelay.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-udprelay.o `test -f 'udprelay.c' || echo '$(srcdir)/'`udprelay.c + +ss_server-udprelay.obj: udprelay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-udprelay.obj -MD -MP -MF $(DEPDIR)/ss_server-udprelay.Tpo -c -o ss_server-udprelay.obj `if test -f 'udprelay.c'; then $(CYGPATH_W) 'udprelay.c'; else $(CYGPATH_W) '$(srcdir)/udprelay.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-udprelay.Tpo $(DEPDIR)/ss_server-udprelay.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='udprelay.c' object='ss_server-udprelay.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-udprelay.obj `if test -f 'udprelay.c'; then $(CYGPATH_W) 'udprelay.c'; else $(CYGPATH_W) '$(srcdir)/udprelay.c'; fi` + +ss_server-cache.o: cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-cache.o -MD -MP -MF $(DEPDIR)/ss_server-cache.Tpo -c -o ss_server-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-cache.Tpo $(DEPDIR)/ss_server-cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cache.c' object='ss_server-cache.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c + +ss_server-cache.obj: cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-cache.obj -MD -MP -MF $(DEPDIR)/ss_server-cache.Tpo -c -o ss_server-cache.obj `if test -f 'cache.c'; then $(CYGPATH_W) 'cache.c'; else $(CYGPATH_W) '$(srcdir)/cache.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-cache.Tpo $(DEPDIR)/ss_server-cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cache.c' object='ss_server-cache.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-cache.obj `if test -f 'cache.c'; then $(CYGPATH_W) 'cache.c'; else $(CYGPATH_W) '$(srcdir)/cache.c'; fi` + +ss_server-acl.o: acl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-acl.o -MD -MP -MF $(DEPDIR)/ss_server-acl.Tpo -c -o ss_server-acl.o `test -f 'acl.c' || echo '$(srcdir)/'`acl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-acl.Tpo $(DEPDIR)/ss_server-acl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='acl.c' object='ss_server-acl.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-acl.o `test -f 'acl.c' || echo '$(srcdir)/'`acl.c + +ss_server-acl.obj: acl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-acl.obj -MD -MP -MF $(DEPDIR)/ss_server-acl.Tpo -c -o ss_server-acl.obj `if test -f 'acl.c'; then $(CYGPATH_W) 'acl.c'; else $(CYGPATH_W) '$(srcdir)/acl.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-acl.Tpo $(DEPDIR)/ss_server-acl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='acl.c' object='ss_server-acl.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-acl.obj `if test -f 'acl.c'; then $(CYGPATH_W) 'acl.c'; else $(CYGPATH_W) '$(srcdir)/acl.c'; fi` + +ss_server-resolv.o: resolv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-resolv.o -MD -MP -MF $(DEPDIR)/ss_server-resolv.Tpo -c -o ss_server-resolv.o `test -f 'resolv.c' || echo '$(srcdir)/'`resolv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-resolv.Tpo $(DEPDIR)/ss_server-resolv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolv.c' object='ss_server-resolv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-resolv.o `test -f 'resolv.c' || echo '$(srcdir)/'`resolv.c + +ss_server-resolv.obj: resolv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-resolv.obj -MD -MP -MF $(DEPDIR)/ss_server-resolv.Tpo -c -o ss_server-resolv.obj `if test -f 'resolv.c'; then $(CYGPATH_W) 'resolv.c'; else $(CYGPATH_W) '$(srcdir)/resolv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-resolv.Tpo $(DEPDIR)/ss_server-resolv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolv.c' object='ss_server-resolv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-resolv.obj `if test -f 'resolv.c'; then $(CYGPATH_W) 'resolv.c'; else $(CYGPATH_W) '$(srcdir)/resolv.c'; fi` + +ss_server-server.o: server.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-server.o -MD -MP -MF $(DEPDIR)/ss_server-server.Tpo -c -o ss_server-server.o `test -f 'server.c' || echo '$(srcdir)/'`server.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-server.Tpo $(DEPDIR)/ss_server-server.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server.c' object='ss_server-server.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-server.o `test -f 'server.c' || echo '$(srcdir)/'`server.c + +ss_server-server.obj: server.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-server.obj -MD -MP -MF $(DEPDIR)/ss_server-server.Tpo -c -o ss_server-server.obj `if test -f 'server.c'; then $(CYGPATH_W) 'server.c'; else $(CYGPATH_W) '$(srcdir)/server.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-server.Tpo $(DEPDIR)/ss_server-server.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server.c' object='ss_server-server.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-server.obj `if test -f 'server.c'; then $(CYGPATH_W) 'server.c'; else $(CYGPATH_W) '$(srcdir)/server.c'; fi` + +ss_server-http.o: http.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-http.o -MD -MP -MF $(DEPDIR)/ss_server-http.Tpo -c -o ss_server-http.o `test -f 'http.c' || echo '$(srcdir)/'`http.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-http.Tpo $(DEPDIR)/ss_server-http.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='ss_server-http.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-http.o `test -f 'http.c' || echo '$(srcdir)/'`http.c + +ss_server-http.obj: http.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-http.obj -MD -MP -MF $(DEPDIR)/ss_server-http.Tpo -c -o ss_server-http.obj `if test -f 'http.c'; then $(CYGPATH_W) 'http.c'; else $(CYGPATH_W) '$(srcdir)/http.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-http.Tpo $(DEPDIR)/ss_server-http.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='ss_server-http.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-http.obj `if test -f 'http.c'; then $(CYGPATH_W) 'http.c'; else $(CYGPATH_W) '$(srcdir)/http.c'; fi` + +ss_server-tls.o: tls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-tls.o -MD -MP -MF $(DEPDIR)/ss_server-tls.Tpo -c -o ss_server-tls.o `test -f 'tls.c' || echo '$(srcdir)/'`tls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-tls.Tpo $(DEPDIR)/ss_server-tls.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tls.c' object='ss_server-tls.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-tls.o `test -f 'tls.c' || echo '$(srcdir)/'`tls.c + +ss_server-tls.obj: tls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-tls.obj -MD -MP -MF $(DEPDIR)/ss_server-tls.Tpo -c -o ss_server-tls.obj `if test -f 'tls.c'; then $(CYGPATH_W) 'tls.c'; else $(CYGPATH_W) '$(srcdir)/tls.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-tls.Tpo $(DEPDIR)/ss_server-tls.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tls.c' object='ss_server-tls.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-tls.obj `if test -f 'tls.c'; then $(CYGPATH_W) 'tls.c'; else $(CYGPATH_W) '$(srcdir)/tls.c'; fi` + +ss_server-rule.o: rule.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-rule.o -MD -MP -MF $(DEPDIR)/ss_server-rule.Tpo -c -o ss_server-rule.o `test -f 'rule.c' || echo '$(srcdir)/'`rule.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-rule.Tpo $(DEPDIR)/ss_server-rule.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rule.c' object='ss_server-rule.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-rule.o `test -f 'rule.c' || echo '$(srcdir)/'`rule.c + +ss_server-rule.obj: rule.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-rule.obj -MD -MP -MF $(DEPDIR)/ss_server-rule.Tpo -c -o ss_server-rule.obj `if test -f 'rule.c'; then $(CYGPATH_W) 'rule.c'; else $(CYGPATH_W) '$(srcdir)/rule.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-rule.Tpo $(DEPDIR)/ss_server-rule.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rule.c' object='ss_server-rule.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-rule.obj `if test -f 'rule.c'; then $(CYGPATH_W) 'rule.c'; else $(CYGPATH_W) '$(srcdir)/rule.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-binPROGRAMS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..ef6a20e --- /dev/null +++ b/server/README.md @@ -0,0 +1,3 @@ +# server + +`ss-server` and `ss-check` from https://github.com/ywb94/shadowsocks-libev diff --git a/server/acl.c b/server/acl.c new file mode 100644 index 0000000..60d4b72 --- /dev/null +++ b/server/acl.c @@ -0,0 +1,597 @@ +/* + * acl.c - Manage the ACL (Access Control List) + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#include +#include + +#include "rule.h" +#include "utils.h" +#include "cache.h" +#include "acl.h" + +static struct ip_set white_list_ipv4; +static struct ip_set white_list_ipv6; + +static struct ip_set black_list_ipv4; +static struct ip_set black_list_ipv6; + +static struct cork_dllist black_list_rules; +static struct cork_dllist white_list_rules; + +static int acl_mode = BLACK_LIST; + +static struct cache *block_list; + +static struct ip_set outbound_block_list_ipv4; +static struct ip_set outbound_block_list_ipv6; +static struct cork_dllist outbound_block_list_rules; + +#ifdef __linux__ + +#include +#include + +#define NO_FIREWALL_MODE 0 +#define IPTABLES_MODE 1 +#define FIREWALLD_MODE 2 + +static FILE *shell_stdin; +static int mode = NO_FIREWALL_MODE; + +static char chain_name[64]; +static char *iptables_init_chain = + "iptables -N %s; iptables -F %s; iptables -A OUTPUT -p tcp --tcp-flags RST RST -j %s"; +static char *iptables_remove_chain = + "iptables -D OUTPUT -p tcp --tcp-flags RST RST -j %s; iptables -F %s; iptables -X %s"; +static char *iptables_add_rule = "iptables -A %s -d %s -j DROP"; +static char *iptables_remove_rule = "iptables -D %s -d %s -j DROP"; + +static char *ip6tables_init_chain = + "ip6tables -N %s; ip6tables -F %s; ip6tables -A OUTPUT -p tcp --tcp-flags RST RST -j %s"; +static char *ip6tables_remove_chain = + "ip6tables -D OUTPUT -p tcp --tcp-flags RST RST -j %s; ip6tables -F %s; ip6tables -X %s"; +static char *ip6tables_add_rule = "ip6tables -A %s -d %s -j DROP"; +static char *ip6tables_remove_rule = "ip6tables -D %s -d %s -j DROP"; + +static char *firewalld_init_chain = + "firewall-cmd --direct --add-chain ipv4 filter %s; \ + firewall-cmd --direct --passthrough ipv4 -F %s; \ + firewall-cmd --direct --passthrough ipv4 -A OUTPUT -p tcp --tcp-flags RST RST -j %s"; +static char *firewalld_remove_chain = + "firewall-cmd --direct --passthrough ipv4 -D OUTPUT -p tcp --tcp-flags RST RST -j %s; \ + firewall-cmd --direct --passthrough ipv4 -F %s; \ + firewall-cmd --direct --remove-chain ipv4 filter %s"; +static char *firewalld_add_rule = "firewall-cmd --direct --passthrough ipv4 -A %s -d %s -j DROP"; +static char *firewalld_remove_rule = "firewall-cmd --direct --passthrough ipv4 -D %s -d %s -j DROP"; + +static char *firewalld6_init_chain = + "firewall-cmd --direct --add-chain ipv6 filter %s; \ + firewall-cmd --direct --passthrough ipv6 -F %s; \ + firewall-cmd --direct --passthrough ipv6 -A OUTPUT -p tcp --tcp-flags RST RST -j %s"; +static char *firewalld6_remove_chain = + "firewall-cmd --direct --passthrough ipv6 -D OUTPUT -p tcp --tcp-flags RST RST -j %s; \ + firewall-cmd --direct --passthrough ipv6 -F %s; \ + firewall-cmd --direct --remove-chain ipv6 filter %s"; +static char *firewalld6_add_rule = "firewall-cmd --direct --passthrough ipv6 -A %s -d %s -j DROP"; +static char *firewalld6_remove_rule = "firewall-cmd --direct --passthrough ipv6 -D %s -d %s -j DROP"; + +static int +run_cmd(const char *cmd) +{ + int ret = 0; + char cmdstring[256]; + + sprintf(cmdstring, "%s\n", cmd); + size_t len = strlen(cmdstring); + + if (shell_stdin != NULL) { + ret = fwrite(cmdstring, 1, len, shell_stdin); + fflush(shell_stdin); + } + + return ret == len; +} + +static int +init_firewall() +{ + int ret = 0; + char cli[256]; + FILE *fp; + + if (getuid() != 0) + return -1; + + sprintf(cli, "firewall-cmd --version 2>&1"); + fp = popen(cli, "r"); + + if (fp == NULL) + return -1; + + if (pclose(fp) == 0) { + mode = FIREWALLD_MODE; + } else { + /* Check whether we have permission to operate iptables. + * Note that checking `iptables --version` is insufficient: + * eg, running within a child user namespace. + */ + sprintf(cli, "iptables -L 2>&1"); + fp = popen(cli, "r"); + if (fp == NULL) + return -1; + if (pclose(fp) == 0) + mode = IPTABLES_MODE; + } + + sprintf(chain_name, "SHADOWSOCKS_LIBEV_%d", getpid()); + + if (mode == FIREWALLD_MODE) { + sprintf(cli, firewalld6_init_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + sprintf(cli, firewalld_init_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + } else if (mode == IPTABLES_MODE) { + sprintf(cli, ip6tables_init_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + sprintf(cli, iptables_init_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + } + + shell_stdin = popen("/bin/sh", "w"); + + return ret; +} + +static int +reset_firewall() +{ + int ret = 0; + char cli[256]; + + if (getuid() != 0) + return -1; + + if (mode == IPTABLES_MODE) { + sprintf(cli, ip6tables_remove_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + sprintf(cli, iptables_remove_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + } else if (mode == FIREWALLD_MODE) { + sprintf(cli, firewalld6_remove_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + sprintf(cli, firewalld_remove_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + } + + if (shell_stdin != NULL) { + run_cmd("exit 0"); + pclose(shell_stdin); + } + + return ret; +} + +static int +set_firewall_rule(char *addr, int add) +{ + char cli[256]; + struct cork_ip ip; + + if (getuid() != 0) + return -1; + + if (cork_ip_init(&ip, addr)) + return -1; + + if (add) { + if (mode == IPTABLES_MODE) + sprintf(cli, ip.version == 4 ? iptables_add_rule : ip6tables_add_rule, + chain_name, addr); + else if (mode == FIREWALLD_MODE) + sprintf(cli, ip.version == 4 ? firewalld_add_rule : firewalld6_add_rule, + chain_name, addr); + return run_cmd(cli); + } else { + if (mode == IPTABLES_MODE) + sprintf(cli, ip.version == 4 ? iptables_remove_rule : ip6tables_remove_rule, + chain_name, addr); + else if (mode == FIREWALLD_MODE) + sprintf(cli, ip.version == 4 ? firewalld_remove_rule : firewalld6_remove_rule, + chain_name, addr); + return run_cmd(cli); + } + + return 0; +} + +static void +free_firewall_rule(void *key, void *element) +{ + if (key == NULL) + return; + char *addr = (char *)key; + set_firewall_rule(addr, 0); + ss_free(element); +} + +#endif + +void +init_block_list(int firewall) +{ + // Initialize cache +#ifdef __linux__ + if (firewall) + init_firewall(); + else + mode = NO_FIREWALL_MODE; + cache_create(&block_list, 256, free_firewall_rule); +#else + cache_create(&block_list, 256, NULL); +#endif +} + +void +free_block_list() +{ +#ifdef __linux__ + if (mode != NO_FIREWALL_MODE) + reset_firewall(); +#endif + cache_clear(block_list, 0); // Remove all items +} + +int +remove_from_block_list(char *addr) +{ + size_t addr_len = strlen(addr); + return cache_remove(block_list, addr, addr_len); +} + +void +clear_block_list() +{ + cache_clear(block_list, 3600); // Clear items older than 1 hour +} + +int +check_block_list(char *addr) +{ + size_t addr_len = strlen(addr); + + if (cache_key_exist(block_list, addr, addr_len)) { + int *count = NULL; + cache_lookup(block_list, addr, addr_len, &count); + + if (count != NULL && *count > MAX_TRIES) + return 1; + } + + return 0; +} + +int +update_block_list(char *addr, int err_level) +{ + size_t addr_len = strlen(addr); + + if (cache_key_exist(block_list, addr, addr_len)) { + int *count = NULL; + cache_lookup(block_list, addr, addr_len, &count); + if (count != NULL) { + if (*count > MAX_TRIES) + return 1; + (*count) += err_level; + } + } else if (err_level > 0) { + int *count = (int *)ss_malloc(sizeof(int)); + *count = 1; + cache_insert(block_list, addr, addr_len, count); +#ifdef __linux__ + if (mode != NO_FIREWALL_MODE) + set_firewall_rule(addr, 1); +#endif + } + + return 0; +} + +static void +parse_addr_cidr(const char *str, char *host, int *cidr) +{ + int ret = -1, n = 0; + char *pch; + + pch = strchr(str, '/'); + while (pch != NULL) { + n++; + ret = pch - str; + pch = strchr(pch + 1, '/'); + } + if (ret == -1) { + strcpy(host, str); + *cidr = -1; + } else { + memcpy(host, str, ret); + host[ret] = '\0'; + *cidr = atoi(str + ret + 1); + } +} + +char * +trimwhitespace(char *str) +{ + char *end; + + // Trim leading space + while (isspace(*str)) + str++; + + if (*str == 0) // All spaces? + return str; + + // Trim trailing space + end = str + strlen(str) - 1; + while (end > str && isspace(*end)) + end--; + + // Write new null terminator + *(end + 1) = 0; + + return str; +} + +int +init_acl(const char *path) +{ + // initialize ipset + ipset_init_library(); + + ipset_init(&white_list_ipv4); + ipset_init(&white_list_ipv6); + ipset_init(&black_list_ipv4); + ipset_init(&black_list_ipv6); + ipset_init(&outbound_block_list_ipv4); + ipset_init(&outbound_block_list_ipv6); + + cork_dllist_init(&black_list_rules); + cork_dllist_init(&white_list_rules); + cork_dllist_init(&outbound_block_list_rules); + + struct ip_set *list_ipv4 = &black_list_ipv4; + struct ip_set *list_ipv6 = &black_list_ipv6; + struct cork_dllist *rules = &black_list_rules; + + FILE *f = fopen(path, "r"); + if (f == NULL) { + LOGE("Invalid acl path."); + return -1; + } + + char buf[257]; + while (!feof(f)) + if (fgets(buf, 256, f)) { + // Trim the newline + int len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') { + buf[len - 1] = '\0'; + } + + char *line = trimwhitespace(buf); + + // Skip comments + if (line[0] == '#') { + continue; + } + + if (strlen(line) == 0) { + continue; + } + + if (strcmp(line, "[outbound_block_list]") == 0) { + list_ipv4 = &outbound_block_list_ipv4; + list_ipv6 = &outbound_block_list_ipv6; + rules = &outbound_block_list_rules; + continue; + } else if (strcmp(line, "[black_list]") == 0 + || strcmp(line, "[bypass_list]") == 0) { + list_ipv4 = &black_list_ipv4; + list_ipv6 = &black_list_ipv6; + rules = &black_list_rules; + continue; + } else if (strcmp(line, "[white_list]") == 0 + || strcmp(line, "[proxy_list]") == 0) { + list_ipv4 = &white_list_ipv4; + list_ipv6 = &white_list_ipv6; + rules = &white_list_rules; + continue; + } else if (strcmp(line, "[reject_all]") == 0 + || strcmp(line, "[bypass_all]") == 0) { + acl_mode = WHITE_LIST; + continue; + } else if (strcmp(line, "[accept_all]") == 0 + || strcmp(line, "[proxy_all]") == 0) { + acl_mode = BLACK_LIST; + continue; + } + + char host[257]; + int cidr; + parse_addr_cidr(line, host, &cidr); + + struct cork_ip addr; + int err = cork_ip_init(&addr, host); + if (!err) { + if (addr.version == 4) { + if (cidr >= 0) { + ipset_ipv4_add_network(list_ipv4, &(addr.ip.v4), cidr); + } else { + ipset_ipv4_add(list_ipv4, &(addr.ip.v4)); + } + } else if (addr.version == 6) { + if (cidr >= 0) { + ipset_ipv6_add_network(list_ipv6, &(addr.ip.v6), cidr); + } else { + ipset_ipv6_add(list_ipv6, &(addr.ip.v6)); + } + } + } else { + rule_t *rule = new_rule(); + accept_rule_arg(rule, line); + init_rule(rule); + add_rule(rules, rule); + } + } + + fclose(f); + + return 0; +} + +void +free_rules(struct cork_dllist *rules) +{ + struct cork_dllist_item *iter; + while ((iter = cork_dllist_head(rules)) != NULL) { + rule_t *rule = cork_container_of(iter, rule_t, entries); + remove_rule(rule); + } +} + +void +free_acl(void) +{ + ipset_done(&black_list_ipv4); + ipset_done(&black_list_ipv6); + ipset_done(&white_list_ipv4); + ipset_done(&white_list_ipv6); + + free_rules(&black_list_rules); + free_rules(&white_list_rules); +} + +int +get_acl_mode(void) +{ + return acl_mode; +} + +/* + * Return 0, if not match. + * Return 1, if match black list. + * Return -1, if match white list. + */ +int +acl_match_host(const char *host) +{ + struct cork_ip addr; + int ret = 0; + int err = cork_ip_init(&addr, host); + + if (err) { + int host_len = strlen(host); + if (lookup_rule(&black_list_rules, host, host_len) != NULL) + ret = 1; + else if (lookup_rule(&white_list_rules, host, host_len) != NULL) + ret = -1; + return ret; + } + + if (addr.version == 4) { + if (ipset_contains_ipv4(&black_list_ipv4, &(addr.ip.v4))) + ret = 1; + else if (ipset_contains_ipv4(&white_list_ipv4, &(addr.ip.v4))) + ret = -1; + } else if (addr.version == 6) { + if (ipset_contains_ipv6(&black_list_ipv6, &(addr.ip.v6))) + ret = 1; + else if (ipset_contains_ipv6(&white_list_ipv6, &(addr.ip.v6))) + ret = -1; + } + + return ret; +} + +int +acl_add_ip(const char *ip) +{ + struct cork_ip addr; + int err = cork_ip_init(&addr, ip); + if (err) { + return -1; + } + + if (addr.version == 4) { + ipset_ipv4_add(&black_list_ipv4, &(addr.ip.v4)); + } else if (addr.version == 6) { + ipset_ipv6_add(&black_list_ipv6, &(addr.ip.v6)); + } + + return 0; +} + +int +acl_remove_ip(const char *ip) +{ + struct cork_ip addr; + int err = cork_ip_init(&addr, ip); + if (err) { + return -1; + } + + if (addr.version == 4) { + ipset_ipv4_remove(&black_list_ipv4, &(addr.ip.v4)); + } else if (addr.version == 6) { + ipset_ipv6_remove(&black_list_ipv6, &(addr.ip.v6)); + } + + return 0; +} + +/* + * Return 0, if not match. + * Return 1, if match black list. + */ +int +outbound_block_match_host(const char *host) +{ + struct cork_ip addr; + int ret = 0; + int err = cork_ip_init(&addr, host); + + if (err) { + int host_len = strlen(host); + if (lookup_rule(&outbound_block_list_rules, host, host_len) != NULL) + ret = 1; + return ret; + } + + if (addr.version == 4) { + if (ipset_contains_ipv4(&outbound_block_list_ipv4, &(addr.ip.v4))) + ret = 1; + } else if (addr.version == 6) { + if (ipset_contains_ipv6(&outbound_block_list_ipv6, &(addr.ip.v6))) + ret = 1; + } + + return ret; +} diff --git a/server/acl.h b/server/acl.h new file mode 100644 index 0000000..d6f18b8 --- /dev/null +++ b/server/acl.h @@ -0,0 +1,53 @@ +/* + * acl.h - Define the ACL interface + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _ACL_H +#define _ACL_H + +#define BLACK_LIST 0 +#define WHITE_LIST 1 + +#define MAX_TRIES 64 +#define MALICIOUS 8 +#define SUSPICIOUS 4 +#define BAD 2 +#define MALFORMED 1 + +int init_acl(const char *path); +void free_acl(void); +void clear_block_list(void); + +int acl_match_host(const char *ip); +int acl_add_ip(const char *ip); +int acl_remove_ip(const char *ip); + +int get_acl_mode(void); + +void init_block_list(int firewall); +void free_block_list(); +int check_block_list(char *addr); +int update_block_list(char *addr, int err_level); +int remove_from_block_list(char *addr); + +int outbound_block_match_host(const char *host); + +#endif // _ACL_H diff --git a/server/auth.c b/server/auth.c new file mode 100644 index 0000000..a36257a --- /dev/null +++ b/server/auth.c @@ -0,0 +1,993 @@ + +#include "auth.h" + +static int auth_simple_pack_unit_size = 2000; +typedef int (*hmac_with_key_func)(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len); +typedef int (*hash_func)(char *auth, char *msg, int msg_len); + +typedef struct auth_simple_global_data { + uint8_t local_client_id[8]; + uint32_t connection_id; +}auth_simple_global_data; + +typedef struct auth_simple_local_data { + int has_sent_header; + char * recv_buffer; + int recv_buffer_size; + uint32_t recv_id; + uint32_t pack_id; + char * salt; + uint8_t * user_key; + char uid[4]; + int user_key_len; + hmac_with_key_func hmac; + hash_func hash; + int hash_len; +}auth_simple_local_data; + +void auth_simple_local_data_init(auth_simple_local_data* local) { + local->has_sent_header = 0; + local->recv_buffer = (char*)malloc(16384); + local->recv_buffer_size = 0; + local->recv_id = 1; + local->pack_id = 1; + local->salt = ""; + local->user_key = 0; + local->user_key_len = 0; + local->hmac = 0; + local->hash = 0; + local->hash_len = 0; + local->salt = ""; +} + +void * auth_simple_init_data() { + auth_simple_global_data *global = (auth_simple_global_data*)malloc(sizeof(auth_simple_global_data)); + rand_bytes(global->local_client_id, 8); + rand_bytes((uint8_t*)&global->connection_id, 4); + global->connection_id &= 0xFFFFFF; + return global; +} + +obfs * auth_simple_new_obfs() { + obfs * self = new_obfs(); + self->l_data = malloc(sizeof(auth_simple_local_data)); + auth_simple_local_data_init((auth_simple_local_data*)self->l_data); + return self; +} + +obfs * auth_aes128_md5_new_obfs() { + obfs * self = new_obfs(); + self->l_data = malloc(sizeof(auth_simple_local_data)); + auth_simple_local_data_init((auth_simple_local_data*)self->l_data); + ((auth_simple_local_data*)self->l_data)->hmac = ss_md5_hmac_with_key; + ((auth_simple_local_data*)self->l_data)->hash = ss_md5_hash_func; + ((auth_simple_local_data*)self->l_data)->hash_len = 16; + ((auth_simple_local_data*)self->l_data)->salt = "auth_aes128_md5"; + return self; +} + +obfs * auth_aes128_sha1_new_obfs() { + obfs * self = new_obfs(); + self->l_data = malloc(sizeof(auth_simple_local_data)); + auth_simple_local_data_init((auth_simple_local_data*)self->l_data); + ((auth_simple_local_data*)self->l_data)->hmac = ss_sha1_hmac_with_key; + ((auth_simple_local_data*)self->l_data)->hash = ss_sha1_hash_func; + ((auth_simple_local_data*)self->l_data)->hash_len = 20; + ((auth_simple_local_data*)self->l_data)->salt = "auth_aes128_sha1"; + return self; +} + +void auth_simple_dispose(obfs *self) { + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + if (local->recv_buffer != NULL) { + free(local->recv_buffer); + local->recv_buffer = NULL; + } + if (local->user_key != NULL) { + free(local->user_key); + local->user_key = NULL; + } + free(local); + self->l_data = NULL; + dispose_obfs(self); +} + +int auth_simple_pack_data(char *data, int datalength, char *outdata) { + unsigned char rand_len = (xorshift128plus() & 0xF) + 1; + int out_size = rand_len + datalength + 6; + outdata[0] = out_size >> 8; + outdata[1] = out_size; + outdata[2] = rand_len; + memmove(outdata + rand_len + 2, data, datalength); + fillcrc32((unsigned char *)outdata, out_size); + return out_size; +} + +void memintcopy_lt(void *mem, uint32_t val) { + ((uint8_t *)mem)[0] = val; + ((uint8_t *)mem)[1] = val >> 8; + ((uint8_t *)mem)[2] = val >> 16; + ((uint8_t *)mem)[3] = val >> 24; +} + +int auth_simple_pack_auth_data(auth_simple_global_data *global, char *data, int datalength, char *outdata) { + unsigned char rand_len = (xorshift128plus() & 0xF) + 1; + int out_size = rand_len + datalength + 6 + 12; + outdata[0] = out_size >> 8; + outdata[1] = out_size; + outdata[2] = rand_len; + ++global->connection_id; + if (global->connection_id > 0xFF000000) { + rand_bytes(global->local_client_id, 8); + rand_bytes((uint8_t*)&global->connection_id, 4); + global->connection_id &= 0xFFFFFF; + } + time_t t = time(NULL); + memintcopy_lt(outdata + rand_len + 2, t); + memmove(outdata + rand_len + 2 + 4, global->local_client_id, 4); + memintcopy_lt(outdata + rand_len + 2 + 8, global->connection_id); + memmove(outdata + rand_len + 2 + 12, data, datalength); + fillcrc32((unsigned char *)outdata, out_size); + return out_size; +} + +int auth_simple_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 64); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + if (len > 0 && local->has_sent_header == 0) { + int head_size = get_head_size(plaindata, datalength, 30); + if (head_size > datalength) + head_size = datalength; + pack_len = auth_simple_pack_auth_data((auth_simple_global_data *)self->server.g_data, data, head_size, buffer); + buffer += pack_len; + data += head_size; + len -= head_size; + local->has_sent_header = 1; + } + while ( len > auth_simple_pack_unit_size ) { + pack_len = auth_simple_pack_data(data, auth_simple_pack_unit_size, buffer); + buffer += pack_len; + data += auth_simple_pack_unit_size; + len -= auth_simple_pack_unit_size; + } + if (len > 0) { + pack_len = auth_simple_pack_data(data, len, buffer); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int auth_simple_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + return -1; + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + while (local->recv_buffer_size > 2) { + int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; + if (length >= 8192 || length < 7) { + free(out_buffer); + local->recv_buffer_size = 0; + return -1; + } + if (length > local->recv_buffer_size) + break; + + int crc = crc32((unsigned char*)recv_buffer, length); + if (crc != -1) { + free(out_buffer); + local->recv_buffer_size = 0; + return -1; + } + int data_size = length - recv_buffer[2] - 6; + memmove(buffer, recv_buffer + 2 + recv_buffer[2], data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + + +int auth_sha1_pack_data(char *data, int datalength, char *outdata) { + unsigned char rand_len = (xorshift128plus() & 0xF) + 1; + int out_size = rand_len + datalength + 6; + outdata[0] = out_size >> 8; + outdata[1] = out_size; + outdata[2] = rand_len; + memmove(outdata + rand_len + 2, data, datalength); + filladler32((unsigned char *)outdata, out_size); + return out_size; +} + +int auth_sha1_pack_auth_data(auth_simple_global_data *global, server_info *server, char *data, int datalength, char *outdata) { + unsigned char rand_len = (xorshift128plus() & 0x7F) + 1; + int data_offset = rand_len + 4 + 2; + int out_size = data_offset + datalength + 12 + OBFS_HMAC_SHA1_LEN; + fillcrc32to((unsigned char *)server->key, server->key_len, (unsigned char *)outdata); + outdata[4] = out_size >> 8; + outdata[5] = out_size; + outdata[6] = rand_len; + ++global->connection_id; + if (global->connection_id > 0xFF000000) { + rand_bytes(global->local_client_id, 8); + rand_bytes((uint8_t*)&global->connection_id, 4); + global->connection_id &= 0xFFFFFF; + } + time_t t = time(NULL); + memintcopy_lt(outdata + data_offset, t); + memmove(outdata + data_offset + 4, global->local_client_id, 4); + memintcopy_lt(outdata + data_offset + 8, global->connection_id); + memmove(outdata + data_offset + 12, data, datalength); + char hash[ONETIMEAUTH_BYTES * 2]; + ss_sha1_hmac(hash, outdata, out_size - OBFS_HMAC_SHA1_LEN, server->iv); + memcpy(outdata + out_size - OBFS_HMAC_SHA1_LEN, hash, OBFS_HMAC_SHA1_LEN); + return out_size; +} + +int auth_sha1_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 256); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + if (len > 0 && local->has_sent_header == 0) { + int head_size = get_head_size(plaindata, datalength, 30); + if (head_size > datalength) + head_size = datalength; + pack_len = auth_sha1_pack_auth_data((auth_simple_global_data *)self->server.g_data, &self->server, data, head_size, buffer); + buffer += pack_len; + data += head_size; + len -= head_size; + local->has_sent_header = 1; + } + while ( len > auth_simple_pack_unit_size ) { + pack_len = auth_sha1_pack_data(data, auth_simple_pack_unit_size, buffer); + buffer += pack_len; + data += auth_simple_pack_unit_size; + len -= auth_simple_pack_unit_size; + } + if (len > 0) { + pack_len = auth_sha1_pack_data(data, len, buffer); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int auth_sha1_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + return -1; + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + while (local->recv_buffer_size > 2) { + int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; + if (length >= 8192 || length < 7) { + free(out_buffer); + local->recv_buffer_size = 0; + return -1; + } + if (length > local->recv_buffer_size) + break; + + if (checkadler32((unsigned char*)recv_buffer, length) == 0) { + free(out_buffer); + local->recv_buffer_size = 0; + return -1; + } + int pos = recv_buffer[2] + 2; + int data_size = length - pos - 4; + memmove(buffer, recv_buffer + pos, data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int auth_sha1_v2_pack_data(char *data, int datalength, char *outdata) { + unsigned int rand_len = (datalength > 1300 ? 0 : datalength > 400 ? (xorshift128plus() & 0x7F) : (xorshift128plus() & 0x3FF)) + 1; + int out_size = rand_len + datalength + 6; + outdata[0] = out_size >> 8; + outdata[1] = out_size; + if (rand_len < 128) + { + outdata[2] = rand_len; + } + else + { + outdata[2] = 0xFF; + outdata[3] = rand_len >> 8; + outdata[4] = rand_len; + } + memmove(outdata + rand_len + 2, data, datalength); + filladler32((unsigned char *)outdata, out_size); + return out_size; +} + +int auth_sha1_v2_pack_auth_data(auth_simple_global_data *global, server_info *server, char *data, int datalength, char *outdata) { + unsigned int rand_len = (datalength > 1300 ? 0 : datalength > 400 ? (xorshift128plus() & 0x7F) : (xorshift128plus() & 0x3FF)) + 1; + int data_offset = rand_len + 4 + 2; + int out_size = data_offset + datalength + 12 + OBFS_HMAC_SHA1_LEN; + const char* salt = "auth_sha1_v2"; + int salt_len = strlen(salt); + unsigned char *crc_salt = (unsigned char*)malloc(salt_len + server->key_len); + memcpy(crc_salt, salt, salt_len); + memcpy(crc_salt + salt_len, server->key, server->key_len); + fillcrc32to(crc_salt, salt_len + server->key_len, (unsigned char *)outdata); + free(crc_salt); + outdata[4] = out_size >> 8; + outdata[5] = out_size; + if (rand_len < 128) + { + outdata[6] = rand_len; + } + else + { + outdata[6] = 0xFF; + outdata[7] = rand_len >> 8; + outdata[8] = rand_len; + } + ++global->connection_id; + if (global->connection_id > 0xFF000000) { + rand_bytes(global->local_client_id, 8); + rand_bytes((uint8_t*)&global->connection_id, 4); + global->connection_id &= 0xFFFFFF; + } + memmove(outdata + data_offset, global->local_client_id, 8); + memintcopy_lt(outdata + data_offset + 8, global->connection_id); + memmove(outdata + data_offset + 12, data, datalength); + char hash[ONETIMEAUTH_BYTES * 2]; + ss_sha1_hmac(hash, outdata, out_size - OBFS_HMAC_SHA1_LEN, server->iv); + memcpy(outdata + out_size - OBFS_HMAC_SHA1_LEN, hash, OBFS_HMAC_SHA1_LEN); + return out_size; +} + +int auth_sha1_v2_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 4096); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + if (len > 0 && local->has_sent_header == 0) { + int head_size = get_head_size(plaindata, datalength, 30); + if (head_size > datalength) + head_size = datalength; + pack_len = auth_sha1_v2_pack_auth_data((auth_simple_global_data *)self->server.g_data, &self->server, data, head_size, buffer); + buffer += pack_len; + data += head_size; + len -= head_size; + local->has_sent_header = 1; + } + while ( len > auth_simple_pack_unit_size ) { + pack_len = auth_sha1_v2_pack_data(data, auth_simple_pack_unit_size, buffer); + buffer += pack_len; + data += auth_simple_pack_unit_size; + len -= auth_simple_pack_unit_size; + } + if (len > 0) { + pack_len = auth_sha1_v2_pack_data(data, len, buffer); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int auth_sha1_v2_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + return -1; + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + char error = 0; + while (local->recv_buffer_size > 2) { + int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; + if (length >= 8192 || length < 7) { + local->recv_buffer_size = 0; + error = 1; + break; + } + if (length > local->recv_buffer_size) + break; + + if (checkadler32((unsigned char*)recv_buffer, length) == 0) { + local->recv_buffer_size = 0; + error = 1; + break; + } + int pos = recv_buffer[2]; + if (pos < 255) + { + pos += 2; + } + else + { + pos = ((recv_buffer[3] << 8) | recv_buffer[4]) + 2; + } + int data_size = length - pos - 4; + memmove(buffer, recv_buffer + pos, data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len; + if (error == 0) { + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + } else { + len = -1; + } + free(out_buffer); + return len; +} + +int auth_sha1_v4_pack_data(char *data, int datalength, char *outdata) { + unsigned int rand_len = (datalength > 1300 ? 0 : datalength > 400 ? (xorshift128plus() & 0x7F) : (xorshift128plus() & 0x3FF)) + 1; + int out_size = rand_len + datalength + 8; + outdata[0] = out_size >> 8; + outdata[1] = out_size; + uint32_t crc_val = crc32((unsigned char*)outdata, 2); + outdata[2] = crc_val; + outdata[3] = crc_val >> 8; + if (rand_len < 128) + { + outdata[4] = rand_len; + } + else + { + outdata[4] = 0xFF; + outdata[5] = rand_len >> 8; + outdata[6] = rand_len; + } + memmove(outdata + rand_len + 4, data, datalength); + filladler32((unsigned char *)outdata, out_size); + return out_size; +} + +int auth_sha1_v4_pack_auth_data(auth_simple_global_data *global, server_info *server, char *data, int datalength, char *outdata) { + unsigned int rand_len = (datalength > 1300 ? 0 : datalength > 400 ? (xorshift128plus() & 0x7F) : (xorshift128plus() & 0x3FF)) + 1; + int data_offset = rand_len + 4 + 2; + int out_size = data_offset + datalength + 12 + OBFS_HMAC_SHA1_LEN; + const char* salt = "auth_sha1_v4"; + int salt_len = strlen(salt); + unsigned char *crc_salt = (unsigned char*)malloc(salt_len + server->key_len + 2); + crc_salt[0] = outdata[0] = out_size >> 8; + crc_salt[1] = outdata[1] = out_size; + + memcpy(crc_salt + 2, salt, salt_len); + memcpy(crc_salt + salt_len + 2, server->key, server->key_len); + fillcrc32to(crc_salt, salt_len + server->key_len + 2, (unsigned char *)outdata + 2); + free(crc_salt); + if (rand_len < 128) + { + outdata[6] = rand_len; + } + else + { + outdata[6] = 0xFF; + outdata[7] = rand_len >> 8; + outdata[8] = rand_len; + } + ++global->connection_id; + if (global->connection_id > 0xFF000000) { + rand_bytes(global->local_client_id, 8); + rand_bytes((uint8_t*)&global->connection_id, 4); + global->connection_id &= 0xFFFFFF; + } + time_t t = time(NULL); + memintcopy_lt(outdata + data_offset, t); + memmove(outdata + data_offset + 4, global->local_client_id, 4); + memintcopy_lt(outdata + data_offset + 8, global->connection_id); + memmove(outdata + data_offset + 12, data, datalength); + char hash[ONETIMEAUTH_BYTES * 2]; + ss_sha1_hmac(hash, outdata, out_size - OBFS_HMAC_SHA1_LEN, server->iv); + memcpy(outdata + out_size - OBFS_HMAC_SHA1_LEN, hash, OBFS_HMAC_SHA1_LEN); + return out_size; +} + +int auth_sha1_v4_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 4096); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + if (len > 0 && local->has_sent_header == 0) { + int head_size = get_head_size(plaindata, datalength, 30); + if (head_size > datalength) + head_size = datalength; + pack_len = auth_sha1_v4_pack_auth_data((auth_simple_global_data *)self->server.g_data, &self->server, data, head_size, buffer); + buffer += pack_len; + data += head_size; + len -= head_size; + local->has_sent_header = 1; + } + while ( len > auth_simple_pack_unit_size ) { + pack_len = auth_sha1_v4_pack_data(data, auth_simple_pack_unit_size, buffer); + buffer += pack_len; + data += auth_simple_pack_unit_size; + len -= auth_simple_pack_unit_size; + } + if (len > 0) { + pack_len = auth_sha1_v4_pack_data(data, len, buffer); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int auth_sha1_v4_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + return -1; + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + char error = 0; + while (local->recv_buffer_size > 4) { + uint32_t crc_val = crc32((unsigned char*)recv_buffer, 2); + if ((((uint32_t)recv_buffer[3] << 8) | recv_buffer[2]) != (crc_val & 0xffff)) { + local->recv_buffer_size = 0; + error = 1; + break; + } + int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; + if (length >= 8192 || length < 7) { + local->recv_buffer_size = 0; + error = 1; + break; + } + if (length > local->recv_buffer_size) + break; + + if (checkadler32((unsigned char*)recv_buffer, length) == 0) { + local->recv_buffer_size = 0; + error = 1; + break; + } + int pos = recv_buffer[4]; + if (pos < 255) + { + pos += 4; + } + else + { + pos = (((int)recv_buffer[5] << 8) | recv_buffer[6]) + 4; + } + int data_size = length - pos - 4; + memmove(buffer, recv_buffer + pos, data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len; + if (error == 0) { + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + } else { + len = -1; + } + free(out_buffer); + return len; +} + + +int auth_aes128_sha1_pack_data(char *data, int datalength, char *outdata, auth_simple_local_data *local, server_info *server) { + unsigned int rand_len = (datalength > 1200 ? 0 : local->pack_id > 4 ? (xorshift128plus() & 0x20) : datalength > 900 ? (xorshift128plus() & 0x80) : (xorshift128plus() & 0x200)) + 1; + int out_size = rand_len + datalength + 8; + memcpy(outdata + rand_len + 4, data, datalength); + outdata[0] = out_size; + outdata[1] = out_size >> 8; + uint8_t key_len = local->user_key_len + 4; + uint8_t *key = (uint8_t*)malloc(key_len); + memcpy(key, local->user_key, local->user_key_len); + memintcopy_lt(key + key_len - 4, local->pack_id); + + { + uint8_t rnd_data[rand_len]; + rand_bytes(rnd_data, rand_len); + memcpy(outdata + 4, rnd_data, rand_len); + } + + { + char hash[20]; + local->hmac(hash, outdata, 2, key, key_len); + memcpy(outdata + 2, hash, 2); + } + + if (rand_len < 128) + { + outdata[4] = rand_len; + } + else + { + outdata[4] = 0xFF; + outdata[5] = rand_len; + outdata[6] = rand_len >> 8; + } + ++local->pack_id; + + { + char hash[20]; + local->hmac(hash, outdata, out_size - 4, key, key_len); + memcpy(outdata + out_size - 4, hash, 4); + } + free(key); + + return out_size; +} + +int auth_aes128_sha1_pack_auth_data(auth_simple_global_data *global, server_info *server, auth_simple_local_data *local, char *data, int datalength, char *outdata) { + unsigned int rand_len = (datalength > 400 ? (xorshift128plus() & 0x200) : (xorshift128plus() & 0x400)); + int data_offset = rand_len + 16 + 4 + 4 + 7; + int out_size = data_offset + datalength + 4; + + char encrypt[24]; + char encrypt_data[16]; + + uint8_t *key = (uint8_t*)malloc(server->iv_len + server->key_len); + uint8_t key_len = server->iv_len + server->key_len; + memcpy(key, server->iv, server->iv_len); + memcpy(key + server->iv_len, server->key, server->key_len); + + { + uint8_t rnd_data[rand_len]; + rand_bytes(rnd_data, rand_len); + memcpy(outdata + data_offset - rand_len, rnd_data, rand_len); + } + + ++global->connection_id; + if (global->connection_id > 0xFF000000) { + rand_bytes(global->local_client_id, 8); + rand_bytes((uint8_t*)&global->connection_id, 4); + global->connection_id &= 0xFFFFFF; + } + time_t t = time(NULL); + memintcopy_lt(encrypt, t); + memcpy(encrypt + 4, global->local_client_id, 4); + memintcopy_lt(encrypt + 8, global->connection_id); + encrypt[12] = out_size; + encrypt[13] = out_size >> 8; + encrypt[14] = rand_len; + encrypt[15] = rand_len >> 8; + + { + + if (local->user_key == NULL) { + if(server->param != NULL && server->param[0] != 0) { + char *param = server->param; + char *delim = strchr(param, ':'); + if(delim != NULL) { + char uid_str[16] = {}; + strncpy(uid_str, param, delim - param); + char key_str[128]; + strcpy(key_str, delim + 1); + long uid_long = strtol(uid_str, NULL, 10); + memintcopy_lt(local->uid, uid_long); + + char hash[21] = {0}; + local->hash(hash, key_str, strlen(key_str)); + + local->user_key_len = local->hash_len; + local->user_key = (uint8_t*)malloc(local->user_key_len); + memcpy(local->user_key, hash, local->hash_len); + } + } + if (local->user_key == NULL) { + rand_bytes((uint8_t *)local->uid, 4); + + local->user_key_len = server->key_len; + local->user_key = (uint8_t*)malloc(local->user_key_len); + memcpy(local->user_key, server->key, local->user_key_len); + } + } + + char encrypt_key_base64[256] = {0}; + unsigned char encrypt_key[local->user_key_len]; + memcpy(encrypt_key, local->user_key, local->user_key_len); + base64_encode(encrypt_key, local->user_key_len, encrypt_key_base64); + + int base64_len; + base64_len = (local->user_key_len + 2) / 3 * 4; + memcpy(encrypt_key_base64 + base64_len, local->salt, strlen(local->salt)); + + char enc_key[16]; + int enc_key_len = base64_len + strlen(local->salt); + bytes_to_key_with_size(encrypt_key_base64, enc_key_len, (uint8_t*)enc_key, 16); + ss_aes_128_cbc(encrypt, encrypt_data, enc_key); + memcpy(encrypt + 4, encrypt_data, 16); + memcpy(encrypt, local->uid, 4); + } + + { + char hash[20]; + local->hmac(hash, encrypt, 20, key, key_len); + memcpy(encrypt + 20, hash, 4); + } + + { + uint8_t rnd[1]; + rand_bytes(rnd, 1); + memcpy(outdata, rnd, 1); + char hash[20]; + local->hmac(hash, (char *)rnd, 1, key, key_len); + memcpy(outdata + 1, hash, 6); + } + + memcpy(outdata + 7, encrypt, 24); + memcpy(outdata + data_offset, data, datalength); + + { + char hash[20]; + local->hmac(hash, outdata, out_size - 4, local->user_key, local->user_key_len); + memmove(outdata + out_size - 4, hash, 4); + } + free(key); + + return out_size; +} + +int auth_aes128_sha1_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 4096); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + if (len > 0 && local->has_sent_header == 0) { + int head_size = 1200; + if (head_size > datalength) + head_size = datalength; + pack_len = auth_aes128_sha1_pack_auth_data((auth_simple_global_data *)self->server.g_data, &self->server, local, data, head_size, buffer); + buffer += pack_len; + data += head_size; + len -= head_size; + local->has_sent_header = 1; + } + while ( len > auth_simple_pack_unit_size ) { + pack_len = auth_aes128_sha1_pack_data(data, auth_simple_pack_unit_size, buffer, local, &self->server); + buffer += pack_len; + data += auth_simple_pack_unit_size; + len -= auth_simple_pack_unit_size; + } + if (len > 0) { + pack_len = auth_aes128_sha1_pack_data(data, len, buffer, local, &self->server); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int auth_aes128_sha1_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + //server_info *server = (server_info*)&self->server; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + return -1; + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + int key_len = local->user_key_len + 4; + uint8_t *key = (uint8_t*)malloc(key_len); + memcpy(key, local->user_key, local->user_key_len); + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + char error = 0; + while (local->recv_buffer_size > 4) { + memintcopy_lt(key + key_len - 4, local->recv_id); + + { + char hash[20]; + local->hmac(hash, (char*)recv_buffer, 2, key, key_len); + + if (memcmp(hash, recv_buffer + 2, 2)) { + local->recv_buffer_size = 0; + error = 1; + break; + } + } + + int length = ((int)recv_buffer[1] << 8) + recv_buffer[0]; + if (length >= 8192 || length < 8) { + local->recv_buffer_size = 0; + error = 1; + break; + } + if (length > local->recv_buffer_size) + break; + + { + char hash[20]; + local->hmac(hash, (char *)recv_buffer, length - 4, key, key_len); + if (memcmp(hash, recv_buffer + length - 4, 4)) + { + local->recv_buffer_size = 0; + error = 1; + break; + } + } + + ++local->recv_id; + int pos = recv_buffer[4]; + if (pos < 255) + { + pos += 4; + } + else + { + pos = (((int)recv_buffer[6] << 8) | recv_buffer[5]) + 4; + } + int data_size = length - pos - 4; + memmove(buffer, recv_buffer + pos, data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len; + if (error == 0) { + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + } else { + len = -1; + } + free(out_buffer); + free(key); + return len; +} + +int auth_aes128_sha1_client_udp_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength + 8); + + if (local->user_key == NULL) { + if(self->server.param != NULL && self->server.param[0] != 0) { + char *param = self->server.param; + char *delim = strchr(param, ':'); + if(delim != NULL) { + char uid_str[16] = {}; + strncpy(uid_str, param, delim - param); + char key_str[128]; + strcpy(key_str, delim + 1); + long uid_long = strtol(uid_str, NULL, 10); + memintcopy_lt(local->uid, uid_long); + + char hash[21] = {0}; + local->hash(hash, key_str, strlen(key_str)); + + local->user_key_len = local->hash_len; + local->user_key = (uint8_t*)malloc(local->user_key_len); + memcpy(local->user_key, hash, local->hash_len); + } + } + if (local->user_key == NULL) { + rand_bytes((uint8_t *)local->uid, 4); + + local->user_key_len = self->server.key_len; + local->user_key = (uint8_t*)malloc(local->user_key_len); + memcpy(local->user_key, self->server.key, local->user_key_len); + } + } + + int outlength = datalength + 8; + memmove(out_buffer, plaindata, datalength); + memmove(out_buffer + datalength, local->uid, 4); + + { + char hash[20]; + local->hmac(hash, out_buffer, outlength - 4, local->user_key, local->user_key_len); + memmove(out_buffer + outlength - 4, hash, 4); + } + + if (*capacity < outlength) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = outlength * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, outlength); + + free(out_buffer); + return outlength; +} + +int auth_aes128_sha1_client_udp_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + if (datalength <= 4) + return 0; + + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + + char hash[20]; + local->hmac(hash, plaindata, datalength - 4, self->server.key, self->server.key_len); + + if (memcmp(hash, plaindata + datalength - 4, 4)) + { + return 0; + } + + return datalength - 4; +} diff --git a/server/auth.h b/server/auth.h new file mode 100644 index 0000000..f7730df --- /dev/null +++ b/server/auth.h @@ -0,0 +1,30 @@ +/* + * auth.h - Define shadowsocksR server's buffers and callbacks + * + * Copyright (C) 2015 - 2016, Break Wa11 + */ + +#ifndef _AUTH_H +#define _AUTH_H + +void * auth_simple_init_data(); +obfs * auth_simple_new_obfs(); +void auth_simple_dispose(obfs *self); + +int auth_simple_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int auth_simple_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + + +int auth_sha1_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int auth_sha1_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + +int auth_sha1_v2_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int auth_sha1_v2_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + +int auth_sha1_v4_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int auth_sha1_v4_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + +int auth_aes128_sha1_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int auth_aes128_sha1_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + +#endif // _AUTH_H diff --git a/server/base64.c b/server/base64.c new file mode 100644 index 0000000..7cf9552 --- /dev/null +++ b/server/base64.c @@ -0,0 +1,119 @@ +#include "base64.h" + +/* BASE 64 encode table */ +static const char base64en[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +#define BASE64_PAD '=' + +#define BASE64DE_FIRST '+' +#define BASE64DE_LAST 'z' + +/* ASCII order for BASE 64 decode, -1 in unused character */ +static const signed char base64de[] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* '+', ',', '-', '.', '/', */ + -1, -1, -1, 62, -1, -1, -1, 63, + /* '0', '1', '2', '3', '4', '5', '6', '7', */ + 52, 53, 54, 55, 56, 57, 58, 59, + /* '8', '9', ':', ';', '<', '=', '>', '?', */ + 60, 61, -1, -1, -1, -1, -1, -1, + /* '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', */ + -1, 0, 1, 2, 3, 4, 5, 6, + /* 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', */ + 7, 8, 9, 10, 11, 12, 13, 14, + /* 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', */ + 15, 16, 17, 18, 19, 20, 21, 22, + /* 'X', 'Y', 'Z', '[', '\', ']', '^', '_', */ + 23, 24, 25, -1, -1, -1, -1, -1, + /* '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', */ + -1, 26, 27, 28, 29, 30, 31, 32, + /* 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', */ + 33, 34, 35, 36, 37, 38, 39, 40, + /* 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', */ + 41, 42, 43, 44, 45, 46, 47, 48, + /* 'x', 'y', 'z', */ + 49, 50, 51, +}; + +int +base64_encode(const unsigned char *in, unsigned int inlen, char *out) +{ + unsigned int i, j; + + for (i = j = 0; i < inlen; i++) { + int s = i % 3; /* from 6/gcd(6, 8) */ + + switch (s) { + case 0: + out[j++] = base64en[(in[i] >> 2) & 0x3F]; + continue; + case 1: + out[j++] = base64en[((in[i-1] & 0x3) << 4) + ((in[i] >> 4) & 0xF)]; + continue; + case 2: + out[j++] = base64en[((in[i-1] & 0xF) << 2) + ((in[i] >> 6) & 0x3)]; + out[j++] = base64en[in[i] & 0x3F]; + } + } + + /* move back */ + i -= 1; + + /* check the last and add padding */ + if ((i % 3) == 0) { + out[j++] = base64en[(in[i] & 0x3) << 4]; + out[j++] = BASE64_PAD; + out[j++] = BASE64_PAD; + } else if ((i % 3) == 1) { + out[j++] = base64en[(in[i] & 0xF) << 2]; + out[j++] = BASE64_PAD; + } + + return BASE64_OK; +} + +int +base64_decode(const char *in, unsigned int inlen, unsigned char *out) +{ + unsigned int i, j; + + for (i = j = 0; i < inlen; i++) { + int c; + int s = i % 4; /* from 8/gcd(6, 8) */ + + if (in[i] == '=') + return BASE64_OK; + + if (in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST || + (c = base64de[(int)in[i]]) == -1) + return BASE64_INVALID; + + switch (s) { + case 0: + out[j] = ((unsigned int)c << 2) & 0xFF; + continue; + case 1: + out[j++] += ((unsigned int)c >> 4) & 0x3; + + /* if not last char with padding */ + if (i < (inlen - 3) || in[inlen - 2] != '=') + out[j] = ((unsigned int)c & 0xF) << 4; + continue; + case 2: + out[j++] += ((unsigned int)c >> 2) & 0xF; + + /* if not last char with padding */ + if (i < (inlen - 2) || in[inlen - 1] != '=') + out[j] = ((unsigned int)c & 0x3) << 6; + continue; + case 3: + out[j++] += (unsigned char)c; + } + } + + return BASE64_OK; +} diff --git a/server/base64.h b/server/base64.h new file mode 100644 index 0000000..6432ba3 --- /dev/null +++ b/server/base64.h @@ -0,0 +1,16 @@ +#ifndef __BASE64_H__ +#define __BASE64_H__ + +enum {BASE64_OK = 0, BASE64_INVALID}; + +#define BASE64_ENCODE_OUT_SIZE(s) (((s) + 2) / 3 * 4) +#define BASE64_DECODE_OUT_SIZE(s) (((s)) / 4 * 3) + +int +base64_encode(const unsigned char *in, unsigned int inlen, char *out); + +int +base64_decode(const char *in, unsigned int inlen, unsigned char *out); + + +#endif /* __BASE64_H__ */ diff --git a/server/cache.c b/server/cache.c new file mode 100644 index 0000000..c1a2995 --- /dev/null +++ b/server/cache.c @@ -0,0 +1,308 @@ +/* + * cache.c - Manage the connection cache for UDPRELAY + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +/* + * Original Author: Oliver Lorenz (ol), olli@olorenz.org, https://olorenz.org + * License: This is licensed under the same terms as uthash itself + */ + +#include +#include + +#include "cache.h" +#include "utils.h" + +#ifdef __MINGW32__ +#include "win32.h" +#endif + +/** Creates a new cache object + * + * @param dst + * Where the newly allocated cache object will be stored in + * + * @param capacity + * The maximum number of elements this cache object can hold + * + * @return EINVAL if dst is NULL, ENOMEM if malloc fails, 0 otherwise + */ +int +cache_create(struct cache **dst, const size_t capacity, + void (*free_cb)(void *key, void *element)) +{ + struct cache *new = NULL; + + if (!dst) { + return EINVAL; + } + + if ((new = malloc(sizeof(*new))) == NULL) { + return ENOMEM; + } + + new->max_entries = capacity; + new->entries = NULL; + new->free_cb = free_cb; + *dst = new; + return 0; +} + +/** Frees an allocated cache object + * + * @param cache + * The cache object to free + * + * @param keep_data + * Whether to free contained data or just delete references to it + * + * @return EINVAL if cache is NULL, 0 otherwise + */ +int +cache_delete(struct cache *cache, int keep_data) +{ + struct cache_entry *entry, *tmp; + + if (!cache) { + return EINVAL; + } + + if (keep_data) { + HASH_CLEAR(hh, cache->entries); + } else { + HASH_ITER(hh, cache->entries, entry, tmp){ + HASH_DEL(cache->entries, entry); + if (entry->data != NULL) { + if (cache->free_cb) { + cache->free_cb(entry->key, entry->data); + } else { + ss_free(entry->data); + } + } + ss_free(entry->key); + ss_free(entry); + } + } + + ss_free(cache); + return 0; +} + +/** Clear old cache object + * + * @param cache + * The cache object to clear + * + * @param age + * Clear only objects older than the age (sec) + * + * @return EINVAL if cache is NULL, 0 otherwise + */ +int +cache_clear(struct cache *cache, ev_tstamp age) +{ + struct cache_entry *entry, *tmp; + + if (!cache) { + return EINVAL; + } + + ev_tstamp now = ev_time(); + + HASH_ITER(hh, cache->entries, entry, tmp){ + if (now - entry->ts > age) { + HASH_DEL(cache->entries, entry); + if (entry->data != NULL) { + if (cache->free_cb) { + cache->free_cb(entry->key, entry->data); + } else { + ss_free(entry->data); + } + } + ss_free(entry->key); + ss_free(entry); + } + } + + return 0; +} + +/** Removes a cache entry + * + * @param cache + * The cache object + * + * @param key + * The key of the entry to remove + * + * @param key_len + * The length of key + * + * @return EINVAL if cache is NULL, 0 otherwise + */ +int +cache_remove(struct cache *cache, char *key, size_t key_len) +{ + struct cache_entry *tmp; + + if (!cache || !key) { + return EINVAL; + } + + HASH_FIND(hh, cache->entries, key, key_len, tmp); + + if (tmp) { + HASH_DEL(cache->entries, tmp); + if (tmp->data != NULL) { + if (cache->free_cb) { + cache->free_cb(tmp->key, tmp->data); + } else { + ss_free(tmp->data); + } + } + ss_free(tmp->key); + ss_free(tmp); + } + + return 0; +} + +/** Checks if a given key is in the cache + * + * @param cache + * The cache object + * + * @param key + * The key to look-up + * + * @param key_len + * The length of key + * + * @param result + * Where to store the result if key is found. + * + * A warning: Even though result is just a pointer, + * you have to call this function with a **ptr, + * otherwise this will blow up in your face. + * + * @return EINVAL if cache is NULL, 0 otherwise + */ +int +cache_lookup(struct cache *cache, char *key, size_t key_len, void *result) +{ + struct cache_entry *tmp = NULL; + char **dirty_hack = result; + + if (!cache || !key || !result) { + return EINVAL; + } + + HASH_FIND(hh, cache->entries, key, key_len, tmp); + if (tmp) { + HASH_DELETE(hh, cache->entries, tmp); + tmp->ts = ev_time(); + HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp); + *dirty_hack = tmp->data; + } else { + *dirty_hack = result = NULL; + } + + return 0; +} + +int +cache_key_exist(struct cache *cache, char *key, size_t key_len) +{ + struct cache_entry *tmp = NULL; + + if (!cache || !key) { + return 0; + } + + HASH_FIND(hh, cache->entries, key, key_len, tmp); + if (tmp) { + HASH_DELETE(hh, cache->entries, tmp); + tmp->ts = ev_time(); + HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp); + return 1; + } else { + return 0; + } + + return 0; +} + +/** Inserts a given pair into the cache + * + * @param cache + * The cache object + * + * @param key + * The key that identifies + * + * @param key_len + * The length of key + * + * @param data + * Data associated with + * + * @return EINVAL if cache is NULL, ENOMEM if malloc fails, 0 otherwise + */ +int +cache_insert(struct cache *cache, char *key, size_t key_len, void *data) +{ + struct cache_entry *entry = NULL; + struct cache_entry *tmp_entry = NULL; + + if (!cache) { + return EINVAL; + } + + if ((entry = malloc(sizeof(*entry))) == NULL) { + return ENOMEM; + } + + entry->key = ss_malloc(key_len + 1); + memcpy(entry->key, key, key_len); + entry->key[key_len] = 0; + + entry->data = data; + entry->ts = ev_time(); + HASH_ADD_KEYPTR(hh, cache->entries, entry->key, key_len, entry); + + if (HASH_COUNT(cache->entries) >= cache->max_entries) { + HASH_ITER(hh, cache->entries, entry, tmp_entry){ + HASH_DELETE(hh, cache->entries, entry); + if (entry->data != NULL) { + if (cache->free_cb) { + cache->free_cb(entry->key, entry->data); + } else { + ss_free(entry->data); + } + } + ss_free(entry->key); + ss_free(entry); + break; + } + } + + return 0; +} diff --git a/server/cache.h b/server/cache.h new file mode 100644 index 0000000..0ec98f5 --- /dev/null +++ b/server/cache.h @@ -0,0 +1,62 @@ +/* + * cache.h - Define the cache manager interface + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +/* + * Original Author: Oliver Lorenz (ol), olli@olorenz.org, https://olorenz.org + * License: This is licensed under the same terms as uthash itself + */ + +#ifndef _CACHE_ +#define _CACHE_ + +#include "uthash.h" +#include "ev.h" + +/** + * A cache entry + */ +struct cache_entry { + char *key; /** +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define __DEBUG__ +#ifdef __DEBUG__ +#define DEBUG(format,...) printf("File: "__FILE__", Line: %05d: "format"/n", __LINE__, ##__VA_ARGS__) +#else +#define DEBUG(format,...) +#endif + +static sigjmp_buf jmpbuf; +static void alarm_func() +{ + siglongjmp(jmpbuf, 1); +} + +static struct hostent *timeGethostbyname(const char *domain, int timeout) +{ + struct hostent *ipHostent = NULL; + signal(SIGALRM, alarm_func); + if(sigsetjmp(jmpbuf, 1) != 0) + { + alarm(0);//timout + signal(SIGALRM, SIG_IGN); + return NULL; + } + alarm(timeout);//setting alarm + ipHostent = gethostbyname(domain); + signal(SIGALRM, SIG_IGN); + return ipHostent; +} + + +#define MY_HTTP_DEFAULT_PORT 80 +#define BUFFER_SIZE 1024 +#define HTTP_POST "POST /%s HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*\r\n"\ + "Content-Type:application/x-www-form-urlencoded\r\nContent-Length: %d\r\n\r\n%s" +#define HTTP_GET "GET /%s HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*\r\n\r\n" + +static int http_parse_url(const char *url,char *host,char *file,int *port) +{ + char *ptr1,*ptr2; + int len = 0; + if(!url || !host || !file || !port){ + return 1; + } + + ptr1 = (char *)url; + + if(!strncmp(ptr1,"http://",strlen("http://"))){ + ptr1 += strlen("http://"); + }else{ + return 1; + } + + ptr2 = strchr(ptr1,'/'); + if(ptr2){ + len = strlen(ptr1) - strlen(ptr2); + memcpy(host,ptr1,len); + host[len] = '\0'; + if(*(ptr2 + 1)){ + memcpy(file,ptr2 + 1,strlen(ptr2) - 1 ); + file[strlen(ptr2) - 1] = '\0'; + } + }else{ + memcpy(host,ptr1,strlen(ptr1)); + host[strlen(ptr1)] = '\0'; + } + //get host and ip + ptr1 = strchr(host,':'); + if(ptr1){ + *ptr1++ = '\0'; + *port = atoi(ptr1); + }else{ + *port = MY_HTTP_DEFAULT_PORT; + } + + return 0; +} + + +static int http_tcpclient_recv(int socket,char *lpbuff){ + int recvnum = 0; + + recvnum = recv(socket, lpbuff,BUFFER_SIZE*4,0); + + return recvnum; +} + +static int http_tcpclient_send(int socket,char *buff,int size){ + int sent=0,tmpres=0; + + while(sent < size){ + tmpres = send(socket,buff+sent,size-sent,0); + if(tmpres == -1){ + return 1; + } + sent += tmpres; + } + return sent; +} + + + + + +int http_get(const char *url,int socket_fd) +{ + char lpbuf[BUFFER_SIZE*4] = {'\0'}; + + char host_addr[BUFFER_SIZE] = {'\0'}; + char file[BUFFER_SIZE] = {'\0'}; + int port = 0; + + + if(!url){ + DEBUG(" failed!\n"); + return 1; + } + + if(http_parse_url(url,host_addr,file,&port)){ + DEBUG("http_parse_url failed!\n"); + return 1; + } + DEBUG("url: %s\thost_addr : %s\tfile:%s\t,%d\n",url,host_addr,file,port); + + + if(socket_fd < 0){ + DEBUG("http_tcpclient_create failed\n"); + return 1; + } + + sprintf(lpbuf,HTTP_GET,file,host_addr,port); + + if(http_tcpclient_send(socket_fd,lpbuf,strlen(lpbuf)) < 0){ + DEBUG("http_tcpclient_send failed..\n"); + return 1; + } + DEBUG("request:\n%s\n",lpbuf); + + if(http_tcpclient_recv(socket_fd,lpbuf) <= 0){ + DEBUG("http_tcpclient_recv failed\n"); + close(socket_fd); + return 1; + } + DEBUG("rec:\n%s\n",lpbuf); + close(socket_fd); + + //return http_parse_result(lpbuf); +return 0; +} + + + +int main(int argc, char *argv[]) +{ + int fd,http_flag=0,http_ret=1; + struct sockaddr_in addr; + struct hostent *host; + struct timeval timeo = {3, 0}; + socklen_t len = sizeof(timeo); + + char http_url[100]="http://"; + + + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (argc >= 4) + timeo.tv_sec = atoi(argv[3]); + if (argc>=5) + http_flag=1; + + if((host=timeGethostbyname(argv[1],timeo.tv_sec)) == NULL) { + DEBUG("gethostbyname err\n"); + return 1; + } + if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len) == -1) + { + + DEBUG("setsockopt send err\n"); + return 1; + } + + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, len) == -1) + { + + DEBUG("setsockopt recv err\n"); + return 1; + } + + addr.sin_family = AF_INET; + addr.sin_addr = *((struct in_addr *)host->h_addr); + //addr.sin_addr.s_addr = inet_addr(argv[1]); + addr.sin_port = htons(atoi(argv[2])); +if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) + { + if (errno == EINPROGRESS) + { + DEBUG("timeout err\n"); + return 1; + } + DEBUG("connect err\n"); + return 1; + } +if(http_flag==0) +{ + close(fd); + return 0; +} +strcat(http_url,argv[1]); +http_ret=http_get(http_url,fd); +if(http_ret==1) +{ +DEBUG("recv err"); + return 1; +} +else +{ +DEBUG("recv ok"); + + return 0; +} + +} \ No newline at end of file diff --git a/server/common.h b/server/common.h new file mode 100644 index 0000000..000f084 --- /dev/null +++ b/server/common.h @@ -0,0 +1,58 @@ +/* + * common.h - Provide global definitions + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _COMMON_H +#define _COMMON_H + +#define DEFAULT_CONF_PATH "/etc/shadowsocks-libev/config.json" + +#ifndef SOL_TCP +#define SOL_TCP IPPROTO_TCP +#endif + +#if defined(MODULE_TUNNEL) || defined(MODULE_REDIR) +#define MODULE_LOCAL +#endif + +int init_udprelay(const char *server_host, const char *server_port, +#ifdef MODULE_LOCAL + const struct sockaddr *remote_addr, const int remote_addr_len, +#ifdef MODULE_TUNNEL + const ss_addr_t tunnel_addr, +#endif +#endif + int mtu, int method, int auth, int timeout, const char *iface, const char *protocol, const char *protocol_param); + +void free_udprelay(void); + +#ifdef ANDROID +int protect_socket(int fd); +int send_traffic_stat(uint64_t tx, uint64_t rx); +#endif + +#define STAGE_ERROR -1 /* Error detected */ +#define STAGE_INIT 0 /* Initial stage */ +#define STAGE_HANDSHAKE 1 /* Handshake with client */ +#define STAGE_PARSE 2 /* Parse the header */ +#define STAGE_RESOLVE 4 /* Resolve the hostname */ +#define STAGE_STREAM 5 /* Stream between client and server */ + +#endif // _COMMON_H diff --git a/server/crc32.c b/server/crc32.c new file mode 100644 index 0000000..6d328d2 --- /dev/null +++ b/server/crc32.c @@ -0,0 +1,97 @@ +static uint32_t crc32_table[256] = {0}; + +void init_crc32_table(void) { + uint32_t c, i, j; + if (crc32_table[0] == 0) { + for (i = 0; i < 256; i++) { + c = i; + for (j = 0; j < 8; j++) { + if (c & 1) + c = 0xedb88320L ^ (c >> 1); + else + c = c >> 1; + } + crc32_table[i] = c; + } + } +} + +uint32_t crc32(unsigned char *buffer, unsigned int size) { + uint32_t crc = 0xFFFFFFFF; + unsigned int i; + for (i = 0; i < size; i++) { + crc = crc32_table[(crc ^ buffer[i]) & 0xFF] ^ (crc >> 8); + } + return crc ^ 0xFFFFFFFF; +} + +void fillcrc32to(unsigned char *buffer, unsigned int size, unsigned char *outbuffer) { + uint32_t crc = 0xFFFFFFFF; + unsigned int i; + for (i = 0; i < size; i++) { + crc = crc32_table[(crc ^ buffer[i]) & 0xff] ^ (crc >> 8); + } + crc ^= 0xFFFFFFFF; + outbuffer[0] = crc; + outbuffer[1] = crc >> 8; + outbuffer[2] = crc >> 16; + outbuffer[3] = crc >> 24; +} + +void fillcrc32(unsigned char *buffer, unsigned int size) { + uint32_t crc = 0xFFFFFFFF; + unsigned int i; + size -= 4; + for (i = 0; i < size; i++) { + crc = crc32_table[(crc ^ buffer[i]) & 0xff] ^ (crc >> 8); + } + buffer += size; + buffer[0] = crc; + buffer[1] = crc >> 8; + buffer[2] = crc >> 16; + buffer[3] = crc >> 24; +} + +void adler32_short(unsigned char *buffer, unsigned int size, uint32_t *a, uint32_t *b) { + for (int i = 0; i < size; i++) { + *a += buffer[i]; + *b += *a; + } + *a %= 65521; + *b %= 65521; +} + +#define NMAX 5552 +uint32_t adler32(unsigned char *buffer, unsigned int size) { + uint32_t a = 1; + uint32_t b = 0; + while ( size >= NMAX ) { + adler32_short(buffer, NMAX, &a, &b); + buffer += NMAX; + size -= NMAX; + } + adler32_short(buffer, size, &a, &b); + return (b << 16) + a; +} +#undef NMAX + +void filladler32(unsigned char *buffer, unsigned int size) { + size -= 4; + uint32_t checksum = adler32(buffer, size); + buffer += size; + buffer[0] = checksum; + buffer[1] = checksum >> 8; + buffer[2] = checksum >> 16; + buffer[3] = checksum >> 24; +} + +int checkadler32(unsigned char *buffer, unsigned int size) { + size -= 4; + uint32_t checksum = adler32(buffer, size); + buffer += size; + return checksum == (((uint32_t)buffer[3] << 24) + | ((uint32_t)buffer[2] << 16) + | ((uint32_t)buffer[1] << 8) + | (uint32_t)buffer[0]); +} + diff --git a/server/encrypt.c b/server/encrypt.c new file mode 100644 index 0000000..37dd5cd --- /dev/null +++ b/server/encrypt.c @@ -0,0 +1,1645 @@ +/* + * encrypt.c - Manage the global encryptor + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(USE_CRYPTO_OPENSSL) + +#include +#include +#include +#include + +#elif defined(USE_CRYPTO_POLARSSL) + +#include +#include +#include +#include +#include +#include +#define CIPHER_UNSUPPORTED "unsupported" + +#include +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +#elif defined(USE_CRYPTO_MBEDTLS) + +#include +#include +#include +#include +#include +#define CIPHER_UNSUPPORTED "unsupported" + +#include +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +#endif + +#include + +#ifndef __MINGW32__ +#include +#endif + +#include "cache.h" +#include "encrypt.h" +#include "utils.h" + +#define OFFSET_ROL(p, o) ((uint64_t)(*(p + o)) << (8 * o)) + +static uint8_t *enc_table; +static uint8_t *dec_table; +static uint8_t enc_key[MAX_KEY_LENGTH]; +static int enc_key_len; +static int enc_iv_len; +static int enc_method; + +static struct cache *iv_cache; + +#ifdef DEBUG +static void +dump(char *tag, char *text, int len) +{ + int i; + printf("%s: ", tag); + for (i = 0; i < len; i++) + printf("0x%02x ", (uint8_t)text[i]); + printf("\n"); +} + +#endif + +static const char *supported_ciphers[CIPHER_NUM] = { + "table", + "rc4", + "rc4-md5-6", + "rc4-md5", + "aes-128-cfb", + "aes-192-cfb", + "aes-256-cfb", + "aes-128-ctr", + "aes-192-ctr", + "aes-256-ctr", + "bf-cfb", + "camellia-128-cfb", + "camellia-192-cfb", + "camellia-256-cfb", + "cast5-cfb", + "des-cfb", + "idea-cfb", + "rc2-cfb", + "seed-cfb", + "salsa20", + "chacha20", + "chacha20-ietf" +}; + +#ifdef USE_CRYPTO_POLARSSL +static const char *supported_ciphers_polarssl[CIPHER_NUM] = { + "table", + "ARC4-128", + "ARC4-128", + "ARC4-128", + "AES-128-CFB128", + "AES-192-CFB128", + "AES-256-CFB128", + "AES-128-CTR", + "AES-192-CTR", + "AES-256-CTR", + "BLOWFISH-CFB64", + "CAMELLIA-128-CFB128", + "CAMELLIA-192-CFB128", + "CAMELLIA-256-CFB128", + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + "salsa20", + "chacha20", + "chacha20-ietf" +}; +#endif + +#ifdef USE_CRYPTO_MBEDTLS +static const char *supported_ciphers_mbedtls[CIPHER_NUM] = { + "table", + "ARC4-128", + "ARC4-128", + "ARC4-128", + "AES-128-CFB128", + "AES-192-CFB128", + "AES-256-CFB128", + "AES-128-CTR", + "AES-192-CTR", + "AES-256-CTR", + "BLOWFISH-CFB64", + "CAMELLIA-128-CFB128", + "CAMELLIA-192-CFB128", + "CAMELLIA-256-CFB128", + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + "salsa20", + "chacha20", + "chacha20-ietf" +}; +#endif + +#ifdef USE_CRYPTO_APPLECC +static const CCAlgorithm supported_ciphers_applecc[CIPHER_NUM] = { + kCCAlgorithmInvalid, + kCCAlgorithmRC4, + kCCAlgorithmRC4, + kCCAlgorithmRC4, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmBlowfish, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmCAST, + kCCAlgorithmDES, + kCCAlgorithmInvalid, + kCCAlgorithmRC2, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid +}; + +static const CCMode supported_modes_applecc[CIPHER_NUM] = { + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCModeRC4, + kCCModeRC4, + kCCModeCFB, + kCCModeCFB, + kCCModeCFB, + kCCModeCTR, + kCCModeCTR, + kCCModeCTR, + kCCModeCFB, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCModeCFB, + kCCModeCFB, + kCCModeCFB, + kCCModeCFB, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid +}; +#endif + +static const int supported_ciphers_iv_size[CIPHER_NUM] = { + 0, 0, 6, 16, 16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 8, 8, 8, 8, 16, 8, 8, 12 +}; + +static const int supported_ciphers_key_size[CIPHER_NUM] = { + 0, 16, 16, 16, 16, 24, 32, 16, 24, 32, 16, 16, 24, 32, 16, 8, 16, 16, 16, 32, 32, 32 +}; + +static int +safe_memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *_s1 = (const unsigned char *)s1; + const unsigned char *_s2 = (const unsigned char *)s2; + int ret = 0; + size_t i; + for (i = 0; i < n; i++) + ret |= _s1[i] ^ _s2[i]; + return !!ret; +} + +int +balloc(buffer_t *ptr, size_t capacity) +{ + sodium_memzero(ptr, sizeof(buffer_t)); + ptr->array = ss_malloc(capacity); + ptr->capacity = capacity; + return capacity; +} + +int +brealloc(buffer_t *ptr, size_t len, size_t capacity) +{ + if (ptr == NULL) + return -1; + size_t real_capacity = max(len, capacity); + if (ptr->capacity < real_capacity) { + ptr->array = ss_realloc(ptr->array, real_capacity); + ptr->capacity = real_capacity; + } + return real_capacity; +} + +void +bfree(buffer_t *ptr) +{ + if (ptr == NULL) + return; + ptr->idx = 0; + ptr->len = 0; + ptr->capacity = 0; + if (ptr->array != NULL) { + ss_free(ptr->array); + } +} + +static int +crypto_stream_xor_ic(uint8_t *c, const uint8_t *m, uint64_t mlen, + const uint8_t *n, uint64_t ic, const uint8_t *k, + int method) +{ + switch (method) { + case SALSA20: + return crypto_stream_salsa20_xor_ic(c, m, mlen, n, ic, k); + case CHACHA20: + return crypto_stream_chacha20_xor_ic(c, m, mlen, n, ic, k); + case CHACHA20IETF: + return crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, n, (uint32_t)ic, k); + } + // always return 0 + return 0; +} + +static int +random_compare(const void *_x, const void *_y, uint32_t i, + uint64_t a) +{ + uint8_t x = *((uint8_t *)_x); + uint8_t y = *((uint8_t *)_y); + return a % (x + i) - a % (y + i); +} + +static void +merge(uint8_t *left, int llength, uint8_t *right, + int rlength, uint32_t salt, uint64_t key) +{ + uint8_t *ltmp = (uint8_t *)malloc(llength * sizeof(uint8_t)); + uint8_t *rtmp = (uint8_t *)malloc(rlength * sizeof(uint8_t)); + + uint8_t *ll = ltmp; + uint8_t *rr = rtmp; + + uint8_t *result = left; + + memcpy(ltmp, left, llength * sizeof(uint8_t)); + memcpy(rtmp, right, rlength * sizeof(uint8_t)); + + while (llength > 0 && rlength > 0) { + if (random_compare(ll, rr, salt, key) <= 0) { + *result = *ll; + ++ll; + --llength; + } else { + *result = *rr; + ++rr; + --rlength; + } + ++result; + } + + if (llength > 0) { + while (llength > 0) { + *result = *ll; + ++result; + ++ll; + --llength; + } + } else { + while (rlength > 0) { + *result = *rr; + ++result; + ++rr; + --rlength; + } + } + + ss_free(ltmp); + ss_free(rtmp); +} + +static void +merge_sort(uint8_t array[], int length, + uint32_t salt, uint64_t key) +{ + uint8_t middle; + uint8_t *left, *right; + int llength; + + if (length <= 1) { + return; + } + + middle = length / 2; + + llength = length - middle; + + left = array; + right = array + llength; + + merge_sort(left, llength, salt, key); + merge_sort(right, middle, salt, key); + merge(left, llength, right, middle, salt, key); +} + +int +enc_get_iv_len() +{ + return enc_iv_len; +} + +uint8_t* enc_get_key() +{ + return enc_key; +} + +int enc_get_key_len() +{ + return enc_key_len; +} + +unsigned char *enc_md5(const unsigned char *d, size_t n, unsigned char *md) +{ +#if defined(USE_CRYPTO_OPENSSL) + return MD5(d, n, md); +#elif defined(USE_CRYPTO_POLARSSL) + static unsigned char m[16]; + if (md == NULL) { + md = m; + } + md5(d, n, md); + return md; +#elif defined(USE_CRYPTO_MBEDTLS) + static unsigned char m[16]; + if (md == NULL) { + md = m; + } + mbedtls_md5(d, n, md); + return md; +#endif +} + +void +enc_table_init(const char *pass) +{ + uint32_t i; + uint64_t key = 0; + uint8_t *digest; + + enc_table = ss_malloc(256); + dec_table = ss_malloc(256); + + digest = enc_md5((const uint8_t *)pass, strlen(pass), NULL); + + for (i = 0; i < 8; i++) + key += OFFSET_ROL(digest, i); + + for (i = 0; i < 256; ++i) + enc_table[i] = i; + for (i = 1; i < 1024; ++i) + merge_sort(enc_table, 256, i, key); + for (i = 0; i < 256; ++i) + // gen decrypt table from encrypt table + dec_table[enc_table[i]] = i; +} + +int +cipher_iv_size(const cipher_t *cipher) +{ +#if defined(USE_CRYPTO_OPENSSL) + if (cipher->info == NULL) + return cipher->iv_len; + else + return EVP_CIPHER_iv_length(cipher->info); +#elif defined(USE_CRYPTO_POLARSSL) || defined(USE_CRYPTO_MBEDTLS) + if (cipher == NULL) { + return 0; + } + return cipher->info->iv_size; +#endif +} + +int +cipher_key_size(const cipher_t *cipher) +{ +#if defined(USE_CRYPTO_OPENSSL) + if (cipher->info == NULL) + return cipher->key_len; + else + return EVP_CIPHER_key_length(cipher->info); +#elif defined(USE_CRYPTO_POLARSSL) + if (cipher == NULL) { + return 0; + } + /* Override PolarSSL 32 bit default key size with sane 128 bit default */ + if (cipher->info->base != NULL && POLARSSL_CIPHER_ID_BLOWFISH == + cipher->info->base->cipher) { + return 128 / 8; + } + return cipher->info->key_length / 8; +#elif defined(USE_CRYPTO_MBEDTLS) + /* + * Semi-API changes (technically public, morally private) + * Renamed a few headers to include _internal in the name. Those headers are + * not supposed to be included by users. + * Changed md_info_t into an opaque structure (use md_get_xxx() accessors). + * Changed pk_info_t into an opaque structure. + * Changed cipher_base_t into an opaque structure. + */ + if (cipher == NULL) { + return 0; + } + /* From Version 1.2.7 released 2013-04-13 Default Blowfish keysize is now 128-bits */ + return cipher->info->key_bitlen / 8; +#endif +} + +void +bytes_to_key_with_size(const char *pass, size_t len, uint8_t *md, size_t md_size) +{ + uint8_t result[128]; + enc_md5((const unsigned char *)pass, len, result); + memcpy(md, result, 16); + int i = 16; + for (; i < md_size; i += 16) { + memcpy(result + 16, pass, len); + enc_md5(result, 16 + len, result); + memcpy(md + i, result, 16); + } +} + +int +bytes_to_key(const cipher_t *cipher, const digest_type_t *md, + const uint8_t *pass, uint8_t *key) +{ + size_t datal; + datal = strlen((const char *)pass); + +#if defined(USE_CRYPTO_OPENSSL) + + MD5_CTX c; + unsigned char md_buf[MAX_MD_SIZE]; + int nkey; + int addmd; + unsigned int i, j, mds; + + mds = 16; + nkey = cipher_key_size(cipher); + if (pass == NULL) + return nkey; + memset(&c, 0, sizeof(MD5_CTX)); + + for (j = 0, addmd = 0; j < nkey; addmd++) { + MD5_Init(&c); + if (addmd) { + MD5_Update(&c, md_buf, mds); + } + MD5_Update(&c, pass, datal); + MD5_Final(md_buf, &c); + + for (i = 0; i < mds; i++, j++) { + if (j >= nkey) + break; + key[j] = md_buf[i]; + } + } + + return nkey; + +#elif defined(USE_CRYPTO_POLARSSL) + md_context_t c; + unsigned char md_buf[MAX_MD_SIZE]; + int nkey; + int addmd; + unsigned int i, j, mds; + + nkey = cipher_key_size(cipher); + mds = md_get_size(md); + memset(&c, 0, sizeof(md_context_t)); + + if (pass == NULL) + return nkey; + if (md_init_ctx(&c, md)) + return 0; + + for (j = 0, addmd = 0; j < nkey; addmd++) { + md_starts(&c); + if (addmd) { + md_update(&c, md_buf, mds); + } + md_update(&c, pass, datal); + md_finish(&c, md_buf); + + for (i = 0; i < mds; i++, j++) { + if (j >= nkey) + break; + key[j] = md_buf[i]; + } + } + + md_free_ctx(&c); + return nkey; + +#elif defined(USE_CRYPTO_MBEDTLS) + + mbedtls_md_context_t c; + unsigned char md_buf[MAX_MD_SIZE]; + int nkey; + int addmd; + unsigned int i, j, mds; + + nkey = cipher_key_size(cipher); + mds = mbedtls_md_get_size(md); + memset(&c, 0, sizeof(mbedtls_md_context_t)); + + if (pass == NULL) + return nkey; + if (mbedtls_md_setup(&c, md, 1)) + return 0; + + for (j = 0, addmd = 0; j < nkey; addmd++) { + mbedtls_md_starts(&c); + if (addmd) { + mbedtls_md_update(&c, md_buf, mds); + } + mbedtls_md_update(&c, pass, datal); + mbedtls_md_finish(&c, &(md_buf[0])); + + for (i = 0; i < mds; i++, j++) { + if (j >= nkey) + break; + key[j] = md_buf[i]; + } + } + + mbedtls_md_free(&c); + return nkey; +#endif +} + +int +rand_bytes(uint8_t *output, int len) +{ + randombytes_buf(output, len); + // always return success + return 0; +} + +const cipher_kt_t * +get_cipher_type(int method) +{ + if (method <= TABLE || method >= CIPHER_NUM) { + LOGE("get_cipher_type(): Illegal method"); + return NULL; + } + + if (method == RC4_MD5 || method == RC4_MD5_6) { + method = RC4; + } + + if (method >= SALSA20) { + return NULL; + } + + const char *ciphername = supported_ciphers[method]; +#if defined(USE_CRYPTO_OPENSSL) + return EVP_get_cipherbyname(ciphername); +#elif defined(USE_CRYPTO_POLARSSL) + const char *polarname = supported_ciphers_polarssl[method]; + if (strcmp(polarname, CIPHER_UNSUPPORTED) == 0) { + LOGE("Cipher %s currently is not supported by PolarSSL library", + ciphername); + return NULL; + } + return cipher_info_from_string(polarname); +#elif defined(USE_CRYPTO_MBEDTLS) + const char *mbedtlsname = supported_ciphers_mbedtls[method]; + if (strcmp(mbedtlsname, CIPHER_UNSUPPORTED) == 0) { + LOGE("Cipher %s currently is not supported by mbed TLS library", + ciphername); + return NULL; + } + return mbedtls_cipher_info_from_string(mbedtlsname); +#endif +} + +const digest_type_t * +get_digest_type(const char *digest) +{ + if (digest == NULL) { + LOGE("get_digest_type(): Digest name is null"); + return NULL; + } + +#if defined(USE_CRYPTO_OPENSSL) + return EVP_get_digestbyname(digest); +#elif defined(USE_CRYPTO_POLARSSL) + return md_info_from_string(digest); +#elif defined(USE_CRYPTO_MBEDTLS) + return mbedtls_md_info_from_string(digest); +#endif +} + +void +cipher_context_init(cipher_ctx_t *ctx, int method, int enc) +{ + if (method <= TABLE || method >= CIPHER_NUM) { + LOGE("cipher_context_init(): Illegal method"); + return; + } + + if (method >= SALSA20) { + enc_iv_len = supported_ciphers_iv_size[method]; + return; + } + + const char *ciphername = supported_ciphers[method]; +#if defined(USE_CRYPTO_APPLECC) + cipher_cc_t *cc = &ctx->cc; + cc->cryptor = NULL; + cc->cipher = supported_ciphers_applecc[method]; + if (cc->cipher == kCCAlgorithmInvalid) { + cc->valid = kCCContextInvalid; + } else { + cc->valid = kCCContextValid; + if (cc->cipher == kCCAlgorithmRC4) { + cc->mode = supported_modes_applecc[method]; + cc->padding = ccNoPadding; + } else { + cc->mode = supported_modes_applecc[method]; + if (cc->mode == kCCModeCTR) { + cc->padding = ccNoPadding; + } else { + cc->padding = ccPKCS7Padding; + } + } + return; + } +#endif + + const cipher_kt_t *cipher = get_cipher_type(method); + +#if defined(USE_CRYPTO_OPENSSL) + ctx->evp = EVP_CIPHER_CTX_new(); + cipher_evp_t *evp = ctx->evp; + + if (cipher == NULL) { + LOGE("Cipher %s not found in OpenSSL library", ciphername); + FATAL("Cannot initialize cipher"); + } + if (!EVP_CipherInit_ex(evp, cipher, NULL, NULL, NULL, enc)) { + LOGE("Cannot initialize cipher %s", ciphername); + exit(EXIT_FAILURE); + } + if (!EVP_CIPHER_CTX_set_key_length(evp, enc_key_len)) { + EVP_CIPHER_CTX_cleanup(evp); + LOGE("Invalid key length: %d", enc_key_len); + exit(EXIT_FAILURE); + } + if (method > RC4_MD5) { + EVP_CIPHER_CTX_set_padding(evp, 1); + } +#elif defined(USE_CRYPTO_POLARSSL) + ctx->evp = (cipher_evp_t *)ss_malloc(sizeof(cipher_evp_t)); + cipher_evp_t *evp = ctx->evp; + + if (cipher == NULL) { + LOGE("Cipher %s not found in PolarSSL library", ciphername); + FATAL("Cannot initialize PolarSSL cipher"); + } + if (cipher_init_ctx(evp, cipher) != 0) { + FATAL("Cannot initialize PolarSSL cipher context"); + } +#elif defined(USE_CRYPTO_MBEDTLS) + ctx->evp = (cipher_evp_t *)ss_malloc(sizeof(cipher_evp_t)); + cipher_evp_t *evp = ctx->evp; + + if (cipher == NULL) { + LOGE("Cipher %s not found in mbed TLS library", ciphername); + FATAL("Cannot initialize mbed TLS cipher"); + } + mbedtls_cipher_init(evp); + if (mbedtls_cipher_setup(evp, cipher) != 0) { + FATAL("Cannot initialize mbed TLS cipher context"); + } +#endif +} + +void +cipher_context_set_iv(cipher_ctx_t *ctx, uint8_t *iv, size_t iv_len, + int enc) +{ + const unsigned char *true_key; + + if (iv == NULL) { + LOGE("cipher_context_set_iv(): IV is null"); + return; + } + + if (!enc) { + memcpy(ctx->iv, iv, iv_len); + } + + if (enc_method >= SALSA20) { + return; + } + + if (enc_method == RC4_MD5 || enc_method == RC4_MD5_6) { + unsigned char key_iv[32]; + memcpy(key_iv, enc_key, 16); + memcpy(key_iv + 16, iv, iv_len); + true_key = enc_md5(key_iv, 16 + iv_len, NULL); + iv_len = 0; + } else { + true_key = enc_key; + } + +#ifdef USE_CRYPTO_APPLECC + cipher_cc_t *cc = &ctx->cc; + if (cc->valid == kCCContextValid) { + memcpy(cc->iv, iv, iv_len); + memcpy(cc->key, true_key, enc_key_len); + cc->iv_len = iv_len; + cc->key_len = enc_key_len; + cc->encrypt = enc ? kCCEncrypt : kCCDecrypt; + if (cc->cryptor != NULL) { + CCCryptorRelease(cc->cryptor); + cc->cryptor = NULL; + } + + CCCryptorStatus ret; + ret = CCCryptorCreateWithMode( + cc->encrypt, + cc->mode, + cc->cipher, + cc->padding, + cc->iv, cc->key, cc->key_len, + NULL, 0, 0, kCCModeOptionCTR_BE, + &cc->cryptor); + if (ret != kCCSuccess) { + if (cc->cryptor != NULL) { + CCCryptorRelease(cc->cryptor); + cc->cryptor = NULL; + } + FATAL("Cannot set CommonCrypto key and IV"); + } + return; + } +#endif + + cipher_evp_t *evp = ctx->evp; + if (evp == NULL) { + LOGE("cipher_context_set_iv(): Cipher context is null"); + return; + } +#if defined(USE_CRYPTO_OPENSSL) + if (!EVP_CipherInit_ex(evp, NULL, NULL, true_key, iv, enc)) { + EVP_CIPHER_CTX_cleanup(evp); + FATAL("Cannot set key and IV"); + } +#elif defined(USE_CRYPTO_POLARSSL) + // XXX: PolarSSL 1.3.11: cipher_free_ctx deprecated, Use cipher_free() instead. + if (cipher_setkey(evp, true_key, enc_key_len * 8, enc) != 0) { + cipher_free_ctx(evp); + FATAL("Cannot set PolarSSL cipher key"); + } +#if POLARSSL_VERSION_NUMBER >= 0x01030000 + if (cipher_set_iv(evp, iv, iv_len) != 0) { + cipher_free_ctx(evp); + FATAL("Cannot set PolarSSL cipher IV"); + } + if (cipher_reset(evp) != 0) { + cipher_free_ctx(evp); + FATAL("Cannot finalize PolarSSL cipher context"); + } +#else + if (cipher_reset(evp, iv) != 0) { + cipher_free_ctx(evp); + FATAL("Cannot set PolarSSL cipher IV"); + } +#endif +#elif defined(USE_CRYPTO_MBEDTLS) + if (mbedtls_cipher_setkey(evp, true_key, enc_key_len * 8, enc) != 0) { + mbedtls_cipher_free(evp); + FATAL("Cannot set mbed TLS cipher key"); + } + + if (mbedtls_cipher_set_iv(evp, iv, iv_len) != 0) { + mbedtls_cipher_free(evp); + FATAL("Cannot set mbed TLS cipher IV"); + } + if (mbedtls_cipher_reset(evp) != 0) { + mbedtls_cipher_free(evp); + FATAL("Cannot finalize mbed TLS cipher context"); + } +#endif + +#ifdef DEBUG + dump("IV", (char *)iv, iv_len); +#endif +} + +void +cipher_context_release(cipher_ctx_t *ctx) +{ + if (enc_method >= SALSA20) { + return; + } + +#ifdef USE_CRYPTO_APPLECC + cipher_cc_t *cc = &ctx->cc; + if (cc->cryptor != NULL) { + CCCryptorRelease(cc->cryptor); + cc->cryptor = NULL; + } + if (cc->valid == kCCContextValid) { + return; + } +#endif + +#if defined(USE_CRYPTO_OPENSSL) + EVP_CIPHER_CTX_free(ctx->evp); +#elif defined(USE_CRYPTO_POLARSSL) +// NOTE: cipher_free_ctx deprecated in PolarSSL 1.3.11 + cipher_free_ctx(ctx->evp); + ss_free(ctx->evp); +#elif defined(USE_CRYPTO_MBEDTLS) +// NOTE: cipher_free_ctx deprecated + mbedtls_cipher_free(ctx->evp); + ss_free(ctx->evp); +#endif +} + +static int +cipher_context_update(cipher_ctx_t *ctx, uint8_t *output, size_t *olen, + const uint8_t *input, size_t ilen) +{ +#ifdef USE_CRYPTO_APPLECC + cipher_cc_t *cc = &ctx->cc; + if (cc->valid == kCCContextValid) { + CCCryptorStatus ret; + ret = CCCryptorUpdate(cc->cryptor, input, ilen, output, + ilen, olen); + return (ret == kCCSuccess) ? 1 : 0; + } +#endif + cipher_evp_t *evp = ctx->evp; +#if defined(USE_CRYPTO_OPENSSL) + int err = 0, tlen = *olen; + err = EVP_CipherUpdate(evp, (uint8_t *)output, &tlen, + (const uint8_t *)input, ilen); + *olen = tlen; + return err; +#elif defined(USE_CRYPTO_POLARSSL) + return !cipher_update(evp, (const uint8_t *)input, ilen, + (uint8_t *)output, olen); +#elif defined(USE_CRYPTO_MBEDTLS) + return !mbedtls_cipher_update(evp, (const uint8_t *)input, ilen, + (uint8_t *)output, olen); +#endif +} +int ss_md5_hmac(char *auth, char *msg, int msg_len, uint8_t *iv) +{ + uint8_t hash[MD5_BYTES]; + uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; + memcpy(auth_key, iv, enc_iv_len); + memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); + +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_md5(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#else + md5_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#endif + + memcpy(auth, hash, MD5_BYTES); + + return 0; +} + +int ss_md5_hmac_with_key(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len) +{ + uint8_t hash[MD5_BYTES]; + +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_md5(), auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#else + md5_hmac(auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#endif + + memcpy(auth, hash, MD5_BYTES); + + return 0; +} + +int ss_md5_hash_func(char *auth, char *msg, int msg_len) +{ + uint8_t hash[MD5_BYTES]; + +#if defined(USE_CRYPTO_OPENSSL) + MD5((uint8_t *)msg, msg_len, (uint8_t *)hash); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), (uint8_t *)msg, msg_len, (uint8_t *)hash); +#else + md5((uint8_t *)msg, msg_len, (uint8_t *)hash); +#endif + + memcpy(auth, hash, MD5_BYTES); + + return 0; +} + +int ss_sha1_hmac(char *auth, char *msg, int msg_len, uint8_t *iv) +{ + uint8_t hash[SHA1_BYTES]; + uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; + memcpy(auth_key, iv, enc_iv_len); + memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); + +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_sha1(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#else + sha1_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#endif + + memcpy(auth, hash, SHA1_BYTES); + + return 0; +} + +int ss_sha1_hmac_with_key(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len) +{ + uint8_t hash[SHA1_BYTES]; + +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_sha1(), auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#else + sha1_hmac(auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#endif + + memcpy(auth, hash, SHA1_BYTES); + + return 0; +} + +int ss_sha1_hash_func(char *auth, char *msg, int msg_len) +{ + uint8_t hash[SHA1_BYTES]; +#if defined(USE_CRYPTO_OPENSSL) + SHA1((uint8_t *)msg, msg_len, (uint8_t *)hash); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), (uint8_t *)msg, msg_len, (uint8_t *)hash); +#else + sha1((uint8_t *)msg, msg_len, (uint8_t *)hash); +#endif + + memcpy(auth, hash, SHA1_BYTES); + + return 0; +} + +int ss_aes_128_cbc(char *encrypt, char *out_data, char *key) +{ + unsigned char iv[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +#if defined(USE_CRYPTO_OPENSSL) + AES_KEY aes; + AES_set_encrypt_key((unsigned char*)key, 128, &aes); + AES_cbc_encrypt((const unsigned char *)encrypt, (unsigned char *)out_data, 16, &aes, iv, AES_ENCRYPT); + +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_aes_context aes; + + unsigned char output[16]; + + mbedtls_aes_setkey_enc( &aes, (unsigned char *)key, 128 ); + mbedtls_aes_crypt_cbc( &aes, MBEDTLS_AES_ENCRYPT, 16, iv, (unsigned char *)encrypt, output ); + + memcpy(out_data, output, 16); +#else + + aes_context aes; + + unsigned char output[16]; + + aes_setkey_enc( &aes, (unsigned char *)key, 128 ); + aes_crypt_cbc( &aes, AES_ENCRYPT, 16, iv, (unsigned char *)encrypt, output ); + + memcpy(out_data, output, 16); +#endif + + return 0; +} + +int ss_onetimeauth(buffer_t *buf, uint8_t *iv, size_t capacity) +{ + uint8_t hash[ONETIMEAUTH_BYTES * 2]; + uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; + memcpy(auth_key, iv, enc_iv_len); + memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); + + brealloc(buf, ONETIMEAUTH_BYTES + buf->len, capacity); + +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_sha1(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, buf->len, (uint8_t *)hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type( + MBEDTLS_MD_SHA1), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, buf->len, + (uint8_t *)hash); +#else + sha1_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, buf->len, (uint8_t *)hash); +#endif + + memcpy(buf->array + buf->len, hash, ONETIMEAUTH_BYTES); + buf->len += ONETIMEAUTH_BYTES; + + return 0; +} + +int +ss_onetimeauth_verify(buffer_t *buf, uint8_t *iv) +{ + uint8_t hash[ONETIMEAUTH_BYTES * 2]; + uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; + memcpy(auth_key, iv, enc_iv_len); + memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); + size_t len = buf->len - ONETIMEAUTH_BYTES; + +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_sha1(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, len, hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type( + MBEDTLS_MD_SHA1), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, len, hash); +#else + sha1_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, len, hash); +#endif + + return safe_memcmp(buf->array + len, hash, ONETIMEAUTH_BYTES); +} + +int +ss_encrypt_all(buffer_t *plain, int method, int auth, size_t capacity) +{ + if (method > TABLE) { + cipher_ctx_t evp; + cipher_context_init(&evp, method, 1); + + size_t iv_len = enc_iv_len; + int err = 1; + + static buffer_t tmp = { 0, 0, 0, NULL }; + brealloc(&tmp, iv_len + plain->len, capacity); + buffer_t *cipher = &tmp; + cipher->len = plain->len; + + uint8_t iv[MAX_IV_LENGTH]; + + rand_bytes(iv, iv_len); + cipher_context_set_iv(&evp, iv, iv_len, 1); + memcpy(cipher->array, iv, iv_len); + + if (auth) { + ss_onetimeauth(plain, iv, capacity); + cipher->len = plain->len; + } + + if (method >= SALSA20) { + crypto_stream_xor_ic((uint8_t *)(cipher->array + iv_len), + (const uint8_t *)plain->array, (uint64_t)(plain->len), + (const uint8_t *)iv, + 0, enc_key, method); + } else { + err = cipher_context_update(&evp, (uint8_t *)(cipher->array + iv_len), + &cipher->len, (const uint8_t *)plain->array, + plain->len); + } + + if (!err) { + bfree(plain); + cipher_context_release(&evp); + return -1; + } + +#ifdef DEBUG + dump("PLAIN", plain->array, plain->len); + dump("CIPHER", cipher->array + iv_len, cipher->len); +#endif + + cipher_context_release(&evp); + + brealloc(plain, iv_len + cipher->len, capacity); + memcpy(plain->array, cipher->array, iv_len + cipher->len); + plain->len = iv_len + cipher->len; + + return 0; + } else { + char *begin = plain->array; + char *ptr = plain->array; + while (ptr < begin + plain->len) { + *ptr = (char)enc_table[(uint8_t)*ptr]; + ptr++; + } + return 0; + } +} + +int +ss_encrypt(buffer_t *plain, enc_ctx_t *ctx, size_t capacity) +{ + if (ctx != NULL) { + static buffer_t tmp = { 0, 0, 0, NULL }; + + int err = 1; + size_t iv_len = 0; + if (!ctx->init) { + iv_len = enc_iv_len; + } + + brealloc(&tmp, iv_len + plain->len, capacity); + buffer_t *cipher = &tmp; + cipher->len = plain->len; + + if (!ctx->init) { + cipher_context_set_iv(&ctx->evp, ctx->evp.iv, iv_len, 1); + memcpy(cipher->array, ctx->evp.iv, iv_len); + ctx->counter = 0; + ctx->init = 1; + } + + if (enc_method >= SALSA20) { + int padding = ctx->counter % SODIUM_BLOCK_SIZE; + brealloc(cipher, iv_len + (padding + cipher->len) * 2, capacity); + if (padding) { + brealloc(plain, plain->len + padding, capacity); + memmove(plain->array + padding, plain->array, plain->len); + sodium_memzero(plain->array, padding); + } + crypto_stream_xor_ic((uint8_t *)(cipher->array + iv_len), + (const uint8_t *)plain->array, + (uint64_t)(plain->len + padding), + (const uint8_t *)ctx->evp.iv, + ctx->counter / SODIUM_BLOCK_SIZE, enc_key, + enc_method); + ctx->counter += plain->len; + if (padding) { + memmove(cipher->array + iv_len, + cipher->array + iv_len + padding, cipher->len); + } + } else { + err = + cipher_context_update(&ctx->evp, + (uint8_t *)(cipher->array + iv_len), + &cipher->len, (const uint8_t *)plain->array, + plain->len); + if (!err) { + return -1; + } + } + +#ifdef DEBUG + dump("PLAIN", plain->array, plain->len); + dump("CIPHER", cipher->array + iv_len, cipher->len); +#endif + + brealloc(plain, iv_len + cipher->len, capacity); + memcpy(plain->array, cipher->array, iv_len + cipher->len); + plain->len = iv_len + cipher->len; + + return 0; + } else { + char *begin = plain->array; + char *ptr = plain->array; + while (ptr < begin + plain->len) { + *ptr = (char)enc_table[(uint8_t)*ptr]; + ptr++; + } + return 0; + } +} + +int +ss_decrypt_all(buffer_t *cipher, int method, int auth, size_t capacity) +{ + if (method > TABLE) { + size_t iv_len = enc_iv_len; + int ret = 1; + + if (cipher->len <= iv_len) { + return -1; + } + + cipher_ctx_t evp; + cipher_context_init(&evp, method, 0); + + static buffer_t tmp = { 0, 0, 0, NULL }; + brealloc(&tmp, cipher->len, capacity); + buffer_t *plain = &tmp; + plain->len = cipher->len - iv_len; + + uint8_t iv[MAX_IV_LENGTH]; + memcpy(iv, cipher->array, iv_len); + cipher_context_set_iv(&evp, iv, iv_len, 0); + + if (method >= SALSA20) { + crypto_stream_xor_ic((uint8_t *)plain->array, + (const uint8_t *)(cipher->array + iv_len), + (uint64_t)(cipher->len - iv_len), + (const uint8_t *)iv, 0, enc_key, method); + } else { + ret = cipher_context_update(&evp, (uint8_t *)plain->array, &plain->len, + (const uint8_t *)(cipher->array + iv_len), + cipher->len - iv_len); + } + + if (auth || (plain->array[0] & ONETIMEAUTH_FLAG)) { + if (plain->len > ONETIMEAUTH_BYTES) { + ret = !ss_onetimeauth_verify(plain, iv); + if (ret) { + plain->len -= ONETIMEAUTH_BYTES; + } + } else { + ret = 0; + } + } + + if (!ret) { + bfree(cipher); + cipher_context_release(&evp); + return -1; + } + +#ifdef DEBUG + dump("PLAIN", plain->array, plain->len); + dump("CIPHER", cipher->array + iv_len, cipher->len - iv_len); +#endif + + cipher_context_release(&evp); + + brealloc(cipher, plain->len, capacity); + memcpy(cipher->array, plain->array, plain->len); + cipher->len = plain->len; + + return 0; + } else { + char *begin = cipher->array; + char *ptr = cipher->array; + while (ptr < begin + cipher->len) { + *ptr = (char)dec_table[(uint8_t)*ptr]; + ptr++; + } + return 0; + } +} + +int +ss_decrypt(buffer_t *cipher, enc_ctx_t *ctx, size_t capacity) +{ + if (ctx != NULL) { + static buffer_t tmp = { 0, 0, 0, NULL }; + + size_t iv_len = 0; + int err = 1; + + brealloc(&tmp, cipher->len, capacity); + buffer_t *plain = &tmp; + plain->len = cipher->len; + + if (!ctx->init) { + uint8_t iv[MAX_IV_LENGTH]; + iv_len = enc_iv_len; + plain->len -= iv_len; + + memcpy(iv, cipher->array, iv_len); + cipher_context_set_iv(&ctx->evp, iv, iv_len, 0); + ctx->counter = 0; + ctx->init = 1; + + if (enc_method > RC4) { + if (cache_key_exist(iv_cache, (char *)iv, iv_len)) { + bfree(cipher); + return -1; + } else { + cache_insert(iv_cache, (char *)iv, iv_len, NULL); + } + } + } + + if (enc_method >= SALSA20) { + int padding = ctx->counter % SODIUM_BLOCK_SIZE; + brealloc(plain, (plain->len + padding) * 2, capacity); + + if (padding) { + brealloc(cipher, cipher->len + padding, capacity); + memmove(cipher->array + iv_len + padding, cipher->array + iv_len, + cipher->len - iv_len); + sodium_memzero(cipher->array + iv_len, padding); + } + crypto_stream_xor_ic((uint8_t *)plain->array, + (const uint8_t *)(cipher->array + iv_len), + (uint64_t)(cipher->len - iv_len + padding), + (const uint8_t *)ctx->evp.iv, + ctx->counter / SODIUM_BLOCK_SIZE, enc_key, + enc_method); + ctx->counter += cipher->len - iv_len; + if (padding) { + memmove(plain->array, plain->array + padding, plain->len); + } + } else { + err = cipher_context_update(&ctx->evp, (uint8_t *)plain->array, &plain->len, + (const uint8_t *)(cipher->array + iv_len), + cipher->len - iv_len); + } + + if (!err) { + bfree(cipher); + return -1; + } + +#ifdef DEBUG + dump("PLAIN", plain->array, plain->len); + dump("CIPHER", cipher->array + iv_len, cipher->len - iv_len); +#endif + + brealloc(cipher, plain->len, capacity); + memcpy(cipher->array, plain->array, plain->len); + cipher->len = plain->len; + + return 0; + } else { + char *begin = cipher->array; + char *ptr = cipher->array; + while (ptr < begin + cipher->len) { + *ptr = (char)dec_table[(uint8_t)*ptr]; + ptr++; + } + return 0; + } +} + +void +enc_ctx_init(int method, enc_ctx_t *ctx, int enc) +{ + sodium_memzero(ctx, sizeof(enc_ctx_t)); + cipher_context_init(&ctx->evp, method, enc); + + if (enc) { + rand_bytes(ctx->evp.iv, enc_iv_len); + } +} + +void +enc_key_init(int method, const char *pass) +{ + if (method <= TABLE || method >= CIPHER_NUM) { + LOGE("enc_key_init(): Illegal method"); + return; + } + + // Initialize cache + cache_create(&iv_cache, 256, NULL); + +#if defined(USE_CRYPTO_OPENSSL) + OpenSSL_add_all_algorithms(); +#else + cipher_kt_t cipher_info; +#endif + + cipher_t cipher; + memset(&cipher, 0, sizeof(cipher_t)); + + // Initialize sodium for random generator + if (sodium_init() == -1) { + FATAL("Failed to initialize sodium"); + } + + if (method == SALSA20 || method == CHACHA20 || method == CHACHA20IETF) { +#if defined(USE_CRYPTO_OPENSSL) + cipher.info = NULL; + cipher.key_len = supported_ciphers_key_size[method]; + cipher.iv_len = supported_ciphers_iv_size[method]; +#endif +#if defined(USE_CRYPTO_POLARSSL) + cipher.info = &cipher_info; + cipher.info->base = NULL; + cipher.info->key_length = supported_ciphers_key_size[method] * 8; + cipher.info->iv_size = supported_ciphers_iv_size[method]; +#endif +#if defined(USE_CRYPTO_MBEDTLS) + // XXX: key_length changed to key_bitlen in mbed TLS 2.0.0 + cipher.info = &cipher_info; + cipher.info->base = NULL; + cipher.info->key_bitlen = supported_ciphers_key_size[method] * 8; + cipher.info->iv_size = supported_ciphers_iv_size[method]; +#endif + } else { + cipher.info = (cipher_kt_t *)get_cipher_type(method); + } + + if (cipher.info == NULL && cipher.key_len == 0) { + do { +#if defined(USE_CRYPTO_POLARSSL) && defined(USE_CRYPTO_APPLECC) + if (supported_ciphers_applecc[method] != kCCAlgorithmInvalid) { + cipher_info.base = NULL; + cipher_info.key_length = supported_ciphers_key_size[method] * 8; + cipher_info.iv_size = supported_ciphers_iv_size[method]; + cipher.info = (cipher_kt_t *)&cipher_info; + break; + } +#endif +#if defined(USE_CRYPTO_MBEDTLS) && defined(USE_CRYPTO_APPLECC) + // XXX: key_length changed to key_bitlen in mbed TLS 2.0.0 + if (supported_ciphers_applecc[method] != kCCAlgorithmInvalid) { + cipher_info.base = NULL; + cipher_info.key_bitlen = supported_ciphers_key_size[method] * 8; + cipher_info.iv_size = supported_ciphers_iv_size[method]; + cipher.info = (cipher_kt_t *)&cipher_info; + break; + } +#endif + LOGE("Cipher %s not found in crypto library", supported_ciphers[method]); + FATAL("Cannot initialize cipher"); + } while (0); + } + + const digest_type_t *md = get_digest_type("MD5"); + if (md == NULL) { + FATAL("MD5 Digest not found in crypto library"); + } + + enc_key_len = bytes_to_key(&cipher, md, (const uint8_t *)pass, enc_key); + + if (enc_key_len == 0) { + FATAL("Cannot generate key and IV"); + } + if (method == RC4_MD5 || method == RC4_MD5_6) { + enc_iv_len = supported_ciphers_iv_size[method]; + } else { + enc_iv_len = cipher_iv_size(&cipher); + } + enc_method = method; +} + +int +enc_init(const char *pass, const char *method) +{ + int m = TABLE; + if (method != NULL) { + for (m = TABLE; m < CIPHER_NUM; m++) + if (strcmp(method, supported_ciphers[m]) == 0) { + break; + } + if (m >= CIPHER_NUM) { + LOGE("Invalid cipher name: %s, use rc4-md5 instead", method); + m = RC4_MD5; + } + } + if (m == TABLE) { + enc_table_init(pass); + } else { + enc_key_init(m, pass); + } + return m; +} + +int +ss_check_hash(buffer_t *buf, chunk_t *chunk, enc_ctx_t *ctx, size_t capacity) +{ + int i, j, k; + ssize_t blen = buf->len; + uint32_t cidx = chunk->idx; + + brealloc(chunk->buf, chunk->len + blen, capacity); + brealloc(buf, chunk->len + blen, capacity); + + for (i = 0, j = 0, k = 0; i < blen; i++) { + chunk->buf->array[cidx++] = buf->array[k++]; + + if (cidx == CLEN_BYTES) { + uint16_t clen = ntohs(*((uint16_t *)chunk->buf->array)); + brealloc(chunk->buf, clen + AUTH_BYTES, capacity); + chunk->len = clen; + } + + if (cidx == chunk->len + AUTH_BYTES) { + // Compare hash + uint8_t hash[ONETIMEAUTH_BYTES * 2]; + uint8_t key[MAX_IV_LENGTH + sizeof(uint32_t)]; + + uint32_t c = htonl(chunk->counter); + memcpy(key, ctx->evp.iv, enc_iv_len); + memcpy(key + enc_iv_len, &c, sizeof(uint32_t)); +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_sha1(), key, enc_iv_len + sizeof(uint32_t), + (uint8_t *)chunk->buf->array + AUTH_BYTES, chunk->len, hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), key, enc_iv_len + sizeof(uint32_t), + (uint8_t *)chunk->buf->array + AUTH_BYTES, chunk->len, hash); +#else + sha1_hmac(key, enc_iv_len + sizeof(uint32_t), + (uint8_t *)chunk->buf->array + AUTH_BYTES, chunk->len, hash); +#endif + + if (safe_memcmp(hash, chunk->buf->array + CLEN_BYTES, ONETIMEAUTH_BYTES) != 0) { + return 0; + } + + // Copy chunk back to buffer + memmove(buf->array + j + chunk->len, buf->array + k, blen - i - 1); + memcpy(buf->array + j, chunk->buf->array + AUTH_BYTES, chunk->len); + + // Reset the base offset + j += chunk->len; + k = j; + cidx = 0; + chunk->counter++; + } + } + + buf->len = j; + chunk->idx = cidx; + return 1; +} + +int +ss_gen_hash(buffer_t *buf, uint32_t *counter, enc_ctx_t *ctx, size_t capacity) +{ + ssize_t blen = buf->len; + uint16_t chunk_len = htons((uint16_t)blen); + uint8_t hash[ONETIMEAUTH_BYTES * 2]; + uint8_t key[MAX_IV_LENGTH + sizeof(uint32_t)]; + uint32_t c = htonl(*counter); + + brealloc(buf, AUTH_BYTES + blen, capacity); + memcpy(key, ctx->evp.iv, enc_iv_len); + memcpy(key + enc_iv_len, &c, sizeof(uint32_t)); +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_sha1(), key, enc_iv_len + sizeof(uint32_t), (uint8_t *)buf->array, blen, hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type( + MBEDTLS_MD_SHA1), key, enc_iv_len + sizeof(uint32_t), (uint8_t *)buf->array, blen, hash); +#else + sha1_hmac(key, enc_iv_len + sizeof(uint32_t), (uint8_t *)buf->array, blen, hash); +#endif + + memmove(buf->array + AUTH_BYTES, buf->array, blen); + memcpy(buf->array + CLEN_BYTES, hash, ONETIMEAUTH_BYTES); + memcpy(buf->array, &chunk_len, CLEN_BYTES); + + *counter = *counter + 1; + buf->len = blen + AUTH_BYTES; + + return 0; +} diff --git a/server/encrypt.h b/server/encrypt.h new file mode 100644 index 0000000..3bb7940 --- /dev/null +++ b/server/encrypt.h @@ -0,0 +1,222 @@ +/* + * encrypt.h - Define the enryptor's interface + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _ENCRYPT_H +#define _ENCRYPT_H + +#ifndef __MINGW32__ +#include +#else + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#endif + +#include +#include +#include +#include + +#if defined(USE_CRYPTO_OPENSSL) + +#include +#include +#include +typedef EVP_CIPHER cipher_kt_t; +typedef EVP_CIPHER_CTX cipher_evp_t; +typedef EVP_MD digest_type_t; +#define MAX_KEY_LENGTH EVP_MAX_KEY_LENGTH +#define MAX_IV_LENGTH EVP_MAX_IV_LENGTH +#define MAX_MD_SIZE EVP_MAX_MD_SIZE + +#elif defined(USE_CRYPTO_POLARSSL) + +#include +#include +typedef cipher_info_t cipher_kt_t; +typedef cipher_context_t cipher_evp_t; +typedef md_info_t digest_type_t; +#define MAX_KEY_LENGTH 64 +#define MAX_IV_LENGTH POLARSSL_MAX_IV_LENGTH +#define MAX_MD_SIZE POLARSSL_MD_MAX_SIZE + +#elif defined(USE_CRYPTO_MBEDTLS) + +#include +#include +typedef mbedtls_cipher_info_t cipher_kt_t; +typedef mbedtls_cipher_context_t cipher_evp_t; +typedef mbedtls_md_info_t digest_type_t; +#define MAX_KEY_LENGTH 64 +#define MAX_IV_LENGTH MBEDTLS_MAX_IV_LENGTH +#define MAX_MD_SIZE MBEDTLS_MD_MAX_SIZE + +/* we must have MBEDTLS_CIPHER_MODE_CFB defined */ +#if !defined(MBEDTLS_CIPHER_MODE_CFB) +#error Cipher Feedback mode a.k.a CFB not supported by your mbed TLS. +#endif + +#endif + +#ifdef USE_CRYPTO_APPLECC + +#include + +#define kCCAlgorithmInvalid UINT32_MAX +#define kCCContextValid 0 +#define kCCContextInvalid -1 + +typedef struct { + CCCryptorRef cryptor; + int valid; + CCOperation encrypt; + CCAlgorithm cipher; + CCMode mode; + CCPadding padding; + uint8_t iv[MAX_IV_LENGTH]; + uint8_t key[MAX_KEY_LENGTH]; + size_t iv_len; + size_t key_len; +} cipher_cc_t; + +#endif + +typedef struct { + cipher_evp_t *evp; +#ifdef USE_CRYPTO_APPLECC + cipher_cc_t cc; +#endif + uint8_t iv[MAX_IV_LENGTH]; +} cipher_ctx_t; + +typedef struct { + cipher_kt_t *info; + size_t iv_len; + size_t key_len; +} cipher_t; + +#ifdef HAVE_STDINT_H +#include +#elif HAVE_INTTYPES_H +#include +#endif + +#define SODIUM_BLOCK_SIZE 64 + +enum crpher_index { + NONE = -1, + TABLE = 0, + RC4, + RC4_MD5_6, + RC4_MD5, + AES_128_CFB, + AES_192_CFB, + AES_256_CFB, + AES_128_CTR, + AES_192_CTR, + AES_256_CTR, + BF_CFB, + CAMELLIA_128_CFB, + CAMELLIA_192_CFB, + CAMELLIA_256_CFB, + CAST5_CFB, + DES_CFB, + IDEA_CFB, + RC2_CFB, + SEED_CFB, + SALSA20, + CHACHA20, + CHACHA20IETF, + CIPHER_NUM, +}; + +#define ONETIMEAUTH_FLAG 0x10 +#define ADDRTYPE_MASK 0xEF + +#define ONETIMEAUTH_BYTES 10U +#define MD5_BYTES 16U +#define SHA1_BYTES 20U +#define CLEN_BYTES 2U +#define AUTH_BYTES (ONETIMEAUTH_BYTES + CLEN_BYTES) + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +typedef struct buffer { + size_t idx; + size_t len; + size_t capacity; + char *array; +} buffer_t; + +typedef struct chunk { + uint32_t idx; + uint32_t len; + uint32_t counter; + buffer_t *buf; +} chunk_t; + +typedef struct enc_ctx { + uint8_t init; + uint64_t counter; + cipher_ctx_t evp; +} enc_ctx_t; + +void bytes_to_key_with_size(const char *pass, size_t len, uint8_t *md, size_t md_size); + +int ss_encrypt_all(buffer_t *plaintext, int method, int auth, size_t capacity); +int ss_decrypt_all(buffer_t *ciphertext, int method, int auth, size_t capacity); +int ss_encrypt(buffer_t *plaintext, enc_ctx_t *ctx, size_t capacity); +int ss_decrypt(buffer_t *ciphertext, enc_ctx_t *ctx, size_t capacity); + +void enc_ctx_init(int method, enc_ctx_t *ctx, int enc); +int enc_init(const char *pass, const char *method); +int enc_get_iv_len(void); +uint8_t* enc_get_key(void); +int enc_get_key_len(void); +void cipher_context_release(cipher_ctx_t *evp); +unsigned char *enc_md5(const unsigned char *d, size_t n, unsigned char *md); + +int ss_md5_hmac(char *auth, char *msg, int msg_len, uint8_t *iv); +int ss_md5_hmac_with_key(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len); +int ss_md5_hash_func(char *auth, char *msg, int msg_len); +int ss_sha1_hmac(char *auth, char *msg, int msg_len, uint8_t *iv); +int ss_sha1_hmac_with_key(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len); +int ss_sha1_hash_func(char *auth, char *msg, int msg_len); +int ss_aes_128_cbc(char *encrypt, char *out_data, char *key); +int ss_onetimeauth(buffer_t *buf, uint8_t *iv, size_t capacity); +int ss_onetimeauth_verify(buffer_t *buf, uint8_t *iv); + +int ss_check_hash(buffer_t *buf, chunk_t *chunk, enc_ctx_t *ctx, size_t capacity); +int ss_gen_hash(buffer_t *buf, uint32_t *counter, enc_ctx_t *ctx, size_t capacity); + +int balloc(buffer_t *ptr, size_t capacity); +int brealloc(buffer_t *ptr, size_t len, size_t capacity); +void bfree(buffer_t *ptr); + +#endif // _ENCRYPT_H diff --git a/server/http.c b/server/http.c new file mode 100644 index 0000000..3bd4a32 --- /dev/null +++ b/server/http.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include /* malloc() */ +#include /* strncpy() */ +#include /* strncasecmp() */ +#include /* isblank() */ + +#include "http.h" +#include "protocol.h" + +#define SERVER_NAME_LEN 256 + +static int parse_http_header(const char *, size_t, char **); +static int get_header(const char *, const char *, int, char **); +static int next_header(const char **, int *); + +static const protocol_t http_protocol_st = { + .default_port = 80, + .parse_packet = &parse_http_header, +}; +const protocol_t *const http_protocol = &http_protocol_st; + +/* + * Parses a HTTP request for the Host: header + * + * Returns: + * >=0 - length of the hostname and updates *hostname + * caller is responsible for freeing *hostname + * -1 - Incomplete request + * -2 - No Host header included in this request + * -3 - Invalid hostname pointer + * -4 - malloc failure + * < -4 - Invalid HTTP request + * + */ +static int +parse_http_header(const char *data, size_t data_len, char **hostname) +{ + int result, i; + + if (hostname == NULL) + return -3; + + if (data_len == 0) + return -1; + + result = get_header("Host:", data, data_len, hostname); + if (result < 0) + return result; + + /* + * if the user specifies the port in the request, it is included here. + * Host: example.com:80 + * so we trim off port portion + */ + for (i = result - 1; i >= 0; i--) + if ((*hostname)[i] == ':') { + (*hostname)[i] = '\0'; + result = i; + break; + } + + return result; +} + +static int +get_header(const char *header, const char *data, int data_len, char **value) +{ + int len, header_len; + + header_len = strlen(header); + + /* loop through headers stopping at first blank line */ + while ((len = next_header(&data, &data_len)) != 0) + if (len > header_len && strncasecmp(header, data, header_len) == 0) { + /* Eat leading whitespace */ + while (header_len < len && isblank(data[header_len])) + header_len++; + + *value = malloc(len - header_len + 1); + if (*value == NULL) + return -4; + + strncpy(*value, data + header_len, len - header_len); + (*value)[len - header_len] = '\0'; + + return len - header_len; + } + + /* If there is no data left after reading all the headers then we do not + * have a complete HTTP request, there must be a blank line */ + if (data_len == 0) + return -1; + + return -2; +} + +static int +next_header(const char **data, int *len) +{ + int header_len; + + /* perhaps we can optimize this to reuse the value of header_len, rather + * than scanning twice. + * Walk our data stream until the end of the header */ + while (*len > 2 && (*data)[0] != '\r' && (*data)[1] != '\n') { + (*len)--; + (*data)++; + } + + /* advanced past the pair */ + *data += 2; + *len -= 2; + + /* Find the length of the next header */ + header_len = 0; + while (*len > header_len + 1 + && (*data)[header_len] != '\r' + && (*data)[header_len + 1] != '\n') + header_len++; + + return header_len; +} diff --git a/server/http.h b/server/http.h new file mode 100644 index 0000000..914815a --- /dev/null +++ b/server/http.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef HTTP_H +#define HTTP_H + +#include +#include "protocol.h" + +const protocol_t *const http_protocol; + +#endif diff --git a/server/http_simple.c b/server/http_simple.c new file mode 100644 index 0000000..c1e34ee --- /dev/null +++ b/server/http_simple.c @@ -0,0 +1,625 @@ + +#include "http_simple.h" + +static char* g_useragent[] = { + "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0", + "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/44.0", + "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/27.0.1453.93 Chrome/27.0.1453.93 Safari/537.36", + "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0", + "Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)", + "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", + "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)", + "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko", + "Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36", + "Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", + "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", +}; + +static int g_useragent_index = -1; + +typedef struct http_simple_local_data { + int has_sent_header; + int has_recv_header; + char *encode_buffer; + int host_matched; + char *recv_buffer; + int recv_buffer_size; +}http_simple_local_data; + +void http_simple_local_data_init(http_simple_local_data* local) { + local->has_sent_header = 0; + local->has_recv_header = 0; + local->encode_buffer = NULL; + + local->recv_buffer = malloc(0); + local->recv_buffer_size = 0; + + local->host_matched = 0; + + if (g_useragent_index == -1) { + g_useragent_index = xorshift128plus() % (sizeof(g_useragent) / sizeof(*g_useragent)); + } +} + +obfs * http_simple_new_obfs() { + obfs * self = new_obfs(); + self->l_data = malloc(sizeof(http_simple_local_data)); + http_simple_local_data_init((http_simple_local_data*)self->l_data); + return self; +} + +void http_simple_dispose(obfs *self) { + http_simple_local_data *local = (http_simple_local_data*)self->l_data; + if (local->encode_buffer != NULL) { + free(local->encode_buffer); + local->encode_buffer = NULL; + } + free(local); + dispose_obfs(self); +} + +char http_simple_hex(char c) { + if (c < 10) return c + '0'; + return c - 10 + 'a'; +} + +int get_data_from_http_header(char *data, char **outdata) { + char *delim = "\r\n"; + char *delim_hex = "%"; + int outlength = 0; + + char *buf = *outdata; + char *p_line; + p_line = strtok(data, delim); + + //while(p_line) + { + char *p_hex; + + p_hex = strtok(p_line, delim_hex); + + while((p_hex = strtok(NULL, delim_hex))) + { + char hex = 0; + + if(strlen(p_hex) <= 0) + { + continue; + } + + if(strlen(p_hex) > 2) + { + char *c_hex = (char*)malloc(2); + memcpy(c_hex, p_hex, 2); + hex = (char)strtol(c_hex, NULL, 16); + free(c_hex); + } + else + { + hex = (char)strtol(p_hex, NULL, 16); + } + + outlength += 1; + buf = (char*)realloc(buf, outlength); + buf[outlength - 1] = hex; + } + + //p_line = strtok(p_line, delim); + } + return outlength; +} + +void get_host_from_http_header(char *data, char **host) { + char* data_begin = strstr(data, "Host: "); + + if(data_begin == NULL) + { + return; + } + + data_begin += 6; + char* data_end = strstr(data_begin, "\r\n"); + char* data_end_port = strstr(data_begin, ":"); + + int host_length = 0; + + if(data_end_port != NULL) + { + host_length = data_end_port - data_begin; + } + else + { + host_length = data_end - data_begin; + } + + if(host_length <= 0) + { + return; + } + + memset(*host, 0x00, 1024); + memcpy(*host, data_begin, host_length); +} + +void http_simple_encode_head(http_simple_local_data *local, char *data, int datalength) { + if (local->encode_buffer == NULL) { + local->encode_buffer = (char*)malloc(datalength * 3 + 1); + } + int pos = 0; + for (; pos < datalength; ++pos) { + local->encode_buffer[pos * 3] = '%'; + local->encode_buffer[pos * 3 + 1] = http_simple_hex(((unsigned char)data[pos] >> 4)); + local->encode_buffer[pos * 3 + 2] = http_simple_hex(data[pos] & 0xF); + } + local->encode_buffer[pos * 3] = 0; +} + +int http_simple_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { + char *encryptdata = *pencryptdata; + http_simple_local_data *local = (http_simple_local_data*)self->l_data; + if (local->has_sent_header) { + return datalength; + } + char hosts[1024]; + char * phost[128]; + int host_num = 0; + int pos; + char hostport[128]; + int head_size = self->server.head_len + (xorshift128plus() & 0x3F); + int outlength; + char * out_buffer = (char*)malloc(datalength + 2048); + char * body_buffer = NULL; + if (head_size > datalength) + head_size = datalength; + http_simple_encode_head(local, encryptdata, head_size); + if (self->server.param && strlen(self->server.param) == 0) + self->server.param = NULL; + strncpy(hosts, self->server.param ? self->server.param : self->server.host, sizeof hosts); + phost[host_num++] = hosts; + for (pos = 0; hosts[pos]; ++pos) { + if (hosts[pos] == ',') { + phost[host_num++] = &hosts[pos + 1]; + hosts[pos] = 0; + } else if (hosts[pos] == '#') { + char * body_pointer = &hosts[pos + 1]; + char * p; + int trans_char = 0; + p = body_buffer = (char*)malloc(2048); + for ( ; *body_pointer; ++body_pointer) { + if (*body_pointer == '\\') { + trans_char = 1; + continue; + } else if (*body_pointer == '\n') { + *p = '\r'; + *++p = '\n'; + continue; + } + if (trans_char) { + if (*body_pointer == '\\' ) { + *p = '\\'; + } else if (*body_pointer == 'n' ) { + *p = '\r'; + *++p = '\n'; + } else { + *p = '\\'; + *p = *body_pointer; + } + trans_char = 0; + } else { + *p = *body_pointer; + } + ++p; + } + *p = 0; + hosts[pos] = 0; + break; + } + } + host_num = xorshift128plus() % host_num; + if (self->server.port == 80) + sprintf(hostport, "%s", phost[host_num]); + else + sprintf(hostport, "%s:%d", phost[host_num], self->server.port); + if (body_buffer) { + sprintf(out_buffer, + "GET /%s HTTP/1.1\r\n" + "Host: %s\r\n" + "%s\r\n\r\n", + local->encode_buffer, + hostport, + body_buffer); + } else { + sprintf(out_buffer, + "GET /%s HTTP/1.1\r\n" + "Host: %s\r\n" + "User-Agent: %s\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + "Accept-Language: en-US,en;q=0.8\r\n" + "Accept-Encoding: gzip, deflate\r\n" + "DNT: 1\r\n" + "Connection: keep-alive\r\n" + "\r\n", + local->encode_buffer, + hostport, + g_useragent[g_useragent_index] + ); + } + //LOGI("http header: %s", out_buffer); + outlength = strlen(out_buffer); + memmove(out_buffer + outlength, encryptdata + head_size, datalength - head_size); + outlength += datalength - head_size; + local->has_sent_header = 1; + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + memmove(encryptdata, out_buffer, outlength); + free(out_buffer); + if (body_buffer != NULL) + free(body_buffer); + if (local->encode_buffer != NULL) { + free(local->encode_buffer); + local->encode_buffer = NULL; + } + return outlength; +} + +int http_simple_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { + char *encryptdata = *pencryptdata; + http_simple_local_data *local = (http_simple_local_data*)self->l_data; + if (local->has_sent_header) { + return datalength; + } + int outlength; + char * out_buffer = (char*)malloc(datalength + 2048); + + time_t now; + struct tm *tm_now; + char datetime[200]; + + time(&now); + tm_now = localtime(&now); + strftime(datetime, 200, "%a, %d %b %Y %H:%M:%S GMT", tm_now); + + sprintf(out_buffer, + "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Encoding: gzip\r\nContent-Type: text/html\r\nDate: " + "%s" + "\r\nServer: nginx\r\nVary: Accept-Encoding\r\n\r\n", + datetime); + + outlength = strlen(out_buffer); + memmove(out_buffer + outlength, encryptdata, datalength); + outlength += datalength; + + local->has_sent_header = 1; + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + memmove(encryptdata, out_buffer, outlength); + free(out_buffer); + return outlength; +} + +int http_simple_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) { + char *encryptdata = *pencryptdata; + http_simple_local_data *local = (http_simple_local_data*)self->l_data; + *needsendback = 0; + if (local->has_recv_header) { + return datalength; + } + char* data_begin = strstr(encryptdata, "\r\n\r\n"); + if (data_begin) { + int outlength; + data_begin += 4; + local->has_recv_header = 1; + outlength = datalength - (data_begin - encryptdata); + memmove(encryptdata, data_begin, outlength); + return outlength; + } else { + return 0; + } +} + +int http_simple_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) { + char *encryptdata = *pencryptdata; + http_simple_local_data *local = (http_simple_local_data*)self->l_data; + *needsendback = 0; + if (local->has_recv_header) { + return datalength; + } + + if(datalength != 0) + { + local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size + datalength); + memmove(local->recv_buffer + local->recv_buffer_size, encryptdata, datalength); + local->recv_buffer_size += datalength; + + int outlength = local->recv_buffer_size; + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + memcpy(encryptdata, local->recv_buffer, local->recv_buffer_size); + } + + if(local->recv_buffer_size > 10) + { + if(strstr(local->recv_buffer, "GET /") == local->recv_buffer || strstr(local->recv_buffer, "POST /") == local->recv_buffer) + { + if(local->recv_buffer_size > 65536) + { + free(local->recv_buffer); + local->recv_buffer = malloc(0); + local->recv_buffer_size = 0; + local->has_sent_header = 1; + local->has_recv_header = 1; + LOGE("http_simple: over size"); + return -1; + } + } + else + { + free(local->recv_buffer); + local->recv_buffer = malloc(0); + local->recv_buffer_size = 0; + local->has_sent_header = 1; + local->has_recv_header = 1; + LOGE("http_simple: not match begin"); + return -1; + } + } + else + { + LOGE("http_simple: too short"); + local->has_sent_header = 1; + local->has_recv_header = 1; + return -1; + } + + char* data_begin = strstr(encryptdata, "\r\n\r\n"); + if (data_begin) { + int outlength; + char *ret_buf = (char*)malloc(*capacity); + memset(ret_buf, 0x00, *capacity); + int ret_buf_len = 0; + ret_buf_len = get_data_from_http_header(encryptdata, &ret_buf); + + if (self->server.param && strlen(self->server.param) == 0) + { + self->server.param = NULL; + } + else + { + if(local->host_matched == 0) + { + char *host = (char*)malloc(1024); + get_host_from_http_header(local->recv_buffer, &host); + char hosts[1024]; + char * phost[128]; + int host_num = 0; + int pos = 0; + int is_match = 0; + char * body_buffer = NULL; + strncpy(hosts, self->server.param, sizeof hosts); + phost[host_num++] = hosts; + + for (pos = 0; hosts[pos]; ++pos) { + if (hosts[pos] == ',') { + phost[host_num++] = &hosts[pos + 1]; + hosts[pos] = 0; + } else if (hosts[pos] == '#') { + char * body_pointer = &hosts[pos + 1]; + char * p; + int trans_char = 0; + p = body_buffer = (char*)malloc(2048); + for ( ; *body_pointer; ++body_pointer) { + if (*body_pointer == '\\') { + trans_char = 1; + continue; + } else if (*body_pointer == '\n') { + *p = '\r'; + *++p = '\n'; + continue; + } + if (trans_char) { + if (*body_pointer == '\\' ) { + *p = '\\'; + } else if (*body_pointer == 'n' ) { + *p = '\r'; + *++p = '\n'; + } else { + *p = '\\'; + *p = *body_pointer; + } + trans_char = 0; + } else { + *p = *body_pointer; + } + ++p; + } + *p = 0; + hosts[pos] = 0; + break; + } + } + + + for(pos = 0; pos < host_num; pos++) + { + if(strcmp(phost[pos], host) == 0) + { + is_match = 1; + local->host_matched = 1; + } + } + + if(is_match == 0) + { + free(local->recv_buffer); + local->recv_buffer = malloc(0); + local->recv_buffer_size = 0; + local->has_sent_header = 1; + local->has_recv_header = 1; + LOGE("http_simple: not match host, host: %s", host); + return -1; + } + + free(host); + } + } + + if(ret_buf_len <= 0) + { + return -1; + } + + data_begin += 4; + local->has_recv_header = 1; + + ret_buf = (char*)realloc(ret_buf, ret_buf_len + datalength - (data_begin - encryptdata)); + outlength = ret_buf_len + datalength - (data_begin - encryptdata); + + memcpy(ret_buf + ret_buf_len, data_begin, datalength - (data_begin - encryptdata)); + + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + + memcpy(encryptdata, ret_buf, outlength); + free(ret_buf); + return outlength; + } else { + return 0; + } +} + +void boundary(char result[]) +{ + char *str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + int i,lstr; + char ss[3] = {0}; + lstr = strlen(str); + srand((unsigned int)time((time_t *)NULL)); + for(i = 0; i < 32; ++i) + { + sprintf(ss, "%c", str[(rand()%lstr)]); + strcat(result, ss); + } +} + +int http_post_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { + char *encryptdata = *pencryptdata; + http_simple_local_data *local = (http_simple_local_data*)self->l_data; + if (local->has_sent_header) { + return datalength; + } + char hosts[1024]; + char * phost[128]; + int host_num = 0; + int pos; + char hostport[128]; + int head_size = self->server.head_len + (xorshift128plus() & 0x3F); + int outlength; + char * out_buffer = (char*)malloc(datalength + 2048); + char * body_buffer = NULL; + if (head_size > datalength) + head_size = datalength; + http_simple_encode_head(local, encryptdata, head_size); + if (self->server.param && strlen(self->server.param) == 0) + self->server.param = NULL; + strncpy(hosts, self->server.param ? self->server.param : self->server.host, sizeof hosts); + phost[host_num++] = hosts; + for (pos = 0; hosts[pos]; ++pos) { + if (hosts[pos] == ',') { + phost[host_num++] = &hosts[pos + 1]; + hosts[pos] = 0; + } else if (hosts[pos] == '#') { + char * body_pointer = &hosts[pos + 1]; + char * p; + int trans_char = 0; + p = body_buffer = (char*)malloc(2048); + for ( ; *body_pointer; ++body_pointer) { + if (*body_pointer == '\\') { + trans_char = 1; + continue; + } else if (*body_pointer == '\n') { + *p = '\r'; + *++p = '\n'; + continue; + } + if (trans_char) { + if (*body_pointer == '\\' ) { + *p = '\\'; + } else if (*body_pointer == 'n' ) { + *p = '\r'; + *++p = '\n'; + } else { + *p = '\\'; + *p = *body_pointer; + } + trans_char = 0; + } else { + *p = *body_pointer; + } + ++p; + } + *p = 0; + hosts[pos] = 0; + break; + } + } + host_num = xorshift128plus() % host_num; + if (self->server.port == 80) + sprintf(hostport, "%s", phost[host_num]); + else + sprintf(hostport, "%s:%d", phost[host_num], self->server.port); + if (body_buffer) { + sprintf(out_buffer, + "POST /%s HTTP/1.1\r\n" + "Host: %s\r\n" + "%s\r\n\r\n", + local->encode_buffer, + hostport, + body_buffer); + } else { + char result[33] = {0}; + boundary(result); + sprintf(out_buffer, + "POST /%s HTTP/1.1\r\n" + "Host: %s\r\n" + "User-Agent: %s\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + "Accept-Language: en-US,en;q=0.8\r\n" + "Accept-Encoding: gzip, deflate\r\n" + "Content-Type: multipart/form-data; boundary=%s\r\n" + "DNT: 1\r\n" + "Connection: keep-alive\r\n" + "\r\n", + local->encode_buffer, + hostport, + g_useragent[g_useragent_index], + result + ); + } + //LOGI("http header: %s", out_buffer); + outlength = strlen(out_buffer); + memmove(out_buffer + outlength, encryptdata + head_size, datalength - head_size); + outlength += datalength - head_size; + local->has_sent_header = 1; + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + memmove(encryptdata, out_buffer, outlength); + free(out_buffer); + if (body_buffer != NULL) + free(body_buffer); + if (local->encode_buffer != NULL) { + free(local->encode_buffer); + local->encode_buffer = NULL; + } + return outlength; +} diff --git a/server/http_simple.h b/server/http_simple.h new file mode 100644 index 0000000..cce24cc --- /dev/null +++ b/server/http_simple.h @@ -0,0 +1,21 @@ +/* + * http_simple.h - Define shadowsocksR server's buffers and callbacks + * + * Copyright (C) 2015 - 2016, Break Wa11 + */ + +#ifndef _HTTP_SIMPLE_H +#define _HTTP_SIMPLE_H + +obfs * http_simple_new_obfs(); +void http_simple_dispose(obfs *self); + +int http_simple_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); +int http_simple_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback); + +int http_post_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); + +int http_simple_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); +int http_simple_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback); + +#endif // _HTTP_SIMPLE_H diff --git a/server/jconf.c b/server/jconf.c new file mode 100644 index 0000000..494aa5f --- /dev/null +++ b/server/jconf.c @@ -0,0 +1,260 @@ +/* + * jconf.c - Parse the JSON format config file + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#include +#include +#include +#include +#include + +#include "utils.h" +#include "jconf.h" +#include "json.h" +#include "string.h" + +#include + +#define check_json_value_type(value, expected_type, message) \ + do { \ + if ((value)->type != (expected_type)) \ + FATAL((message)); \ + } while(0) + +static char * +to_string(const json_value *value) +{ + if (value->type == json_string) { + return ss_strndup(value->u.string.ptr, value->u.string.length); + } else if (value->type == json_integer) { + return strdup(ss_itoa(value->u.integer)); + } else if (value->type == json_null) { + return "null"; + } else { + LOGE("%d", value->type); + FATAL("Invalid config format."); + } + return 0; +} + +void +free_addr(ss_addr_t *addr) +{ + ss_free(addr->host); + ss_free(addr->port); +} + +void +parse_addr(const char *str, ss_addr_t *addr) +{ + int ipv6 = 0, ret = -1, n = 0; + char *pch; + + struct cork_ip ip; + if (cork_ip_init(&ip, str) != -1) { + addr->host = strdup(str); + addr->port = NULL; + return; + } + + pch = strchr(str, ':'); + while (pch != NULL) { + n++; + ret = pch - str; + pch = strchr(pch + 1, ':'); + } + if (n > 1) { + ipv6 = 1; + if (str[ret - 1] != ']') { + ret = -1; + } + } + + if (ret == -1) { + if (ipv6) { + addr->host = ss_strndup(str + 1, strlen(str) - 2); + } else { + addr->host = strdup(str); + } + addr->port = NULL; + } else { + if (ipv6) { + addr->host = ss_strndup(str + 1, ret - 2); + } else { + addr->host = ss_strndup(str, ret); + } + addr->port = strdup(str + ret + 1); + } +} + +jconf_t * +read_jconf(const char *file) +{ + static jconf_t conf; + + memset(&conf, 0, sizeof(jconf_t)); + + char *buf; + json_value *obj; + + FILE *f = fopen(file, "rb"); + if (f == NULL) { + FATAL("Invalid config path."); + } + + fseek(f, 0, SEEK_END); + long pos = ftell(f); + fseek(f, 0, SEEK_SET); + + if (pos >= MAX_CONF_SIZE) { + FATAL("Too large config file."); + } + + buf = ss_malloc(pos + 1); + if (buf == NULL) { + FATAL("No enough memory."); + } + + int nread = fread(buf, pos, 1, f); + if (!nread) { + FATAL("Failed to read the config file."); + } + fclose(f); + + buf[pos] = '\0'; // end of string + + json_settings settings = { 0UL, 0, NULL, NULL, NULL }; + char error_buf[512]; + obj = json_parse_ex(&settings, buf, pos, error_buf); + + if (obj == NULL) { + FATAL(error_buf); + } + + if (obj->type == json_object) { + unsigned int i, j; + for (i = 0; i < obj->u.object.length; i++) { + char *name = obj->u.object.values[i].name; + json_value *value = obj->u.object.values[i].value; + if (strcmp(name, "server") == 0) { + if (value->type == json_array) { + for (j = 0; j < value->u.array.length; j++) { + if (j >= MAX_REMOTE_NUM) { + break; + } + json_value *v = value->u.array.values[j]; + char *addr_str = to_string(v); + parse_addr(addr_str, conf.remote_addr + j); + ss_free(addr_str); + conf.remote_num = j + 1; + } + } else if (value->type == json_string) { + conf.remote_addr[0].host = to_string(value); + conf.remote_addr[0].port = NULL; + conf.remote_num = 1; + } + } else if (strcmp(name, "port_password") == 0) { + if (value->type == json_object) { + for (j = 0; j < value->u.object.length; j++) { + if (j >= MAX_PORT_NUM) { + break; + } + json_value *v = value->u.object.values[j].value; + if (v->type == json_string) { + conf.port_password[j].port = ss_strndup(value->u.object.values[j].name, + value->u.object.values[j].name_length); + conf.port_password[j].password = to_string(v); + conf.port_password_num = j + 1; + } + } + } + } else if (strcmp(name, "server_port") == 0) { + conf.remote_port = to_string(value); + } else if (strcmp(name, "local_address") == 0) { + conf.local_addr = to_string(value); + } else if (strcmp(name, "local_port") == 0) { + conf.local_port = to_string(value); + } else if (strcmp(name, "password") == 0) { + conf.password = to_string(value); + } else if (strcmp(name, "protocol") == 0) { // SSR + conf.protocol = to_string(value); + } else if (strcmp(name, "protocol_param") == 0) { // SSR + conf.protocol_param = to_string(value); + } else if (strcmp(name, "method") == 0) { + conf.method = to_string(value); + } else if (strcmp(name, "obfs") == 0) { // SSR + conf.obfs = to_string(value); + } else if (strcmp(name, "obfs_param") == 0) { // SSR + conf.obfs_param = to_string(value); + } else if (strcmp(name, "timeout") == 0) { + conf.timeout = to_string(value); + } else if (strcmp(name, "user") == 0) { + conf.user = to_string(value); + } else if (strcmp(name, "fast_open") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'fast_open' must be a boolean"); + conf.fast_open = value->u.boolean; + } else if (strcmp(name, "auth") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'auth' must be a boolean"); + conf.auth = value->u.boolean; + } else if (strcmp(name, "nofile") == 0) { + check_json_value_type(value, json_integer, + "invalid config file: option 'nofile' must be an integer"); + conf.nofile = value->u.integer; + } else if (strcmp(name, "nameserver") == 0) { + conf.nameserver = to_string(value); + } else if (strcmp(name, "tunnel_address") == 0) { + conf.tunnel_address = to_string(value); + } else if (strcmp(name, "mode") == 0) { + char *mode_str = to_string(value); + + if (strcmp(mode_str, "tcp_only") == 0) + conf.mode = TCP_ONLY; + else if (strcmp(mode_str, "tcp_and_udp") == 0) + conf.mode = TCP_AND_UDP; + else if (strcmp(mode_str, "udp_only") == 0) + conf.mode = UDP_ONLY; + else + LOGI("ignore unknown mode: %s, use tcp_only as fallback", + mode_str); + ss_free(mode_str); + } else if (strcmp(name, "mtu") == 0) { + check_json_value_type(value, json_integer, + "invalid config file: option 'mtu' must be an integer"); + conf.mtu = value->u.integer; + } else if (strcmp(name, "mptcp") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'mptcp' must be a boolean"); + conf.mptcp = value->u.boolean; + } else if (strcmp(name, "ipv6_first") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'ipv6_first' must be a boolean"); + conf.ipv6_first = value->u.boolean; + } + } + } else { + FATAL("Invalid config file"); + } + + ss_free(buf); + json_value_free(obj); + return &conf; +} diff --git a/server/jconf.h b/server/jconf.h new file mode 100644 index 0000000..9a7e5e3 --- /dev/null +++ b/server/jconf.h @@ -0,0 +1,78 @@ +/* + * jconf.h - Define the config data structure + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _JCONF_H +#define _JCONF_H + +#define MAX_PORT_NUM 1024 +#define MAX_REMOTE_NUM 10 +#define MAX_CONF_SIZE 128 * 1024 +#define MAX_DNS_NUM 4 +#define MAX_CONNECT_TIMEOUT 10 +#define MAX_REQUEST_TIMEOUT 60 +#define MIN_UDP_TIMEOUT 10 + +#define TCP_ONLY 0 +#define TCP_AND_UDP 1 +#define UDP_ONLY 3 + +typedef struct { + char *host; + char *port; +} ss_addr_t; + +typedef struct { + char *port; + char *password; +} ss_port_password_t; + +typedef struct { + int remote_num; + ss_addr_t remote_addr[MAX_REMOTE_NUM]; + int port_password_num; + ss_port_password_t port_password[MAX_PORT_NUM]; + char *remote_port; + char *local_addr; + char *local_port; + char *password; + char *protocol; // SSR + char *protocol_param; // SSR + char *method; + char *obfs; // SSR + char *obfs_param; // SSR + char *timeout; + char *user; + int auth; + int fast_open; + int nofile; + char *nameserver; + char *tunnel_address; + int mode; + int mtu; + int mptcp; + int ipv6_first; +} jconf_t; + +jconf_t *read_jconf(const char *file); +void parse_addr(const char *str, ss_addr_t *addr); +void free_addr(ss_addr_t *addr); + +#endif // _JCONF_H diff --git a/server/json.c b/server/json.c new file mode 100644 index 0000000..18e95ef --- /dev/null +++ b/server/json.c @@ -0,0 +1,1002 @@ +/* vim: set et ts=3 sw=3 sts=3 ft=c: + * + * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. + * https://github.com/udp/json-parser + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "json.h" +#include "utils.h" + +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#endif + +#ifdef __cplusplus +const struct _json_value json_value_none; /* zero-d by ctor */ +#else +const struct _json_value json_value_none = { NULL, 0, { 0 }, { NULL } }; +#endif + +#include +#include +#include +#include + +typedef unsigned short json_uchar; + +static unsigned char +hex_value(json_char c) +{ + if (isdigit((uint8_t)c)) { + return c - '0'; + } + + switch (c) { + case 'a': + case 'A': + return 0x0A; + case 'b': + case 'B': + return 0x0B; + case 'c': + case 'C': + return 0x0C; + case 'd': + case 'D': + return 0x0D; + case 'e': + case 'E': + return 0x0E; + case 'f': + case 'F': + return 0x0F; + default: + return 0xFF; + } +} + +typedef struct { + unsigned long used_memory; + + unsigned int uint_max; + unsigned long ulong_max; + + json_settings settings; + int first_pass; +} json_state; + +static void * +default_alloc(size_t size, int zero, void *user_data) +{ + return zero ? calloc(1, size) : ss_malloc(size); +} + +static void +default_free(void *ptr, void *user_data) +{ + ss_free(ptr); +} + +static void * +json_alloc(json_state *state, unsigned long size, int zero) +{ + if ((state->ulong_max - state->used_memory) < size) { + return 0; + } + + if (state->settings.max_memory + && (state->used_memory += size) > state->settings.max_memory) { + return 0; + } + + return state->settings.mem_alloc(size, zero, state->settings.user_data); +} + +static int +new_value(json_state *state, json_value **top, json_value **root, + json_value **alloc, json_type type) +{ + json_value *value; + int values_size; + + if (!state->first_pass) { + value = *top = *alloc; + *alloc = (*alloc)->_reserved.next_alloc; + + if (!*root) { + *root = value; + } + + switch (value->type) { + case json_array: + + if (!(value->u.array.values = (json_value **)json_alloc + (state, value->u.array.length * + sizeof(json_value *), 0))) { + return 0; + } + + value->u.array.length = 0; + break; + + case json_object: + + values_size = sizeof(*value->u.object.values) * + value->u.object.length; + + if (!((*(void **)&value->u.object.values) = json_alloc + (state, + values_size + + ((size_t)value->u. + object.values), + 0))) { + return 0; + } + + value->_reserved.object_mem = (*(char **)&value->u.object.values) + + values_size; + + value->u.object.length = 0; + break; + + case json_string: + + if (!(value->u.string.ptr = (json_char *)json_alloc + (state, + (value->u.string.length + + 1) * sizeof(json_char), 0))) { + return 0; + } + + value->u.string.length = 0; + break; + + default: + break; + } + + return 1; + } + + value = (json_value *)json_alloc(state, sizeof(json_value), 1); + + if (!value) { + return 0; + } + + if (!*root) { + *root = value; + } + + value->type = type; + value->parent = *top; + + if (*alloc) { + (*alloc)->_reserved.next_alloc = value; + } + + *alloc = *top = value; + + return 1; +} + +#define e_off \ + ((int)(i - cur_line_begin)) + +#define whitespace \ +case '\n': \ + ++cur_line; cur_line_begin = i; \ +case ' ': \ +case '\t': \ +case '\r' + +#define string_add(b) \ + do { if (!state.first_pass) { string[string_length] = b; \ + } ++string_length; } while (0) + +static const long + flag_next = 1 << 0, + flag_reproc = 1 << 1, + flag_need_comma = 1 << 2, + flag_seek_value = 1 << 3, + flag_escaped = 1 << 4, + flag_string = 1 << 5, + flag_need_colon = 1 << 6, + flag_done = 1 << 7, + flag_num_negative = 1 << 8, + flag_num_zero = 1 << 9, + flag_num_e = 1 << 10, + flag_num_e_got_sign = 1 << 11, + flag_num_e_negative = 1 << 12, + flag_line_comment = 1 << 13, + flag_block_comment = 1 << 14; + +json_value * +json_parse_ex(json_settings *settings, + const json_char *json, + size_t length, + char *error_buf) +{ + json_char error[json_error_max]; + int cur_line; + const json_char *cur_line_begin, *i, *end; + json_value *top, *root, *alloc = 0; + json_state state = { 0UL, 0U, 0UL, { 0UL, 0, NULL, NULL, NULL }, 0 }; + long flags; + long num_digits = 0, num_e = 0; + json_int_t num_fraction = 0; + + /* Skip UTF-8 BOM + */ + if (length >= 3 && ((unsigned char)json[0]) == 0xEF + && ((unsigned char)json[1]) == 0xBB + && ((unsigned char)json[2]) == 0xBF) { + json += 3; + length -= 3; + } + + error[0] = '\0'; + end = (json + length); + + memcpy(&state.settings, settings, sizeof(json_settings)); + + if (!state.settings.mem_alloc) { + state.settings.mem_alloc = default_alloc; + } + + if (!state.settings.mem_free) { + state.settings.mem_free = default_free; + } + + memset(&state.uint_max, 0xFF, sizeof(state.uint_max)); + memset(&state.ulong_max, 0xFF, sizeof(state.ulong_max)); + + state.uint_max -= 8; /* limit of how much can be added before next check */ + state.ulong_max -= 8; + + for (state.first_pass = 1; state.first_pass >= 0; --state.first_pass) { + json_uchar uchar; + unsigned char uc_b1, uc_b2, uc_b3, uc_b4; + json_char *string = 0; + unsigned int string_length = 0; + + top = root = 0; + flags = flag_seek_value; + + cur_line = 1; + cur_line_begin = json; + + for (i = json;; ++i) { + json_char b = (i == end ? 0 : *i); + + if (flags & flag_string) { + if (!b) { + sprintf(error, "Unexpected EOF in string (at %d:%d)", + cur_line, e_off); + goto e_failed; + } + + if (string_length > state.uint_max) { + goto e_overflow; + } + + if (flags & flag_escaped) { + flags &= ~flag_escaped; + + switch (b) { + case 'b': + string_add('\b'); + break; + case 'f': + string_add('\f'); + break; + case 'n': + string_add('\n'); + break; + case 'r': + string_add('\r'); + break; + case 't': + string_add('\t'); + break; + case 'u': + + if (end - i < 4 || + (uc_b1 = hex_value(*++i)) == 0xFF || + (uc_b2 = hex_value(*++i)) == 0xFF + || (uc_b3 = hex_value(*++i)) == 0xFF || + (uc_b4 = hex_value(*++i)) == 0xFF) { + sprintf(error, + "Invalid character value `%c` (at %d:%d)", + b, cur_line, e_off); + goto e_failed; + } + + uc_b1 = uc_b1 * 16 + uc_b2; + uc_b2 = uc_b3 * 16 + uc_b4; + + uchar = ((json_char)uc_b1) * 256 + uc_b2; + + if (sizeof(json_char) >= sizeof(json_uchar) || + (uc_b1 == 0 && uc_b2 <= 0x7F)) { + string_add((json_char)uchar); + break; + } + + if (uchar <= 0x7FF) { + if (state.first_pass) { + string_length += 2; + } else { + string[string_length++] = 0xC0 | + ((uc_b2 & + 0xC0) >> + 6) | + ((uc_b1 & 0x7) << 2); + string[string_length++] = 0x80 | + (uc_b2 & 0x3F); + } + + break; + } + + if (state.first_pass) { + string_length += 3; + } else { + string[string_length++] = 0xE0 | + ((uc_b1 & 0xF0) >> 4); + string[string_length++] = 0x80 | + ((uc_b1 & + 0xF) << + 2) | + ((uc_b2 & 0xC0) >> 6); + string[string_length++] = 0x80 | (uc_b2 & 0x3F); + } + + break; + + default: + string_add(b); + } + + continue; + } + + if (b == '\\') { + flags |= flag_escaped; + continue; + } + + if (b == '"') { + if (!state.first_pass) { + string[string_length] = 0; + } + + flags &= ~flag_string; + string = 0; + + switch (top->type) { + case json_string: + + top->u.string.length = string_length; + flags |= flag_next; + + break; + + case json_object: + + if (state.first_pass) { + (*(json_char **)&top->u.object.values) += + string_length + 1; + } else { + top->u.object.values[top->u.object.length].name + = (json_char *)top->_reserved.object_mem; + + top->u.object.values[top->u.object.length]. + name_length + = string_length; + + (*(json_char **)&top->_reserved.object_mem) += + string_length + 1; + } + + flags |= flag_seek_value | flag_need_colon; + continue; + + default: + break; + } + } else { + string_add(b); + continue; + } + } + + if (state.settings.settings & json_enable_comments) { + if (flags & (flag_line_comment | flag_block_comment)) { + if (flags & flag_line_comment) { + if (b == '\r' || b == '\n' || !b) { + flags &= ~flag_line_comment; + --i; /* so null can be reproc'd */ + } + + continue; + } + + if (flags & flag_block_comment) { + if (!b) { + sprintf(error, + "%d:%d: Unexpected EOF in block comment", + cur_line, e_off); + goto e_failed; + } + + if (b == '*' && i < (end - 1) && i[1] == '/') { + flags &= ~flag_block_comment; + ++i; /* skip closing sequence */ + } + + continue; + } + } else if (b == '/') { + if (!(flags & (flag_seek_value | flag_done)) && top->type != + json_object) { + sprintf(error, "%d:%d: Comment not allowed here", + cur_line, e_off); + goto e_failed; + } + + if (++i == end) { + sprintf(error, "%d:%d: EOF unexpected", cur_line, + e_off); + goto e_failed; + } + + switch (b = *i) { + case '/': + flags |= flag_line_comment; + continue; + + case '*': + flags |= flag_block_comment; + continue; + + default: + sprintf(error, + "%d:%d: Unexpected `%c` in comment opening sequence", cur_line, e_off, + b); + goto e_failed; + } + } + } + + if (flags & flag_done) { + if (!b) { + break; + } + + switch (b) { +whitespace: + continue; + + default: + sprintf(error, "%d:%d: Trailing garbage: `%c`", cur_line, + e_off, b); + goto e_failed; + } + } + + if (flags & flag_seek_value) { + switch (b) { +whitespace: + continue; + + case ']': + + if (top->type == json_array) { + flags = + (flags & + ~(flag_need_comma | flag_seek_value)) | flag_next; + } else { + sprintf(error, "%d:%d: Unexpected ]", cur_line, e_off); + goto e_failed; + } + + break; + + default: + + if (flags & flag_need_comma) { + if (b == ',') { + flags &= ~flag_need_comma; + continue; + } else { + sprintf(error, "%d:%d: Expected , before %c", + cur_line, e_off, b); + goto e_failed; + } + } + + if (flags & flag_need_colon) { + if (b == ':') { + flags &= ~flag_need_colon; + continue; + } else { + sprintf(error, "%d:%d: Expected : before %c", + cur_line, e_off, b); + goto e_failed; + } + } + + flags &= ~flag_seek_value; + + switch (b) { + case '{': + + if (!new_value(&state, &top, &root, &alloc, + json_object)) { + goto e_alloc_failure; + } + + continue; + + case '[': + + if (!new_value(&state, &top, &root, &alloc, + json_array)) { + goto e_alloc_failure; + } + + flags |= flag_seek_value; + continue; + + case '"': + + if (!new_value(&state, &top, &root, &alloc, + json_string)) { + goto e_alloc_failure; + } + + flags |= flag_string; + + string = top->u.string.ptr; + string_length = 0; + + continue; + + case 't': + + if ((end - i) < 3 || *(++i) != 'r' || *(++i) != 'u' || + *(++i) != 'e') { + goto e_unknown_value; + } + + if (!new_value(&state, &top, &root, &alloc, + json_boolean)) { + goto e_alloc_failure; + } + + top->u.boolean = 1; + + flags |= flag_next; + break; + + case 'f': + + if ((end - i) < 4 || *(++i) != 'a' || *(++i) != 'l' || + *(++i) != 's' || *(++i) != 'e') { + goto e_unknown_value; + } + + if (!new_value(&state, &top, &root, &alloc, + json_boolean)) { + goto e_alloc_failure; + } + + flags |= flag_next; + break; + + case 'n': + + if ((end - i) < 3 || *(++i) != 'u' || *(++i) != 'l' || + *(++i) != 'l') { + goto e_unknown_value; + } + + if (!new_value(&state, &top, &root, &alloc, + json_null)) { + goto e_alloc_failure; + } + + flags |= flag_next; + break; + + default: + + if (isdigit((uint8_t)b) || b == '-') { + if (!new_value(&state, &top, &root, &alloc, + json_integer)) { + goto e_alloc_failure; + } + + if (!state.first_pass) { + while (isdigit((uint8_t)b) || b == '+' || b == + '-' + || b == 'e' || b == 'E' || b == '.') { + if ((++i) == end) { + b = 0; + break; + } + + b = *i; + } + + flags |= flag_next | flag_reproc; + break; + } + + flags &= ~(flag_num_negative | flag_num_e | + flag_num_e_got_sign | + flag_num_e_negative | + flag_num_zero); + + num_digits = 0; + num_fraction = 0; + num_e = 0; + + if (b != '-') { + flags |= flag_reproc; + break; + } + + flags |= flag_num_negative; + continue; + } else { + sprintf(error, + "%d:%d: Unexpected %c when seeking value", + cur_line, e_off, b); + goto e_failed; + } + } + } + } else { + switch (top->type) { + case json_object: + + switch (b) { +whitespace: + continue; + + case '"': + + if (flags & flag_need_comma) { + sprintf(error, "%d:%d: Expected , before \"", + cur_line, e_off); + goto e_failed; + } + + flags |= flag_string; + + string = (json_char *)top->_reserved.object_mem; + string_length = 0; + + break; + + case '}': + + flags = (flags & ~flag_need_comma) | flag_next; + break; + + case ',': + + if (flags & flag_need_comma) { + flags &= ~flag_need_comma; + break; + } + + default: + + sprintf(error, "%d:%d: Unexpected `%c` in object", + cur_line, e_off, b); + goto e_failed; + } + + break; + + case json_integer: + case json_double: + + if (isdigit((uint8_t)b)) { + ++num_digits; + + if (top->type == json_integer || flags & flag_num_e) { + if (!(flags & flag_num_e)) { + if (flags & flag_num_zero) { + sprintf(error, + "%d:%d: Unexpected `0` before `%c`", + cur_line, e_off, b); + goto e_failed; + } + + if (num_digits == 1 && b == '0') { + flags |= flag_num_zero; + } + } else { + flags |= flag_num_e_got_sign; + num_e = (num_e * 10) + (b - '0'); + continue; + } + + top->u.integer = (top->u.integer * 10) + (b - '0'); + continue; + } + + num_fraction = (num_fraction * 10) + (b - '0'); + continue; + } + + if (b == '+' || b == '-') { + if ((flags & flag_num_e) && + !(flags & flag_num_e_got_sign)) { + flags |= flag_num_e_got_sign; + + if (b == '-') { + flags |= flag_num_e_negative; + } + + continue; + } + } else if (b == '.' && top->type == json_integer) { + if (!num_digits) { + sprintf(error, "%d:%d: Expected digit before `.`", + cur_line, e_off); + goto e_failed; + } + + top->type = json_double; + top->u.dbl = (double)top->u.integer; + + num_digits = 0; + continue; + } + + if (!(flags & flag_num_e)) { + if (top->type == json_double) { + if (!num_digits) { + sprintf(error, + "%d:%d: Expected digit after `.`", + cur_line, e_off); + goto e_failed; + } + + top->u.dbl += ((double)num_fraction) / + (pow(10, (double)num_digits)); + } + + if (b == 'e' || b == 'E') { + flags |= flag_num_e; + + if (top->type == json_integer) { + top->type = json_double; + top->u.dbl = (double)top->u.integer; + } + + num_digits = 0; + flags &= ~flag_num_zero; + + continue; + } + } else { + if (!num_digits) { + sprintf(error, "%d:%d: Expected digit after `e`", + cur_line, e_off); + goto e_failed; + } + + top->u.dbl *= + pow(10, + (double)((flags & + flag_num_e_negative) ? -num_e : num_e)); + } + + if (flags & flag_num_negative) { + if (top->type == json_integer) { + top->u.integer = -top->u.integer; + } else { + top->u.dbl = -top->u.dbl; + } + } + + flags |= flag_next | flag_reproc; + break; + + default: + break; + } + } + + if (flags & flag_reproc) { + flags &= ~flag_reproc; + --i; + } + + if (flags & flag_next) { + flags = (flags & ~flag_next) | flag_need_comma; + + if (!top->parent) { + /* root value done */ + + flags |= flag_done; + continue; + } + + if (top->parent->type == json_array) { + flags |= flag_seek_value; + } + + if (!state.first_pass) { + json_value *parent = top->parent; + + switch (parent->type) { + case json_object: + + parent->u.object.values + [parent->u.object.length].value = top; + + break; + + case json_array: + + parent->u.array.values + [parent->u.array.length] = top; + + break; + + default: + break; + } + } + + if ((++top->parent->u.array.length) > state.uint_max) { + goto e_overflow; + } + + top = top->parent; + + continue; + } + } + + alloc = root; + } + + return root; + +e_unknown_value: + + sprintf(error, "%d:%d: Unknown value", cur_line, e_off); + goto e_failed; + +e_alloc_failure: + + strcpy(error, "Memory allocation failure"); + goto e_failed; + +e_overflow: + + sprintf(error, "%d:%d: Too long (caught overflow)", cur_line, e_off); + goto e_failed; + +e_failed: + + if (error_buf) { + if (*error) { + strcpy(error_buf, error); + } else { + strcpy(error_buf, "Unknown error"); + } + } + + if (state.first_pass) { + alloc = root; + } + + while (alloc) { + top = alloc->_reserved.next_alloc; + state.settings.mem_free(alloc, state.settings.user_data); + alloc = top; + } + + if (!state.first_pass) { + json_value_free_ex(&state.settings, root); + } + + return 0; +} + +json_value * +json_parse(const json_char *json, size_t length) +{ + json_settings settings = { 0UL, 0, NULL, NULL, NULL }; + return json_parse_ex(&settings, json, length, 0); +} + +void +json_value_free_ex(json_settings *settings, json_value *value) +{ + json_value *cur_value; + + if (!value) { + return; + } + + value->parent = 0; + + while (value) { + switch (value->type) { + case json_array: + + if (!value->u.array.length) { + settings->mem_free(value->u.array.values, settings->user_data); + break; + } + + value = value->u.array.values[--value->u.array.length]; + continue; + + case json_object: + + if (!value->u.object.length) { + settings->mem_free(value->u.object.values, settings->user_data); + break; + } + + value = value->u.object.values[--value->u.object.length].value; + continue; + + case json_string: + + settings->mem_free(value->u.string.ptr, settings->user_data); + break; + + default: + break; + } + + cur_value = value; + value = value->parent; + settings->mem_free(cur_value, settings->user_data); + } +} + +void +json_value_free(json_value *value) +{ + json_settings settings = { 0UL, 0, NULL, NULL, NULL }; + settings.mem_free = default_free; + json_value_free_ex(&settings, value); +} diff --git a/server/json.h b/server/json.h new file mode 100644 index 0000000..016fc5a --- /dev/null +++ b/server/json.h @@ -0,0 +1,249 @@ +/* vim: set et ts=3 sw=3 sts=3 ft=c: + * + * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. + * https://github.com/udp/json-parser + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _JSON_H +#define _JSON_H + +#ifndef json_char +#define json_char char +#endif + +#ifndef json_int_t +#ifndef _MSC_VER +#include +#define json_int_t int64_t +#else +#define json_int_t __int64 +#endif +#endif + +#include + +#ifdef __cplusplus + +#include + +extern "C" +{ +#endif + +typedef struct { + unsigned long max_memory; + int settings; + + /* Custom allocator support (leave null to use malloc/free) + */ + + void * (*mem_alloc)(size_t, int zero, void *user_data); + void (*mem_free)(void *, void *user_data); + + void *user_data; /* will be passed to mem_alloc and mem_free */ +} json_settings; + +#define json_enable_comments 0x01 + +typedef enum { + json_none, + json_object, + json_array, + json_integer, + json_double, + json_string, + json_boolean, + json_null +} json_type; + +extern const struct _json_value json_value_none; + +typedef struct _json_value { + struct _json_value *parent; + + json_type type; + + union { + int boolean; + json_int_t integer; + double dbl; + + struct { + unsigned int length; + json_char *ptr; /* null terminated */ + } string; + + struct { + unsigned int length; + + struct { + json_char *name; + unsigned int name_length; + + struct _json_value *value; + } *values; + +#if defined(__cplusplus) && __cplusplus >= 201103L + decltype(values) begin() const + { + return values; + } + decltype(values) end() const + { + return values + length; + } +#endif + } object; + + struct { + unsigned int length; + struct _json_value **values; + +#if defined(__cplusplus) && __cplusplus >= 201103L + decltype(values) begin() const + { + return values; + } + decltype(values) end() const + { + return values + length; + } +#endif + } array; + } u; + + union { + struct _json_value *next_alloc; + void *object_mem; + } _reserved; + + /* Some C++ operator sugar */ + +#ifdef __cplusplus + +public: + + inline _json_value(){ + memset(this, 0, sizeof(_json_value)); + } + + inline const struct _json_value &operator [] (int index) const { + if (type != json_array || index < 0 + || ((unsigned int)index) >= u.array.length) { + return json_value_none; + } + + return *u.array.values[index]; + } + + inline const struct _json_value &operator [] (const char *index) const { + if (type != json_object) { + return json_value_none; + } + + for (unsigned int i = 0; i < u.object.length; ++i) + if (!strcmp(u.object.values[i].name, index)) { + return *u.object.values[i].value; + } + + return json_value_none; + } + + inline operator const char * () const + { + switch (type) { + case json_string: + return u.string.ptr; + + default: + return ""; + } + } + + inline operator + json_int_t() const + { + switch (type) { + case json_integer: + return u.integer; + + case json_double: + return (json_int_t)u.dbl; + + default: + return 0; + } + } + + inline operator + bool() const + { + if (type != json_boolean) { + return false; + } + + return u.boolean != 0; + } + + inline operator double () const + { + switch (type) { + case json_integer: + return (double)u.integer; + + case json_double: + return u.dbl; + + default: + return 0; + } + } + +#endif +} json_value; + +json_value *json_parse(const json_char *json, + size_t length); + +#define json_error_max 128 +json_value *json_parse_ex(json_settings *settings, + const json_char *json, + size_t length, + char *error); + +void json_value_free(json_value *); + +/* Not usually necessary, unless you used a custom mem_alloc and now want to + * use a custom mem_free. + */ +void json_value_free_ex(json_settings *settings, + json_value *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/server/list.c b/server/list.c new file mode 100644 index 0000000..dde085d --- /dev/null +++ b/server/list.c @@ -0,0 +1,370 @@ +#include "list.h" + +/// 文件:list_impl.c +/// 功能:实现链表的基本操作 +/// 作者:bluewind +/// 完成时间:2011.5.29 +/// 修改时间:2011.5.31, 2011.7.2 +/// 修改备注:在头节点处添加一个空节点,可以优化添加、删除节点代码 +/// 再次修改,链表增加节点数据data_size,限制数据大小,修改了 +/// 添加复制数据代码,修正重复添加节点后释放节点的Bug,添加了前 +/// 插、排序和遍历功能,7.3 添加tail尾指针,改进后插法性能,并改名 +/// -------------------------------------------------------------- + +void swap_data(Node n1, Node n2); + +/// -------------------------------------------------------------- +// 函数名:list_init +// 功能: 链表初始化 +// 参数: 无 +// 返回值:已初始化链表指针 +// 备注: 链表本身动态分配,由list_destroy函数管理释放 +/// -------------------------------------------------------------- +List list_init(unsigned int data_size) +{ + List list = (List) malloc(sizeof(struct clist)); + if(list != NULL) //内存分配成功 + { + list->head = (Node) malloc(sizeof(node)); //为头节点分配内存 + if(list->head) //内存分配成功 + { + list->head->data = NULL; //初始化头节点 + list->head->next = NULL; + list->data_size = data_size; + list->tail = list->head; + list->size = 0; + + list->add_back = list_add_back; //初始化成员函数 + list->add_front = list_add_front; + list->delete_node = list_delete_node; + list->delete_at = list_delete_at; + list->modify_at = list_modify_at; + list->have_same = list_have_same; + list->have_same_cmp = list_have_same_cmp; + list->foreach = list_foreach; + list->clear = list_clear; + list->sort = list_sort; + list->destroy = list_destroy; + } + } + return list; +} + +/// -------------------------------------------------------------- +// 函数名:list_add_back +// 功能: 添加链表结点 (后插法) +// 参数: l--链表指针,data--链表数据指针,可为任意类型 +// 返回值:int型,为1表示添加成功,为0表示添加失败 +// 备注: 如果链表本身为空或是分配节点内存失败,将返回0 +/// -------------------------------------------------------------- +int list_add_back(List l, void *data) +{ + Node new_node = (Node) malloc(sizeof(node)); + + if(l != NULL && new_node != NULL) //链表本身不为空,且内存申请成功 + { + new_node->data = malloc(l->data_size); + memcpy(new_node->data, data, l->data_size); + new_node->next = NULL; + + l->tail->next = new_node; //添加节点 + l->tail = new_node; //记录尾节点位置 + l->size ++; //链表元素总数加1 + + return 1; + } + + return 0; +} + +/// -------------------------------------------------------------- +// 函数名:list_add_front +// 功能: 添加链表结点 (前插法) +// 参数: l--链表指针,data--链表数据指针,可为任意类型 +// 返回值:int型,为1表示添加成功,为0表示添加失败 +// 备注: 如果链表本身为空或是分配节点内存失败,将返回0 +/// -------------------------------------------------------------- +int list_add_front(List l, void *data) +{ + Node new_node = (Node) malloc(sizeof(node)); + + if(l != NULL && new_node != NULL) + { + new_node->data = malloc(l->data_size); + memcpy(new_node->data, data, l->data_size); + new_node->next = l->head->next; + + l->head->next = new_node; + if(!l->size) //记录尾指针位置 + l->tail = new_node; + l->size ++; + + return 1; + } + + return 0; +} + +/// -------------------------------------------------------------- +// 函数名:list_delete_node +// 功能:删除链表结点 +// 参数:l--链表指针,data--链表数据指针,可为任意类型 +// *pfunc为指向一个数据类型比较的函数指针 +// 返回值:int型,为1表示删除成功,为0表示没有找到匹配数据 +// 备注:*pfunc函数接口参数ndata为节点数据,data为比较数据,返回为真表示匹配数据 +/// -------------------------------------------------------------- +int list_delete_node(List l, void *data, int (*pfunc)(void *ndata, void *data)) +{ + if(l != NULL) + { + Node prev = l->head; //前一个节点 + Node curr = l->head->next; //当前节点 + + while(curr != NULL) + { + if(pfunc(curr->data, data)) //如果找到匹配数据 + { + if(curr == l->tail) //如果是删除尾节点 + l->tail = prev; + + prev->next = prev->next->next; //修改前节点next指针指向下下个节点 + + free(curr->data); //释放节点数据 + free(curr); //释放节点 + + l->size--; //链表元素总数减1 + return 1; //返回真值 + } + prev = prev->next; //没有找到匹配时移动前节点和当前节点 + curr = curr->next; + } + } + + return 0; //没有找到匹配数据 +} + +/// -------------------------------------------------------------- +// 函数名:list_delete_at +// 功能: 修改链表节点元素值 +// 参数: l--链表指针,index--索引值, 范围(0 -- size-1) +// 返回值:int型,为1表示删除成功,为0表示删除失败 +// 备注: 如果链表本身为空或是index为非法值,将返回0 +/// -------------------------------------------------------------- +int list_delete_at(List l, unsigned int index) +{ + unsigned int cindex = 0; + + if(l != NULL && index >= 0 && index < l->size) + { + Node prev = l->head; //前一个节点 + Node curr = l->head->next; //当前节点 + + while(cindex != index) + { + prev = prev->next; + curr = curr->next; + cindex ++; + } + + if(index == (l->size) - 1) + l->tail = prev; + + prev->next = prev->next->next; + free(curr->data); + free(curr); + l->size --; + + return 1; + } + + return 0; +} + +/// -------------------------------------------------------------- +// 函数名:list_modify_at +// 功能: 修改链表节点元素值 +// 参数: l--链表指针,index--索引值, 范围(0 -- size-1) +// data--链表数据指针 +// 返回值:int型,为1表示修改成功,为0表示修改失败 +// 备注: 如果链表本身为空或是index为非法值,将返回0 +/// -------------------------------------------------------------- +int list_modify_at(List l, unsigned int index, void *new_data) +{ + unsigned int cindex = 0; + + if(l != NULL && index >= 0 && index < l->size ) //非空链表,并且index值合法 + { + Node curr = l->head->next; + while(cindex != index) + { + curr = curr->next; + cindex ++; + } + memcpy(curr->data, new_data, l->data_size); + return 1; + } + + return 0; +} + +/// -------------------------------------------------------------- +// 函数名:list_sort +// 功能: 链表排序 +// 参数: l--链表指针,*pfunc为指向一个数据类型比较的函数指针 +// 返回值:无 +// 备注: 使用简单选择排序法,相比冒泡法每次交换,效率高一点 +/// -------------------------------------------------------------- +void list_sort(List l, compare pfunc) +{ + if(l != NULL) + { + Node min, icurr, jcurr; + + icurr = l->head->next; + while(icurr) + { + min = icurr; //记录最小值 + jcurr = icurr->next; //内循环指向下一个节点 + while(jcurr) + { + if(pfunc(min->data, jcurr->data)) //如果找到n+1到最后一个元素最小值 + min = jcurr; //记录下最小值的位置 + + jcurr = jcurr->next; + } + + if(min != icurr) //当最小值位置和n+1元素位置不相同时 + { + swap_data(min, icurr); //才进行交换,减少交换次数 + } + + icurr = icurr->next; + } + } +} + +void swap_data(Node n1, Node n2) +{ + void *temp; + + temp = n2->data; + n2->data = n1->data; + n1->data = temp; +} + + +int list_have_same(List l, void *data, int (*pfunc)(void *ndata, void *data)) +{ + if(l != NULL) + { + Node curr; + + for(curr = l->head->next; curr != NULL; curr = curr->next) + { + if(pfunc(curr->data, data)) + { + return 1; + } + } + } + + return 0; +} + +int list_have_same_cmp(List l, void *data) +{ + if(l != NULL) + { + Node curr; + + for(curr = l->head->next; curr != NULL; curr = curr->next) + { + if(memcmp(curr->data, data, l->data_size)) + { + return 1; + } + } + } + + return 0; +} + +/// -------------------------------------------------------------- +// 函数名:list_foreach +// 功能: 遍历链表元素 +// 参数: l--链表指针,doit为指向一个处理数据的函数指针 +// 返回值:无 +// 备注: doit申明为void (*dofunc)(void *ndata)原型 +/// -------------------------------------------------------------- +void list_foreach(List l, dofunc doit) +{ + if(l != NULL) + { + Node curr; + + for(curr = l->head->next; curr != NULL; curr = curr->next) + { + doit(curr->data); + } + } +} + +/// -------------------------------------------------------------- +// 函数名:list_clear +// 功能: 清空链表元素 +// 参数: l--链表指针 +// 返回值:无 +// 备注: 没有使用先Destroy再Init链表的办法,直接实现 +/// -------------------------------------------------------------- +void list_clear(List l) +{ + if(l != NULL) + { + Node temp; + Node curr = l->head->next; + + while(curr != NULL) + { + temp = curr->next; + + free(curr->data); //释放节点和数据 + free(curr); + + curr = temp; + } + + l->size = 0; //重置链表数据 + l->head->next = NULL; + l->tail = l->head; + } +} + +/// -------------------------------------------------------------- +// 函数名:list_destroy +// 功能: 释放链表 +// 参数: l--链表指针 +// 返回值:空链表指针 +/// -------------------------------------------------------------- +List list_destroy(List l) +{ + if(l != NULL) + { + Node temp; + + while(l->head) + { + temp = l->head->next; + + if(l->head->data != NULL) //如果是头节点就不释放数据空间 + free(l->head->data); //先释放节点数据(但是节点数据里也有指针?) + free(l->head); //再释放节点 + + l->head = temp; + } + + free(l); //释放链表本身占用空间 + l = NULL; + } + + return l; +} diff --git a/server/list.h b/server/list.h new file mode 100644 index 0000000..ab49720 --- /dev/null +++ b/server/list.h @@ -0,0 +1,61 @@ +#ifndef LIST_H_H +#define LIST_H_H + +#include +#include +#include + +typedef struct clist *List; + +typedef int (*compare)(void *ndata, void *data); +typedef void (*dofunc)(void *ndata); + +typedef int (*lpf0)(List l, void *data); +typedef int (*lpf1)(List l, void *data, compare pfunc); +typedef List (*lpf2)(List l); +typedef void (*lpf3)(List l); +typedef void (*lpf4)(List l, dofunc pfunc); +typedef int (*lpf5)(List l, unsigned int index, void *new_data); +typedef void (*lpf6)(List l, compare pfunc); +typedef int (*lpf7)(List l, unsigned int index); + +typedef struct cnode +{ + void *data; + struct cnode *next; +}node, *Node; + +typedef struct clist +{ + Node head; + Node tail; + unsigned int size; + unsigned int data_size; + lpf0 add_back; + lpf0 add_front; + lpf1 delete_node; + lpf1 have_same; + lpf0 have_same_cmp; + lpf4 foreach; + lpf3 clear; + lpf2 destroy; + lpf5 modify_at; + lpf6 sort; + lpf7 delete_at; +}list; + +//初始化链表 +List list_init(unsigned int data_size); +int list_add_back(List l, void *data); +int list_add_front(List l, void *data); +int list_delete_node(List l, void *data, compare pfunc); +int list_delete_at(List l, unsigned int index); +int list_modify_at(List l, unsigned int index, void *new_data); +int list_have_same(List l, void *data, compare pfunc); +int list_have_same_cmp(List l, void *data); +void list_foreach(List l, dofunc doit); +void list_sort(List l, compare pfunc); +void list_clear(List l); +//释放链表 +List list_destroy(List l); +#endif diff --git a/server/netutils.c b/server/netutils.c new file mode 100644 index 0000000..3a32b4d --- /dev/null +++ b/server/netutils.c @@ -0,0 +1,297 @@ +/* + * netutils.c - Network utilities + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#include + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __MINGW32__ +#include "win32.h" +#define sleep(n) Sleep(1000 * (n)) +#else +#include +#include +#include +#include +#endif + +#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__) +#include +#include +#define SET_INTERFACE +#endif + +#include "netutils.h" +#include "utils.h" + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT 15 +#endif + +extern int verbose; + +static const char valid_label_bytes[] = + "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; + +#if defined(MODULE_LOCAL) +extern int keep_resolving; +#endif + +int +set_reuseport(int socket) +{ + int opt = 1; + return setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); +} + +size_t +get_sockaddr_len(struct sockaddr *addr) +{ + if (addr->sa_family == AF_INET) { + return sizeof(struct sockaddr_in); + } else if (addr->sa_family == AF_INET6) { + return sizeof(struct sockaddr_in6); + } + return 0; +} + +#ifdef SET_INTERFACE +int +setinterface(int socket_fd, const char *interface_name) +{ + struct ifreq interface; + memset(&interface, 0, sizeof(struct ifreq)); + strncpy(interface.ifr_name, interface_name, IFNAMSIZ); + int res = setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &interface, + sizeof(struct ifreq)); + return res; +} + +#endif + +int +bind_to_address(int socket_fd, const char *host) +{ + if (host != NULL) { + struct cork_ip ip; + struct sockaddr_storage storage; + memset(&storage, 0, sizeof(struct sockaddr_storage)); + if (cork_ip_init(&ip, host) != -1) { + if (ip.version == 4) { + struct sockaddr_in *addr = (struct sockaddr_in *)&storage; + dns_pton(AF_INET, host, &addr->sin_addr); + addr->sin_family = AF_INET; + return bind(socket_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); + } else if (ip.version == 6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; + dns_pton(AF_INET6, host, &addr->sin6_addr); + addr->sin6_family = AF_INET6; + return bind(socket_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in6)); + } + } + } + return -1; +} + +ssize_t +get_sockaddr(char *host, char *port, + struct sockaddr_storage *storage, int block, + int ipv6first) +{ + struct cork_ip ip; + if (cork_ip_init(&ip, host) != -1) { + if (ip.version == 4) { + struct sockaddr_in *addr = (struct sockaddr_in *)storage; + addr->sin_family = AF_INET; + dns_pton(AF_INET, host, &(addr->sin_addr)); + if (port != NULL) { + addr->sin_port = htons(atoi(port)); + } + } else if (ip.version == 6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; + addr->sin6_family = AF_INET6; + dns_pton(AF_INET6, host, &(addr->sin6_addr)); + if (port != NULL) { + addr->sin6_port = htons(atoi(port)); + } + } + return 0; + } else { + struct addrinfo hints; + struct addrinfo *result, *rp; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ + hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */ + + int err, i; + + for (i = 1; i < 8; i++) { + err = getaddrinfo(host, port, &hints, &result); +#if defined(MODULE_LOCAL) + if (!keep_resolving) + break; +#endif + if ((!block || !err)) { + break; + } else { + sleep(pow(2, i)); + LOGE("failed to resolve server name, wait %.0f seconds", pow(2, i)); + } + } + + if (err != 0) { + LOGE("getaddrinfo: %s", gai_strerror(err)); + return -1; + } + + int prefer_af = ipv6first ? AF_INET6 : AF_INET; + for (rp = result; rp != NULL; rp = rp->ai_next) + if (rp->ai_family == prefer_af) { + if (rp->ai_family == AF_INET) + memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in)); + else if (rp->ai_family == AF_INET6) + memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in6)); + break; + } + + if (rp == NULL) { + for (rp = result; rp != NULL; rp = rp->ai_next) { + if (rp->ai_family == AF_INET) + memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in)); + else if (rp->ai_family == AF_INET6) + memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in6)); + break; + } + } + + if (rp == NULL) { + LOGE("failed to resolve remote addr"); + return -1; + } + + freeaddrinfo(result); + return 0; + } + + return -1; +} + +int +sockaddr_cmp(struct sockaddr_storage *addr1, + struct sockaddr_storage *addr2, socklen_t len) +{ + struct sockaddr_in *p1_in = (struct sockaddr_in *)addr1; + struct sockaddr_in *p2_in = (struct sockaddr_in *)addr2; + struct sockaddr_in6 *p1_in6 = (struct sockaddr_in6 *)addr1; + struct sockaddr_in6 *p2_in6 = (struct sockaddr_in6 *)addr2; + if (p1_in->sin_family < p2_in->sin_family) + return -1; + if (p1_in->sin_family > p2_in->sin_family) + return 1; + /* compare ip4 */ + if (p1_in->sin_family == AF_INET) { + /* just order it, ntohs not required */ + if (p1_in->sin_port < p2_in->sin_port) + return -1; + if (p1_in->sin_port > p2_in->sin_port) + return 1; + return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, INET_SIZE); + } else if (p1_in6->sin6_family == AF_INET6) { + /* just order it, ntohs not required */ + if (p1_in6->sin6_port < p2_in6->sin6_port) + return -1; + if (p1_in6->sin6_port > p2_in6->sin6_port) + return 1; + return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr, + INET6_SIZE); + } else { + /* eek unknown type, perform this comparison for sanity. */ + return memcmp(addr1, addr2, len); + } +} + +int +sockaddr_cmp_addr(struct sockaddr_storage *addr1, + struct sockaddr_storage *addr2, socklen_t len) +{ + struct sockaddr_in *p1_in = (struct sockaddr_in *)addr1; + struct sockaddr_in *p2_in = (struct sockaddr_in *)addr2; + struct sockaddr_in6 *p1_in6 = (struct sockaddr_in6 *)addr1; + struct sockaddr_in6 *p2_in6 = (struct sockaddr_in6 *)addr2; + if (p1_in->sin_family < p2_in->sin_family) + return -1; + if (p1_in->sin_family > p2_in->sin_family) + return 1; + /* compare ip4 */ + if (p1_in->sin_family == AF_INET) { + return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, INET_SIZE); + } else if (p1_in6->sin6_family == AF_INET6) { + return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr, + INET6_SIZE); + } else { + /* eek unknown type, perform this comparison for sanity. */ + return memcmp(addr1, addr2, len); + } +} + +int +validate_hostname(const char *hostname, const int hostname_len) +{ + if (hostname == NULL) + return 0; + + if (hostname_len < 1 || hostname_len > 255) + return 0; + + if (hostname[0] == '.') + return 0; + + const char *label = hostname; + while (label < hostname + hostname_len) { + size_t label_len = hostname_len - (label - hostname); + char *next_dot = strchr(label, '.'); + if (next_dot != NULL) + label_len = next_dot - label; + + if (label + label_len > hostname + hostname_len) + return 0; + + if (label_len > 63 || label_len < 1) + return 0; + + if (label[0] == '-' || label[label_len - 1] == '-') + return 0; + + if (strspn(label, valid_label_bytes) < label_len) + return 0; + + label += label_len + 1; + } + + return 1; +} diff --git a/server/netutils.h b/server/netutils.h new file mode 100644 index 0000000..0725592 --- /dev/null +++ b/server/netutils.h @@ -0,0 +1,98 @@ +/* + * netutils.h - Network utilities + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _NETUTILS_H +#define _NETUTILS_H + +#if defined(__linux__) +#include +#elif !defined(__MINGW32__) +#include +#endif + +// only enable TCP_FASTOPEN on linux +#if defined(__linux__) +#include +/* conditional define for TCP_FASTOPEN */ +#ifndef TCP_FASTOPEN +#define TCP_FASTOPEN 23 +#endif +/* conditional define for MSG_FASTOPEN */ +#ifndef MSG_FASTOPEN +#define MSG_FASTOPEN 0x20000000 +#endif +#elif !defined(__APPLE__) +#ifdef TCP_FASTOPEN +#undef TCP_FASTOPEN +#endif +#endif + +/* Backward compatibility for MPTCP_ENABLED between kernel 3 & 4 */ +#ifndef MPTCP_ENABLED +#ifdef TCP_CC_INFO +#define MPTCP_ENABLED 42 +#else +#define MPTCP_ENABLED 26 +#endif +#endif + +/** byte size of ip4 address */ +#define INET_SIZE 4 +/** byte size of ip6 address */ +#define INET6_SIZE 16 + +size_t get_sockaddr_len(struct sockaddr *addr); +ssize_t get_sockaddr(char *host, char *port, + struct sockaddr_storage *storage, int block, + int ipv6first); +int set_reuseport(int socket); + +#ifdef SET_INTERFACE +int setinterface(int socket_fd, const char *interface_name); +#endif + +int bind_to_address(int socket_fd, const char *address); + +/** + * Compare two sockaddrs. Imposes an ordering on the addresses. + * Compares address and port. + * @param addr1: address 1. + * @param addr2: address 2. + * @param len: lengths of addr. + * @return: 0 if addr1 == addr2. -1 if addr1 is smaller, +1 if larger. + */ +int sockaddr_cmp(struct sockaddr_storage *addr1, + struct sockaddr_storage *addr2, socklen_t len); + +/** + * Compare two sockaddrs. Compares address, not the port. + * @param addr1: address 1. + * @param addr2: address 2. + * @param len: lengths of addr. + * @return: 0 if addr1 == addr2. -1 if addr1 is smaller, +1 if larger. + */ +int sockaddr_cmp_addr(struct sockaddr_storage *addr1, + struct sockaddr_storage *addr2, socklen_t len); + +int validate_hostname(const char *hostname, const int hostname_len); + +#endif diff --git a/server/obfs.c b/server/obfs.c new file mode 100644 index 0000000..5c885bf --- /dev/null +++ b/server/obfs.c @@ -0,0 +1,205 @@ +#include +#include + +#include "utils.h" +#include "obfs.h" + +int rand_bytes(uint8_t *output, int len); +#define OBFS_HMAC_SHA1_LEN 10 + +#include "obfsutil.c" +#include "crc32.c" +#include "base64.c" +#include "http_simple.c" +#include "tls1.2_ticket.c" +#include "verify.c" +#include "auth.c" + +void * init_data() { + return malloc(1); +} + +obfs * new_obfs() { + obfs * self = (obfs*)malloc(sizeof(obfs)); + self->l_data = NULL; + return self; +} + +void set_server_info(obfs *self, server_info *server) { + memmove(&self->server, server, sizeof(server_info)); +} + +void get_server_info(obfs *self, server_info *server) { + memmove(server, &self->server, sizeof(server_info)); +} + +void dispose_obfs(obfs *self) { + free(self); +} + +obfs_class * new_obfs_class(char *plugin_name) +{ + if (plugin_name == NULL) + return NULL; + if (strcmp(plugin_name, "origin") == 0) + return NULL; + if (strcmp(plugin_name, "plain") == 0) + return NULL; + init_crc32_table(); + init_shift128plus(); + if (strcmp(plugin_name, "http_simple") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = init_data; + plugin->new_obfs = http_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = http_simple_dispose; + + plugin->client_encode = http_simple_client_encode; + plugin->client_decode = http_simple_client_decode; + + plugin->server_encode = http_simple_server_encode; + plugin->server_decode = http_simple_server_decode; + + return plugin; + } else if (strcmp(plugin_name, "http_post") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = init_data; + plugin->new_obfs = http_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = http_simple_dispose; + + plugin->client_encode = http_post_client_encode; + plugin->client_decode = http_simple_client_decode; + + plugin->server_encode = http_simple_server_encode; + plugin->server_decode = http_simple_server_decode; + + return plugin; + } else if (strcmp(plugin_name, "tls1.2_ticket_auth") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = tls12_ticket_auth_init_data; + plugin->new_obfs = tls12_ticket_auth_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = tls12_ticket_auth_dispose; + + plugin->client_encode = tls12_ticket_auth_client_encode; + plugin->client_decode = tls12_ticket_auth_client_decode; + + plugin->server_encode = tls12_ticket_auth_server_encode; + plugin->server_decode = tls12_ticket_auth_server_decode; + + return plugin; + } else if (strcmp(plugin_name, "verify_simple") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = init_data; + plugin->new_obfs = verify_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = verify_simple_dispose; + + plugin->client_pre_encrypt = verify_simple_client_pre_encrypt; + plugin->client_post_decrypt = verify_simple_client_post_decrypt; + plugin->client_udp_pre_encrypt = NULL; + plugin->client_udp_post_decrypt = NULL; + + plugin->server_pre_encrypt = verify_simple_server_pre_encrypt; + plugin->server_post_decrypt = verify_simple_server_post_decrypt; + plugin->server_udp_pre_encrypt = NULL; + plugin->server_udp_post_decrypt = NULL; + + return plugin; + } else if (strcmp(plugin_name, "auth_simple") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = auth_simple_init_data; + plugin->new_obfs = auth_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = auth_simple_dispose; + + plugin->client_pre_encrypt = auth_simple_client_pre_encrypt; + plugin->client_post_decrypt = auth_simple_client_post_decrypt; + plugin->client_udp_pre_encrypt = NULL; + plugin->client_udp_post_decrypt = NULL; + + return plugin; + } else if (strcmp(plugin_name, "auth_sha1") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = auth_simple_init_data; + plugin->new_obfs = auth_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = auth_simple_dispose; + + plugin->client_pre_encrypt = auth_sha1_client_pre_encrypt; + plugin->client_post_decrypt = auth_sha1_client_post_decrypt; + plugin->client_udp_pre_encrypt = NULL; + plugin->client_udp_post_decrypt = NULL; + + return plugin; + } else if (strcmp(plugin_name, "auth_sha1_v2") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = auth_simple_init_data; + plugin->new_obfs = auth_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = auth_simple_dispose; + + plugin->client_pre_encrypt = auth_sha1_v2_client_pre_encrypt; + plugin->client_post_decrypt = auth_sha1_v2_client_post_decrypt; + plugin->client_udp_pre_encrypt = NULL; + plugin->client_udp_post_decrypt = NULL; + + return plugin; + } else if (strcmp(plugin_name, "auth_sha1_v4") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = auth_simple_init_data; + plugin->new_obfs = auth_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = auth_simple_dispose; + + plugin->client_pre_encrypt = auth_sha1_v4_client_pre_encrypt; + plugin->client_post_decrypt = auth_sha1_v4_client_post_decrypt; + plugin->client_udp_pre_encrypt = NULL; + plugin->client_udp_post_decrypt = NULL; + + return plugin; + } else if (strcmp(plugin_name, "auth_aes128_md5") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = auth_simple_init_data; + plugin->new_obfs = auth_aes128_md5_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = auth_simple_dispose; + + plugin->client_pre_encrypt = auth_aes128_sha1_client_pre_encrypt; + plugin->client_post_decrypt = auth_aes128_sha1_client_post_decrypt; + plugin->client_udp_pre_encrypt = auth_aes128_sha1_client_udp_pre_encrypt; + plugin->client_udp_post_decrypt = auth_aes128_sha1_client_udp_post_decrypt; + + return plugin; + } else if (strcmp(plugin_name, "auth_aes128_sha1") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = auth_simple_init_data; + plugin->new_obfs = auth_aes128_sha1_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = auth_simple_dispose; + + plugin->client_pre_encrypt = auth_aes128_sha1_client_pre_encrypt; + plugin->client_post_decrypt = auth_aes128_sha1_client_post_decrypt; + plugin->client_udp_pre_encrypt = auth_aes128_sha1_client_udp_pre_encrypt; + plugin->client_udp_post_decrypt = auth_aes128_sha1_client_udp_post_decrypt; + + return plugin; + } + LOGE("Load obfs '%s' failed", plugin_name); + return NULL; +} + +void free_obfs_class(obfs_class *plugin) { + free(plugin); +} diff --git a/server/obfs.h b/server/obfs.h new file mode 100644 index 0000000..74c60c9 --- /dev/null +++ b/server/obfs.h @@ -0,0 +1,100 @@ +/* + * obfs.h - Define shadowsocksR server's buffers and callbacks + * + * Copyright (C) 2015 - 2016, Break Wa11 + */ + +#ifndef _OBFS_H +#define _OBFS_H + +#include +#include + +typedef struct server_info { + char host[64]; + uint16_t port; + char *param; + void *g_data; + uint8_t *iv; + size_t iv_len; + uint8_t *recv_iv; + size_t recv_iv_len; + uint8_t *key; + size_t key_len; + int head_len; + size_t tcp_mss; +}server_info; + +typedef struct obfs { + server_info server; + void *l_data; +}obfs; + +typedef struct obfs_class { + void * (*init_data)(); + obfs * (*new_obfs)(); + void (*get_server_info)(obfs *self, server_info *server); + void (*set_server_info)(obfs *self, server_info *server); + void (*dispose)(obfs *self); + + int (*client_pre_encrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*client_encode)(obfs *self, + char **pencryptdata, + int datalength, + size_t* capacity); + int (*client_decode)(obfs *self, + char **pencryptdata, + int datalength, + size_t* capacity, + int *needsendback); + int (*client_post_decrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*client_udp_pre_encrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*client_udp_post_decrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*server_pre_encrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*server_post_decrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*server_udp_pre_encrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*server_udp_post_decrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*server_encode)(obfs *self, + char **pencryptdata, + int datalength, + size_t* capacity); + int (*server_decode)(obfs *self, + char **pencryptdata, + int datalength, + size_t* capacity, + int *needsendback); +}obfs_class; + +obfs_class * new_obfs_class(char *plugin_name); +void free_obfs_class(obfs_class *plugin); + +void set_server_info(obfs *self, server_info *server); +void get_server_info(obfs *self, server_info *server); +obfs * new_obfs(); +void dispose_obfs(obfs *self); + +#endif // _OBFS_H diff --git a/server/obfsutil.c b/server/obfsutil.c new file mode 100644 index 0000000..d00959b --- /dev/null +++ b/server/obfsutil.c @@ -0,0 +1,36 @@ +int get_head_size(char *plaindata, int size, int def_size) { + if (plaindata == NULL || size < 2) + return def_size; + int head_type = plaindata[0] & 0x7; + if (head_type == 1) + return 7; + if (head_type == 4) + return 19; + if (head_type == 3) + return 4 + plaindata[1]; + return def_size; +} + +static int shift128plus_init_flag = 0; +static uint64_t shift128plus_s[2] = {0x10000000, 0xFFFFFFFF}; + +void init_shift128plus(void) { + if (shift128plus_init_flag == 0) { + shift128plus_init_flag = 1; + uint32_t seed = time(NULL); + shift128plus_s[0] = seed | 0x100000000L; + shift128plus_s[1] = ((uint64_t)seed << 32) | 0x1; + } +} + +uint64_t xorshift128plus(void) { + uint64_t x = shift128plus_s[0]; + uint64_t const y = shift128plus_s[1]; + shift128plus_s[0] = y; + x ^= x << 23; // a + x ^= x >> 17; // b + x ^= y ^ (y >> 26); // c + shift128plus_s[1] = x; + return x + y; +} + diff --git a/server/protocol.h b/server/protocol.h new file mode 100644 index 0000000..eaa866e --- /dev/null +++ b/server/protocol.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PROTOCOL_H +#define PROTOCOL_H + +typedef struct protocol { + const int default_port; + int(*const parse_packet)(const char *, size_t, char **); +} protocol_t; + +#endif diff --git a/server/resolv.c b/server/resolv.c new file mode 100644 index 0000000..f580d06 --- /dev/null +++ b/server/resolv.c @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2014, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef __MINGW32__ +#include "win32.h" +#else +#include +#include +#include +#include +#endif + +#include "resolv.h" +#include "utils.h" +#include "netutils.h" + +/* + * Implement DNS resolution interface using libudns + */ + +struct ResolvQuery { + void (*client_cb)(struct sockaddr *, void *); + void (*client_free_cb)(void *); + void *client_cb_data; + struct dns_query *queries[2]; + size_t response_count; + struct sockaddr **responses; + uint16_t port; +}; + +extern int verbose; + +static struct ev_io resolv_io_watcher; +static struct ev_timer resolv_timeout_watcher; +static const int MODE_IPV4_ONLY = 0; +static const int MODE_IPV6_ONLY = 1; +static const int MODE_IPV4_FIRST = 2; +static const int MODE_IPV6_FIRST = 3; +static int resolv_mode = 0; + +static void resolv_sock_cb(struct ev_loop *, struct ev_io *, int); +static void resolv_timeout_cb(struct ev_loop *, struct ev_timer *, int); +static void dns_query_v4_cb(struct dns_ctx *, struct dns_rr_a4 *, void *); +static void dns_query_v6_cb(struct dns_ctx *, struct dns_rr_a6 *, void *); +static void dns_timer_setup_cb(struct dns_ctx *, int, void *); +static void process_client_callback(struct ResolvQuery *); +static inline int all_queries_are_null(struct ResolvQuery *); +static struct sockaddr *choose_ipv4_first(struct ResolvQuery *); +static struct sockaddr *choose_ipv6_first(struct ResolvQuery *); +static struct sockaddr *choose_any(struct ResolvQuery *); + +int +resolv_init(struct ev_loop *loop, char **nameservers, int nameserver_num, int ipv6first) +{ + if (ipv6first) + resolv_mode = MODE_IPV6_FIRST; + else + resolv_mode = MODE_IPV4_FIRST; + + struct dns_ctx *ctx = &dns_defctx; + if (nameservers == NULL) { + /* Nameservers not specified, use system resolver config */ + dns_init(ctx, 0); + } else { + dns_reset(ctx); + + for (int i = 0; i < nameserver_num; i++) { + char *server = nameservers[i]; + dns_add_serv(ctx, server); + } + } + + int sockfd = dns_open(ctx); + if (sockfd < 0) { + FATAL("Failed to open DNS resolver socket"); + } + + if (nameserver_num == 1 && nameservers != NULL) { + if (strncmp("127.0.0.1", nameservers[0], 9) == 0 + || strncmp("::1", nameservers[0], 3) == 0) { + if (verbose) { + LOGI("bind UDP resolver to %s", nameservers[0]); + } + if (bind_to_address(sockfd, nameservers[0]) == -1) + ERROR("bind_to_address"); + } + } + +#ifdef __MINGW32__ + setnonblocking(sockfd); +#else + int flags = fcntl(sockfd, F_GETFL, 0); + fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); +#endif + + ev_io_init(&resolv_io_watcher, resolv_sock_cb, sockfd, EV_READ); + resolv_io_watcher.data = ctx; + + ev_io_start(loop, &resolv_io_watcher); + + ev_timer_init(&resolv_timeout_watcher, resolv_timeout_cb, 0.0, 0.0); + resolv_timeout_watcher.data = ctx; + + dns_set_tmcbck(ctx, dns_timer_setup_cb, loop); + + return sockfd; +} + +void +resolv_shutdown(struct ev_loop *loop) +{ + struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; + + ev_io_stop(loop, &resolv_io_watcher); + + if (ev_is_active(&resolv_timeout_watcher)) { + ev_timer_stop(loop, &resolv_timeout_watcher); + } + + dns_close(ctx); +} + +struct ResolvQuery * +resolv_query(const char *hostname, void (*client_cb)(struct sockaddr *, void *), + void (*client_free_cb)(void *), void *client_cb_data, + uint16_t port) +{ + struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; + + /* + * Wrap udns's call back in our own + */ + struct ResolvQuery *cb_data = ss_malloc(sizeof(struct ResolvQuery)); + if (cb_data == NULL) { + LOGE("Failed to allocate memory for DNS query callback data."); + return NULL; + } + memset(cb_data, 0, sizeof(struct ResolvQuery)); + + cb_data->client_cb = client_cb; + cb_data->client_free_cb = client_free_cb; + cb_data->client_cb_data = client_cb_data; + memset(cb_data->queries, 0, sizeof(cb_data->queries)); + cb_data->response_count = 0; + cb_data->responses = NULL; + cb_data->port = port; + + /* Submit A and AAAA queries */ + if (resolv_mode != MODE_IPV6_ONLY) { + cb_data->queries[0] = dns_submit_a4(ctx, + hostname, 0, + dns_query_v4_cb, cb_data); + if (cb_data->queries[0] == NULL) { + LOGE("Failed to submit DNS query: %s", + dns_strerror(dns_status(ctx))); + } + } + + if (resolv_mode != MODE_IPV4_ONLY) { + cb_data->queries[1] = dns_submit_a6(ctx, + hostname, 0, + dns_query_v6_cb, cb_data); + if (cb_data->queries[1] == NULL) { + LOGE("Failed to submit DNS query: %s", + dns_strerror(dns_status(ctx))); + } + } + + if (all_queries_are_null(cb_data)) { + if (cb_data->client_free_cb != NULL) { + cb_data->client_free_cb(cb_data->client_cb_data); + } + ss_free(cb_data); + } + + return cb_data; +} + +void +resolv_cancel(struct ResolvQuery *query_handle) +{ + struct ResolvQuery *cb_data = (struct ResolvQuery *)query_handle; + struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; + + for (int i = 0; i < sizeof(cb_data->queries) / sizeof(cb_data->queries[0]); + i++) + if (cb_data->queries[i] != NULL) { + dns_cancel(ctx, cb_data->queries[i]); + ss_free(cb_data->queries[i]); + } + + if (cb_data->client_free_cb != NULL) { + cb_data->client_free_cb(cb_data->client_cb_data); + } + + ss_free(cb_data); +} + +/* + * DNS UDP socket activity callback + */ +static void +resolv_sock_cb(struct ev_loop *loop, struct ev_io *w, int revents) +{ + struct dns_ctx *ctx = (struct dns_ctx *)w->data; + + if (revents & EV_READ) { + dns_ioevent(ctx, ev_now(loop)); + } +} + +/* + * Wrapper for client callback we provide to udns + */ +static void +dns_query_v4_cb(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data) +{ + struct ResolvQuery *cb_data = (struct ResolvQuery *)data; + + if (result == NULL) { + if (verbose) { + LOGI("IPv4 resolv: %s", dns_strerror(dns_status(ctx))); + } + } else if (result->dnsa4_nrr > 0) { + struct sockaddr **new_responses = ss_realloc(cb_data->responses, + (cb_data->response_count + + result->dnsa4_nrr) * + sizeof(struct sockaddr *)); + if (new_responses == NULL) { + LOGE("Failed to allocate memory for additional DNS responses"); + } else { + cb_data->responses = new_responses; + + for (int i = 0; i < result->dnsa4_nrr; i++) { + struct sockaddr_in *sa = + (struct sockaddr_in *)ss_malloc(sizeof(struct sockaddr_in)); + sa->sin_family = AF_INET; + sa->sin_port = cb_data->port; + sa->sin_addr = result->dnsa4_addr[i]; + + cb_data->responses[cb_data->response_count] = + (struct sockaddr *)sa; + if (cb_data->responses[cb_data->response_count] == NULL) { + LOGE( + "Failed to allocate memory for DNS query result address"); + } else { + cb_data->response_count++; + } + } + } + } + + ss_free(result); + cb_data->queries[0] = NULL; /* mark A query as being completed */ + + /* Once all queries have completed, call client callback */ + if (all_queries_are_null(cb_data)) { + return process_client_callback(cb_data); + } +} + +static void +dns_query_v6_cb(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data) +{ + struct ResolvQuery *cb_data = (struct ResolvQuery *)data; + + if (result == NULL) { + if (verbose) { + LOGI("IPv6 resolv: %s", dns_strerror(dns_status(ctx))); + } + } else if (result->dnsa6_nrr > 0) { + struct sockaddr **new_responses = ss_realloc(cb_data->responses, + (cb_data->response_count + + result->dnsa6_nrr) * + sizeof(struct sockaddr *)); + if (new_responses == NULL) { + LOGE("Failed to allocate memory for additional DNS responses"); + } else { + cb_data->responses = new_responses; + + for (int i = 0; i < result->dnsa6_nrr; i++) { + struct sockaddr_in6 *sa = + (struct sockaddr_in6 *)ss_malloc(sizeof(struct sockaddr_in6)); + sa->sin6_family = AF_INET6; + sa->sin6_port = cb_data->port; + sa->sin6_addr = result->dnsa6_addr[i]; + + cb_data->responses[cb_data->response_count] = + (struct sockaddr *)sa; + if (cb_data->responses[cb_data->response_count] == NULL) { + LOGE( + "Failed to allocate memory for DNS query result address"); + } else { + cb_data->response_count++; + } + } + } + } + + ss_free(result); + cb_data->queries[1] = NULL; /* mark AAAA query as being completed */ + + /* Once all queries have completed, call client callback */ + if (all_queries_are_null(cb_data)) { + return process_client_callback(cb_data); + } +} + +/* + * Called once all queries have been completed + */ +static void +process_client_callback(struct ResolvQuery *cb_data) +{ + struct sockaddr *best_address = NULL; + + if (resolv_mode == MODE_IPV4_FIRST) { + best_address = choose_ipv4_first(cb_data); + } else if (resolv_mode == MODE_IPV6_FIRST) { + best_address = choose_ipv6_first(cb_data); + } else { + best_address = choose_any(cb_data); + } + + cb_data->client_cb(best_address, cb_data->client_cb_data); + + for (int i = 0; i < cb_data->response_count; i++) + ss_free(cb_data->responses[i]); + + ss_free(cb_data->responses); + if (cb_data->client_free_cb != NULL) { + cb_data->client_free_cb(cb_data->client_cb_data); + } + ss_free(cb_data); +} + +static struct sockaddr * +choose_ipv4_first(struct ResolvQuery *cb_data) +{ + for (int i = 0; i < cb_data->response_count; i++) + if (cb_data->responses[i]->sa_family == AF_INET) { + return cb_data->responses[i]; + } + + return choose_any(cb_data); +} + +static struct sockaddr * +choose_ipv6_first(struct ResolvQuery *cb_data) +{ + for (int i = 0; i < cb_data->response_count; i++) + if (cb_data->responses[i]->sa_family == AF_INET6) { + return cb_data->responses[i]; + } + + return choose_any(cb_data); +} + +static struct sockaddr * +choose_any(struct ResolvQuery *cb_data) +{ + if (cb_data->response_count >= 1) { + return cb_data->responses[0]; + } + + return NULL; +} + +/* + * DNS timeout callback + */ +static void +resolv_timeout_cb(struct ev_loop *loop, struct ev_timer *w, int revents) +{ + struct dns_ctx *ctx = (struct dns_ctx *)w->data; + + if (revents & EV_TIMER) { + dns_timeouts(ctx, 30, ev_now(loop)); + } +} + +/* + * Callback to setup DNS timeout callback + */ +static void +dns_timer_setup_cb(struct dns_ctx *ctx, int timeout, void *data) +{ + struct ev_loop *loop = (struct ev_loop *)data; + + if (ev_is_active(&resolv_timeout_watcher)) { + ev_timer_stop(loop, &resolv_timeout_watcher); + } + + if (ctx != NULL && timeout >= 0) { + ev_timer_set(&resolv_timeout_watcher, timeout, 0.0); + ev_timer_start(loop, &resolv_timeout_watcher); + } +} + +static inline int +all_queries_are_null(struct ResolvQuery *cb_data) +{ + int result = 1; + + for (int i = 0; i < sizeof(cb_data->queries) / sizeof(cb_data->queries[0]); + i++) + result = result && cb_data->queries[i] == NULL; + + return result; +} diff --git a/server/resolv.h b/server/resolv.h new file mode 100644 index 0000000..0552922 --- /dev/null +++ b/server/resolv.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef RESOLV_H +#define RESOLV_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifdef __MINGW32__ +#include "win32.h" +#else +#include +#endif + +struct ResolvQuery; + +int resolv_init(struct ev_loop *, char **, int, int); +struct ResolvQuery *resolv_query(const char *, void (*)(struct sockaddr *, + void *), void (*)( + void *), void *, uint16_t); +void resolv_cancel(struct ResolvQuery *); +void resolv_shutdown(struct ev_loop *); + +#endif diff --git a/server/rule.c b/server/rule.c new file mode 100644 index 0000000..8aae04e --- /dev/null +++ b/server/rule.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * Copyright (c) 2011 Manuel Kasper + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#ifdef __MINGW32__ +extern void ss_error(const char *s); +#endif + +#include "rule.h" +#include "utils.h" + +static void free_rule(rule_t *); + +rule_t * +new_rule() +{ + rule_t *rule; + + rule = calloc(1, sizeof(rule_t)); + if (rule == NULL) { + ERROR("malloc"); + return NULL; + } + + return rule; +} + +int +accept_rule_arg(rule_t *rule, const char *arg) +{ + if (rule->pattern == NULL) { + rule->pattern = strdup(arg); + if (rule->pattern == NULL) { + ERROR("strdup failed"); + return -1; + } + } else { + LOGE("Unexpected table rule argument: %s", arg); + return -1; + } + + return 1; +} + +void +add_rule(struct cork_dllist *rules, rule_t *rule) +{ + cork_dllist_add(rules, &rule->entries); +} + +int +init_rule(rule_t *rule) +{ + if (rule->pattern_re == NULL) { + const char *reerr; + int reerroffset; + + rule->pattern_re = + pcre_compile(rule->pattern, 0, &reerr, &reerroffset, NULL); + if (rule->pattern_re == NULL) { + LOGE("Regex compilation of \"%s\" failed: %s, offset %d", + rule->pattern, reerr, reerroffset); + return 0; + } + } + + return 1; +} + +rule_t * +lookup_rule(const struct cork_dllist *rules, const char *name, size_t name_len) +{ + struct cork_dllist_item *curr, *next; + + if (name == NULL) { + name = ""; + name_len = 0; + } + + cork_dllist_foreach_void(rules, curr, next) { + rule_t *rule = cork_container_of(curr, rule_t, entries); + if (pcre_exec(rule->pattern_re, NULL, + name, name_len, 0, 0, NULL, 0) >= 0) + return rule; + } + + return NULL; +} + +void +remove_rule(rule_t *rule) +{ + cork_dllist_remove(&rule->entries); + free_rule(rule); +} + +static void +free_rule(rule_t *rule) +{ + if (rule == NULL) + return; + + ss_free(rule->pattern); + if (rule->pattern_re != NULL) + pcre_free(rule->pattern_re); + ss_free(rule); +} diff --git a/server/rule.h b/server/rule.h new file mode 100644 index 0000000..015bc42 --- /dev/null +++ b/server/rule.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * Copyright (c) 2011 Manuel Kasper + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef RULE_H +#define RULE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifdef HAVE_PCRE_H +#include +#elif HAVE_PCRE_PCRE_H +#include +#endif + +typedef struct rule { + char *pattern; + + /* Runtime fields */ + pcre *pattern_re; + + struct cork_dllist_item entries; +} rule_t; + +void add_rule(struct cork_dllist *, rule_t *); +int init_rule(rule_t *); +rule_t *lookup_rule(const struct cork_dllist *, const char *, size_t); +void remove_rule(rule_t *); +rule_t *new_rule(); +int accept_rule_arg(rule_t *, const char *); + +#endif diff --git a/server/server.c b/server/server.c new file mode 100644 index 0000000..65b0e42 --- /dev/null +++ b/server/server.c @@ -0,0 +1,2209 @@ +/* + * server.c - Provide shadowsocks service + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __MINGW32__ +#include +#include +#include +#include +#include +#include +#endif + +#include +#include + +#ifdef __MINGW32__ +#include "win32.h" +#endif + +#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__) +#include +#include +#define SET_INTERFACE +#endif + +#include "netutils.h" +#include "utils.h" +#include "acl.h" +#include "server.h" + +#include "obfs.c" // I don't want to modify makefile + +#ifndef EAGAIN +#define EAGAIN EWOULDBLOCK +#endif + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif + +#ifndef BUF_SIZE +#define BUF_SIZE 2048 +#endif + +#ifndef SSMAXCONN +#define SSMAXCONN 1024 +#endif + +#ifndef UPDATE_INTERVAL +#define UPDATE_INTERVAL 30 +#endif + +static void signal_cb(EV_P_ ev_signal *w, int revents); +static void accept_cb(EV_P_ ev_io *w, int revents); +static void server_send_cb(EV_P_ ev_io *w, int revents); +static void server_recv_cb(EV_P_ ev_io *w, int revents); +static void remote_recv_cb(EV_P_ ev_io *w, int revents); +static void remote_send_cb(EV_P_ ev_io *w, int revents); +static void server_timeout_cb(EV_P_ ev_timer *watcher, int revents); +static void block_list_clear_cb(EV_P_ ev_timer *watcher, int revents); + +static remote_t *new_remote(int fd); +static server_t *new_server(int fd, listen_ctx_t *listener); +static remote_t *connect_to_remote(EV_P_ struct addrinfo *res, + server_t *server); + +static void free_remote(remote_t *remote); +static void close_and_free_remote(EV_P_ remote_t *remote); +static void free_server(server_t *server); +static void close_and_free_server(EV_P_ server_t *server); +static void server_resolve_cb(struct sockaddr *addr, void *data); +static void query_free_cb(void *data); + +static size_t parse_header_len(const char atyp, const char *data, size_t offset); +static int is_header_complete(const buffer_t *buf); + +int verbose = 0; + +static int acl = 0; +static int mode = TCP_ONLY; +static int auth = 0; +static int ipv6first = 0; + +static int protocol_compatible = 0;//SSR +static int obfs_compatible = 0;//SSR + +static int fast_open = 0; +#ifdef HAVE_SETRLIMIT +static int nofile = 0; +#endif +static int remote_conn = 0; +static int server_conn = 0; + +static char *bind_address = NULL; +static char *server_port = NULL; +static char *manager_address = NULL; +uint64_t tx = 0; +uint64_t rx = 0; +ev_timer stat_update_watcher; +ev_timer block_list_watcher; + +static struct cork_dllist connections; + +static void +stat_update_cb(EV_P_ ev_timer *watcher, int revents) +{ + struct sockaddr_un svaddr, claddr; + int sfd = -1; + size_t msgLen; + char resp[BUF_SIZE]; + + if (verbose) { + LOGI("update traffic stat: tx: %" PRIu64 " rx: %" PRIu64 "", tx, rx); + } + + snprintf(resp, BUF_SIZE, "stat: {\"%s\":%" PRIu64 "}", server_port, tx + rx); + msgLen = strlen(resp) + 1; + + ss_addr_t ip_addr = { .host = NULL, .port = NULL }; + parse_addr(manager_address, &ip_addr); + + if (ip_addr.host == NULL || ip_addr.port == NULL) { + sfd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (sfd == -1) { + ERROR("stat_socket"); + return; + } + + memset(&claddr, 0, sizeof(struct sockaddr_un)); + claddr.sun_family = AF_UNIX; + snprintf(claddr.sun_path, sizeof(claddr.sun_path), "/tmp/shadowsocks.%s", server_port); + + unlink(claddr.sun_path); + + if (bind(sfd, (struct sockaddr *)&claddr, sizeof(struct sockaddr_un)) == -1) { + ERROR("stat_bind"); + close(sfd); + return; + } + + memset(&svaddr, 0, sizeof(struct sockaddr_un)); + svaddr.sun_family = AF_UNIX; + strncpy(svaddr.sun_path, manager_address, sizeof(svaddr.sun_path) - 1); + + if (sendto(sfd, resp, strlen(resp) + 1, 0, (struct sockaddr *)&svaddr, + sizeof(struct sockaddr_un)) != msgLen) { + ERROR("stat_sendto"); + close(sfd); + return; + } + + unlink(claddr.sun_path); + } else { + struct sockaddr_storage storage; + memset(&storage, 0, sizeof(struct sockaddr_storage)); + if (get_sockaddr(ip_addr.host, ip_addr.port, &storage, 0, ipv6first) == -1) { + ERROR("failed to parse the manager addr"); + return; + } + + sfd = socket(storage.ss_family, SOCK_DGRAM, 0); + + if (sfd == -1) { + ERROR("stat_socket"); + return; + } + + size_t addr_len = get_sockaddr_len((struct sockaddr *)&storage); + if (sendto(sfd, resp, strlen(resp) + 1, 0, (struct sockaddr *)&storage, + addr_len) != msgLen) { + ERROR("stat_sendto"); + close(sfd); + return; + } + } + + close(sfd); +} + +static void +free_connections(struct ev_loop *loop) +{ + struct cork_dllist_item *curr, *next; + cork_dllist_foreach_void(&connections, curr, next) { + server_t *server = cork_container_of(curr, server_t, entries); + remote_t *remote = server->remote; + close_and_free_server(loop, server); + close_and_free_remote(loop, remote); + } +} + +static size_t +parse_header_len(const char atyp, const char *data, size_t offset) +{ + size_t len = 0; + if ((atyp & ADDRTYPE_MASK) == 1) { + // IP V4 + len += sizeof(struct in_addr); + } else if ((atyp & ADDRTYPE_MASK) == 3) { + // Domain name + uint8_t name_len = *(uint8_t *)(data + offset); + len += name_len + 1; + } else if ((atyp & ADDRTYPE_MASK) == 4) { + // IP V6 + len += sizeof(struct in6_addr); + } else { + return 0; + } + len += 2; + return len; +} + +static int +is_header_complete(const buffer_t *buf) +{ + size_t header_len = 0; + size_t buf_len = buf->len; + + char atyp = buf->array[header_len]; + + // 1 byte for atyp + header_len++; + + if ((atyp & ADDRTYPE_MASK) == 1) { + // IP V4 + header_len += sizeof(struct in_addr); + } else if ((atyp & ADDRTYPE_MASK) == 3) { + // Domain name + // domain len + len of domain + if (buf_len < header_len + 1) + return 0; + uint8_t name_len = *(uint8_t *)(buf->array + header_len); + header_len += name_len + 1; + } else if ((atyp & ADDRTYPE_MASK) == 4) { + // IP V6 + header_len += sizeof(struct in6_addr); + } else { + return -1; + } + + // len of port + header_len += 2; + + // size of ONETIMEAUTH_BYTES + if (auth || (atyp & ONETIMEAUTH_FLAG)) { + header_len += ONETIMEAUTH_BYTES; + } + + return buf_len >= header_len ? 1 : 0; +} + +static char * +get_peer_name(int fd) +{ + static char peer_name[INET6_ADDRSTRLEN] = { 0 }; + struct sockaddr_storage addr; + socklen_t len = sizeof(struct sockaddr_storage); + memset(&addr, 0, len); + memset(peer_name, 0, INET6_ADDRSTRLEN); + int err = getpeername(fd, (struct sockaddr *)&addr, &len); + if (err == 0) { + if (addr.ss_family == AF_INET) { + struct sockaddr_in *s = (struct sockaddr_in *)&addr; + dns_ntop(AF_INET, &s->sin_addr, peer_name, INET_ADDRSTRLEN); + } else if (addr.ss_family == AF_INET6) { + struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; + dns_ntop(AF_INET6, &s->sin6_addr, peer_name, INET6_ADDRSTRLEN); + } + } else { + return NULL; + } + return peer_name; +} + +#ifdef __linux__ +static void +set_linger(int fd) +{ + struct linger so_linger; + memset(&so_linger, 0, sizeof(struct linger)); + so_linger.l_onoff = 1; + so_linger.l_linger = 0; + setsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger); +} +#endif + +static void +reset_addr(int fd) +{ + char *peer_name; + peer_name = get_peer_name(fd); + if (peer_name != NULL) { + remove_from_block_list(peer_name); + } +} + +static void +report_addr(int fd, int err_level) +{ +#ifdef __linux__ + set_linger(fd); +#endif + + char *peer_name; + peer_name = get_peer_name(fd); + if (peer_name != NULL) { + LOGE("failed to handshake with %s", peer_name); + update_block_list(peer_name, err_level); + } +} + +int +setfastopen(int fd) +{ + int s = 0; +#ifdef TCP_FASTOPEN + if (fast_open) { +#ifdef __APPLE__ + int opt = 1; +#else + int opt = 5; +#endif + s = setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &opt, sizeof(opt)); + + if (s == -1) { + if (errno == EPROTONOSUPPORT || errno == ENOPROTOOPT) { + LOGE("fast open is not supported on this platform"); + fast_open = 0; + } else { + ERROR("setsockopt"); + } + } + } +#endif + return s; +} + +#ifndef __MINGW32__ +int +setnonblocking(int fd) +{ + int flags; + if (-1 == (flags = fcntl(fd, F_GETFL, 0))) { + flags = 0; + } + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +#endif + +int +create_and_bind(const char *host, const char *port, int mptcp) +{ + struct addrinfo hints; + struct addrinfo *result, *rp, *ipv4v6bindall; + int s, listen_sock; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ + hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */ + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* For wildcard IP address */ + hints.ai_protocol = IPPROTO_TCP; + + for (int i = 1; i < 8; i++) { + s = getaddrinfo(host, port, &hints, &result); + if (s == 0) { + break; + } else { + sleep(pow(2, i)); + LOGE("failed to resolve server name, wait %.0f seconds", pow(2, i)); + } + } + + if (s != 0) { + LOGE("getaddrinfo: %s", gai_strerror(s)); + return -1; + } + + rp = result; + + /* + * On Linux, with net.ipv6.bindv6only = 0 (the default), getaddrinfo(NULL) with + * AI_PASSIVE returns 0.0.0.0 and :: (in this order). AI_PASSIVE was meant to + * return a list of addresses to listen on, but it is impossible to listen on + * 0.0.0.0 and :: at the same time, if :: implies dualstack mode. + */ + if (!host) { + ipv4v6bindall = result; + + /* Loop over all address infos found until a IPV6 address is found. */ + while (ipv4v6bindall) { + if (ipv4v6bindall->ai_family == AF_INET6) { + rp = ipv4v6bindall; /* Take first IPV6 address available */ + break; + } + ipv4v6bindall = ipv4v6bindall->ai_next; /* Get next address info, if any */ + } + } + + for (/*rp = result*/; rp != NULL; rp = rp->ai_next) { + listen_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (listen_sock == -1) { + continue; + } + + if (rp->ai_family == AF_INET6) { + int ipv6only = host ? 1 : 0; + setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)); + } + + int opt = 1; + setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); +#ifdef SO_NOSIGPIPE + setsockopt(listen_sock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); +#endif + int err = set_reuseport(listen_sock); + if (err == 0) { + LOGI("tcp port reuse enabled"); + } + + if (mptcp == 1) { + int err = setsockopt(listen_sock, SOL_TCP, MPTCP_ENABLED, &opt, sizeof(opt)); + if (err == -1) { + ERROR("failed to enable multipath TCP"); + } + } + + s = bind(listen_sock, rp->ai_addr, rp->ai_addrlen); + if (s == 0) { + /* We managed to bind successfully! */ + break; + } else { + ERROR("bind"); + } + + close(listen_sock); + } + + if (rp == NULL) { + LOGE("Could not bind"); + return -1; + } + + freeaddrinfo(result); + + return listen_sock; +} + +static remote_t * +connect_to_remote(EV_P_ struct addrinfo *res, + server_t *server) +{ + int sockfd; +#ifdef SET_INTERFACE + const char *iface = server->listen_ctx->iface; +#endif + + if (acl) { + char ipstr[INET6_ADDRSTRLEN]; + memset(ipstr, 0, INET6_ADDRSTRLEN); + + if (res->ai_addr->sa_family == AF_INET) { + struct sockaddr_in *s = (struct sockaddr_in *)res->ai_addr; + dns_ntop(AF_INET, &s->sin_addr, ipstr, INET_ADDRSTRLEN); + } else if (res->ai_addr->sa_family == AF_INET6) { + struct sockaddr_in6 *s = (struct sockaddr_in6 *)res->ai_addr; + dns_ntop(AF_INET6, &s->sin6_addr, ipstr, INET6_ADDRSTRLEN); + } + + if (outbound_block_match_host(ipstr) == 1) { + if (verbose) + LOGI("outbound blocked %s", ipstr); + return NULL; + } + } + + // initialize remote socks + sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sockfd == -1) { + ERROR("socket"); + close(sockfd); + return NULL; + } + + int opt = 1; + setsockopt(sockfd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); +#ifdef SO_NOSIGPIPE + setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); +#endif + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + // setup remote socks + + if (setnonblocking(sockfd) == -1) + ERROR("setnonblocking"); + + if (bind_address != NULL) + if (bind_to_address(sockfd, bind_address) == -1) { + ERROR("bind_to_address"); + close(sockfd); + return NULL; + } + +#ifdef SET_INTERFACE + if (iface) { + if (setinterface(sockfd, iface) == -1) { + ERROR("setinterface"); + close(sockfd); + return NULL; + } + } +#endif + + remote_t *remote = new_remote(sockfd); + +#ifdef TCP_FASTOPEN + if (fast_open) { +#ifdef __APPLE__ + ((struct sockaddr_in *)(res->ai_addr))->sin_len = sizeof(struct sockaddr_in); + sa_endpoints_t endpoints; + memset((char *)&endpoints, 0, sizeof(endpoints)); + endpoints.sae_dstaddr = res->ai_addr; + endpoints.sae_dstaddrlen = res->ai_addrlen; + + struct iovec iov; + iov.iov_base = server->buf->array + server->buf->idx; + iov.iov_len = server->buf->len; + size_t len; + int s = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY, CONNECT_DATA_IDEMPOTENT, + &iov, 1, &len, NULL); + if (s == 0) { + s = len; + } +#else + ssize_t s = sendto(sockfd, server->buf->array + server->buf->idx, + server->buf->len, MSG_FASTOPEN, res->ai_addr, + res->ai_addrlen); +#endif + if (s == -1) { + if (errno == CONNECT_IN_PROGRESS || errno == EAGAIN + || errno == EWOULDBLOCK) { + // The remote server doesn't support tfo or it's the first connection to the server. + // It will automatically fall back to conventional TCP. + } else if (errno == EOPNOTSUPP || errno == EPROTONOSUPPORT || + errno == ENOPROTOOPT) { + // Disable fast open as it's not supported + fast_open = 0; + LOGE("fast open is not supported on this platform"); + } else { + ERROR("sendto"); + } + } else if (s <= server->buf->len) { + server->buf->idx += s; + server->buf->len -= s; + } else { + server->buf->idx = 0; + server->buf->len = 0; + } + } +#endif + + if (!fast_open) { + int r = connect(sockfd, res->ai_addr, res->ai_addrlen); + + if (r == -1 && errno != CONNECT_IN_PROGRESS) { + ERROR("connect"); + close_and_free_remote(EV_A_ remote); + return NULL; + } + } + + return remote; +} + +static void +server_recv_cb(EV_P_ ev_io *w, int revents) +{ + server_ctx_t *server_recv_ctx = (server_ctx_t *)w; + server_t *server = server_recv_ctx->server; + remote_t *remote = NULL; + + int len = server->buf->len; + buffer_t *buf = server->buf; + + if (server->stage > STAGE_PARSE) { + remote = server->remote; + buf = remote->buf; + len = 0; + + ev_timer_again(EV_A_ & server->recv_ctx->watcher); + } + + if (len > BUF_SIZE) { + ERROR("out of recv buffer"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + + ssize_t r = recv(server->fd, buf->array + len, BUF_SIZE - len, 0); + + if (r == 0) { + // connection closed + if (verbose) { + LOGI("server_recv close the connection"); + } + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } else if (r == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // no data + // continue to wait for recv + return; + } else { + ERROR("server recv"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + } + + tx += r; + + if (server->stage == STAGE_ERROR) { + server->buf->len = 0; + server->buf->idx = 0; + return; + } + + // handle incomplete header part 1 + if (server->stage == STAGE_INIT) { + buf->len += r; + if (buf->len <= enc_get_iv_len() + 1) { + // wait for more + return; + } + } else { + buf->len = r; + } + + // SSR beg + + if (server->obfs_plugin) { + obfs_class *obfs_plugin = server->obfs_plugin; + if (obfs_plugin->server_decode) { + int needsendback = 0; + + if(obfs_compatible == 1) + { + char *back_buf = (char*)malloc(sizeof(buffer_t)); + memcpy(back_buf, buf, sizeof(buffer_t)); + buf->len = obfs_plugin->server_decode(server->obfs, &buf->array, buf->len, &buf->capacity, &needsendback); + + if ((int)buf->len < 0) + { + LOGE("obfs_compatible"); + memcpy(buf, back_buf, sizeof(buffer_t)); + free(back_buf); + server->obfs_compatible_state = 1; + } + } + else + { + buf->len = obfs_plugin->server_decode(server->obfs, &buf->array, buf->len, &buf->capacity, &needsendback); + if ((int)buf->len < 0) { + LOGE("server_decode"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + } + + if (needsendback) { + size_t capacity = BUF_SIZE; + char *sendback_buf = (char*)malloc(capacity); + obfs_class *obfs_plugin = server->obfs_plugin; + if (obfs_plugin->server_encode) { + int len = obfs_plugin->server_encode(server->obfs, &sendback_buf, 0, &capacity); + send(server->fd, sendback_buf, len, 0); + } + free(sendback_buf); + return; + } + } + } + + int err = ss_decrypt(buf, server->d_ctx, BUF_SIZE); + + if (err) { + report_addr(server->fd, MALICIOUS); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + + if (server->protocol_plugin) { + obfs_class *protocol_plugin = server->protocol_plugin; + if (protocol_plugin->server_post_decrypt) { + + if(protocol_compatible == 1) + { + char *back_buf = (char*)malloc(sizeof(buffer_t)); + memcpy(back_buf, buf, sizeof(buffer_t)); + buf->len = protocol_plugin->server_post_decrypt(server->protocol, &buf->array, buf->len, &buf->capacity); + + if ((int)buf->len < 0) { + LOGE("protocol_compatible"); + memcpy(buf, back_buf, sizeof(buffer_t)); + free(back_buf); + server->protocol_compatible_state = 1; + } + if ( buf->len == 0 ) + { + LOGE("protocol_compatible"); + memcpy(buf, back_buf, sizeof(buffer_t)); + free(back_buf); + server->protocol_compatible_state = 1; + } + } + else + { + buf->len = protocol_plugin->server_post_decrypt(server->protocol, &buf->array, buf->len, &buf->capacity); + if ((int)buf->len < 0) { + LOGE("server_post_decrypt"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + if ( buf->len == 0 ) + { + LOGE("server_post_decrypt"); + return; + } + } + } + } + // SSR end + + // handle incomplete header part 2 + if (server->stage == STAGE_INIT) { + int ret = is_header_complete(server->buf); + if (ret == 1) { + bfree(server->header_buf); + ss_free(server->header_buf); + server->stage = STAGE_PARSE; + } else if (ret == -1) { + server->stage = STAGE_ERROR; + report_addr(server->fd, MALFORMED); + server->buf->len = 0; + server->buf->idx = 0; + return; + } else { + server->stage = STAGE_HANDSHAKE; + } + } + + if (server->stage == STAGE_HANDSHAKE) { + size_t header_len = server->header_buf->len; + brealloc(server->header_buf, server->buf->len + header_len, BUF_SIZE); + memcpy(server->header_buf->array + header_len, + server->buf->array, server->buf->len); + server->header_buf->len = server->buf->len + header_len; + + int ret = is_header_complete(server->buf); + + if (ret == 1) { + brealloc(server->buf, server->header_buf->len, BUF_SIZE); + memcpy(server->buf->array, server->header_buf->array, server->header_buf->len); + server->buf->len = server->header_buf->len; + bfree(server->header_buf); + ss_free(server->header_buf); + server->stage = STAGE_PARSE; + } else { + if (ret == -1) + server->stage = STAGE_ERROR; + server->buf->len = 0; + server->buf->idx = 0; + return; + } + } + + // handshake and transmit data + if (server->stage == STAGE_STREAM) { + if (server->auth && !ss_check_hash(remote->buf, server->chunk, server->d_ctx, BUF_SIZE)) { + LOGE("hash error"); + report_addr(server->fd, BAD); + close_and_free_server(EV_A_ server); + close_and_free_remote(EV_A_ remote); + return; + } + + int s = send(remote->fd, remote->buf->array, remote->buf->len, 0); + if (s == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // no data, wait for send + remote->buf->idx = 0; + ev_io_stop(EV_A_ & server_recv_ctx->io); + ev_io_start(EV_A_ & remote->send_ctx->io); + } else { + ERROR("server_recv_send"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + } + } else if (s < remote->buf->len) { + remote->buf->len -= s; + remote->buf->idx = s; + ev_io_stop(EV_A_ & server_recv_ctx->io); + ev_io_start(EV_A_ & remote->send_ctx->io); + } + return; + } else if (server->stage == STAGE_PARSE) { + /* + * Shadowsocks TCP Relay Header: + * + * +------+----------+----------+----------------+ + * | ATYP | DST.ADDR | DST.PORT | HMAC-SHA1 | + * +------+----------+----------+----------------+ + * | 1 | Variable | 2 | 10 | + * +------+----------+----------+----------------+ + * + * If ATYP & ONETIMEAUTH_FLAG(0x10) != 0, Authentication (HMAC-SHA1) is enabled. + * + * The key of HMAC-SHA1 is (IV + KEY) and the input is the whole header. + * The output of HMAC-SHA is truncated to 10 bytes (leftmost bits). + */ + + /* + * Shadowsocks Request's Chunk Authentication for TCP Relay's payload + * (No chunk authentication for response's payload): + * + * +------+-----------+-------------+------+ + * | LEN | HMAC-SHA1 | DATA | ... + * +------+-----------+-------------+------+ + * | 2 | 10 | Variable | ... + * +------+-----------+-------------+------+ + * + * The key of HMAC-SHA1 is (IV + CHUNK ID) + * The output of HMAC-SHA is truncated to 10 bytes (leftmost bits). + */ + + int offset = 0; + int need_query = 0; + char atyp = server->buf->array[offset++]; + char host[257] = { 0 }; + uint16_t port = 0; + struct addrinfo info; + struct sockaddr_storage storage; + memset(&info, 0, sizeof(struct addrinfo)); + memset(&storage, 0, sizeof(struct sockaddr_storage)); + + if (auth || (atyp & ONETIMEAUTH_FLAG)) { + size_t header_len = parse_header_len(atyp, server->buf->array, offset); + size_t len = server->buf->len; + + if (header_len == 0 || len < offset + header_len + ONETIMEAUTH_BYTES) { + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } + + server->buf->len = offset + header_len + ONETIMEAUTH_BYTES; + if (ss_onetimeauth_verify(server->buf, server->d_ctx->evp.iv)) { + report_addr(server->fd, BAD); + close_and_free_server(EV_A_ server); + return; + } + + server->buf->len = len; + server->auth = 1; + } + + // get remote addr and port + if ((atyp & ADDRTYPE_MASK) == 1) { + // IP V4 + struct sockaddr_in *addr = (struct sockaddr_in *)&storage; + size_t in_addr_len = sizeof(struct in_addr); + addr->sin_family = AF_INET; + if (server->buf->len >= in_addr_len + 3) { + addr->sin_addr = *(struct in_addr *)(server->buf->array + offset); + dns_ntop(AF_INET, (const void *)(server->buf->array + offset), + host, INET_ADDRSTRLEN); + offset += in_addr_len; + } else { + LOGE("invalid header with addr type %d", atyp); + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } + addr->sin_port = *(uint16_t *)(server->buf->array + offset); + info.ai_family = AF_INET; + info.ai_socktype = SOCK_STREAM; + info.ai_protocol = IPPROTO_TCP; + info.ai_addrlen = sizeof(struct sockaddr_in); + info.ai_addr = (struct sockaddr *)addr; + } else if ((atyp & ADDRTYPE_MASK) == 3) { + // Domain name + uint8_t name_len = *(uint8_t *)(server->buf->array + offset); + if (name_len + 4 <= server->buf->len) { + memcpy(host, server->buf->array + offset + 1, name_len); + offset += name_len + 1; + } else { + LOGE("invalid name length: %d", name_len); + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } + if (acl && outbound_block_match_host(host) == 1) { + if (verbose) + LOGI("outbound blocked %s", host); + close_and_free_server(EV_A_ server); + return; + } + struct cork_ip ip; + if (cork_ip_init(&ip, host) != -1) { + info.ai_socktype = SOCK_STREAM; + info.ai_protocol = IPPROTO_TCP; + if (ip.version == 4) { + struct sockaddr_in *addr = (struct sockaddr_in *)&storage; + dns_pton(AF_INET, host, &(addr->sin_addr)); + addr->sin_port = *(uint16_t *)(server->buf->array + offset); + addr->sin_family = AF_INET; + info.ai_family = AF_INET; + info.ai_addrlen = sizeof(struct sockaddr_in); + info.ai_addr = (struct sockaddr *)addr; + } else if (ip.version == 6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; + dns_pton(AF_INET6, host, &(addr->sin6_addr)); + addr->sin6_port = *(uint16_t *)(server->buf->array + offset); + addr->sin6_family = AF_INET6; + info.ai_family = AF_INET6; + info.ai_addrlen = sizeof(struct sockaddr_in6); + info.ai_addr = (struct sockaddr *)addr; + } + } else { + if (!validate_hostname(host, name_len)) { + LOGE("invalid host name"); + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } + need_query = 1; + } + } else if ((atyp & ADDRTYPE_MASK) == 4) { + // IP V6 + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; + size_t in6_addr_len = sizeof(struct in6_addr); + addr->sin6_family = AF_INET6; + if (server->buf->len >= in6_addr_len + 3) { + addr->sin6_addr = *(struct in6_addr *)(server->buf->array + offset); + dns_ntop(AF_INET6, (const void *)(server->buf->array + offset), + host, INET6_ADDRSTRLEN); + offset += in6_addr_len; + } else { + LOGE("invalid header with addr type %d", atyp); + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } + addr->sin6_port = *(uint16_t *)(server->buf->array + offset); + info.ai_family = AF_INET6; + info.ai_socktype = SOCK_STREAM; + info.ai_protocol = IPPROTO_TCP; + info.ai_addrlen = sizeof(struct sockaddr_in6); + info.ai_addr = (struct sockaddr *)addr; + } + + if (offset == 1) { + LOGE("invalid header with addr type %d", atyp); + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } + + port = (*(uint16_t *)(server->buf->array + offset)); + + offset += 2; + + if (server->auth) { + offset += ONETIMEAUTH_BYTES; + } + + if (server->buf->len < offset) { + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } else { + server->buf->len -= offset; + memmove(server->buf->array, server->buf->array + offset, server->buf->len); + } + + if (verbose) { + if ((atyp & ADDRTYPE_MASK) == 4) + LOGI("connect to [%s]:%d", host, ntohs(port)); + else + LOGI("connect to %s:%d", host, ntohs(port)); + } + + if (server->auth && !ss_check_hash(server->buf, server->chunk, server->d_ctx, BUF_SIZE)) { + LOGE("hash error"); + report_addr(server->fd, BAD); + close_and_free_server(EV_A_ server); + return; + } + + + if (!need_query) { + remote_t *remote = connect_to_remote(EV_A_ &info, server); + + if (remote == NULL) { + LOGE("connect error"); + close_and_free_server(EV_A_ server); + return; + } else { + server->remote = remote; + remote->server = server; + + // XXX: should handle buffer carefully + if (server->buf->len > 0) { + memcpy(remote->buf->array, server->buf->array, server->buf->len); + remote->buf->len = server->buf->len; + remote->buf->idx = 0; + server->buf->len = 0; + server->buf->idx = 0; + } + + // waiting on remote connected event + ev_io_stop(EV_A_ & server_recv_ctx->io); + ev_io_start(EV_A_ & remote->send_ctx->io); + } + } else { + query_t *query = (query_t *)ss_malloc(sizeof(query_t)); + query->server = server; + snprintf(query->hostname, 256, "%s", host); + + server->stage = STAGE_RESOLVE; + server->query = resolv_query(host, server_resolve_cb, + query_free_cb, query, port); + + ev_io_stop(EV_A_ & server_recv_ctx->io); + } + + return; + } + // should not reach here + FATAL("server context error"); +} + +static void +server_send_cb(EV_P_ ev_io *w, int revents) +{ + server_ctx_t *server_send_ctx = (server_ctx_t *)w; + server_t *server = server_send_ctx->server; + remote_t *remote = server->remote; + + if (remote == NULL) { + LOGE("invalid server"); + close_and_free_server(EV_A_ server); + return; + } + + if (server->buf->len == 0) { + // close and free + if (verbose) { + LOGI("server_send close the connection"); + } + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } else { + // has data to send + ssize_t s = send(server->fd, server->buf->array + server->buf->idx, + server->buf->len, 0); + if (s == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + ERROR("server_send_send"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + } + return; + } else if (s < server->buf->len) { + // partly sent, move memory, wait for the next time to send + server->buf->len -= s; + server->buf->idx += s; + return; + } else { + // all sent out, wait for reading + server->buf->len = 0; + server->buf->idx = 0; + ev_io_stop(EV_A_ & server_send_ctx->io); + if (remote != NULL) { + ev_io_start(EV_A_ & remote->recv_ctx->io); + return; + } else { + LOGE("invalid remote"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + } + } +} + +static void +block_list_clear_cb(EV_P_ ev_timer *watcher, int revents) +{ + clear_block_list(); +} + +static void +server_timeout_cb(EV_P_ ev_timer *watcher, int revents) +{ + server_ctx_t *server_ctx + = cork_container_of(watcher, server_ctx_t, watcher); + server_t *server = server_ctx->server; + remote_t *remote = server->remote; + + if (verbose) { + LOGI("TCP connection timeout"); + } + + if (server->stage < STAGE_PARSE) { + if (verbose) { + size_t len = server->stage ? + server->header_buf->len : server->buf->len; +#ifdef __MINGW32__ + LOGI("incomplete header: %u", len); +#else + LOGI("incomplete header: %zu", len); +#endif + } + report_addr(server->fd, SUSPICIOUS); + } + + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); +} + +static void +query_free_cb(void *data) +{ + if (data != NULL) { + ss_free(data); + } +} + +static void +server_resolve_cb(struct sockaddr *addr, void *data) +{ + query_t *query = (query_t *)data; + server_t *server = query->server; + struct ev_loop *loop = server->listen_ctx->loop; + + server->query = NULL; + + if (addr == NULL) { + LOGE("unable to resolve %s", query->hostname); + close_and_free_server(EV_A_ server); + } else { + if (verbose) { + LOGI("successfully resolved %s", query->hostname); + } + + struct addrinfo info; + memset(&info, 0, sizeof(struct addrinfo)); + info.ai_socktype = SOCK_STREAM; + info.ai_protocol = IPPROTO_TCP; + info.ai_addr = addr; + + if (addr->sa_family == AF_INET) { + info.ai_family = AF_INET; + info.ai_addrlen = sizeof(struct sockaddr_in); + } else if (addr->sa_family == AF_INET6) { + info.ai_family = AF_INET6; + info.ai_addrlen = sizeof(struct sockaddr_in6); + } + + remote_t *remote = connect_to_remote(EV_A_ &info, server); + + if (remote == NULL) { + close_and_free_server(EV_A_ server); + } else { + server->remote = remote; + remote->server = server; + + // XXX: should handle buffer carefully + if (server->buf->len > 0) { + memcpy(remote->buf->array, server->buf->array + server->buf->idx, + server->buf->len); + remote->buf->len = server->buf->len; + remote->buf->idx = 0; + server->buf->len = 0; + server->buf->idx = 0; + } + + // listen to remote connected event + ev_io_start(EV_A_ & remote->send_ctx->io); + } + } +} + +static void +remote_recv_cb(EV_P_ ev_io *w, int revents) +{ + remote_ctx_t *remote_recv_ctx = (remote_ctx_t *)w; + remote_t *remote = remote_recv_ctx->remote; + server_t *server = remote->server; + + if (server == NULL) { + LOGE("invalid server"); + close_and_free_remote(EV_A_ remote); + return; + } + + ev_timer_again(EV_A_ & server->recv_ctx->watcher); + + ssize_t r = recv(remote->fd, server->buf->array, BUF_SIZE, 0); + + if (r == 0) { + // connection closed + if (verbose) { + LOGI("remote_recv close the connection"); + } + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } else if (r == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // no data + // continue to wait for recv + return; + } else { + ERROR("remote recv"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + } + + rx += r; + + server->buf->len = r; + + // SSR beg + server_info _server_info; + if (server->obfs_plugin) { + server->obfs_plugin->get_server_info(server->obfs, &_server_info); + _server_info.head_len = get_head_size(server->buf->array, server->buf->len, 30); + server->obfs_plugin->set_server_info(server->obfs, &_server_info); + } + + if (server->protocol_plugin && server->obfs_compatible_state == 0) { + obfs_class *protocol_plugin = server->protocol_plugin; + if (protocol_plugin->server_pre_encrypt) { + server->buf->len = protocol_plugin->server_pre_encrypt(server->protocol, &server->buf->array, server->buf->len, &server->buf->capacity); + } + } + + int err = ss_encrypt(server->buf, server->e_ctx, BUF_SIZE); + + if (err) { + LOGE("invalid password or cipher"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + + if (server->obfs_plugin && server->obfs_compatible_state == 0) { + obfs_class *obfs_plugin = server->obfs_plugin; + if (obfs_plugin->server_encode) { + server->buf->len = obfs_plugin->server_encode(server->obfs, &server->buf->array, server->buf->len, &server->buf->capacity); + } + } + // SSR end + + int s = send(server->fd, server->buf->array, server->buf->len, 0); + + if (s == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // no data, wait for send + server->buf->idx = 0; + ev_io_stop(EV_A_ & remote_recv_ctx->io); + ev_io_start(EV_A_ & server->send_ctx->io); + } else { + ERROR("remote_recv_send"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + } else if (s < server->buf->len) { + server->buf->len -= s; + server->buf->idx = s; + ev_io_stop(EV_A_ & remote_recv_ctx->io); + ev_io_start(EV_A_ & server->send_ctx->io); + } + + // Disable TCP_NODELAY after the first response are sent + if (!remote->recv_ctx->connected) { + int opt = 0; + setsockopt(server->fd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); + setsockopt(remote->fd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); + remote->recv_ctx->connected = 1; + } +} + +static void +remote_send_cb(EV_P_ ev_io *w, int revents) +{ + remote_ctx_t *remote_send_ctx = (remote_ctx_t *)w; + remote_t *remote = remote_send_ctx->remote; + server_t *server = remote->server; + + if (server == NULL) { + LOGE("invalid server"); + close_and_free_remote(EV_A_ remote); + return; + } + + if (!remote_send_ctx->connected) { + struct sockaddr_storage addr; + socklen_t len = sizeof(struct sockaddr_storage); + memset(&addr, 0, len); + int r = getpeername(remote->fd, (struct sockaddr *)&addr, &len); + if (r == 0) { + if (verbose) { + LOGI("remote connected"); + } + remote_send_ctx->connected = 1; + + // Clear the state of this address in the block list + reset_addr(server->fd); + + if (remote->buf->len == 0) { + server->stage = STAGE_STREAM; + ev_io_stop(EV_A_ & remote_send_ctx->io); + ev_io_start(EV_A_ & server->recv_ctx->io); + ev_io_start(EV_A_ & remote->recv_ctx->io); + return; + } + } else { + ERROR("getpeername"); + // not connected + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + } + + if (remote->buf->len == 0) { + // close and free + if (verbose) { + LOGI("remote_send close the connection"); + } + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } else { + // has data to send + ssize_t s = send(remote->fd, remote->buf->array + remote->buf->idx, + remote->buf->len, 0); + if (s == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + ERROR("remote_send_send"); + // close and free + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + } + return; + } else if (s < remote->buf->len) { + // partly sent, move memory, wait for the next time to send + remote->buf->len -= s; + remote->buf->idx += s; + return; + } else { + // all sent out, wait for reading + remote->buf->len = 0; + remote->buf->idx = 0; + ev_io_stop(EV_A_ & remote_send_ctx->io); + if (server != NULL) { + ev_io_start(EV_A_ & server->recv_ctx->io); + if (server->stage != STAGE_STREAM) { + server->stage = STAGE_STREAM; + ev_io_start(EV_A_ & remote->recv_ctx->io); + } + } else { + LOGE("invalid server"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + } + return; + } + } +} + +static remote_t * +new_remote(int fd) +{ + if (verbose) { + remote_conn++; + } + + remote_t *remote; + + remote = ss_malloc(sizeof(remote_t)); + remote->recv_ctx = ss_malloc(sizeof(remote_ctx_t)); + remote->send_ctx = ss_malloc(sizeof(remote_ctx_t)); + remote->buf = ss_malloc(sizeof(buffer_t)); + remote->fd = fd; + remote->recv_ctx->remote = remote; + remote->recv_ctx->connected = 0; + remote->send_ctx->remote = remote; + remote->send_ctx->connected = 0; + remote->server = NULL; + + ev_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, EV_READ); + ev_io_init(&remote->send_ctx->io, remote_send_cb, fd, EV_WRITE); + + balloc(remote->buf, BUF_SIZE); + + return remote; +} + +static void +free_remote(remote_t *remote) +{ + if (remote->server != NULL) { + remote->server->remote = NULL; + } + if (remote->buf != NULL) { + bfree(remote->buf); + ss_free(remote->buf); + } + ss_free(remote->recv_ctx); + ss_free(remote->send_ctx); + ss_free(remote); +} + +static void +close_and_free_remote(EV_P_ remote_t *remote) +{ + if (remote != NULL) { + ev_io_stop(EV_A_ & remote->send_ctx->io); + ev_io_stop(EV_A_ & remote->recv_ctx->io); + close(remote->fd); + free_remote(remote); + if (verbose) { + remote_conn--; + LOGI("current remote connection: %d", remote_conn); + } + } +} + +static server_t * +new_server(int fd, listen_ctx_t *listener) +{ + if (verbose) { + server_conn++; + } + + server_t *server; + server = ss_malloc(sizeof(server_t)); + + memset(server, 0, sizeof(server_t)); + + server->recv_ctx = ss_malloc(sizeof(server_ctx_t)); + server->send_ctx = ss_malloc(sizeof(server_ctx_t)); + server->buf = ss_malloc(sizeof(buffer_t)); + server->header_buf = ss_malloc(sizeof(buffer_t)); + server->fd = fd; + server->recv_ctx->server = server; + server->recv_ctx->connected = 0; + server->send_ctx->server = server; + server->send_ctx->connected = 0; + server->stage = STAGE_INIT; + server->query = NULL; + server->listen_ctx = listener; + server->remote = NULL; + + if (listener->method) { + server->e_ctx = ss_malloc(sizeof(enc_ctx_t)); + server->d_ctx = ss_malloc(sizeof(enc_ctx_t)); + enc_ctx_init(listener->method, server->e_ctx, 1); + enc_ctx_init(listener->method, server->d_ctx, 0); + } else { + server->e_ctx = NULL; + server->d_ctx = NULL; + } + + int request_timeout = min(MAX_REQUEST_TIMEOUT, listener->timeout) + + rand() % MAX_REQUEST_TIMEOUT; + + ev_io_init(&server->recv_ctx->io, server_recv_cb, fd, EV_READ); + ev_io_init(&server->send_ctx->io, server_send_cb, fd, EV_WRITE); + ev_timer_init(&server->recv_ctx->watcher, server_timeout_cb, + request_timeout, listener->timeout); + + balloc(server->buf, BUF_SIZE); + balloc(server->header_buf, BUF_SIZE); + + server->chunk = (chunk_t *)malloc(sizeof(chunk_t)); + memset(server->chunk, 0, sizeof(chunk_t)); + server->chunk->buf = ss_malloc(sizeof(buffer_t)); + memset(server->chunk->buf, 0, sizeof(buffer_t)); + + cork_dllist_add(&connections, &server->entries); + + return server; +} + +static void +free_server(server_t *server) +{ + cork_dllist_remove(&server->entries); + + if (server->chunk != NULL) { + if (server->chunk->buf != NULL) { + bfree(server->chunk->buf); + ss_free(server->chunk->buf); + } + ss_free(server->chunk); + } + if (server->remote != NULL) { + server->remote->server = NULL; + } + if (server->e_ctx != NULL) { + cipher_context_release(&server->e_ctx->evp); + ss_free(server->e_ctx); + } + if (server->d_ctx != NULL) { + cipher_context_release(&server->d_ctx->evp); + ss_free(server->d_ctx); + } + if (server->buf != NULL) { + bfree(server->buf); + ss_free(server->buf); + } + if (server->header_buf != NULL) { + bfree(server->header_buf); + ss_free(server->header_buf); + } + + ss_free(server->recv_ctx); + ss_free(server->send_ctx); + ss_free(server); +} + +static void +close_and_free_server(EV_P_ server_t *server) +{ + if (server != NULL) { + if (server->query != NULL) { + resolv_cancel(server->query); + server->query = NULL; + } + ev_io_stop(EV_A_ & server->send_ctx->io); + ev_io_stop(EV_A_ & server->recv_ctx->io); + ev_timer_stop(EV_A_ & server->recv_ctx->watcher); + close(server->fd); + free_server(server); + if (verbose) { + server_conn--; + LOGI("current server connection: %d", server_conn); + } + } +} + +static void +signal_cb(EV_P_ ev_signal *w, int revents) +{ + if (revents & EV_SIGNAL) { + switch (w->signum) { + case SIGINT: + case SIGTERM: + ev_unloop(EV_A_ EVUNLOOP_ALL); + } + } +} + +static void +accept_cb(EV_P_ ev_io *w, int revents) +{ + listen_ctx_t *listener = (listen_ctx_t *)w; + int serverfd = accept(listener->fd, NULL, NULL); + if (serverfd == -1) { + ERROR("accept"); + return; + } + + char *peer_name = get_peer_name(serverfd); + if (peer_name != NULL) { + int in_white_list = 0; + if (acl) { + if ((get_acl_mode() == BLACK_LIST && acl_match_host(peer_name) == 1) + || (get_acl_mode() == WHITE_LIST && acl_match_host(peer_name) >= 0)) { + LOGE("Access denied from %s", peer_name); + close(serverfd); + return; + } else if (acl_match_host(peer_name) == -1) { + in_white_list = 1; + } + } + if (!in_white_list && check_block_list(peer_name)) { + LOGE("block all requests from %s", peer_name); +#ifdef __linux__ + set_linger(serverfd); +#endif + close(serverfd); + return; + } + } + + int opt = 1; + setsockopt(serverfd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); +#ifdef SO_NOSIGPIPE + setsockopt(serverfd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); +#endif + setnonblocking(serverfd); + + if (verbose) { + LOGI("accept a connection"); + } + + server_t *server = new_server(serverfd, listener); + + // SSR beg + server->obfs_plugin = new_obfs_class(server->listen_ctx->obfs_name); + if (server->obfs_plugin) { + server->obfs = server->obfs_plugin->new_obfs(); + server->obfs_compatible_state = 0; + } + server->protocol_plugin = new_obfs_class(server->listen_ctx->protocol_name); + if (server->protocol_plugin) { + server->protocol = server->protocol_plugin->new_obfs(); + server->protocol_compatible_state = 0; + } + server_info _server_info; + memset(&_server_info, 0, sizeof(server_info)); + _server_info.param = server->listen_ctx->obfs_param; + if(server->obfs_plugin) + _server_info.g_data = server->obfs_plugin->init_data(); + _server_info.head_len = 7; + _server_info.iv = server->e_ctx->evp.iv; + _server_info.iv_len = enc_get_iv_len(); + _server_info.key = enc_get_key(); + _server_info.key_len = enc_get_key_len(); + _server_info.tcp_mss = 1460; + + if (server->obfs_plugin) + server->obfs_plugin->set_server_info(server->obfs, &_server_info); + + _server_info.param = server->listen_ctx->protocol_param; + if (server->protocol_plugin) + _server_info.g_data = server->protocol_plugin->init_data(); + + if (server->protocol_plugin) + server->protocol_plugin->set_server_info(server->protocol, &_server_info); + // SSR end + + ev_io_start(EV_A_ & server->recv_ctx->io); + ev_timer_start(EV_A_ & server->recv_ctx->watcher); +} + +int +main(int argc, char **argv) +{ + int i, c; + int pid_flags = 0; + int mptcp = 0; + int firewall = 0; + int mtu = 0; + char *user = NULL; + char *password = NULL; + char *timeout = NULL; + char *protocol = NULL; // SSR + char *protocol_param = NULL; // SSR + char *method = NULL; + char *obfs = NULL; // SSR + char *obfs_param = NULL; // SSR + char *pid_path = NULL; + char *conf_path = NULL; + char *iface = NULL; + + int server_num = 0; + const char *server_host[MAX_REMOTE_NUM]; + + char *nameservers[MAX_DNS_NUM + 1]; + int nameserver_num = 0; + + int option_index = 0; + static struct option long_options[] = { + { "fast-open", no_argument, 0, 0 }, + { "acl", required_argument, 0, 0 }, + { "manager-address", required_argument, 0, 0 }, + { "mtu", required_argument, 0, 0 }, + { "help", no_argument, 0, 0 }, +#ifdef __linux__ + { "mptcp", no_argument, 0, 0 }, + { "firewall", no_argument, 0, 0 }, +#endif + { 0, 0, 0, 0 } + }; + + opterr = 0; + + USE_TTY(); + + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:b:c:i:d:a:n:O:o:G:g:huUvA6", + long_options, &option_index)) != -1) { + switch (c) { + case 0: + if (option_index == 0) { + fast_open = 1; + } else if (option_index == 1) { + LOGI("initializing acl..."); + acl = !init_acl(optarg); + } else if (option_index == 2) { + manager_address = optarg; + } else if (option_index == 3) { + mtu = atoi(optarg); + LOGI("set MTU to %d", mtu); + } else if (option_index == 4) { + usage(); + exit(EXIT_SUCCESS); + } else if (option_index == 5) { + mptcp = 1; + LOGI("enable multipath TCP"); + } else if (option_index == 6) { + firewall = 1; + LOGI("enable firewall rules"); + } + break; + case 's': + if (server_num < MAX_REMOTE_NUM) { + server_host[server_num++] = optarg; + } + break; + case 'b': + bind_address = optarg; + break; + case 'p': + server_port = optarg; + break; + case 'k': + password = optarg; + break; + case 'f': + pid_flags = 1; + pid_path = optarg; + break; + case 't': + timeout = optarg; + break; + // SSR beg + case 'O': + protocol = optarg; + break; + case 'm': + method = optarg; + break; + case 'o': + obfs = optarg; + break; + case 'G': + protocol_param = optarg; + break; + case 'g': + obfs_param = optarg; + break; + // SSR end + case 'c': + conf_path = optarg; + break; + case 'i': + iface = optarg; + break; + case 'd': + if (nameserver_num < MAX_DNS_NUM) { + nameservers[nameserver_num++] = optarg; + } + break; + case 'a': + user = optarg; + break; +#ifdef HAVE_SETRLIMIT + case 'n': + nofile = atoi(optarg); + break; +#endif + case 'u': + mode = TCP_AND_UDP; + break; + case 'U': + mode = UDP_ONLY; + break; + case 'v': + verbose = 1; + break; + case 'h': + usage(); + exit(EXIT_SUCCESS); + case 'A': + auth = 1; + break; + case '6': + ipv6first = 1; + break; + case '?': + // The option character is not recognized. + LOGE("Unrecognized option: %s", optarg); + opterr = 1; + break; + } + } + + if (opterr) { + usage(); + exit(EXIT_FAILURE); + } + + if (argc == 1) { + if (conf_path == NULL) { + conf_path = DEFAULT_CONF_PATH; + } + } + + if (conf_path != NULL) { + jconf_t *conf = read_jconf(conf_path); + if (server_num == 0) { + server_num = conf->remote_num; + for (i = 0; i < server_num; i++) + server_host[i] = conf->remote_addr[i].host; + } + if (server_port == NULL) { + server_port = conf->remote_port; + } + if (password == NULL) { + password = conf->password; + } + // SSR beg + if (protocol == NULL) { + protocol = conf->protocol; + LOGI("protocol %s", protocol); + } + if (protocol_param == NULL) { + protocol_param = conf->protocol_param; + LOGI("protocol_param %s", obfs_param); + } + if (method == NULL) { + method = conf->method; + LOGI("method %s", method); + } + if (obfs == NULL) { + obfs = conf->obfs; + LOGI("obfs %s", obfs); + } + if (obfs_param == NULL) { + obfs_param = conf->obfs_param; + LOGI("obfs_param %s", obfs_param); + } + // SSR end + if (timeout == NULL) { + timeout = conf->timeout; + } + if (user == NULL) { + user = conf->user; + } + if (auth == 0) { + auth = conf->auth; + } + if (mode == TCP_ONLY) { + mode = conf->mode; + } + if (mtu == 0) { + mtu = conf->mtu; + } + if (mptcp == 0) { + mptcp = conf->mptcp; + } +#ifdef TCP_FASTOPEN + if (fast_open == 0) { + fast_open = conf->fast_open; + } +#endif +#ifdef HAVE_SETRLIMIT + if (nofile == 0) { + nofile = conf->nofile; + } +#endif + if (conf->nameserver != NULL) { + nameservers[nameserver_num++] = conf->nameserver; + } + if (ipv6first == 0) { + ipv6first = conf->ipv6_first; + } + } + + //_compatible + if(strlen(protocol)>11) + { + char *text; + text = (char*)malloc(12); + memcpy(text, protocol + strlen(protocol) - 11, 12); + + if(strcmp(text, "_compatible") == 0) + { + free(text); + text = (char*)malloc(strlen(protocol) - 11); + memcpy(text, protocol, strlen(protocol) - 11); + int length = strlen(protocol) - 11; + free(protocol); + obfs = (char*)malloc(length); + memset(protocol, 0x00, length); + memcpy(protocol, text, length); + LOGI("protocol compatible enable, %s", protocol); + free(text); + protocol_compatible = 1; + } + } + + if(strlen(obfs)>11) + { + char *text; + text = (char*)malloc(12); + memcpy(text, obfs + strlen(obfs) - 11, 12); + + if(strcmp(text, "_compatible") == 0) + { + free(text); + text = (char*)malloc(strlen(obfs) - 11); + memcpy(text, obfs, strlen(obfs) - 11); + int length = strlen(obfs) - 11; + free(obfs); + obfs = (char*)malloc(length); + memset(obfs, 0x00, length); + memcpy(obfs, text, length); + LOGI("obfs compatible enable, %s", obfs); + free(text); + obfs_compatible = 1; + } + } + + + if (server_num == 0) { + server_host[server_num++] = NULL; + } + + if (server_num == 0 || server_port == NULL || password == NULL) { + usage(); + exit(EXIT_FAILURE); + } + + if (protocol && strcmp(protocol, "verify_sha1") == 0) { + auth = 1; + protocol = NULL; + } + + if (method == NULL) { + method = "rc4-md5"; + } + + if (timeout == NULL) { + timeout = "60"; + } + +#ifdef HAVE_SETRLIMIT + /* + * no need to check the return value here since we will show + * the user an error message if setrlimit(2) fails + */ + if (nofile > 1024) { + if (verbose) { + LOGI("setting NOFILE to %d", nofile); + } + set_nofile(nofile); + } +#endif + + if (pid_flags) { + USE_SYSLOG(argv[0]); + daemonize(pid_path); + } + + if (ipv6first) { + LOGI("resolving hostname to IPv6 address first"); + } + + if (fast_open == 1) { +#ifdef TCP_FASTOPEN + LOGI("using tcp fast open"); +#else + LOGE("tcp fast open is not supported by this environment"); + fast_open = 0; +#endif + } + + if (auth) { + LOGI("onetime authentication enabled"); + } + + if (mode != TCP_ONLY) { + LOGI("UDP relay enabled"); + } + + if (mode == UDP_ONLY) { + LOGI("TCP relay disabled"); + } + +#ifdef __MINGW32__ + winsock_init(); +#else + // ignore SIGPIPE + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, SIG_IGN); + signal(SIGABRT, SIG_IGN); +#endif + + struct ev_signal sigint_watcher; + struct ev_signal sigterm_watcher; + ev_signal_init(&sigint_watcher, signal_cb, SIGINT); + ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); + ev_signal_start(EV_DEFAULT, &sigint_watcher); + ev_signal_start(EV_DEFAULT, &sigterm_watcher); + + // setup keys + LOGI("initializing ciphers... %s", method); + int m = enc_init(password, method); + + // initialize ev loop + struct ev_loop *loop = EV_DEFAULT; + + // setup udns + if (nameserver_num == 0) { +#ifdef __MINGW32__ + nameservers[nameserver_num++] = "8.8.8.8"; + resolv_init(loop, nameservers, nameserver_num, ipv6first); +#else + resolv_init(loop, NULL, 0, ipv6first); +#endif + } else { + resolv_init(loop, nameservers, nameserver_num, ipv6first); + } + + for (int i = 0; i < nameserver_num; i++) + LOGI("using nameserver: %s", nameservers[i]); + + // initialize listen context + listen_ctx_t listen_ctx_list[server_num]; + + // bind to each interface + while (server_num > 0) { + int index = --server_num; + const char *host = server_host[index]; + + if (mode != UDP_ONLY) { + // Bind to port + int listenfd; + listenfd = create_and_bind(host, server_port, mptcp); + if (listenfd == -1) { + FATAL("bind() error"); + } + if (listen(listenfd, SSMAXCONN) == -1) { + FATAL("listen() error"); + } + setfastopen(listenfd); + setnonblocking(listenfd); + listen_ctx_t *listen_ctx = &listen_ctx_list[index]; + + // Setup proxy context + listen_ctx->timeout = atoi(timeout); + listen_ctx->fd = listenfd; + listen_ctx->method = m; + listen_ctx->iface = iface; + + // SSR beg + listen_ctx->protocol_name = protocol; + listen_ctx->protocol_param = protocol_param; + listen_ctx->method = m; + listen_ctx->obfs_name = obfs; + listen_ctx->obfs_param = obfs_param; + listen_ctx->list_protocol_global = malloc(sizeof(void *)); + listen_ctx->list_obfs_global = malloc(sizeof(void *)); + memset(listen_ctx->list_protocol_global, 0, sizeof(void *)); + memset(listen_ctx->list_obfs_global, 0, sizeof(void *)); + // SSR end + + listen_ctx->loop = loop; + + ev_io_init(&listen_ctx->io, accept_cb, listenfd, EV_READ); + ev_io_start(loop, &listen_ctx->io); + } + + // Setup UDP + if (mode != TCP_ONLY) { + init_udprelay(server_host[index], server_port, mtu, m, + auth, atoi(timeout), iface, protocol, protocol_param); + } + + if (host && strcmp(host, ":") > 0) + LOGI("listening at [%s]:%s", host, server_port); + else + LOGI("listening at %s:%s", host ? host : "*", server_port); + } + + if (manager_address != NULL) { + ev_timer_init(&stat_update_watcher, stat_update_cb, UPDATE_INTERVAL, UPDATE_INTERVAL); + ev_timer_start(EV_DEFAULT, &stat_update_watcher); + } + + ev_timer_init(&block_list_watcher, block_list_clear_cb, UPDATE_INTERVAL, UPDATE_INTERVAL); + ev_timer_start(EV_DEFAULT, &block_list_watcher); + + // setuid + if (user != NULL && ! run_as(user)) { + FATAL("failed to switch user"); + } + +#ifndef __MINGW32__ + if (geteuid() == 0){ + LOGI("running from root user"); + } else if (firewall) { + LOGE("firewall setup requires running from root user"); + exit(-1); + } +#endif + + // init block list + init_block_list(firewall); + + // Init connections + cork_dllist_init(&connections); + + // start ev loop + ev_run(loop, 0); + + if (verbose) { + LOGI("closed gracefully"); + } + + // Free block list + free_block_list(); + + if (manager_address != NULL) { + ev_timer_stop(EV_DEFAULT, &stat_update_watcher); + } + ev_timer_stop(EV_DEFAULT, &block_list_watcher); + + // Clean up + for (int i = 0; i <= server_num; i++) { + listen_ctx_t *listen_ctx = &listen_ctx_list[i]; + if (mode != UDP_ONLY) { + ev_io_stop(loop, &listen_ctx->io); + close(listen_ctx->fd); + } + } + + if (mode != UDP_ONLY) { + free_connections(loop); + } + + if (mode != TCP_ONLY) { + free_udprelay(); + } + + resolv_shutdown(loop); + +#ifdef __MINGW32__ + winsock_cleanup(); +#endif + + ev_signal_stop(EV_DEFAULT, &sigint_watcher); + ev_signal_stop(EV_DEFAULT, &sigterm_watcher); + + return 0; +} diff --git a/server/server.h b/server/server.h new file mode 100644 index 0000000..4cd3cf6 --- /dev/null +++ b/server/server.h @@ -0,0 +1,115 @@ +/* + * server.h - Define shadowsocks server's buffers and callbacks + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _SERVER_H +#define _SERVER_H + +#include +#include +#include + +#include "encrypt.h" +#include "jconf.h" +#include "resolv.h" +#include "obfs.h" +#include "protocol.h" + +#include "common.h" + +typedef struct listen_ctx { + ev_io io; + int fd; + int timeout; + int method; + char *iface; + struct ev_loop *loop; + + // SSR + char *protocol_name; + char *protocol_param; + char *obfs_name; + char *obfs_param; + void **list_protocol_global; + void **list_obfs_global; +} listen_ctx_t; + +typedef struct server_ctx { + ev_io io; + ev_timer watcher; + int connected; + struct server *server; +} server_ctx_t; + +typedef struct server { + int fd; + int stage; + buffer_t *buf; + ssize_t buf_capacity; + buffer_t *header_buf; + + int auth; + struct chunk *chunk; + + struct enc_ctx *e_ctx; + struct enc_ctx *d_ctx; + struct server_ctx *recv_ctx; + struct server_ctx *send_ctx; + struct listen_ctx *listen_ctx; + struct remote *remote; + + struct ResolvQuery *query; + + struct cork_dllist_item entries; + + // SSR + obfs *protocol; + obfs *obfs; + obfs_class *protocol_plugin; + obfs_class *obfs_plugin; + int obfs_compatible_state; + int protocol_compatible_state; +} server_t; + +typedef struct query { + server_t *server; + char hostname[257]; +} query_t; + +typedef struct remote_ctx { + ev_io io; + int connected; + struct remote *remote; +} remote_ctx_t; + +typedef struct remote { + int fd; + buffer_t *buf; + ssize_t buf_capacity; + struct remote_ctx *recv_ctx; + struct remote_ctx *send_ctx; + struct server *server; + + // SSR + int remote_index; +} remote_t; + +#endif // _SERVER_H diff --git a/server/tls.c b/server/tls.c new file mode 100644 index 0000000..5c42216 --- /dev/null +++ b/server/tls.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This is a minimal TLS implementation intended only to parse the server name + * extension. This was created based primarily on Wireshark dissection of a + * TLS handshake and RFC4366. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include /* malloc() */ +#include /* strncpy() */ + +#ifndef __MINGW32__ +#include +#else +#include +#endif + +#include "tls.h" +#include "protocol.h" +#include "utils.h" + +#define SERVER_NAME_LEN 256 +#define TLS_HEADER_LEN 5 +#define TLS_HANDSHAKE_CONTENT_TYPE 0x16 +#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 + +#ifndef MIN +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#endif + +extern int verbose; + +static int parse_tls_header(const char *, size_t, char **); +static int parse_extensions(const char *, size_t, char **); +static int parse_server_name_extension(const char *, size_t, char **); + +static const protocol_t tls_protocol_st = { + .default_port = 443, + .parse_packet = &parse_tls_header, +}; +const protocol_t *const tls_protocol = &tls_protocol_st; + +/* Parse a TLS packet for the Server Name Indication extension in the client + * hello handshake, returning the first servername found (pointer to static + * array) + * + * Returns: + * >=0 - length of the hostname and updates *hostname + * caller is responsible for freeing *hostname + * -1 - Incomplete request + * -2 - No Host header included in this request + * -3 - Invalid hostname pointer + * -4 - malloc failure + * < -4 - Invalid TLS client hello + */ +static int +parse_tls_header(const char *data, size_t data_len, char **hostname) +{ + char tls_content_type; + char tls_version_major; + char tls_version_minor; + size_t pos = TLS_HEADER_LEN; + size_t len; + + if (hostname == NULL) + return -3; + + /* Check that our TCP payload is at least large enough for a TLS header */ + if (data_len < TLS_HEADER_LEN) + return -1; + + /* SSL 2.0 compatible Client Hello + * + * High bit of first byte (length) and content type is Client Hello + * + * See RFC5246 Appendix E.2 + */ + if (data[0] & 0x80 && data[2] == 1) { + if (verbose) + LOGI("Received SSL 2.0 Client Hello which can not support SNI."); + return -2; + } + + tls_content_type = data[0]; + if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { + if (verbose) + LOGI("Request did not begin with TLS handshake."); + return -5; + } + + tls_version_major = data[1]; + tls_version_minor = data[2]; + if (tls_version_major < 3) { + if (verbose) + LOGI("Received SSL %d.%d handshake which can not support SNI.", + tls_version_major, tls_version_minor); + + return -2; + } + + /* TLS record length */ + len = ((unsigned char)data[3] << 8) + + (unsigned char)data[4] + TLS_HEADER_LEN; + data_len = MIN(data_len, len); + + /* Check we received entire TLS record length */ + if (data_len < len) + return -1; + + /* + * Handshake + */ + if (pos + 1 > data_len) { + return -5; + } + if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { + if (verbose) + LOGI("Not a client hello"); + + return -5; + } + + /* Skip past fixed length records: + * 1 Handshake Type + * 3 Length + * 2 Version (again) + * 32 Random + * to Session ID Length + */ + pos += 38; + + /* Session ID */ + if (pos + 1 > data_len) + return -5; + len = (unsigned char)data[pos]; + pos += 1 + len; + + /* Cipher Suites */ + if (pos + 2 > data_len) + return -5; + len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; + pos += 2 + len; + + /* Compression Methods */ + if (pos + 1 > data_len) + return -5; + len = (unsigned char)data[pos]; + pos += 1 + len; + + if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) { + if (verbose) + LOGI("Received SSL 3.0 handshake without extensions"); + return -2; + } + + /* Extensions */ + if (pos + 2 > data_len) + return -5; + len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; + pos += 2; + + if (pos + len > data_len) + return -5; + return parse_extensions(data + pos, len, hostname); +} + +static int +parse_extensions(const char *data, size_t data_len, char **hostname) +{ + size_t pos = 0; + size_t len; + + /* Parse each 4 bytes for the extension header */ + while (pos + 4 <= data_len) { + /* Extension Length */ + len = ((unsigned char)data[pos + 2] << 8) + + (unsigned char)data[pos + 3]; + + /* Check if it's a server name extension */ + if (data[pos] == 0x00 && data[pos + 1] == 0x00) { + /* There can be only one extension of each type, so we break + * our state and move p to beinnging of the extension here */ + if (pos + 4 + len > data_len) + return -5; + return parse_server_name_extension(data + pos + 4, len, hostname); + } + pos += 4 + len; /* Advance to the next extension header */ + } + /* Check we ended where we expected to */ + if (pos != data_len) + return -5; + + return -2; +} + +static int +parse_server_name_extension(const char *data, size_t data_len, + char **hostname) +{ + size_t pos = 2; /* skip server name list length */ + size_t len; + + while (pos + 3 < data_len) { + len = ((unsigned char)data[pos + 1] << 8) + + (unsigned char)data[pos + 2]; + + if (pos + 3 + len > data_len) + return -5; + + switch (data[pos]) { /* name type */ + case 0x00: /* host_name */ + *hostname = malloc(len + 1); + if (*hostname == NULL) { + ERROR("malloc() failure"); + return -4; + } + + strncpy(*hostname, data + pos + 3, len); + + (*hostname)[len] = '\0'; + + return len; + default: + if (verbose) + LOGI("Unknown server name extension name type: %d", + data[pos]); + } + pos += 3 + len; + } + /* Check we ended where we expected to */ + if (pos != data_len) + return -5; + + return -2; +} diff --git a/server/tls.h b/server/tls.h new file mode 100644 index 0000000..3998913 --- /dev/null +++ b/server/tls.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef TLS_H +#define TLS_H + +#include "protocol.h" + +const protocol_t *const tls_protocol; + +#endif diff --git a/server/tls1.2_ticket.c b/server/tls1.2_ticket.c new file mode 100644 index 0000000..88970c0 --- /dev/null +++ b/server/tls1.2_ticket.c @@ -0,0 +1,609 @@ + +#include "tls1.2_ticket.h" +#include "list.c" + +typedef struct tls12_ticket_auth_global_data { + uint8_t local_client_id[32]; + List client_data; + time_t startup_time; +}tls12_ticket_auth_global_data; + +typedef struct tls12_ticket_auth_local_data { + int handshake_status; + char *send_buffer; + int send_buffer_size; + char *recv_buffer; + int recv_buffer_size; +}tls12_ticket_auth_local_data; + +void tls12_ticket_auth_local_data_init(tls12_ticket_auth_local_data* local) { + local->handshake_status = 0; + local->send_buffer = malloc(0); + local->send_buffer_size = 0; + local->recv_buffer = malloc(0); + local->recv_buffer_size = 0; +} + +void * tls12_ticket_auth_init_data() { + tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)malloc(sizeof(tls12_ticket_auth_global_data)); + rand_bytes(global->local_client_id, 32); + global->client_data = list_init(22); + global->startup_time = time(NULL); + return global; +} + +obfs * tls12_ticket_auth_new_obfs() { + obfs * self = new_obfs(); + self->l_data = malloc(sizeof(tls12_ticket_auth_local_data)); + tls12_ticket_auth_local_data_init((tls12_ticket_auth_local_data*)self->l_data); + return self; +} + +void tls12_ticket_auth_dispose(obfs *self) { + tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; + if (local->send_buffer != NULL) { + free(local->send_buffer); + local->send_buffer = NULL; + } + if (local->recv_buffer != NULL) { + free(local->recv_buffer); + local->recv_buffer = NULL; + } + free(local); + dispose_obfs(self); +} + +int tls12_ticket_pack_auth_data(tls12_ticket_auth_global_data *global, server_info *server, char *outdata) { + int out_size = 32; + time_t t = time(NULL); + outdata[0] = t >> 24; + outdata[1] = t >> 16; + outdata[2] = t >> 8; + outdata[3] = t; + rand_bytes((uint8_t*)outdata + 4, 18); + + uint8_t *key = (uint8_t*)malloc(server->key_len + 32); + char hash[ONETIMEAUTH_BYTES * 2]; + memcpy(key, server->key, server->key_len); + memcpy(key + server->key_len, global->local_client_id, 32); + ss_sha1_hmac_with_key(hash, outdata, out_size - OBFS_HMAC_SHA1_LEN, key, server->key_len + 32); + free(key); + memcpy(outdata + out_size - OBFS_HMAC_SHA1_LEN, hash, OBFS_HMAC_SHA1_LEN); + return out_size; +} + +void tls12_ticket_auth_pack_data(char *encryptdata, int datalength, int start, int len, char *out_buffer, int outlength) { + out_buffer[outlength] = 0x17; + out_buffer[outlength + 1] = 0x3; + out_buffer[outlength + 2] = 0x3; + out_buffer[outlength + 3] = len >> 8; + out_buffer[outlength + 4] = len; + memcpy(out_buffer + outlength + 5, encryptdata + start, len); +} + +int tls12_ticket_auth_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { + char *encryptdata = *pencryptdata; + tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; + tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)self->server.g_data; + char * out_buffer = NULL; + + if (local->handshake_status == 8) { + if (datalength < 1024) { + if (*capacity < datalength + 5) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = (datalength + 5) * 2); + encryptdata = *pencryptdata; + } + memmove(encryptdata + 5, encryptdata, datalength); + encryptdata[0] = 0x17; + encryptdata[1] = 0x3; + encryptdata[2] = 0x3; + encryptdata[3] = datalength >> 8; + encryptdata[4] = datalength; + return datalength + 5; + } else { + out_buffer = (char*)malloc(datalength + 2048); + int start = 0; + int outlength = 0; + int len; + while (datalength - start > 2048) { + len = xorshift128plus() % 4096 + 100; + if (len > datalength - start) + len = datalength - start; + tls12_ticket_auth_pack_data(encryptdata, datalength, start, len, out_buffer, outlength); + outlength += len + 5; + start += len; + } + if (datalength - start > 0) { + len = datalength - start; + tls12_ticket_auth_pack_data(encryptdata, datalength, start, len, out_buffer, outlength); + outlength += len + 5; + } + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + memcpy(encryptdata, out_buffer, outlength); + free(out_buffer); + return outlength; + } + } + local->send_buffer = (char*)realloc(local->send_buffer, local->send_buffer_size + datalength + 5); + memcpy(local->send_buffer + local->send_buffer_size + 5, encryptdata, datalength); + local->send_buffer[local->send_buffer_size] = 0x17; + local->send_buffer[local->send_buffer_size + 1] = 0x3; + local->send_buffer[local->send_buffer_size + 2] = 0x3; + local->send_buffer[local->send_buffer_size + 3] = datalength >> 8; + local->send_buffer[local->send_buffer_size + 4] = datalength; + local->send_buffer_size += datalength + 5; + + if (local->handshake_status == 0) { +#define CSTR_DECL(name, len, str) const char* name = str; const int len = sizeof(str) - 1; + CSTR_DECL(tls_data0, tls_data0_len, "\x00\x1c\xc0\x2b\xc0\x2f\xcc\xa9\xcc\xa8\xcc\x14\xcc\x13\xc0\x0a\xc0\x14\xc0\x09\xc0\x13\x00\x9c\x00\x35\x00\x2f\x00\x0a\x01\x00" + ); + CSTR_DECL(tls_data1, tls_data1_len, "\xff\x01\x00\x01\x00" + ); + CSTR_DECL(tls_data2, tls_data2_len, "\x00\x17\x00\x00\x00\x23\x00\xd0"); + CSTR_DECL(tls_data3, tls_data3_len, "\x00\x0d\x00\x16\x00\x14\x06\x01\x06\x03\x05\x01\x05\x03\x04\x01\x04\x03\x03\x01\x03\x03\x02\x01\x02\x03\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x12\x00\x00\x75\x50\x00\x00\x00\x0b\x00\x02\x01\x00\x00\x0a\x00\x06\x00\x04\x00\x17\x00\x18" + //"00150066000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" // padding + ); + uint8_t tls_data[2048]; + int tls_data_len = 0; + memcpy(tls_data, tls_data1, tls_data1_len); + tls_data_len += tls_data1_len; + + char hosts[1024]; + char * phost[128]; + int host_num = 0; + int pos; + + char sni[256] = {0}; + if (self->server.param && strlen(self->server.param) == 0) + self->server.param = NULL; + strncpy(hosts, self->server.param ? self->server.param : self->server.host, sizeof hosts); + phost[host_num++] = hosts; + for (pos = 0; hosts[pos]; ++pos) { + if (hosts[pos] == ',') { + phost[host_num++] = &hosts[pos + 1]; + } + } + host_num = xorshift128plus() % host_num; + + sprintf(sni, "%s", phost[host_num]); + int sni_len = strlen(sni); + if (sni_len > 0 && sni[sni_len - 1] >= '0' && sni[sni_len - 1] <= '9') + sni_len = 0; + tls_data[tls_data_len] = '\0'; + tls_data[tls_data_len + 1] = '\0'; + tls_data[tls_data_len + 2] = (sni_len + 5) >> 8; + tls_data[tls_data_len + 3] = (sni_len + 5); + tls_data[tls_data_len + 4] = (sni_len + 3) >> 8; + tls_data[tls_data_len + 5] = (sni_len + 3); + tls_data[tls_data_len + 6] = '\0'; + tls_data[tls_data_len + 7] = sni_len >> 8; + tls_data[tls_data_len + 8] = sni_len; + memcpy(tls_data + tls_data_len + 9, sni, sni_len); + tls_data_len += 9 + sni_len; + memcpy(tls_data + tls_data_len, tls_data2, tls_data2_len); + tls_data_len += tls_data2_len; + rand_bytes(tls_data + tls_data_len, 208); + tls_data_len += 208; + memcpy(tls_data + tls_data_len, tls_data3, tls_data3_len); + tls_data_len += tls_data3_len; + + datalength = 11 + 32 + 1 + 32 + tls_data0_len + 2 + tls_data_len; + out_buffer = (char*)malloc(datalength); + char *pdata = out_buffer + datalength - tls_data_len; + int len = tls_data_len; + memcpy(pdata, tls_data, tls_data_len); + pdata[-1] = tls_data_len; + pdata[-2] = tls_data_len >> 8; + pdata -= 2; len += 2; + memcpy(pdata - tls_data0_len, tls_data0, tls_data0_len); + pdata -= tls_data0_len; len += tls_data0_len; + memcpy(pdata - 32, global->local_client_id, 32); + pdata -= 32; len += 32; + pdata[-1] = 0x20; + pdata -= 1; len += 1; + tls12_ticket_pack_auth_data(global, &self->server, pdata - 32); + pdata -= 32; len += 32; + pdata[-1] = 0x3; + pdata[-2] = 0x3; // tls version + pdata -= 2; len += 2; + pdata[-1] = len; + pdata[-2] = len >> 8; + pdata[-3] = 0; + pdata[-4] = 1; + pdata -= 4; len += 4; + + pdata[-1] = len; + pdata[-2] = len >> 8; + pdata -= 2; len += 2; + pdata[-1] = 0x1; + pdata[-2] = 0x3; // tls version + pdata -= 2; len += 2; + pdata[-1] = 0x16; // tls handshake + pdata -= 1; len += 1; + + local->handshake_status = 1; + } else if (datalength == 0) { + datalength = local->send_buffer_size + 43; + out_buffer = (char*)malloc(datalength); + char *pdata = out_buffer; + memcpy(pdata, "\x14\x03\x03\x00\x01\x01", 6); + pdata += 6; + memcpy(pdata, "\x16\x03\x03\x00\x20", 5); + pdata += 5; + rand_bytes((uint8_t*)pdata, 22); + pdata += 22; + + uint8_t *key = (uint8_t*)malloc(self->server.key_len + 32); + char hash[ONETIMEAUTH_BYTES * 2]; + memcpy(key, self->server.key, self->server.key_len); + memcpy(key + self->server.key_len, global->local_client_id, 32); + ss_sha1_hmac_with_key(hash, out_buffer, pdata - out_buffer, key, self->server.key_len + 32); + free(key); + memcpy(pdata, hash, OBFS_HMAC_SHA1_LEN); + + pdata += OBFS_HMAC_SHA1_LEN; + memcpy(pdata, local->send_buffer, local->send_buffer_size); + free(local->send_buffer); + local->send_buffer = NULL; + + local->handshake_status = 8; + } else { + return 0; + } + if (*capacity < datalength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = datalength * 2); + encryptdata = *pencryptdata; + } + memmove(encryptdata, out_buffer, datalength); + free(out_buffer); + return datalength; +} + +int tls12_ticket_auth_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { + char *encryptdata = *pencryptdata; + tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; + tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)self->server.g_data; + char * out_buffer = NULL; + + if (local->handshake_status == 8) { + if (datalength < 1024) { + if (*capacity < datalength + 5) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = (datalength + 5) * 2); + encryptdata = *pencryptdata; + } + memmove(encryptdata + 5, encryptdata, datalength); + encryptdata[0] = 0x17; + encryptdata[1] = 0x3; + encryptdata[2] = 0x3; + encryptdata[3] = datalength >> 8; + encryptdata[4] = datalength; + return datalength + 5; + } else { + out_buffer = (char*)malloc(datalength + 2048); + int start = 0; + int outlength = 0; + int len; + while (datalength - start > 2048) { + len = xorshift128plus() % 4096 + 100; + if (len > datalength - start) + len = datalength - start; + tls12_ticket_auth_pack_data(encryptdata, datalength, start, len, out_buffer, outlength); + outlength += len + 5; + start += len; + } + if (datalength - start > 0) { + len = datalength - start; + tls12_ticket_auth_pack_data(encryptdata, datalength, start, len, out_buffer, outlength); + outlength += len + 5; + } + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + memcpy(encryptdata, out_buffer, outlength); + free(out_buffer); + return outlength; + } + } + + local->handshake_status = 3; + + out_buffer = (char*)malloc(43 + 86); + int data_len = 0; + char *p_data = out_buffer + 86; + + memcpy(p_data - 10, "\xc0\x2f\x00\x00\x05\xff\x01\x00\x01\x00", 10); + p_data -= 10;data_len += 10; + + memcpy(p_data - 32, global->local_client_id, 32); + p_data -= 32;data_len += 32; + + p_data[-1] = 0x20; + p_data -= 1;data_len += 1; + + tls12_ticket_pack_auth_data(global, &self->server, p_data - 32); + p_data -= 32;data_len += 32; + + p_data[-1] = 0x3; + p_data[-2] = 0x3; // tls version + p_data -= 2;data_len += 2; + + p_data[-1] = data_len; + p_data[-2] = data_len >> 8; + p_data[-3] = 0x00; + p_data[-4] = 0x02; + p_data -= 4; data_len += 4; + + p_data[-1] = data_len; + p_data[-2] = data_len >> 8; + p_data[-3] = 0x03; + p_data[-4] = 0x03; + p_data[-5] = 0x16; + p_data -= 5; data_len += 5; + + memcpy(out_buffer, p_data, data_len); + char *pdata = out_buffer + 86; + + memcpy(pdata, "\x14\x03\x03\x00\x01\x01", 6); + pdata += 6; + memcpy(pdata, "\x16\x03\x03\x00\x20", 5); + pdata += 5; + rand_bytes((uint8_t*)pdata, 22); + pdata += 22; + + uint8_t *key = (uint8_t*)malloc(self->server.key_len + 32); + char hash[ONETIMEAUTH_BYTES * 2]; + memcpy(key, self->server.key, self->server.key_len); + memcpy(key + self->server.key_len, global->local_client_id, 32); + ss_sha1_hmac_with_key(hash, out_buffer, 43 + 86, key, self->server.key_len + 32); + free(key); + memcpy(pdata, hash, OBFS_HMAC_SHA1_LEN); + + memmove(encryptdata, out_buffer, 43 + 86); + free(out_buffer); + return 43 + 86; +} + +int tls12_ticket_auth_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) { + char *encryptdata = *pencryptdata; + tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; + tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)self->server.g_data; + + *needsendback = 0; + + if (local->handshake_status == 8) { + local->recv_buffer_size += datalength; + local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size); + memcpy(local->recv_buffer + local->recv_buffer_size - datalength, encryptdata, datalength); + datalength = 0; + while (local->recv_buffer_size > 5) { + if (local->recv_buffer[0] != 0x17) + return -1; + int size = ((int)(unsigned char)local->recv_buffer[3] << 8) + (unsigned char)local->recv_buffer[4]; + if (size + 5 > local->recv_buffer_size) + break; + if (*capacity < datalength + size) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = (datalength + size) * 2); + encryptdata = *pencryptdata; + } + memcpy(encryptdata + datalength, local->recv_buffer + 5, size); + datalength += size; + local->recv_buffer_size -= 5 + size; + memmove(local->recv_buffer, local->recv_buffer + 5 + size, local->recv_buffer_size); + } + return datalength; + } + if (datalength < 11 + 32 + 1 + 32) { + return -1; + } + + uint8_t *key = (uint8_t*)malloc(self->server.key_len + 32); + char hash[ONETIMEAUTH_BYTES * 2]; + memcpy(key, self->server.key, self->server.key_len); + memcpy(key + self->server.key_len, global->local_client_id, 32); + ss_sha1_hmac_with_key(hash, encryptdata + 11, 22, key, self->server.key_len + 32); + free(key); + + if (memcmp(encryptdata + 33, hash, OBFS_HMAC_SHA1_LEN)) { + return -1; + } + + *needsendback = 1; + return 0; +} + +int tls12_ticket_auth_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) { + char *encryptdata = *pencryptdata; + tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; + tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)self->server.g_data; + + *needsendback = 0; + + if (local->handshake_status == 8) { + if(datalength != 0) + { + local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size + datalength); + memmove(local->recv_buffer + local->recv_buffer_size, encryptdata, datalength); + local->recv_buffer_size += datalength; + } + datalength = 0; + + while (local->recv_buffer_size > 5) { + if (local->recv_buffer[0] != 0x17 || local->recv_buffer[1] != 0x03 || local->recv_buffer[2] != 0x03) + { + LOGE("server_decode data error, wrong tls version 3"); + return -1; + } + int size = ((int)(unsigned char)local->recv_buffer[3] << 8) + (unsigned char)local->recv_buffer[4]; + if (size + 5 > local->recv_buffer_size) + break; + if (*capacity < local->recv_buffer_size + size) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = (local->recv_buffer_size + size) * 2); + encryptdata = *pencryptdata; + } + memcpy(encryptdata + datalength, local->recv_buffer + 5, size); + datalength += size; + local->recv_buffer_size -= 5 + size; + memmove(local->recv_buffer, local->recv_buffer + 5 + size, local->recv_buffer_size); + } + return datalength; + } + + if (local->handshake_status == 3) { + + char *verify = encryptdata; + + if(datalength < 43) + { + LOGE("server_decode data error, too short:%d", (int)datalength); + return -1; + } + + if(encryptdata[0] != 0x14 || encryptdata[1] != 0x03 || encryptdata[2] != 0x03 || encryptdata[3] != 0x00 || encryptdata[4] != 0x01 || encryptdata[5] != 0x01) + { + LOGE("server_decode data error, wrong tls version"); + return -1; + } + + encryptdata += 6; + + if(encryptdata[0] != 0x16 || encryptdata[1] != 0x03 || encryptdata[2] != 0x03 || encryptdata[3] != 0x00 || encryptdata[4] != 0x20) + { + LOGE("server_decode data error, wrong tls version 2"); + return -1; + } + + uint8_t *key = (uint8_t*)malloc(self->server.key_len + 32); + char hash[ONETIMEAUTH_BYTES * 2]; + memcpy(key, self->server.key, self->server.key_len); + memcpy(key + self->server.key_len, global->local_client_id, 32); + ss_sha1_hmac_with_key(hash, verify, 33, key, self->server.key_len + 32); + free(key); + + if (memcmp(verify + 33, hash, OBFS_HMAC_SHA1_LEN) != 0) { + LOGE("server_decode data error, hash Mismatch %d",(int)memcmp(verify + 33, hash, OBFS_HMAC_SHA1_LEN)); + return -1; + } + + local->recv_buffer_size = datalength - 43; + local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size); + memmove(local->recv_buffer, encryptdata + 37, datalength - 43); + + local->handshake_status = 8; + return tls12_ticket_auth_server_decode(self, pencryptdata, 0, capacity, needsendback); + } + + local->handshake_status = 2; + if(encryptdata[0] != 0x16 || encryptdata[1] != 0x03 || encryptdata[2] != 0x01) + { + return -1; + } + + encryptdata += 3; + + { + int size = ((int)(unsigned char)encryptdata[0] << 8) + (unsigned char)encryptdata[1]; + if(size != datalength - 5) + { + LOGE("tls_auth wrong tls head size"); + return -1; + } + } + + encryptdata += 2; + + if(encryptdata[0] != 0x01 || encryptdata[1] != 0x00) + { + LOGE("tls_auth not client hello message"); + return -1; + } + + encryptdata += 2; + + { + int size = ((int)(unsigned char)encryptdata[0] << 8) + (unsigned char)encryptdata[1]; + if(size != datalength - 9) + { + LOGE("tls_auth wrong message size"); + return -1; + } + } + + encryptdata += 2; + + if(encryptdata[0] != 0x03 || encryptdata[1] != 0x03) + { + LOGE("tls_auth wrong tls version"); + return -1; + } + + encryptdata += 2; + + char *verifyid = encryptdata; + + encryptdata += 32; + + int sessionid_len = encryptdata[0]; + if(sessionid_len < 32) + { + LOGE("tls_auth wrong sessionid_len"); + return -1; + } + + char *sessionid = encryptdata + 1; + memcpy(global->local_client_id , sessionid, sessionid_len); + + uint8_t *key = (uint8_t*)malloc(self->server.key_len + sessionid_len); + char hash[ONETIMEAUTH_BYTES * 2]; + memcpy(key, self->server.key, self->server.key_len); + memcpy(key + self->server.key_len, global->local_client_id, sessionid_len); + ss_sha1_hmac_with_key(hash, verifyid, 22, key, self->server.key_len + sessionid_len); + free(key); + + encryptdata += (sessionid_len + 1); + + long utc_time = ((int)(unsigned char)verifyid[0] << 24) + ((int)(unsigned char)verifyid[1] << 16) + ((int)(unsigned char)verifyid[2] << 8) + (unsigned char)verifyid[3]; + time_t t = time(NULL); + + + if (self->server.param && strlen(self->server.param) == 0) + { + self->server.param = NULL; + } + + int max_time_dif = 0; + int time_dif = utc_time - t; + if(self->server.param) + { + max_time_dif = atoi(self->server.param); + } + + if(max_time_dif > 0 && (time_dif < -max_time_dif || time_dif > max_time_dif || utc_time - global->startup_time < -max_time_dif / 2)) + { + LOGE("tls_auth wrong time"); + return -1; + } + + if (memcmp(verifyid + 22, hash, OBFS_HMAC_SHA1_LEN)) { + LOGE("tls_auth wrong sha1"); + return -1; + } + + int search_result = global->client_data->have_same_cmp(global->client_data, verifyid); + if(search_result != 0) + { + LOGE("replay attack detect!"); + return -1; + } + + global->client_data->add_back(global->client_data, verifyid); + + encryptdata += 48; + + *needsendback = 1; + + return 0; +} diff --git a/server/tls1.2_ticket.h b/server/tls1.2_ticket.h new file mode 100644 index 0000000..10a57c9 --- /dev/null +++ b/server/tls1.2_ticket.h @@ -0,0 +1,20 @@ +/* + * http_simple.h - Define shadowsocksR server's buffers and callbacks + * + * Copyright (C) 2015 - 2016, Break Wa11 + */ + +#ifndef _TLS1_2_TICKET_H +#define _TLS1_2_TICKET_H + +void * tls12_ticket_auth_init_data(); +obfs * tls12_ticket_auth_new_obfs(); +void tls12_ticket_auth_dispose(obfs *self); + +int tls12_ticket_auth_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); +int tls12_ticket_auth_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback); + +int tls12_ticket_auth_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); +int tls12_ticket_auth_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback); + +#endif // _TLS1_2_TICKET_H diff --git a/server/udprelay.c b/server/udprelay.c new file mode 100644 index 0000000..d9251ee --- /dev/null +++ b/server/udprelay.c @@ -0,0 +1,1452 @@ +/* + * udprelay.c - Setup UDP relay for both client and server + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __MINGW32__ +#include +#include +#include +#include +#include +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__) +#include +#include +#define SET_INTERFACE +#endif + +#ifdef __MINGW32__ +#include "win32.h" +#endif + +#include +#include + +#include "utils.h" +#include "netutils.h" +#include "cache.h" +#include "udprelay.h" + +#ifdef MODULE_REMOTE +#define MAX_UDP_CONN_NUM 512 +#else +#define MAX_UDP_CONN_NUM 256 +#endif + +#ifdef MODULE_REMOTE +#ifdef MODULE_ +#error "MODULE_REMOTE and MODULE_LOCAL should not be both defined" +#endif +#endif + +#ifndef EAGAIN +#define EAGAIN EWOULDBLOCK +#endif + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif + +static void server_recv_cb(EV_P_ ev_io *w, int revents); +static void remote_recv_cb(EV_P_ ev_io *w, int revents); +static void remote_timeout_cb(EV_P_ ev_timer *watcher, int revents); + +static char *hash_key(const int af, const struct sockaddr_storage *addr); +#ifdef MODULE_REMOTE +static void query_resolve_cb(struct sockaddr *addr, void *data); +#endif +static void close_and_free_remote(EV_P_ remote_ctx_t *ctx); +static remote_ctx_t *new_remote(int fd, server_ctx_t *server_ctx); + +#ifdef ANDROID +extern uint64_t tx; +extern uint64_t rx; +extern int vpn; +#endif + +extern int verbose; +#ifdef MODULE_REMOTE +extern uint64_t tx; +extern uint64_t rx; +#endif + +static int packet_size = DEFAULT_PACKET_SIZE; +static int buf_size = DEFAULT_PACKET_SIZE * 2; +static int server_num = 0; +static server_ctx_t *server_ctx_list[MAX_REMOTE_NUM] = { NULL }; + +#ifndef __MINGW32__ +static int +setnonblocking(int fd) +{ + int flags; + if (-1 == (flags = fcntl(fd, F_GETFL, 0))) { + flags = 0; + } + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +#endif + +#if defined(MODULE_REMOTE) && defined(SO_BROADCAST) +static int +set_broadcast(int socket_fd) +{ + int opt = 1; + return setsockopt(socket_fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)); +} + +#endif + +#ifdef SO_NOSIGPIPE +static int +set_nosigpipe(int socket_fd) +{ + int opt = 1; + return setsockopt(socket_fd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); +} + +#endif + +#ifdef MODULE_REDIR + +#ifndef IP_TRANSPARENT +#define IP_TRANSPARENT 19 +#endif + +#ifndef IP_RECVORIGDSTADDR +#define IP_RECVORIGDSTADDR 20 +#endif + +static int +get_dstaddr(struct msghdr *msg, struct sockaddr_storage *dstaddr) +{ + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVORIGDSTADDR) { + memcpy(dstaddr, CMSG_DATA(cmsg), sizeof(struct sockaddr_in)); + dstaddr->ss_family = AF_INET; + return 0; + } else if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IP_RECVORIGDSTADDR) { + memcpy(dstaddr, CMSG_DATA(cmsg), sizeof(struct sockaddr_in6)); + dstaddr->ss_family = AF_INET6; + return 0; + } + } + + return 1; +} + +#endif + +#define HASH_KEY_LEN sizeof(struct sockaddr_storage) + sizeof(int) +static char * +hash_key(const int af, const struct sockaddr_storage *addr) +{ + size_t addr_len = sizeof(struct sockaddr_storage); + static char key[HASH_KEY_LEN]; + + memset(key, 0, HASH_KEY_LEN); + memcpy(key, &af, sizeof(int)); + memcpy(key + sizeof(int), (const uint8_t *)addr, addr_len); + + return key; +} + +#if defined(MODULE_REDIR) || defined(MODULE_REMOTE) +static int +construct_udprealy_header(const struct sockaddr_storage *in_addr, + char *addr_header) +{ + int addr_header_len = 0; + if (in_addr->ss_family == AF_INET) { + struct sockaddr_in *addr = (struct sockaddr_in *)in_addr; + size_t addr_len = sizeof(struct in_addr); + addr_header[addr_header_len++] = 1; + memcpy(addr_header + addr_header_len, &addr->sin_addr, addr_len); + addr_header_len += addr_len; + memcpy(addr_header + addr_header_len, &addr->sin_port, 2); + addr_header_len += 2; + } else if (in_addr->ss_family == AF_INET6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)in_addr; + size_t addr_len = sizeof(struct in6_addr); + addr_header[addr_header_len++] = 4; + memcpy(addr_header + addr_header_len, &addr->sin6_addr, addr_len); + addr_header_len += addr_len; + memcpy(addr_header + addr_header_len, &addr->sin6_port, 2); + addr_header_len += 2; + } else { + return 0; + } + return addr_header_len; +} + +#endif + +static int +parse_udprealy_header(const char *buf, const size_t buf_len, + char *host, char *port, struct sockaddr_storage *storage) +{ + const uint8_t atyp = *(uint8_t *)buf; + int offset = 1; + + // get remote addr and port + if ((atyp & ADDRTYPE_MASK) == 1) { + // IP V4 + size_t in_addr_len = sizeof(struct in_addr); + if (buf_len >= in_addr_len + 3) { + if (storage != NULL) { + struct sockaddr_in *addr = (struct sockaddr_in *)storage; + addr->sin_family = AF_INET; + addr->sin_addr = *(struct in_addr *)(buf + offset); + addr->sin_port = *(uint16_t *)(buf + offset + in_addr_len); + } + if (host != NULL) { + dns_ntop(AF_INET, (const void *)(buf + offset), + host, INET_ADDRSTRLEN); + } + offset += in_addr_len; + } + } else if ((atyp & ADDRTYPE_MASK) == 3) { + // Domain name + uint8_t name_len = *(uint8_t *)(buf + offset); + if (name_len + 4 <= buf_len) { + if (storage != NULL) { + char tmp[257] = { 0 }; + struct cork_ip ip; + memcpy(tmp, buf + offset + 1, name_len); + if (cork_ip_init(&ip, tmp) != -1) { + if (ip.version == 4) { + struct sockaddr_in *addr = (struct sockaddr_in *)storage; + dns_pton(AF_INET, tmp, &(addr->sin_addr)); + addr->sin_port = *(uint16_t *)(buf + offset + 1 + name_len); + addr->sin_family = AF_INET; + } else if (ip.version == 6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; + dns_pton(AF_INET, tmp, &(addr->sin6_addr)); + addr->sin6_port = *(uint16_t *)(buf + offset + 1 + name_len); + addr->sin6_family = AF_INET6; + } + } + } + if (host != NULL) { + memcpy(host, buf + offset + 1, name_len); + } + offset += 1 + name_len; + } + } else if ((atyp & ADDRTYPE_MASK) == 4) { + // IP V6 + size_t in6_addr_len = sizeof(struct in6_addr); + if (buf_len >= in6_addr_len + 3) { + if (storage != NULL) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; + addr->sin6_family = AF_INET6; + addr->sin6_addr = *(struct in6_addr *)(buf + offset); + addr->sin6_port = *(uint16_t *)(buf + offset + in6_addr_len); + } + if (host != NULL) { + dns_ntop(AF_INET6, (const void *)(buf + offset), + host, INET6_ADDRSTRLEN); + } + offset += in6_addr_len; + } + } + + if (offset == 1) { + LOGE("[udp] invalid header with addr type %d", atyp); + return 0; + } + + if (port != NULL) { + sprintf(port, "%d", ntohs(*(uint16_t *)(buf + offset))); + } + offset += 2; + + return offset; +} + +static char * +get_addr_str(const struct sockaddr *sa) +{ + static char s[SS_ADDRSTRLEN]; + memset(s, 0, SS_ADDRSTRLEN); + char addr[INET6_ADDRSTRLEN] = { 0 }; + char port[PORTSTRLEN] = { 0 }; + uint16_t p; + + switch (sa->sa_family) { + case AF_INET: + dns_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), + addr, INET_ADDRSTRLEN); + p = ntohs(((struct sockaddr_in *)sa)->sin_port); + sprintf(port, "%d", p); + break; + + case AF_INET6: + dns_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), + addr, INET6_ADDRSTRLEN); + p = ntohs(((struct sockaddr_in *)sa)->sin_port); + sprintf(port, "%d", p); + break; + + default: + strncpy(s, "Unknown AF", SS_ADDRSTRLEN); + } + + int addr_len = strlen(addr); + int port_len = strlen(port); + memcpy(s, addr, addr_len); + memcpy(s + addr_len + 1, port, port_len); + s[addr_len] = ':'; + + return s; +} + +int +create_remote_socket(int ipv6) +{ + int remote_sock; + + if (ipv6) { + // Try to bind IPv6 first + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(struct sockaddr_in6)); + addr.sin6_family = AF_INET6; + addr.sin6_addr = in6addr_any; + addr.sin6_port = 0; + remote_sock = socket(AF_INET6, SOCK_DGRAM, 0); + if (remote_sock == -1) { + ERROR("[udp] cannot create socket"); + return -1; + } + if (bind(remote_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { + FATAL("[udp] cannot bind remote"); + return -1; + } + } else { + // Or else bind to IPv4 + struct sockaddr_in addr; + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = 0; + remote_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (remote_sock == -1) { + ERROR("[udp] cannot create socket"); + return -1; + } + + if (bind(remote_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { + FATAL("[udp] cannot bind remote"); + return -1; + } + } + return remote_sock; +} + +int +create_server_socket(const char *host, const char *port) +{ + struct addrinfo hints; + struct addrinfo *result, *rp, *ipv4v6bindall; + int s, server_sock; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ + hints.ai_socktype = SOCK_DGRAM; /* We want a UDP socket */ + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* For wildcard IP address */ + hints.ai_protocol = IPPROTO_UDP; + + s = getaddrinfo(host, port, &hints, &result); + if (s != 0) { + LOGE("[udp] getaddrinfo: %s", gai_strerror(s)); + return -1; + } + + rp = result; + + /* + * On Linux, with net.ipv6.bindv6only = 0 (the default), getaddrinfo(NULL) with + * AI_PASSIVE returns 0.0.0.0 and :: (in this order). AI_PASSIVE was meant to + * return a list of addresses to listen on, but it is impossible to listen on + * 0.0.0.0 and :: at the same time, if :: implies dualstack mode. + */ + if (!host) { + ipv4v6bindall = result; + + /* Loop over all address infos found until a IPV6 address is found. */ + while (ipv4v6bindall) { + if (ipv4v6bindall->ai_family == AF_INET6) { + rp = ipv4v6bindall; /* Take first IPV6 address available */ + break; + } + ipv4v6bindall = ipv4v6bindall->ai_next; /* Get next address info, if any */ + } + } + + for (/*rp = result*/; rp != NULL; rp = rp->ai_next) { + server_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (server_sock == -1) { + continue; + } + + if (rp->ai_family == AF_INET6) { + int ipv6only = host ? 1 : 0; + setsockopt(server_sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)); + } + + int opt = 1; + setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); +#ifdef SO_NOSIGPIPE + set_nosigpipe(server_sock); +#endif + int err = set_reuseport(server_sock); + if (err == 0) { + LOGI("udp port reuse enabled"); + } +#ifdef IP_TOS + // Set QoS flag + int tos = 46; + setsockopt(server_sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); +#endif + +#ifdef MODULE_REDIR + if (setsockopt(server_sock, SOL_IP, IP_TRANSPARENT, &opt, sizeof(opt))) { + ERROR("[udp] setsockopt IP_TRANSPARENT"); + exit(EXIT_FAILURE); + } + if (setsockopt(server_sock, IPPROTO_IP, IP_RECVORIGDSTADDR, &opt, sizeof(opt))) { + FATAL("[udp] setsockopt IP_RECVORIGDSTADDR"); + } +#endif + + s = bind(server_sock, rp->ai_addr, rp->ai_addrlen); + if (s == 0) { + /* We managed to bind successfully! */ + break; + } else { + ERROR("[udp] bind"); + } + + close(server_sock); + } + + if (rp == NULL) { + LOGE("[udp] cannot bind"); + return -1; + } + + freeaddrinfo(result); + + return server_sock; +} + +remote_ctx_t * +new_remote(int fd, server_ctx_t *server_ctx) +{ + remote_ctx_t *ctx = ss_malloc(sizeof(remote_ctx_t)); + memset(ctx, 0, sizeof(remote_ctx_t)); + + ctx->fd = fd; + ctx->server_ctx = server_ctx; + + ev_io_init(&ctx->io, remote_recv_cb, fd, EV_READ); + ev_timer_init(&ctx->watcher, remote_timeout_cb, server_ctx->timeout, + server_ctx->timeout); + + return ctx; +} + +server_ctx_t * +new_server_ctx(int fd) +{ + server_ctx_t *ctx = ss_malloc(sizeof(server_ctx_t)); + memset(ctx, 0, sizeof(server_ctx_t)); + + ctx->fd = fd; + + ev_io_init(&ctx->io, server_recv_cb, fd, EV_READ); + + return ctx; +} + +#ifdef MODULE_REMOTE +struct query_ctx * +new_query_ctx(char *buf, size_t len) +{ + struct query_ctx *ctx = ss_malloc(sizeof(struct query_ctx)); + memset(ctx, 0, sizeof(struct query_ctx)); + ctx->buf = ss_malloc(sizeof(buffer_t)); + balloc(ctx->buf, len); + memcpy(ctx->buf->array, buf, len); + ctx->buf->len = len; + return ctx; +} + +void +close_and_free_query(EV_P_ struct query_ctx *ctx) +{ + if (ctx != NULL) { + if (ctx->query != NULL) { + resolv_cancel(ctx->query); + ctx->query = NULL; + } + if (ctx->buf != NULL) { + bfree(ctx->buf); + ss_free(ctx->buf); + } + ss_free(ctx); + } +} + +#endif + +void +close_and_free_remote(EV_P_ remote_ctx_t *ctx) +{ + if (ctx != NULL) { + ev_timer_stop(EV_A_ & ctx->watcher); + ev_io_stop(EV_A_ & ctx->io); + close(ctx->fd); + ss_free(ctx); + } +} + +static void +remote_timeout_cb(EV_P_ ev_timer *watcher, int revents) +{ + remote_ctx_t *remote_ctx + = cork_container_of(watcher, remote_ctx_t, watcher); + + if (verbose) { + LOGI("[udp] connection timeout"); + } + + char *key = hash_key(remote_ctx->af, &remote_ctx->src_addr); + cache_remove(remote_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN); +} + +#ifdef MODULE_REMOTE +static void +query_resolve_cb(struct sockaddr *addr, void *data) +{ + struct query_ctx *query_ctx = (struct query_ctx *)data; + struct ev_loop *loop = query_ctx->server_ctx->loop; + + if (verbose) { + LOGI("[udp] udns resolved"); + } + + query_ctx->query = NULL; + + if (addr == NULL) { + LOGE("[udp] udns returned an error"); + } else { + remote_ctx_t *remote_ctx = query_ctx->remote_ctx; + int cache_hit = 0; + + // Lookup in the conn cache + if (remote_ctx == NULL) { + char *key = hash_key(AF_UNSPEC, &query_ctx->src_addr); + cache_lookup(query_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)&remote_ctx); + } + + if (remote_ctx == NULL) { + int remotefd = create_remote_socket(addr->sa_family == AF_INET6); + if (remotefd != -1) { + setnonblocking(remotefd); +#ifdef SO_BROADCAST + set_broadcast(remotefd); +#endif +#ifdef SO_NOSIGPIPE + set_nosigpipe(remotefd); +#endif +#ifdef IP_TOS + // Set QoS flag + int tos = 46; + setsockopt(remotefd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); +#endif +#ifdef SET_INTERFACE + if (query_ctx->server_ctx->iface) { + if (setinterface(remotefd, query_ctx->server_ctx->iface) == -1) + ERROR("setinterface"); + } +#endif + remote_ctx = new_remote(remotefd, query_ctx->server_ctx); + remote_ctx->src_addr = query_ctx->src_addr; + remote_ctx->server_ctx = query_ctx->server_ctx; + remote_ctx->addr_header_len = query_ctx->addr_header_len; + memcpy(remote_ctx->addr_header, query_ctx->addr_header, + query_ctx->addr_header_len); + } else { + ERROR("[udp] bind() error"); + } + } else { + cache_hit = 1; + } + + if (remote_ctx != NULL) { + memcpy(&remote_ctx->dst_addr, addr, sizeof(struct sockaddr_storage)); + + size_t addr_len = get_sockaddr_len(addr); + int s = sendto(remote_ctx->fd, query_ctx->buf->array, query_ctx->buf->len, + 0, addr, addr_len); + + if (s == -1) { + ERROR("[udp] sendto_remote"); + if (!cache_hit) { + close_and_free_remote(EV_A_ remote_ctx); + } + } else { + if (!cache_hit) { + // Add to conn cache + char *key = hash_key(AF_UNSPEC, &remote_ctx->src_addr); + cache_insert(query_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); + ev_io_start(EV_A_ & remote_ctx->io); + ev_timer_start(EV_A_ & remote_ctx->watcher); + } + } + } + } + + // clean up + close_and_free_query(EV_A_ query_ctx); +} + +#endif + +static void +remote_recv_cb(EV_P_ ev_io *w, int revents) +{ + ssize_t r; + remote_ctx_t *remote_ctx = (remote_ctx_t *)w; + server_ctx_t *server_ctx = remote_ctx->server_ctx; + + // server has been closed + if (server_ctx == NULL) { + LOGE("[udp] invalid server"); + close_and_free_remote(EV_A_ remote_ctx); + return; + } + + struct sockaddr_storage src_addr; + socklen_t src_addr_len = sizeof(struct sockaddr_storage); + memset(&src_addr, 0, src_addr_len); + + buffer_t *buf = ss_malloc(sizeof(buffer_t)); + balloc(buf, buf_size); + + // recv + r = recvfrom(remote_ctx->fd, buf->array, buf_size, 0, (struct sockaddr *)&src_addr, &src_addr_len); + + if (r == -1) { + // error on recv + // simply drop that packet + ERROR("[udp] remote_recv_recvfrom"); + goto CLEAN_UP; + } else if (r > packet_size) { + LOGE("[udp] remote_recv_recvfrom fragmentation"); + goto CLEAN_UP; + } + + buf->len = r; + +#ifdef MODULE_LOCAL + int err = ss_decrypt_all(buf, server_ctx->method, 0, buf_size); + if (err) { + // drop the packet silently + goto CLEAN_UP; + } + + //SSR beg + if (server_ctx->protocol_plugin) { + obfs_class *protocol_plugin = server_ctx->protocol_plugin; + if (protocol_plugin->client_udp_post_decrypt) { + buf->len = protocol_plugin->client_udp_post_decrypt(server_ctx->protocol, &buf->array, buf->len, &buf->capacity); + if ((int)buf->len < 0) { + LOGE("client_udp_post_decrypt"); + close_and_free_remote(EV_A_ remote_ctx); + return; + } + if ( buf->len == 0 ) + return; + } + } + // SSR end + +#ifdef MODULE_REDIR + struct sockaddr_storage dst_addr; + memset(&dst_addr, 0, sizeof(struct sockaddr_storage)); + int len = parse_udprealy_header(buf->array, buf->len, NULL, NULL, &dst_addr); + + if (dst_addr.ss_family != AF_INET && dst_addr.ss_family != AF_INET6) { + LOGI("[udp] ss-redir does not support domain name"); + goto CLEAN_UP; + } + + if (verbose) { + char src[SS_ADDRSTRLEN]; + char dst[SS_ADDRSTRLEN]; + strcpy(src, get_addr_str((struct sockaddr *)&src_addr)); + strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr)); + LOGI("[udp] recv %s via %s", dst, src); + } +#else + int len = parse_udprealy_header(buf->array, buf->len, NULL, NULL, NULL); +#endif + + if (len == 0) { + LOGI("[udp] error in parse header"); + // error in parse header + goto CLEAN_UP; + } + + // server may return using a different address type other than the type we + // have used during sending +#if defined(MODULE_TUNNEL) || defined(MODULE_REDIR) + // Construct packet + buf->len -= len; + memmove(buf->array, buf->array + len, buf->len); +#else +#ifdef ANDROID + rx += buf->len; +#endif + // Construct packet + brealloc(buf, buf->len + 3, buf_size); + memmove(buf->array + 3, buf->array, buf->len); + memset(buf->array, 0, 3); + buf->len += 3; +#endif + +#endif + +#ifdef MODULE_REMOTE + + rx += buf->len; + + char addr_header_buf[512]; + char *addr_header = remote_ctx->addr_header; + int addr_header_len = remote_ctx->addr_header_len; + + if (remote_ctx->af == AF_INET || remote_ctx->af == AF_INET6) { + addr_header_len = construct_udprealy_header(&src_addr, addr_header_buf); + addr_header = addr_header_buf; + } + + // Construct packet + brealloc(buf, buf->len + addr_header_len, buf_size); + memmove(buf->array + addr_header_len, buf->array, buf->len); + memcpy(buf->array, addr_header, addr_header_len); + buf->len += addr_header_len; + + int err = ss_encrypt_all(buf, server_ctx->method, 0, buf_size); + if (err) { + // drop the packet silently + goto CLEAN_UP; + } + +#endif + + if (buf->len > packet_size) { + LOGE("[udp] remote_recv_sendto fragmentation"); + goto CLEAN_UP; + } + + size_t remote_src_addr_len = get_sockaddr_len((struct sockaddr *)&remote_ctx->src_addr); + +#ifdef MODULE_REDIR + + size_t remote_dst_addr_len = get_sockaddr_len((struct sockaddr *)&dst_addr); + + int src_fd = socket(remote_ctx->src_addr.ss_family, SOCK_DGRAM, 0); + if (src_fd < 0) { + ERROR("[udp] remote_recv_socket"); + goto CLEAN_UP; + } + int opt = 1; + if (setsockopt(src_fd, SOL_IP, IP_TRANSPARENT, &opt, sizeof(opt))) { + ERROR("[udp] remote_recv_setsockopt"); + close(src_fd); + goto CLEAN_UP; + } + if (setsockopt(src_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { + ERROR("[udp] remote_recv_setsockopt"); + close(src_fd); + goto CLEAN_UP; + } +#ifdef IP_TOS + // Set QoS flag + int tos = 46; + setsockopt(src_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); +#endif + if (bind(src_fd, (struct sockaddr *)&dst_addr, remote_dst_addr_len) != 0) { + ERROR("[udp] remote_recv_bind"); + close(src_fd); + goto CLEAN_UP; + } + + int s = sendto(src_fd, buf->array, buf->len, 0, + (struct sockaddr *)&remote_ctx->src_addr, remote_src_addr_len); + if (s == -1) { + ERROR("[udp] remote_recv_sendto"); + close(src_fd); + goto CLEAN_UP; + } + close(src_fd); + +#else + + int s = sendto(server_ctx->fd, buf->array, buf->len, 0, + (struct sockaddr *)&remote_ctx->src_addr, remote_src_addr_len); + if (s == -1) { + ERROR("[udp] remote_recv_sendto"); + goto CLEAN_UP; + } + +#endif + + // handle the UDP packet successfully, + // triger the timer + ev_timer_again(EV_A_ & remote_ctx->watcher); + +CLEAN_UP: + + bfree(buf); + ss_free(buf); +} + +static void +server_recv_cb(EV_P_ ev_io *w, int revents) +{ + server_ctx_t *server_ctx = (server_ctx_t *)w; + struct sockaddr_storage src_addr; + memset(&src_addr, 0, sizeof(struct sockaddr_storage)); + + buffer_t *buf = ss_malloc(sizeof(buffer_t)); + balloc(buf, buf_size); + + socklen_t src_addr_len = sizeof(struct sockaddr_storage); + unsigned int offset = 0; + +#ifdef MODULE_REDIR + char control_buffer[64] = { 0 }; + struct msghdr msg; + memset(&msg, 0, sizeof(struct msghdr)); + struct iovec iov[1]; + struct sockaddr_storage dst_addr; + memset(&dst_addr, 0, sizeof(struct sockaddr_storage)); + + msg.msg_name = &src_addr; + msg.msg_namelen = src_addr_len; + msg.msg_control = control_buffer; + msg.msg_controllen = sizeof(control_buffer); + + iov[0].iov_base = buf->array; + iov[0].iov_len = buf_size; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + buf->len = recvmsg(server_ctx->fd, &msg, 0); + if (buf->len == -1) { + ERROR("[udp] server_recvmsg"); + goto CLEAN_UP; + } else if (buf->len > packet_size) { + ERROR("[udp] UDP server_recv_recvmsg fragmentation"); + goto CLEAN_UP; + } + + if (get_dstaddr(&msg, &dst_addr)) { + LOGE("[udp] unable to get dest addr"); + goto CLEAN_UP; + } + + src_addr_len = msg.msg_namelen; +#else + ssize_t r; + r = recvfrom(server_ctx->fd, buf->array, buf_size, + 0, (struct sockaddr *)&src_addr, &src_addr_len); + + if (r == -1) { + // error on recv + // simply drop that packet + ERROR("[udp] server_recv_recvfrom"); + goto CLEAN_UP; + } else if (r > packet_size) { + ERROR("[udp] server_recv_recvfrom fragmentation"); + goto CLEAN_UP; + } + + buf->len = r; +#endif + +#ifdef MODULE_REMOTE + tx += buf->len; + + int err = ss_decrypt_all(buf, server_ctx->method, server_ctx->auth, buf_size); + if (err) { + // drop the packet silently + goto CLEAN_UP; + } +#endif + +#ifdef MODULE_LOCAL +#if !defined(MODULE_TUNNEL) && !defined(MODULE_REDIR) +#ifdef ANDROID + tx += buf->len; +#endif + uint8_t frag = *(uint8_t *)(buf->array + 2); + offset += 3; +#endif +#endif + + /* + * + * SOCKS5 UDP Request + * +----+------+------+----------+----------+----------+ + * |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | + * +----+------+------+----------+----------+----------+ + * | 2 | 1 | 1 | Variable | 2 | Variable | + * +----+------+------+----------+----------+----------+ + * + * SOCKS5 UDP Response + * +----+------+------+----------+----------+----------+ + * |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | + * +----+------+------+----------+----------+----------+ + * | 2 | 1 | 1 | Variable | 2 | Variable | + * +----+------+------+----------+----------+----------+ + * + * shadowsocks UDP Request (before encrypted) + * +------+----------+----------+----------+-------------+ + * | ATYP | DST.ADDR | DST.PORT | DATA | HMAC-SHA1 | + * +------+----------+----------+----------+-------------+ + * | 1 | Variable | 2 | Variable | 10 | + * +------+----------+----------+----------+-------------+ + * + * If ATYP & ONETIMEAUTH_FLAG(0x10) != 0, Authentication (HMAC-SHA1) is enabled. + * + * The key of HMAC-SHA1 is (IV + KEY) and the input is the whole packet. + * The output of HMAC-SHA is truncated to 10 bytes (leftmost bits). + * + * shadowsocks UDP Response (before encrypted) + * +------+----------+----------+----------+ + * | ATYP | DST.ADDR | DST.PORT | DATA | + * +------+----------+----------+----------+ + * | 1 | Variable | 2 | Variable | + * +------+----------+----------+----------+ + * + * shadowsocks UDP Request and Response (after encrypted) + * +-------+--------------+ + * | IV | PAYLOAD | + * +-------+--------------+ + * | Fixed | Variable | + * +-------+--------------+ + * + */ + +#ifdef MODULE_REDIR + if (verbose) { + char src[SS_ADDRSTRLEN]; + char dst[SS_ADDRSTRLEN]; + strcpy(src, get_addr_str((struct sockaddr *)&src_addr)); + strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr)); + LOGI("[udp] redir to %s from %s", dst, src); + } + + char addr_header[512] = { 0 }; + int addr_header_len = construct_udprealy_header(&dst_addr, addr_header); + + if (addr_header_len == 0) { + LOGE("[udp] failed to parse tproxy addr"); + goto CLEAN_UP; + } + + // reconstruct the buffer + brealloc(buf, buf->len + addr_header_len, buf_size); + memmove(buf->array + addr_header_len, buf->array, buf->len); + memcpy(buf->array, addr_header, addr_header_len); + buf->len += addr_header_len; + +#elif MODULE_TUNNEL + + char addr_header[512] = { 0 }; + char *host = server_ctx->tunnel_addr.host; + char *port = server_ctx->tunnel_addr.port; + uint16_t port_num = (uint16_t)atoi(port); + uint16_t port_net_num = htons(port_num); + int addr_header_len = 0; + + struct cork_ip ip; + if (cork_ip_init(&ip, host) != -1) { + if (ip.version == 4) { + // send as IPv4 + struct in_addr host_addr; + memset(&host_addr, 0, sizeof(struct in_addr)); + int host_len = sizeof(struct in_addr); + + if (dns_pton(AF_INET, host, &host_addr) == -1) { + FATAL("IP parser error"); + } + addr_header[addr_header_len++] = 1; + memcpy(addr_header + addr_header_len, &host_addr, host_len); + addr_header_len += host_len; + } else if (ip.version == 6) { + // send as IPv6 + struct in6_addr host_addr; + memset(&host_addr, 0, sizeof(struct in6_addr)); + int host_len = sizeof(struct in6_addr); + + if (dns_pton(AF_INET6, host, &host_addr) == -1) { + FATAL("IP parser error"); + } + addr_header[addr_header_len++] = 4; + memcpy(addr_header + addr_header_len, &host_addr, host_len); + addr_header_len += host_len; + } else { + FATAL("IP parser error"); + } + } else { + // send as domain + int host_len = strlen(host); + + addr_header[addr_header_len++] = 3; + addr_header[addr_header_len++] = host_len; + memcpy(addr_header + addr_header_len, host, host_len); + addr_header_len += host_len; + } + memcpy(addr_header + addr_header_len, &port_net_num, 2); + addr_header_len += 2; + + // reconstruct the buffer + brealloc(buf, buf->len + addr_header_len, buf_size); + memmove(buf->array + addr_header_len, buf->array, buf->len); + memcpy(buf->array, addr_header, addr_header_len); + buf->len += addr_header_len; + +#else + + char host[257] = { 0 }; + char port[64] = { 0 }; + struct sockaddr_storage dst_addr; + memset(&dst_addr, 0, sizeof(struct sockaddr_storage)); + + int addr_header_len = parse_udprealy_header(buf->array + offset, buf->len - offset, + host, port, &dst_addr); + if (addr_header_len == 0) { + // error in parse header + goto CLEAN_UP; + } + + char *addr_header = buf->array + offset; +#endif + +#ifdef MODULE_LOCAL + char *key = hash_key(server_ctx->remote_addr->sa_family, &src_addr); +#else + char *key = hash_key(dst_addr.ss_family, &src_addr); +#endif + + struct cache *conn_cache = server_ctx->conn_cache; + + remote_ctx_t *remote_ctx = NULL; + cache_lookup(conn_cache, key, HASH_KEY_LEN, (void *)&remote_ctx); + + if (remote_ctx != NULL) { + if (sockaddr_cmp(&src_addr, &remote_ctx->src_addr, sizeof(src_addr))) { + remote_ctx = NULL; + } + } + + // reset the timer + if (remote_ctx != NULL) { + ev_timer_again(EV_A_ & remote_ctx->watcher); + } + + if (remote_ctx == NULL) { + if (verbose) { +#ifdef MODULE_REDIR + char src[SS_ADDRSTRLEN]; + char dst[SS_ADDRSTRLEN]; + strcpy(src, get_addr_str((struct sockaddr *)&src_addr)); + strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr)); + LOGI("[udp] cache miss: %s <-> %s", dst, src); +#else + LOGI("[udp] cache miss: %s:%s <-> %s", host, port, + get_addr_str((struct sockaddr *)&src_addr)); +#endif + } + } else { + if (verbose) { +#ifdef MODULE_REDIR + char src[SS_ADDRSTRLEN]; + char dst[SS_ADDRSTRLEN]; + strcpy(src, get_addr_str((struct sockaddr *)&src_addr)); + strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr)); + LOGI("[udp] cache hit: %s <-> %s", dst, src); +#else + LOGI("[udp] cache hit: %s:%s <-> %s", host, port, + get_addr_str((struct sockaddr *)&src_addr)); +#endif + } + } + +#ifdef MODULE_LOCAL + +#if !defined(MODULE_TUNNEL) && !defined(MODULE_REDIR) + if (frag) { + LOGE("[udp] drop a message since frag is not 0, but %d", frag); + goto CLEAN_UP; + } +#endif + + const struct sockaddr *remote_addr = server_ctx->remote_addr; + const int remote_addr_len = server_ctx->remote_addr_len; + + if (remote_ctx == NULL) { + // Bind to any port + int remotefd = create_remote_socket(remote_addr->sa_family == AF_INET6); + if (remotefd < 0) { + ERROR("[udp] udprelay bind() error"); + goto CLEAN_UP; + } + setnonblocking(remotefd); + +#ifdef SO_NOSIGPIPE + set_nosigpipe(remotefd); +#endif +#ifdef IP_TOS + // Set QoS flag + int tos = 46; + setsockopt(remotefd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); +#endif +#ifdef SET_INTERFACE + if (server_ctx->iface) { + if (setinterface(remotefd, server_ctx->iface) == -1) + ERROR("setinterface"); + } +#endif + +#ifdef ANDROID + if (vpn) { + if (protect_socket(remotefd) == -1) { + ERROR("protect_socket"); + close(remotefd); + goto CLEAN_UP; + } + } +#endif + + // Init remote_ctx + remote_ctx = new_remote(remotefd, server_ctx); + remote_ctx->src_addr = src_addr; + remote_ctx->af = remote_addr->sa_family; + remote_ctx->addr_header_len = addr_header_len; + memcpy(remote_ctx->addr_header, addr_header, addr_header_len); + + // Add to conn cache + cache_insert(conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); + + // Start remote io + ev_io_start(EV_A_ & remote_ctx->io); + ev_timer_start(EV_A_ & remote_ctx->watcher); + } + + if (offset > 0) { + buf->len -= offset; + memmove(buf->array, buf->array + offset, buf->len); + } + + if (server_ctx->auth) { + buf->array[0] |= ONETIMEAUTH_FLAG; + } + + // SSR beg + if (server_ctx->protocol_plugin) { + obfs_class *protocol_plugin = server_ctx->protocol_plugin; + if (protocol_plugin->client_udp_pre_encrypt) { + buf->len = protocol_plugin->client_udp_pre_encrypt(server_ctx->protocol, &buf->array, buf->len, &buf->capacity); + } + } + //SSR end + + int err = ss_encrypt_all(buf, server_ctx->method, server_ctx->auth, buf->len); + + if (err) { + // drop the packet silently + goto CLEAN_UP; + } + + if (buf->len > packet_size) { + LOGE("[udp] server_recv_sendto fragmentation"); + goto CLEAN_UP; + } + + int s = sendto(remote_ctx->fd, buf->array, buf->len, 0, remote_addr, remote_addr_len); + + if (s == -1) { + ERROR("[udp] server_recv_sendto"); + } + +#else + + int cache_hit = 0; + int need_query = 0; + + if (buf->len - addr_header_len > packet_size) { + LOGE("[udp] server_recv_sendto fragmentation"); + goto CLEAN_UP; + } + + if (remote_ctx != NULL) { + cache_hit = 1; + // detect destination mismatch + if (remote_ctx->addr_header_len != addr_header_len + || memcmp(addr_header, remote_ctx->addr_header, addr_header_len) != 0) { + if (dst_addr.ss_family != AF_INET && dst_addr.ss_family != AF_INET6) { + need_query = 1; + } + } else { + memcpy(&dst_addr, &remote_ctx->dst_addr, sizeof(struct sockaddr_storage)); + } + } else { + if (dst_addr.ss_family == AF_INET || dst_addr.ss_family == AF_INET6) { + int remotefd = create_remote_socket(dst_addr.ss_family == AF_INET6); + if (remotefd != -1) { + setnonblocking(remotefd); +#ifdef SO_BROADCAST + set_broadcast(remotefd); +#endif +#ifdef SO_NOSIGPIPE + set_nosigpipe(remotefd); +#endif +#ifdef IP_TOS + // Set QoS flag + int tos = 46; + setsockopt(remotefd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); +#endif +#ifdef SET_INTERFACE + if (server_ctx->iface) { + if (setinterface(remotefd, server_ctx->iface) == -1) + ERROR("setinterface"); + } +#endif + remote_ctx = new_remote(remotefd, server_ctx); + remote_ctx->src_addr = src_addr; + remote_ctx->server_ctx = server_ctx; + remote_ctx->addr_header_len = addr_header_len; + memcpy(remote_ctx->addr_header, addr_header, addr_header_len); + memcpy(&remote_ctx->dst_addr, &dst_addr, sizeof(struct sockaddr_storage)); + } else { + ERROR("[udp] bind() error"); + goto CLEAN_UP; + } + } + } + + if (remote_ctx != NULL && !need_query) { + size_t addr_len = get_sockaddr_len((struct sockaddr *)&dst_addr); + int s = sendto(remote_ctx->fd, buf->array + addr_header_len, + buf->len - addr_header_len, 0, + (struct sockaddr *)&dst_addr, addr_len); + + if (s == -1) { + ERROR("[udp] sendto_remote"); + if (!cache_hit) { + close_and_free_remote(EV_A_ remote_ctx); + } + } else { + if (!cache_hit) { + // Add to conn cache + remote_ctx->af = dst_addr.ss_family; + char *key = hash_key(remote_ctx->af, &remote_ctx->src_addr); + cache_insert(server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); + + ev_io_start(EV_A_ & remote_ctx->io); + ev_timer_start(EV_A_ & remote_ctx->watcher); + } + } + } else { + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + + struct query_ctx *query_ctx = new_query_ctx(buf->array + addr_header_len, + buf->len - addr_header_len); + query_ctx->server_ctx = server_ctx; + query_ctx->addr_header_len = addr_header_len; + query_ctx->src_addr = src_addr; + memcpy(query_ctx->addr_header, addr_header, addr_header_len); + + if (need_query) { + query_ctx->remote_ctx = remote_ctx; + } + + struct ResolvQuery *query = resolv_query(host, query_resolve_cb, + NULL, query_ctx, htons(atoi(port))); + if (query == NULL) { + ERROR("[udp] unable to create DNS query"); + close_and_free_query(EV_A_ query_ctx); + goto CLEAN_UP; + } + query_ctx->query = query; + } +#endif + +CLEAN_UP: + bfree(buf); + ss_free(buf); +} + +void +free_cb(void *key, void *element) +{ + remote_ctx_t *remote_ctx = (remote_ctx_t *)element; + + if (verbose) { + LOGI("[udp] one connection freed"); + } + + close_and_free_remote(EV_DEFAULT, remote_ctx); +} + +int +init_udprelay(const char *server_host, const char *server_port, +#ifdef MODULE_LOCAL + const struct sockaddr *remote_addr, const int remote_addr_len, +#ifdef MODULE_TUNNEL + const ss_addr_t tunnel_addr, +#endif +#endif + int mtu, int method, int auth, int timeout, const char *iface, const char *protocol, const char *protocol_param) +{ + // Initialize ev loop + struct ev_loop *loop = EV_DEFAULT; + + // Initialize MTU + if (mtu > 0) { + packet_size = mtu - 1 - 28 - 2 - 64; + buf_size = packet_size * 2; + } + + // Initialize cache + struct cache *conn_cache; + cache_create(&conn_cache, MAX_UDP_CONN_NUM, free_cb); + + // //////////////////////////////////////////////// + // Setup server context + + // Bind to port + int serverfd = create_server_socket(server_host, server_port); + if (serverfd < 0) { + FATAL("[udp] bind() error"); + } + setnonblocking(serverfd); + if (protocol != NULL && strcmp(protocol, "verify_sha1") == 0) { + auth = 1; + protocol = NULL; + } + + server_ctx_t *server_ctx = new_server_ctx(serverfd); +#ifdef MODULE_REMOTE + server_ctx->loop = loop; +#endif + server_ctx->auth = auth; + server_ctx->timeout = max(timeout, MIN_UDP_TIMEOUT); + server_ctx->method = method; + server_ctx->iface = iface; + server_ctx->conn_cache = conn_cache; +#ifdef MODULE_LOCAL + server_ctx->remote_addr = remote_addr; + server_ctx->remote_addr_len = remote_addr_len; + //SSR beg + server_ctx->protocol_plugin = new_obfs_class((char *)protocol); + if (server_ctx->protocol_plugin) { + server_ctx->protocol = server_ctx->protocol_plugin->new_obfs(); + server_ctx->protocol_global = server_ctx->protocol_plugin->init_data(); + } + + server_info _server_info; + memset(&_server_info, 0, sizeof(server_info)); + strcpy(_server_info.host, inet_ntoa(((struct sockaddr_in*)remote_addr)->sin_addr)); + _server_info.port = ((struct sockaddr_in*)remote_addr)->sin_port; + _server_info.port = _server_info.port >> 8 | _server_info.port << 8; + _server_info.g_data = server_ctx->protocol_global; + _server_info.param = (char *)protocol_param; + _server_info.key = enc_get_key(); + _server_info.key_len = enc_get_key_len(); + + if (server_ctx->protocol_plugin) + server_ctx->protocol_plugin->set_server_info(server_ctx->protocol, &_server_info); + //SSR end +#ifdef MODULE_TUNNEL + server_ctx->tunnel_addr = tunnel_addr; +#endif +#endif + + ev_io_start(loop, &server_ctx->io); + + server_ctx_list[server_num++] = server_ctx; + + return 0; +} + +void +free_udprelay() +{ + struct ev_loop *loop = EV_DEFAULT; + while (server_num-- > 0) { + server_ctx_t *server_ctx = server_ctx_list[server_num]; + +#ifdef MODULE_LOCAL + //SSR beg + if (server_ctx->protocol_plugin) { + server_ctx->protocol_plugin->dispose(server_ctx->protocol); + server_ctx->protocol = NULL; + free_obfs_class(server_ctx->protocol_plugin); + server_ctx->protocol_plugin = NULL; + } + //SSR end +#endif + + ev_io_stop(loop, &server_ctx->io); + close(server_ctx->fd); + cache_delete(server_ctx->conn_cache, 0); + ss_free(server_ctx); + server_ctx_list[server_num] = NULL; + } +} diff --git a/server/udprelay.h b/server/udprelay.h new file mode 100644 index 0000000..89876d4 --- /dev/null +++ b/server/udprelay.h @@ -0,0 +1,95 @@ +/* + * udprelay.h - Define UDP relay's buffers and callbacks + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _UDPRELAY_H +#define _UDPRELAY_H + +#include +#include + +#include "encrypt.h" +#include "jconf.h" +#include "obfs.h" + +#ifdef MODULE_REMOTE +#include "resolv.h" +#endif + +#include "cache.h" + +#include "common.h" + +#define MAX_UDP_PACKET_SIZE (65507) + +#define DEFAULT_PACKET_SIZE 1397 // 1492 - 1 - 28 - 2 - 64 = 1397, the default MTU for UDP relay + +typedef struct server_ctx { + ev_io io; + int fd; + int method; + int auth; + int timeout; + const char *iface; + struct cache *conn_cache; +#ifdef MODULE_LOCAL + const struct sockaddr *remote_addr; + int remote_addr_len; +#ifdef MODULE_TUNNEL + ss_addr_t tunnel_addr; +#endif +#endif +#ifdef MODULE_REMOTE + struct ev_loop *loop; +#endif + // SSR + obfs *protocol; + obfs_class *protocol_plugin; + void *protocol_global; +} server_ctx_t; + +#ifdef MODULE_REMOTE +typedef struct query_ctx { + struct ResolvQuery *query; + struct sockaddr_storage src_addr; + buffer_t *buf; + int addr_header_len; + char addr_header[384]; + struct server_ctx *server_ctx; + struct remote_ctx *remote_ctx; +} query_ctx_t; +#endif + +typedef struct remote_ctx { + ev_io io; + ev_timer watcher; + int af; + int fd; + int addr_header_len; + char addr_header[384]; + struct sockaddr_storage src_addr; +#ifdef MODULE_REMOTE + struct sockaddr_storage dst_addr; +#endif + struct server_ctx *server_ctx; +} remote_ctx_t; + +#endif // _UDPRELAY_H diff --git a/server/uthash.h b/server/uthash.h new file mode 100644 index 0000000..45d1f9f --- /dev/null +++ b/server/uthash.h @@ -0,0 +1,1074 @@ +/* +Copyright (c) 2003-2016, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.0.1 + +#include /* memcmp,strlen */ +#include /* ptrdiff_t */ +#include /* exit() */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#define DECLTYPE(x) +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while (0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while (0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ +#if defined(_WIN32) +#if defined(_MSC_VER) && _MSC_VER >= 1600 +#include +#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__) +#include +#else +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#endif +#elif defined(__GNUC__) && !defined(__VXWORKS__) +#include +#else +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#endif + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#endif +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif +#ifndef uthash_memcmp +#define uthash_memcmp(a,b,n) memcmp(a,b,n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho))) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FCN(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + (head) = (add); \ + HASH_MAKE_TABLE(hh, head); \ + } else { \ + struct UT_hash_handle *_hs_iter = &(head)->hh; \ + (add)->hh.tbl = (head)->hh.tbl; \ + do { \ + if (cmpfcn(DECLTYPE(head) ELMT_FROM_HH((head)->hh.tbl, _hs_iter), add) > 0) \ + break; \ + } while ((_hs_iter = _hs_iter->next)); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = _hs_iter->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter->prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + _hs_iter->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + HASH_FSCK(hh, head); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + (head) = (add); \ + HASH_MAKE_TABLE(hh, head); \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + HASH_FSCK(hh, head); \ +} while (0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ +} while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + head = NULL; \ + } else { \ + unsigned _hd_bkt; \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev != NULL) { \ + ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ + HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count; \ + char *_prev; \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %u, actual %u\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %u, actual %u\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %u, actual %u\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,hashv) \ +do { \ + unsigned _hb_keylen=(unsigned)keylen; \ + const unsigned char *_hb_key=(const unsigned char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,hashv) \ +do { \ + unsigned _sx_i; \ + const unsigned char *_hs_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,hashv) \ +do { \ + unsigned _fn_i; \ + const unsigned char *_hf_key=(const unsigned char*)(key); \ + hashv = 2166136261U; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ +} while (0) + +#define HASH_OAT(key,keylen,hashv) \ +do { \ + unsigned _ho_i; \ + const unsigned char *_ho_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ +} while (0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,hashv) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned const char *_hj_key=(unsigned const char*)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ +} while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,hashv) \ +do { \ + unsigned const char *_sfh_key=(unsigned const char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0U; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) +#define MUR_GETBLOCK(p,i) p[i] +#else /* non intel */ +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL) +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) +#else /* assume little endian non-intel */ +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) +#endif +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ + (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ + (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ + MUR_ONE_THREE(p)))) +#endif +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define MUR_FMIX(_h) \ +do { \ + _h ^= _h >> 16; \ + _h *= 0x85ebca6bu; \ + _h ^= _h >> 13; \ + _h *= 0xc2b2ae35u; \ + _h ^= _h >> 16; \ +} while (0) + +#define HASH_MUR(key,keylen,hashv) \ +do { \ + const uint8_t *_mur_data = (const uint8_t*)(key); \ + const int _mur_nblocks = (int)(keylen) / 4; \ + uint32_t _mur_h1 = 0xf88D5353u; \ + uint32_t _mur_c1 = 0xcc9e2d51u; \ + uint32_t _mur_c2 = 0x1b873593u; \ + uint32_t _mur_k1 = 0; \ + const uint8_t *_mur_tail; \ + const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ + int _mur_i; \ + for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) { \ + _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + \ + _mur_h1 ^= _mur_k1; \ + _mur_h1 = MUR_ROTL32(_mur_h1,13); \ + _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \ + } \ + _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ + _mur_k1=0; \ + switch((keylen) & 3U) { \ + case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ + case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ + case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + _mur_h1 ^= _mur_k1; \ + } \ + _mur_h1 ^= (uint32_t)(keylen); \ + MUR_FMIX(_mur_h1); \ + hashv = _mur_h1; \ +} while (0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ +do { \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH)) \ + && ((addhh)->tbl->noexpand != 1U)) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1U)) + \ + (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev = \ + _he_thh; } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + tbl->num_buckets *= 2U; \ + tbl->log2_num_buckets++; \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1U) : 0U; \ + if (tbl->ineff_expands > 1U) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while (0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) { break; } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL){ \ + _hs_tail->next = NULL; \ + } \ + if ( _hs_nmerges <= 1U ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src != NULL) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; } \ + if (dst == NULL) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head != NULL) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)=NULL; \ + } \ +} while (0) + +#define HASH_OVERHEAD(hh,head) \ + ((head != NULL) ? ( \ + (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + \ + (HASH_BLOOM_BYTELEN))) : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/server/utils.c b/server/utils.c new file mode 100644 index 0000000..14a60c7 --- /dev/null +++ b/server/utils.c @@ -0,0 +1,448 @@ +/* + * utils.c - Misc utilities + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#ifndef __MINGW32__ +#include +#include +#endif + +#include +#include + +#include "utils.h" + +#ifdef HAVE_SETRLIMIT +#include +#include +#endif + +#define INT_DIGITS 19 /* enough for 64 bit integer */ + +#ifdef LIB_ONLY +FILE *logfile; +#endif + +#ifdef HAS_SYSLOG +int use_syslog = 0; +#endif + +#ifndef __MINGW32__ +void +ERROR(const char *s) +{ + char *msg = strerror(errno); + LOGE("%s: %s", s, msg); +} + +#endif + +int use_tty = 1; + +char * +ss_itoa(int i) +{ + /* Room for INT_DIGITS digits, - and '\0' */ + static char buf[INT_DIGITS + 2]; + char *p = buf + INT_DIGITS + 1; /* points to terminating '\0' */ + if (i >= 0) { + do { + *--p = '0' + (i % 10); + i /= 10; + } while (i != 0); + return p; + } else { /* i < 0 */ + do { + *--p = '0' - (i % 10); + i /= 10; + } while (i != 0); + *--p = '-'; + } + return p; +} + +int +ss_isnumeric(const char *s) { + if (!s || !*s) + return 0; + while (isdigit(*s)) + ++s; + return *s == '\0'; +} + +/* + * setuid() and setgid() for a specified user. + */ +int +run_as(const char *user) +{ +#ifndef __MINGW32__ + if (user[0]) { + /* Convert user to a long integer if it is a non-negative number. + * -1 means it is a user name. */ + long uid = -1; + if (ss_isnumeric(user)) { + errno = 0; + char *endptr; + uid = strtol(user, &endptr, 10); + if (errno || endptr == user) + uid = -1; + } + +#ifdef HAVE_GETPWNAM_R + struct passwd pwdbuf, *pwd; + memset(&pwdbuf, 0, sizeof(struct passwd)); + size_t buflen; + int err; + + for (buflen = 128;; buflen *= 2) { + char buf[buflen]; /* variable length array */ + + /* Note that we use getpwnam_r() instead of getpwnam(), + * which returns its result in a statically allocated buffer and + * cannot be considered thread safe. */ + err = uid >= 0 ? getpwuid_r((uid_t)uid, &pwdbuf, buf, buflen, &pwd) + : getpwnam_r(user, &pwdbuf, buf, buflen, &pwd); + + if (err == 0 && pwd) { + /* setgid first, because we may not be allowed to do it anymore after setuid */ + if (setgid(pwd->pw_gid) != 0) { + LOGE( + "Could not change group id to that of run_as user '%s': %s", + pwd->pw_name, strerror(errno)); + return 0; + } + + if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { + LOGE("Could not change supplementary groups for user '%s'.", pwd->pw_name); + return 0; + } + + if (setuid(pwd->pw_uid) != 0) { + LOGE( + "Could not change user id to that of run_as user '%s': %s", + pwd->pw_name, strerror(errno)); + return 0; + } + break; + } else if (err != ERANGE) { + if (err) { + LOGE("run_as user '%s' could not be found: %s", user, + strerror(err)); + } else { + LOGE("run_as user '%s' could not be found.", user); + } + return 0; + } else if (buflen >= 16 * 1024) { + /* If getpwnam_r() seems defective, call it quits rather than + * keep on allocating ever larger buffers until we crash. */ + LOGE( + "getpwnam_r() requires more than %u bytes of buffer space.", + (unsigned)buflen); + return 0; + } + /* Else try again with larger buffer. */ + } +#else + /* No getpwnam_r() :-( We'll use getpwnam() and hope for the best. */ + struct passwd *pwd; + + if (!(pwd = uid >=0 ? getpwuid((uid_t)uid) : getpwnam(user))) { + LOGE("run_as user %s could not be found.", user); + return 0; + } + /* setgid first, because we may not allowed to do it anymore after setuid */ + if (setgid(pwd->pw_gid) != 0) { + LOGE("Could not change group id to that of run_as user '%s': %s", + pwd->pw_name, strerror(errno)); + return 0; + } + if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { + LOGE("Could not change supplementary groups for user '%s'.", pwd->pw_name); + return 0; + } + if (setuid(pwd->pw_uid) != 0) { + LOGE("Could not change user id to that of run_as user '%s': %s", + pwd->pw_name, strerror(errno)); + return 0; + } +#endif + } + +#endif // __MINGW32__ + return 1; +} + +char * +ss_strndup(const char *s, size_t n) +{ + size_t len = strlen(s); + char *ret; + + if (len <= n) { + return strdup(s); + } + + ret = ss_malloc(n + 1); + strncpy(ret, s, n); + ret[n] = '\0'; + return ret; +} + +void +FATAL(const char *msg) +{ + LOGE("%s", msg); + exit(-1); +} + +void * +ss_malloc(size_t size) +{ + void *tmp = malloc(size); + if (tmp == NULL) + exit(EXIT_FAILURE); + return tmp; +} + +void * +ss_realloc(void *ptr, size_t new_size) +{ + void *new = realloc(ptr, new_size); + if (new == NULL) { + free(ptr); + ptr = NULL; + exit(EXIT_FAILURE); + } + return new; +} + +void +usage() +{ + printf("\n"); + printf("shadowsocks-libev %s with %s\n\n", VERSION, USING_CRYPTO); + printf( + " maintained by Max Lv and Linus Yang \n\n"); + printf(" usage:\n\n"); +#ifdef MODULE_LOCAL + printf(" ss-local\n"); +#elif MODULE_REMOTE + printf(" ss-server\n"); +#elif MODULE_TUNNEL + printf(" ss-tunnel\n"); +#elif MODULE_REDIR + printf(" ss-redir\n"); +#elif MODULE_MANAGER + printf(" ss-manager\n"); +#endif + printf("\n"); + printf( + " -s Host name or IP address of your remote server.\n"); + printf( + " -p Port number of your remote server.\n"); + printf( + " -l Port number of your local server.\n"); + printf( + " -k Password of your remote server.\n"); + printf( + " -m Encrypt method: table, rc4, rc4-md5,\n"); + printf( + " aes-128-cfb, aes-192-cfb, aes-256-cfb,\n"); + printf( + " aes-128-ctr, aes-192-ctr, aes-256-ctr,\n"); + printf( + " bf-cfb, camellia-128-cfb, camellia-192-cfb,\n"); + printf( + " camellia-256-cfb, cast5-cfb, des-cfb,\n"); + printf( + " idea-cfb, rc2-cfb, seed-cfb, salsa20,\n"); + printf( + " chacha20 and chacha20-ietf.\n"); + printf( + " The default cipher is rc4-md5.\n"); + printf("\n"); + printf( + " [-a ] Run as another user.\n"); + printf( + " [-f ] The file path to store pid.\n"); + printf( + " [-t ] Socket timeout in seconds.\n"); + printf( + " [-c ] The path to config file.\n"); +#ifdef HAVE_SETRLIMIT + printf( + " [-n ] Max number of open files.\n"); +#endif +#ifndef MODULE_REDIR + printf( + " [-i ] Network interface to bind.\n"); +#endif + printf( + " [-b ] Local address to bind.\n"); + printf("\n"); + printf( + " [-u] Enable UDP relay.\n"); +#ifdef MODULE_REDIR + printf( + " TPROXY is required in redir mode.\n"); +#endif + printf( + " [-U] Enable UDP relay and disable TCP relay.\n"); + printf( + " [-A] Enable onetime authentication.\n"); +#ifdef MODULE_REMOTE + printf( + " [-6] Resovle hostname to IPv6 address first.\n"); +#endif + printf("\n"); +#ifdef MODULE_TUNNEL + printf( + " [-L :] Destination server address and port\n"); + printf( + " for local port forwarding.\n"); +#endif +#ifdef MODULE_REMOTE + printf( + " [-d ] Name servers for internal DNS resolver.\n"); +#endif +#if defined(MODULE_REMOTE) || defined(MODULE_LOCAL) + printf( + " [--fast-open] Enable TCP fast open.\n"); + printf( + " with Linux kernel > 3.7.0.\n"); + printf( + " [--acl ] Path to ACL (Access Control List).\n"); +#endif +#if defined(MODULE_REMOTE) || defined(MODULE_MANAGER) + printf( + " [--manager-address ] UNIX domain socket address.\n"); +#endif +#ifdef MODULE_MANAGER + printf( + " [--executable ] Path to the executable of ss-server.\n"); +#endif + printf( + " [--mtu ] MTU of your network interface.\n"); +#ifdef __linux__ + printf( + " [--mptcp] Enable Multipath TCP on MPTCP Kernel.\n"); +#ifdef MODULE_REMOTE + printf( + " [--firewall] Setup firewall rules for auto blocking.\n"); +#endif +#endif + printf("\n"); + printf( + " [-v] Verbose mode.\n"); + printf( + " [-h, --help] Print this message.\n"); + printf("\n"); +} + +void +daemonize(const char *path) +{ +#ifndef __MINGW32__ + /* Our process ID and Session ID */ + pid_t pid, sid; + + /* Fork off the parent process */ + pid = fork(); + if (pid < 0) { + exit(EXIT_FAILURE); + } + + /* If we got a good PID, then + * we can exit the parent process. */ + if (pid > 0) { + FILE *file = fopen(path, "w"); + if (file == NULL) { + FATAL("Invalid pid file\n"); + } + + fprintf(file, "%d", (int)pid); + fclose(file); + exit(EXIT_SUCCESS); + } + + /* Change the file mode mask */ + umask(0); + + /* Open any logs here */ + + /* Create a new SID for the child process */ + sid = setsid(); + if (sid < 0) { + /* Log the failure */ + exit(EXIT_FAILURE); + } + + /* Change the current working directory */ + if ((chdir("/")) < 0) { + /* Log the failure */ + exit(EXIT_FAILURE); + } + + /* Close out the standard file descriptors */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); +#endif +} + +#ifdef HAVE_SETRLIMIT +int +set_nofile(int nofile) +{ + struct rlimit limit = { nofile, nofile }; /* set both soft and hard limit */ + + if (nofile <= 0) { + FATAL("nofile must be greater than 0\n"); + } + + if (setrlimit(RLIMIT_NOFILE, &limit) < 0) { + if (errno == EPERM) { + LOGE( + "insufficient permission to change NOFILE, not starting as root?"); + return -1; + } else if (errno == EINVAL) { + LOGE("invalid nofile, decrease nofile and try again"); + return -1; + } else { + LOGE("setrlimit failed: %s", strerror(errno)); + return -1; + } + } + + return 0; +} + +#endif diff --git a/server/utils.h b/server/utils.h new file mode 100644 index 0000000..0fb7f5a --- /dev/null +++ b/server/utils.h @@ -0,0 +1,232 @@ +/* + * utils.h - Misc utilities + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#if defined(USE_CRYPTO_OPENSSL) + +#include +#define USING_CRYPTO OPENSSL_VERSION_TEXT + +#elif defined(USE_CRYPTO_POLARSSL) +#include +#define USING_CRYPTO POLARSSL_VERSION_STRING_FULL + +#elif defined(USE_CRYPTO_MBEDTLS) +#include +#define USING_CRYPTO MBEDTLS_VERSION_STRING_FULL + +#endif + +#ifndef _UTILS_H +#define _UTILS_H + +#include +#include +#include +#include + +#define PORTSTRLEN 16 +#define SS_ADDRSTRLEN (INET6_ADDRSTRLEN + PORTSTRLEN + 1) + +#ifdef ANDROID + +#include + +#define USE_TTY() +#define USE_SYSLOG(ident) +#define LOGI(...) \ + ((void)__android_log_print(ANDROID_LOG_DEBUG, "shadowsocks", \ + __VA_ARGS__)) +#define LOGE(...) \ + ((void)__android_log_print(ANDROID_LOG_ERROR, "shadowsocks", \ + __VA_ARGS__)) + +#else + +#define STR(x) # x +#define TOSTR(x) STR(x) + +#ifdef LIB_ONLY + +extern FILE *logfile; + +#define TIME_FORMAT "%Y-%m-%d %H:%M:%S" + +#define USE_TTY() + +#define USE_SYSLOG(ident) + +#define USE_LOGFILE(ident) \ + do { \ + if (ident != NULL) { logfile = fopen(ident, "w+"); } } \ + while (0) + +#define CLOSE_LOGFILE \ + do { \ + if (logfile != NULL) { fclose(logfile); } } \ + while (0) + +#define LOGI(format, ...) \ + do { \ + if (logfile != NULL) { \ + time_t now = time(NULL); \ + char timestr[20]; \ + strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ + fprintf(logfile, " %s INFO: " format "\n", timestr, ## __VA_ARGS__); \ + fflush(logfile); } \ + } \ + while (0) + +#define LOGE(format, ...) \ + do { \ + if (logfile != NULL) { \ + time_t now = time(NULL); \ + char timestr[20]; \ + strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ + fprintf(logfile, " %s ERROR: " format "\n", timestr, \ + ## __VA_ARGS__); \ + fflush(logfile); } \ + } \ + while (0) + +#elif defined(_WIN32) + +#define TIME_FORMAT "%Y-%m-%d %H:%M:%S" + +#define USE_TTY() + +#define USE_SYSLOG(ident) + +#define LOGI(format, ...) \ + do { \ + time_t now = time(NULL); \ + char timestr[20]; \ + strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ + fprintf(stderr, " %s INFO: " format "\n", timestr, ## __VA_ARGS__); \ + fflush(stderr); } \ + while (0) + +#define LOGE(format, ...) \ + do { \ + time_t now = time(NULL); \ + char timestr[20]; \ + strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ + fprintf(stderr, " %s ERROR: " format "\n", timestr, ## __VA_ARGS__); \ + fflush(stderr); } \ + while (0) + +#else + +#include + +extern int use_tty; +#define USE_TTY() \ + do { \ + use_tty = isatty(STDERR_FILENO); \ + } while (0) \ + +#define HAS_SYSLOG +extern int use_syslog; + +#define TIME_FORMAT "%F %T" + +#define USE_SYSLOG(ident) \ + do { \ + use_syslog = 1; \ + openlog((ident), LOG_CONS | LOG_PID, 0); } \ + while (0) + +#define LOGI(format, ...) \ + do { \ + if (use_syslog) { \ + syslog(LOG_INFO, format, ## __VA_ARGS__); \ + } else { \ + time_t now = time(NULL); \ + char timestr[20]; \ + strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ + if (use_tty) { \ + fprintf(stderr, "\e[01;32m %s INFO: \e[0m" format "\n", timestr, \ + ## __VA_ARGS__); \ + } else { \ + fprintf(stderr, " %s INFO: " format "\n", timestr, \ + ## __VA_ARGS__); \ + } \ + } \ + } \ + while (0) + +#define LOGE(format, ...) \ + do { \ + if (use_syslog) { \ + syslog(LOG_ERR, format, ## __VA_ARGS__); \ + } else { \ + time_t now = time(NULL); \ + char timestr[20]; \ + strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ + if (use_tty) { \ + fprintf(stderr, "\e[01;35m %s ERROR: \e[0m" format "\n", timestr, \ + ## __VA_ARGS__); \ + } else { \ + fprintf(stderr, " %s ERROR: " format "\n", timestr, \ + ## __VA_ARGS__); \ + } \ + } } \ + while (0) + +#endif +/* _WIN32 */ + +#endif + +#ifdef __MINGW32__ + +#ifdef ERROR +#undef ERROR +#endif +#define ERROR(s) ss_error(s) + +#else + +void ERROR(const char *s); + +#endif + +char *ss_itoa(int i); +int ss_isnumeric(const char *s); +int run_as(const char *user); +void FATAL(const char *msg); +void usage(void); +void daemonize(const char *path); +char *ss_strndup(const char *s, size_t n); +#ifdef HAVE_SETRLIMIT +int set_nofile(int nofile); +#endif + +void *ss_malloc(size_t size); +void *ss_realloc(void *ptr, size_t new_size); + +#define ss_free(ptr) \ + do { \ + free(ptr); \ + ptr = NULL; \ + } while (0) + +#endif // _UTILS_H diff --git a/server/verify.c b/server/verify.c new file mode 100644 index 0000000..9e7393d --- /dev/null +++ b/server/verify.c @@ -0,0 +1,188 @@ + +#include "verify.h" + +static int verify_simple_pack_unit_size = 2000; + +typedef struct verify_simple_local_data { + char * recv_buffer; + int recv_buffer_size; +}verify_simple_local_data; + +void verify_simple_local_data_init(verify_simple_local_data* local) { + local->recv_buffer = (char*)malloc(16384); + local->recv_buffer_size = 0; +} + +obfs * verify_simple_new_obfs() { + obfs * self = new_obfs(); + self->l_data = malloc(sizeof(verify_simple_local_data)); + verify_simple_local_data_init((verify_simple_local_data*)self->l_data); + return self; +} + +void verify_simple_dispose(obfs *self) { + verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; + if (local->recv_buffer != NULL) { + free(local->recv_buffer); + local->recv_buffer = NULL; + } + free(local); + self->l_data = NULL; + dispose_obfs(self); +} + +int verify_simple_pack_data(char *data, int datalength, char *outdata) { + unsigned char rand_len = (xorshift128plus() & 0xF) + 1; + int out_size = rand_len + datalength + 6; + outdata[0] = out_size >> 8; + outdata[1] = out_size; + outdata[2] = rand_len; + memmove(outdata + rand_len + 2, data, datalength); + fillcrc32((unsigned char *)outdata, out_size); + return out_size; +} + +int verify_simple_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t *capacity) { + char *plaindata = *pplaindata; + //verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 32); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + while ( len > verify_simple_pack_unit_size ) { + pack_len = verify_simple_pack_data(data, verify_simple_pack_unit_size, buffer); + buffer += pack_len; + data += verify_simple_pack_unit_size; + len -= verify_simple_pack_unit_size; + } + if (len > 0) { + pack_len = verify_simple_pack_data(data, len, buffer); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int verify_simple_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t *capacity) { + char *plaindata = *pplaindata; + verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + return -1; + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + while (local->recv_buffer_size > 2) { + int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; + if (length >= 8192 || length < 7) { + free(out_buffer); + local->recv_buffer_size = 0; + return -1; + } + if (length > local->recv_buffer_size) + break; + + int crc = crc32((unsigned char*)recv_buffer, length); + if (crc != -1) { + free(out_buffer); + local->recv_buffer_size = 0; + return -1; + } + int data_size = length - recv_buffer[2] - 6; + memmove(buffer, recv_buffer + 2 + recv_buffer[2], data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int verify_simple_server_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t *capacity) { + char *plaindata = *pplaindata; + //verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 32); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + while ( len > verify_simple_pack_unit_size ) { + pack_len = verify_simple_pack_data(data, verify_simple_pack_unit_size, buffer); + buffer += pack_len; + data += verify_simple_pack_unit_size; + len -= verify_simple_pack_unit_size; + } + if (len > 0) { + pack_len = verify_simple_pack_data(data, len, buffer); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int verify_simple_server_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t *capacity) { + char *plaindata = *pplaindata; + verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + { + LOGE("verify_simple: wrong buf length %d", local->recv_buffer_size + datalength); + return -1; + } + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + while (local->recv_buffer_size > 2) { + int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; + if (length >= 8192 || length < 7) { + free(out_buffer); + local->recv_buffer_size = 0; + LOGE("verify_simple: wrong length %d", length); + return -1; + } + if (length > local->recv_buffer_size) + break; + + int crc = crc32((unsigned char*)recv_buffer, length); + if (crc != -1) { + free(out_buffer); + local->recv_buffer_size = 0; + LOGE("verify_simple: wrong crc"); + return -1; + } + int data_size = length - recv_buffer[2] - 6; + memmove(buffer, recv_buffer + 2 + recv_buffer[2], data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} diff --git a/server/verify.h b/server/verify.h new file mode 100644 index 0000000..57c6ff9 --- /dev/null +++ b/server/verify.h @@ -0,0 +1,19 @@ +/* + * verify.h - Define shadowsocksR server's buffers and callbacks + * + * Copyright (C) 2015 - 2016, Break Wa11 + */ + +#ifndef _VERIFY_H +#define _VERIFY_H + +obfs * verify_simple_new_obfs(); +void verify_simple_dispose(obfs *self); + +int verify_simple_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int verify_simple_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + +int verify_simple_server_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int verify_simple_server_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + +#endif // _VERIFY_H -- 2.19.1