windows: support automatic nameserver detection for --dns option

This commit is contained in:
nom3ad 2024-05-01 20:08:15 +05:30 committed by Brian May
parent 51287dc4db
commit b826ae6b91
3 changed files with 39 additions and 12 deletions

View File

@ -3,7 +3,9 @@ import socket
import errno import errno
import os import os
import threading import threading
import subprocess
import traceback import traceback
import re
if sys.platform != "win32": if sys.platform != "win32":
import fcntl import fcntl
@ -114,18 +116,43 @@ def resolvconf_nameservers(systemd_resolved):
return nsservers return nsservers
def resolvconf_random_nameserver(systemd_resolved): def windows_nameservers():
out = subprocess.check_output(["powershell", "-NonInteractive", "-NoProfile", "-Command", "Get-DnsClientServerAddress"],
encoding="utf-8")
servers = set()
for line in out.splitlines():
if line.startswith("Loopback "):
continue
m = re.search(r'{.+}', line)
if not m:
continue
for s in m.group().strip('{}').split(','):
s = s.strip()
if s.startswith('fec0:0:0:ffff'):
continue
servers.add(s)
debug2("Found DNS servers: %s" % servers)
return [(socket.AF_INET6 if ':' in s else socket.AF_INET, s) for s in servers]
def get_random_nameserver():
"""Return a random nameserver selected from servers produced by """Return a random nameserver selected from servers produced by
resolvconf_nameservers(). See documentation for resolvconf_nameservers()/windows_nameservers()
resolvconf_nameservers() for a description of the parameter.
""" """
lines = resolvconf_nameservers(systemd_resolved) if sys.platform == "win32":
if lines: if globals().get('_nameservers') is None:
if len(lines) > 1: ns_list = windows_nameservers()
globals()['_nameservers'] = ns_list
else:
ns_list = globals()['_nameservers']
else:
ns_list = resolvconf_nameservers(systemd_resolved=False)
if ns_list:
if len(ns_list) > 1:
# don't import this unless we really need it # don't import this unless we really need it
import random import random
random.shuffle(lines) random.shuffle(ns_list)
return lines[0] return ns_list[0]
else: else:
return (socket.AF_INET, '127.0.0.1') return (socket.AF_INET, '127.0.0.1')

View File

@ -14,7 +14,7 @@ import sshuttle.hostwatch as hostwatch
import subprocess as ssubprocess import subprocess as ssubprocess
from sshuttle.ssnet import Handler, Proxy, Mux, MuxWrapper from sshuttle.ssnet import Handler, Proxy, Mux, MuxWrapper
from sshuttle.helpers import b, log, debug1, debug2, debug3, Fatal, \ from sshuttle.helpers import b, log, debug1, debug2, debug3, Fatal, \
resolvconf_random_nameserver, which, get_env, SocketRWShim get_random_nameserver, which, get_env, SocketRWShim
def _ipmatch(ipstr): def _ipmatch(ipstr):
@ -199,7 +199,7 @@ class DnsProxy(Handler):
self.tries += 1 self.tries += 1
if self.to_nameserver is None: if self.to_nameserver is None:
_, peer = resolvconf_random_nameserver(False) _, peer = get_random_nameserver()
port = 53 port = 53
else: else:
peer = self.to_ns_peer peer = self.to_ns_peer

View File

@ -143,7 +143,7 @@ nameserver 2404:6800:4004:80c::4
@patch('sshuttle.helpers.open', create=True) @patch('sshuttle.helpers.open', create=True)
def test_resolvconf_random_nameserver(mock_open): def test_get_random_nameserver(mock_open):
mock_open.return_value = io.StringIO(u""" mock_open.return_value = io.StringIO(u"""
# Generated by NetworkManager # Generated by NetworkManager
search pri search pri
@ -156,7 +156,7 @@ nameserver 2404:6800:4004:80c::2
nameserver 2404:6800:4004:80c::3 nameserver 2404:6800:4004:80c::3
nameserver 2404:6800:4004:80c::4 nameserver 2404:6800:4004:80c::4
""") """)
ns = sshuttle.helpers.resolvconf_random_nameserver(False) ns = sshuttle.helpers.get_random_nameserver()
assert ns in [ assert ns in [
(AF_INET, u'192.168.1.1'), (AF_INET, u'192.168.2.1'), (AF_INET, u'192.168.1.1'), (AF_INET, u'192.168.2.1'),
(AF_INET, u'192.168.3.1'), (AF_INET, u'192.168.4.1'), (AF_INET, u'192.168.3.1'), (AF_INET, u'192.168.4.1'),