mirror of
https://github.com/sshuttle/sshuttle.git
synced 2024-11-24 00:44:07 +01:00
ensure non loopback address for windivert method
This commit is contained in:
parent
9c5517fd25
commit
7b8f140870
@ -295,10 +295,10 @@ class FirewallClient:
|
||||
|
||||
else:
|
||||
# In windows, if client/firewall processes is running as admin user, stdio can be used for communication.
|
||||
# But if firewall process is run with elevated mode, access to stdio is lost.
|
||||
# But if firewall process is run with elevated mode, access to stdio is lost.
|
||||
# So we have to use a socketpair (as in unix).
|
||||
# But socket need to be "shared" to child process as it can't be directly set as stdio in Windows
|
||||
can_use_stdio = is_admin_user()
|
||||
# But socket need to be "shared" to child process as it can't be directly set as stdio in Windows
|
||||
can_use_stdio = is_admin_user()
|
||||
pstdout = ssubprocess.PIPE if can_use_stdio else None
|
||||
pstdin = ssubprocess.PIPE
|
||||
preexec_fn = None
|
||||
@ -306,25 +306,27 @@ class FirewallClient:
|
||||
penv['PYTHONPATH'] = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
def get_pfile():
|
||||
if can_use_stdio:
|
||||
import io
|
||||
if can_use_stdio:
|
||||
self.p.stdin.write(b'STDIO:\n')
|
||||
self.p.stdin.flush()
|
||||
|
||||
class RWPair:
|
||||
def __init__(self, r, w):
|
||||
self.r = r
|
||||
self.r = r
|
||||
self.w = w
|
||||
self.read = r.read
|
||||
self.readline = r.readline
|
||||
self.write = w.write
|
||||
self.flush = w.flush
|
||||
|
||||
def close(self):
|
||||
for f in self.r, self.w:
|
||||
try:
|
||||
f.close()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
return RWPair(self.p.stdout, self.p.stdin)
|
||||
# import io
|
||||
# return io.BufferedRWPair(self.p.stdout, self.p.stdin, 1)
|
||||
else:
|
||||
import base64
|
||||
@ -885,7 +887,11 @@ def main(listenip_v6, listenip_v4,
|
||||
|
||||
# listenip_v4 contains user specified value or it is set to "auto".
|
||||
if listenip_v4 == "auto":
|
||||
listenip_v4 = ('127.0.0.1', 0)
|
||||
if sys.platform == 'win32':
|
||||
listenip_v4 = ('0.0.0.0', 0) # windivert method won't work with loopback interface
|
||||
else:
|
||||
listenip_v4 = ('127.0.0.1', 0)
|
||||
debug1("Using default IPv4 listen address " + listenip_v4[0])
|
||||
|
||||
# listenip_v6 is...
|
||||
# None when IPv6 is disabled.
|
||||
@ -895,8 +901,11 @@ def main(listenip_v6, listenip_v4,
|
||||
debug1("IPv6 disabled by --disable-ipv6")
|
||||
if listenip_v6 == "auto":
|
||||
if avail.ipv6:
|
||||
debug1("IPv6 enabled: Using default IPv6 listen address ::1")
|
||||
listenip_v6 = ('::1', 0)
|
||||
if sys.platform == 'win32':
|
||||
listenip_v6 = ('::', 0) # windivert method won't work with loopback interface
|
||||
else:
|
||||
listenip_v6 = ('::1', 0)
|
||||
debug1("IPv6 enabled: Using default IPv6 listen address " + listenip_v6[0])
|
||||
else:
|
||||
debug1("IPv6 disabled since it isn't supported by method "
|
||||
"%s." % fw.method.name)
|
||||
|
@ -4,6 +4,8 @@ import ipaddress
|
||||
import threading
|
||||
from collections import namedtuple
|
||||
import socket
|
||||
import subprocess
|
||||
import re
|
||||
from multiprocessing import shared_memory
|
||||
import struct
|
||||
from functools import wraps
|
||||
@ -270,6 +272,18 @@ class Method(BaseMethod):
|
||||
def __init__(self, name):
|
||||
super().__init__(name)
|
||||
|
||||
def _get_local_proxy_listen_addr(self, port, family):
|
||||
proto = "TCPv6" if family.version == 6 else "TCP"
|
||||
for line in subprocess.check_output(["netstat", "-a", "-n", "-p", proto]).decode().splitlines():
|
||||
try:
|
||||
_, local_addr, _, state, *_ = re.split(r"\s+", line.strip())
|
||||
except ValueError:
|
||||
continue
|
||||
port_suffix = ":" + str(port)
|
||||
if state == "LISTENING" and local_addr.endswith(port_suffix):
|
||||
return ipaddress.ip_address(local_addr[:-len(port_suffix)].strip("[]"))
|
||||
raise Fatal("Could not find listening address for {}/{}".format(port, proto))
|
||||
|
||||
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp, user, tmark):
|
||||
log(f"{port=}, {dnsport=}, {nslist=}, {family=}, {subnets=}, {udp=}, {user=}, {tmark=}")
|
||||
|
||||
@ -279,16 +293,20 @@ class Method(BaseMethod):
|
||||
family = IPFamily(family)
|
||||
|
||||
# using loopback proxy address never worked.
|
||||
# >>> self.proxy_addr[family] = family.loopback_addr
|
||||
# See: https://github.com/basil00/Divert/issues/17#issuecomment-341100167 ,https://github.com/basil00/Divert/issues/82)
|
||||
# As a workaround we use another interface ip instead.
|
||||
# self.proxy_addr[family] = family.loopback_addr
|
||||
|
||||
local_addr = self._get_local_proxy_listen_addr(port, family)
|
||||
for addr in (ipaddress.ip_address(info[4][0]) for info in socket.getaddrinfo(socket.gethostname(), None)):
|
||||
if addr.is_loopback or addr.version != family.version:
|
||||
continue
|
||||
self.proxy_addr[family] = str(addr)
|
||||
break
|
||||
if local_addr.is_unspecified or local_addr == addr:
|
||||
debug2("Found non loopback address to connect to proxy: " + str(addr))
|
||||
self.proxy_addr[family] = str(addr)
|
||||
break
|
||||
else:
|
||||
raise Fatal(f"Could not find a non loopback proxy address for {family.name}")
|
||||
raise Fatal("Windivert method requires proxy to listen on non loopback address")
|
||||
|
||||
self.proxy_port = port
|
||||
|
||||
@ -423,6 +441,8 @@ class Method(BaseMethod):
|
||||
else:
|
||||
# ip_checks.append(f"ip.SrcAddr=={hex(int(addr))}") # only Windivert >=2 supports this
|
||||
ip_filters.append(f"ipv6.SrcAddr=={addr}")
|
||||
if not ip_filters:
|
||||
raise Fatal("At least ipv4 or ipv6 address is expected")
|
||||
filter = f"{direction} and {proto.filter} and ({' or '.join(ip_filters)}) and tcp.SrcPort=={self.proxy_port}"
|
||||
debug2(f"[INGRESS] {filter=}")
|
||||
with pydivert.WinDivert(filter) as w:
|
||||
|
@ -12,7 +12,7 @@ import ipaddress
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import sshuttle.helpers as helpers
|
||||
from sshuttle.helpers import debug2, debug3, which, get_path, Fatal
|
||||
from sshuttle.helpers import debug2, which, get_path, Fatal
|
||||
|
||||
|
||||
def get_module_source(name):
|
||||
|
Loading…
Reference in New Issue
Block a user