Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

windows draft #147

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
setproctitle
dnspython
portalocker
36 changes: 35 additions & 1 deletion vpn_slice/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from subprocess import CalledProcessError
from sys import platform, stderr
from time import sleep
from itertools import islice

try:
from setproctitle import setproctitle
Expand Down Expand Up @@ -66,6 +67,17 @@ def get_default_providers():
dns = DNSPythonProvider or DigProvider,
hosts = PosixHostsFileProvider,
)
elif platform.startswith('win32'):
from .win import WinProcessProvider, WinHostsFileProvider, WinRouteProvider, WinNrptSplitDNSProvider, WinNrptProvider
from .dnspython import DNSPythonProvider
return dict(
process = WinProcessProvider,
route = WinRouteProvider,
dns = DNSPythonProvider,
hosts = WinHostsFileProvider,
domain_vpn_dns=WinNrptSplitDNSProvider,
nrpt = WinNrptProvider,
)
else:
return dict(
platform = OSError('Your platform, {}, is unsupported'.format(platform))
Expand Down Expand Up @@ -161,6 +173,10 @@ def do_disconnect(env, args):
except OSError:
print("WARNING: failed to deconfigure domains vpn dns", file=stderr)

# unset NRPT DNS rules (Windows only)
for (domain, servers) in args.nrpt.items():
providers.nrpt.remove_nrtp(domain, servers)


def do_connect(env, args):
global providers
Expand Down Expand Up @@ -211,6 +227,8 @@ def do_connect(env, args):
print("WARNING: guessing default MTU of %d (couldn't determine MTU of %s)" % (mtu, dev), file=stderr)
providers.route.set_link_info(env.tundev, state='up', mtu=mtu)

providers.route.remove_address(env.tundev)

# set IPv4, IPv6 addresses for tunnel device
if env.myaddr:
providers.route.add_address(env.tundev, env.myaddr)
Expand Down Expand Up @@ -254,6 +272,10 @@ def do_connect(env, args):
else:
providers.domain_vpn_dns.configure_domain_vpn_dns(args.vpn_domains, env.dns)

# set NRPT DNS rules (Windows only)
for (domain, servers) in args.nrpt.items():
providers.nrpt.add_nrtp(domain, servers)


def do_post_connect(env, args):
global providers
Expand Down Expand Up @@ -321,7 +343,7 @@ def do_post_connect(env, args):
for ip in ip_routes:
if args.verbose > 1:
print("Adding route to %s (for named hosts) through %s." % (ip, env.tundev), file=stderr)
providers.route.replace_route(ip, dev=env.tundev)
providers.route.replace_route(ip, dev=env.tundev, via=(env.via if platform.startswith("win32") else None))
else:
providers.route.flush_cache()
if args.verbose:
Expand Down Expand Up @@ -412,6 +434,9 @@ def parse_env(environ=os.environ):
assert env.network.netmask == env.netmask, \
"IPv4 network (INTERNAL_IP4_{{NETADDR,NETMASK}}) {ad}/{nm} does not match INTERNAL_IP4_NETMASKLEN={nml} (implies /{nmi})".format(
ad=orig_netaddr, nm=env.netmask, nml=env.netmasklen, nmi=env.network.netmask)
# first/lowest IP-addr in the range should be internal GW (seen in Windows vpnc-script.js)
lowest_hosts = islice(env.network.hosts(), 0, 2)
env.via = lowest_hosts[1] if len(lowest_hosts) == 2 else env.network.network_address
assert env.network.netmask == env.netmask

# Need to match behavior of original vpnc-script here
Expand Down Expand Up @@ -478,6 +503,8 @@ def parse_args_and_env(args=None, environ=os.environ):
g.add_argument('--no-ns-hosts', action='store_false', dest='ns_hosts', default=True, help='Do not add nameserver aliases to /etc/hosts (default is to name them dns0.tun0, etc.)')
g.add_argument('--nbns', action='store_true', dest='nbns', help='Include NBNS (Windows/NetBIOS nameservers) as well as DNS nameservers')
g.add_argument('--domains-vpn-dns', dest='vpn_domains', default=None, help="comma separated domains to query with vpn dns")
if platform.startswith('win32'):
g.add_argument('--nrpt', default=[], action='append', help='NRPT DNS mapping in form .sub.domain.org=8.8.8.8,4.4.4.4')
g.add_argument('--kerberos-dc', metavar='REALM', help='Lookup Kerberos5 domain controller (DC) hosts for the given realm, and add routes and /etc/hosts entries for them.')
g = p.add_argument_group('Debugging options')
g.add_argument('--self-test', action='store_true', help='Stop after verifying that environment variables and providers are configured properly.')
Expand Down Expand Up @@ -537,6 +564,13 @@ def finalize_args_and_env(args, env):
if exe and os.path.basename(exe) in ('dash', 'bash', 'sh', 'tcsh', 'csh', 'ksh', 'zsh'):
args.ppid = providers.process.ppid_of(args.ppid)

if args.nrpt:
args_nrpt = args.nrpt
args.nrpt = {}
for nrpt in args_nrpt:
dom, dns = str.split(nrpt,"=", 1);
args.nrpt[dom] = str.split(dns, ",");


def main(args=None, environ=os.environ):
global providers
Expand Down
4 changes: 2 additions & 2 deletions vpn_slice/freebsd.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import os

from .posix import PosixProcessProvider
from .portable import PythonOsProcessProvider


class ProcfsProvider(PosixProcessProvider):
class ProcfsProvider(PythonOsProcessProvider):
def pid2exe(self, pid):
try:
return os.readlink('/proc/%d/file' % pid)
Expand Down
4 changes: 2 additions & 2 deletions vpn_slice/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
import stat
import subprocess

from .posix import PosixProcessProvider
from .portable import PythonOsProcessProvider
from .provider import FirewallProvider, RouteProvider, TunnelPrepProvider
from .util import get_executable


class ProcfsProvider(PosixProcessProvider):
class ProcfsProvider(PythonOsProcessProvider):
def pid2exe(self, pid):
try:
return os.readlink('/proc/%d/exe' % pid)
Expand Down
4 changes: 2 additions & 2 deletions vpn_slice/mac.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import subprocess
from ipaddress import ip_interface

from .posix import PosixProcessProvider
from .portable import PythonOsProcessProvider
from .provider import FirewallProvider, RouteProvider, SplitDNSProvider
from .util import get_executable


class PsProvider(PosixProcessProvider):
class PsProvider(PythonOsProcessProvider):
def __init__(self):
self.lsof = get_executable('/usr/sbin/lsof')
self.ps = get_executable('/bin/ps')
Expand Down
19 changes: 19 additions & 0 deletions vpn_slice/portable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import os
from signal import SIGTERM

from .provider import ProcessProvider


class PythonOsProcessProvider(ProcessProvider):
def kill(self, pid, signal=SIGTERM):
os.kill(pid, signal)

def pid(self):
return os.getpid()

def is_alive(self, pid):
try:
os.kill(pid, 0)
return True
except ProcessLookupError:
return False
7 changes: 5 additions & 2 deletions vpn_slice/posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def __init__(self, path):
def write_hosts(self, host_map, name):
tag = 'vpn-slice-{} AUTOCREATED'.format(name)
with open(self.path, 'r+') as hostf:
fcntl.flock(hostf, fcntl.LOCK_EX) # POSIX only, obviously
self.lock_hosts_file(hostf)
lines = hostf.readlines()
keeplines = [l for l in lines if not l.endswith('# %s\n' % tag)]
hostf.seek(0, 0)
Expand All @@ -110,11 +110,14 @@ def write_hosts(self, host_map, name):
hostf.truncate()
return len(host_map) or len(lines) - len(keeplines)


class PosixHostsFileProvider(HostsFileProvider):
def __init__(self):
super().__init__('/etc/hosts')

def lock_hosts_file(self, hostf):
import fcntl
fcntl.flock(hostf, fcntl.LOCK_EX) # POSIX only, obviously


class PosixProcessProvider(ProcessProvider):
def kill(self, pid, signal=SIGTERM):
Expand Down
Loading