summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian May <brian@linuxpenguins.xyz>2015-10-29 13:13:59 +1100
committerBrian May <brian@linuxpenguins.xyz>2015-10-29 13:13:59 +1100
commit0fb714893a5928cb4890c6027411525f9d824978 (patch)
tree203610d13a8223d26f3317bb144fbe2db096911a
parent3cf5002b62650c26a50e18af8d8c5c91d754bab9 (diff)
parent28be71ef9ade0fd2aa122c738735f72e7ba5e12c (diff)
Merge pull request #23 from vieira/ns-hosts
dns: Added --ns-hosts to tunnel only some requests
-rw-r--r--src/client.py15
-rw-r--r--src/firewall.py20
-rw-r--r--src/helpers.py12
-rw-r--r--src/main.py12
4 files changed, 37 insertions, 22 deletions
diff --git a/src/client.py b/src/client.py
index 0fe0e07..48be2fa 100644
--- a/src/client.py
+++ b/src/client.py
@@ -277,15 +277,18 @@ class MultiListener:
class FirewallClient:
def __init__(self, port_v6, port_v4, subnets_include, subnets_exclude,
- dnsport_v6, dnsport_v4, method, udp):
+ dnsport_v6, dnsport_v4, ns_hosts, method, udp):
self.auto_nets = []
self.subnets_include = subnets_include
self.subnets_exclude = subnets_exclude
+ self.ns_hosts = ns_hosts
argvbase = ([sys.argv[1], sys.argv[0], sys.argv[1]] +
['-v'] * (helpers.verbose or 0) +
['--firewall', str(port_v6), str(port_v4),
str(dnsport_v6), str(dnsport_v4),
method, str(int(udp))])
+ if dnsport_v4 or dnsport_v6:
+ argvbase += ['--ns-hosts', ns_hosts]
if ssyslog._p:
argvbase += ['--syslog']
argv_tries = [
@@ -599,7 +602,7 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
def main(listenip_v6, listenip_v4,
- ssh_cmd, remotename, python, latency_control, dns,
+ ssh_cmd, remotename, python, latency_control, dns, ns_hosts,
method, seed_hosts, auto_nets,
subnets_include, subnets_exclude, syslog, daemon, pidfile):
@@ -695,7 +698,9 @@ def main(listenip_v6, listenip_v4,
udp_listener.print_listening("UDP redirector")
bound = False
- if dns:
+ if dns or ns_hosts:
+ if dns:
+ ns_hosts += resolvconf_nameservers()
# search for spare port for DNS
debug2('Binding DNS:')
ports = xrange(12300, 9000, -1)
@@ -735,9 +740,11 @@ def main(listenip_v6, listenip_v4,
dnsport_v6 = 0
dnsport_v4 = 0
dns_listener = None
+ ns_hosts = []
fw = FirewallClient(redirectport_v6, redirectport_v4, subnets_include,
- subnets_exclude, dnsport_v6, dnsport_v4, method, udp)
+ subnets_exclude, dnsport_v6, dnsport_v4, ns_hosts,
+ method, udp)
if fw.method == "tproxy":
tcp_listener.setsockopt(socket.SOL_IP, IP_TRANSPARENT, 1)
diff --git a/src/firewall.py b/src/firewall.py
index c8f2ec5..07ce437 100644
--- a/src/firewall.py
+++ b/src/firewall.py
@@ -83,7 +83,7 @@ def _ipt_ttl(family, *args):
# multiple copies shouldn't have overlapping subnets, or only the most-
# recently-started one will win (because we use "-I OUTPUT 1" instead of
# "-A OUTPUT").
-def do_iptables_nat(port, dnsport, family, subnets, udp):
+def do_iptables_nat(port, dnsport, nslist, family, subnets, udp):
# only ipv4 supported with NAT
if family != socket.AF_INET:
raise Exception(
@@ -134,7 +134,6 @@ def do_iptables_nat(port, dnsport, family, subnets, udp):
'--to-ports', str(port))
if dnsport:
- nslist = resolvconf_nameservers()
for f, ip in filter(lambda i: i[0] == family, nslist):
ipt_ttl('-A', chain, '-j', 'REDIRECT',
'--dest', '%s/32' % ip,
@@ -143,7 +142,7 @@ def do_iptables_nat(port, dnsport, family, subnets, udp):
'--to-ports', str(dnsport))
-def do_iptables_tproxy(port, dnsport, family, subnets, udp):
+def do_iptables_tproxy(port, dnsport, nslist, family, subnets, udp):
if family not in [socket.AF_INET, socket.AF_INET6]:
raise Exception(
'Address family "%s" unsupported by tproxy method'
@@ -194,7 +193,6 @@ def do_iptables_tproxy(port, dnsport, family, subnets, udp):
'-m', 'udp', '-p', 'udp')
if dnsport:
- nslist = resolvconf_nameservers()
for f, ip in filter(lambda i: i[0] == family, nslist):
ipt('-A', mark_chain, '-j', 'MARK', '--set-mark', '1',
'--dest', '%s/32' % ip,
@@ -442,7 +440,6 @@ def do_ipfw(port, dnsport, family, subnets, udp):
IPPROTO_DIVERT)
divertsock.bind(('0.0.0.0', port)) # IP field is ignored
- nslist = resolvconf_nameservers()
for f, ip in filter(lambda i: i[0] == family, nslist):
# relabel and then catch outgoing DNS requests
ipfw('add', sport, 'divert', sport,
@@ -483,7 +480,7 @@ def pfctl(args, stdin = None):
_pf_context = {'started_by_sshuttle': False, 'Xtoken':''}
-def do_pf(port, dnsport, family, subnets, udp):
+def do_pf(port, dnsport, nslist, family, subnets, udp):
global _pf_started_by_sshuttle
tables = []
translating_rules = []
@@ -502,7 +499,6 @@ def do_pf(port, dnsport, family, subnets, udp):
filtering_rules.append('pass out route-to lo0 inet proto tcp to <forward_subnets> keep state')
if dnsport:
- nslist = resolvconf_nameservers()
tables.append('table <dns_servers> {%s}' % ','.join([ns[1] for ns in nslist]))
translating_rules.append('rdr pass on lo0 proto udp to <dns_servers> port 53 -> 127.0.0.1 port %r' % dnsport)
filtering_rules.append('pass out route-to lo0 inet proto udp to <dns_servers> port 53 keep state')
@@ -690,7 +686,7 @@ def pf_add_anchor_rule(type, name):
# exit. In case that fails, it's not the end of the world; future runs will
# supercede it in the transproxy list, at least, so the leftover rules
# are hopefully harmless.
-def main(port_v6, port_v4, dnsport_v6, dnsport_v4, method, udp, syslog):
+def main(port_v6, port_v4, dnsport_v6, dnsport_v4, nslist, method, udp, syslog):
assert(port_v6 >= 0)
assert(port_v6 <= 65535)
assert(port_v4 >= 0)
@@ -777,14 +773,14 @@ def main(port_v6, port_v4, dnsport_v6, dnsport_v4, method, udp, syslog):
subnets_v6 = filter(lambda i: i[0] == socket.AF_INET6, subnets)
if port_v6:
do_wait = do_it(
- port_v6, dnsport_v6, socket.AF_INET6, subnets_v6, udp)
+ port_v6, dnsport_v6, nslist, socket.AF_INET6, subnets_v6, udp)
elif len(subnets_v6) > 0:
debug1("IPv6 subnets defined but IPv6 disabled\n")
subnets_v4 = filter(lambda i: i[0] == socket.AF_INET, subnets)
if port_v4:
do_wait = do_it(
- port_v4, dnsport_v4, socket.AF_INET, subnets_v4, udp)
+ port_v4, dnsport_v4, nslist, socket.AF_INET, subnets_v4, udp)
elif len(subnets_v4) > 0:
debug1('IPv4 subnets defined but IPv4 disabled\n')
@@ -826,7 +822,7 @@ def main(port_v6, port_v4, dnsport_v6, dnsport_v4, method, udp, syslog):
except:
pass
if port_v6:
- do_it(port_v6, 0, socket.AF_INET6, [], udp)
+ do_it(port_v6, 0, [], socket.AF_INET6, [], udp)
if port_v4:
- do_it(port_v4, 0, socket.AF_INET, [], udp)
+ do_it(port_v4, 0, [], socket.AF_INET, [], udp)
restore_etc_hosts(port_v6 or port_v4)
diff --git a/src/helpers.py b/src/helpers.py
index abb9f80..6d8c9a3 100644
--- a/src/helpers.py
+++ b/src/helpers.py
@@ -48,10 +48,7 @@ def resolvconf_nameservers():
for line in open('/etc/resolv.conf'):
words = line.lower().split()
if len(words) >= 2 and words[0] == 'nameserver':
- if ':' in words[1]:
- l.append((socket.AF_INET6, words[1]))
- else:
- l.append((socket.AF_INET, words[1]))
+ l.append(family_ip_tuple(words[1]))
return l
@@ -82,6 +79,13 @@ def islocal(ip, family):
return True # it's a local IP, or there would have been an error
+def family_ip_tuple(ip):
+ if ':' in ip:
+ return (socket.AF_INET6, ip)
+ else:
+ return (socket.AF_INET, ip)
+
+
def family_to_string(family):
if family == socket.AF_INET6:
return "AF_INET6"
diff --git a/src/main.py b/src/main.py
index fe8275c..135f06f 100644
--- a/src/main.py
+++ b/src/main.py
@@ -7,7 +7,7 @@ import client
import server
import firewall
import hostwatch
-from helpers import log, Fatal
+from helpers import family_ip_tuple, log, Fatal
# 1.2.3.4/5 or just 1.2.3.4
@@ -105,6 +105,9 @@ def parse_ipport6(s):
(ip, port) = (ip or '::', int(port or 0))
return (ip, port)
+def parse_list(list):
+ return re.split(r'[\s,]+', list.strip()) if list else []
+
optspec = """
sshuttle [-l [ip:]port] [-r [username@]sshserver[:port]] <subnets...>
@@ -116,6 +119,7 @@ l,listen= transproxy to this ip address and port number
H,auto-hosts scan for remote hostnames and update local /etc/hosts
N,auto-nets automatically determine subnets to route
dns capture local DNS requests and forward to the remote DNS server
+ns-hosts= capture and forward remote DNS requests to the following servers
method= auto, nat, tproxy, pf or ipfw
python= path to python interpreter on the remote server
r,remote= ssh hostname (and optional username) of remote sshuttle server
@@ -153,8 +157,10 @@ try:
elif opt.firewall:
if len(extra) != 6:
o.fatal('exactly six arguments expected')
+ port, dnsport = int(extra[0]), int(extra[1])
+ nslist = [family_ip_tuple(ns) for ns in parse_list(opt.ns_hosts)]
sys.exit(firewall.main(int(extra[0]), int(extra[1]),
- int(extra[2]), int(extra[3]),
+ int(extra[2]), int(extra[3]), nslist,
extra[4], int(extra[5]), opt.syslog))
elif opt.hostwatch:
sys.exit(hostwatch.hw_main(extra))
@@ -171,6 +177,7 @@ try:
remotename = opt.remote
if remotename == '' or remotename == '-':
remotename = None
+ nslist = [family_ip_tuple(ns) for ns in parse_list(opt.ns_hosts)]
if opt.seed_hosts and not opt.auto_hosts:
o.fatal('--seed-hosts only works if you also use -H')
if opt.seed_hosts:
@@ -208,6 +215,7 @@ try:
opt.python,
opt.latency_control,
opt.dns,
+ opt.ns_hosts,
method,
sh,
opt.auto_nets,