mirror of
https://github.com/sshuttle/sshuttle.git
synced 2025-02-11 16:09:14 +01:00
Merge pull request #23 from vieira/ns-hosts
dns: Added --ns-hosts to tunnel only some requests
This commit is contained in:
commit
0fb714893a
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
12
src/main.py
12
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,
|
||||
|
Loading…
Reference in New Issue
Block a user