Skip to content
This repository has been archived by the owner on Dec 29, 2020. It is now read-only.

Commit

Permalink
Merge branch 'master' into manyuser
Browse files Browse the repository at this point in the history
  • Loading branch information
mengskysama committed Aug 2, 2016
2 parents 61d9343 + 8e8ee5d commit 4cbeca0
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 23 deletions.
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion debian/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
"timeout":300,
"method":"aes-256-cfb",
"fast_open": false,
"workers": 1
"workers": 1,
"prefer_ipv6": false
}
29 changes: 17 additions & 12 deletions shadowsocks/asyncdns.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,13 @@ def __str__(self):
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 = {}
Expand All @@ -262,6 +262,10 @@ def __init__(self, server_list=None):
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
Expand Down Expand Up @@ -342,17 +346,18 @@ def _handle_data(self, data):
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

Expand Down Expand Up @@ -418,14 +423,14 @@ def resolve(self, hostname, callback):
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:
Expand Down
5 changes: 3 additions & 2 deletions shadowsocks/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down
8 changes: 7 additions & 1 deletion shadowsocks/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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')
Expand Down Expand Up @@ -290,6 +294,7 @@ def print_local_help():
-k PASSWORD password
-m METHOD encryption method, default: aes-256-cfb
-t TIMEOUT timeout in seconds, default: 300
-a ONE_TIME_AUTH one time auth
--fast-open use TCP_FASTOPEN, requires Linux 3.7+
General options:
Expand Down Expand Up @@ -324,6 +329,7 @@ def print_server_help():
--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
Expand Down
60 changes: 55 additions & 5 deletions shadowsocks/tcprelay.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@

MSG_FASTOPEN = 0x20000000

# SOCKS METHOD definition
METHOD_NOAUTH = 0

# SOCKS command definition
CMD_CONNECT = 1
CMD_BIND = 2
Expand All @@ -53,7 +56,7 @@
# 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
Expand Down Expand Up @@ -93,6 +96,16 @@
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):
Expand Down Expand Up @@ -320,6 +333,7 @@ def _handle_stage_addr(self, data):
if self._is_local is False:
# spec https://shadowsocks.org/en/spec/one-time-auth.html
if self._ota_enable or addrtype & ADDRTYPE_AUTH:
self._ota_enable = True
if len(data) < header_length + ONETIMEAUTH_BYTES:
logging.warn('one time auth header is too short')
return None
Expand All @@ -330,6 +344,7 @@ def _handle_stage_addr(self, data):
if onetimeauth_verify(_hash, _data, key) is False:
logging.warn('one time auth fail')
self.destroy()
return
header_length += ONETIMEAUTH_BYTES
self._remote_address = (common.to_str(remote_addr), remote_port)
# pause reading
Expand Down Expand Up @@ -486,6 +501,44 @@ def _handle_stage_stream(self, data):
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 SOCKS 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
Expand Down Expand Up @@ -516,10 +569,7 @@ def _on_local_read(self):
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 \
Expand Down
1 change: 1 addition & 0 deletions shadowsocks/udprelay.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ def _handle_server(self):
server_addr, server_port = dest_addr, dest_port
# spec https://shadowsocks.org/en/spec/one-time-auth.html
if self._one_time_auth_enable or addrtype & ADDRTYPE_AUTH:
self._one_time_auth_enable = True
if len(data) < header_length + ONETIMEAUTH_BYTES:
logging.warn('UDP one time auth header is too short')
return
Expand Down
8 changes: 6 additions & 2 deletions tests/socksify/install.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
#!/bin/bash

if [ ! -d dante-1.4.0 ]; then
wget http://www.inet.no/dante/files/dante-1.4.0.tar.gz || exit 1
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
#
mv dante-dante-1.4.0 dante-1.4.0
fi
pushd dante-1.4.0
./configure && make -j4 && make install || exit 1
Expand Down

0 comments on commit 4cbeca0

Please sign in to comment.