summaryrefslogtreecommitdiffstats
path: root/sshuttle/cmdline.py
diff options
context:
space:
mode:
authorvieira <vieira@yubo.be>2016-03-21 23:45:19 +0000
committerBrian May <brian@linuxpenguins.xyz>2016-03-22 13:12:59 +1100
commit05bacf6fd66bf89ad9a9b9f3e0b09e75bc73d706 (patch)
tree95a0f420ce19b8e6a1afbfac2d5e91ba4c90633f /sshuttle/cmdline.py
parentdea3f21943f111ae7991eaad380d365c263a9f9b (diff)
Use argparse for command line options
Fixes the kind of problems reported on #75 but does break the command line "API" (hopefully).
Diffstat (limited to 'sshuttle/cmdline.py')
-rw-r--r--sshuttle/cmdline.py183
1 files changed, 16 insertions, 167 deletions
diff --git a/sshuttle/cmdline.py b/sshuttle/cmdline.py
index 48c0fbe..279e507 100644
--- a/sshuttle/cmdline.py
+++ b/sshuttle/cmdline.py
@@ -2,198 +2,47 @@ import sys
import re
import socket
import sshuttle.helpers as helpers
-import sshuttle.options as options
import sshuttle.client as client
import sshuttle.firewall as firewall
import sshuttle.hostwatch as hostwatch
import sshuttle.ssyslog as ssyslog
+from sshuttle.options import parser
from sshuttle.helpers import family_ip_tuple, log, Fatal
-
-# 1.2.3.4/5 or just 1.2.3.4
-def parse_subnet4(s):
- m = re.match(r'(\d+)(?:\.(\d+)\.(\d+)\.(\d+))?(?:/(\d+))?$', s)
- if not m:
- raise Fatal('%r is not a valid IP subnet format' % s)
- (a, b, c, d, width) = m.groups()
- (a, b, c, d) = (int(a or 0), int(b or 0), int(c or 0), int(d or 0))
- if width is None:
- width = 32
- else:
- width = int(width)
- if a > 255 or b > 255 or c > 255 or d > 255:
- raise Fatal('%d.%d.%d.%d has numbers > 255' % (a, b, c, d))
- if width > 32:
- raise Fatal('*/%d is greater than the maximum of 32' % width)
- return(socket.AF_INET, '%d.%d.%d.%d' % (a, b, c, d), width)
-
-
-# 1:2::3/64 or just 1:2::3
-def parse_subnet6(s):
- m = re.match(r'(?:([a-fA-F\d:]+))?(?:/(\d+))?$', s)
- if not m:
- raise Fatal('%r is not a valid IP subnet format' % s)
- (net, width) = m.groups()
- if width is None:
- width = 128
- else:
- width = int(width)
- if width > 128:
- raise Fatal('*/%d is greater than the maximum of 128' % width)
- return(socket.AF_INET6, net, width)
-
-
-# Subnet file, supporting empty lines and hash-started comment lines
-def parse_subnet_file(s):
- try:
- handle = open(s, 'r')
- except OSError:
- raise Fatal('Unable to open subnet file: %s' % s)
-
- raw_config_lines = handle.readlines()
- config_lines = []
- for line_no, line in enumerate(raw_config_lines):
- line = line.strip()
- if len(line) == 0:
- continue
- if line[0] == '#':
- continue
- config_lines.append(line)
-
- return config_lines
-
-
-# list of:
-# 1.2.3.4/5 or just 1.2.3.4
-# 1:2::3/64 or just 1:2::3
-def parse_subnets(subnets_str):
- subnets = []
- for s in subnets_str:
- if ':' in s:
- subnet = parse_subnet6(s)
- else:
- subnet = parse_subnet4(s)
- subnets.append(subnet)
- return subnets
-
-
-# 1.2.3.4:567 or just 1.2.3.4 or just 567
-def parse_ipport4(s):
- s = str(s)
- m = re.match(r'(?:(\d+)\.(\d+)\.(\d+)\.(\d+))?(?::)?(?:(\d+))?$', s)
- if not m:
- raise Fatal('%r is not a valid IP:port format' % s)
- (a, b, c, d, port) = m.groups()
- (a, b, c, d, port) = (int(a or 0), int(b or 0), int(c or 0), int(d or 0),
- int(port or 0))
- if a > 255 or b > 255 or c > 255 or d > 255:
- raise Fatal('%d.%d.%d.%d has numbers > 255' % (a, b, c, d))
- if port > 65535:
- raise Fatal('*:%d is greater than the maximum of 65535' % port)
- if a is None:
- a = b = c = d = 0
- return ('%d.%d.%d.%d' % (a, b, c, d), port)
-
-
-# [1:2::3]:456 or [1:2::3] or 456
-def parse_ipport6(s):
- s = str(s)
- m = re.match(r'(?:\[([^]]*)])?(?::)?(?:(\d+))?$', s)
- if not m:
- raise Fatal('%s is not a valid IP:port format' % s)
- (ip, port) = m.groups()
- (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...>
-sshuttle --firewall <port> <subnets...>
-sshuttle --hostwatch
---
-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 or pf
-python= path to python interpreter on the remote server
-r,remote= ssh hostname (and optional username) of remote sshuttle server
-x,exclude= exclude this subnet (can be used more than once)
-X,exclude-from= exclude the subnets in a file (whitespace separated)
-v,verbose increase debug message verbosity
-V,version print the sshuttle version number and exit
-e,ssh-cmd= the command to use to connect to the remote [ssh]
-seed-hosts= with -H, use these hostnames for initial scan (comma-separated)
-no-latency-control sacrifice latency to improve bandwidth benchmarks
-wrap= restart counting channel numbers after this number (for testing)
-disable-ipv6 disables ipv6 support
-D,daemon run in the background as a daemon
-s,subnets= file where the subnets are stored, instead of on the command line
-syslog send log messages to syslog (default if you use --daemon)
-pidfile= pidfile name (only if using --daemon) [./sshuttle.pid]
-server (internal use only)
-firewall (internal use only)
-hostwatch (internal use only)
-"""
-
-
def main():
- o = options.Options(optspec)
- (opt, flags, extra) = o.parse(sys.argv[1:])
+ opt = parser.parse_args()
- if opt.version:
- from sshuttle.version import version
- print(version)
- return 0
if opt.daemon:
opt.syslog = 1
if opt.wrap:
import sshuttle.ssnet as ssnet
- ssnet.MAX_CHANNEL = int(opt.wrap)
- helpers.verbose = opt.verbose or 0
+ ssnet.MAX_CHANNEL = opt.wrap
+ helpers.verbose = opt.verbose
try:
if opt.firewall:
- if len(extra) != 0:
- o.fatal('exactly zero arguments expected')
+ if opt.subnets:
+ parser.error('exactly zero arguments expected')
return firewall.main(opt.method, opt.syslog)
elif opt.hostwatch:
- return hostwatch.hw_main(extra)
+ return hostwatch.hw_main(opt.subnets)
else:
- if len(extra) < 1 and not opt.auto_nets and not opt.subnets:
- o.fatal('at least one subnet, subnet file, or -N expected')
- includes = extra
- excludes = ['127.0.0.0/8']
- for k, v in flags:
- if k in ('-x', '--exclude'):
- excludes.append(v)
- if k in ('-X', '--exclude-from'):
- excludes += open(v).read().split()
+ includes = opt.subnets_from_file or opt.subnets
+ excludes = opt.exclude
+ if not includes and not opt.auto_nets:
+ parser.error('at least one subnet, subnet file, or -N expected')
remotename = opt.remote
if remotename == '' or remotename == '-':
remotename = None
- nslist = [family_ip_tuple(ns) for ns in parse_list(opt.ns_hosts)]
+ nslist = [family_ip_tuple(ns) for ns in opt.ns_hosts]
if opt.seed_hosts and not opt.auto_hosts:
- o.fatal('--seed-hosts only works if you also use -H')
+ parser.error('--seed-hosts only works if you also use -H')
if opt.seed_hosts:
sh = re.split(r'[\s,]+', (opt.seed_hosts or "").strip())
elif opt.auto_hosts:
sh = []
else:
sh = None
- if opt.subnets:
- includes = parse_subnet_file(opt.subnets)
- if not opt.method:
- method_name = "auto"
- elif opt.method in ["auto", "nat", "tproxy", "pf"]:
- method_name = opt.method
- else:
- o.fatal("method_name %s not supported" % opt.method)
if opt.listen:
ipport_v6 = None
ipport_v4 = None
@@ -218,11 +67,11 @@ def main():
opt.latency_control,
opt.dns,
nslist,
- method_name,
+ opt.method,
sh,
opt.auto_nets,
- parse_subnets(includes),
- parse_subnets(excludes),
+ includes,
+ excludes,
opt.daemon, opt.pidfile)
if return_code == 0: