2016-05-06 11:23:48 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# Copyright (c) 2015-2016 The Bitcoin Core developers
|
2015-04-17 19:26:03 +02:00
|
|
|
# Distributed under the MIT software license, see the accompanying
|
|
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2019-01-07 10:55:35 +01:00
|
|
|
"""Dummy Socks5 server for testing."""
|
2016-05-06 11:23:48 +02:00
|
|
|
|
Merge #12987: tests/tools: Enable additional Python flake8 rules for automatic linting via Travis
643aad17fa Enable additional flake8 rules (practicalswift)
f020aca297 Minor Python cleanups to make flake8 pass with the new rules enabled (practicalswift)
Pull request description:
Enabled rules:
```
* E242: tab after ','
* E266: too many leading '#' for block comment
* E401: multiple imports on one line
* E402: module level import not at top of file
* E701: multiple statements on one line (colon)
* E901: SyntaxError: invalid syntax
* E902: TokenError: EOF in multi-line string
* F821: undefined name 'Foo'
* W293: blank line contains whitespace
* W606: 'async' and 'await' are reserved keywords starting with Python 3.7
```
Note to reviewers:
* In general we don't allow whitespace cleanups to existing code, but in order to allow for enabling Travis checking for these rules a few smaller whitespace cleanups had to made as part of this PR.
* Use [this `?w=1` link](https://github.com/bitcoin/bitcoin/pull/12987/files?w=1) to show a diff without whitespace changes.
Before this commit:
```
$ flake8 -qq --statistics --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
5 E266 too many leading '#' for block comment
4 E401 multiple imports on one line
6 E402 module level import not at top of file
5 E701 multiple statements on one line (colon)
1 F812 list comprehension redefines 'n' from line 159
4 F821 undefined name 'ConnectionRefusedError'
28 W293 blank line contains whitespace
```
After this commit:
```
$ flake8 -qq --statistics --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
$
```
Tree-SHA512: fc7d5e752298a50d4248afc620ee2c173135b4ca008e48e02913ac968e5a24a5fd5396926047ec62f1d580d537434ccae01f249bb2f3338fa59dc630bf97ca7a
Signed-off-by: pasta <pasta@dashboost.org>
2018-04-16 17:49:49 +02:00
|
|
|
import socket
|
|
|
|
import threading
|
|
|
|
import queue
|
2017-03-19 10:13:45 +01:00
|
|
|
import logging
|
|
|
|
|
|
|
|
logger = logging.getLogger("TestFramework.socks5")
|
2015-04-17 19:26:03 +02:00
|
|
|
|
Merge #12987: tests/tools: Enable additional Python flake8 rules for automatic linting via Travis
643aad17fa Enable additional flake8 rules (practicalswift)
f020aca297 Minor Python cleanups to make flake8 pass with the new rules enabled (practicalswift)
Pull request description:
Enabled rules:
```
* E242: tab after ','
* E266: too many leading '#' for block comment
* E401: multiple imports on one line
* E402: module level import not at top of file
* E701: multiple statements on one line (colon)
* E901: SyntaxError: invalid syntax
* E902: TokenError: EOF in multi-line string
* F821: undefined name 'Foo'
* W293: blank line contains whitespace
* W606: 'async' and 'await' are reserved keywords starting with Python 3.7
```
Note to reviewers:
* In general we don't allow whitespace cleanups to existing code, but in order to allow for enabling Travis checking for these rules a few smaller whitespace cleanups had to made as part of this PR.
* Use [this `?w=1` link](https://github.com/bitcoin/bitcoin/pull/12987/files?w=1) to show a diff without whitespace changes.
Before this commit:
```
$ flake8 -qq --statistics --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
5 E266 too many leading '#' for block comment
4 E401 multiple imports on one line
6 E402 module level import not at top of file
5 E701 multiple statements on one line (colon)
1 F812 list comprehension redefines 'n' from line 159
4 F821 undefined name 'ConnectionRefusedError'
28 W293 blank line contains whitespace
```
After this commit:
```
$ flake8 -qq --statistics --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
$
```
Tree-SHA512: fc7d5e752298a50d4248afc620ee2c173135b4ca008e48e02913ac968e5a24a5fd5396926047ec62f1d580d537434ccae01f249bb2f3338fa59dc630bf97ca7a
Signed-off-by: pasta <pasta@dashboost.org>
2018-04-16 17:49:49 +02:00
|
|
|
# Protocol constants
|
2015-04-17 19:26:03 +02:00
|
|
|
class Command:
|
|
|
|
CONNECT = 0x01
|
|
|
|
|
|
|
|
class AddressType:
|
|
|
|
IPV4 = 0x01
|
|
|
|
DOMAINNAME = 0x03
|
|
|
|
IPV6 = 0x04
|
|
|
|
|
Merge #12987: tests/tools: Enable additional Python flake8 rules for automatic linting via Travis
643aad17fa Enable additional flake8 rules (practicalswift)
f020aca297 Minor Python cleanups to make flake8 pass with the new rules enabled (practicalswift)
Pull request description:
Enabled rules:
```
* E242: tab after ','
* E266: too many leading '#' for block comment
* E401: multiple imports on one line
* E402: module level import not at top of file
* E701: multiple statements on one line (colon)
* E901: SyntaxError: invalid syntax
* E902: TokenError: EOF in multi-line string
* F821: undefined name 'Foo'
* W293: blank line contains whitespace
* W606: 'async' and 'await' are reserved keywords starting with Python 3.7
```
Note to reviewers:
* In general we don't allow whitespace cleanups to existing code, but in order to allow for enabling Travis checking for these rules a few smaller whitespace cleanups had to made as part of this PR.
* Use [this `?w=1` link](https://github.com/bitcoin/bitcoin/pull/12987/files?w=1) to show a diff without whitespace changes.
Before this commit:
```
$ flake8 -qq --statistics --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
5 E266 too many leading '#' for block comment
4 E401 multiple imports on one line
6 E402 module level import not at top of file
5 E701 multiple statements on one line (colon)
1 F812 list comprehension redefines 'n' from line 159
4 F821 undefined name 'ConnectionRefusedError'
28 W293 blank line contains whitespace
```
After this commit:
```
$ flake8 -qq --statistics --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
$
```
Tree-SHA512: fc7d5e752298a50d4248afc620ee2c173135b4ca008e48e02913ac968e5a24a5fd5396926047ec62f1d580d537434ccae01f249bb2f3338fa59dc630bf97ca7a
Signed-off-by: pasta <pasta@dashboost.org>
2018-04-16 17:49:49 +02:00
|
|
|
# Utility functions
|
2015-04-17 19:26:03 +02:00
|
|
|
def recvall(s, n):
|
2019-01-07 10:55:35 +01:00
|
|
|
"""Receive n bytes from a socket, or fail."""
|
2015-04-17 19:26:03 +02:00
|
|
|
rv = bytearray()
|
|
|
|
while n > 0:
|
|
|
|
d = s.recv(n)
|
|
|
|
if not d:
|
|
|
|
raise IOError('Unexpected end of stream')
|
|
|
|
rv.extend(d)
|
|
|
|
n -= len(d)
|
|
|
|
return rv
|
|
|
|
|
Merge #12987: tests/tools: Enable additional Python flake8 rules for automatic linting via Travis
643aad17fa Enable additional flake8 rules (practicalswift)
f020aca297 Minor Python cleanups to make flake8 pass with the new rules enabled (practicalswift)
Pull request description:
Enabled rules:
```
* E242: tab after ','
* E266: too many leading '#' for block comment
* E401: multiple imports on one line
* E402: module level import not at top of file
* E701: multiple statements on one line (colon)
* E901: SyntaxError: invalid syntax
* E902: TokenError: EOF in multi-line string
* F821: undefined name 'Foo'
* W293: blank line contains whitespace
* W606: 'async' and 'await' are reserved keywords starting with Python 3.7
```
Note to reviewers:
* In general we don't allow whitespace cleanups to existing code, but in order to allow for enabling Travis checking for these rules a few smaller whitespace cleanups had to made as part of this PR.
* Use [this `?w=1` link](https://github.com/bitcoin/bitcoin/pull/12987/files?w=1) to show a diff without whitespace changes.
Before this commit:
```
$ flake8 -qq --statistics --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
5 E266 too many leading '#' for block comment
4 E401 multiple imports on one line
6 E402 module level import not at top of file
5 E701 multiple statements on one line (colon)
1 F812 list comprehension redefines 'n' from line 159
4 F821 undefined name 'ConnectionRefusedError'
28 W293 blank line contains whitespace
```
After this commit:
```
$ flake8 -qq --statistics --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
$
```
Tree-SHA512: fc7d5e752298a50d4248afc620ee2c173135b4ca008e48e02913ac968e5a24a5fd5396926047ec62f1d580d537434ccae01f249bb2f3338fa59dc630bf97ca7a
Signed-off-by: pasta <pasta@dashboost.org>
2018-04-16 17:49:49 +02:00
|
|
|
# Implementation classes
|
2017-10-17 21:03:09 +02:00
|
|
|
class Socks5Configuration():
|
2019-01-07 10:55:35 +01:00
|
|
|
"""Proxy configuration."""
|
2015-04-17 19:26:03 +02:00
|
|
|
def __init__(self):
|
|
|
|
self.addr = None # Bind address (must be set)
|
|
|
|
self.af = socket.AF_INET # Bind address family
|
|
|
|
self.unauth = False # Support unauthenticated
|
|
|
|
self.auth = False # Support authentication
|
|
|
|
|
2017-10-17 21:03:09 +02:00
|
|
|
class Socks5Command():
|
2019-01-07 10:55:35 +01:00
|
|
|
"""Information about an incoming socks5 command."""
|
2015-04-17 19:26:03 +02:00
|
|
|
def __init__(self, cmd, atyp, addr, port, username, password):
|
|
|
|
self.cmd = cmd # Command (one of Command.*)
|
|
|
|
self.atyp = atyp # Address type (one of AddressType.*)
|
|
|
|
self.addr = addr # Address
|
|
|
|
self.port = port # Port to connect to
|
|
|
|
self.username = username
|
|
|
|
self.password = password
|
|
|
|
def __repr__(self):
|
|
|
|
return 'Socks5Command(%s,%s,%s,%s,%s,%s)' % (self.cmd, self.atyp, self.addr, self.port, self.username, self.password)
|
|
|
|
|
2017-10-17 21:03:09 +02:00
|
|
|
class Socks5Connection():
|
2018-11-07 18:20:01 +01:00
|
|
|
def __init__(self, serv, conn):
|
2015-04-17 19:26:03 +02:00
|
|
|
self.serv = serv
|
|
|
|
self.conn = conn
|
|
|
|
|
|
|
|
def handle(self):
|
2019-01-07 10:55:35 +01:00
|
|
|
"""Handle socks5 request according to RFC192."""
|
2015-04-17 19:26:03 +02:00
|
|
|
try:
|
|
|
|
# Verify socks version
|
|
|
|
ver = recvall(self.conn, 1)[0]
|
|
|
|
if ver != 0x05:
|
|
|
|
raise IOError('Invalid socks version %i' % ver)
|
|
|
|
# Choose authentication method
|
|
|
|
nmethods = recvall(self.conn, 1)[0]
|
|
|
|
methods = bytearray(recvall(self.conn, nmethods))
|
|
|
|
method = None
|
|
|
|
if 0x02 in methods and self.serv.conf.auth:
|
|
|
|
method = 0x02 # username/password
|
|
|
|
elif 0x00 in methods and self.serv.conf.unauth:
|
|
|
|
method = 0x00 # unauthenticated
|
|
|
|
if method is None:
|
|
|
|
raise IOError('No supported authentication method was offered')
|
|
|
|
# Send response
|
|
|
|
self.conn.sendall(bytearray([0x05, method]))
|
|
|
|
# Read authentication (optional)
|
|
|
|
username = None
|
|
|
|
password = None
|
|
|
|
if method == 0x02:
|
|
|
|
ver = recvall(self.conn, 1)[0]
|
|
|
|
if ver != 0x01:
|
|
|
|
raise IOError('Invalid auth packet version %i' % ver)
|
|
|
|
ulen = recvall(self.conn, 1)[0]
|
|
|
|
username = str(recvall(self.conn, ulen))
|
|
|
|
plen = recvall(self.conn, 1)[0]
|
|
|
|
password = str(recvall(self.conn, plen))
|
|
|
|
# Send authentication response
|
|
|
|
self.conn.sendall(bytearray([0x01, 0x00]))
|
|
|
|
|
|
|
|
# Read connect request
|
2017-08-28 22:53:34 +02:00
|
|
|
ver, cmd, _, atyp = recvall(self.conn, 4)
|
2015-04-17 19:26:03 +02:00
|
|
|
if ver != 0x05:
|
|
|
|
raise IOError('Invalid socks version %i in connect request' % ver)
|
|
|
|
if cmd != Command.CONNECT:
|
|
|
|
raise IOError('Unhandled command %i in connect request' % cmd)
|
|
|
|
|
|
|
|
if atyp == AddressType.IPV4:
|
|
|
|
addr = recvall(self.conn, 4)
|
|
|
|
elif atyp == AddressType.DOMAINNAME:
|
|
|
|
n = recvall(self.conn, 1)[0]
|
2016-04-10 16:54:28 +02:00
|
|
|
addr = recvall(self.conn, n)
|
2015-04-17 19:26:03 +02:00
|
|
|
elif atyp == AddressType.IPV6:
|
|
|
|
addr = recvall(self.conn, 16)
|
|
|
|
else:
|
|
|
|
raise IOError('Unknown address type %i' % atyp)
|
|
|
|
port_hi,port_lo = recvall(self.conn, 2)
|
|
|
|
port = (port_hi << 8) | port_lo
|
|
|
|
|
|
|
|
# Send dummy response
|
|
|
|
self.conn.sendall(bytearray([0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
|
|
|
|
|
|
|
|
cmdin = Socks5Command(cmd, atyp, addr, port, username, password)
|
|
|
|
self.serv.queue.put(cmdin)
|
2017-03-19 10:13:45 +01:00
|
|
|
logger.info('Proxy: %s', cmdin)
|
2015-04-17 19:26:03 +02:00
|
|
|
# Fall through to disconnect
|
2016-03-19 21:36:32 +01:00
|
|
|
except Exception as e:
|
2017-03-19 10:13:45 +01:00
|
|
|
logger.exception("socks5 request handling failed.")
|
2015-04-17 19:26:03 +02:00
|
|
|
self.serv.queue.put(e)
|
|
|
|
finally:
|
|
|
|
self.conn.close()
|
|
|
|
|
2017-10-17 21:03:09 +02:00
|
|
|
class Socks5Server():
|
2015-04-17 19:26:03 +02:00
|
|
|
def __init__(self, conf):
|
|
|
|
self.conf = conf
|
|
|
|
self.s = socket.socket(conf.af)
|
|
|
|
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
|
|
self.s.bind(conf.addr)
|
|
|
|
self.s.listen(5)
|
|
|
|
self.running = False
|
|
|
|
self.thread = None
|
2016-05-06 11:23:48 +02:00
|
|
|
self.queue = queue.Queue() # report connections and exceptions to client
|
2015-04-17 19:26:03 +02:00
|
|
|
|
|
|
|
def run(self):
|
|
|
|
while self.running:
|
2018-11-07 18:20:01 +01:00
|
|
|
(sockconn, _) = self.s.accept()
|
2015-04-17 19:26:03 +02:00
|
|
|
if self.running:
|
2018-11-07 18:20:01 +01:00
|
|
|
conn = Socks5Connection(self, sockconn)
|
2015-04-17 19:26:03 +02:00
|
|
|
thread = threading.Thread(None, conn.handle)
|
|
|
|
thread.daemon = True
|
|
|
|
thread.start()
|
Merge #12987: tests/tools: Enable additional Python flake8 rules for automatic linting via Travis
643aad17fa Enable additional flake8 rules (practicalswift)
f020aca297 Minor Python cleanups to make flake8 pass with the new rules enabled (practicalswift)
Pull request description:
Enabled rules:
```
* E242: tab after ','
* E266: too many leading '#' for block comment
* E401: multiple imports on one line
* E402: module level import not at top of file
* E701: multiple statements on one line (colon)
* E901: SyntaxError: invalid syntax
* E902: TokenError: EOF in multi-line string
* F821: undefined name 'Foo'
* W293: blank line contains whitespace
* W606: 'async' and 'await' are reserved keywords starting with Python 3.7
```
Note to reviewers:
* In general we don't allow whitespace cleanups to existing code, but in order to allow for enabling Travis checking for these rules a few smaller whitespace cleanups had to made as part of this PR.
* Use [this `?w=1` link](https://github.com/bitcoin/bitcoin/pull/12987/files?w=1) to show a diff without whitespace changes.
Before this commit:
```
$ flake8 -qq --statistics --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
5 E266 too many leading '#' for block comment
4 E401 multiple imports on one line
6 E402 module level import not at top of file
5 E701 multiple statements on one line (colon)
1 F812 list comprehension redefines 'n' from line 159
4 F821 undefined name 'ConnectionRefusedError'
28 W293 blank line contains whitespace
```
After this commit:
```
$ flake8 -qq --statistics --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 .
$
```
Tree-SHA512: fc7d5e752298a50d4248afc620ee2c173135b4ca008e48e02913ac968e5a24a5fd5396926047ec62f1d580d537434ccae01f249bb2f3338fa59dc630bf97ca7a
Signed-off-by: pasta <pasta@dashboost.org>
2018-04-16 17:49:49 +02:00
|
|
|
|
2015-04-17 19:26:03 +02:00
|
|
|
def start(self):
|
|
|
|
assert(not self.running)
|
|
|
|
self.running = True
|
|
|
|
self.thread = threading.Thread(None, self.run)
|
|
|
|
self.thread.daemon = True
|
|
|
|
self.thread.start()
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
self.running = False
|
|
|
|
# connect to self to end run loop
|
|
|
|
s = socket.socket(self.conf.af)
|
|
|
|
s.connect(self.conf.addr)
|
|
|
|
s.close()
|
|
|
|
self.thread.join()
|
|
|
|
|