mirror of
https://github.com/sshuttle/sshuttle.git
synced 2025-07-05 01:00:37 +02:00
Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
2c20a1fd5a | |||
915f72de35 | |||
1ffc3f52a1 | |||
8520ea2787 | |||
6a394deaf2 | |||
83d5c59a57 | |||
1cfd9eb9d7 | |||
f8d58fa4f0 | |||
d2d5a37541 | |||
e9be2deea0 | |||
22b1b54bfd | |||
a43c668dde | |||
e0dfb95596 | |||
5d28ce8272 | |||
f876c5db5e | |||
2e1beefc9a | |||
5a20783baa | |||
495b3c39ea | |||
f3cbc5018a | |||
e73e797f33 | |||
1d64879613 | |||
8fad282bfd | |||
1dda9dd621 | |||
74e308a29f | |||
516ff7bc4a | |||
89c5b57019 |
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,4 +1,13 @@
|
|||||||
sshuttle/version.py
|
/sshuttle/version.py
|
||||||
|
/docs/conf.py
|
||||||
|
/tmp/
|
||||||
|
/.cache/
|
||||||
|
/.eggs/
|
||||||
|
/.tox/
|
||||||
|
/build/
|
||||||
|
/dist/
|
||||||
|
/sshuttle.egg-info/
|
||||||
|
/docs/_build/
|
||||||
*.pyc
|
*.pyc
|
||||||
*~
|
*~
|
||||||
*.8
|
*.8
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
|
Release 0.78.1 (6th August, 2016)
|
||||||
|
=================================
|
||||||
|
* Fix readthedocs versioning.
|
||||||
|
* Don't crash on ENETUNREACH.
|
||||||
|
* Various bug fixes.
|
||||||
|
* Improvements to BSD and OSX support.
|
||||||
|
|
||||||
|
|
||||||
Release 0.78.0 (Apr 8, 2016)
|
Release 0.78.0 (Apr 8, 2016)
|
||||||
============================
|
============================
|
||||||
|
|
||||||
|
@ -6,8 +6,10 @@ include LICENSE
|
|||||||
include run
|
include run
|
||||||
include tox.ini
|
include tox.ini
|
||||||
exclude sshuttle/version.py
|
exclude sshuttle/version.py
|
||||||
|
exclude docs/conf.py
|
||||||
recursive-include docs *.bat
|
recursive-include docs *.bat
|
||||||
recursive-include docs *.py
|
recursive-include docs *.py
|
||||||
recursive-include docs *.rst
|
recursive-include docs *.rst
|
||||||
recursive-include docs Makefile
|
recursive-include docs Makefile
|
||||||
recursive-include sshuttle *.py
|
recursive-include sshuttle *.py
|
||||||
|
recursive-exclude docs/_build *
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
# import sys
|
# import sys
|
||||||
# import os
|
# import os
|
||||||
|
from setuptools_scm import get_version
|
||||||
|
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
@ -54,7 +55,6 @@ copyright = '2016, Brian May'
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
from setuptools_scm import get_version
|
|
||||||
version = get_version(root="..")
|
version = get_version(root="..")
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = version
|
release = version
|
@ -136,6 +136,10 @@ Options
|
|||||||
if you use this option to give it a few names to start
|
if you use this option to give it a few names to start
|
||||||
from.
|
from.
|
||||||
|
|
||||||
|
If this option is used *without* :option:`--auto-hosts`,
|
||||||
|
then the listed hostnames will be scanned and added, but
|
||||||
|
no further hostnames will be added.
|
||||||
|
|
||||||
.. option:: --no-latency-control
|
.. option:: --no-latency-control
|
||||||
|
|
||||||
Sacrifice latency to improve bandwidth benchmarks. ssh
|
Sacrifice latency to improve bandwidth benchmarks. ssh
|
||||||
|
7
setup.py
7
setup.py
@ -18,7 +18,12 @@
|
|||||||
# along with python-tldap If not, see <http://www.gnu.org/licenses/>.
|
# along with python-tldap If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
with open("./docs/conf.orig.py", "r") as src:
|
||||||
|
with open("./docs/conf.py", "w") as dst:
|
||||||
|
dst.write("# FILE COPIED FROM conf.orig.py; DO NOT CHANGE\n")
|
||||||
|
shutil.copyfileobj(src, dst)
|
||||||
|
|
||||||
def version_scheme(version):
|
def version_scheme(version):
|
||||||
from setuptools_scm.version import guess_next_dev_version
|
from setuptools_scm.version import guess_next_dev_version
|
||||||
@ -31,7 +36,7 @@ setup(
|
|||||||
'write_to': "sshuttle/version.py",
|
'write_to': "sshuttle/version.py",
|
||||||
'version_scheme': version_scheme,
|
'version_scheme': version_scheme,
|
||||||
},
|
},
|
||||||
setup_requires=['setuptools_scm'],
|
setup_requires=['setuptools_scm', 'pytest-runner'],
|
||||||
# version=version,
|
# version=version,
|
||||||
url='https://github.com/sshuttle/sshuttle',
|
url='https://github.com/sshuttle/sshuttle',
|
||||||
author='Brian May',
|
author='Brian May',
|
||||||
|
@ -34,4 +34,4 @@ sshuttle.helpers.verbose = verbosity
|
|||||||
|
|
||||||
import sshuttle.cmdline_options as options
|
import sshuttle.cmdline_options as options
|
||||||
from sshuttle.server import main
|
from sshuttle.server import main
|
||||||
main(options.latency_control)
|
main(options.latency_control, options.auto_hosts)
|
||||||
|
@ -400,7 +400,7 @@ def ondns(listener, method, mux, handlers):
|
|||||||
|
|
||||||
def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
||||||
python, latency_control,
|
python, latency_control,
|
||||||
dns_listener, seed_hosts, auto_nets, daemon):
|
dns_listener, seed_hosts, auto_hosts, auto_nets, daemon):
|
||||||
|
|
||||||
debug1('Starting client with Python version %s\n'
|
debug1('Starting client with Python version %s\n'
|
||||||
% platform.python_version())
|
% platform.python_version())
|
||||||
@ -418,7 +418,8 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
|||||||
(serverproc, serversock) = ssh.connect(
|
(serverproc, serversock) = ssh.connect(
|
||||||
ssh_cmd, remotename, python,
|
ssh_cmd, remotename, python,
|
||||||
stderr=ssyslog._p and ssyslog._p.stdin,
|
stderr=ssyslog._p and ssyslog._p.stdin,
|
||||||
options=dict(latency_control=latency_control))
|
options=dict(latency_control=latency_control,
|
||||||
|
auto_hosts=auto_hosts))
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
if e.args[0] == errno.EPIPE:
|
if e.args[0] == errno.EPIPE:
|
||||||
raise Fatal("failed to establish ssh session (1)")
|
raise Fatal("failed to establish ssh session (1)")
|
||||||
@ -514,7 +515,7 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
|||||||
|
|
||||||
def main(listenip_v6, listenip_v4,
|
def main(listenip_v6, listenip_v4,
|
||||||
ssh_cmd, remotename, python, latency_control, dns, nslist,
|
ssh_cmd, remotename, python, latency_control, dns, nslist,
|
||||||
method_name, seed_hosts, auto_nets,
|
method_name, seed_hosts, auto_hosts, auto_nets,
|
||||||
subnets_include, subnets_exclude, daemon, pidfile):
|
subnets_include, subnets_exclude, daemon, pidfile):
|
||||||
|
|
||||||
if daemon:
|
if daemon:
|
||||||
@ -548,6 +549,7 @@ def main(listenip_v6, listenip_v4,
|
|||||||
listenip_v6 = None
|
listenip_v6 = None
|
||||||
|
|
||||||
required.ipv6 = len(subnets_v6) > 0 or listenip_v6 is not None
|
required.ipv6 = len(subnets_v6) > 0 or listenip_v6 is not None
|
||||||
|
required.ipv4 = len(subnets_v4) > 0 or listenip_v4 is not None
|
||||||
required.udp = avail.udp
|
required.udp = avail.udp
|
||||||
required.dns = len(nslist) > 0
|
required.dns = len(nslist) > 0
|
||||||
|
|
||||||
@ -570,6 +572,14 @@ def main(listenip_v6, listenip_v4,
|
|||||||
if listenip_v4 == "auto":
|
if listenip_v4 == "auto":
|
||||||
listenip_v4 = ('127.0.0.1', 0)
|
listenip_v4 = ('127.0.0.1', 0)
|
||||||
|
|
||||||
|
if required.ipv4 and \
|
||||||
|
not any(listenip_v4[0] == sex[1] for sex in subnets_v4):
|
||||||
|
subnets_exclude.append((socket.AF_INET, listenip_v4[0], 32))
|
||||||
|
|
||||||
|
if required.ipv6 and \
|
||||||
|
not any(listenip_v6[0] == sex[1] for sex in subnets_v6):
|
||||||
|
subnets_exclude.append((socket.AF_INET6, listenip_v6[0], 128))
|
||||||
|
|
||||||
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
|
||||||
ports = [0, ]
|
ports = [0, ]
|
||||||
@ -713,7 +723,7 @@ def main(listenip_v6, listenip_v4,
|
|||||||
try:
|
try:
|
||||||
return _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
return _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
||||||
python, latency_control, dns_listener,
|
python, latency_control, dns_listener,
|
||||||
seed_hosts, auto_nets, daemon)
|
seed_hosts, auto_hosts, auto_nets, daemon)
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
if daemon:
|
if daemon:
|
||||||
|
@ -29,13 +29,12 @@ def main():
|
|||||||
includes = opt.subnets
|
includes = opt.subnets
|
||||||
excludes = opt.exclude
|
excludes = opt.exclude
|
||||||
if not includes and not opt.auto_nets:
|
if not includes and not opt.auto_nets:
|
||||||
parser.error('at least one subnet, subnet file, or -N expected')
|
parser.error('at least one subnet, subnet file, '
|
||||||
|
'or -N expected')
|
||||||
remotename = opt.remote
|
remotename = opt.remote
|
||||||
if remotename == '' or remotename == '-':
|
if remotename == '' or remotename == '-':
|
||||||
remotename = None
|
remotename = None
|
||||||
nslist = [family_ip_tuple(ns) for ns in opt.ns_hosts]
|
nslist = [family_ip_tuple(ns) for ns in opt.ns_hosts]
|
||||||
if opt.seed_hosts and not opt.auto_hosts:
|
|
||||||
parser.error('--seed-hosts only works if you also use -H')
|
|
||||||
if opt.seed_hosts:
|
if opt.seed_hosts:
|
||||||
sh = re.split(r'[\s,]+', (opt.seed_hosts or "").strip())
|
sh = re.split(r'[\s,]+', (opt.seed_hosts or "").strip())
|
||||||
elif opt.auto_hosts:
|
elif opt.auto_hosts:
|
||||||
@ -68,6 +67,7 @@ def main():
|
|||||||
nslist,
|
nslist,
|
||||||
opt.method,
|
opt.method,
|
||||||
sh,
|
sh,
|
||||||
|
opt.auto_hosts,
|
||||||
opt.auto_nets,
|
opt.auto_nets,
|
||||||
includes,
|
includes,
|
||||||
excludes,
|
excludes,
|
||||||
|
@ -16,6 +16,7 @@ else:
|
|||||||
def b(s):
|
def b(s):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def log(s):
|
def log(s):
|
||||||
global logprefix
|
global logprefix
|
||||||
try:
|
try:
|
||||||
|
@ -70,8 +70,8 @@ def read_host_cache():
|
|||||||
def found_host(hostname, ip):
|
def found_host(hostname, ip):
|
||||||
hostname = re.sub(r'\..*', '', hostname)
|
hostname = re.sub(r'\..*', '', hostname)
|
||||||
hostname = re.sub(r'[^-\w]', '_', hostname)
|
hostname = re.sub(r'[^-\w]', '_', hostname)
|
||||||
if (ip.startswith('127.') or ip.startswith('255.')
|
if (ip.startswith('127.') or ip.startswith('255.') or
|
||||||
or hostname == 'localhost'):
|
hostname == 'localhost'):
|
||||||
return
|
return
|
||||||
oldip = hostnames.get(hostname)
|
oldip = hostnames.get(hostname)
|
||||||
if oldip != ip:
|
if oldip != ip:
|
||||||
@ -255,7 +255,7 @@ def _stdin_still_ok(timeout):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def hw_main(seed_hosts):
|
def hw_main(seed_hosts, auto_hosts):
|
||||||
if helpers.verbose >= 2:
|
if helpers.verbose >= 2:
|
||||||
helpers.logprefix = 'HH: '
|
helpers.logprefix = 'HH: '
|
||||||
else:
|
else:
|
||||||
@ -264,16 +264,17 @@ def hw_main(seed_hosts):
|
|||||||
debug1('Starting hostwatch with Python version %s\n'
|
debug1('Starting hostwatch with Python version %s\n'
|
||||||
% platform.python_version())
|
% platform.python_version())
|
||||||
|
|
||||||
read_host_cache()
|
for h in seed_hosts:
|
||||||
|
check_host(h)
|
||||||
|
|
||||||
|
if auto_hosts:
|
||||||
|
read_host_cache()
|
||||||
_enqueue(_check_etc_hosts)
|
_enqueue(_check_etc_hosts)
|
||||||
_enqueue(_check_netstat)
|
_enqueue(_check_netstat)
|
||||||
check_host('localhost')
|
check_host('localhost')
|
||||||
check_host(socket.gethostname())
|
check_host(socket.gethostname())
|
||||||
check_workgroup('workgroup')
|
check_workgroup('workgroup')
|
||||||
check_workgroup('-')
|
check_workgroup('-')
|
||||||
for h in seed_hosts:
|
|
||||||
check_host(h)
|
|
||||||
|
|
||||||
while 1:
|
while 1:
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
import socket
|
import socket
|
||||||
import subprocess as ssubprocess
|
import subprocess as ssubprocess
|
||||||
from sshuttle.helpers import log, debug1, Fatal, family_to_string
|
from sshuttle.helpers import log, debug1, Fatal, family_to_string
|
||||||
@ -18,7 +19,11 @@ def ipt_chain_exists(family, table, name):
|
|||||||
else:
|
else:
|
||||||
raise Exception('Unsupported family "%s"' % family_to_string(family))
|
raise Exception('Unsupported family "%s"' % family_to_string(family))
|
||||||
argv = [cmd, '-t', table, '-nL']
|
argv = [cmd, '-t', table, '-nL']
|
||||||
p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE)
|
env = {
|
||||||
|
'PATH': os.environ['PATH'],
|
||||||
|
'LC_ALL': "C",
|
||||||
|
}
|
||||||
|
p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, env=env)
|
||||||
for line in p.stdout:
|
for line in p.stdout:
|
||||||
if line.startswith(b'Chain %s ' % name.encode("ASCII")):
|
if line.startswith(b'Chain %s ' % name.encode("ASCII")):
|
||||||
return True
|
return True
|
||||||
@ -35,7 +40,11 @@ def ipt(family, table, *args):
|
|||||||
else:
|
else:
|
||||||
raise Exception('Unsupported family "%s"' % family_to_string(family))
|
raise Exception('Unsupported family "%s"' % family_to_string(family))
|
||||||
debug1('>> %s\n' % ' '.join(argv))
|
debug1('>> %s\n' % ' '.join(argv))
|
||||||
rv = ssubprocess.call(argv)
|
env = {
|
||||||
|
'PATH': os.environ['PATH'],
|
||||||
|
'LC_ALL': "C",
|
||||||
|
}
|
||||||
|
rv = ssubprocess.call(argv, env=env)
|
||||||
if rv:
|
if rv:
|
||||||
raise Fatal('%r returned %d' % (argv, rv))
|
raise Fatal('%r returned %d' % (argv, rv))
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ from sshuttle.helpers import debug1, debug2, debug3, Fatal, family_to_string
|
|||||||
from sshuttle.methods import BaseMethod
|
from sshuttle.methods import BaseMethod
|
||||||
|
|
||||||
|
|
||||||
_pf_context = {'started_by_sshuttle': False, 'Xtoken': None}
|
_pf_context = {'started_by_sshuttle': False, 'Xtoken': []}
|
||||||
_pf_fd = None
|
_pf_fd = None
|
||||||
|
|
||||||
|
|
||||||
@ -59,7 +59,8 @@ class Generic(object):
|
|||||||
pfctl('-e')
|
pfctl('-e')
|
||||||
_pf_context['started_by_sshuttle'] = True
|
_pf_context['started_by_sshuttle'] = True
|
||||||
|
|
||||||
def disable(self):
|
def disable(self, anchor):
|
||||||
|
pfctl('-a %s -F all' % anchor)
|
||||||
if _pf_context['started_by_sshuttle']:
|
if _pf_context['started_by_sshuttle']:
|
||||||
pfctl('-d')
|
pfctl('-d')
|
||||||
|
|
||||||
@ -96,12 +97,12 @@ class Generic(object):
|
|||||||
def _get_natlook_port(self, xport):
|
def _get_natlook_port(self, xport):
|
||||||
return xport
|
return xport
|
||||||
|
|
||||||
def add_anchors(self, status=None):
|
def add_anchors(self, anchor, status=None):
|
||||||
if status is None:
|
if status is None:
|
||||||
status = pfctl('-s all')[0]
|
status = pfctl('-s all')[0]
|
||||||
self.status = status
|
self.status = status
|
||||||
if b'\nanchor "sshuttle"' not in status:
|
if ('\nanchor "%s"' % anchor).encode('ASCII') not in status:
|
||||||
self._add_anchor_rule(self.PF_PASS, b"sshuttle")
|
self._add_anchor_rule(self.PF_PASS, anchor.encode('ASCII'))
|
||||||
|
|
||||||
def _add_anchor_rule(self, type, name, pr=None):
|
def _add_anchor_rule(self, type, name, pr=None):
|
||||||
if pr is None:
|
if pr is None:
|
||||||
@ -120,10 +121,20 @@ class Generic(object):
|
|||||||
'I', self.PF_CHANGE_ADD_TAIL), 4) # action = PF_CHANGE_ADD_TAIL
|
'I', self.PF_CHANGE_ADD_TAIL), 4) # action = PF_CHANGE_ADD_TAIL
|
||||||
ioctl(pf_get_dev(), pf.DIOCCHANGERULE, pr)
|
ioctl(pf_get_dev(), pf.DIOCCHANGERULE, pr)
|
||||||
|
|
||||||
def add_rules(self, rules):
|
def _inet_version(self, family):
|
||||||
|
return b'inet' if family == socket.AF_INET else b'inet6'
|
||||||
|
|
||||||
|
def _lo_addr(self, family):
|
||||||
|
return b'127.0.0.1' if family == socket.AF_INET else b'::1'
|
||||||
|
|
||||||
|
def add_rules(self, anchor, rules):
|
||||||
assert isinstance(rules, bytes)
|
assert isinstance(rules, bytes)
|
||||||
debug3("rules:\n" + rules.decode("ASCII"))
|
debug3("rules:\n" + rules.decode("ASCII"))
|
||||||
pfctl('-a sshuttle -f /dev/stdin', rules)
|
pfctl('-a %s -f /dev/stdin' % anchor, rules)
|
||||||
|
|
||||||
|
def has_skip_loopback(self):
|
||||||
|
return b'skip' in pfctl('-s Interfaces -i lo -v')[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FreeBsd(Generic):
|
class FreeBsd(Generic):
|
||||||
@ -153,11 +164,11 @@ class FreeBsd(Generic):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(FreeBsd, self).__init__()
|
super(FreeBsd, self).__init__()
|
||||||
|
|
||||||
def add_anchors(self):
|
def add_anchors(self, anchor):
|
||||||
status = pfctl('-s all')[0]
|
status = pfctl('-s all')[0]
|
||||||
if b'\nrdr-anchor "sshuttle"' not in status:
|
if ('\nrdr-anchor "%s"' % anchor).encode('ASCII') not in status:
|
||||||
self._add_anchor_rule(self.PF_RDR, b'sshuttle')
|
self._add_anchor_rule(self.PF_RDR, anchor.encode('ASCII'))
|
||||||
super(FreeBsd, self).add_anchors(status=status)
|
super(FreeBsd, self).add_anchors(anchor, status=status)
|
||||||
|
|
||||||
def _add_anchor_rule(self, type, name):
|
def _add_anchor_rule(self, type, name):
|
||||||
pr = self.pfioc_rule()
|
pr = self.pfioc_rule()
|
||||||
@ -168,17 +179,20 @@ class FreeBsd(Generic):
|
|||||||
memmove(addressof(pr) + self.POOL_TICKET_OFFSET, ppa[4:8], 4)
|
memmove(addressof(pr) + self.POOL_TICKET_OFFSET, ppa[4:8], 4)
|
||||||
super(FreeBsd, self)._add_anchor_rule(type, name, pr=pr)
|
super(FreeBsd, self)._add_anchor_rule(type, name, pr=pr)
|
||||||
|
|
||||||
def add_rules(self, includes, port, dnsport, nslist):
|
def add_rules(self, anchor, includes, port, dnsport, nslist, family):
|
||||||
|
inet_version = self._inet_version(family)
|
||||||
|
lo_addr = self._lo_addr(family)
|
||||||
|
|
||||||
tables = [
|
tables = [
|
||||||
b'table <forward_subnets> {%s}' % b','.join(includes)
|
b'table <forward_subnets> {%s}' % b','.join(includes)
|
||||||
]
|
]
|
||||||
translating_rules = [
|
translating_rules = [
|
||||||
b'rdr pass on lo0 proto tcp '
|
b'rdr pass on lo0 %s proto tcp to <forward_subnets> '
|
||||||
b'to <forward_subnets> -> 127.0.0.1 port %r' % port
|
b'-> %s port %r' % (inet_version, lo_addr, port)
|
||||||
]
|
]
|
||||||
filtering_rules = [
|
filtering_rules = [
|
||||||
b'pass out route-to lo0 inet proto tcp '
|
b'pass out route-to lo0 %s proto tcp '
|
||||||
b'to <forward_subnets> keep state'
|
b'to <forward_subnets> keep state' % inet_version
|
||||||
]
|
]
|
||||||
|
|
||||||
if len(nslist) > 0:
|
if len(nslist) > 0:
|
||||||
@ -186,16 +200,16 @@ class FreeBsd(Generic):
|
|||||||
b'table <dns_servers> {%s}' %
|
b'table <dns_servers> {%s}' %
|
||||||
b','.join([ns[1].encode("ASCII") for ns in nslist]))
|
b','.join([ns[1].encode("ASCII") for ns in nslist]))
|
||||||
translating_rules.append(
|
translating_rules.append(
|
||||||
b'rdr pass on lo0 proto udp to '
|
b'rdr pass on lo0 %s proto udp to <dns_servers> '
|
||||||
b'<dns_servers> port 53 -> 127.0.0.1 port %r' % dnsport)
|
b'port 53 -> %s port %r' % (inet_version, lo_addr, dnsport))
|
||||||
filtering_rules.append(
|
filtering_rules.append(
|
||||||
b'pass out route-to lo0 inet proto udp to '
|
b'pass out route-to lo0 %s proto udp to '
|
||||||
b'<dns_servers> port 53 keep state')
|
b'<dns_servers> port 53 keep state' % inet_version)
|
||||||
|
|
||||||
rules = b'\n'.join(tables + translating_rules + filtering_rules) \
|
rules = b'\n'.join(tables + translating_rules + filtering_rules) \
|
||||||
+ b'\n'
|
+ b'\n'
|
||||||
|
|
||||||
super(FreeBsd, self).add_rules(rules)
|
super(FreeBsd, self).add_rules(anchor, rules)
|
||||||
|
|
||||||
|
|
||||||
class OpenBsd(Generic):
|
class OpenBsd(Generic):
|
||||||
@ -225,24 +239,28 @@ class OpenBsd(Generic):
|
|||||||
self.pfioc_natlook = pfioc_natlook
|
self.pfioc_natlook = pfioc_natlook
|
||||||
super(OpenBsd, self).__init__()
|
super(OpenBsd, self).__init__()
|
||||||
|
|
||||||
def add_anchors(self):
|
def add_anchors(self, anchor):
|
||||||
# before adding anchors and rules we must override the skip lo
|
# before adding anchors and rules we must override the skip lo
|
||||||
# that comes by default in openbsd pf.conf so the rules we will add,
|
# that comes by default in openbsd pf.conf so the rules we will add,
|
||||||
# which rely on translating/filtering packets on lo, can work
|
# which rely on translating/filtering packets on lo, can work
|
||||||
|
if self.has_skip_loopback():
|
||||||
pfctl('-f /dev/stdin', b'match on lo\n')
|
pfctl('-f /dev/stdin', b'match on lo\n')
|
||||||
super(OpenBsd, self).add_anchors()
|
super(OpenBsd, self).add_anchors(anchor)
|
||||||
|
|
||||||
|
def add_rules(self, anchor, includes, port, dnsport, nslist, family):
|
||||||
|
inet_version = self._inet_version(family)
|
||||||
|
lo_addr = self._lo_addr(family)
|
||||||
|
|
||||||
def add_rules(self, includes, port, dnsport, nslist):
|
|
||||||
tables = [
|
tables = [
|
||||||
b'table <forward_subnets> {%s}' % b','.join(includes)
|
b'table <forward_subnets> {%s}' % b','.join(includes)
|
||||||
]
|
]
|
||||||
translating_rules = [
|
translating_rules = [
|
||||||
b'pass in on lo0 inet proto tcp '
|
b'pass in on lo0 %s proto tcp to <forward_subnets> '
|
||||||
b'divert-to 127.0.0.1 port %r' % port
|
b'divert-to %s port %r' % (inet_version, lo_addr, port)
|
||||||
]
|
]
|
||||||
filtering_rules = [
|
filtering_rules = [
|
||||||
b'pass out inet proto tcp '
|
b'pass out %s proto tcp to <forward_subnets> '
|
||||||
b'to <forward_subnets> route-to lo0 keep state'
|
b'route-to lo0 keep state' % inet_version
|
||||||
]
|
]
|
||||||
|
|
||||||
if len(nslist) > 0:
|
if len(nslist) > 0:
|
||||||
@ -250,16 +268,16 @@ class OpenBsd(Generic):
|
|||||||
b'table <dns_servers> {%s}' %
|
b'table <dns_servers> {%s}' %
|
||||||
b','.join([ns[1].encode("ASCII") for ns in nslist]))
|
b','.join([ns[1].encode("ASCII") for ns in nslist]))
|
||||||
translating_rules.append(
|
translating_rules.append(
|
||||||
b'pass in on lo0 inet proto udp to <dns_servers>'
|
b'pass in on lo0 %s proto udp to <dns_servers> port 53 '
|
||||||
b'port 53 rdr-to 127.0.0.1 port %r' % dnsport)
|
b'rdr-to %s port %r' % (inet_version, lo_addr, dnsport))
|
||||||
filtering_rules.append(
|
filtering_rules.append(
|
||||||
b'pass out inet proto udp to '
|
b'pass out %s proto udp to <dns_servers> port 53 '
|
||||||
b'<dns_servers> port 53 route-to lo0 keep state')
|
b'route-to lo0 keep state' % inet_version)
|
||||||
|
|
||||||
rules = b'\n'.join(tables + translating_rules + filtering_rules) \
|
rules = b'\n'.join(tables + translating_rules + filtering_rules) \
|
||||||
+ b'\n'
|
+ b'\n'
|
||||||
|
|
||||||
super(OpenBsd, self).add_rules(rules)
|
super(OpenBsd, self).add_rules(anchor, rules)
|
||||||
|
|
||||||
|
|
||||||
class Darwin(FreeBsd):
|
class Darwin(FreeBsd):
|
||||||
@ -292,19 +310,20 @@ class Darwin(FreeBsd):
|
|||||||
|
|
||||||
def enable(self):
|
def enable(self):
|
||||||
o = pfctl('-E')
|
o = pfctl('-E')
|
||||||
_pf_context['Xtoken'] = \
|
_pf_context['Xtoken'].append(re.search(b'Token : (.+)', o[1]).group(1))
|
||||||
re.search(b'Token : (.+)', o[1]).group(1)
|
|
||||||
|
|
||||||
def disable(self):
|
def disable(self, anchor):
|
||||||
if _pf_context['Xtoken'] is not None:
|
pfctl('-a %s -F all' % anchor)
|
||||||
pfctl('-X %s' % _pf_context['Xtoken'].decode("ASCII"))
|
if _pf_context['Xtoken']:
|
||||||
|
pfctl('-X %s' % _pf_context['Xtoken'].pop().decode("ASCII"))
|
||||||
|
|
||||||
def add_anchors(self):
|
def add_anchors(self, anchor):
|
||||||
# before adding anchors and rules we must override the skip lo
|
# before adding anchors and rules we must override the skip lo
|
||||||
# that in some cases ends up in the chain so the rules we will add,
|
# that in some cases ends up in the chain so the rules we will add,
|
||||||
# which rely on translating/filtering packets on lo, can work
|
# which rely on translating/filtering packets on lo, can work
|
||||||
|
if self.has_skip_loopback():
|
||||||
pfctl('-f /dev/stdin', b'pass on lo\n')
|
pfctl('-f /dev/stdin', b'pass on lo\n')
|
||||||
super(Darwin, self).add_anchors()
|
super(Darwin, self).add_anchors(anchor)
|
||||||
|
|
||||||
def _add_natlook_ports(self, pnl, src_port, dst_port):
|
def _add_natlook_ports(self, pnl, src_port, dst_port):
|
||||||
pnl.sxport.port = socket.htons(src_port)
|
pnl.sxport.port = socket.htons(src_port)
|
||||||
@ -326,9 +345,14 @@ def pfctl(args, stdin=None):
|
|||||||
argv = ['pfctl'] + list(args.split(" "))
|
argv = ['pfctl'] + list(args.split(" "))
|
||||||
debug1('>> %s\n' % ' '.join(argv))
|
debug1('>> %s\n' % ' '.join(argv))
|
||||||
|
|
||||||
|
env = {
|
||||||
|
'PATH': os.environ['PATH'],
|
||||||
|
'LC_ALL': "C",
|
||||||
|
}
|
||||||
p = ssubprocess.Popen(argv, stdin=ssubprocess.PIPE,
|
p = ssubprocess.Popen(argv, stdin=ssubprocess.PIPE,
|
||||||
stdout=ssubprocess.PIPE,
|
stdout=ssubprocess.PIPE,
|
||||||
stderr=ssubprocess.PIPE)
|
stderr=ssubprocess.PIPE,
|
||||||
|
env=env)
|
||||||
o = p.communicate(stdin)
|
o = p.communicate(stdin)
|
||||||
if p.returncode:
|
if p.returncode:
|
||||||
raise Fatal('%r returned %d' % (argv, p.returncode))
|
raise Fatal('%r returned %d' % (argv, p.returncode))
|
||||||
@ -344,9 +368,17 @@ def pf_get_dev():
|
|||||||
return _pf_fd
|
return _pf_fd
|
||||||
|
|
||||||
|
|
||||||
|
def pf_get_anchor(family, port):
|
||||||
|
return 'sshuttle%s-%d' % ('' if family == socket.AF_INET else '6', port)
|
||||||
|
|
||||||
|
|
||||||
class Method(BaseMethod):
|
class Method(BaseMethod):
|
||||||
|
|
||||||
|
def get_supported_features(self):
|
||||||
|
result = super(Method, self).get_supported_features()
|
||||||
|
result.ipv6 = True
|
||||||
|
return result
|
||||||
|
|
||||||
def get_tcp_dstip(self, sock):
|
def get_tcp_dstip(self, sock):
|
||||||
pfile = self.firewall.pfile
|
pfile = self.firewall.pfile
|
||||||
|
|
||||||
@ -372,7 +404,7 @@ class Method(BaseMethod):
|
|||||||
translating_rules = []
|
translating_rules = []
|
||||||
filtering_rules = []
|
filtering_rules = []
|
||||||
|
|
||||||
if family != socket.AF_INET:
|
if family not in [socket.AF_INET, socket.AF_INET6]:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Address family "%s" unsupported by pf method_name'
|
'Address family "%s" unsupported by pf method_name'
|
||||||
% family_to_string(family))
|
% family_to_string(family))
|
||||||
@ -391,20 +423,20 @@ class Method(BaseMethod):
|
|||||||
snet.encode("ASCII"),
|
snet.encode("ASCII"),
|
||||||
swidth))
|
swidth))
|
||||||
|
|
||||||
pf.add_anchors()
|
anchor = pf_get_anchor(family, port)
|
||||||
pf.add_rules(includes, port, dnsport, nslist)
|
pf.add_anchors(anchor)
|
||||||
|
pf.add_rules(anchor, includes, port, dnsport, nslist, family)
|
||||||
pf.enable()
|
pf.enable()
|
||||||
|
|
||||||
def restore_firewall(self, port, family, udp):
|
def restore_firewall(self, port, family, udp):
|
||||||
if family != socket.AF_INET:
|
if family not in [socket.AF_INET, socket.AF_INET6]:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Address family "%s" unsupported by pf method_name'
|
'Address family "%s" unsupported by pf method_name'
|
||||||
% family_to_string(family))
|
% family_to_string(family))
|
||||||
if udp:
|
if udp:
|
||||||
raise Exception("UDP not supported by pf method_name")
|
raise Exception("UDP not supported by pf method_name")
|
||||||
|
|
||||||
pfctl('-a sshuttle -F all')
|
pf.disable(pf_get_anchor(family, port))
|
||||||
pf.disable()
|
|
||||||
|
|
||||||
def firewall_command(self, line):
|
def firewall_command(self, line):
|
||||||
if line.startswith('QUERY_PF_NAT '):
|
if line.startswith('QUERY_PF_NAT '):
|
||||||
|
@ -134,7 +134,7 @@ parser.add_argument(
|
|||||||
"-H", "--auto-hosts",
|
"-H", "--auto-hosts",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="""
|
help="""
|
||||||
scan for remote hostnames and update local /etc/hosts
|
continuously scan for remote hostnames and update local /etc/hosts as they are found
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@ -187,7 +187,7 @@ parser.add_argument(
|
|||||||
"-x", "--exclude",
|
"-x", "--exclude",
|
||||||
metavar="IP/MASK",
|
metavar="IP/MASK",
|
||||||
action="append",
|
action="append",
|
||||||
default=[parse_subnet('127.0.0.1/8')],
|
default=[],
|
||||||
type=parse_subnet,
|
type=parse_subnet,
|
||||||
help="""
|
help="""
|
||||||
exclude this subnet (can be used more than once)
|
exclude this subnet (can be used more than once)
|
||||||
@ -232,7 +232,7 @@ parser.add_argument(
|
|||||||
metavar="HOSTNAME[,HOSTNAME]",
|
metavar="HOSTNAME[,HOSTNAME]",
|
||||||
default=[],
|
default=[],
|
||||||
help="""
|
help="""
|
||||||
with -H, use these hostnames for initial scan (comma-separated)
|
comma-separated list of hostnames for initial scan (may be used with or without --auto-hosts)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -63,7 +63,11 @@ def _shl(n, bits):
|
|||||||
def _list_routes():
|
def _list_routes():
|
||||||
# FIXME: IPv4 only
|
# FIXME: IPv4 only
|
||||||
argv = ['netstat', '-rn']
|
argv = ['netstat', '-rn']
|
||||||
p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE)
|
env = {
|
||||||
|
'PATH': os.environ['PATH'],
|
||||||
|
'LC_ALL': "C",
|
||||||
|
}
|
||||||
|
p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, env=env)
|
||||||
routes = []
|
routes = []
|
||||||
for line in p.stdout:
|
for line in p.stdout:
|
||||||
cols = re.split(r'\s+', line.decode("ASCII"))
|
cols = re.split(r'\s+', line.decode("ASCII"))
|
||||||
@ -94,7 +98,7 @@ def _exc_dump():
|
|||||||
return ''.join(traceback.format_exception(*exc_info))
|
return ''.join(traceback.format_exception(*exc_info))
|
||||||
|
|
||||||
|
|
||||||
def start_hostwatch(seed_hosts):
|
def start_hostwatch(seed_hosts, auto_hosts):
|
||||||
s1, s2 = socket.socketpair()
|
s1, s2 = socket.socketpair()
|
||||||
pid = os.fork()
|
pid = os.fork()
|
||||||
if not pid:
|
if not pid:
|
||||||
@ -106,7 +110,7 @@ def start_hostwatch(seed_hosts):
|
|||||||
os.dup2(s1.fileno(), 1)
|
os.dup2(s1.fileno(), 1)
|
||||||
os.dup2(s1.fileno(), 0)
|
os.dup2(s1.fileno(), 0)
|
||||||
s1.close()
|
s1.close()
|
||||||
rv = hostwatch.hw_main(seed_hosts) or 0
|
rv = hostwatch.hw_main(seed_hosts, auto_hosts) or 0
|
||||||
except Exception:
|
except Exception:
|
||||||
log('%s\n' % _exc_dump())
|
log('%s\n' % _exc_dump())
|
||||||
rv = 98
|
rv = 98
|
||||||
@ -219,11 +223,11 @@ class UdpProxy(Handler):
|
|||||||
log('UDP recv from %r port %d: %s\n' % (peer[0], peer[1], e))
|
log('UDP recv from %r port %d: %s\n' % (peer[0], peer[1], e))
|
||||||
return
|
return
|
||||||
debug2('UDP response: %d bytes\n' % len(data))
|
debug2('UDP response: %d bytes\n' % len(data))
|
||||||
hdr = "%s,%r," % (peer[0], peer[1])
|
hdr = b("%s,%r," % (peer[0], peer[1]))
|
||||||
self.mux.send(self.chan, ssnet.CMD_UDP_DATA, hdr + data)
|
self.mux.send(self.chan, ssnet.CMD_UDP_DATA, hdr + data)
|
||||||
|
|
||||||
|
|
||||||
def main(latency_control):
|
def main(latency_control, auto_hosts):
|
||||||
debug1('Starting server with Python version %s\n'
|
debug1('Starting server with Python version %s\n'
|
||||||
% platform.python_version())
|
% platform.python_version())
|
||||||
|
|
||||||
@ -273,7 +277,8 @@ def main(latency_control):
|
|||||||
|
|
||||||
def got_host_req(data):
|
def got_host_req(data):
|
||||||
if not hw.pid:
|
if not hw.pid:
|
||||||
(hw.pid, hw.sock) = start_hostwatch(data.strip().split())
|
(hw.pid, hw.sock) = start_hostwatch(
|
||||||
|
data.strip().split(), auto_hosts)
|
||||||
handlers.append(Handler(socks=[hw.sock],
|
handlers.append(Handler(socks=[hw.sock],
|
||||||
callback=hostwatch_ready))
|
callback=hostwatch_ready))
|
||||||
mux.got_host_req = got_host_req
|
mux.got_host_req = got_host_req
|
||||||
@ -281,6 +286,12 @@ def main(latency_control):
|
|||||||
def new_channel(channel, data):
|
def new_channel(channel, data):
|
||||||
(family, dstip, dstport) = data.decode("ASCII").split(',', 2)
|
(family, dstip, dstport) = data.decode("ASCII").split(',', 2)
|
||||||
family = int(family)
|
family = int(family)
|
||||||
|
# AF_INET is the same constant on Linux and BSD but AF_INET6
|
||||||
|
# is different. As the client and server can be running on
|
||||||
|
# different platforms we can not just set the socket family
|
||||||
|
# to what comes in the wire.
|
||||||
|
if family != socket.AF_INET:
|
||||||
|
family = socket.AF_INET6
|
||||||
dstport = int(dstport)
|
dstport = int(dstport)
|
||||||
outwrap = ssnet.connect_dst(family, dstip, dstport)
|
outwrap = ssnet.connect_dst(family, dstip, dstport)
|
||||||
handlers.append(Proxy(MuxWrapper(mux, channel), outwrap))
|
handlers.append(Proxy(MuxWrapper(mux, channel), outwrap))
|
||||||
@ -300,7 +311,7 @@ def main(latency_control):
|
|||||||
def udp_req(channel, cmd, data):
|
def udp_req(channel, cmd, data):
|
||||||
debug2('Incoming UDP request channel=%d, cmd=%d\n' % (channel, cmd))
|
debug2('Incoming UDP request channel=%d, cmd=%d\n' % (channel, cmd))
|
||||||
if cmd == ssnet.CMD_UDP_DATA:
|
if cmd == ssnet.CMD_UDP_DATA:
|
||||||
(dstip, dstport, data) = data.split(",", 2)
|
(dstip, dstport, data) = data.split(b(','), 2)
|
||||||
dstport = int(dstport)
|
dstport = int(dstport)
|
||||||
debug2('is incoming UDP data. %r %d.\n' % (dstip, dstport))
|
debug2('is incoming UDP data. %r %d.\n' % (dstip, dstport))
|
||||||
h = udphandlers[channel]
|
h = udphandlers[channel]
|
||||||
|
@ -15,6 +15,7 @@ except ImportError:
|
|||||||
# Python 2.x
|
# Python 2.x
|
||||||
from pipes import quote
|
from pipes import quote
|
||||||
|
|
||||||
|
|
||||||
def readfile(name):
|
def readfile(name):
|
||||||
tokens = name.split(".")
|
tokens = name.split(".")
|
||||||
f = None
|
f = None
|
||||||
@ -105,7 +106,7 @@ def connect(ssh_cmd, rhostport, python, stderr, options):
|
|||||||
if not rhost:
|
if not rhost:
|
||||||
# ignore the --python argument when running locally; we already know
|
# ignore the --python argument when running locally; we already know
|
||||||
# which python version works.
|
# which python version works.
|
||||||
argv = [sys.argv[1], '-c', pyscript]
|
argv = [sys.executable, '-c', pyscript]
|
||||||
else:
|
else:
|
||||||
if ssh_cmd:
|
if ssh_cmd:
|
||||||
sshl = ssh_cmd.split(' ')
|
sshl = ssh_cmd.split(' ')
|
||||||
|
@ -54,7 +54,8 @@ cmd_to_name = {
|
|||||||
|
|
||||||
NET_ERRS = [errno.ECONNREFUSED, errno.ETIMEDOUT,
|
NET_ERRS = [errno.ECONNREFUSED, errno.ETIMEDOUT,
|
||||||
errno.EHOSTUNREACH, errno.ENETUNREACH,
|
errno.EHOSTUNREACH, errno.ENETUNREACH,
|
||||||
errno.EHOSTDOWN, errno.ENETDOWN]
|
errno.EHOSTDOWN, errno.ENETDOWN,
|
||||||
|
errno.ENETUNREACH]
|
||||||
|
|
||||||
|
|
||||||
def _add(l, elem):
|
def _add(l, elem):
|
||||||
@ -357,8 +358,8 @@ class Mux(Handler):
|
|||||||
|
|
||||||
def amount_queued(self):
|
def amount_queued(self):
|
||||||
total = 0
|
total = 0
|
||||||
for b in self.outbuf:
|
for byte in self.outbuf:
|
||||||
total += len(b)
|
total += len(byte)
|
||||||
return total
|
return total
|
||||||
|
|
||||||
def check_fullness(self):
|
def check_fullness(self):
|
||||||
@ -375,7 +376,8 @@ class Mux(Handler):
|
|||||||
def send(self, channel, cmd, data):
|
def send(self, channel, cmd, data):
|
||||||
assert isinstance(data, binary_type)
|
assert isinstance(data, binary_type)
|
||||||
assert len(data) <= 65535
|
assert len(data) <= 65535
|
||||||
p = struct.pack('!ccHHH', b('S'), b('S'), channel, cmd, len(data)) + data
|
p = struct.pack('!ccHHH', b('S'), b('S'), channel, cmd, len(data)) \
|
||||||
|
+ data
|
||||||
self.outbuf.append(p)
|
self.outbuf.append(p)
|
||||||
debug2(' > channel=%d cmd=%s len=%d (fullness=%d)\n'
|
debug2(' > channel=%d cmd=%s len=%d (fullness=%d)\n'
|
||||||
% (channel, cmd_to_name.get(cmd, hex(cmd)),
|
% (channel, cmd_to_name.get(cmd, hex(cmd)),
|
||||||
|
@ -10,7 +10,7 @@ from sshuttle.methods.pf import FreeBsd, Darwin, OpenBsd
|
|||||||
def test_get_supported_features():
|
def test_get_supported_features():
|
||||||
method = get_method('pf')
|
method = get_method('pf')
|
||||||
features = method.get_supported_features()
|
features = method.get_supported_features()
|
||||||
assert not features.ipv6
|
assert features.ipv6
|
||||||
assert not features.udp
|
assert not features.udp
|
||||||
assert features.dns
|
assert features.dns
|
||||||
|
|
||||||
@ -155,6 +155,8 @@ def test_firewall_command_openbsd(mock_pf_get_dev, mock_ioctl, mock_stdout):
|
|||||||
|
|
||||||
|
|
||||||
def pfctl(args, stdin=None):
|
def pfctl(args, stdin=None):
|
||||||
|
if args == '-s Interfaces -i lo -v':
|
||||||
|
return (b'lo0 (skip)',)
|
||||||
if args == '-s all':
|
if args == '-s all':
|
||||||
return (b'INFO:\nStatus: Disabled\nanother mary had a little lamb\n',
|
return (b'INFO:\nStatus: Disabled\nanother mary had a little lamb\n',
|
||||||
b'little lamb\n')
|
b'little lamb\n')
|
||||||
@ -174,19 +176,45 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
method = get_method('pf')
|
method = get_method('pf')
|
||||||
assert method.name == 'pf'
|
assert method.name == 'pf'
|
||||||
|
|
||||||
with pytest.raises(Exception) as excinfo:
|
# IPV6
|
||||||
|
|
||||||
method.setup_firewall(
|
method.setup_firewall(
|
||||||
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::'),
|
||||||
(10, 128, True, u'2404:6800:4004:80c::101f')],
|
(10, 128, True, u'2404:6800:4004:80c::101f')],
|
||||||
True)
|
False)
|
||||||
assert str(excinfo.value) \
|
assert mock_ioctl.mock_calls == [
|
||||||
== 'Address family "AF_INET6" unsupported by pf method_name'
|
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
||||||
assert mock_pf_get_dev.mock_calls == []
|
call(mock_pf_get_dev(), 0xCC20441A, ANY),
|
||||||
assert mock_ioctl.mock_calls == []
|
call(mock_pf_get_dev(), 0xCC20441A, ANY),
|
||||||
assert mock_pfctl.mock_calls == []
|
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
||||||
|
call(mock_pf_get_dev(), 0xCC20441A, ANY),
|
||||||
|
call(mock_pf_get_dev(), 0xCC20441A, ANY),
|
||||||
|
]
|
||||||
|
assert mock_pfctl.mock_calls == [
|
||||||
|
call('-s Interfaces -i lo -v'),
|
||||||
|
call('-f /dev/stdin', b'pass on lo\n'),
|
||||||
|
call('-s all'),
|
||||||
|
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'rdr pass on lo0 inet6 proto tcp '
|
||||||
|
b'to <forward_subnets> -> ::1 port 1024\n'
|
||||||
|
b'rdr pass on lo0 inet6 proto udp '
|
||||||
|
b'to <dns_servers> port 53 -> ::1 port 1026\n'
|
||||||
|
b'pass out route-to lo0 inet6 proto tcp '
|
||||||
|
b'to <forward_subnets> keep state\n'
|
||||||
|
b'pass out route-to lo0 inet6 proto udp '
|
||||||
|
b'to <dns_servers> port 53 keep state\n'),
|
||||||
|
call('-E'),
|
||||||
|
]
|
||||||
|
mock_pf_get_dev.reset_mock()
|
||||||
|
mock_ioctl.reset_mock()
|
||||||
|
mock_pfctl.reset_mock()
|
||||||
|
|
||||||
with pytest.raises(Exception) as excinfo:
|
with pytest.raises(Exception) as excinfo:
|
||||||
method.setup_firewall(
|
method.setup_firewall(
|
||||||
@ -215,14 +243,15 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
call(mock_pf_get_dev(), 0xCC20441A, ANY),
|
call(mock_pf_get_dev(), 0xCC20441A, ANY),
|
||||||
]
|
]
|
||||||
assert mock_pfctl.mock_calls == [
|
assert mock_pfctl.mock_calls == [
|
||||||
|
call('-s Interfaces -i lo -v'),
|
||||||
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 -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 <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 proto tcp '
|
b'rdr pass on lo0 inet proto tcp '
|
||||||
b'to <forward_subnets> -> 127.0.0.1 port 1025\n'
|
b'to <forward_subnets> -> 127.0.0.1 port 1025\n'
|
||||||
b'rdr pass on lo0 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 route-to lo0 inet proto tcp '
|
||||||
b'to <forward_subnets> keep state\n'
|
b'to <forward_subnets> keep state\n'
|
||||||
@ -237,7 +266,7 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
method.restore_firewall(1025, 2, False)
|
method.restore_firewall(1025, 2, False)
|
||||||
assert mock_ioctl.mock_calls == []
|
assert mock_ioctl.mock_calls == []
|
||||||
assert mock_pfctl.mock_calls == [
|
assert mock_pfctl.mock_calls == [
|
||||||
call('-a sshuttle -F all'),
|
call('-a sshuttle-1025 -F all'),
|
||||||
call("-X abcdefg"),
|
call("-X abcdefg"),
|
||||||
]
|
]
|
||||||
mock_pf_get_dev.reset_mock()
|
mock_pf_get_dev.reset_mock()
|
||||||
@ -256,19 +285,34 @@ def test_setup_firewall_freebsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
method = get_method('pf')
|
method = get_method('pf')
|
||||||
assert method.name == 'pf'
|
assert method.name == 'pf'
|
||||||
|
|
||||||
with pytest.raises(Exception) as excinfo:
|
|
||||||
method.setup_firewall(
|
method.setup_firewall(
|
||||||
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::'),
|
||||||
(10, 128, True, u'2404:6800:4004:80c::101f')],
|
(10, 128, True, u'2404:6800:4004:80c::101f')],
|
||||||
True)
|
False)
|
||||||
assert str(excinfo.value) \
|
|
||||||
== 'Address family "AF_INET6" unsupported by pf method_name'
|
assert mock_pfctl.mock_calls == [
|
||||||
assert mock_pf_get_dev.mock_calls == []
|
call('-s all'),
|
||||||
assert mock_ioctl.mock_calls == []
|
call('-a sshuttle6-1024 -f /dev/stdin',
|
||||||
assert mock_pfctl.mock_calls == []
|
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'rdr pass on lo0 inet6 proto tcp '
|
||||||
|
b'to <forward_subnets> -> ::1 port 1024\n'
|
||||||
|
b'rdr pass on lo0 inet6 proto udp '
|
||||||
|
b'to <dns_servers> port 53 -> ::1 port 1026\n'
|
||||||
|
b'pass out route-to lo0 inet6 proto tcp '
|
||||||
|
b'to <forward_subnets> keep state\n'
|
||||||
|
b'pass out route-to lo0 inet6 proto udp '
|
||||||
|
b'to <dns_servers> port 53 keep state\n'),
|
||||||
|
call('-e'),
|
||||||
|
]
|
||||||
|
mock_pf_get_dev.reset_mock()
|
||||||
|
mock_ioctl.reset_mock()
|
||||||
|
mock_pfctl.reset_mock()
|
||||||
|
|
||||||
with pytest.raises(Exception) as excinfo:
|
with pytest.raises(Exception) as excinfo:
|
||||||
method.setup_firewall(
|
method.setup_firewall(
|
||||||
@ -298,12 +342,12 @@ 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 -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 <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 proto tcp '
|
b'rdr pass on lo0 inet proto tcp '
|
||||||
b'to <forward_subnets> -> 127.0.0.1 port 1025\n'
|
b'to <forward_subnets> -> 127.0.0.1 port 1025\n'
|
||||||
b'rdr pass on lo0 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 route-to lo0 inet proto tcp '
|
||||||
b'to <forward_subnets> keep state\n'
|
b'to <forward_subnets> keep state\n'
|
||||||
@ -318,7 +362,7 @@ def test_setup_firewall_freebsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
method.restore_firewall(1025, 2, False)
|
method.restore_firewall(1025, 2, False)
|
||||||
assert mock_ioctl.mock_calls == []
|
assert mock_ioctl.mock_calls == []
|
||||||
assert mock_pfctl.mock_calls == [
|
assert mock_pfctl.mock_calls == [
|
||||||
call('-a sshuttle -F all'),
|
call('-a sshuttle-1025 -F all'),
|
||||||
call("-d"),
|
call("-d"),
|
||||||
]
|
]
|
||||||
mock_pf_get_dev.reset_mock()
|
mock_pf_get_dev.reset_mock()
|
||||||
@ -337,19 +381,40 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
method = get_method('pf')
|
method = get_method('pf')
|
||||||
assert method.name == 'pf'
|
assert method.name == 'pf'
|
||||||
|
|
||||||
with pytest.raises(Exception) as excinfo:
|
|
||||||
method.setup_firewall(
|
method.setup_firewall(
|
||||||
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::'),
|
||||||
(10, 128, True, u'2404:6800:4004:80c::101f')],
|
(10, 128, True, u'2404:6800:4004:80c::101f')],
|
||||||
True)
|
False)
|
||||||
assert str(excinfo.value) \
|
|
||||||
== 'Address family "AF_INET6" unsupported by pf method_name'
|
assert mock_ioctl.mock_calls == [
|
||||||
assert mock_pf_get_dev.mock_calls == []
|
call(mock_pf_get_dev(), 0xcd48441a, ANY),
|
||||||
assert mock_ioctl.mock_calls == []
|
call(mock_pf_get_dev(), 0xcd48441a, ANY),
|
||||||
assert mock_pfctl.mock_calls == []
|
]
|
||||||
|
assert mock_pfctl.mock_calls == [
|
||||||
|
call('-s Interfaces -i lo -v'),
|
||||||
|
call('-f /dev/stdin', b'match on lo\n'),
|
||||||
|
call('-s all'),
|
||||||
|
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'pass in on lo0 inet6 proto tcp to '
|
||||||
|
b'<forward_subnets> divert-to ::1 port 1024\n'
|
||||||
|
b'pass in on lo0 inet6 proto udp '
|
||||||
|
b'to <dns_servers> port 53 rdr-to ::1 port 1026\n'
|
||||||
|
b'pass out inet6 proto tcp to '
|
||||||
|
b'<forward_subnets> route-to lo0 keep state\n'
|
||||||
|
b'pass out inet6 proto udp to '
|
||||||
|
b'<dns_servers> port 53 route-to lo0 keep state\n'),
|
||||||
|
call('-e'),
|
||||||
|
]
|
||||||
|
mock_pf_get_dev.reset_mock()
|
||||||
|
mock_ioctl.reset_mock()
|
||||||
|
mock_pfctl.reset_mock()
|
||||||
|
|
||||||
with pytest.raises(Exception) as excinfo:
|
with pytest.raises(Exception) as excinfo:
|
||||||
method.setup_firewall(
|
method.setup_firewall(
|
||||||
@ -374,12 +439,13 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
call(mock_pf_get_dev(), 0xcd48441a, ANY),
|
call(mock_pf_get_dev(), 0xcd48441a, ANY),
|
||||||
]
|
]
|
||||||
assert mock_pfctl.mock_calls == [
|
assert mock_pfctl.mock_calls == [
|
||||||
|
call('-s Interfaces -i lo -v'),
|
||||||
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 -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 <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 divert-to 127.0.0.1 port 1025\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 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 inet proto tcp to '
|
||||||
@ -395,7 +461,7 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
method.restore_firewall(1025, 2, False)
|
method.restore_firewall(1025, 2, False)
|
||||||
assert mock_ioctl.mock_calls == []
|
assert mock_ioctl.mock_calls == []
|
||||||
assert mock_pfctl.mock_calls == [
|
assert mock_pfctl.mock_calls == [
|
||||||
call('-a sshuttle -F all'),
|
call('-a sshuttle-1025 -F all'),
|
||||||
call("-d"),
|
call("-d"),
|
||||||
]
|
]
|
||||||
mock_pf_get_dev.reset_mock()
|
mock_pf_get_dev.reset_mock()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
import io
|
import io
|
||||||
import socket
|
import socket
|
||||||
import sshuttle.server
|
import sshuttle.server
|
||||||
@ -34,8 +35,12 @@ Destination Gateway Genmask Flags MSS Window irtt Iface
|
|||||||
|
|
||||||
routes = sshuttle.server._list_routes()
|
routes = sshuttle.server._list_routes()
|
||||||
|
|
||||||
|
env = {
|
||||||
|
'PATH': os.environ['PATH'],
|
||||||
|
'LC_ALL': "C",
|
||||||
|
}
|
||||||
assert mock_popen.mock_calls == [
|
assert mock_popen.mock_calls == [
|
||||||
call(['netstat', '-rn'], stdout=-1),
|
call(['netstat', '-rn'], stdout=-1, env=env),
|
||||||
call().wait()
|
call().wait()
|
||||||
]
|
]
|
||||||
assert routes == [
|
assert routes == [
|
||||||
|
Reference in New Issue
Block a user