Merge remote-tracking branch 'shadowsocks/master'
This commit is contained in:
commit
73c0bcadc3
7 changed files with 96 additions and 23 deletions
9
Dockerfile
Normal file
9
Dockerfile
Normal file
|
@ -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
debian/config.json
vendored
3
debian/config.json
vendored
|
@ -7,5 +7,6 @@
|
|||
"timeout":300,
|
||||
"method":"aes-256-cfb",
|
||||
"fast_open": false,
|
||||
"workers": 1
|
||||
"workers": 1,
|
||||
"prefer_ipv6": false
|
||||
}
|
|
@ -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:
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,16 @@ 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 +494,44 @@ 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 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
|
||||
|
@ -506,10 +557,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 \
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue