From ef2f98a523a7357b87891b2fbfb627f8f0944827 Mon Sep 17 00:00:00 2001 From: WindyMadman Date: Tue, 8 Aug 2023 02:10:36 +0800 Subject: [PATCH] Add SSR lib --- shadowsocks-libev/Makefile | 129 + shadowsocks-libev/README.md | 185 ++ .../files/shadowsocks-libev.config | 60 + .../files/shadowsocks-libev.init | 320 +++ shadowsocks-libev/files/ss-rules/chain.uc | 122 + shadowsocks-libev/files/ss-rules/set.uc | 114 + shadowsocks-libev/files/ss-rules/ss-rules.uc | 8 + shadowsocks-rust/Makefile | 85 + shadowsocksr-libev/Makefile | 64 + .../0001-Add-ss-server-and-ss-check.patch | 397 +++ ...Revert-verify_simple-and-auth_simple.patch | 20 + .../patches/0003-Refine-Usage.patch | 37 + .../patches/100-fix-gcc-10.patch | 20 + ...-Fix-Werror-sizeof-pointer-memaccess.patch | 11 + .../102-Read-listening-mode-from-config.patch | 34 + ...Add-TPROXY-support-for-TCP-ssr-redir.patch | 154 ++ shadowsocksr-libev/src/server/Makefile.am | 55 + shadowsocksr-libev/src/server/Makefile.in | 919 +++++++ shadowsocksr-libev/src/server/README.md | 3 + shadowsocksr-libev/src/server/acl.c | 597 +++++ shadowsocksr-libev/src/server/acl.h | 53 + shadowsocksr-libev/src/server/auth.c | 993 ++++++++ shadowsocksr-libev/src/server/auth.h | 30 + shadowsocksr-libev/src/server/base64.c | 119 + shadowsocksr-libev/src/server/base64.h | 16 + shadowsocksr-libev/src/server/cache.c | 308 +++ shadowsocksr-libev/src/server/cache.h | 62 + shadowsocksr-libev/src/server/check.c | 236 ++ shadowsocksr-libev/src/server/common.h | 58 + shadowsocksr-libev/src/server/crc32.c | 97 + shadowsocksr-libev/src/server/encrypt.c | 1645 ++++++++++++ shadowsocksr-libev/src/server/encrypt.h | 222 ++ shadowsocksr-libev/src/server/http.c | 152 ++ shadowsocksr-libev/src/server/http.h | 34 + shadowsocksr-libev/src/server/http_simple.c | 626 +++++ shadowsocksr-libev/src/server/http_simple.h | 21 + shadowsocksr-libev/src/server/jconf.c | 260 ++ shadowsocksr-libev/src/server/jconf.h | 78 + shadowsocksr-libev/src/server/json.c | 1002 ++++++++ shadowsocksr-libev/src/server/json.h | 249 ++ shadowsocksr-libev/src/server/list.c | 370 +++ shadowsocksr-libev/src/server/list.h | 61 + shadowsocksr-libev/src/server/netutils.c | 297 +++ shadowsocksr-libev/src/server/netutils.h | 98 + shadowsocksr-libev/src/server/obfs.c | 205 ++ shadowsocksr-libev/src/server/obfs.h | 100 + shadowsocksr-libev/src/server/obfsutil.c | 36 + shadowsocksr-libev/src/server/protocol.h | 34 + shadowsocksr-libev/src/server/resolv.c | 444 ++++ shadowsocksr-libev/src/server/resolv.h | 50 + shadowsocksr-libev/src/server/rule.c | 137 + shadowsocksr-libev/src/server/rule.h | 58 + shadowsocksr-libev/src/server/server.c | 2209 +++++++++++++++++ shadowsocksr-libev/src/server/server.h | 115 + shadowsocksr-libev/src/server/tls.c | 263 ++ shadowsocksr-libev/src/server/tls.h | 33 + shadowsocksr-libev/src/server/tls1.2_ticket.c | 609 +++++ shadowsocksr-libev/src/server/tls1.2_ticket.h | 20 + shadowsocksr-libev/src/server/udprelay.c | 1452 +++++++++++ shadowsocksr-libev/src/server/udprelay.h | 95 + shadowsocksr-libev/src/server/uthash.h | 1074 ++++++++ shadowsocksr-libev/src/server/utils.c | 448 ++++ shadowsocksr-libev/src/server/utils.h | 232 ++ shadowsocksr-libev/src/server/verify.c | 188 ++ shadowsocksr-libev/src/server/verify.h | 19 + 65 files changed, 18242 insertions(+) create mode 100644 shadowsocks-libev/Makefile create mode 100644 shadowsocks-libev/README.md create mode 100644 shadowsocks-libev/files/shadowsocks-libev.config create mode 100644 shadowsocks-libev/files/shadowsocks-libev.init create mode 100644 shadowsocks-libev/files/ss-rules/chain.uc create mode 100644 shadowsocks-libev/files/ss-rules/set.uc create mode 100644 shadowsocks-libev/files/ss-rules/ss-rules.uc create mode 100644 shadowsocks-rust/Makefile create mode 100644 shadowsocksr-libev/Makefile create mode 100644 shadowsocksr-libev/patches/0001-Add-ss-server-and-ss-check.patch create mode 100644 shadowsocksr-libev/patches/0002-Revert-verify_simple-and-auth_simple.patch create mode 100644 shadowsocksr-libev/patches/0003-Refine-Usage.patch create mode 100644 shadowsocksr-libev/patches/100-fix-gcc-10.patch create mode 100644 shadowsocksr-libev/patches/101-Fix-Werror-sizeof-pointer-memaccess.patch create mode 100644 shadowsocksr-libev/patches/102-Read-listening-mode-from-config.patch create mode 100644 shadowsocksr-libev/patches/103-Add-TPROXY-support-for-TCP-ssr-redir.patch create mode 100644 shadowsocksr-libev/src/server/Makefile.am create mode 100644 shadowsocksr-libev/src/server/Makefile.in create mode 100644 shadowsocksr-libev/src/server/README.md create mode 100644 shadowsocksr-libev/src/server/acl.c create mode 100644 shadowsocksr-libev/src/server/acl.h create mode 100644 shadowsocksr-libev/src/server/auth.c create mode 100644 shadowsocksr-libev/src/server/auth.h create mode 100644 shadowsocksr-libev/src/server/base64.c create mode 100644 shadowsocksr-libev/src/server/base64.h create mode 100644 shadowsocksr-libev/src/server/cache.c create mode 100644 shadowsocksr-libev/src/server/cache.h create mode 100644 shadowsocksr-libev/src/server/check.c create mode 100644 shadowsocksr-libev/src/server/common.h create mode 100644 shadowsocksr-libev/src/server/crc32.c create mode 100644 shadowsocksr-libev/src/server/encrypt.c create mode 100644 shadowsocksr-libev/src/server/encrypt.h create mode 100644 shadowsocksr-libev/src/server/http.c create mode 100644 shadowsocksr-libev/src/server/http.h create mode 100644 shadowsocksr-libev/src/server/http_simple.c create mode 100644 shadowsocksr-libev/src/server/http_simple.h create mode 100644 shadowsocksr-libev/src/server/jconf.c create mode 100644 shadowsocksr-libev/src/server/jconf.h create mode 100644 shadowsocksr-libev/src/server/json.c create mode 100644 shadowsocksr-libev/src/server/json.h create mode 100644 shadowsocksr-libev/src/server/list.c create mode 100644 shadowsocksr-libev/src/server/list.h create mode 100644 shadowsocksr-libev/src/server/netutils.c create mode 100644 shadowsocksr-libev/src/server/netutils.h create mode 100644 shadowsocksr-libev/src/server/obfs.c create mode 100644 shadowsocksr-libev/src/server/obfs.h create mode 100644 shadowsocksr-libev/src/server/obfsutil.c create mode 100644 shadowsocksr-libev/src/server/protocol.h create mode 100644 shadowsocksr-libev/src/server/resolv.c create mode 100644 shadowsocksr-libev/src/server/resolv.h create mode 100644 shadowsocksr-libev/src/server/rule.c create mode 100644 shadowsocksr-libev/src/server/rule.h create mode 100644 shadowsocksr-libev/src/server/server.c create mode 100644 shadowsocksr-libev/src/server/server.h create mode 100644 shadowsocksr-libev/src/server/tls.c create mode 100644 shadowsocksr-libev/src/server/tls.h create mode 100644 shadowsocksr-libev/src/server/tls1.2_ticket.c create mode 100644 shadowsocksr-libev/src/server/tls1.2_ticket.h create mode 100644 shadowsocksr-libev/src/server/udprelay.c create mode 100644 shadowsocksr-libev/src/server/udprelay.h create mode 100644 shadowsocksr-libev/src/server/uthash.h create mode 100644 shadowsocksr-libev/src/server/utils.c create mode 100644 shadowsocksr-libev/src/server/utils.h create mode 100644 shadowsocksr-libev/src/server/verify.c create mode 100644 shadowsocksr-libev/src/server/verify.h diff --git a/shadowsocks-libev/Makefile b/shadowsocks-libev/Makefile new file mode 100644 index 0000000..0c4ce1b --- /dev/null +++ b/shadowsocks-libev/Makefile @@ -0,0 +1,129 @@ +# +# Copyright (C) 2017-2020 Yousong Zhou +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +# Checklist when bumping versions +# +# - update cipher list by checking src/crypto.c:crypto_init() +# - check if default mode has changed from being tcp_only +# +PKG_NAME:=shadowsocks-libev +PKG_VERSION:=3.3.5 +PKG_RELEASE:=9 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://github.com/shadowsocks/shadowsocks-libev/releases/download/v$(PKG_VERSION) +PKG_HASH:=cfc8eded35360f4b67e18dc447b0c00cddb29cc57a3cec48b135e5fb87433488 + +PKG_MAINTAINER:=Yousong Zhou + +PKG_LICENSE:=GPL-3.0-or-later +PKG_LICENSE_FILES:=LICENSE + +PKG_FIXUP:=autoreconf +PKG_INSTALL:=1 +PKG_BUILD_FLAGS:=no-mips16 lto +PKG_BUILD_PARALLEL:=1 +PKG_BUILD_DEPENDS:=c-ares pcre + +include $(INCLUDE_DIR)/package.mk + + +define Package/shadowsocks-libev-config + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=shadowsocks-libev config scripts + URL:=https://github.com/shadowsocks/shadowsocks-libev +endef + +define Package/shadowsocks-libev-config/conffiles +/etc/config/shadowsocks-libev +endef + +define Package/shadowsocks-libev-config/install + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_DATA) ./files/shadowsocks-libev.config $(1)/etc/config/shadowsocks-libev + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/shadowsocks-libev.init $(1)/etc/init.d/shadowsocks-libev +endef + + +define Package/shadowsocks-libev/Default + define Package/shadowsocks-libev-$(1) + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=shadowsocks-libev $(1) + URL:=https://github.com/shadowsocks/shadowsocks-libev + DEPENDS:=+libev +libmbedtls +libpthread +libsodium +shadowsocks-libev-config $(DEPENDS_$(1)) + endef + + define Package/shadowsocks-libev-$(1)/install + $$(INSTALL_DIR) $$(1)/usr/bin + $$(INSTALL_BIN) $$(PKG_INSTALL_DIR)/usr/bin/$(1) $$(1)/usr/bin + endef + +endef + +DEPENDS_ss-local = +libpcre +DEPENDS_ss-server = +libcares +libpcre + +SHADOWSOCKS_COMPONENTS:=ss-local ss-redir ss-tunnel ss-server +define shadowsocks-libev/templates + $(foreach component,$(SHADOWSOCKS_COMPONENTS), + $(call Package/shadowsocks-libev/Default,$(component)) + ) +endef +$(eval $(call shadowsocks-libev/templates)) + + +define Package/shadowsocks-libev-ss-rules + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=shadowsocks-libev ss-rules + URL:=https://github.com/shadowsocks/shadowsocks-libev + DEPENDS:=+firewall4 \ + +ip \ + +resolveip \ + +ucode \ + +ucode-mod-fs \ + +shadowsocks-libev-ss-redir \ + +shadowsocks-libev-config \ + +kmod-nft-tproxy +endef + +define Package/shadowsocks-libev-ss-rules/install + $(INSTALL_DIR) $(1)/usr/share/ss-rules + $(INSTALL_DATA) ./files/ss-rules/* $(1)/usr/share/ss-rules/ +endef + +define Build/Prepare + $(call Build/Prepare/Default) + $(FIND) $(PKG_BUILD_DIR) \ + -name '*.o' \ + -o -name '*.lo' \ + -o -name '.deps' \ + -o -name '.libs' \ + | $(XARGS) rm -rvf +endef + +CONFIGURE_ARGS += \ + --disable-documentation \ + --disable-silent-rules \ + --disable-assert \ + --disable-ssp \ + +TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed + +$(eval $(call BuildPackage,shadowsocks-libev-config)) +$(eval $(call BuildPackage,shadowsocks-libev-ss-rules)) +$(foreach component,$(SHADOWSOCKS_COMPONENTS), \ + $(eval $(call BuildPackage,shadowsocks-libev-$(component))) \ +) diff --git a/shadowsocks-libev/README.md b/shadowsocks-libev/README.md new file mode 100644 index 0000000..c4a8825 --- /dev/null +++ b/shadowsocks-libev/README.md @@ -0,0 +1,185 @@ +Skip to [recipes](#recipes) for quick setup instructions + +# components + +`ss-local` provides SOCKS5 proxy with UDP associate support. + + socks5 ss plain + --------> tcp:local_address:local_port ----> ss server -------> dest + +`ss-redir`. The REDIRECT and TPROXY part are to be provided by `ss-rules` script. REDIRECT is for tcp traffic (`SO_ORIGINAL_DST` only supports TCP). TPROXY is for udp messages, but it's only available in the PREROUTING chain and as such cannot proxy local out traffic. + + plain plain ss plain + ---------> REDIRECT ------> tcp:local_address:local_port ----> ss server -----> original dest + + plain plain ss plain + ---------> TPROXY -------> udp:local_address:local_port -----> ss server -----> original dest + +`ss-tunnel` provides ssh `-L` local-forwarding-like tunnel. Typically it's used to tunnel DNS traffic to the remote. + + plain ss plain + ---------> tcp|udp:local_address:local_port ------> ss server -------> tunnel_address + +`ss-server`, the "ss server" in the above diagram + +# uci + +Option names are the same as those used in json config files. Check `validate_xxx` func definition of the [service script](files/shadowsocks-libev.init) and shadowsocks-libev's own documentation for supported options and expected value types. A [sample config file](files/shadowsocks-libev.config) is also provided for reference. + +Every section have a `disabled` option to temporarily turn off the component instance or component instances referring to it. + +Section type `server` is for definition of remote shadowsocks servers. They will be referred to from other component sections and as such should be named (as compared to anonymous section). + +Section type `ss_local`, `ss_redir`, `ss_tunnel` are for specification of shadowsocks-libev components. They share mostly a common set of options like `local_port`, `verbose`, `fast_open`, `timeout`, etc. + +Plugin options should be specified in `server` section and will be inherited by other compoenents referring to it. + +We can have multiple instances of component and `server` sections. The relationship between them is many-to-one. This will have the following implications + + - It's possible to have both `ss_local` and `ss_redir` referring to the same `server` definition + - It's possible to have multiple instances of `ss_redir` listening on the same address:port with `reuse_port` enabled referring to the same or different `server` sections + +`ss_rules` section is for configuring the behaviour of `ss-rules` script. There can only exist at most one such section with the name also being `ss_rules` + + redir_tcp name of ss_redir section with mode tcp_only or tcp_and_udp + redir_udp name of ss_redir section with mode udp_only or tcp_and_udp + ifnames only apply rules on packets from these ifnames + + --- for incoming packets having source address in + + src_ips_bypass will bypass the redir chain + src_ips_forward will always go through the redir chain + src_ips_checkdst will continue to have their destination addresses checked + + --- otherwise, the default action can be specified with + + src_default bypass, forward, [checkdst] + + --- if the previous check result is checkdst, + --- then packets having destination address in + + dst_ips_bypass_file + dst_ips_bypass will bypass the redir chain + dst_ips_forward_file + dst_ips_forward will go through the redir chain + + --- otherwise, the default action can be specified with + + dst_default [bypass], forward + + --- for local out tcp packets, the default action can be specified with + + local_default [bypass], forward, checkdst + +ss-rules now uses nft set for storing addresses/networks. Those set names are also part of the API and can be populated by other programs, e.g. dnsmasq with builtin nft set support. Note that while nftables set supports storing cidr networks when `interval` flag is on, it rejects elements with overlaping intervals. + +Extra nftables expressions can be specified with `nft_tcp_extra` and `nft_udp_extra` to apply ss_rules only to selected tcp/udp traffics. E.g. `tcp dport { 80, 443 }`, `udp dport 53`, etc. + +# incompatible changes + +| Commit date | Commit ID | Subject | Comment | +| ----------- | --------- | ------- | ------- | +| 2022-03-01 | fdaf2de2a | shadowsocks-libev: ss-rules: convert to using nft | ss-rules now uses nftables. UCI option ipt_args and dst_forward_recentrst are now deprecated and removed | +| 2020-08-03 | 7d7cbae75 | shadowsocks-libev: support ss-server option local_address_{v4,v6} | ss_server bind_address now deprecated, use local_address | +| 2019-05-09 | afe7d3424 | shadowsocks-libev: move plugin options to server section | This is a revision against c19e949 committed 2019-05-06 | +| 2017-07-02 | b61af9703 | shadowsocks-libev: rewrite | Packaging of shadowsocks-libev was rewritten from scratch | + +# notes and faq + +Useful paths and commands for debugging + + # check current running status + ubus call service list '{"name": "shadowsocks-libev"}' + ubus call service list '{"name": "shadowsocks-libev", "verbose": true}' + + # dump validate definition + ubus call service validate '{"package": "shadowsocks-libev"}' + ubus call service validate '{"package": "shadowsocks-libev"}' \ + | jsonfilter -e '$["shadowsocks-libev"]["ss_tunnel"]' + + # check json config + ls -l /var/etc/shadowsocks-libev/ + + # set uci config option verbose to 1, restart the service and follow the log + logread -f + +ss-redir needs to open a new socket and setsockopt IP_TRANSPARENT when sending udp reply to client. This requires `CAP_NET_ADMIN` and as such the process cannot run as `nobody` + +ss-local, ss-redir, etc. supports specifying an array of remote ss server, but supporting this in uci seems to be overkill. The workaround can be defining multiple `server` sections and multiple `ss-redir` instances with `reuse_port` enabled + +# recipes + +## forward all + +This will setup firewall rules to forward almost all incoming tcp/udp and locally generated tcp traffic (excluding those to private addresses like 192.168.0.0/16 etc.) through remote shadowsocks server + +Install components. +Retry each command till it succeed + + opkg install shadowsocks-libev-ss-redir + opkg install shadowsocks-libev-ss-rules + opkg install shadowsocks-libev-ss-tunnel + +Edit uci config `/etc/config/shadowsocks-libev`. +Replace `config server 'sss0'` section with parameters of your own remote shadowsocks server. +As for other options, change them only when you know the effect. + + config server 'sss0' + option disabled 0 + option server '_sss_addr_' + option server_port '_sss_port_' + option password '********' + option method 'aes-256-cfb' + + config ss_tunnel + option disabled 0 + option server 'sss0' + option local_address '0.0.0.0' + option local_port '8053' + option tunnel_address '8.8.8.8:53' + option mode 'tcp_and_udp' + + config ss_redir ssr0 + option disabled 0 + option server 'sss0' + option local_address '0.0.0.0' + option local_port '1100' + option mode 'tcp_and_udp' + option reuse_port 1 + + config ss_rules 'ss_rules' + option disabled 0 + option redir_tcp 'ssr0' + option redir_udp 'ssr0' + option src_default 'checkdst' + option dst_default 'forward' + option local_default 'forward' + +Restart shadowsocks-libev components + + /etc/init.d/shadowsocks-libev restart + +Check if things are in place + + nft list ruleset | sed -r -n '/^\t[a-z]+ ss_rules[^ ]+ \{/,/^\t\}/p' + netstat -lntp | grep -E '8053|1100' + ps ww | grep ss- + +Edit `/etc/config/dhcp`, making sure options are present in the first dnsmasq section like the following to let it use local tunnel endpoint for upstream dns query. +Option `noresolv` instructs dnsmasq to not use other dns servers like advertised by local isp. +Option `localuse` intends to make sure the device you are configuring also uses this dnsmasq instance as the resolver, not the ones from other sources. + + config dnsmasq + ... + list server '127.0.0.1#8053' + option noresolv 1 + option localuse 1 + +Restart dnsmasq + + /etc/init.d/dnsmasq restart + +Check network on your computer + + nslookup www.google.com + curl -vv https://www.google.com diff --git a/shadowsocks-libev/files/shadowsocks-libev.config b/shadowsocks-libev/files/shadowsocks-libev.config new file mode 100644 index 0000000..1d41127 --- /dev/null +++ b/shadowsocks-libev/files/shadowsocks-libev.config @@ -0,0 +1,60 @@ +config ss_local + option disabled 1 + option server 'sss0' + option local_address '0.0.0.0' + option local_port '1080' + option timeout '30' + +config ss_tunnel + option disabled 1 + option server 'sss0' + option local_address '0.0.0.0' + option local_port '1090' + option tunnel_address 'example.com:80' + option mode 'tcp_and_udp' + option timeout '60' + +config ss_redir hi + option disabled 1 + option server 'sss0' + option local_address '0.0.0.0' + option local_port '1100' + option mode 'tcp_and_udp' + option timeout '60' + option fast_open 1 + option verbose 1 + option reuse_port 1 + +config ss_redir hj + option disabled 1 + option server 'sss0' + option local_address '0.0.0.0' + option local_port '1100' + option mode 'tcp_and_udp' + option timeout '60' + option fast_open 1 + option verbose 1 + option reuse_port 1 + +config ss_rules 'ss_rules' + option disabled 1 + option redir_tcp 'hi' + option redir_udp 'hi' + option src_default 'checkdst' + option dst_default 'bypass' + option local_default 'checkdst' + list src_ips_forward '192.168.1.4' + list dst_ips_forward '8.8.8.8' + +config server 'sss0' + option disabled 1 + option server '192.168.1.3' + option server_port '9001' + option password '********' + option method 'aes-256-cfb' + +config ss_server + option disabled 1 + option server_port '9001' + option password '********' + option method 'aes-256-cfb' diff --git a/shadowsocks-libev/files/shadowsocks-libev.init b/shadowsocks-libev/files/shadowsocks-libev.init new file mode 100644 index 0000000..5330b87 --- /dev/null +++ b/shadowsocks-libev/files/shadowsocks-libev.init @@ -0,0 +1,320 @@ +#!/bin/sh /etc/rc.common +# +# Copyright (C) 2017-2019 Yousong Zhou +# +# This is free software, licensed under the GNU General Public License v3. +# See /LICENSE for more information. +# + +USE_PROCD=1 +START=99 + +ss_confdir=/var/etc/shadowsocks-libev +ss_bindir=/usr/bin + +ssrules_uc="/usr/share/ss-rules/ss-rules.uc" +ssrules_nft="/etc/nftables.d/90-ss-rules.nft" + +ss_mkjson_server_conf() { + local cfgserver + + config_get cfgserver "$cfg" server + [ -n "$cfgserver" ] || return 1 + eval "$(validate_server_section "$cfg" ss_validate_mklocal)" + validate_server_section "$cfgserver" || return 1 + [ "$disabled" = 0 ] || return 1 + ss_mkjson_server_conf_ "$cfgserver" +} + +ss_mkjson_server_conf_() { + [ -n "$server_port" ] || return 1 + [ -z "$server" ] || json_add_string server "$server" + json_add_int server_port "$server_port" + [ -z "$method" ] || json_add_string method "$method" + [ -z "$key" ] || json_add_string key "$key" + [ -z "$password" ] || json_add_string password "$password" + [ -z "$plugin" ] || json_add_string plugin "$plugin" + [ -z "$plugin_opts" ] || json_add_string plugin_opts "$plugin_opts" +} + +ss_mkjson_ss_local_conf() { + ss_mkjson_server_conf +} + +ss_mkjson_ss_redir_conf() { + ss_mkjson_server_conf +} + +ss_mkjson_ss_server_conf() { + ss_mkjson_server_conf_ +} + +ss_mkjson_ss_tunnel_conf() { + ss_mkjson_server_conf || return 1 + [ -n "$tunnel_address" ] || return 1 + json_add_string tunnel_address "$tunnel_address" +} + +ss_xxx() { + local cfg="$1" + local cfgtype="$2" + local bin="$ss_bindir/${cfgtype/_/-}" + local confjson="$ss_confdir/$cfgtype.$cfg.json" + + [ -x "$bin" ] || return + eval "$("validate_${cfgtype}_section" "$cfg" ss_validate_mklocal)" + "validate_${cfgtype}_section" "$cfg" || return + [ "$disabled" = 0 ] || return + + json_init + ss_mkjson_${cfgtype}_conf || return + json_add_boolean use_syslog 1 + json_add_boolean ipv6_first "$ipv6_first" + json_add_boolean fast_open "$fast_open" + json_add_boolean reuse_port "$reuse_port" + json_add_boolean no_delay "$no_delay" + [ -z "$local_address" ] || json_add_string local_address "$local_address" + [ -z "$local_port" ] || json_add_int local_port "$local_port" + [ -z "$local_ipv4_address" ] || json_add_string local_ipv4_address "$local_ipv4_address" + [ -z "$local_ipv6_address" ] || json_add_string local_ipv6_address "$local_ipv6_address" + [ -z "$mode" ] || json_add_string mode "$mode" + [ -z "$mtu" ] || json_add_int mtu "$mtu" + [ -z "$timeout" ] || json_add_int timeout "$timeout" + [ -z "$user" ] || json_add_string user "$user" + [ -z "$acl" ] || json_add_string acl "$acl" + json_dump -i >"$confjson" + + procd_open_instance "$cfgtype.$cfg" + procd_set_param command "$bin" -c "$confjson" + [ "$verbose" = 0 ] || procd_append_param command -v + if [ -n "$bind_address" ]; then + echo "$cfgtype $cfg: uci option bind_address deprecated, please switch to local_address" >&2 + procd_append_param command -b "$bind_address" + fi + procd_set_param file "$confjson" + procd_set_param respawn + procd_close_instance + ss_rules_cb +} + +ss_rules_cb() { + local cfgserver server + + if [ "$cfgtype" = ss_redir ]; then + config_get cfgserver "$cfg" server + config_get server "$cfgserver" server + ss_redir_servers="$ss_redir_servers $server" + if [ "$mode" = tcp_only -o "$mode" = "tcp_and_udp" ]; then + eval "ss_rules_redir_tcp_$cfg=$local_port" + fi + if [ "$mode" = udp_only -o "$mode" = "tcp_and_udp" ]; then + eval "ss_rules_redir_udp_$cfg=$local_port" + fi + fi +} + +ss_rules_nft_gen() { + local cfg="ss_rules" + local cfgtype + local local_port_tcp local_port_udp + local remote_servers + + [ -s "$ssrules_uc" ] || return 1 + + config_get cfgtype "$cfg" TYPE + [ "$cfgtype" = ss_rules ] || return 1 + + eval "$(validate_ss_rules_section "$cfg" ss_validate_mklocal)" + validate_ss_rules_section "$cfg" || return 1 + [ "$disabled" = 0 ] || return 2 + + eval local_port_tcp="\$ss_rules_redir_tcp_$redir_tcp" + eval local_port_udp="\$ss_rules_redir_udp_$redir_udp" + [ -n "$local_port_tcp" -o -n "$local_port_udp" ] || return 1 + remote_servers="$(echo $ss_redir_servers \ + | tr ' ' '\n' \ + | sort -u \ + | xargs -n 1 resolveip \ + | sort -u)" + + local tmp="/tmp/ssrules" + json_init + json_add_string o_remote_servers "$remote_servers" + json_add_int o_redir_tcp_port "$local_port_tcp" + json_add_int o_redir_udp_port "$local_port_udp" + json_add_string o_ifnames "$ifnames" + json_add_string o_local_default "$local_default" + json_add_string o_src_bypass "$src_ips_bypass" + json_add_string o_src_forward "$src_ips_forward" + json_add_string o_src_checkdst "$src_ips_checkdst" + json_add_string o_src_default "$src_default" + json_add_string o_dst_bypass "$dst_ips_bypass" + json_add_string o_dst_forward "$dst_ips_forward" + json_add_string o_dst_bypass_file "$dst_ips_bypass_file" + json_add_string o_dst_forward_file "$dst_ips_forward_file" + json_add_string o_dst_default "$dst_default" + json_add_string o_nft_tcp_extra "$nft_tcp_extra" + json_add_string o_nft_udp_extra "$nft_udp_extra" + json_dump -i >"$tmp.json" + + if utpl -S -F "$tmp.json" "$ssrules_uc" >"$tmp.nft" \ + && ! cmp -s "$tmp.nft" "$ssrules_nft"; then + echo "table inet chk {include \"$tmp.nft\";}" >"$tmp.nft.chk" + if nft -f "$tmp.nft.chk" -c; then + mv "$tmp.nft" "$ssrules_nft" + fw4 restart + fi + rm -f "$tmp.nft.chk" + fi + rm -f "$tmp.json" + rm -f "$tmp.nft" +} + +ss_rules_nft_reset() { + if [ -f "$ssrules_nft" ]; then + rm -f "$ssrules_nft" + fw4 restart + fi +} + +ss_rules() { + if ! ss_rules_nft_gen; then + ss_rules_nft_reset + fi +} + +start_service() { + local cfgtype + + mkdir -p "$ss_confdir" + config_load shadowsocks-libev + for cfgtype in ss_local ss_redir ss_server ss_tunnel; do + config_foreach ss_xxx "$cfgtype" "$cfgtype" + done + ss_rules +} + +stop_service() { + ss_rules_nft_reset + rm -rf "$ss_confdir" +} + +service_triggers() { + procd_add_reload_interface_trigger wan + procd_add_reload_trigger shadowsocks-libev + procd_open_validate + validate_server_section + validate_ss_local_section + validate_ss_redir_section + validate_ss_rules_section + validate_ss_server_section + validate_ss_tunnel_section + procd_close_validate +} + +ss_validate_mklocal() { + local tuple opts + + shift 2 + for tuple in "$@"; do + opts="${tuple%%:*} $opts" + done + [ -z "$opts" ] || echo "local $opts" +} + +ss_validate() { + uci_validate_section shadowsocks-libev "$@" +} + +validate_common_server_options_() { + local cfgtype="$1"; shift + local cfg="$1"; shift + local func="$1"; shift + local stream_methods='"table", "rc4", "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", "salsa20", "chacha20", "chacha20-ietf"' + local aead_methods='"aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305"' + + "${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \ + 'disabled:bool:0' \ + 'server:host' \ + 'server_port:port' \ + 'password:string' \ + 'key:string' \ + "method:or($stream_methods, $aead_methods)" \ + 'plugin:string' \ + 'plugin_opts:string' +} + +validate_common_client_options_() { + validate_common_options_ "$@" \ + 'server:uci("shadowsocks-libev", "@server")' \ + 'local_address:ipaddr:0.0.0.0' \ + 'local_port:port' +} + +validate_common_options_() { + local cfgtype="$1"; shift + local cfg="$1"; shift + local func="$1"; shift + + "${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \ + 'disabled:bool:0' \ + 'fast_open:bool:0' \ + 'ipv6_first:bool:0' \ + 'no_delay:bool:0' \ + 'reuse_port:bool:0' \ + 'verbose:bool:0' \ + 'mode:or("tcp_only", "udp_only", "tcp_and_udp"):tcp_only' \ + 'mtu:uinteger' \ + 'timeout:uinteger' \ + 'user:string' +} + +validate_server_section() { + validate_common_server_options_ server "$1" "$2" +} + +validate_ss_local_section() { + validate_common_client_options_ ss_local "$1" "$2" \ + 'acl:file' +} + +validate_ss_redir_section() { + validate_common_client_options_ ss_redir "$1" "$2" +} + +validate_ss_rules_section() { + "${2:-ss_validate}" ss_rules "$1" \ + 'disabled:bool:0' \ + 'redir_tcp:uci("shadowsocks-libev", "@ss_redir")' \ + 'redir_udp:uci("shadowsocks-libev", "@ss_redir")' \ + 'src_ips_bypass:or(ipaddr,cidr)' \ + 'src_ips_forward:or(ipaddr,cidr)' \ + 'src_ips_checkdst:or(ipaddr,cidr)' \ + 'dst_ips_bypass_file:file' \ + 'dst_ips_bypass:or(ipaddr,cidr)' \ + 'dst_ips_forward_file:file' \ + 'dst_ips_forward:or(ipaddr,cidr)' \ + 'src_default:or("bypass", "forward", "checkdst"):checkdst' \ + 'dst_default:or("bypass", "forward"):bypass' \ + 'local_default:or("bypass", "forward", "checkdst"):bypass' \ + 'nft_tcp_extra:string' \ + 'nft_udp_extra:string' \ + 'ifnames:maxlength(15)' +} + +validate_ss_server_section() { + validate_common_server_options_ ss_server "$1" \ + validate_common_options_ \ + "$2" \ + 'local_address:ipaddr' \ + 'local_ipv4_address:ip4addr' \ + 'local_ipv6_address:ip6addr' \ + 'bind_address:ipaddr' \ + 'acl:file' +} + +validate_ss_tunnel_section() { + validate_common_client_options_ ss_tunnel "$1" \ + "$2" \ + 'tunnel_address:regex(".+\:[0-9]+")' +} diff --git a/shadowsocks-libev/files/ss-rules/chain.uc b/shadowsocks-libev/files/ss-rules/chain.uc new file mode 100644 index 0000000..3047f16 --- /dev/null +++ b/shadowsocks-libev/files/ss-rules/chain.uc @@ -0,0 +1,122 @@ +{% +function get_local_verdict() { + let v = o_local_default; + if (v == "checkdst") { + return "goto ss_rules_dst_" + proto; + } else if (v == "forward") { + return "goto ss_rules_forward_" + proto; + } else { + return null; + } +} + +function get_src_default_verdict() { + let v = o_src_default; + if (v == "checkdst") { + return "goto ss_rules_dst_" + proto; + } else if (v == "forward") { + return "goto ss_rules_forward_" + proto; + } else { + return "accept"; + } +} + +function get_dst_default_verdict() { + let v = o_dst_default; + if (v == "forward") { + return "goto ss_rules_forward_" + proto; + } else { + return "accept"; + } +} + +function get_ifnames() { + let res = []; + for (let ifname in split(o_ifnames, /[ \t\n]/)) { + ifname = trim(ifname); + if (ifname) push(res, ifname); + } + return res; +} + +let type, hook, priority, redir_port; +if (proto == "tcp") { + type = "nat"; + hook = "prerouting"; + priority = -1; + redir_port = o_redir_tcp_port; +} else if (proto == "udp") { + type = "filter"; + hook = "prerouting"; + priority = "mangle"; + redir_port = o_redir_udp_port; + if (system(" + set -o errexit + iprr() { + while ip $1 rule del fwmark 1 lookup 100 2>/dev/null; do true; done + ip $1 rule add fwmark 1 lookup 100 + ip $1 route flush table 100 2>/dev/null || true + ip $1 route add local default dev lo table 100 + } + iprr -4 + iprr -6 + ") != 0) { + return ; + } +} else { + return; +} + +%} +{% if (redir_port): %} + +chain ss_rules_pre_{{ proto }} { + type {{ type }} hook {{ hook }} priority {{ priority }}; + meta l4proto {{ proto }}{%- let ifnames=get_ifnames(); if (length(ifnames)): %} iifname { {{join(", ", ifnames)}} }{% endif %} goto ss_rules_pre_src_{{ proto }}; +} + +chain ss_rules_pre_src_{{ proto }} { + ip daddr @ss_rules_dst_bypass_ accept; + ip6 daddr @ss_rules6_dst_bypass_ accept; + goto ss_rules_src_{{ proto }}; +} + +chain ss_rules_src_{{ proto }} { + ip saddr @ss_rules_src_bypass accept; + ip saddr @ss_rules_src_forward goto ss_rules_forward_{{ proto }}; + ip saddr @ss_rules_src_checkdst goto ss_rules_dst_{{ proto }}; + ip6 saddr @ss_rules6_src_bypass accept; + ip6 saddr @ss_rules6_src_forward goto ss_rules_forward_{{ proto }}; + ip6 saddr @ss_rules6_src_checkdst goto ss_rules_dst_{{ proto }}; + {{ get_src_default_verdict() }}; +} + +chain ss_rules_dst_{{ proto }} { + ip daddr @ss_rules_dst_bypass accept; + ip daddr @ss_rules_dst_forward goto ss_rules_forward_{{ proto }}; + ip6 daddr @ss_rules6_dst_bypass accept; + ip6 daddr @ss_rules6_dst_forward goto ss_rules_forward_{{ proto }}; + {{ get_dst_default_verdict() }}; +} + +{% if (proto == "tcp"): %} +chain ss_rules_forward_{{ proto }} { + meta l4proto tcp {{ o_nft_tcp_extra }} redirect to :{{ redir_port }}; +} +{% let local_verdict = get_local_verdict(); if (local_verdict): %} +chain ss_rules_local_out { + type {{ type }} hook output priority -1; + meta l4proto != tcp accept; + ip daddr @ss_rules_dst_bypass_ accept; + ip daddr @ss_rules_dst_bypass accept; + ip6 daddr @ss_rules6_dst_bypass_ accept; + ip6 daddr @ss_rules6_dst_bypass accept; + {{ local_verdict }}; +} +{% endif %} +{% elif (proto == "udp"): %} +chain ss_rules_forward_{{ proto }} { + meta l4proto udp {{ o_nft_udp_extra }} meta mark set 1 tproxy to :{{ redir_port }}; +} +{% endif %} +{% endif %} diff --git a/shadowsocks-libev/files/ss-rules/set.uc b/shadowsocks-libev/files/ss-rules/set.uc new file mode 100644 index 0000000..38140e7 --- /dev/null +++ b/shadowsocks-libev/files/ss-rules/set.uc @@ -0,0 +1,114 @@ +{% +let fs = require("fs"); + +let o_dst_bypass4_ = " + 0.0.0.0/8 + 10.0.0.0/8 + 100.64.0.0/10 + 127.0.0.0/8 + 169.254.0.0/16 + 172.16.0.0/12 + 192.0.0.0/24 + 192.0.2.0/24 + 192.31.196.0/24 + 192.52.193.0/24 + 192.88.99.0/24 + 192.168.0.0/16 + 192.175.48.0/24 + 198.18.0.0/15 + 198.51.100.0/24 + 203.0.113.0/24 + 224.0.0.0/4 + 240.0.0.0/4 +"; +let o_dst_bypass6_ = " + ::1/128 + ::/128 + ::ffff:0:0/96 + 64:ff9b:1::/48 + 100::/64 + fe80::/10 + 2001::/23 + fc00::/7 +"; +let o_dst_bypass_ = o_dst_bypass4_ + " " + o_dst_bypass6_; + +let set_suffix = { + "src_bypass": { + str: o_src_bypass, + }, + "src_forward": { + str: o_src_forward, + }, + "src_checkdst": { + str: o_src_checkdst, + }, + "dst_bypass": { + str: o_dst_bypass, + file: o_dst_bypass_file, + }, + "dst_bypass_": { + str: o_dst_bypass_, + }, + "dst_forward": { + str: o_dst_forward, + file: o_dst_forward_file, + }, + "dst_forward_rrst_": {}, +}; + +function set_name(suf, af) { + if (af == 4) { + return "ss_rules_"+suf; + } else { + return "ss_rules6_"+suf; + } +} + +function set_elements_parse(res, str, af) { + for (let addr in split(str, /[ \t\n]/)) { + addr = trim(addr); + if (!addr) continue; + if (af == 4 && index(addr, ":") != -1) continue; + if (af == 6 && index(addr, ":") == -1) continue; + push(res, addr); + } +} + +function set_elements(suf, af) { + let obj = set_suffix[suf]; + let res = []; + let addr; + + let str = obj["str"]; + if (str) { + set_elements_parse(res, str, af); + } + + let file = obj["file"]; + if (file) { + let fd = fs.open(file); + if (fd) { + str = fd.read("all"); + set_elements_parse(res, str, af); + } + } + + return res; +} +%} + +{% for (let suf in set_suffix): for (let af in [4, 6]): %} +set {{ set_name(suf, af) }} { + type ipv{{af}}_addr; + flags interval; + auto-merge; +{% let elems = set_elements(suf, af); if (length(elems)): %} + elements = { +{% for (let i = 0; i < length(elems); i++): %} + {{ elems[i] }}{% if (i < length(elems) - 1): %},{% endif %}{% print("\n") %} +{% endfor %} + } +{% endif %} +} +{% endfor; endfor %} diff --git a/shadowsocks-libev/files/ss-rules/ss-rules.uc b/shadowsocks-libev/files/ss-rules/ss-rules.uc new file mode 100644 index 0000000..f3955b2 --- /dev/null +++ b/shadowsocks-libev/files/ss-rules/ss-rules.uc @@ -0,0 +1,8 @@ +{% + +include("set.uc"); +include("chain.uc", {proto: "tcp"}); +include("chain.uc", {proto: "udp"}); + +%} + diff --git a/shadowsocks-rust/Makefile b/shadowsocks-rust/Makefile new file mode 100644 index 0000000..92ba6ba --- /dev/null +++ b/shadowsocks-rust/Makefile @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2017-2020 Yousong Zhou +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=shadowsocks-rust +PKG_VERSION:=1.15.2 +PKG_RELEASE:=1 + +PKG_SOURCE_HEADER:=shadowsocks-v$(PKG_VERSION) +PKG_SOURCE_BODY:=unknown-linux-musl +PKG_SOURCE_FOOTER:=tar.xz +PKG_SOURCE_URL:=https://github.com/shadowsocks/shadowsocks-rust/releases/download/v$(PKG_VERSION)/ + +ifeq ($(ARCH),aarch64) + PKG_SOURCE:=$(PKG_SOURCE_HEADER).aarch64-$(PKG_SOURCE_BODY).$(PKG_SOURCE_FOOTER) + PKG_HASH:=97850893c5a35b68ccd419b542c1785e4c0006e8c0c0b10eac8e5b8c67b12704 +else ifeq ($(ARCH),arm) + # Referred to golang/golang-values.mk + ARM_CPU_FEATURES:=$(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE)))) + ifeq ($(ARM_CPU_FEATURES),) + PKG_SOURCE:=$(PKG_SOURCE_HEADER).arm-$(PKG_SOURCE_BODY)eabi.$(PKG_SOURCE_FOOTER) + PKG_HASH:=728f4550abe4f18679555fa00b88ce889d2f412be7fa0d96bf153d086ad0c63e + else + PKG_SOURCE:=$(PKG_SOURCE_HEADER).arm-$(PKG_SOURCE_BODY)eabihf.$(PKG_SOURCE_FOOTER) + PKG_HASH:=f13ae6497843347c91ef8b0634cee96f5043a644b2fba30009cafd6c9e65d7df + endif +else ifeq ($(ARCH),i386) + PKG_SOURCE:=$(PKG_SOURCE_HEADER).i686-$(PKG_SOURCE_BODY).$(PKG_SOURCE_FOOTER) + PKG_HASH:=a8558a9e898f9ba875136c3d038e968ca8d301a7dcde977f6483d5072f57695f +else ifeq ($(ARCH),mips) + PKG_SOURCE:=$(PKG_SOURCE_HEADER).mips-$(PKG_SOURCE_BODY).$(PKG_SOURCE_FOOTER) + PKG_HASH:=a3f99d549c9f417fef63fa323840e0c4fb4dc96a53cf8329293c2a4e485ed239 +else ifeq ($(ARCH),mipsel) + PKG_SOURCE:=$(PKG_SOURCE_HEADER).mipsel-$(PKG_SOURCE_BODY).$(PKG_SOURCE_FOOTER) + PKG_HASH:=47d0f10d94216376057fc4238ebab68d19e3882293d5fe76ac3d29c41458a985 +else ifeq ($(ARCH),x86_64) + PKG_SOURCE:=$(PKG_SOURCE_HEADER).x86_64-$(PKG_SOURCE_BODY).$(PKG_SOURCE_FOOTER) + PKG_HASH:=69c2df2bd4e9e2ff0d70faa14b70888de2eb205ab2a49dd7066c86363e2acc50 +# Set the default value to make OpenWrt Package Checker happy +else + PKG_SOURCE:=dummy + PKG_HASH:=dummy +endif + +PKG_MAINTAINER:=Tianling Shen +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE + +include $(INCLUDE_DIR)/package.mk + +TAR_CMD:=$(HOST_TAR) -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) + +define Package/shadowsocks-rust/Default + define Package/shadowsocks-rust-$(1) + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=shadowsocks-rust $(1) + URL:=https://github.com/shadowsocks/shadowsocks-rust + DEPENDS:=@USE_MUSL @(aarch64||arm||i386||mips||mipsel||x86_64) @!(TARGET_x86_geode||TARGET_x86_legacy) + endef + + define Package/shadowsocks-rust-$(1)/install + $$(INSTALL_DIR) $$(1)/usr/bin + $$(INSTALL_BIN) $$(PKG_BUILD_DIR)/$(1) $$(1)/usr/bin + endef +endef + +SHADOWSOCKS_COMPONENTS:=sslocal ssmanager ssserver ssurl ssservice +define shadowsocks-rust/templates + $(foreach component,$(SHADOWSOCKS_COMPONENTS), + $(call Package/shadowsocks-rust/Default,$(component)) + ) +endef +$(eval $(call shadowsocks-rust/templates)) + +define Build/Compile +endef + +$(foreach component,$(SHADOWSOCKS_COMPONENTS), \ + $(eval $(call BuildPackage,shadowsocks-rust-$(component))) \ +) diff --git a/shadowsocksr-libev/Makefile b/shadowsocksr-libev/Makefile new file mode 100644 index 0000000..53612b2 --- /dev/null +++ b/shadowsocksr-libev/Makefile @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2017-2020 Yousong Zhou +# Copyright (C) 2018 Lean +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=shadowsocksr-libev +PKG_VERSION:=2.5.6 +PKG_RELEASE:=9 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/shadowsocksrr/shadowsocksr-libev +PKG_SOURCE_DATE:=2018-03-07 +PKG_SOURCE_VERSION:=d63ff863800a5645aca4309d5dd5962bd1e95543 +PKG_MIRROR_HASH:=34308ed827a5dd4f4e35619914102d55b00604faa44fda051d1d25fb4a319325 + +PKG_LICENSE:=GPL-3.0 +PKG_LICENSE_FILES:=LICENSE + +PKG_FIXUP:=autoreconf +PKG_USE_MIPS16:=0 +PKG_BUILD_PARALLEL:=1 +PKG_INSTALL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/shadowsocksr-libev/Default + define Package/shadowsocksr-libev-ssr-$(1) + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=shadowsocksr-libev ssr-$(1) + URL:=https://github.com/shadowsocksrr/shadowsocksr-libev + DEPENDS:=+libev +libsodium +libopenssl +libpthread +libpcre +libudns +zlib + endef + + define Package/shadowsocksr-libev-ssr-$(1)/install + $$(INSTALL_DIR) $$(1)/usr/bin + $$(INSTALL_BIN) $$(PKG_INSTALL_DIR)/usr/bin/ss-$(1) $$(1)/usr/bin/ssr-$(1) + endef +endef + +SHADOWSOCKSR_COMPONENTS:=check local nat redir server +define shadowsocksr-libev/templates + $(foreach component,$(SHADOWSOCKSR_COMPONENTS), + $(call Package/shadowsocksr-libev/Default,$(component)) + ) +endef +$(eval $(call shadowsocksr-libev/templates)) + +CONFIGURE_ARGS += \ + --disable-documentation \ + --disable-ssp \ + --disable-assert \ + --enable-system-shared-lib + +TARGET_CFLAGS += -flto +TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed + +$(foreach component,$(SHADOWSOCKSR_COMPONENTS), \ + $(eval $(call BuildPackage,shadowsocksr-libev-ssr-$(component))) \ +) diff --git a/shadowsocksr-libev/patches/0001-Add-ss-server-and-ss-check.patch b/shadowsocksr-libev/patches/0001-Add-ss-server-and-ss-check.patch new file mode 100644 index 0000000..290ede5 --- /dev/null +++ b/shadowsocksr-libev/patches/0001-Add-ss-server-and-ss-check.patch @@ -0,0 +1,397 @@ +--- a/.gitignore ++++ b/.gitignore +@@ -2,6 +2,7 @@ build/ + .deps/ + /Makefile + src/Makefile ++server/Makefile + libev/Makefile + libudns/Makefile + libcork/Makefile +--- 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 +--- 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 +--- 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 director + --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/configu + + + +-# 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___extens + + + +-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}ma + # + 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" >>confdef + + + 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 +--- 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/shadowsocksr-libev/patches/0002-Revert-verify_simple-and-auth_simple.patch b/shadowsocksr-libev/patches/0002-Revert-verify_simple-and-auth_simple.patch new file mode 100644 index 0000000..0f81146 --- /dev/null +++ b/shadowsocksr-libev/patches/0002-Revert-verify_simple-and-auth_simple.patch @@ -0,0 +1,20 @@ +--- a/src/obfs/obfs.c ++++ b/src/obfs/obfs.c +@@ -88,7 +88,7 @@ obfs_class *new_obfs_class(const char *p + plugin->client_decode = tls12_ticket_auth_client_decode; + + return plugin; +- /*} else if (strcmp(plugin_name, "verify_simple") == 0) { ++ } else if (strcmp(plugin_name, "verify_simple") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs_class)); + plugin->init_data = init_data; + plugin->new_obfs = verify_simple_new_obfs; +@@ -115,7 +115,7 @@ obfs_class *new_obfs_class(const char *p + plugin->client_udp_pre_encrypt = NULL; + plugin->client_udp_post_decrypt = NULL; + +- return plugin;*/ ++ return plugin; + } else if (strcmp(plugin_name, "auth_sha1") == 0) { + obfs_class *plugin = (obfs_class *) malloc(sizeof(obfs_class)); + plugin->init_data = auth_simple_init_data; diff --git a/shadowsocksr-libev/patches/0003-Refine-Usage.patch b/shadowsocksr-libev/patches/0003-Refine-Usage.patch new file mode 100644 index 0000000..1d8d7f2 --- /dev/null +++ b/shadowsocksr-libev/patches/0003-Refine-Usage.patch @@ -0,0 +1,37 @@ +--- a/src/utils.c ++++ b/src/utils.c +@@ -258,8 +258,6 @@ usage() + { + printf("\n"); + printf("shadowsocks-libev %s with %s\n\n", VERSION, USING_CRYPTO); +- printf( +- " maintained by Max Lv and Linus Yang \n\n"); + printf(" usage:\n\n"); + #ifdef MODULE_LOCAL + printf(" ss-local\n"); +@@ -299,6 +297,25 @@ usage() + " The default cipher is rc4-md5.\n"); + printf("\n"); + printf( ++ " -o Obfs of your remote server: plain,\n"); ++ printf( ++ " http_simple, http_post and tls1.2_ticket_auth.\n"); ++ printf( ++ " -g Obfs-Param of your remote server.\n"); ++ printf( ++ " -O Protocol of your remote server: origin,\n"); ++ printf( ++ " auth_sha1, auth_sha1_v2, auth_sha1_v4,\n"); ++ printf( ++ " auth_aes128_md5, auth_aes128_sha1,\n"); ++ printf( ++ " auth_chain_a, auth_chain_b, auth_chain_c,\n"); ++ printf( ++ " auth_chain_d, auth_chain_e and auth_chain_f.\n"); ++ printf( ++ " -G Protocol-Param of your remote server.\n"); ++ printf("\n"); ++ printf( + " [-a ] Run as another user.\n"); + printf( + " [-f ] The file path to store pid.\n"); diff --git a/shadowsocksr-libev/patches/100-fix-gcc-10.patch b/shadowsocksr-libev/patches/100-fix-gcc-10.patch new file mode 100644 index 0000000..1049fc2 --- /dev/null +++ b/shadowsocksr-libev/patches/100-fix-gcc-10.patch @@ -0,0 +1,20 @@ +--- a/src/http.h ++++ b/src/http.h +@@ -29,6 +29,6 @@ + #include + #include "protocol.h" + +-const protocol_t *const http_protocol; ++extern const protocol_t *const http_protocol; + + #endif +--- a/src/tls.h ++++ b/src/tls.h +@@ -28,6 +28,6 @@ + + #include "protocol.h" + +-const protocol_t *const tls_protocol; ++extern const protocol_t *const tls_protocol; + + #endif diff --git a/shadowsocksr-libev/patches/101-Fix-Werror-sizeof-pointer-memaccess.patch b/shadowsocksr-libev/patches/101-Fix-Werror-sizeof-pointer-memaccess.patch new file mode 100644 index 0000000..673d0ef --- /dev/null +++ b/shadowsocksr-libev/patches/101-Fix-Werror-sizeof-pointer-memaccess.patch @@ -0,0 +1,11 @@ +--- a/src/local.c ++++ b/src/local.c +@@ -718,7 +718,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven + + ss_free(hostname); + } else { +- strncpy(host, ip, sizeof(ip)); ++ strncpy(host, ip, INET6_ADDRSTRLEN); + } + } + diff --git a/shadowsocksr-libev/patches/102-Read-listening-mode-from-config.patch b/shadowsocksr-libev/patches/102-Read-listening-mode-from-config.patch new file mode 100644 index 0000000..57f6f76 --- /dev/null +++ b/shadowsocksr-libev/patches/102-Read-listening-mode-from-config.patch @@ -0,0 +1,34 @@ +--- a/src/jconf.c ++++ b/src/jconf.c +@@ -259,6 +259,19 @@ read_jconf(const char *file) + conf.server_legacy.obfs = to_string(value); + } else if (strcmp(name, "obfs_param") == 0) { // SSR + conf.server_legacy.obfs_param = 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 { + match = 0; + } +--- a/src/redir.c ++++ b/src/redir.c +@@ -1252,6 +1252,9 @@ main(int argc, char **argv) + if (user == NULL) { + user = conf->user; + } ++ if (mode == TCP_ONLY) { ++ mode = conf->mode; ++ } + if (mtu == 0) { + mtu = conf->mtu; + } diff --git a/shadowsocksr-libev/patches/103-Add-TPROXY-support-for-TCP-ssr-redir.patch b/shadowsocksr-libev/patches/103-Add-TPROXY-support-for-TCP-ssr-redir.patch new file mode 100644 index 0000000..317d819 --- /dev/null +++ b/shadowsocksr-libev/patches/103-Add-TPROXY-support-for-TCP-ssr-redir.patch @@ -0,0 +1,154 @@ +--- a/completions/bash/ss-redir ++++ b/completions/bash/ss-redir +@@ -2,7 +2,7 @@ _ss_redir() + { + local cur prev opts ciphers + ciphers='rc4-md5 table rc4 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 and chacha20-ietf' +- opts='-s -b -p -k -f -t -m -c -a -n -u -U -v -h -A --mtu --help --mptcp -l' ++ opts='-s -b -p -k -f -t -m -c -a -n -u -U -T -v -h -A --mtu --help --mptcp -l' + cur=${COMP_WORDS[COMP_CWORD]} + prev="${COMP_WORDS[COMP_CWORD-1]}" + case "$prev" in +--- a/src/jconf.c ++++ b/src/jconf.c +@@ -338,7 +338,11 @@ read_jconf(const char *file) + check_json_value_type(value, json_boolean, + "invalid config file: option 'ipv6_first' must be a boolean"); + conf.ipv6_first = value->u.boolean; +- } ++ } else if (strcmp(name, "tcp_tproxy") == 0) { ++ check_json_value_type(value, json_boolean, ++ "invalid config file: option 'tcp_tproxy' must be a boolean"); ++ conf.tcp_tproxy = value->u.boolean; ++ } + } + } + } else { +--- a/src/jconf.h ++++ b/src/jconf.h +@@ -105,6 +105,7 @@ typedef struct { + int mtu; + int mptcp; + int ipv6_first; ++ int tcp_tproxy; + } jconf_t; + + jconf_t *read_jconf(const char *file); +--- a/src/redir.c ++++ b/src/redir.c +@@ -71,6 +71,14 @@ + #define IP6T_SO_ORIGINAL_DST 80 + #endif + ++#ifndef IP_TRANSPARENT ++#define IP_TRANSPARENT 19 ++#endif ++ ++#ifndef IPV6_TRANSPARENT ++#define IPV6_TRANSPARENT 75 ++#endif ++ + #include "includeobfs.h" // I don't want to modify makefile + #include "jconf.h" + +@@ -101,18 +109,28 @@ static struct cork_dllist inactive_profi + static listen_ctx_t *current_profile; + static struct cork_dllist all_connections; + ++static int tcp_tproxy = 0; /* use tproxy instead of redirect (for tcp) */ ++ + int + getdestaddr(int fd, struct sockaddr_storage *destaddr) + { + socklen_t socklen = sizeof(*destaddr); + int error = 0; + +- error = getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, destaddr, &socklen); +- if (error) { // Didn't find a proper way to detect IP version. +- error = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen); +- if (error) { +- return -1; +- } ++ if (tcp_tproxy) { ++ error = getsockname(fd, (void *)destaddr, &socklen); ++ } else { ++ error = getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, destaddr, &socklen); ++ if (error) { // Didn't find a proper way to detect IP version. ++ error = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen); ++ if (error) { ++ return -1; ++ } ++ } ++ } ++ ++ if (error) { ++ return -1; + } + return 0; + } +@@ -164,6 +182,23 @@ create_and_bind(const char *addr, const + if (err == 0) { + LOGI("tcp port reuse enabled"); + } ++ ++ if (tcp_tproxy) { ++ int level = 0, optname = 0; ++ if (rp->ai_family == AF_INET) { ++ level = IPPROTO_IP; ++ optname = IP_TRANSPARENT; ++ } else { ++ level = IPPROTO_IPV6; ++ optname = IPV6_TRANSPARENT; ++ } ++ ++ if (setsockopt(listen_sock, level, optname, &opt, sizeof(opt)) != 0) { ++ ERROR("setsockopt IP_TRANSPARENT"); ++ exit(EXIT_FAILURE); ++ } ++ LOGI("tcp tproxy mode enabled"); ++ } + + s = bind(listen_sock, rp->ai_addr, rp->ai_addrlen); + if (s == 0) { +@@ -1094,7 +1129,7 @@ main(int argc, char **argv) + + USE_TTY(); + +- while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:b:a:n:huUvA6" ++ while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:b:a:n:huUTvA6" + "O:o:G:g:", + long_options, &option_index)) != -1) { + switch (c) { +@@ -1169,6 +1204,9 @@ main(int argc, char **argv) + case 'U': + mode = UDP_ONLY; + break; ++ case 'T': ++ tcp_tproxy = 1; ++ break; + case 'v': + verbose = 1; + break; +@@ -1255,6 +1293,9 @@ main(int argc, char **argv) + if (mode == TCP_ONLY) { + mode = conf->mode; + } ++ if (tcp_tproxy == 0) { ++ tcp_tproxy = conf->tcp_tproxy; ++ } + if (mtu == 0) { + mtu = conf->mtu; + } +--- a/src/utils.c ++++ b/src/utils.c +@@ -342,6 +342,10 @@ usage() + #endif + printf( + " [-U] Enable UDP relay and disable TCP relay.\n"); ++#ifdef MODULE_REDIR ++ printf( ++ " [-T] Use tproxy instead of redirect (for tcp).\n"); ++#endif + #ifdef MODULE_REMOTE + printf( + " [-6] Resovle hostname to IPv6 address first.\n"); diff --git a/shadowsocksr-libev/src/server/Makefile.am b/shadowsocksr-libev/src/server/Makefile.am new file mode 100644 index 0000000..3ae8bc2 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/Makefile.in b/shadowsocksr-libev/src/server/Makefile.in new file mode 100644 index 0000000..3bfa53e --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/README.md b/shadowsocksr-libev/src/server/README.md new file mode 100644 index 0000000..ef6a20e --- /dev/null +++ b/shadowsocksr-libev/src/server/README.md @@ -0,0 +1,3 @@ +# server + +`ss-server` and `ss-check` from https://github.com/ywb94/shadowsocks-libev diff --git a/shadowsocksr-libev/src/server/acl.c b/shadowsocksr-libev/src/server/acl.c new file mode 100644 index 0000000..60d4b72 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/acl.h b/shadowsocksr-libev/src/server/acl.h new file mode 100644 index 0000000..d6f18b8 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/auth.c b/shadowsocksr-libev/src/server/auth.c new file mode 100644 index 0000000..a36257a --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/auth.h b/shadowsocksr-libev/src/server/auth.h new file mode 100644 index 0000000..f7730df --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/base64.c b/shadowsocksr-libev/src/server/base64.c new file mode 100644 index 0000000..7cf9552 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/base64.h b/shadowsocksr-libev/src/server/base64.h new file mode 100644 index 0000000..6432ba3 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/cache.c b/shadowsocksr-libev/src/server/cache.c new file mode 100644 index 0000000..c1a2995 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/cache.h b/shadowsocksr-libev/src/server/cache.h new file mode 100644 index 0000000..0ec98f5 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/common.h b/shadowsocksr-libev/src/server/common.h new file mode 100644 index 0000000..000f084 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/crc32.c b/shadowsocksr-libev/src/server/crc32.c new file mode 100644 index 0000000..6d328d2 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/encrypt.c b/shadowsocksr-libev/src/server/encrypt.c new file mode 100644 index 0000000..37dd5cd --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/encrypt.h b/shadowsocksr-libev/src/server/encrypt.h new file mode 100644 index 0000000..3bb7940 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/http.c b/shadowsocksr-libev/src/server/http.c new file mode 100644 index 0000000..3bd4a32 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/http.h b/shadowsocksr-libev/src/server/http.h new file mode 100644 index 0000000..914815a --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/http_simple.c b/shadowsocksr-libev/src/server/http_simple.c new file mode 100644 index 0000000..cee15c5 --- /dev/null +++ b/shadowsocksr-libev/src/server/http_simple.c @@ -0,0 +1,626 @@ + +#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); + } + *outdata = buf; + 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/shadowsocksr-libev/src/server/http_simple.h b/shadowsocksr-libev/src/server/http_simple.h new file mode 100644 index 0000000..cce24cc --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/jconf.c b/shadowsocksr-libev/src/server/jconf.c new file mode 100644 index 0000000..494aa5f --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/jconf.h b/shadowsocksr-libev/src/server/jconf.h new file mode 100644 index 0000000..9a7e5e3 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/json.c b/shadowsocksr-libev/src/server/json.c new file mode 100644 index 0000000..18e95ef --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/json.h b/shadowsocksr-libev/src/server/json.h new file mode 100644 index 0000000..016fc5a --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/list.c b/shadowsocksr-libev/src/server/list.c new file mode 100644 index 0000000..dde085d --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/list.h b/shadowsocksr-libev/src/server/list.h new file mode 100644 index 0000000..ab49720 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/netutils.c b/shadowsocksr-libev/src/server/netutils.c new file mode 100644 index 0000000..3a32b4d --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/netutils.h b/shadowsocksr-libev/src/server/netutils.h new file mode 100644 index 0000000..0725592 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/obfs.c b/shadowsocksr-libev/src/server/obfs.c new file mode 100644 index 0000000..5c885bf --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/obfs.h b/shadowsocksr-libev/src/server/obfs.h new file mode 100644 index 0000000..74c60c9 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/obfsutil.c b/shadowsocksr-libev/src/server/obfsutil.c new file mode 100644 index 0000000..d00959b --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/protocol.h b/shadowsocksr-libev/src/server/protocol.h new file mode 100644 index 0000000..eaa866e --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/resolv.c b/shadowsocksr-libev/src/server/resolv.c new file mode 100644 index 0000000..f580d06 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/resolv.h b/shadowsocksr-libev/src/server/resolv.h new file mode 100644 index 0000000..0552922 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/rule.c b/shadowsocksr-libev/src/server/rule.c new file mode 100644 index 0000000..8aae04e --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/rule.h b/shadowsocksr-libev/src/server/rule.h new file mode 100644 index 0000000..015bc42 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/server.c b/shadowsocksr-libev/src/server/server.c new file mode 100644 index 0000000..65b0e42 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/server.h b/shadowsocksr-libev/src/server/server.h new file mode 100644 index 0000000..4cd3cf6 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/tls.c b/shadowsocksr-libev/src/server/tls.c new file mode 100644 index 0000000..5c42216 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/tls.h b/shadowsocksr-libev/src/server/tls.h new file mode 100644 index 0000000..3998913 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/tls1.2_ticket.c b/shadowsocksr-libev/src/server/tls1.2_ticket.c new file mode 100644 index 0000000..88970c0 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/tls1.2_ticket.h b/shadowsocksr-libev/src/server/tls1.2_ticket.h new file mode 100644 index 0000000..10a57c9 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/udprelay.c b/shadowsocksr-libev/src/server/udprelay.c new file mode 100644 index 0000000..d9251ee --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/udprelay.h b/shadowsocksr-libev/src/server/udprelay.h new file mode 100644 index 0000000..89876d4 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/uthash.h b/shadowsocksr-libev/src/server/uthash.h new file mode 100644 index 0000000..45d1f9f --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/utils.c b/shadowsocksr-libev/src/server/utils.c new file mode 100644 index 0000000..14a60c7 --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/utils.h b/shadowsocksr-libev/src/server/utils.h new file mode 100644 index 0000000..0fb7f5a --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/verify.c b/shadowsocksr-libev/src/server/verify.c new file mode 100644 index 0000000..9e7393d --- /dev/null +++ b/shadowsocksr-libev/src/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/shadowsocksr-libev/src/server/verify.h b/shadowsocksr-libev/src/server/verify.h new file mode 100644 index 0000000..57c6ff9 --- /dev/null +++ b/shadowsocksr-libev/src/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