mirror of
https://github.com/sshuttle/sshuttle.git
synced 2024-11-21 23:43:18 +01:00
Adds support for tunneling specific port ranges (#144)
* Adds support for tunneling specific port ranges This set of changes implements the ability of specifying a port or port range for an IP or subnet to only tunnel those ports for that subnet. Also supports excluding a port or port range for a given IP or subnet. When, for a given subnet, there are intercepting ranges being added and excluded, the most specific, i.e., smaller range, takes precedence. In case of a tie the exclusion wins. For different subnets, the most specific, i.e., largest swidth, takes precedence independent of any eventual port ranges. Examples: Tunnels all traffic to the 188.0.0.0/8 subnet except those to port 443. ``` sshuttle -r <server> 188.0.0.0/8 -x 188.0.0.0/8:443 ``` Only tunnels traffic to port 80 of the 188.0.0.0/8 subnet. ``` sshuttle -r <server> 188.0.0.0/8:80 ``` Tunnels traffic to the 188.0.0.0/8 subnet and the port range that goes from 80 to 89. ``` sshuttle -r <server> 188.0.0.0/8:80-89 -x 188.0.0.0/8:80-90 ``` * Allow subnets to be specified with domain names Simplifies the implementation of address parsing by using socket.getaddrinfo(), which can handle domain resolution, IPv4 and IPv6 addresses. This was proposed and mostly implemented by @DavidBuchanan314 in #146. Signed-off-by: David Buchanan <DavidBuchanan314@users.noreply.github.com> Signed-off-by: João Vieira <vieira@yubo.be> * Also use getaddrinfo for parsing listen addr:port * Fixes tests for tunneling a port range * Updates documentation to include port/port range Adds some examples with subnet:port and subnet:port-port. Also clarifies the versions of Python supported on the server while maintaining the recommendation for Python 2.7, 3.5 or later. Mentions support for pfSense. * In Py2 only named arguments may follow *expression Fixes issue in Python 2.7 where *expression may only be followed by named arguments. * Use right regex to extract ip4/6, mask and ports * Tests for parse_subnetport
This commit is contained in:
parent
ef83a5c573
commit
c4a41ada09
@ -31,11 +31,18 @@ Options
|
|||||||
.. option:: subnets
|
.. option:: subnets
|
||||||
|
|
||||||
A list of subnets to route over the VPN, in the form
|
A list of subnets to route over the VPN, in the form
|
||||||
``a.b.c.d[/width]``. Valid examples are 1.2.3.4 (a
|
``a.b.c.d[/width][port[-port]]``. Valid examples are 1.2.3.4 (a
|
||||||
single IP address), 1.2.3.4/32 (equivalent to 1.2.3.4),
|
single IP address), 1.2.3.4/32 (equivalent to 1.2.3.4),
|
||||||
1.2.3.0/24 (a 24-bit subnet, ie. with a 255.255.255.0
|
1.2.3.0/24 (a 24-bit subnet, ie. with a 255.255.255.0
|
||||||
netmask), and 0/0 ('just route everything through the
|
netmask), and 0/0 ('just route everything through the
|
||||||
VPN').
|
VPN'). Any of the previous examples are also valid if you append
|
||||||
|
a port or a port range, so 1.2.3.4:8000 will only tunnel traffic
|
||||||
|
that has as the destination port 8000 of 1.2.3.4 and
|
||||||
|
1.2.3.0/24:8000-9000 will tunnel traffic going to any port between
|
||||||
|
8000 and 9000 (inclusive) for all IPs in the 1.2.3.0/24 subnet.
|
||||||
|
It is also possible to use a name in which case the first IP it resolves
|
||||||
|
to during startup will be routed over the VPN. Valid examples are
|
||||||
|
example.com, example.com:8000 and example.com:8000-9000.
|
||||||
|
|
||||||
.. option:: --method [auto|nat|tproxy|pf]
|
.. option:: --method [auto|nat|tproxy|pf]
|
||||||
|
|
||||||
@ -54,9 +61,11 @@ Options
|
|||||||
connections from other machines on your network (ie. to
|
connections from other machines on your network (ie. to
|
||||||
run :program:`sshuttle` on a router) try enabling IP Forwarding in
|
run :program:`sshuttle` on a router) try enabling IP Forwarding in
|
||||||
your kernel, then using ``--listen 0.0.0.0:0``.
|
your kernel, then using ``--listen 0.0.0.0:0``.
|
||||||
|
You can use any name resolving to an IP address of the machine running
|
||||||
|
:program:`sshuttle`, e.g. ``--listen localhost``.
|
||||||
|
|
||||||
For the tproxy method this can be an IPv6 address. Use this option twice if
|
For the tproxy and pf methods this can be an IPv6 address. Use this option
|
||||||
required, to provide both IPv4 and IPv6 addresses.
|
twice if required, to provide both IPv4 and IPv6 addresses.
|
||||||
|
|
||||||
.. option:: -H, --auto-hosts
|
.. option:: -H, --auto-hosts
|
||||||
|
|
||||||
@ -176,7 +185,7 @@ Options
|
|||||||
|
|
||||||
.. option:: --disable-ipv6
|
.. option:: --disable-ipv6
|
||||||
|
|
||||||
If using the tproxy method, this will disable IPv6 support.
|
If using tproxy or pf methods, this will disable IPv6 support.
|
||||||
|
|
||||||
.. option:: --firewall
|
.. option:: --firewall
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ Overview
|
|||||||
As far as I know, sshuttle is the only program that solves the following
|
As far as I know, sshuttle is the only program that solves the following
|
||||||
common case:
|
common case:
|
||||||
|
|
||||||
- Your client machine (or router) is Linux, FreeBSD, or MacOS.
|
- Your client machine (or router) is Linux, MacOS, FreeBSD, OpenBSD or pfSense.
|
||||||
|
|
||||||
- You have access to a remote network via ssh.
|
- You have access to a remote network via ssh.
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ order to get the ``recvmsg()`` function. See :doc:`tproxy` for more
|
|||||||
information.
|
information.
|
||||||
|
|
||||||
|
|
||||||
MacOS / FreeBSD / OpenBSD
|
MacOS / FreeBSD / OpenBSD / pfSense
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Method: pf
|
Method: pf
|
||||||
|
|
||||||
@ -65,8 +65,9 @@ cmd.exe with Administrator access. See :doc:`windows` for more information.
|
|||||||
|
|
||||||
Server side Requirements
|
Server side Requirements
|
||||||
------------------------
|
------------------------
|
||||||
Server requirements are more relaxed, however it is recommended that you use
|
The server can run in any version of Python between 2.4 and 3.6.
|
||||||
Python 2.7 or Python 3.5.
|
However it is recommended that you use Python 2.7, Python 3.5 or later whenever
|
||||||
|
possible as support for older versions might be dropped in the future.
|
||||||
|
|
||||||
|
|
||||||
Additional Suggested Software
|
Additional Suggested Software
|
||||||
|
@ -255,12 +255,13 @@ class FirewallClient:
|
|||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.pfile.write(b'ROUTES\n')
|
self.pfile.write(b'ROUTES\n')
|
||||||
for (family, ip, width) in self.subnets_include + self.auto_nets:
|
for (family, ip, width, fport, lport) \
|
||||||
self.pfile.write(b'%d,%d,0,%s\n'
|
in self.subnets_include + self.auto_nets:
|
||||||
% (family, width, ip.encode("ASCII")))
|
self.pfile.write(b'%d,%d,0,%s,%d,%d\n'
|
||||||
for (family, ip, width) in self.subnets_exclude:
|
% (family, width, ip.encode("ASCII"), fport, lport))
|
||||||
self.pfile.write(b'%d,%d,1,%s\n'
|
for (family, ip, width, fport, lport) in self.subnets_exclude:
|
||||||
% (family, width, ip.encode("ASCII")))
|
self.pfile.write(b'%d,%d,1,%s,%d,%d\n'
|
||||||
|
% (family, width, ip.encode("ASCII"), fport, lport))
|
||||||
|
|
||||||
self.pfile.write(b'NSLIST\n')
|
self.pfile.write(b'NSLIST\n')
|
||||||
for (family, ip) in self.nslist:
|
for (family, ip) in self.nslist:
|
||||||
@ -484,7 +485,7 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
|||||||
debug2("Ignored auto net %d/%s/%d\n" % (family, ip, width))
|
debug2("Ignored auto net %d/%s/%d\n" % (family, ip, width))
|
||||||
else:
|
else:
|
||||||
debug2("Adding auto net %d/%s/%d\n" % (family, ip, width))
|
debug2("Adding auto net %d/%s/%d\n" % (family, ip, width))
|
||||||
fw.auto_nets.append((family, ip, width))
|
fw.auto_nets.append((family, ip, width, 0, 0))
|
||||||
|
|
||||||
# we definitely want to do this *after* starting ssh, or we might end
|
# we definitely want to do this *after* starting ssh, or we might end
|
||||||
# up intercepting the ssh connection!
|
# up intercepting the ssh connection!
|
||||||
@ -591,11 +592,11 @@ def main(listenip_v6, listenip_v4,
|
|||||||
|
|
||||||
if required.ipv4 and \
|
if required.ipv4 and \
|
||||||
not any(listenip_v4[0] == sex[1] for sex in subnets_v4):
|
not any(listenip_v4[0] == sex[1] for sex in subnets_v4):
|
||||||
subnets_exclude.append((socket.AF_INET, listenip_v4[0], 32))
|
subnets_exclude.append((socket.AF_INET, listenip_v4[0], 32, 0, 0))
|
||||||
|
|
||||||
if required.ipv6 and \
|
if required.ipv6 and \
|
||||||
not any(listenip_v6[0] == sex[1] for sex in subnets_v6):
|
not any(listenip_v6[0] == sex[1] for sex in subnets_v6):
|
||||||
subnets_exclude.append((socket.AF_INET6, listenip_v6[0], 128))
|
subnets_exclude.append((socket.AF_INET6, listenip_v6[0], 128, 0, 0))
|
||||||
|
|
||||||
if listenip_v6 and listenip_v6[1] and listenip_v4 and listenip_v4[1]:
|
if listenip_v6 and listenip_v6[1] and listenip_v4 and listenip_v4[1]:
|
||||||
# if both ports given, no need to search for a spare port
|
# if both ports given, no need to search for a spare port
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import re
|
import re
|
||||||
|
import socket
|
||||||
import sshuttle.helpers as helpers
|
import sshuttle.helpers as helpers
|
||||||
import sshuttle.client as client
|
import sshuttle.client as client
|
||||||
import sshuttle.firewall as firewall
|
import sshuttle.firewall as firewall
|
||||||
import sshuttle.hostwatch as hostwatch
|
import sshuttle.hostwatch as hostwatch
|
||||||
import sshuttle.ssyslog as ssyslog
|
import sshuttle.ssyslog as ssyslog
|
||||||
from sshuttle.options import parser, parse_ipport6, parse_ipport4
|
from sshuttle.options import parser, parse_ipport
|
||||||
from sshuttle.helpers import family_ip_tuple, log, Fatal
|
from sshuttle.helpers import family_ip_tuple, log, Fatal
|
||||||
|
|
||||||
|
|
||||||
@ -46,10 +47,11 @@ def main():
|
|||||||
ipport_v4 = None
|
ipport_v4 = None
|
||||||
list = opt.listen.split(",")
|
list = opt.listen.split(",")
|
||||||
for ip in list:
|
for ip in list:
|
||||||
if '[' in ip and ']' in ip:
|
family, ip, port = parse_ipport(ip)
|
||||||
ipport_v6 = parse_ipport6(ip)
|
if family == socket.AF_INET6:
|
||||||
|
ipport_v6 = (ip, port)
|
||||||
else:
|
else:
|
||||||
ipport_v4 = parse_ipport4(ip)
|
ipport_v4 = (ip, port)
|
||||||
else:
|
else:
|
||||||
# parse_ipport4('127.0.0.1:0')
|
# parse_ipport4('127.0.0.1:0')
|
||||||
ipport_v4 = "auto"
|
ipport_v4 = "auto"
|
||||||
|
@ -74,6 +74,15 @@ def setup_daemon():
|
|||||||
return sys.stdin, sys.stdout
|
return sys.stdin, sys.stdout
|
||||||
|
|
||||||
|
|
||||||
|
# Note that we're sorting in a very particular order:
|
||||||
|
# we need to go from most-specific (largest swidth) to least-specific,
|
||||||
|
# and at any given level of specificity, smaller port ranges come
|
||||||
|
# before larger port ranges. On ties excludes come first.
|
||||||
|
# s:(inet, subnet width, exclude flag, subnet, first port, last port)
|
||||||
|
def subnet_weight(s):
|
||||||
|
return (s[1], s[-2] or -65535 - s[-1], s[2])
|
||||||
|
|
||||||
|
|
||||||
# This is some voodoo for setting up the kernel's transparent
|
# This is some voodoo for setting up the kernel's transparent
|
||||||
# proxying stuff. If subnets is empty, we just delete our sshuttle rules;
|
# proxying stuff. If subnets is empty, we just delete our sshuttle rules;
|
||||||
# otherwise we delete it, then make them from scratch.
|
# otherwise we delete it, then make them from scratch.
|
||||||
@ -119,10 +128,17 @@ def main(method_name, syslog):
|
|||||||
elif line.startswith("NSLIST\n"):
|
elif line.startswith("NSLIST\n"):
|
||||||
break
|
break
|
||||||
try:
|
try:
|
||||||
(family, width, exclude, ip) = line.strip().split(',', 3)
|
(family, width, exclude, ip, fport, lport) = \
|
||||||
|
line.strip().split(',', 5)
|
||||||
except:
|
except:
|
||||||
raise Fatal('firewall: expected route or NSLIST but got %r' % line)
|
raise Fatal('firewall: expected route or NSLIST but got %r' % line)
|
||||||
subnets.append((int(family), int(width), bool(int(exclude)), ip))
|
subnets.append((
|
||||||
|
int(family),
|
||||||
|
int(width),
|
||||||
|
bool(int(exclude)),
|
||||||
|
ip,
|
||||||
|
int(fport),
|
||||||
|
int(lport)))
|
||||||
debug2('firewall manager: Got subnets: %r\n' % subnets)
|
debug2('firewall manager: Got subnets: %r\n' % subnets)
|
||||||
|
|
||||||
nslist = []
|
nslist = []
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import socket
|
import socket
|
||||||
|
from sshuttle.firewall import subnet_weight
|
||||||
from sshuttle.helpers import family_to_string
|
from sshuttle.helpers import family_to_string
|
||||||
from sshuttle.linux import ipt, ipt_ttl, ipt_chain_exists, nonfatal
|
from sshuttle.linux import ipt, ipt_ttl, ipt_chain_exists, nonfatal
|
||||||
from sshuttle.methods import BaseMethod
|
from sshuttle.methods import BaseMethod
|
||||||
@ -38,22 +39,21 @@ class Method(BaseMethod):
|
|||||||
_ipt('-I', 'OUTPUT', '1', '-j', chain)
|
_ipt('-I', 'OUTPUT', '1', '-j', chain)
|
||||||
_ipt('-I', 'PREROUTING', '1', '-j', chain)
|
_ipt('-I', 'PREROUTING', '1', '-j', chain)
|
||||||
|
|
||||||
# create new subnet entries. Note that we're sorting in a very
|
# create new subnet entries.
|
||||||
# particular order: we need to go from most-specific (largest
|
for f, swidth, sexclude, snet, fport, lport \
|
||||||
# swidth) to least-specific, and at any given level of specificity,
|
in sorted(subnets, key=subnet_weight, reverse=True):
|
||||||
# we want excludes to come first. That's why the columns are in
|
tcp_ports = ('-p', 'tcp')
|
||||||
# such a non- intuitive order.
|
if fport:
|
||||||
for f, swidth, sexclude, snet \
|
tcp_ports = tcp_ports + ('--dport', '%d:%d' % (fport, lport))
|
||||||
in sorted(subnets, key=lambda s: s[1], reverse=True):
|
|
||||||
if sexclude:
|
if sexclude:
|
||||||
_ipt('-A', chain, '-j', 'RETURN',
|
_ipt('-A', chain, '-j', 'RETURN',
|
||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
'-p', 'tcp')
|
*tcp_ports)
|
||||||
else:
|
else:
|
||||||
_ipt_ttl('-A', chain, '-j', 'REDIRECT',
|
_ipt_ttl('-A', chain, '-j', 'REDIRECT',
|
||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
'-p', 'tcp',
|
*(tcp_ports + ('--to-ports', str(port))))
|
||||||
'--to-ports', str(port))
|
|
||||||
|
|
||||||
for f, ip in [i for i in nslist if i[0] == family]:
|
for f, ip in [i for i in nslist if i[0] == family]:
|
||||||
_ipt_ttl('-A', chain, '-j', 'REDIRECT',
|
_ipt_ttl('-A', chain, '-j', 'REDIRECT',
|
||||||
|
@ -9,6 +9,7 @@ import shlex
|
|||||||
from fcntl import ioctl
|
from fcntl import ioctl
|
||||||
from ctypes import c_char, c_uint8, c_uint16, c_uint32, Union, Structure, \
|
from ctypes import c_char, c_uint8, c_uint16, c_uint32, Union, Structure, \
|
||||||
sizeof, addressof, memmove
|
sizeof, addressof, memmove
|
||||||
|
from sshuttle.firewall import subnet_weight
|
||||||
from sshuttle.helpers import debug1, debug2, debug3, Fatal, family_to_string
|
from sshuttle.helpers import debug1, debug2, debug3, Fatal, family_to_string
|
||||||
from sshuttle.methods import BaseMethod
|
from sshuttle.methods import BaseMethod
|
||||||
|
|
||||||
@ -186,16 +187,18 @@ class FreeBsd(Generic):
|
|||||||
inet_version = self._inet_version(family)
|
inet_version = self._inet_version(family)
|
||||||
lo_addr = self._lo_addr(family)
|
lo_addr = self._lo_addr(family)
|
||||||
|
|
||||||
tables = [
|
tables = []
|
||||||
b'table <forward_subnets> {%s}' % b','.join(includes)
|
|
||||||
]
|
|
||||||
translating_rules = [
|
translating_rules = [
|
||||||
b'rdr pass on lo0 %s proto tcp to <forward_subnets> '
|
b'rdr pass on lo0 %s proto tcp to %s '
|
||||||
b'-> %s port %r' % (inet_version, lo_addr, port)
|
b'-> %s port %r' % (inet_version, subnet, lo_addr, port)
|
||||||
|
for exclude, subnet in includes if not exclude
|
||||||
]
|
]
|
||||||
filtering_rules = [
|
filtering_rules = [
|
||||||
b'pass out route-to lo0 %s proto tcp '
|
b'pass out route-to lo0 %s proto tcp '
|
||||||
b'to <forward_subnets> keep state' % inet_version
|
b'to %s keep state' % (inet_version, subnet)
|
||||||
|
if not exclude else
|
||||||
|
b'pass out quick %s proto tcp to %s' % (inet_version, subnet)
|
||||||
|
for exclude, subnet in includes
|
||||||
]
|
]
|
||||||
|
|
||||||
if len(nslist) > 0:
|
if len(nslist) > 0:
|
||||||
@ -254,16 +257,18 @@ class OpenBsd(Generic):
|
|||||||
inet_version = self._inet_version(family)
|
inet_version = self._inet_version(family)
|
||||||
lo_addr = self._lo_addr(family)
|
lo_addr = self._lo_addr(family)
|
||||||
|
|
||||||
tables = [
|
tables = []
|
||||||
b'table <forward_subnets> {%s}' % b','.join(includes)
|
|
||||||
]
|
|
||||||
translating_rules = [
|
translating_rules = [
|
||||||
b'pass in on lo0 %s proto tcp to <forward_subnets> '
|
b'pass in on lo0 %s proto tcp to %s '
|
||||||
b'divert-to %s port %r' % (inet_version, lo_addr, port)
|
b'divert-to %s port %r' % (inet_version, subnet, lo_addr, port)
|
||||||
|
for exclude, subnet in includes if not exclude
|
||||||
]
|
]
|
||||||
filtering_rules = [
|
filtering_rules = [
|
||||||
b'pass out %s proto tcp to <forward_subnets> '
|
b'pass out %s proto tcp to %s '
|
||||||
b'route-to lo0 keep state' % inet_version
|
b'route-to lo0 keep state' % (inet_version, subnet)
|
||||||
|
if not exclude else
|
||||||
|
b'pass out quick %s proto tcp to %s' % (inet_version, subnet)
|
||||||
|
for exclude, subnet in includes
|
||||||
]
|
]
|
||||||
|
|
||||||
if len(nslist) > 0:
|
if len(nslist) > 0:
|
||||||
@ -429,12 +434,12 @@ class Method(BaseMethod):
|
|||||||
# If a given subnet is both included and excluded, list the
|
# If a given subnet is both included and excluded, list the
|
||||||
# exclusion first; the table will ignore the second, opposite
|
# exclusion first; the table will ignore the second, opposite
|
||||||
# definition
|
# definition
|
||||||
for f, swidth, sexclude, snet in sorted(
|
for f, swidth, sexclude, snet, fport, lport \
|
||||||
subnets, key=lambda s: (s[1], s[2]), reverse=True):
|
in sorted(subnets, key=subnet_weight, reverse=True):
|
||||||
includes.append(b"%s%s/%d" %
|
includes.append((sexclude, b"%s/%d%s" % (
|
||||||
(b"!" if sexclude else b"",
|
snet.encode("ASCII"),
|
||||||
snet.encode("ASCII"),
|
swidth,
|
||||||
swidth))
|
b" port %d:%d" % (fport, lport) if fport else b"")))
|
||||||
|
|
||||||
anchor = pf_get_anchor(family, port)
|
anchor = pf_get_anchor(family, port)
|
||||||
pf.add_anchors(anchor)
|
pf.add_anchors(anchor)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import struct
|
import struct
|
||||||
|
from sshuttle.firewall import subnet_weight
|
||||||
from sshuttle.helpers import family_to_string
|
from sshuttle.helpers import family_to_string
|
||||||
from sshuttle.linux import ipt, ipt_ttl, ipt_chain_exists
|
from sshuttle.linux import ipt, ipt_ttl, ipt_chain_exists
|
||||||
from sshuttle.methods import BaseMethod
|
from sshuttle.methods import BaseMethod
|
||||||
@ -163,6 +164,11 @@ class Method(BaseMethod):
|
|||||||
def _ipt_ttl(*args):
|
def _ipt_ttl(*args):
|
||||||
return ipt_ttl(family, table, *args)
|
return ipt_ttl(family, table, *args)
|
||||||
|
|
||||||
|
def _ipt_proto_ports(proto, fport, lport):
|
||||||
|
return proto + ('--dport', '%d:%d' % (fport, lport)) \
|
||||||
|
if fport else proto
|
||||||
|
|
||||||
|
|
||||||
mark_chain = 'sshuttle-m-%s' % port
|
mark_chain = 'sshuttle-m-%s' % port
|
||||||
tproxy_chain = 'sshuttle-t-%s' % port
|
tproxy_chain = 'sshuttle-t-%s' % port
|
||||||
divert_chain = 'sshuttle-d-%s' % port
|
divert_chain = 'sshuttle-d-%s' % port
|
||||||
@ -197,33 +203,44 @@ class Method(BaseMethod):
|
|||||||
'-m', 'udp', '-p', 'udp', '--dport', '53',
|
'-m', 'udp', '-p', 'udp', '--dport', '53',
|
||||||
'--on-port', str(dnsport))
|
'--on-port', str(dnsport))
|
||||||
|
|
||||||
for f, swidth, sexclude, snet \
|
for f, swidth, sexclude, snet, fport, lport \
|
||||||
in sorted(subnets, key=lambda s: s[1], reverse=True):
|
in sorted(subnets, key=subnet_weight, reverse=True):
|
||||||
|
tcp_ports = ('-p', 'tcp')
|
||||||
|
tcp_ports = _ipt_proto_ports(tcp_ports, fport, lport)
|
||||||
|
|
||||||
if sexclude:
|
if sexclude:
|
||||||
_ipt('-A', mark_chain, '-j', 'RETURN',
|
_ipt('-A', mark_chain, '-j', 'RETURN',
|
||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
'-m', 'tcp', '-p', 'tcp')
|
'-m', 'tcp',
|
||||||
|
*tcp_ports)
|
||||||
_ipt('-A', tproxy_chain, '-j', 'RETURN',
|
_ipt('-A', tproxy_chain, '-j', 'RETURN',
|
||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
'-m', 'tcp', '-p', 'tcp')
|
'-m', 'tcp',
|
||||||
|
*tcp_ports)
|
||||||
else:
|
else:
|
||||||
_ipt('-A', mark_chain, '-j', 'MARK', '--set-mark', '1',
|
_ipt('-A', mark_chain, '-j', 'MARK', '--set-mark', '1',
|
||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
'-m', 'tcp', '-p', 'tcp')
|
'-m', 'tcp',
|
||||||
|
*tcp_ports)
|
||||||
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
|
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x1/0x1',
|
'--tproxy-mark', '0x1/0x1',
|
||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
'-m', 'tcp', '-p', 'tcp',
|
'-m', 'tcp',
|
||||||
'--on-port', str(port))
|
*(tcp_ports + ('--on-port', str(port))))
|
||||||
|
|
||||||
if udp:
|
if udp:
|
||||||
|
udp_ports = ('-p', 'udp')
|
||||||
|
udp_ports = _ipt_proto_ports(udp_ports, fport, lport)
|
||||||
|
|
||||||
if sexclude:
|
if sexclude:
|
||||||
_ipt('-A', mark_chain, '-j', 'RETURN',
|
_ipt('-A', mark_chain, '-j', 'RETURN',
|
||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
'-m', 'udp', '-p', 'udp')
|
'-m', 'udp',
|
||||||
|
*udp_ports)
|
||||||
_ipt('-A', tproxy_chain, '-j', 'RETURN',
|
_ipt('-A', tproxy_chain, '-j', 'RETURN',
|
||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
'-m', 'udp', '-p', 'udp')
|
'-m', 'udp',
|
||||||
|
*udp_ports)
|
||||||
else:
|
else:
|
||||||
_ipt('-A', mark_chain, '-j', 'MARK', '--set-mark', '1',
|
_ipt('-A', mark_chain, '-j', 'MARK', '--set-mark', '1',
|
||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
@ -231,8 +248,8 @@ class Method(BaseMethod):
|
|||||||
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
|
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x1/0x1',
|
'--tproxy-mark', '0x1/0x1',
|
||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
'-m', 'udp', '-p', 'udp',
|
'-m', 'udp',
|
||||||
'--on-port', str(port))
|
*(udp_ports + ('--on-port', str(port))))
|
||||||
|
|
||||||
def restore_firewall(self, port, family, udp):
|
def restore_firewall(self, port, family, udp):
|
||||||
if family not in [socket.AF_INET, socket.AF_INET6]:
|
if family not in [socket.AF_INET, socket.AF_INET6]:
|
||||||
|
@ -4,41 +4,8 @@ from argparse import ArgumentParser, Action, ArgumentTypeError as Fatal
|
|||||||
from sshuttle import __version__
|
from sshuttle import __version__
|
||||||
|
|
||||||
|
|
||||||
# 1.2.3.4/5 or just 1.2.3.4
|
|
||||||
def parse_subnet4(s):
|
|
||||||
m = re.match(r'(\d+)(?:\.(\d+)\.(\d+)\.(\d+))?(?:/(\d+))?$', s)
|
|
||||||
if not m:
|
|
||||||
raise Fatal('%r is not a valid IP subnet format' % s)
|
|
||||||
(a, b, c, d, width) = m.groups()
|
|
||||||
(a, b, c, d) = (int(a or 0), int(b or 0), int(c or 0), int(d or 0))
|
|
||||||
if width is None:
|
|
||||||
width = 32
|
|
||||||
else:
|
|
||||||
width = int(width)
|
|
||||||
if a > 255 or b > 255 or c > 255 or d > 255:
|
|
||||||
raise Fatal('%d.%d.%d.%d has numbers > 255' % (a, b, c, d))
|
|
||||||
if width > 32:
|
|
||||||
raise Fatal('*/%d is greater than the maximum of 32' % width)
|
|
||||||
return(socket.AF_INET, '%d.%d.%d.%d' % (a, b, c, d), width)
|
|
||||||
|
|
||||||
|
|
||||||
# 1:2::3/64 or just 1:2::3
|
|
||||||
def parse_subnet6(s):
|
|
||||||
m = re.match(r'(?:([a-fA-F\d:]+))?(?:/(\d+))?$', s)
|
|
||||||
if not m:
|
|
||||||
raise Fatal('%r is not a valid IP subnet format' % s)
|
|
||||||
(net, width) = m.groups()
|
|
||||||
if width is None:
|
|
||||||
width = 128
|
|
||||||
else:
|
|
||||||
width = int(width)
|
|
||||||
if width > 128:
|
|
||||||
raise Fatal('*/%d is greater than the maximum of 128' % width)
|
|
||||||
return(socket.AF_INET6, net, width)
|
|
||||||
|
|
||||||
|
|
||||||
# Subnet file, supporting empty lines and hash-started comment lines
|
# Subnet file, supporting empty lines and hash-started comment lines
|
||||||
def parse_subnet_file(s):
|
def parse_subnetport_file(s):
|
||||||
try:
|
try:
|
||||||
handle = open(s, 'r')
|
handle = open(s, 'r')
|
||||||
except OSError:
|
except OSError:
|
||||||
@ -52,47 +19,66 @@ def parse_subnet_file(s):
|
|||||||
continue
|
continue
|
||||||
if line[0] == '#':
|
if line[0] == '#':
|
||||||
continue
|
continue
|
||||||
subnets.append(parse_subnet(line))
|
subnets.append(parse_subnetport(line))
|
||||||
|
|
||||||
return subnets
|
return subnets
|
||||||
|
|
||||||
|
|
||||||
# 1.2.3.4/5 or just 1.2.3.4
|
# 1.2.3.4/5:678, 1.2.3.4:567, 1.2.3.4/16 or just 1.2.3.4
|
||||||
# 1:2::3/64 or just 1:2::3
|
# [1:2::3/64]:456, [1:2::3]:456, 1:2::3/64 or just 1:2::3
|
||||||
def parse_subnet(subnet_str):
|
# example.com:123 or just example.com
|
||||||
if ':' in subnet_str:
|
def parse_subnetport(s):
|
||||||
return parse_subnet6(subnet_str)
|
if s.count(':') > 1:
|
||||||
|
rx = r'(?:\[?([\w\:]+)(?:/(\d+))?]?)(?::(\d+)(?:-(\d+))?)?$'
|
||||||
else:
|
else:
|
||||||
return parse_subnet4(subnet_str)
|
rx = r'([\w\.]+)(?:/(\d+))?(?::(\d+)(?:-(\d+))?)?$'
|
||||||
|
|
||||||
|
m = re.match(rx, s)
|
||||||
|
if not m:
|
||||||
|
raise Fatal('%r is not a valid address/mask:port format' % s)
|
||||||
|
|
||||||
|
addr, width, fport, lport = m.groups()
|
||||||
|
try:
|
||||||
|
addrinfo = socket.getaddrinfo(addr, 0, 0, socket.SOCK_STREAM)
|
||||||
|
except socket.gaierror:
|
||||||
|
raise Fatal('Unable to resolve address: %s' % addr)
|
||||||
|
|
||||||
|
family, _, _, _, addr = min(addrinfo)
|
||||||
|
max_width = 32 if family == socket.AF_INET else 128
|
||||||
|
width = int(width or max_width)
|
||||||
|
if not 0 <= width <= max_width:
|
||||||
|
raise Fatal('width %d is not between 0 and %d' % (width, max_width))
|
||||||
|
|
||||||
|
return (family, addr[0], width, int(fport or 0), int(lport or fport or 0))
|
||||||
|
|
||||||
|
|
||||||
# 1.2.3.4:567 or just 1.2.3.4 or just 567
|
# 1.2.3.4:567 or just 1.2.3.4 or just 567
|
||||||
def parse_ipport4(s):
|
# [1:2::3]:456 or [1:2::3] or just [::]:567
|
||||||
|
# example.com:123 or just example.com
|
||||||
|
def parse_ipport(s):
|
||||||
s = str(s)
|
s = str(s)
|
||||||
m = re.match(r'(?:(\d+)\.(\d+)\.(\d+)\.(\d+))?(?::)?(?:(\d+))?$', s)
|
if s.isdigit():
|
||||||
|
rx = r'()(\d+)$'
|
||||||
|
elif ']' in s:
|
||||||
|
rx = r'(?:\[([^]]+)])(?::(\d+))?$'
|
||||||
|
else:
|
||||||
|
rx = r'([\w\.]+)(?::(\d+))?$'
|
||||||
|
|
||||||
|
m = re.match(rx, s)
|
||||||
if not m:
|
if not m:
|
||||||
raise Fatal('%r is not a valid IP:port format' % s)
|
raise Fatal('%r is not a valid IP:port format' % s)
|
||||||
(a, b, c, d, port) = m.groups()
|
|
||||||
(a, b, c, d, port) = (int(a or 0), int(b or 0), int(c or 0), int(d or 0),
|
|
||||||
int(port or 0))
|
|
||||||
if a > 255 or b > 255 or c > 255 or d > 255:
|
|
||||||
raise Fatal('%d.%d.%d.%d has numbers > 255' % (a, b, c, d))
|
|
||||||
if port > 65535:
|
|
||||||
raise Fatal('*:%d is greater than the maximum of 65535' % port)
|
|
||||||
if a is None:
|
|
||||||
a = b = c = d = 0
|
|
||||||
return ('%d.%d.%d.%d' % (a, b, c, d), port)
|
|
||||||
|
|
||||||
|
ip, port = m.groups()
|
||||||
|
ip = ip or '0.0.0.0'
|
||||||
|
port = int(port or 0)
|
||||||
|
|
||||||
# [1:2::3]:456 or [1:2::3] or 456
|
try:
|
||||||
def parse_ipport6(s):
|
addrinfo = socket.getaddrinfo(ip, port, 0, socket.SOCK_STREAM)
|
||||||
s = str(s)
|
except socket.gaierror:
|
||||||
m = re.match(r'(?:\[([^]]*)])?(?::)?(?:(\d+))?$', s)
|
raise Fatal('%r is not a valid IP:port format' % s)
|
||||||
if not m:
|
|
||||||
raise Fatal('%s is not a valid IP:port format' % s)
|
family, _, _, _, addr = min(addrinfo)
|
||||||
(ip, port) = m.groups()
|
return (family,) + addr[:2]
|
||||||
(ip, port) = (ip or '::', int(port or 0))
|
|
||||||
return (ip, port)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_list(list):
|
def parse_list(list):
|
||||||
@ -116,9 +102,9 @@ parser = ArgumentParser(
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"subnets",
|
"subnets",
|
||||||
metavar="IP/MASK [IP/MASK...]",
|
metavar="IP/MASK[:PORT[-PORT]]...",
|
||||||
nargs="*",
|
nargs="*",
|
||||||
type=parse_subnet,
|
type=parse_subnetport,
|
||||||
help="""
|
help="""
|
||||||
capture and forward traffic to these subnets (whitespace separated)
|
capture and forward traffic to these subnets (whitespace separated)
|
||||||
"""
|
"""
|
||||||
@ -185,10 +171,10 @@ parser.add_argument(
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-x", "--exclude",
|
"-x", "--exclude",
|
||||||
metavar="IP/MASK",
|
metavar="IP/MASK[:PORT[-PORT]]",
|
||||||
action="append",
|
action="append",
|
||||||
default=[],
|
default=[],
|
||||||
type=parse_subnet,
|
type=parse_subnetport,
|
||||||
help="""
|
help="""
|
||||||
exclude this subnet (can be used more than once)
|
exclude this subnet (can be used more than once)
|
||||||
"""
|
"""
|
||||||
@ -198,7 +184,7 @@ parser.add_argument(
|
|||||||
metavar="PATH",
|
metavar="PATH",
|
||||||
action=Concat,
|
action=Concat,
|
||||||
dest="exclude",
|
dest="exclude",
|
||||||
type=parse_subnet_file,
|
type=parse_subnetport_file,
|
||||||
help="""
|
help="""
|
||||||
exclude the subnets in a file (whitespace separated)
|
exclude the subnets in a file (whitespace separated)
|
||||||
"""
|
"""
|
||||||
@ -271,7 +257,7 @@ parser.add_argument(
|
|||||||
action=Concat,
|
action=Concat,
|
||||||
dest="subnets_file",
|
dest="subnets_file",
|
||||||
default=[],
|
default=[],
|
||||||
type=parse_subnet_file,
|
type=parse_subnetport_file,
|
||||||
help="""
|
help="""
|
||||||
file where the subnets are stored, instead of on the command line
|
file where the subnets are stored, instead of on the command line
|
||||||
"""
|
"""
|
||||||
|
@ -6,10 +6,10 @@ import sshuttle.firewall
|
|||||||
|
|
||||||
def setup_daemon():
|
def setup_daemon():
|
||||||
stdin = io.StringIO(u"""ROUTES
|
stdin = io.StringIO(u"""ROUTES
|
||||||
2,24,0,1.2.3.0
|
2,24,0,1.2.3.0,8000,9000
|
||||||
2,32,1,1.2.3.66
|
2,32,1,1.2.3.66,8080,8080
|
||||||
10,64,0,2404:6800:4004:80c::
|
10,64,0,2404:6800:4004:80c::,0,0
|
||||||
10,128,1,2404:6800:4004:80c::101f
|
10,128,1,2404:6800:4004:80c::101f,80,80
|
||||||
NSLIST
|
NSLIST
|
||||||
2,1.2.3.33
|
2,1.2.3.33
|
||||||
10,2404:6800:4004:80c::33
|
10,2404:6800:4004:80c::33
|
||||||
@ -88,14 +88,15 @@ def test_main(mock_get_method, mock_setup_daemon, mock_rewrite_etc_hosts):
|
|||||||
1024, 1026,
|
1024, 1026,
|
||||||
[(10, u'2404:6800:4004:80c::33')],
|
[(10, u'2404:6800:4004:80c::33')],
|
||||||
10,
|
10,
|
||||||
[(10, 64, False, u'2404:6800:4004:80c::'),
|
[(10, 64, False, u'2404:6800:4004:80c::', 0, 0),
|
||||||
(10, 128, True, u'2404:6800:4004:80c::101f')],
|
(10, 128, True, u'2404:6800:4004:80c::101f', 80, 80)],
|
||||||
True),
|
True),
|
||||||
call().setup_firewall(
|
call().setup_firewall(
|
||||||
1025, 1027,
|
1025, 1027,
|
||||||
[(2, u'1.2.3.33')],
|
[(2, u'1.2.3.33')],
|
||||||
2,
|
2,
|
||||||
[(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
|
[(2, 24, False, u'1.2.3.0', 8000, 9000),
|
||||||
|
(2, 32, True, u'1.2.3.66', 8080, 8080)],
|
||||||
True),
|
True),
|
||||||
call().restore_firewall(1024, 10, True),
|
call().restore_firewall(1024, 10, True),
|
||||||
call().restore_firewall(1025, 2, True),
|
call().restore_firewall(1025, 2, True),
|
||||||
|
@ -86,8 +86,8 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
1024, 1026,
|
1024, 1026,
|
||||||
[(10, u'2404:6800:4004:80c::33')],
|
[(10, u'2404:6800:4004:80c::33')],
|
||||||
10,
|
10,
|
||||||
[(10, 64, False, u'2404:6800:4004:80c::'),
|
[(10, 64, False, u'2404:6800:4004:80c::', 0, 0),
|
||||||
(10, 128, True, u'2404:6800:4004:80c::101f')],
|
(10, 128, True, u'2404:6800:4004:80c::101f', 80, 80)],
|
||||||
True)
|
True)
|
||||||
assert str(excinfo.value) \
|
assert str(excinfo.value) \
|
||||||
== 'Address family "AF_INET6" unsupported by nat method_name'
|
== 'Address family "AF_INET6" unsupported by nat method_name'
|
||||||
@ -100,7 +100,8 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
1025, 1027,
|
1025, 1027,
|
||||||
[(2, u'1.2.3.33')],
|
[(2, u'1.2.3.33')],
|
||||||
2,
|
2,
|
||||||
[(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
|
[(2, 24, False, u'1.2.3.0', 8000, 9000),
|
||||||
|
(2, 32, True, u'1.2.3.66', 8080, 8080)],
|
||||||
True)
|
True)
|
||||||
assert str(excinfo.value) == 'UDP not supported by nat method_name'
|
assert str(excinfo.value) == 'UDP not supported by nat method_name'
|
||||||
assert mock_ipt_chain_exists.mock_calls == []
|
assert mock_ipt_chain_exists.mock_calls == []
|
||||||
@ -111,14 +112,16 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
1025, 1027,
|
1025, 1027,
|
||||||
[(2, u'1.2.3.33')],
|
[(2, u'1.2.3.33')],
|
||||||
2,
|
2,
|
||||||
[(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
|
[(2, 24, False, u'1.2.3.0', 8000, 9000),
|
||||||
|
(2, 32, True, u'1.2.3.66', 8080, 8080)],
|
||||||
False)
|
False)
|
||||||
assert mock_ipt_chain_exists.mock_calls == [
|
assert mock_ipt_chain_exists.mock_calls == [
|
||||||
call(2, 'nat', 'sshuttle-1025')
|
call(2, 'nat', 'sshuttle-1025')
|
||||||
]
|
]
|
||||||
assert mock_ipt_ttl.mock_calls == [
|
assert mock_ipt_ttl.mock_calls == [
|
||||||
call(2, 'nat', '-A', 'sshuttle-1025', '-j', 'REDIRECT',
|
call(2, 'nat', '-A', 'sshuttle-1025', '-j', 'REDIRECT',
|
||||||
'--dest', u'1.2.3.0/24', '-p', 'tcp', '--to-ports', '1025'),
|
'--dest', u'1.2.3.0/24', '-p', 'tcp', '--dport', '8000:9000',
|
||||||
|
'--to-ports', '1025'),
|
||||||
call(2, 'nat', '-A', 'sshuttle-1025', '-j', 'REDIRECT',
|
call(2, 'nat', '-A', 'sshuttle-1025', '-j', 'REDIRECT',
|
||||||
'--dest', u'1.2.3.33/32', '-p', 'udp',
|
'--dest', u'1.2.3.33/32', '-p', 'udp',
|
||||||
'--dport', '53', '--to-ports', '1027')
|
'--dport', '53', '--to-ports', '1027')
|
||||||
@ -133,7 +136,7 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
call(2, 'nat', '-I', 'OUTPUT', '1', '-j', 'sshuttle-1025'),
|
call(2, 'nat', '-I', 'OUTPUT', '1', '-j', 'sshuttle-1025'),
|
||||||
call(2, 'nat', '-I', 'PREROUTING', '1', '-j', 'sshuttle-1025'),
|
call(2, 'nat', '-I', 'PREROUTING', '1', '-j', 'sshuttle-1025'),
|
||||||
call(2, 'nat', '-A', 'sshuttle-1025', '-j', 'RETURN',
|
call(2, 'nat', '-A', 'sshuttle-1025', '-j', 'RETURN',
|
||||||
'--dest', u'1.2.3.66/32', '-p', 'tcp')
|
'--dest', u'1.2.3.66/32', '-p', 'tcp', '--dport', '8080:8080')
|
||||||
]
|
]
|
||||||
mock_ipt_chain_exists.reset_mock()
|
mock_ipt_chain_exists.reset_mock()
|
||||||
mock_ipt_ttl.reset_mock()
|
mock_ipt_ttl.reset_mock()
|
||||||
|
@ -182,8 +182,8 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
1024, 1026,
|
1024, 1026,
|
||||||
[(10, u'2404:6800:4004:80c::33')],
|
[(10, u'2404:6800:4004:80c::33')],
|
||||||
10,
|
10,
|
||||||
[(10, 64, False, u'2404:6800:4004:80c::'),
|
[(10, 64, False, u'2404:6800:4004:80c::', 8000, 9000),
|
||||||
(10, 128, True, u'2404:6800:4004:80c::101f')],
|
(10, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
|
||||||
False)
|
False)
|
||||||
assert mock_ioctl.mock_calls == [
|
assert mock_ioctl.mock_calls == [
|
||||||
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
||||||
@ -198,16 +198,15 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
call('-f /dev/stdin', b'pass on lo\n'),
|
call('-f /dev/stdin', b'pass on lo\n'),
|
||||||
call('-s all'),
|
call('-s all'),
|
||||||
call('-a sshuttle6-1024 -f /dev/stdin',
|
call('-a sshuttle6-1024 -f /dev/stdin',
|
||||||
b'table <forward_subnets> {'
|
|
||||||
b'!2404:6800:4004:80c::101f/128,2404:6800:4004:80c::/64'
|
|
||||||
b'}\n'
|
|
||||||
b'table <dns_servers> {2404:6800:4004:80c::33}\n'
|
b'table <dns_servers> {2404:6800:4004:80c::33}\n'
|
||||||
b'rdr pass on lo0 inet6 proto tcp '
|
b'rdr pass on lo0 inet6 proto tcp to '
|
||||||
b'to <forward_subnets> -> ::1 port 1024\n'
|
b'2404:6800:4004:80c::/64 port 8000:9000 -> ::1 port 1024\n'
|
||||||
b'rdr pass on lo0 inet6 proto udp '
|
b'rdr pass on lo0 inet6 proto udp '
|
||||||
b'to <dns_servers> port 53 -> ::1 port 1026\n'
|
b'to <dns_servers> port 53 -> ::1 port 1026\n'
|
||||||
b'pass out route-to lo0 inet6 proto tcp '
|
b'pass out quick inet6 proto tcp to '
|
||||||
b'to <forward_subnets> keep state\n'
|
b'2404:6800:4004:80c::101f/128 port 8080:8080\n'
|
||||||
|
b'pass out route-to lo0 inet6 proto tcp to '
|
||||||
|
b'2404:6800:4004:80c::/64 port 8000:9000 keep state\n'
|
||||||
b'pass out route-to lo0 inet6 proto udp '
|
b'pass out route-to lo0 inet6 proto udp '
|
||||||
b'to <dns_servers> port 53 keep state\n'),
|
b'to <dns_servers> port 53 keep state\n'),
|
||||||
call('-E'),
|
call('-E'),
|
||||||
@ -221,7 +220,8 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
1025, 1027,
|
1025, 1027,
|
||||||
[(2, u'1.2.3.33')],
|
[(2, u'1.2.3.33')],
|
||||||
2,
|
2,
|
||||||
[(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
|
[(2, 24, False, u'1.2.3.0', 0, 0),
|
||||||
|
(2, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
True)
|
True)
|
||||||
assert str(excinfo.value) == 'UDP not supported by pf method_name'
|
assert str(excinfo.value) == 'UDP not supported by pf method_name'
|
||||||
assert mock_pf_get_dev.mock_calls == []
|
assert mock_pf_get_dev.mock_calls == []
|
||||||
@ -232,7 +232,7 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
1025, 1027,
|
1025, 1027,
|
||||||
[(2, u'1.2.3.33')],
|
[(2, u'1.2.3.33')],
|
||||||
2,
|
2,
|
||||||
[(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
|
[(2, 24, False, u'1.2.3.0', 0, 0), (2, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
False)
|
False)
|
||||||
assert mock_ioctl.mock_calls == [
|
assert mock_ioctl.mock_calls == [
|
||||||
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
||||||
@ -247,14 +247,13 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
call('-f /dev/stdin', b'pass on lo\n'),
|
call('-f /dev/stdin', b'pass on lo\n'),
|
||||||
call('-s all'),
|
call('-s all'),
|
||||||
call('-a sshuttle-1025 -f /dev/stdin',
|
call('-a sshuttle-1025 -f /dev/stdin',
|
||||||
b'table <forward_subnets> {!1.2.3.66/32,1.2.3.0/24}\n'
|
|
||||||
b'table <dns_servers> {1.2.3.33}\n'
|
b'table <dns_servers> {1.2.3.33}\n'
|
||||||
b'rdr pass on lo0 inet proto tcp '
|
b'rdr pass on lo0 inet proto tcp to 1.2.3.0/24 '
|
||||||
b'to <forward_subnets> -> 127.0.0.1 port 1025\n'
|
b'-> 127.0.0.1 port 1025\n'
|
||||||
b'rdr pass on lo0 inet proto udp '
|
b'rdr pass on lo0 inet proto udp '
|
||||||
b'to <dns_servers> port 53 -> 127.0.0.1 port 1027\n'
|
b'to <dns_servers> port 53 -> 127.0.0.1 port 1027\n'
|
||||||
b'pass out route-to lo0 inet proto tcp '
|
b'pass out quick inet proto tcp to 1.2.3.66/32 port 80:80\n'
|
||||||
b'to <forward_subnets> keep state\n'
|
b'pass out route-to lo0 inet proto tcp to 1.2.3.0/24 keep state\n'
|
||||||
b'pass out route-to lo0 inet proto udp '
|
b'pass out route-to lo0 inet proto udp '
|
||||||
b'to <dns_servers> port 53 keep state\n'),
|
b'to <dns_servers> port 53 keep state\n'),
|
||||||
call('-E'),
|
call('-E'),
|
||||||
@ -289,23 +288,22 @@ def test_setup_firewall_freebsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
1024, 1026,
|
1024, 1026,
|
||||||
[(10, u'2404:6800:4004:80c::33')],
|
[(10, u'2404:6800:4004:80c::33')],
|
||||||
10,
|
10,
|
||||||
[(10, 64, False, u'2404:6800:4004:80c::'),
|
[(10, 64, False, u'2404:6800:4004:80c::', 8000, 9000),
|
||||||
(10, 128, True, u'2404:6800:4004:80c::101f')],
|
(10, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
|
||||||
False)
|
False)
|
||||||
|
|
||||||
assert mock_pfctl.mock_calls == [
|
assert mock_pfctl.mock_calls == [
|
||||||
call('-s all'),
|
call('-s all'),
|
||||||
call('-a sshuttle6-1024 -f /dev/stdin',
|
call('-a sshuttle6-1024 -f /dev/stdin',
|
||||||
b'table <forward_subnets> {'
|
|
||||||
b'!2404:6800:4004:80c::101f/128,2404:6800:4004:80c::/64'
|
|
||||||
b'}\n'
|
|
||||||
b'table <dns_servers> {2404:6800:4004:80c::33}\n'
|
b'table <dns_servers> {2404:6800:4004:80c::33}\n'
|
||||||
b'rdr pass on lo0 inet6 proto tcp '
|
b'rdr pass on lo0 inet6 proto tcp to 2404:6800:4004:80c::/64 '
|
||||||
b'to <forward_subnets> -> ::1 port 1024\n'
|
b'port 8000:9000 -> ::1 port 1024\n'
|
||||||
b'rdr pass on lo0 inet6 proto udp '
|
b'rdr pass on lo0 inet6 proto udp '
|
||||||
b'to <dns_servers> port 53 -> ::1 port 1026\n'
|
b'to <dns_servers> port 53 -> ::1 port 1026\n'
|
||||||
b'pass out route-to lo0 inet6 proto tcp '
|
b'pass out quick inet6 proto tcp to '
|
||||||
b'to <forward_subnets> keep state\n'
|
b'2404:6800:4004:80c::101f/128 port 8080:8080\n'
|
||||||
|
b'pass out route-to lo0 inet6 proto tcp to '
|
||||||
|
b'2404:6800:4004:80c::/64 port 8000:9000 keep state\n'
|
||||||
b'pass out route-to lo0 inet6 proto udp '
|
b'pass out route-to lo0 inet6 proto udp '
|
||||||
b'to <dns_servers> port 53 keep state\n'),
|
b'to <dns_servers> port 53 keep state\n'),
|
||||||
call('-e'),
|
call('-e'),
|
||||||
@ -319,7 +317,8 @@ def test_setup_firewall_freebsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
1025, 1027,
|
1025, 1027,
|
||||||
[(2, u'1.2.3.33')],
|
[(2, u'1.2.3.33')],
|
||||||
2,
|
2,
|
||||||
[(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
|
[(2, 24, False, u'1.2.3.0', 0, 0),
|
||||||
|
(2, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
True)
|
True)
|
||||||
assert str(excinfo.value) == 'UDP not supported by pf method_name'
|
assert str(excinfo.value) == 'UDP not supported by pf method_name'
|
||||||
assert mock_pf_get_dev.mock_calls == []
|
assert mock_pf_get_dev.mock_calls == []
|
||||||
@ -330,7 +329,7 @@ def test_setup_firewall_freebsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
1025, 1027,
|
1025, 1027,
|
||||||
[(2, u'1.2.3.33')],
|
[(2, u'1.2.3.33')],
|
||||||
2,
|
2,
|
||||||
[(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
|
[(2, 24, False, u'1.2.3.0', 0, 0), (2, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
False)
|
False)
|
||||||
assert mock_ioctl.mock_calls == [
|
assert mock_ioctl.mock_calls == [
|
||||||
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
||||||
@ -343,14 +342,13 @@ def test_setup_firewall_freebsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
assert mock_pfctl.mock_calls == [
|
assert mock_pfctl.mock_calls == [
|
||||||
call('-s all'),
|
call('-s all'),
|
||||||
call('-a sshuttle-1025 -f /dev/stdin',
|
call('-a sshuttle-1025 -f /dev/stdin',
|
||||||
b'table <forward_subnets> {!1.2.3.66/32,1.2.3.0/24}\n'
|
|
||||||
b'table <dns_servers> {1.2.3.33}\n'
|
b'table <dns_servers> {1.2.3.33}\n'
|
||||||
b'rdr pass on lo0 inet proto tcp '
|
b'rdr pass on lo0 inet proto tcp to 1.2.3.0/24 -> '
|
||||||
b'to <forward_subnets> -> 127.0.0.1 port 1025\n'
|
b'127.0.0.1 port 1025\n'
|
||||||
b'rdr pass on lo0 inet proto udp '
|
b'rdr pass on lo0 inet proto udp '
|
||||||
b'to <dns_servers> port 53 -> 127.0.0.1 port 1027\n'
|
b'to <dns_servers> port 53 -> 127.0.0.1 port 1027\n'
|
||||||
b'pass out route-to lo0 inet proto tcp '
|
b'pass out quick inet proto tcp to 1.2.3.66/32 port 80:80\n'
|
||||||
b'to <forward_subnets> keep state\n'
|
b'pass out route-to lo0 inet proto tcp to 1.2.3.0/24 keep state\n'
|
||||||
b'pass out route-to lo0 inet proto udp '
|
b'pass out route-to lo0 inet proto udp '
|
||||||
b'to <dns_servers> port 53 keep state\n'),
|
b'to <dns_servers> port 53 keep state\n'),
|
||||||
call('-e'),
|
call('-e'),
|
||||||
@ -385,8 +383,8 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
1024, 1026,
|
1024, 1026,
|
||||||
[(10, u'2404:6800:4004:80c::33')],
|
[(10, u'2404:6800:4004:80c::33')],
|
||||||
10,
|
10,
|
||||||
[(10, 64, False, u'2404:6800:4004:80c::'),
|
[(10, 64, False, u'2404:6800:4004:80c::', 8000, 9000),
|
||||||
(10, 128, True, u'2404:6800:4004:80c::101f')],
|
(10, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
|
||||||
False)
|
False)
|
||||||
|
|
||||||
assert mock_ioctl.mock_calls == [
|
assert mock_ioctl.mock_calls == [
|
||||||
@ -398,16 +396,15 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
call('-f /dev/stdin', b'match on lo\n'),
|
call('-f /dev/stdin', b'match on lo\n'),
|
||||||
call('-s all'),
|
call('-s all'),
|
||||||
call('-a sshuttle6-1024 -f /dev/stdin',
|
call('-a sshuttle6-1024 -f /dev/stdin',
|
||||||
b'table <forward_subnets> {'
|
|
||||||
b'!2404:6800:4004:80c::101f/128,2404:6800:4004:80c::/64'
|
|
||||||
b'}\n'
|
|
||||||
b'table <dns_servers> {2404:6800:4004:80c::33}\n'
|
b'table <dns_servers> {2404:6800:4004:80c::33}\n'
|
||||||
b'pass in on lo0 inet6 proto tcp to '
|
b'pass in on lo0 inet6 proto tcp to 2404:6800:4004:80c::/64 '
|
||||||
b'<forward_subnets> divert-to ::1 port 1024\n'
|
b'port 8000:9000 divert-to ::1 port 1024\n'
|
||||||
b'pass in on lo0 inet6 proto udp '
|
b'pass in on lo0 inet6 proto udp '
|
||||||
b'to <dns_servers> port 53 rdr-to ::1 port 1026\n'
|
b'to <dns_servers> port 53 rdr-to ::1 port 1026\n'
|
||||||
b'pass out inet6 proto tcp to '
|
b'pass out quick inet6 proto tcp to '
|
||||||
b'<forward_subnets> route-to lo0 keep state\n'
|
b'2404:6800:4004:80c::101f/128 port 8080:8080\n'
|
||||||
|
b'pass out inet6 proto tcp to 2404:6800:4004:80c::/64 '
|
||||||
|
b'port 8000:9000 route-to lo0 keep state\n'
|
||||||
b'pass out inet6 proto udp to '
|
b'pass out inet6 proto udp to '
|
||||||
b'<dns_servers> port 53 route-to lo0 keep state\n'),
|
b'<dns_servers> port 53 route-to lo0 keep state\n'),
|
||||||
call('-e'),
|
call('-e'),
|
||||||
@ -421,7 +418,8 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
1025, 1027,
|
1025, 1027,
|
||||||
[(2, u'1.2.3.33')],
|
[(2, u'1.2.3.33')],
|
||||||
2,
|
2,
|
||||||
[(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
|
[(2, 24, False, u'1.2.3.0', 0, 0),
|
||||||
|
(2, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
True)
|
True)
|
||||||
assert str(excinfo.value) == 'UDP not supported by pf method_name'
|
assert str(excinfo.value) == 'UDP not supported by pf method_name'
|
||||||
assert mock_pf_get_dev.mock_calls == []
|
assert mock_pf_get_dev.mock_calls == []
|
||||||
@ -432,7 +430,8 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
1025, 1027,
|
1025, 1027,
|
||||||
[(2, u'1.2.3.33')],
|
[(2, u'1.2.3.33')],
|
||||||
2,
|
2,
|
||||||
[(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
|
[(2, 24, False, u'1.2.3.0', 0, 0),
|
||||||
|
(2, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
False)
|
False)
|
||||||
assert mock_ioctl.mock_calls == [
|
assert mock_ioctl.mock_calls == [
|
||||||
call(mock_pf_get_dev(), 0xcd48441a, ANY),
|
call(mock_pf_get_dev(), 0xcd48441a, ANY),
|
||||||
@ -443,13 +442,13 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
call('-f /dev/stdin', b'match on lo\n'),
|
call('-f /dev/stdin', b'match on lo\n'),
|
||||||
call('-s all'),
|
call('-s all'),
|
||||||
call('-a sshuttle-1025 -f /dev/stdin',
|
call('-a sshuttle-1025 -f /dev/stdin',
|
||||||
b'table <forward_subnets> {!1.2.3.66/32,1.2.3.0/24}\n'
|
|
||||||
b'table <dns_servers> {1.2.3.33}\n'
|
b'table <dns_servers> {1.2.3.33}\n'
|
||||||
b'pass in on lo0 inet proto tcp to <forward_subnets> divert-to 127.0.0.1 port 1025\n'
|
b'pass in on lo0 inet proto tcp to 1.2.3.0/24 divert-to '
|
||||||
|
b'127.0.0.1 port 1025\n'
|
||||||
b'pass in on lo0 inet proto udp to '
|
b'pass in on lo0 inet proto udp to '
|
||||||
b'<dns_servers> port 53 rdr-to 127.0.0.1 port 1027\n'
|
b'<dns_servers> port 53 rdr-to 127.0.0.1 port 1027\n'
|
||||||
b'pass out inet proto tcp to '
|
b'pass out quick inet proto tcp to 1.2.3.66/32 port 80:80\n'
|
||||||
b'<forward_subnets> route-to lo0 keep state\n'
|
b'pass out inet proto tcp to 1.2.3.0/24 route-to lo0 keep state\n'
|
||||||
b'pass out inet proto udp to '
|
b'pass out inet proto udp to '
|
||||||
b'<dns_servers> port 53 route-to lo0 keep state\n'),
|
b'<dns_servers> port 53 route-to lo0 keep state\n'),
|
||||||
call('-e'),
|
call('-e'),
|
||||||
|
@ -102,8 +102,8 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
1024, 1026,
|
1024, 1026,
|
||||||
[(10, u'2404:6800:4004:80c::33')],
|
[(10, u'2404:6800:4004:80c::33')],
|
||||||
10,
|
10,
|
||||||
[(10, 64, False, u'2404:6800:4004:80c::'),
|
[(10, 64, False, u'2404:6800:4004:80c::', 8000, 9000),
|
||||||
(10, 128, True, u'2404:6800:4004:80c::101f')],
|
(10, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
|
||||||
True)
|
True)
|
||||||
assert mock_ipt_chain_exists.mock_calls == [
|
assert mock_ipt_chain_exists.mock_calls == [
|
||||||
call(10, 'mangle', 'sshuttle-m-1024'),
|
call(10, 'mangle', 'sshuttle-m-1024'),
|
||||||
@ -144,28 +144,30 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
'-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1026'),
|
'-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1026'),
|
||||||
call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN',
|
call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN',
|
||||||
'--dest', u'2404:6800:4004:80c::101f/128',
|
'--dest', u'2404:6800:4004:80c::101f/128',
|
||||||
'-m', 'tcp', '-p', 'tcp'),
|
'-m', 'tcp', '-p', 'tcp', '--dport', '8080:8080'),
|
||||||
call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'RETURN',
|
call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'RETURN',
|
||||||
'--dest', u'2404:6800:4004:80c::101f/128',
|
'--dest', u'2404:6800:4004:80c::101f/128',
|
||||||
'-m', 'tcp', '-p', 'tcp'),
|
'-m', 'tcp', '-p', 'tcp', '--dport', '8080:8080'),
|
||||||
call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN',
|
call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN',
|
||||||
'--dest', u'2404:6800:4004:80c::101f/128',
|
'--dest', u'2404:6800:4004:80c::101f/128',
|
||||||
'-m', 'udp', '-p', 'udp'),
|
'-m', 'udp', '-p', 'udp', '--dport', '8080:8080'),
|
||||||
call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'RETURN',
|
call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'RETURN',
|
||||||
'--dest', u'2404:6800:4004:80c::101f/128',
|
'--dest', u'2404:6800:4004:80c::101f/128',
|
||||||
'-m', 'udp', '-p', 'udp'),
|
'-m', 'udp', '-p', 'udp', '--dport', '8080:8080'),
|
||||||
call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
|
call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
|
||||||
'--set-mark', '1', '--dest', u'2404:6800:4004:80c::/64',
|
'--set-mark', '1', '--dest', u'2404:6800:4004:80c::/64',
|
||||||
'-m', 'tcp', '-p', 'tcp'),
|
'-m', 'tcp', '-p', 'tcp', '--dport', '8000:9000'),
|
||||||
call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
|
call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x1/0x1', '--dest', u'2404:6800:4004:80c::/64',
|
'--tproxy-mark', '0x1/0x1', '--dest', u'2404:6800:4004:80c::/64',
|
||||||
'-m', 'tcp', '-p', 'tcp', '--on-port', '1024'),
|
'-m', 'tcp', '-p', 'tcp', '--dport', '8000:9000',
|
||||||
|
'--on-port', '1024'),
|
||||||
call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
|
call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
|
||||||
'--set-mark', '1', '--dest', u'2404:6800:4004:80c::/64',
|
'--set-mark', '1', '--dest', u'2404:6800:4004:80c::/64',
|
||||||
'-m', 'udp', '-p', 'udp'),
|
'-m', 'udp', '-p', 'udp'),
|
||||||
call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
|
call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x1/0x1', '--dest', u'2404:6800:4004:80c::/64',
|
'--tproxy-mark', '0x1/0x1', '--dest', u'2404:6800:4004:80c::/64',
|
||||||
'-m', 'udp', '-p', 'udp', '--on-port', '1024')
|
'-m', 'udp', '-p', 'udp', '--dport', '8000:9000',
|
||||||
|
'--on-port', '1024')
|
||||||
]
|
]
|
||||||
mock_ipt_chain_exists.reset_mock()
|
mock_ipt_chain_exists.reset_mock()
|
||||||
mock_ipt_ttl.reset_mock()
|
mock_ipt_ttl.reset_mock()
|
||||||
@ -198,7 +200,7 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
1025, 1027,
|
1025, 1027,
|
||||||
[(2, u'1.2.3.33')],
|
[(2, u'1.2.3.33')],
|
||||||
2,
|
2,
|
||||||
[(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
|
[(2, 24, False, u'1.2.3.0', 0, 0), (2, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
True)
|
True)
|
||||||
assert mock_ipt_chain_exists.mock_calls == [
|
assert mock_ipt_chain_exists.mock_calls == [
|
||||||
call(2, 'mangle', 'sshuttle-m-1025'),
|
call(2, 'mangle', 'sshuttle-m-1025'),
|
||||||
@ -237,13 +239,17 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
'--tproxy-mark', '0x1/0x1', '--dest', u'1.2.3.33/32',
|
'--tproxy-mark', '0x1/0x1', '--dest', u'1.2.3.33/32',
|
||||||
'-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1027'),
|
'-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1027'),
|
||||||
call(2, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN',
|
call(2, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN',
|
||||||
'--dest', u'1.2.3.66/32', '-m', 'tcp', '-p', 'tcp'),
|
'--dest', u'1.2.3.66/32', '-m', 'tcp', '-p', 'tcp',
|
||||||
|
'--dport', '80:80'),
|
||||||
call(2, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'RETURN',
|
call(2, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'RETURN',
|
||||||
'--dest', u'1.2.3.66/32', '-m', 'tcp', '-p', 'tcp'),
|
'--dest', u'1.2.3.66/32', '-m', 'tcp', '-p', 'tcp',
|
||||||
|
'--dport', '80:80'),
|
||||||
call(2, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN',
|
call(2, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN',
|
||||||
'--dest', u'1.2.3.66/32', '-m', 'udp', '-p', 'udp'),
|
'--dest', u'1.2.3.66/32', '-m', 'udp', '-p', 'udp',
|
||||||
|
'--dport', '80:80'),
|
||||||
call(2, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'RETURN',
|
call(2, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'RETURN',
|
||||||
'--dest', u'1.2.3.66/32', '-m', 'udp', '-p', 'udp'),
|
'--dest', u'1.2.3.66/32', '-m', 'udp', '-p', 'udp',
|
||||||
|
'--dport', '80:80'),
|
||||||
call(2, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
|
call(2, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
|
||||||
'--set-mark', '1', '--dest', u'1.2.3.0/24',
|
'--set-mark', '1', '--dest', u'1.2.3.0/24',
|
||||||
'-m', 'tcp', '-p', 'tcp'),
|
'-m', 'tcp', '-p', 'tcp'),
|
||||||
|
101
sshuttle/tests/client/test_options.py
Normal file
101
sshuttle/tests/client/test_options.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import socket
|
||||||
|
import pytest
|
||||||
|
import sshuttle.options
|
||||||
|
from argparse import ArgumentTypeError as Fatal
|
||||||
|
|
||||||
|
_ip4_reprs = {
|
||||||
|
'0.0.0.0': '0.0.0.0',
|
||||||
|
'255.255.255.255': '255.255.255.255',
|
||||||
|
'10.0': '10.0.0.0',
|
||||||
|
'184.172.10.74': '184.172.10.74',
|
||||||
|
'3098282570': '184.172.10.74',
|
||||||
|
'0xb8.0xac.0x0a.0x4a': '184.172.10.74',
|
||||||
|
'0270.0254.0012.0112': '184.172.10.74',
|
||||||
|
'localhost': '127.0.0.1'
|
||||||
|
}
|
||||||
|
|
||||||
|
_ip4_swidths = (1, 8, 22, 27, 32)
|
||||||
|
|
||||||
|
_ip6_reprs = {
|
||||||
|
'::': '::',
|
||||||
|
'::1': '::1',
|
||||||
|
'fc00::': 'fc00::',
|
||||||
|
'2a01:7e00:e000:188::1': '2a01:7e00:e000:188::1'
|
||||||
|
}
|
||||||
|
|
||||||
|
_ip6_swidths = (48, 64, 96, 115, 128)
|
||||||
|
|
||||||
|
def test_parse_subnetport_ip4():
|
||||||
|
for ip_repr, ip in _ip4_reprs.items():
|
||||||
|
assert sshuttle.options.parse_subnetport(ip_repr) \
|
||||||
|
== (socket.AF_INET, ip, 32, 0, 0)
|
||||||
|
with pytest.raises(Fatal) as excinfo:
|
||||||
|
sshuttle.options.parse_subnetport('10.256.0.0')
|
||||||
|
assert str(excinfo.value) == 'Unable to resolve address: 10.256.0.0'
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_subnetport_ip4_with_mask():
|
||||||
|
for ip_repr, ip in _ip4_reprs.items():
|
||||||
|
for swidth in _ip4_swidths:
|
||||||
|
assert sshuttle.options.parse_subnetport(
|
||||||
|
'/'.join((ip_repr, str(swidth)))
|
||||||
|
) == (socket.AF_INET, ip, swidth, 0, 0)
|
||||||
|
assert sshuttle.options.parse_subnetport('0/0') \
|
||||||
|
== (socket.AF_INET, '0.0.0.0', 0, 0, 0)
|
||||||
|
with pytest.raises(Fatal) as excinfo:
|
||||||
|
sshuttle.options.parse_subnetport('10.0.0.0/33')
|
||||||
|
assert str(excinfo.value) == 'width 33 is not between 0 and 32'
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_subnetport_ip4_with_port():
|
||||||
|
for ip_repr, ip in _ip4_reprs.items():
|
||||||
|
assert sshuttle.options.parse_subnetport(':'.join((ip_repr, '80'))) \
|
||||||
|
== (socket.AF_INET, ip, 32, 80, 80)
|
||||||
|
assert sshuttle.options.parse_subnetport(':'.join((ip_repr, '80-90'))) \
|
||||||
|
== (socket.AF_INET, ip, 32, 80, 90)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_subnetport_ip4_with_mask_and_port():
|
||||||
|
for ip_repr, ip in _ip4_reprs.items():
|
||||||
|
assert sshuttle.options.parse_subnetport(ip_repr + '/32:80') \
|
||||||
|
== (socket.AF_INET, ip, 32, 80, 80)
|
||||||
|
assert sshuttle.options.parse_subnetport(ip_repr + '/16:80-90') \
|
||||||
|
== (socket.AF_INET, ip, 16, 80, 90)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_subnetport_ip6():
|
||||||
|
for ip_repr, ip in _ip6_reprs.items():
|
||||||
|
assert sshuttle.options.parse_subnetport(ip_repr) \
|
||||||
|
== (socket.AF_INET6, ip, 128, 0, 0)
|
||||||
|
with pytest.raises(Fatal) as excinfo:
|
||||||
|
sshuttle.options.parse_subnetport('2001::1::3f')
|
||||||
|
assert str(excinfo.value) == 'Unable to resolve address: 2001::1::3f'
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_subnetport_ip6_with_mask():
|
||||||
|
for ip_repr, ip in _ip6_reprs.items():
|
||||||
|
for swidth in _ip4_swidths + _ip6_swidths:
|
||||||
|
assert sshuttle.options.parse_subnetport(
|
||||||
|
'/'.join((ip_repr, str(swidth)))
|
||||||
|
) == (socket.AF_INET6, ip, swidth, 0, 0)
|
||||||
|
assert sshuttle.options.parse_subnetport('::/0') \
|
||||||
|
== (socket.AF_INET6, '::', 0, 0, 0)
|
||||||
|
with pytest.raises(Fatal) as excinfo:
|
||||||
|
sshuttle.options.parse_subnetport('fc00::/129')
|
||||||
|
assert str(excinfo.value) == 'width 129 is not between 0 and 128'
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_subnetport_ip6_with_port():
|
||||||
|
for ip_repr, ip in _ip6_reprs.items():
|
||||||
|
assert sshuttle.options.parse_subnetport('[' + ip_repr + ']:80') \
|
||||||
|
== (socket.AF_INET6, ip, 128, 80, 80)
|
||||||
|
assert sshuttle.options.parse_subnetport('[' + ip_repr + ']:80-90') \
|
||||||
|
== (socket.AF_INET6, ip, 128, 80, 90)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_subnetport_ip6_with_mask_and_port():
|
||||||
|
for ip_repr, ip in _ip6_reprs.items():
|
||||||
|
assert sshuttle.options.parse_subnetport('[' + ip_repr + '/128]:80') \
|
||||||
|
== (socket.AF_INET6, ip, 128, 80, 80)
|
||||||
|
assert sshuttle.options.parse_subnetport('[' + ip_repr + '/16]:80-90') \
|
||||||
|
== (socket.AF_INET6, ip, 16, 80, 90)
|
Loading…
Reference in New Issue
Block a user