From b276d52735ce848202b8d1ea82e1f088216c0655 Mon Sep 17 00:00:00 2001 From: htc Date: Sat, 20 Feb 2016 10:37:25 +0800 Subject: [PATCH 1/7] Add support for option 'prefer-ipv6' --- debian/config.json | 3 ++- shadowsocks/asyncdns.py | 29 +++++++++++++++++------------ shadowsocks/server.py | 5 +++-- shadowsocks/shell.py | 7 ++++++- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/debian/config.json b/debian/config.json index 35cb14a..7846cb2 100644 --- a/debian/config.json +++ b/debian/config.json @@ -7,5 +7,6 @@ "timeout":300, "method":"aes-256-cfb", "fast_open": false, - "workers": 1 + "workers": 1, + "prefer_ipv6": false } \ No newline at end of file diff --git a/shadowsocks/asyncdns.py b/shadowsocks/asyncdns.py index 25a0e6b..44528d7 100644 --- a/shadowsocks/asyncdns.py +++ b/shadowsocks/asyncdns.py @@ -242,13 +242,13 @@ class DNSResponse(object): return '%s: %s' % (self.hostname, str(self.answers)) -STATUS_IPV4 = 0 -STATUS_IPV6 = 1 +STATUS_FIRST = 0 +STATUS_SECOND = 1 class DNSResolver(object): - def __init__(self, server_list=None): + def __init__(self, server_list=None, prefer_ipv6=False): self._loop = None self._hosts = {} self._hostname_status = {} @@ -261,6 +261,10 @@ class DNSResolver(object): self._parse_resolv() else: self._servers = server_list + if prefer_ipv6: + self._QTYPES = [QTYPE_AAAA, QTYPE_A] + else: + self._QTYPES = [QTYPE_A, QTYPE_AAAA] self._parse_hosts() # TODO monitor hosts change and reload hosts # TODO parse /etc/gai.conf and follow its rules @@ -341,17 +345,18 @@ class DNSResolver(object): answer[2] == QCLASS_IN: ip = answer[0] break - if not ip and self._hostname_status.get(hostname, STATUS_IPV6) \ - == STATUS_IPV4: - self._hostname_status[hostname] = STATUS_IPV6 - self._send_req(hostname, QTYPE_AAAA) + if not ip and self._hostname_status.get(hostname, STATUS_SECOND) \ + == STATUS_FIRST: + self._hostname_status[hostname] = STATUS_SECOND + self._send_req(hostname, self._QTYPES[1]) else: if ip: self._cache[hostname] = ip self._call_callback(hostname, ip) - elif self._hostname_status.get(hostname, None) == STATUS_IPV6: + elif self._hostname_status.get(hostname, None) \ + == STATUS_SECOND: for question in response.questions: - if question[1] == QTYPE_AAAA: + if question[1] == self._QTYPES[1]: self._call_callback(hostname, None) break @@ -417,14 +422,14 @@ class DNSResolver(object): return arr = self._hostname_to_cb.get(hostname, None) if not arr: - self._hostname_status[hostname] = STATUS_IPV4 - self._send_req(hostname, QTYPE_A) + self._hostname_status[hostname] = STATUS_FIRST + self._send_req(hostname, self._QTYPES[0]) self._hostname_to_cb[hostname] = [callback] self._cb_to_hostname[callback] = hostname else: arr.append(callback) # TODO send again only if waited too long - self._send_req(hostname, QTYPE_A) + self._send_req(hostname, self._QTYPES[0]) def close(self): if self._sock: diff --git a/shadowsocks/server.py b/shadowsocks/server.py index 1be4c0f..4dc5621 100755 --- a/shadowsocks/server.py +++ b/shadowsocks/server.py @@ -58,9 +58,10 @@ def main(): udp_servers = [] if 'dns_server' in config: # allow override settings in resolv.conf - dns_resolver = asyncdns.DNSResolver(config['dns_server']) + dns_resolver = asyncdns.DNSResolver(config['dns_server'], + config['prefer_ipv6']) else: - dns_resolver = asyncdns.DNSResolver() + dns_resolver = asyncdns.DNSResolver(prefer_ipv6=config['prefer_ipv6']) port_password = config['port_password'] del config['port_password'] diff --git a/shadowsocks/shell.py b/shadowsocks/shell.py index 380bf25..42efbc0 100644 --- a/shadowsocks/shell.py +++ b/shadowsocks/shell.py @@ -137,7 +137,8 @@ def get_config(is_local): else: shortopts = 'hd:s:p:k:m:c:t:vqa' longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers=', - 'forbidden-ip=', 'user=', 'manager-address=', 'version'] + 'forbidden-ip=', 'user=', 'manager-address=', 'version', + 'prefer-ipv6'] try: config_path = find_config() optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) @@ -207,6 +208,8 @@ def get_config(is_local): elif key == '-q': v_count -= 1 config['verbose'] = v_count + elif key == '--prefer-ipv6': + config['prefer_ipv6'] = True except getopt.GetoptError as e: print(e, file=sys.stderr) print_help(is_local) @@ -229,6 +232,7 @@ def get_config(is_local): config['local_address'] = to_str(config.get('local_address', '127.0.0.1')) config['local_port'] = config.get('local_port', 1080) config['one_time_auth'] = config.get('one_time_auth', False) + config['prefer_ipv6'] = config.get('prefer_ipv6', False) if is_local: if config.get('server', None) is None: logging.error('server addr not specified') @@ -324,6 +328,7 @@ Proxy options: --workers WORKERS number of workers, available on Unix/Linux --forbidden-ip IPLIST comma seperated IP list forbidden to connect --manager-address ADDR optional server manager UDP address, see wiki + --prefer-ipv6 resolve ipv6 address first General options: -h, --help show this help message and exit From 9d6b9fcde0f1e6511a7ef067868fafe9c0c672a3 Mon Sep 17 00:00:00 2001 From: htc550605125 Date: Tue, 23 Feb 2016 20:47:04 -0500 Subject: [PATCH 2/7] add dockerfile --- Dockerfile | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..36ddaa0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM stackbrew/debian:jessie +RUN apt-get update +RUN apt-get install -y python python-setuptools + +ADD . /shadowsocks + +WORKDIR /shadowsocks +RUN python setup.py install +CMD ssserver From 1f24b31f891e237cf17949dfc42e280bc077c8cc Mon Sep 17 00:00:00 2001 From: mengskysama Date: Sat, 16 Apr 2016 11:07:22 +0800 Subject: [PATCH 3/7] Update install.sh replace dante corrupt src --- tests/socksify/install.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/socksify/install.sh b/tests/socksify/install.sh index 8eff72d..9de6458 100755 --- a/tests/socksify/install.sh +++ b/tests/socksify/install.sh @@ -1,8 +1,11 @@ #!/bin/bash if [ ! -d dante-1.4.0 ]; then - wget http://www.inet.no/dante/files/dante-1.4.0.tar.gz || exit 1 + #wget http://www.inet.no/dante/files/dante-1.4.0.tar.gz || exit 1 + wget https://codeload.github.com/notpeter/dante/tar.gz/dante-1.4.0 -O dante-1.4.0.tar.gz || exit 1 tar xf dante-1.4.0.tar.gz || exit 1 + # + mv dante-dante-1.4.0 dante-1.4.0 fi pushd dante-1.4.0 ./configure && make -j4 && make install || exit 1 From bb53b0cb901d87daafae713bba902ecb6e713506 Mon Sep 17 00:00:00 2001 From: mengskysama Date: Sat, 16 Apr 2016 11:46:06 +0800 Subject: [PATCH 4/7] fix build dante cache and old src corrupt and slow --- tests/socksify/install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/socksify/install.sh b/tests/socksify/install.sh index 9de6458..86c4e1d 100755 --- a/tests/socksify/install.sh +++ b/tests/socksify/install.sh @@ -1,6 +1,7 @@ #!/bin/bash -if [ ! -d dante-1.4.0 ]; then +if [ ! -d dante-1.4.0 ] || [ ! -d dante-1.4.0/configure ]; then + rm dante-1.4.0 -rf #wget http://www.inet.no/dante/files/dante-1.4.0.tar.gz || exit 1 wget https://codeload.github.com/notpeter/dante/tar.gz/dante-1.4.0 -O dante-1.4.0.tar.gz || exit 1 tar xf dante-1.4.0.tar.gz || exit 1 From aae990a2fc6ea4c5709e1a44f1e02ca1a2be29fe Mon Sep 17 00:00:00 2001 From: watermeter Date: Sat, 16 Apr 2016 11:54:12 +0800 Subject: [PATCH 5/7] Add auth method check for TCP socks connections (#528) * Add auth method check for TCP socks connections * remove meaningless return at end of function * remove extra blank lines --- shadowsocks/tcprelay.py | 54 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 6430f26..cf45351 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -36,6 +36,9 @@ TIMEOUTS_CLEAN_SIZE = 512 MSG_FASTOPEN = 0x20000000 +# SOCKS METHOD definition +METHOD_NOAUTH = 0 + # SOCKS command definition CMD_CONNECT = 1 CMD_BIND = 2 @@ -52,7 +55,7 @@ CMD_UDP_ASSOCIATE = 3 # for each handler, it could be at one of several stages: # as sslocal: -# stage 0 SOCKS hello received from local, send hello to local +# stage 0 auth METHOD received from local, reply with selection message # stage 1 addr received from local, query DNS for remote # stage 2 UDP assoc # stage 3 DNS resolved, connect to remote @@ -92,6 +95,14 @@ WAIT_STATUS_READWRITING = WAIT_STATUS_READING | WAIT_STATUS_WRITING BUF_SIZE = 32 * 1024 +# helper exceptions for TCPRelayHandler + +class BadSocksHeader(Exception): + pass + +class NoAcceptableMethods(Exception): + pass + class TCPRelayHandler(object): def __init__(self, server, fd_to_handlers, loop, local_sock, config, dns_resolver, is_local): @@ -481,6 +492,42 @@ class TCPRelayHandler(object): self._write_to_sock(data, self._remote_sock) return + def _check_auth_method(self, data): + # VER, NMETHODS, and at least 1 METHODS + if len(data) < 3: + logging.warning('method selection header too short') + raise BadSocksHeader + socks_version = common.ord(data[0]) + nmethods = common.ord(data[1]) + if socks_version != 5: + logging.warning('unsupported SOCKS protocol version ' + str(socks_version)) + raise BadSocksHeader + if nmethods < 1 or len(data) != nmethods + 2: + logging.warning('NMETHODS and number of METHODS mismatch') + raise BadSocksHeader + noauth_exist = False + for method in data[2:]: + if common.ord(method) == METHOD_NOAUTH: + noauth_exist = True + break + if not noauth_exist: + logging.warning('none of METHOD\'s requested by client is supported') + raise NoAcceptableMethods + + def _handle_stage_init(self, data): + try: + self._check_auth_method(data) + except BadSocksHeader: + self.destroy() + return + except NoAcceptableMethods: + self._write_to_sock(b'\x05\xff', self._local_sock) + self.destroy() + return + + self._write_to_sock(b'\x05\00', self._local_sock) + self._stage = STAGE_ADDR + def _on_local_read(self): # handle all local read events and dispatch them to methods for # each stage @@ -506,10 +553,7 @@ class TCPRelayHandler(object): self._handle_stage_stream(data) return elif is_local and self._stage == STAGE_INIT: - # TODO check auth method - self._write_to_sock(b'\x05\00', self._local_sock) - self._stage = STAGE_ADDR - return + self._handle_stage_init(data) elif self._stage == STAGE_CONNECTING: self._handle_stage_connecting(data) elif (is_local and self._stage == STAGE_ADDR) or \ From f62a550e9f8351ab3dc9d57e0fb554d5022b3d81 Mon Sep 17 00:00:00 2001 From: mengskysama Date: Sat, 16 Apr 2016 12:13:59 +0800 Subject: [PATCH 6/7] pep8 --- shadowsocks/tcprelay.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index cf45351..5100f1c 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -100,9 +100,11 @@ BUF_SIZE = 32 * 1024 class BadSocksHeader(Exception): pass + class NoAcceptableMethods(Exception): pass + class TCPRelayHandler(object): def __init__(self, server, fd_to_handlers, loop, local_sock, config, dns_resolver, is_local): @@ -500,7 +502,8 @@ class TCPRelayHandler(object): socks_version = common.ord(data[0]) nmethods = common.ord(data[1]) if socks_version != 5: - logging.warning('unsupported SOCKS protocol version ' + str(socks_version)) + logging.warning('unsupported SOCKS protocol version ' + + str(socks_version)) raise BadSocksHeader if nmethods < 1 or len(data) != nmethods + 2: logging.warning('NMETHODS and number of METHODS mismatch') @@ -511,7 +514,8 @@ class TCPRelayHandler(object): noauth_exist = True break if not noauth_exist: - logging.warning('none of METHOD\'s requested by client is supported') + logging.warning('none of SOCKS METHOD\'s ' + 'requested by client is supported') raise NoAcceptableMethods def _handle_stage_init(self, data): From 9844ba9dc75028e1bbe7ea02e7ba7f4b181bf178 Mon Sep 17 00:00:00 2001 From: mengskysama Date: Sat, 16 Apr 2016 12:19:32 +0800 Subject: [PATCH 7/7] pep8 --- shadowsocks/tcprelay.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index 5100f1c..1387ac7 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -502,7 +502,7 @@ class TCPRelayHandler(object): socks_version = common.ord(data[0]) nmethods = common.ord(data[1]) if socks_version != 5: - logging.warning('unsupported SOCKS protocol version ' + + logging.warning('unsupported SOCKS protocol version ' + str(socks_version)) raise BadSocksHeader if nmethods < 1 or len(data) != nmethods + 2: