Commit f1c79c7e authored by Brian May's avatar Brian May

PEP8 fixes.

parent 5529a04c
import sys, zlib import sys
import zlib
z = zlib.decompressobj() z = zlib.decompressobj()
mainmod = sys.modules[__name__] mainmod = sys.modules[__name__]
......
This diff is collapsed.
This diff is collapsed.
import sys, os, socket, errno import sys
import socket
import errno
logprefix = '' logprefix = ''
verbose = 0 verbose = 0
def log(s): def log(s):
try: try:
sys.stdout.flush() sys.stdout.flush()
...@@ -13,14 +16,17 @@ def log(s): ...@@ -13,14 +16,17 @@ def log(s):
# our tty closes. That sucks, but it's no reason to abort the program. # our tty closes. That sucks, but it's no reason to abort the program.
pass pass
def debug1(s): def debug1(s):
if verbose >= 1: if verbose >= 1:
log(s) log(s)
def debug2(s): def debug2(s):
if verbose >= 2: if verbose >= 2:
log(s) log(s)
def debug3(s): def debug3(s):
if verbose >= 3: if verbose >= 3:
log(s) log(s)
...@@ -43,9 +49,9 @@ def resolvconf_nameservers(): ...@@ -43,9 +49,9 @@ def resolvconf_nameservers():
words = line.lower().split() words = line.lower().split()
if len(words) >= 2 and words[0] == 'nameserver': if len(words) >= 2 and words[0] == 'nameserver':
if ':' in words[1]: if ':' in words[1]:
l.append((socket.AF_INET6,words[1])) l.append((socket.AF_INET6, words[1]))
else: else:
l.append((socket.AF_INET,words[1])) l.append((socket.AF_INET, words[1]))
return l return l
...@@ -58,10 +64,10 @@ def resolvconf_random_nameserver(): ...@@ -58,10 +64,10 @@ def resolvconf_random_nameserver():
random.shuffle(l) random.shuffle(l)
return l[0] return l[0]
else: else:
return (socket.AF_INET,'127.0.0.1') return (socket.AF_INET, '127.0.0.1')
def islocal(ip,family): def islocal(ip, family):
sock = socket.socket(family) sock = socket.socket(family)
try: try:
try: try:
...@@ -83,4 +89,3 @@ def family_to_string(family): ...@@ -83,4 +89,3 @@ def family_to_string(family):
return "AF_INET" return "AF_INET"
else: else:
return str(family) return str(family)
import time, socket, re, select, errno import time
import socket
import re
import select
import errno
import os
import sys
if not globals().get('skip_imports'): if not globals().get('skip_imports'):
import compat.ssubprocess as ssubprocess import compat.ssubprocess as ssubprocess
import helpers import helpers
from helpers import * from helpers import log, debug1, debug2, debug3
POLL_TIME = 60*15 POLL_TIME = 60 * 15
NETSTAT_POLL_TIME = 30 NETSTAT_POLL_TIME = 30
CACHEFILE=os.path.expanduser('~/.sshuttle.hosts') CACHEFILE = os.path.expanduser('~/.sshuttle.hosts')
_nmb_ok = True _nmb_ok = True
...@@ -28,7 +34,7 @@ def write_host_cache(): ...@@ -28,7 +34,7 @@ def write_host_cache():
tmpname = '%s.%d.tmp' % (CACHEFILE, os.getpid()) tmpname = '%s.%d.tmp' % (CACHEFILE, os.getpid())
try: try:
f = open(tmpname, 'wb') f = open(tmpname, 'wb')
for name,ip in sorted(hostnames.items()): for name, ip in sorted(hostnames.items()):
f.write('%s,%s\n' % (name, ip)) f.write('%s,%s\n' % (name, ip))
f.close() f.close()
os.rename(tmpname, CACHEFILE) os.rename(tmpname, CACHEFILE)
...@@ -50,18 +56,18 @@ def read_host_cache(): ...@@ -50,18 +56,18 @@ def read_host_cache():
for line in f: for line in f:
words = line.strip().split(',') words = line.strip().split(',')
if len(words) == 2: if len(words) == 2:
(name,ip) = words (name, ip) = words
name = re.sub(r'[^-\w]', '-', name).strip() name = re.sub(r'[^-\w]', '-', name).strip()
ip = re.sub(r'[^0-9.]', '', ip).strip() ip = re.sub(r'[^0-9.]', '', ip).strip()
if name and ip: if name and ip:
found_host(name, ip) found_host(name, ip)
def found_host(hostname, ip): def found_host(hostname, ip):
hostname = re.sub(r'\..*', '', hostname) hostname = re.sub(r'\..*', '', hostname)
hostname = re.sub(r'[^-\w]', '_', hostname) hostname = re.sub(r'[^-\w]', '_', hostname)
if (ip.startswith('127.') or ip.startswith('255.') if (ip.startswith('127.') or ip.startswith('255.')
or hostname == 'localhost'): or hostname == 'localhost'):
return return
oldip = hostnames.get(hostname) oldip = hostnames.get(hostname)
if oldip != ip: if oldip != ip:
...@@ -94,7 +100,7 @@ def _check_revdns(ip): ...@@ -94,7 +100,7 @@ def _check_revdns(ip):
debug3('< %s\n' % r[0]) debug3('< %s\n' % r[0])
check_host(r[0]) check_host(r[0])
found_host(r[0], ip) found_host(r[0], ip)
except socket.herror, e: except socket.herror:
pass pass
...@@ -105,7 +111,7 @@ def _check_dns(hostname): ...@@ -105,7 +111,7 @@ def _check_dns(hostname):
debug3('< %s\n' % ip) debug3('< %s\n' % ip)
check_host(ip) check_host(ip)
found_host(hostname, ip) found_host(hostname, ip)
except socket.gaierror, e: except socket.gaierror:
pass pass
...@@ -123,7 +129,7 @@ def _check_netstat(): ...@@ -123,7 +129,7 @@ def _check_netstat():
for ip in re.findall(r'\d+\.\d+\.\d+\.\d+', content): for ip in re.findall(r'\d+\.\d+\.\d+\.\d+', content):
debug3('< %s\n' % ip) debug3('< %s\n' % ip)
check_host(ip) check_host(ip)
def _check_smb(hostname): def _check_smb(hostname):
return return
...@@ -187,7 +193,7 @@ def _check_nmb(hostname, is_workgroup, is_master): ...@@ -187,7 +193,7 @@ def _check_nmb(hostname, is_workgroup, is_master):
global _nmb_ok global _nmb_ok
if not _nmb_ok: if not _nmb_ok:
return return
argv = ['nmblookup'] + ['-M']*is_master + ['--', hostname] argv = ['nmblookup'] + ['-M'] * is_master + ['--', hostname]
debug2(' > n%d%d: %s\n' % (is_workgroup, is_master, hostname)) debug2(' > n%d%d: %s\n' % (is_workgroup, is_master, hostname))
try: try:
p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null) p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null)
...@@ -228,13 +234,13 @@ def check_workgroup(hostname): ...@@ -228,13 +234,13 @@ def check_workgroup(hostname):
def _enqueue(op, *args): def _enqueue(op, *args):
t = (op,args) t = (op, args)
if queue.get(t) == None: if queue.get(t) is None:
queue[t] = 0 queue[t] = 0
def _stdin_still_ok(timeout): def _stdin_still_ok(timeout):
r,w,x = select.select([sys.stdin.fileno()], [], [], timeout) r, w, x = select.select([sys.stdin.fileno()], [], [], timeout)
if r: if r:
b = os.read(sys.stdin.fileno(), 4096) b = os.read(sys.stdin.fileno(), 4096)
if not b: if not b:
...@@ -249,7 +255,7 @@ def hw_main(seed_hosts): ...@@ -249,7 +255,7 @@ def hw_main(seed_hosts):
helpers.logprefix = 'hostwatch: ' helpers.logprefix = 'hostwatch: '
read_host_cache() read_host_cache()
_enqueue(_check_etc_hosts) _enqueue(_check_etc_hosts)
_enqueue(_check_netstat) _enqueue(_check_netstat)
check_host('localhost') check_host('localhost')
...@@ -261,8 +267,8 @@ def hw_main(seed_hosts): ...@@ -261,8 +267,8 @@ def hw_main(seed_hosts):
while 1: while 1:
now = time.time() now = time.time()
for t,last_polled in queue.items(): for t, last_polled in queue.items():
(op,args) = t (op, args) = t
if not _stdin_still_ok(0): if not _stdin_still_ok(0):
break break
maxtime = POLL_TIME maxtime = POLL_TIME
...@@ -275,7 +281,7 @@ def hw_main(seed_hosts): ...@@ -275,7 +281,7 @@ def hw_main(seed_hosts):
sys.stdout.flush() sys.stdout.flush()
except IOError: except IOError:
break break
# FIXME: use a smarter timeout based on oldest last_polled # FIXME: use a smarter timeout based on oldest last_polled
if not _stdin_still_ok(1): if not _stdin_still_ok(1):
break break
import sys, os, re, socket import sys
import helpers, options, client, server, firewall, hostwatch import re
import compat.ssubprocess as ssubprocess import socket
from helpers import * import helpers
import options
import client
import server
import firewall
import hostwatch
from helpers import log, Fatal
# 1.2.3.4/5 or just 1.2.3.4 # 1.2.3.4/5 or just 1.2.3.4
...@@ -9,17 +15,17 @@ def parse_subnet4(s): ...@@ -9,17 +15,17 @@ def parse_subnet4(s):
m = re.match(r'(\d+)(?:\.(\d+)\.(\d+)\.(\d+))?(?:/(\d+))?$', s) m = re.match(r'(\d+)(?:\.(\d+)\.(\d+)\.(\d+))?(?:/(\d+))?$', s)
if not m: if not m:
raise Fatal('%r is not a valid IP subnet format' % s) raise Fatal('%r is not a valid IP subnet format' % s)
(a,b,c,d,width) = m.groups() (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)) (a, b, c, d) = (int(a or 0), int(b or 0), int(c or 0), int(d or 0))
if width == None: if width is None:
width = 32 width = 32
else: else:
width = int(width) width = int(width)
if a > 255 or b > 255 or c > 255 or d > 255: 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)) raise Fatal('%d.%d.%d.%d has numbers > 255' % (a, b, c, d))
if width > 32: if width > 32:
raise Fatal('*/%d is greater than the maximum of 32' % width) raise Fatal('*/%d is greater than the maximum of 32' % width)
return(socket.AF_INET, '%d.%d.%d.%d' % (a,b,c,d), width) return(socket.AF_INET, '%d.%d.%d.%d' % (a, b, c, d), width)
# 1:2::3/64 or just 1:2::3 # 1:2::3/64 or just 1:2::3
...@@ -27,8 +33,8 @@ def parse_subnet6(s): ...@@ -27,8 +33,8 @@ def parse_subnet6(s):
m = re.match(r'(?:([a-fA-F\d:]+))?(?:/(\d+))?$', s) m = re.match(r'(?:([a-fA-F\d:]+))?(?:/(\d+))?$', s)
if not m: if not m:
raise Fatal('%r is not a valid IP subnet format' % s) raise Fatal('%r is not a valid IP subnet format' % s)
(net,width) = m.groups() (net, width) = m.groups()
if width == None: if width is None:
width = 128 width = 128
else: else:
width = int(width) width = int(width)
...@@ -41,7 +47,7 @@ def parse_subnet6(s): ...@@ -41,7 +47,7 @@ def parse_subnet6(s):
def parse_subnet_file(s): def parse_subnet_file(s):
try: try:
handle = open(s, 'r') handle = open(s, 'r')
except OSError, e: except OSError:
raise Fatal('Unable to open subnet file: %s' % s) raise Fatal('Unable to open subnet file: %s' % s)
raw_config_lines = handle.readlines() raw_config_lines = handle.readlines()
...@@ -77,16 +83,16 @@ def parse_ipport4(s): ...@@ -77,16 +83,16 @@ def parse_ipport4(s):
m = re.match(r'(?:(\d+)\.(\d+)\.(\d+)\.(\d+))?(?::)?(?:(\d+))?$', s) m = re.match(r'(?:(\d+)\.(\d+)\.(\d+)\.(\d+))?(?::)?(?:(\d+))?$', s)
if not m: if not m:
raise Fatal('%r is not a valid IP:port format' % s) raise Fatal('%r is not a valid IP:port format' % s)
(a,b,c,d,port) = m.groups() (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), (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)) int(port or 0))
if a > 255 or b > 255 or c > 255 or d > 255: 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)) raise Fatal('%d.%d.%d.%d has numbers > 255' % (a, b, c, d))
if port > 65535: if port > 65535:
raise Fatal('*:%d is greater than the maximum of 65535' % port) raise Fatal('*:%d is greater than the maximum of 65535' % port)
if a == None: if a is None:
a = b = c = d = 0 a = b = c = d = 0
return ('%d.%d.%d.%d' % (a,b,c,d), port) return ('%d.%d.%d.%d' % (a, b, c, d), port)
# [1:2::3]:456 or [1:2::3] or 456 # [1:2::3]:456 or [1:2::3] or 456
...@@ -95,8 +101,8 @@ def parse_ipport6(s): ...@@ -95,8 +101,8 @@ def parse_ipport6(s):
m = re.match(r'(?:\[([^]]*)])?(?::)?(?:(\d+))?$', s) m = re.match(r'(?:\[([^]]*)])?(?::)?(?:(\d+))?$', s)
if not m: if not m:
raise Fatal('%s is not a valid IP:port format' % s) raise Fatal('%s is not a valid IP:port format' % s)
(ip,port) = m.groups() (ip, port) = m.groups()
(ip,port) = (ip or '::', int(port or 0)) (ip, port) = (ip or '::', int(port or 0))
return (ip, port) return (ip, port)
...@@ -156,8 +162,8 @@ try: ...@@ -156,8 +162,8 @@ try:
o.fatal('at least one subnet, subnet file, or -N expected') o.fatal('at least one subnet, subnet file, or -N expected')
includes = extra includes = extra
excludes = ['127.0.0.0/8'] excludes = ['127.0.0.0/8']
for k,v in flags: for k, v in flags:
if k in ('-x','--exclude'): if k in ('-x', '--exclude'):
excludes.append(v) excludes.append(v)
remotename = opt.remote remotename = opt.remote
if remotename == '' or remotename == '-': if remotename == '' or remotename == '-':
...@@ -174,10 +180,10 @@ try: ...@@ -174,10 +180,10 @@ try:
includes = parse_subnet_file(opt.subnets) includes = parse_subnet_file(opt.subnets)
if not opt.method: if not opt.method:
method = "auto" method = "auto"
elif opt.method in [ "auto", "nat", "tproxy", "ipfw" ]: elif opt.method in ["auto", "nat", "tproxy", "ipfw"]:
method = opt.method method = opt.method
else: else:
o.fatal("method %s not supported"%opt.method) o.fatal("method %s not supported" % opt.method)
if not opt.listen: if not opt.listen:
if opt.method == "tproxy": if opt.method == "tproxy":
ipport_v6 = parse_ipport6('[::1]:0') ipport_v6 = parse_ipport6('[::1]:0')
...@@ -194,23 +200,23 @@ try: ...@@ -194,23 +200,23 @@ try:
else: else:
ipport_v4 = parse_ipport4(ip) ipport_v4 = parse_ipport4(ip)
return_code = client.main(ipport_v6, ipport_v4, return_code = client.main(ipport_v6, ipport_v4,
opt.ssh_cmd, opt.ssh_cmd,
remotename, remotename,
opt.python, opt.python,
opt.latency_control, opt.latency_control,
opt.dns, opt.dns,
method, method,
sh, sh,
opt.auto_nets, opt.auto_nets,
parse_subnets(includes), parse_subnets(includes),
parse_subnets(excludes), parse_subnets(excludes),
opt.syslog, opt.daemon, opt.pidfile) opt.syslog, opt.daemon, opt.pidfile)
if return_code == 0: if return_code == 0:
log('Normal exit code, exiting...') log('Normal exit code, exiting...')
else: else:
log('Abnormal exit code detected, failing...' % return_code) log('Abnormal exit code detected, failing...' % return_code)
sys.exit(return_code) sys.exit(return_code)
except Fatal, e: except Fatal, e:
log('fatal: %s\n' % e) log('fatal: %s\n' % e)
......
"""Command-line options parser. """Command-line options parser.
With the help of an options spec string, easily parse command-line options. With the help of an options spec string, easily parse command-line options.
""" """
import sys, os, textwrap, getopt, re, struct import sys
import os
import textwrap
import getopt
import re
import struct
class OptDict: class OptDict:
def __init__(self): def __init__(self):
self._opts = {} self._opts = {}
...@@ -46,7 +53,8 @@ def _atoi(v): ...@@ -46,7 +53,8 @@ def _atoi(v):
def _remove_negative_kv(k, v): def _remove_negative_kv(k, v):
if k.startswith('no-') or k.startswith('no_'): if k.startswith('no-') or k.startswith('no_'):
return k[3:], not v return k[3:], not v
return k,v return k, v
def _remove_negative_k(k): def _remove_negative_k(k):
return _remove_negative_kv(k, None)[0] return _remove_negative_kv(k, None)[0]
...@@ -55,15 +63,17 @@ def _remove_negative_k(k): ...@@ -55,15 +63,17 @@ def _remove_negative_k(k):
def _tty_width(): def _tty_width():
s = struct.pack("HHHH", 0, 0, 0, 0) s = struct.pack("HHHH", 0, 0, 0, 0)
try: try:
import fcntl, termios import fcntl
import termios
s = fcntl.ioctl(sys.stderr.fileno(), termios.TIOCGWINSZ, s) s = fcntl.ioctl(sys.stderr.fileno(), termios.TIOCGWINSZ, s)
except (IOError, ImportError): except (IOError, ImportError):
return _atoi(os.environ.get('WIDTH')) or 70 return _atoi(os.environ.get('WIDTH')) or 70
(ysize,xsize,ypix,xpix) = struct.unpack('HHHH', s) (ysize, xsize, ypix, xpix) = struct.unpack('HHHH', s)
return xsize or 70 return xsize or 70
class Options: class Options:
"""Option parser. """Option parser.
When constructed, two strings are mandatory. The first one is the command When constructed, two strings are mandatory. The first one is the command
name showed before error messages. The second one is a string called an name showed before error messages. The second one is a string called an
...@@ -76,6 +86,7 @@ class Options: ...@@ -76,6 +86,7 @@ class Options:
By default, the parser function is getopt.gnu_getopt, and the abort By default, the parser function is getopt.gnu_getopt, and the abort
behaviour is to exit the program. behaviour is to exit the program.
""" """
def __init__(self, optspec, optfunc=getopt.gnu_getopt, def __init__(self, optspec, optfunc=getopt.gnu_getopt,
onabort=_default_onabort): onabort=_default_onabort):
self.optspec = optspec self.optspec = optspec
...@@ -95,7 +106,8 @@ class Options: ...@@ -95,7 +106,8 @@ class Options:
first_syn = True first_syn = True
while lines: while lines:
l = lines.pop() l = lines.pop()
if l == '--': break if l == '--':
break
out.append('%s: %s\n' % (first_syn and 'usage' or ' or', l)) out.append('%s: %s\n' % (first_syn and 'usage' or ' or', l))
first_syn = False first_syn = False
out.append('\n') out.append('\n')
...@@ -122,7 +134,7 @@ class Options: ...@@ -122,7 +134,7 @@ class Options:
flagl = flags.split(',') flagl = flags.split(',')
flagl_nice = [] flagl_nice = []
for _f in flagl: for _f in flagl:
f,dvi = _remove_negative_kv(_f, _intify(defval)) f, dvi = _remove_negative_kv(_f, _intify(defval))
self._aliases[f] = _remove_negative_k(flagl[0]) self._aliases[f] = _remove_negative_k(flagl[0])
self._hasparms[f] = has_parm self._hasparms[f] = has_parm
self._defaults[f] = dvi self._defaults[f] = dvi
...@@ -140,8 +152,8 @@ class Options: ...@@ -140,8 +152,8 @@ class Options:
flags_nice += ' ...' flags_nice += ' ...'
prefix = ' %-20s ' % flags_nice prefix = ' %-20s ' % flags_nice
argtext = '\n'.join(textwrap.wrap(extra, width=_tty_width(), argtext = '\n'.join(textwrap.wrap(extra, width=_tty_width(),
initial_indent=prefix, initial_indent=prefix,
subsequent_indent=' '*28)) subsequent_indent=' ' * 28))
out.append(argtext + '\n') out.append(argtext + '\n')
last_was_option = True last_was_option = True
else: else:
...@@ -170,17 +182,18 @@ class Options: ...@@ -170,17 +182,18 @@ class Options:
and "extra" is a list of positional arguments. and "extra" is a list of positional arguments.
""" """
try: try:
(flags,extra) = self.optfunc(args, self._shortopts, self._longopts) (flags, extra) = self.optfunc(
args, self._shortopts, self._longopts)
except getopt.GetoptError, e: except getopt.GetoptError, e:
self.fatal(e) self.fatal(e)
opt = OptDict() opt = OptDict()
for k,v in self._defaults.iteritems(): for k, v in self._defaults.iteritems():
k = self._aliases[k] k = self._aliases[k]
opt[k] = v opt[k] = v
for (k,v) in flags: for (k, v) in flags:
k = k.lstrip('-') k = k.lstrip('-')
if k in ('h', '?', 'help'): if k in ('h', '?', 'help'):
self.usage() self.usage()
...@@ -195,6 +208,6 @@ class Options: ...@@ -195,6 +208,6 @@ class Options:
else: else:
v = _intify(v) v = _intify(v)
opt[k] = v opt[k] = v
for (f1,f2) in self._aliases.iteritems(): for (f1, f2) in self._aliases.iteritems():
opt[f1] = opt._opts.get(f2) opt[f1] = opt._opts.get(f2)
return (opt,flags,extra) return (opt, flags, extra)
import re, struct, socket, select, traceback, time import re
import struct
import socket
import traceback
import time
import sys
import os
if not globals().get('skip_imports'): if not globals().get('skip_imports'):
import ssnet, helpers, hostwatch import ssnet
import helpers
import hostwatch
import compat.ssubprocess as ssubprocess import compat.ssubprocess as ssubprocess
from ssnet import SockWrapper, Handler, Proxy, Mux, MuxWrapper from ssnet import Handler, Proxy, Mux, MuxWrapper
from helpers import * from helpers import log, debug1, debug2, debug3, Fatal, \
resolvconf_random_nameserver
if not globals().get('latency_control'):
latency_control = None
def _ipmatch(ipstr): def _ipmatch(ipstr):
...@@ -14,13 +27,13 @@ def _ipmatch(ipstr): ...@@ -14,13 +27,13 @@ def _ipmatch(ipstr):
g = m.groups() g = m.groups()
ips = g[0] ips = g[0]
width = int(g[4] or 32) width = int(g[4] or 32)
if g[1] == None: if g[1] is None:
ips += '.0.0.0' ips += '.0.0.0'
width = min(width, 8) width = min(width, 8)
elif g[2] == None: elif g[2] is None:
ips += '.0.0' ips += '.0.0'
width = min(width, 16) width = min(width, 16)
elif g[3] == None: elif g[3] is None:
ips += '.0' ips += '.0'
width = min(width, 24) width = min(width, 24)
return (struct.unpack('!I', socket.inet_aton(ips))[0], width) return (struct.unpack('!I', socket.inet_aton(ips))[0], width)
...@@ -38,12 +51,12 @@ def _maskbits(netmask): ...@@ -38,12 +51,12 @@ def _maskbits(netmask):
return 32 return 32
for i in range(32): for i in range(32):
if netmask[0] & _shl(1, i): if netmask[0] & _shl(1, i):
return 32-i return 32 - i
return 0 return 0
def _shl(n, bits): def _shl(n, bits):
return n * int(2**bits) return n * int(2 ** bits)
def _list_routes(): def _list_routes():
...@@ -58,8 +71,9 @@ def _list_routes(): ...@@ -58,8 +71,9 @@ def _list_routes():
maskw = _ipmatch(cols[2]) # linux only maskw = _ipmatch(cols[2]) # linux only
mask = _maskbits(maskw) # returns 32 if maskw is null mask = _maskbits(maskw) # returns 32 if maskw is null
width = min(ipw[1], mask) width = min(ipw[1], mask)
ip = ipw[0] & _shl(_shl(1, width) - 1, 32-width) ip = ipw[0] & _shl(_shl(1, width) - 1, 32 - width)
routes.append((socket.AF_INET, socket.inet_ntoa(struct.pack('!I', ip)), width)) routes.append(
(socket.AF_INET, socket.inet_ntoa(struct.pack('!I', ip)), width))
rv = p.wait() rv = p.wait()
if rv != 0: if rv != 0:
log('WARNING: %r returned %d\n' % (argv, rv)) log('WARNING: %r returned %d\n' % (argv, rv))
...@@ -68,9 +82,9 @@ def _list_routes(): ...@@ -68,9 +82,9 @@ def _list_routes():
def list_routes(): def list_routes():
for (family, ip,width) in _list_routes(): for (family, ip, width) in _list_routes():
if not ip.startswith('0.') and not ip.startswith('127.'): if not ip.startswith('0.') and not ip.startswith('127.'):
yield (family, ip,width) yield (family, ip, width)
def _exc_dump(): def _exc_dump():
...@@ -79,7 +93,7 @@ def _exc_dump(): ...@@ -79,7 +93,7 @@ def _exc_dump():
def start_hostwatch(seed_hosts): def start_hostwatch(seed_hosts):
s1,s2 = socket.socketpair() s1, s2 = socket.socketpair()
pid = os.fork() pid = os.fork()
if not pid: if not pid:
# child # child
...@@ -91,27 +105,29 @@ def start_hostwatch(seed_hosts): ...@@ -91,27 +105,29 @@ def start_hostwatch(seed_hosts):
os.dup2(s1.fileno(), 0) os.dup2(s1.fileno(), 0)
s1.close() s1.close()
rv = hostwatch.hw_main(seed_hosts) or 0 rv = hostwatch.hw_main(seed_hosts) or 0
except Exception, e: except Exception:
log('%s\n' % _exc_dump()) log('%s\n' % _exc_dump())
rv = 98 rv = 98
finally: finally:
os._exit(rv) os._exit(rv)
s1.close() s1.close()
return pid,s2 return pid, s2
class Hostwatch: class Hostwatch:
def __init__(self): def __init__(self):
self.pid = 0 self.pid = 0
self.sock = None self.sock = None
class DnsProxy(Handler): class DnsProxy(Handler):
def __init__(self, mux, chan, request): def __init__(self, mux, chan, request):
# FIXME! IPv4 specific # FIXME! IPv4 specific
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Handler.__init__(self, [sock]) Handler.__init__(self, [sock])
self.timeout = time.time()+30 self.timeout = time.time() + 30
self.mux = mux self.mux = mux
self.chan = chan self.chan = chan
self.tries = 0 self.tries = 0
...@@ -164,10 +180,11 @@ class DnsProxy(Handler): ...@@ -164,10 +180,11 @@ class DnsProxy(Handler):
class UdpProxy(Handler): class UdpProxy(Handler):
def __init__(self, mux, chan, family): def __init__(self, mux, chan, family):
sock = socket.socket(family, socket.SOCK_DGRAM) sock = socket.socket(family, socket.SOCK_DGRAM)
Handler.__init__(self, [sock]) Handler.__init__(self, [sock])
self.timeout = time.time()+30 self.timeout = time.time() + 30
self.mux = mux self.mux = mux
self.chan = chan self.chan = chan
self.sock = sock self.sock = sock
...@@ -177,33 +194,35 @@ class UdpProxy(Handler): ...@@ -177,33 +194,35 @@ class UdpProxy(Handler):
def send(self, dstip, data): def send(self, dstip, data):
debug2('UDP: sending to %r port %d\n' % dstip) debug2('UDP: sending to %r port %d\n' % dstip)
try: try:
self.sock.sendto(data,dstip) self.sock.sendto(data, dstip)
except socket.error, e: except socket.error, e:
log('UDP send to %r port %d: %s\n' % (dstip[0], dstip[1], e)) log('UDP send to %r port %d: %s\n' % (dstip[0], dstip[1], e))
return return
def callback(self): def callback(self):
try: try:
data,peer = self.sock.recvfrom(4096) data, peer = self.sock.recvfrom(4096)
except socket.error, e: except socket.error, e:
log('UDP recv from %r port %d: %s\n' % (peer[0], peer[1], e)) log('UDP recv from %r port %d: %s\n' % (peer[0], peer[1], e))
return return
debug2('UDP response: %d bytes\n' % len(data)) debug2('UDP response: %d bytes\n' % len(data))
hdr = "%s,%r,"%(peer[0], peer[1]) hdr = "%s,%r," % (peer[0], peer[1])
self.mux.send(self.chan, ssnet.CMD_UDP_DATA, hdr+data) self.mux.send(self.chan, ssnet.CMD_UDP_DATA, hdr + data)
def main(): def main():
if helpers.verbose >= 1: if helpers.verbose >= 1:
helpers.logprefix = ' s: ' helpers.logprefix = ' s: '
else: else:
helpers.logprefix = 'server: ' helpers.logprefix = 'server: '
assert latency_control is not None
debug1('latency control setting = %r\n' % latency_control) debug1('latency control setting = %r\n' % latency_control)
routes = list(list_routes()) routes = list(list_routes())
debug1('available routes:\n') debug1('available routes:\n')
for r in routes: for r in routes:
debug1(' %d/%s/%d\n' % r) debug1(' %d/%s/%d\n' % r)
# synchronization header # synchronization header
sys.stdout.write('\0\0SSHUTTLE0001') sys.stdout.write('\0\0SSHUTTLE0001')
sys.stdout.flush() sys.stdout.flush()
...@@ -221,7 +240,7 @@ def main(): ...@@ -221,7 +240,7 @@ def main():
hw = Hostwatch() hw = Hostwatch()
hw.leftover = '' hw.leftover = ''
def hostwatch_ready(): def hostwatch_ready():
assert(hw.pid) assert(hw.pid)
content = hw.sock.recv(4096) content = hw.sock.recv(4096)
...@@ -239,13 +258,13 @@ def main(): ...@@ -239,13 +258,13 @@ def main():
def got_host_req(data): def got_host_req(data):
if not hw.pid: if not hw.pid:
(hw.pid,hw.sock) = start_hostwatch(data.strip().split()) (hw.pid, hw.sock) = start_hostwatch(data.strip().split())
handlers.append(Handler(socks = [hw.sock], handlers.append(Handler(socks=[hw.sock],
callback = hostwatch_ready)) callback=hostwatch_ready))
mux.got_host_req = got_host_req mux.got_host_req = got_host_req
def new_channel(channel, data): def new_channel(channel, data):
(family,dstip,dstport) = data.split(',', 2) (family, dstip, dstport) = data.split(',', 2)
family = int(family) family = int(family)
dstport = int(dstport) dstport = int(dstport)
outwrap = ssnet.connect_dst(family, dstip, dstport) outwrap = ssnet.connect_dst(family, dstip, dstport)
...@@ -253,6 +272,7 @@ def main(): ...@@ -253,6 +272,7 @@ def main():
mux.new_channel = new_channel mux.new_channel = new_channel
dnshandlers = {} dnshandlers = {}
def dns_req(channel, data): def dns_req(channel, data):
debug2('Incoming DNS request channel=%d.\n' % channel) debug2('Incoming DNS request channel=%d.\n' % channel)
h = DnsProxy(mux, channel, data) h = DnsProxy(mux, channel, data)
...@@ -261,14 +281,15 @@ def main(): ...@@ -261,14 +281,15 @@ def main():
mux.got_dns_req = dns_req mux.got_dns_req = dns_req
udphandlers = {} udphandlers = {}
def udp_req(channel, cmd, data): def udp_req(channel, cmd, data):
debug2('Incoming UDP request channel=%d, cmd=%d\n' % (channel,cmd)) debug2('Incoming UDP request channel=%d, cmd=%d\n' % (channel, cmd))
if cmd == ssnet.CMD_UDP_DATA: if cmd == ssnet.CMD_UDP_DATA:
(dstip,dstport,data) = data.split(",",2) (dstip, dstport, data) = data.split(",", 2)
dstport = int(dstport) dstport = int(dstport)
debug2('is incoming UDP data. %r %d.\n' % (dstip,dstport)) debug2('is incoming UDP data. %r %d.\n' % (dstip, dstport))
h = udphandlers[channel] h = udphandlers[channel]
h.send((dstip,dstport),data) h.send((dstip, dstport), data)
elif cmd == ssnet.CMD_UDP_CLOSE: elif cmd == ssnet.CMD_UDP_CLOSE:
debug2('is incoming UDP close\n') debug2('is incoming UDP close\n')
h = udphandlers[channel] h = udphandlers[channel]
...@@ -280,21 +301,21 @@ def main(): ...@@ -280,21 +301,21 @@ def main():
family = int(data) family = int(data)
mux.channels[channel] = lambda cmd, data: udp_req(channel, cmd, data) mux.channels[channel] = lambda cmd, data: udp_req(channel, cmd, data)
if channel in udphandlers: if channel in udphandlers:
raise Fatal('UDP connection channel %d already open'%channel) raise Fatal('UDP connection channel %d already open' % channel)
else: else:
h = UdpProxy(mux, channel, family) h = UdpProxy(mux, channel, family)
handlers.append(h) handlers.append(h)
udphandlers[channel] = h udphandlers[channel] = h
mux.got_udp_open = udp_open mux.got_udp_open = udp_open
while mux.ok: while mux.ok:
if hw.pid: if hw.pid:
assert(hw.pid > 0) assert(hw.pid > 0)
(rpid, rv) = os.waitpid(hw.pid, os.WNOHANG) (rpid, rv) = os.waitpid(hw.pid, os.WNOHANG)
if rpid: if rpid:
raise Fatal('hostwatch exited unexpectedly: code 0x%04x\n' % rv) raise Fatal(
'hostwatch exited unexpectedly: code 0x%04x\n' % rv)
ssnet.runonce(handlers, mux) ssnet.runonce(handlers, mux)
if latency_control: if latency_control:
mux.check_fullness() mux.check_fullness()
...@@ -302,12 +323,12 @@ def main(): ...@@ -302,12 +323,12 @@ def main():
if dnshandlers: if dnshandlers:
now = time.time() now = time.time()
for channel,h in dnshandlers.items(): for channel, h in dnshandlers.items():
if h.timeout < now or not h.ok: if h.timeout < now or not h.ok:
debug3('expiring dnsreqs channel=%d\n' % channel) debug3('expiring dnsreqs channel=%d\n' % channel)
del dnshandlers[channel] del dnshandlers[channel]
h.ok = False h.ok = False
for channel,h in udphandlers.items(): for channel, h in udphandlers.items():
if not h.ok: if not h.ok:
debug3('expiring UDP channel=%d\n' % channel) debug3('expiring UDP channel=%d\n' % channel)
del udphandlers[channel] del udphandlers[channel]
......
import sys, os, re, socket, zlib import sys
import os
import re
import socket
import zlib
import compat.ssubprocess as ssubprocess import compat.ssubprocess as ssubprocess
import helpers import helpers
from helpers import * from helpers import debug2
def readfile(name): def readfile(name):
...@@ -15,7 +19,7 @@ def readfile(name): ...@@ -15,7 +19,7 @@ def readfile(name):
def empackage(z, filename, data=None): def empackage(z, filename, data=None):
(path,basename) = os.path.split(filename) (path, basename) = os.path.split(filename)
if not data: if not data:
data = readfile(filename) data = readfile(filename)
content = z.compress(data) content = z.compress(data)
...@@ -24,7 +28,6 @@ def empackage(z, filename, data=None): ...@@ -24,7 +28,6 @@ def empackage(z, filename, data=None):
def connect(ssh_cmd, rhostport, python, stderr, options): def connect(ssh_cmd, rhostport, python, stderr, options):
main_exe = sys.argv[0]
portl = [] portl = []
if (rhostport or '').count(':') > 1: if (rhostport or '').count(':') > 1:
...@@ -35,9 +38,11 @@ def connect(ssh_cmd, rhostport, python, stderr, options): ...@@ -35,9 +38,11 @@ def connect(ssh_cmd, rhostport, python, stderr, options):
result[1] = result[1].strip(':') result[1] = result[1].strip(':')
if result[1] is not '': if result[1] is not '':
portl = ['-p', str(int(result[1]))] portl = ['-p', str(int(result[1]))]
else: # can't disambiguate IPv6 colons and a port number. pass the hostname through. # can't disambiguate IPv6 colons and a port number. pass the hostname
# through.
else:
rhost = rhostport rhost = rhostport
else: # IPv4 else: # IPv4
l = (rhostport or '').split(':', 1) l = (rhostport or '').split(':', 1)
rhost = l[0] rhost = l[0]
if len(l) > 1: if len(l) > 1:
...@@ -48,7 +53,7 @@ def connect(ssh_cmd, rhostport, python, stderr, options): ...@@ -48,7 +53,7 @@ def connect(ssh_cmd, rhostport, python, stderr, options):
z = zlib.compressobj(1) z = zlib.compressobj(1)
content = readfile('assembler.py') content = readfile('assembler.py')
optdata = ''.join("%s=%r\n" % (k,v) for (k,v) in options.items()) optdata = ''.join("%s=%r\n" % (k, v) for (k, v) in options.items())
content2 = (empackage(z, 'cmdline_options.py', optdata) + content2 = (empackage(z, 'cmdline_options.py', optdata) +
empackage(z, 'helpers.py') + empackage(z, 'helpers.py') +
empackage(z, 'compat/ssubprocess.py') + empackage(z, 'compat/ssubprocess.py') +
...@@ -56,7 +61,7 @@ def connect(ssh_cmd, rhostport, python, stderr, options): ...@@ -56,7 +61,7 @@ def connect(ssh_cmd, rhostport, python, stderr, options):
empackage(z, 'hostwatch.py') + empackage(z, 'hostwatch.py') +
empackage(z, 'server.py') + empackage(z, 'server.py') +
"\n") "\n")
pyscript = r""" pyscript = r"""
import sys; import sys;
skip_imports=1; skip_imports=1;
...@@ -65,7 +70,6 @@ def connect(ssh_cmd, rhostport, python, stderr, options): ...@@ -65,7 +70,6 @@ def connect(ssh_cmd, rhostport, python, stderr, options):
""" % (helpers.verbose or 0, len(content)) """ % (helpers.verbose or 0, len(content))
pyscript = re.sub(r'\s+', ' ', pyscript.strip()) pyscript = re.sub(r'\s+', ' ', pyscript.strip())
if not rhost: if not rhost:
# ignore the --python argument when running locally; we already know # ignore the --python argument when running locally; we already know
# which python version works. # which python version works.
...@@ -80,14 +84,15 @@ def connect(ssh_cmd, rhostport, python, stderr, options): ...@@ -80,14 +84,15 @@ def connect(ssh_cmd, rhostport, python, stderr, options):
else: else:
pycmd = ("P=python2; $P -V 2>/dev/null || P=python; " pycmd = ("P=python2; $P -V 2>/dev/null || P=python; "
"exec \"$P\" -c '%s'") % pyscript "exec \"$P\" -c '%s'") % pyscript
argv = (sshl + argv = (sshl +
portl + portl +
[rhost, '--', pycmd]) [rhost, '--', pycmd])
(s1,s2) = socket.socketpair() (s1, s2) = socket.socketpair()
def setup(): def setup():
# runs in the child process # runs in the child process
s2.close() s2.close()
s1a,s1b = os.dup(s1.fileno()), os.dup(s1.fileno()) s1a, s1b = os.dup(s1.fileno()), os.dup(s1.fileno())
s1.close() s1.close()
debug2('executing: %r\n' % argv) debug2('executing: %r\n' % argv)
p = ssubprocess.Popen(argv, stdin=s1a, stdout=s1b, preexec_fn=setup, p = ssubprocess.Popen(argv, stdin=s1a, stdout=s1b, preexec_fn=setup,
......
import struct, socket, errno, select import struct
import socket
import errno
import select
import os
if not globals().get('skip_imports'): if not globals().get('skip_imports'):
from helpers import * from helpers import log, debug1, debug2, debug3, Fatal
MAX_CHANNEL = 65535 MAX_CHANNEL = 65535
# these don't exist in the socket module in python 2.3! # these don't exist in the socket module in python 2.3!
SHUT_RD = 0 SHUT_RD = 0
SHUT_WR = 1 SHUT_WR = 1
...@@ -92,7 +96,10 @@ def _try_peername(sock): ...@@ -92,7 +96,10 @@ def _try_peername(sock):
_swcount = 0 _swcount = 0
class SockWrapper: class SockWrapper:
def __init__(self, rsock, wsock, connect_to=None, peername=None): def __init__(self, rsock, wsock, connect_to=None, peername=None):
global _swcount global _swcount
_swcount += 1 _swcount += 1
...@@ -177,8 +184,8 @@ class SockWrapper: ...@@ -177,8 +184,8 @@ class SockWrapper:
if not self.shut_read: if not self.shut_read:
debug2('%r: done reading\n' % self) debug2('%r: done reading\n' % self)
self.shut_read = True self.shut_read = True
#self.rsock.shutdown(SHUT_RD) # doesn't do anything anyway # self.rsock.shutdown(SHUT_RD) # doesn't do anything anyway
def nowrite(self): def nowrite(self):
if not self.shut_write: if not self.shut_write:
debug2('%r: done writing\n' % self) debug2('%r: done writing\n' % self)
...@@ -206,7 +213,7 @@ class SockWrapper: ...@@ -206,7 +213,7 @@ class SockWrapper:
# unexpected error... stream is dead # unexpected error... stream is dead
self.seterr('uwrite: %s' % e) self.seterr('uwrite: %s' % e)
return 0 return 0
def write(self, buf): def write(self, buf):
assert(buf) assert(buf)
return self.uwrite(buf) return self.uwrite(buf)
...@@ -221,7 +228,7 @@ class SockWrapper: ...@@ -221,7 +228,7 @@ class SockWrapper:
return _nb_clean(os.read, self.rsock.fileno(), 65536) return _nb_clean(os.read, self.rsock.fileno(), 65536)
except OSError, e: except OSError, e:
self.seterr('uread: %s' % e) self.seterr('uread: %s' % e)
return '' # unexpected error... we'll call it EOF return '' # unexpected error... we'll call it EOF
def fill(self): def fill(self):
if self.buf: if self.buf:
...@@ -243,7 +250,8 @@ class SockWrapper: ...@@ -243,7 +250,8 @@ class SockWrapper:
class Handler: class Handler:
def __init__(self, socks = None, callback = None):
def __init__(self, socks=None, callback=None):
self.ok = True self.ok = True
self.socks = socks or [] self.socks = socks or []
if callback: if callback:
...@@ -255,7 +263,7 @@ class Handler: ...@@ -255,7 +263,7 @@ class Handler:
def callback(self): def callback(self):
log('--no callback defined-- %r\n' % self) log('--no callback defined-- %r\n' % self)
(r,w,x) = select.select(self.socks, [], [], 0) (r, w, x) = select.select(self.socks, [], [], 0)
for s in r: for s in r:
v = s.recv(4096) v = s.recv(4096)
if not v: if not v:
...@@ -265,6 +273,7 @@ class Handler: ...@@ -265,6 +273,7 @@ class Handler:
class Proxy(Handler): class Proxy(Handler):
def __init__(self, wrap1, wrap2): def __init__(self, wrap1, wrap2):
Handler.__init__(self, [wrap1.rsock, wrap1.wsock, Handler.__init__(self, [wrap1.rsock, wrap1.wsock,
wrap2.rsock, wrap2.wsock]) wrap2.rsock, wrap2.wsock])
...@@ -272,9 +281,11 @@ class Proxy(Handler): ...@@ -272,9 +281,11 @@ class Proxy(Handler):
self.wrap2 = wrap2 self.wrap2 = wrap2
def pre_select(self, r, w, x): def pre_select(self, r, w, x):
if self.wrap1.shut_write: self.wrap2.noread() if self.wrap1.shut_write:
if self.wrap2.shut_write: self.wrap1.noread() self.wrap2.noread()
if self.wrap2.shut_write:
self.wrap1.noread()
if self.wrap1.connect_to: if self.wrap1.connect_to:
_add(w, self.wrap1.rsock) _add(w, self.wrap1.rsock)
elif self.wrap1.buf: elif self.wrap1.buf:
...@@ -305,13 +316,14 @@ class Proxy(Handler): ...@@ -305,13 +316,14 @@ class Proxy(Handler):
self.wrap2.buf = [] self.wrap2.buf = []
self.wrap2.noread() self.wrap2.noread()
if (self.wrap1.shut_read and self.wrap2.shut_read and if (self.wrap1.shut_read and self.wrap2.shut_read and
not self.wrap1.buf and not self.wrap2.buf): not self.wrap1.buf and not self.wrap2.buf):
self.ok = False self.ok = False
self.wrap1.nowrite() self.wrap1.nowrite()
self.wrap2.nowrite() self.wrap2.nowrite()
class Mux(Handler): class Mux(Handler):
def __init__(self, rsock, wsock): def __init__(self, rsock, wsock):
Handler.__init__(self, [rsock, wsock]) Handler.__init__(self, [rsock, wsock])
self.rsock = rsock self.rsock = rsock
...@@ -342,31 +354,31 @@ class Mux(Handler): ...@@ -342,31 +354,31 @@ class Mux(Handler):
for b in self.outbuf: for b in self.outbuf:
total += len(b) total += len(b)
return total return total
def check_fullness(self): def check_fullness(self):
if self.fullness > 32768: if self.fullness > 32768:
if not self.too_full: if not self.too_full:
self.send(0, CMD_PING, 'rttest') self.send(0, CMD_PING, 'rttest')
self.too_full = True self.too_full = True
#ob = [] #ob = []
#for b in self.outbuf: # for b in self.outbuf:
# (s1,s2,c) = struct.unpack('!ccH', b[:4]) # (s1,s2,c) = struct.unpack('!ccH', b[:4])
# ob.append(c) # ob.append(c)
#log('outbuf: %d %r\n' % (self.amount_queued(), ob)) #log('outbuf: %d %r\n' % (self.amount_queued(), ob))
def send(self, channel, cmd, data): def send(self, channel, cmd, data):
data = str(data) data = str(data)
assert(len(data) <= 65535) assert(len(data) <= 65535)
p = struct.pack('!ccHHH', 'S', 'S', channel, cmd, len(data)) + data p = struct.pack('!ccHHH', 'S', 'S', channel, cmd, len(data)) + data
self.outbuf.append(p) self.outbuf.append(p)
debug2(' > channel=%d cmd=%s len=%d (fullness=%d)\n' debug2(' > channel=%d cmd=%s len=%d (fullness=%d)\n'
% (channel, cmd_to_name.get(cmd,hex(cmd)), % (channel, cmd_to_name.get(cmd, hex(cmd)),
len(data), self.fullness)) len(data), self.fullness))
self.fullness += len(data) self.fullness += len(data)
def got_packet(self, channel, cmd, data): def got_packet(self, channel, cmd, data):
debug2('< channel=%d cmd=%s len=%d\n' debug2('< channel=%d cmd=%s len=%d\n'
% (channel, cmd_to_name.get(cmd,hex(cmd)), len(data))) % (channel, cmd_to_name.get(cmd, hex(cmd)), len(data)))
if cmd == CMD_PING: if cmd == CMD_PING:
self.send(0, CMD_PONG, data) self.send(0, CMD_PONG, data)
elif cmd == CMD_PONG: elif cmd == CMD_PONG:
...@@ -405,8 +417,8 @@ class Mux(Handler): ...@@ -405,8 +417,8 @@ class Mux(Handler):
else: else:
callback = self.channels.get(channel) callback = self.channels.get(channel)
if not callback: if not callback:
log('warning: closed channel %d got cmd=%s len=%d\n' log('warning: closed channel %d got cmd=%s len=%d\n'
% (channel, cmd_to_name.get(cmd,hex(cmd)), len(data))) % (channel, cmd_to_name.get(cmd, hex(cmd)), len(data)))
else: else:
callback(cmd, data) callback(cmd, data)
...@@ -427,18 +439,18 @@ class Mux(Handler): ...@@ -427,18 +439,18 @@ class Mux(Handler):
except OSError, e: except OSError, e:
raise Fatal('other end: %r' % e) raise Fatal('other end: %r' % e)
#log('<<< %r\n' % b) #log('<<< %r\n' % b)
if b == '': # EOF if b == '': # EOF
self.ok = False self.ok = False
if b: if b:
self.inbuf += b self.inbuf += b
def handle(self): def handle(self):
self.fill() self.fill()
#log('inbuf is: (%d,%d) %r\n' # log('inbuf is: (%d,%d) %r\n'
# % (self.want, len(self.inbuf), self.inbuf)) # % (self.want, len(self.inbuf), self.inbuf))
while 1: while 1:
if len(self.inbuf) >= (self.want or HDR_LEN): if len(self.inbuf) >= (self.want or HDR_LEN):
(s1,s2,channel,cmd,datalen) = \ (s1, s2, channel, cmd, datalen) = \
struct.unpack('!ccHHH', self.inbuf[:HDR_LEN]) struct.unpack('!ccHHH', self.inbuf[:HDR_LEN])
assert(s1 == 'S') assert(s1 == 'S')
assert(s2 == 'S') assert(s2 == 'S')
...@@ -457,7 +469,7 @@ class Mux(Handler): ...@@ -457,7 +469,7 @@ class Mux(Handler):
_add(w, self.wsock) _add(w, self.wsock)
def callback(self): def callback(self):
(r,w,x) = select.select([self.rsock], [self.wsock], [], 0) (r, w, x) = select.select([self.rsock], [self.wsock], [], 0)
if self.rsock in r: if self.rsock in r:
self.handle() self.handle()
if self.outbuf and self.wsock in w: if self.outbuf and self.wsock in w:
...@@ -465,6 +477,7 @@ class Mux(Handler): ...@@ -465,6 +477,7 @@ class Mux(Handler):
class MuxWrapper(SockWrapper): class MuxWrapper(SockWrapper):
def __init__(self, mux, channel): def __init__(self, mux, channel):
SockWrapper.__init__(self, mux.rsock, mux.wsock) SockWrapper.__init__(self, mux.rsock, mux.wsock)
self.mux = mux self.mux = mux
...@@ -478,7 +491,7 @@ class MuxWrapper(SockWrapper): ...@@ -478,7 +491,7 @@ class MuxWrapper(SockWrapper):
SockWrapper.__del__(self) SockWrapper.__del__(self)
def __repr__(self): def __repr__(self):
return 'SW%r:Mux#%d' % (self.peername,self.channel) return 'SW%r:Mux#%d' % (self.peername, self.channel)
def noread(self): def noread(self):
if not self.shut_read: if not self.shut_read:
...@@ -511,7 +524,7 @@ class MuxWrapper(SockWrapper): ...@@ -511,7 +524,7 @@ class MuxWrapper(SockWrapper):
def uread(self): def uread(self):
if self.shut_read: if self.shut_read:
return '' # EOF return '' # EOF
else: else:
return None # no data available right now return None # no data available right now
...@@ -523,7 +536,7 @@ class MuxWrapper(SockWrapper): ...@@ -523,7 +536,7 @@ class MuxWrapper(SockWrapper):
elif cmd == CMD_TCP_DATA: elif cmd == CMD_TCP_DATA:
self.buf.append(data) self.buf.append(data)
else: else:
raise Exception('unknown command %d (%d bytes)' raise Exception('unknown command %d (%d bytes)'
% (cmd, len(data))) % (cmd, len(data)))
...@@ -532,8 +545,8 @@ def connect_dst(family, ip, port): ...@@ -532,8 +545,8 @@ def connect_dst(family, ip, port):
outsock = socket.socket(family) outsock = socket.socket(family)
outsock.setsockopt(socket.SOL_IP, socket.IP_TTL, 42) outsock.setsockopt(socket.SOL_IP, socket.IP_TTL, 42)
return SockWrapper(outsock, outsock, return SockWrapper(outsock, outsock,
connect_to = (ip,port), connect_to=(ip, port),
peername = '%s:%d' % (ip,port)) peername = '%s:%d' % (ip, port))
def runonce(handlers, mux): def runonce(handlers, mux):
...@@ -545,14 +558,14 @@ def runonce(handlers, mux): ...@@ -545,14 +558,14 @@ def runonce(handlers, mux):
handlers.remove(h) handlers.remove(h)
for s in handlers: for s in handlers:
s.pre_select(r,w,x) s.pre_select(r, w, x)
debug2('Waiting: %d r=%r w=%r x=%r (fullness=%d/%d)\n' debug2('Waiting: %d r=%r w=%r x=%r (fullness=%d/%d)\n'
% (len(handlers), _fds(r), _fds(w), _fds(x), % (len(handlers), _fds(r), _fds(w), _fds(x),
mux.fullness, mux.too_full)) mux.fullness, mux.too_full))
(r,w,x) = select.select(r,w,x) (r, w, x) = select.select(r, w, x)
debug2(' Ready: %d r=%r w=%r x=%r\n' debug2(' Ready: %d r=%r w=%r x=%r\n'
% (len(handlers), _fds(r), _fds(w), _fds(x))) % (len(handlers), _fds(r), _fds(w), _fds(x)))
ready = r+w+x ready = r + w + x
did = {} did = {}
for h in handlers: for h in handlers:
for s in h.socks: for s in h.socks:
......
import sys, os import sys
import os
from compat import ssubprocess from compat import ssubprocess
_p = None _p = None
def start_syslog(): def start_syslog():
global _p global _p
_p = ssubprocess.Popen(['logger', _p = ssubprocess.Popen(['logger',
......
#!/usr/bin/env python #!/usr/bin/env python
import sys, os, socket, select, struct, time import socket
import select
import struct
import time
listener = socket.socket() listener = socket.socket()
listener.bind(('127.0.0.1', 0)) listener.bind(('127.0.0.1', 0))
...@@ -23,7 +26,7 @@ while 1: ...@@ -23,7 +26,7 @@ while 1:
if count >= 16384: if count >= 16384:
count = 1 count = 1
print 'cli CREATING %d' % count print 'cli CREATING %d' % count
b = struct.pack('I', count) + 'x'*count b = struct.pack('I', count) + 'x' * count
remain[c] = count remain[c] = count
print 'cli >> %r' % len(b) print 'cli >> %r' % len(b)
c.send(b) c.send(b)
...@@ -32,13 +35,13 @@ while 1: ...@@ -32,13 +35,13 @@ while 1:
r = [listener] r = [listener]
time.sleep(0.1) time.sleep(0.1)
else: else:
r = [listener]+servers+clients r = [listener] + servers + clients
print 'select(%d)' % len(r) print 'select(%d)' % len(r)
r,w,x = select.select(r, [], [], 5) r, w, x = select.select(r, [], [], 5)
assert(r) assert(r)
for i in r: for i in r:
if i == listener: if i == listener:
s,addr = listener.accept() s, addr = listener.accept()
servers.append(s) servers.append(s)
elif i in servers: elif i in servers:
b = i.recv(4096) b = i.recv(4096)
...@@ -47,7 +50,7 @@ while 1: ...@@ -47,7 +50,7 @@ while 1:
assert(len(b) >= 4) assert(len(b) >= 4)
want = struct.unpack('I', b[:4])[0] want = struct.unpack('I', b[:4])[0]
b = b[4:] b = b[4:]
#i.send('y'*want) # i.send('y'*want)
else: else:
want = remain[i] want = remain[i]
if want < len(b): if want < len(b):
...@@ -64,7 +67,7 @@ while 1: ...@@ -64,7 +67,7 @@ while 1:
del remain[i] del remain[i]
else: else:
print 'srv >> %r' % len(b) print 'srv >> %r' % len(b)
i.send('y'*len(b)) i.send('y' * len(b))
if not want: if not want:
i.shutdown(socket.SHUT_WR) i.shutdown(socket.SHUT_WR)
elif i in clients: elif i in clients:
......
import sys, os, re, subprocess import re
import subprocess
def askpass(prompt): def askpass(prompt):
prompt = prompt.replace('"', "'") prompt = prompt.replace('"', "'")
...@@ -6,7 +8,7 @@ def askpass(prompt): ...@@ -6,7 +8,7 @@ def askpass(prompt):
if 'yes/no' in prompt: if 'yes/no' in prompt:
return "yes" return "yes"
script=""" script = """
tell application "Finder" tell application "Finder"
activate activate
display dialog "%s" \ display dialog "%s" \
......
import sys, os, pty import sys
import os
import pty
from AppKit import * from AppKit import *
import my, models, askpass import my
import models
import askpass
def sshuttle_args(host, auto_nets, auto_hosts, dns, nets, debug, def sshuttle_args(host, auto_nets, auto_hosts, dns, nets, debug,
no_latency_control): no_latency_control):
...@@ -21,21 +26,25 @@ def sshuttle_args(host, auto_nets, auto_hosts, dns, nets, debug, ...@@ -21,21 +26,25 @@ def sshuttle_args(host, auto_nets, auto_hosts, dns, nets, debug,
class _Callback(NSObject): class _Callback(NSObject):
def initWithFunc_(self, func): def initWithFunc_(self, func):
self = super(_Callback, self).init() self = super(_Callback, self).init()
self.func = func self.func = func
return self return self
def func_(self, obj): def func_(self, obj):
return self.func(obj) return self.func(obj)
class Callback: class Callback:
def __init__(self, func): def __init__(self, func):
self.obj = _Callback.alloc().initWithFunc_(func) self.obj = _Callback.alloc().initWithFunc_(func)
self.sel = self.obj.func_ self.sel = self.obj.func_
class Runner: class Runner:
def __init__(self, argv, logfunc, promptfunc, serverobj): def __init__(self, argv, logfunc, promptfunc, serverobj):
print 'in __init__' print 'in __init__'
self.id = argv self.id = argv
...@@ -49,7 +58,7 @@ class Runner: ...@@ -49,7 +58,7 @@ class Runner:
self.logfunc('\nConnecting to %s.\n' % self.serverobj.host()) self.logfunc('\nConnecting to %s.\n' % self.serverobj.host())
print 'will run: %r' % argv print 'will run: %r' % argv
self.serverobj.setConnected_(False) self.serverobj.setConnected_(False)
pid,fd = pty.fork() pid, fd = pty.fork()
if pid == 0: if pid == 0:
# child # child
try: try:
...@@ -62,19 +71,20 @@ class Runner: ...@@ -62,19 +71,20 @@ class Runner:
# parent # parent
self.pid = pid self.pid = pid
self.file = NSFileHandle.alloc()\ self.file = NSFileHandle.alloc()\
.initWithFileDescriptor_closeOnDealloc_(fd, True) .initWithFileDescriptor_closeOnDealloc_(fd, True)
self.cb = Callback(self.gotdata) self.cb = Callback(self.gotdata)
NSNotificationCenter.defaultCenter()\ NSNotificationCenter.defaultCenter()\
.addObserver_selector_name_object_(self.cb.obj, self.cb.sel, .addObserver_selector_name_object_(
NSFileHandleDataAvailableNotification, self.file) self.cb.obj, self.cb.sel,
NSFileHandleDataAvailableNotification, self.file)
self.file.waitForDataInBackgroundAndNotify() self.file.waitForDataInBackgroundAndNotify()
def __del__(self): def __del__(self):
self.wait() self.wait()
def _try_wait(self, options): def _try_wait(self, options):
if self.rv == None and self.pid > 0: if self.rv is None and self.pid > 0:
pid,code = os.waitpid(self.pid, options) pid, code = os.waitpid(self.pid, options)
if pid == self.pid: if pid == self.pid:
if os.WIFEXITED(code): if os.WIFEXITED(code):
self.rv = os.WEXITSTATUS(code) self.rv = os.WEXITSTATUS(code)
...@@ -88,14 +98,14 @@ class Runner: ...@@ -88,14 +98,14 @@ class Runner:
def wait(self): def wait(self):
return self._try_wait(0) return self._try_wait(0)
def poll(self): def poll(self):
return self._try_wait(os.WNOHANG) return self._try_wait(os.WNOHANG)
def kill(self): def kill(self):
assert(self.pid > 0) assert(self.pid > 0)
print 'killing: pid=%r rv=%r' % (self.pid, self.rv) print 'killing: pid=%r rv=%r' % (self.pid, self.rv)
if self.rv == None: if self.rv is None:
self.logfunc('Disconnecting from %s.\n' % self.serverobj.host()) self.logfunc('Disconnecting from %s.\n' % self.serverobj.host())
os.kill(self.pid, 15) os.kill(self.pid, 15)
self.wait() self.wait()
...@@ -118,12 +128,13 @@ class Runner: ...@@ -118,12 +128,13 @@ class Runner:
self.file.writeData_(my.Data(resp + '\n')) self.file.writeData_(my.Data(resp + '\n'))
self.file.waitForDataInBackgroundAndNotify() self.file.waitForDataInBackgroundAndNotify()
self.poll() self.poll()
#print 'gotdata done!' # print 'gotdata done!'
class SshuttleApp(NSObject): class SshuttleApp(NSObject):
def initialize(self): def initialize(self):
d = my.PList('UserDefaults') d = my.PList('UserDefaults')
my.Defaults().registerDefaults_(d) my.Defaults().registerDefaults_(d)
...@@ -137,7 +148,7 @@ class SshuttleController(NSObject): ...@@ -137,7 +148,7 @@ class SshuttleController(NSObject):
serversController = objc.IBOutlet() serversController = objc.IBOutlet()
logField = objc.IBOutlet() logField = objc.IBOutlet()
latencyControlField = objc.IBOutlet() latencyControlField = objc.IBOutlet()
servers = [] servers = []
conns = {} conns = {}
...@@ -145,12 +156,14 @@ class SshuttleController(NSObject): ...@@ -145,12 +156,14 @@ class SshuttleController(NSObject):
host = server.host() host = server.host()
print 'connecting %r' % host print 'connecting %r' % host
self.fill_menu() self.fill_menu()
def logfunc(msg): def logfunc(msg):
print 'log! (%d bytes)' % len(msg) print 'log! (%d bytes)' % len(msg)
self.logField.textStorage()\ self.logField.textStorage()\
.appendAttributedString_(NSAttributedString.alloc()\ .appendAttributedString_(NSAttributedString.alloc()
.initWithString_(msg)) .initWithString_(msg))
self.logField.didChangeText() self.logField.didChangeText()
def promptfunc(prompt): def promptfunc(prompt):
print 'prompt! %r' % prompt print 'prompt! %r' % prompt
return askpass.askpass(prompt) return askpass.askpass(prompt)
...@@ -164,12 +177,12 @@ class SshuttleController(NSObject): ...@@ -164,12 +177,12 @@ class SshuttleController(NSObject):
manual_nets = [] manual_nets = []
noLatencyControl = (server.latencyControl() != models.LAT_INTERACTIVE) noLatencyControl = (server.latencyControl() != models.LAT_INTERACTIVE)
conn = Runner(sshuttle_args(host, conn = Runner(sshuttle_args(host,
auto_nets = nets_mode == models.NET_AUTO, auto_nets=nets_mode == models.NET_AUTO,
auto_hosts = server.autoHosts(), auto_hosts=server.autoHosts(),
dns = server.useDns(), dns=server.useDns(),
nets = manual_nets, nets=manual_nets,
debug = self.debugField.state(), debug=self.debugField.state(),
no_latency_control = noLatencyControl), no_latency_control=noLatencyControl),
logfunc=logfunc, promptfunc=promptfunc, logfunc=logfunc, promptfunc=promptfunc,
serverobj=server) serverobj=server)
self.conns[host] = conn self.conns[host] = conn
...@@ -182,8 +195,8 @@ class SshuttleController(NSObject): ...@@ -182,8 +195,8 @@ class SshuttleController(NSObject):
conn.kill() conn.kill()
self.fill_menu() self.fill_menu()
self.logField.textStorage().setAttributedString_( self.logField.textStorage().setAttributedString_(
NSAttributedString.alloc().initWithString_('')) NSAttributedString.alloc().initWithString_(''))
@objc.IBAction @objc.IBAction
def cmd_connect(self, sender): def cmd_connect(self, sender):
server = sender.representedObject() server = sender.representedObject()
...@@ -213,6 +226,7 @@ class SshuttleController(NSObject): ...@@ -213,6 +226,7 @@ class SshuttleController(NSObject):
it.setRepresentedObject_(obj) it.setRepresentedObject_(obj)
it.setTarget_(self) it.setTarget_(self)
it.setAction_(func) it.setAction_(func)
def addnote(name): def addnote(name):
additem(name, None, None) additem(name, None, None)
...@@ -271,8 +285,9 @@ class SshuttleController(NSObject): ...@@ -271,8 +285,9 @@ class SshuttleController(NSObject):
sl = [] sl = []
for s in l: for s in l:
host = s.get('host', None) host = s.get('host', None)
if not host: continue if not host:
continue
nets = s.get('nets', []) nets = s.get('nets', [])
nl = [] nl = []
for n in nets: for n in nets:
...@@ -282,7 +297,7 @@ class SshuttleController(NSObject): ...@@ -282,7 +297,7 @@ class SshuttleController(NSObject):
net.setSubnet_(subnet) net.setSubnet_(subnet)
net.setWidth_(width) net.setWidth_(width)
nl.append(net) nl.append(net)
autoNets = s.get('autoNets', models.NET_AUTO) autoNets = s.get('autoNets', models.NET_AUTO)
autoHosts = s.get('autoHosts', True) autoHosts = s.get('autoHosts', True)
useDns = s.get('useDns', autoNets == models.NET_ALL) useDns = s.get('useDns', autoNets == models.NET_ALL)
...@@ -302,11 +317,13 @@ class SshuttleController(NSObject): ...@@ -302,11 +317,13 @@ class SshuttleController(NSObject):
l = [] l = []
for s in self.servers: for s in self.servers:
host = s.host() host = s.host()
if not host: continue if not host:
continue
nets = [] nets = []
for n in s.nets(): for n in s.nets():
subnet = n.subnet() subnet = n.subnet()
if not subnet: continue if not subnet:
continue
nets.append((subnet, n.width())) nets.append((subnet, n.width()))
d = dict(host=s.host(), d = dict(host=s.host(),
nets=nets, nets=nets,
...@@ -352,9 +369,9 @@ class SshuttleController(NSObject): ...@@ -352,9 +369,9 @@ class SshuttleController(NSObject):
statusitem.setHighlightMode_(True) statusitem.setHighlightMode_(True)
statusitem.setMenu_(self.menu) statusitem.setMenu_(self.menu)
self.fill_menu() self.fill_menu()
models.configchange_callback = my.DelayedCallback(self.save_servers) models.configchange_callback = my.DelayedCallback(self.save_servers)
def sc(server): def sc(server):
if server.wantConnect(): if server.wantConnect():
self._connect(server) self._connect(server)
......
...@@ -35,24 +35,29 @@ def _validate_width(v): ...@@ -35,24 +35,29 @@ def _validate_width(v):
class SshuttleNet(NSObject): class SshuttleNet(NSObject):
def subnet(self): def subnet(self):
return getattr(self, '_k_subnet', None) return getattr(self, '_k_subnet', None)
def setSubnet_(self, v): def setSubnet_(self, v):
self._k_subnet = v self._k_subnet = v
config_changed() config_changed()
@objc_validator @objc_validator
def validateSubnet_error_(self, value, error): def validateSubnet_error_(self, value, error):
#print 'validateSubnet!' # print 'validateSubnet!'
return True, _validate_ip(value), error return True, _validate_ip(value), error
def width(self): def width(self):
return getattr(self, '_k_width', 24) return getattr(self, '_k_width', 24)
def setWidth_(self, v): def setWidth_(self, v):
self._k_width = v self._k_width = v
config_changed() config_changed()
@objc_validator @objc_validator
def validateWidth_error_(self, value, error): def validateWidth_error_(self, value, error):
#print 'validateWidth!' # print 'validateWidth!'
return True, _validate_width(value), error return True, _validate_width(value), error
NET_ALL = 0 NET_ALL = 0
...@@ -62,30 +67,37 @@ NET_MANUAL = 2 ...@@ -62,30 +67,37 @@ NET_MANUAL = 2
LAT_BANDWIDTH = 0 LAT_BANDWIDTH = 0
LAT_INTERACTIVE = 1 LAT_INTERACTIVE = 1
class SshuttleServer(NSObject): class SshuttleServer(NSObject):
def init(self): def init(self):
self = super(SshuttleServer, self).init() self = super(SshuttleServer, self).init()
config_changed() config_changed()
return self return self
def wantConnect(self): def wantConnect(self):
return getattr(self, '_k_wantconnect', False) return getattr(self, '_k_wantconnect', False)
def setWantConnect_(self, v): def setWantConnect_(self, v):
self._k_wantconnect = v self._k_wantconnect = v
self.setError_(None) self.setError_(None)
config_changed() config_changed()
if setconnect_callback: setconnect_callback(self) if setconnect_callback:
setconnect_callback(self)
def connected(self): def connected(self):
return getattr(self, '_k_connected', False) return getattr(self, '_k_connected', False)
def setConnected_(self, v): def setConnected_(self, v):
print 'setConnected of %r to %r' % (self, v) print 'setConnected of %r to %r' % (self, v)
self._k_connected = v self._k_connected = v
if v: self.setError_(None) # connected ok, so no error if v:
self.setError_(None) # connected ok, so no error
config_changed() config_changed()
def error(self): def error(self):
return getattr(self, '_k_error', None) return getattr(self, '_k_error', None)
def setError_(self, v): def setError_(self, v):
self._k_error = v self._k_error = v
config_changed() config_changed()
...@@ -107,40 +119,47 @@ class SshuttleServer(NSObject): ...@@ -107,40 +119,47 @@ class SshuttleServer(NSObject):
suffix = " (all traffic)" suffix = " (all traffic)"
elif an == NET_MANUAL: elif an == NET_MANUAL:
n = self.nets() n = self.nets()
suffix = ' (%d subnet%s)' % (len(n), len(n)!=1 and 's' or '') suffix = ' (%d subnet%s)' % (len(n), len(n) != 1 and 's' or '')
return self.host() + suffix return self.host() + suffix
def setTitle_(self, v): def setTitle_(self, v):
# title is always auto-generated # title is always auto-generated
config_changed() config_changed()
def host(self): def host(self):
return getattr(self, '_k_host', None) return getattr(self, '_k_host', None)
def setHost_(self, v): def setHost_(self, v):
self._k_host = v self._k_host = v
self.setTitle_(None) self.setTitle_(None)
config_changed() config_changed()
@objc_validator @objc_validator
def validateHost_error_(self, value, error): def validateHost_error_(self, value, error):
#print 'validatehost! %r %r %r' % (self, value, error) # print 'validatehost! %r %r %r' % (self, value, error)
while value.startswith('-'): while value.startswith('-'):
value = value[1:] value = value[1:]
return True, value, error return True, value, error
def nets(self): def nets(self):
return getattr(self, '_k_nets', []) return getattr(self, '_k_nets', [])
def setNets_(self, v): def setNets_(self, v):
self._k_nets = v self._k_nets = v
self.setTitle_(None) self.setTitle_(None)
config_changed() config_changed()
def netsHidden(self): def netsHidden(self):
#print 'checking netsHidden' # print 'checking netsHidden'
return self.autoNets() != NET_MANUAL return self.autoNets() != NET_MANUAL
def setNetsHidden_(self, v): def setNetsHidden_(self, v):
config_changed() config_changed()
#print 'setting netsHidden to %r' % v # print 'setting netsHidden to %r' % v
def autoNets(self): def autoNets(self):
return getattr(self, '_k_autoNets', NET_AUTO) return getattr(self, '_k_autoNets', NET_AUTO)
def setAutoNets_(self, v): def setAutoNets_(self, v):
self._k_autoNets = v self._k_autoNets = v
self.setNetsHidden_(-1) self.setNetsHidden_(-1)
...@@ -150,18 +169,21 @@ class SshuttleServer(NSObject): ...@@ -150,18 +169,21 @@ class SshuttleServer(NSObject):
def autoHosts(self): def autoHosts(self):
return getattr(self, '_k_autoHosts', True) return getattr(self, '_k_autoHosts', True)
def setAutoHosts_(self, v): def setAutoHosts_(self, v):
self._k_autoHosts = v self._k_autoHosts = v
config_changed() config_changed()
def useDns(self): def useDns(self):
return getattr(self, '_k_useDns', False) return getattr(self, '_k_useDns', False)
def setUseDns_(self, v): def setUseDns_(self, v):
self._k_useDns = v self._k_useDns = v
config_changed() config_changed()
def latencyControl(self): def latencyControl(self):
return getattr(self, '_k_latencyControl', LAT_INTERACTIVE) return getattr(self, '_k_latencyControl', LAT_INTERACTIVE)
def setLatencyControl_(self, v): def setLatencyControl_(self, v):
self._k_latencyControl = v self._k_latencyControl = v
config_changed() config_changed()
import sys, os import os
from AppKit import * from AppKit import *
import PyObjCTools.AppHelper import PyObjCTools.AppHelper
...@@ -44,11 +44,13 @@ def Defaults(): ...@@ -44,11 +44,13 @@ def Defaults():
# #
def DelayedCallback(func, *args, **kwargs): def DelayedCallback(func, *args, **kwargs):
flag = [0] flag = [0]
def _go(): def _go():
if flag[0]: if flag[0]:
print 'running %r (flag=%r)' % (func, flag) print 'running %r (flag=%r)' % (func, flag)
flag[0] = 0 flag[0] = 0
func(*args, **kwargs) func(*args, **kwargs)
def call(): def call():
flag[0] += 1 flag[0] += 1
PyObjCTools.AppHelper.callAfter(_go) PyObjCTools.AppHelper.callAfter(_go)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment