Compare commits

...

17 Commits

Author SHA1 Message Date
Tianyi Cui
29d2e06bf5 Added --exclude-from feature.
(Slightly modified by apenwarr)
2012-07-06 15:13:30 -04:00
Miguel Landaeta
bff1610050 Document missing --dns option in sshuttle manpage 2012-07-06 15:06:07 -04:00
Avery Pennarun
cce6a9d96d firewall.py: catch SIGINT and SIGTERM too.
There were still a few conditions under some OSes that would cause
firewall.py to terminate without cleaning up the firewall settings.  'pkill
sshuttle' was one of them.  Ignore a couple more signals to further ensure a
correct cleanup.

(This only affects sshuttle --firewall, which is a subprocess of the main
sshuttle process.  The firewall is supposed to exit automatically whenever
the client exits, and so far that part seems to work reliably.)
2012-07-06 15:00:28 -04:00
Avery Pennarun
5743f29ed6 server.py: slightly rearrange previous commit.
Add some documentation about the int() vs long() and the reason behind
_shl().  Instead of "from __future__ import generators", just don't use
generators.
2012-07-06 15:00:28 -04:00
Saul
42bc6d62db Two small changes to server.py that allow it to run on python2.2 2012-04-19 23:00:29 -07:00
Avery Pennarun
274ee854d4 clean.do: don't forget to do version/clean. 2012-02-07 12:17:56 -05:00
David Held
12f6a52ec6 Fix runpython.do for systems with unxpected configurations.
If the expected arch directory doesn't exist, give up and don't specify arch at
all. Currently it expands to '*' which fails.

[slightly modified by apenwarr]
2012-02-07 12:16:31 -05:00
Avery Pennarun
e737f4b944 firewall.py: add comments about sysctl problems. 2012-01-08 19:13:39 -05:00
Avery Pennarun
d9f761a8a3 ui-macos: tell the user that we need to reboot on MacOS Lion. 2012-01-08 19:01:18 -05:00
Avery Pennarun
bd20841782 firewall.py: clean up repeated calls to ssubprocess.call().
And make sshuttle exit with a well-defined exit code (111) if it needs to
reboot.
2012-01-08 19:01:18 -05:00
Avery Pennarun
4c1a505e37 firewall.py: workaround MacOS 10.7 Lion bug.
On top of the bug that already existed in 10.6, Lion also makes the sysctl
needed to fix the problem into a read-only variable, so we have to actually
change it at kernel boot time and force people to reboot.  Nice job, Apple.
2012-01-08 19:01:18 -05:00
Avery Pennarun
41d1f73dc2 Add a --version (-V) option.
Now that we imported the feature from redo, might as well use it.
2012-01-06 13:45:43 -05:00
Avery Pennarun
cbc32ff8d8 Import the non-pandoc manpage generator from redo.
This makes it easier (possible?) to generate sshuttle.8 from sshuttle.md on
MacOS.  We also import the git-enhanced version numbering magic so the
generated manpage can have a real version number.
2012-01-06 13:35:12 -05:00
Jimmy Tang
6698992f4f Use the new arguments from redo v0.10.
(apenwarr: also updates to the matching, latest minimal/do)
2012-01-06 13:24:56 -05:00
Avery Pennarun
e2c682084c firewall: catch SIGHUP and SIGPIPE.
Not sure if this will fix anything, but it might stop the problem reported
on some MacOS versions where the firewall doesn't get cleaned up correctly.
2012-01-06 13:14:55 -05:00
Avery Pennarun
89e914e9d1 ui-macos/main.py: fix wait() to avoid deadlock.
If the subprocess was trying to write to its stdout/stderr, its process
would never actually finish because it was blocked waiting for us to read
it, but we were blocked on waitpid().  Instead, use waitpid(WNOHANG) and
continually read from the subprocess (which should be a blocking operation)
until it exits.
2012-01-02 18:46:30 -05:00
Avery Pennarun
2268e76771 ipfw: don't use 'log' parameter.
I guess we were causing the kernel to syslog on every single packet on
MacOS.  Oops.
2012-01-02 18:19:19 -05:00
33 changed files with 560 additions and 56 deletions

3
Documentation/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.8
/md-to-man
/*.md.tmp

5
Documentation/all.do Normal file
View File

@ -0,0 +1,5 @@
/bin/ls *.md |
sed 's/\.md/.8/' |
xargs redo-ifchange
redo-always

1
Documentation/clean.do Normal file
View File

@ -0,0 +1 @@
rm -f *~ .*~ *.8 t/*.8 md-to-man *.tmp t/*.tmp

View File

@ -0,0 +1,2 @@
redo-ifchange md-to-man $2.md.tmp
. ./md-to-man $1 $2 $3

View File

@ -0,0 +1,3 @@
redo-ifchange ../version/vars $2.md
. ../version/vars
sed -e "s/%VERSION%/$TAG/" -e "s/%DATE%/$DATE/" $2.md

View File

@ -0,0 +1,8 @@
redo-ifchange md2man.py
if ./md2man.py </dev/null >/dev/null; then
echo './md2man.py $2.md.tmp'
else
echo "Warning: md2man.py missing modules; can't generate manpages." >&2
echo "Warning: try this: sudo easy_install markdown BeautifulSoup" >&2
echo 'echo Skipping: $2.1 >&2'
fi

278
Documentation/md2man.py Executable file
View File

@ -0,0 +1,278 @@
#!/usr/bin/env python
import sys, os, markdown, re
from BeautifulSoup import BeautifulSoup
def _split_lines(s):
return re.findall(r'([^\n]*\n?)', s)
class Writer:
def __init__(self):
self.started = False
self.indent = 0
self.last_wrote = '\n'
def _write(self, s):
if s:
self.last_wrote = s
sys.stdout.write(s)
def writeln(self, s):
if s:
self.linebreak()
self._write('%s\n' % s)
def write(self, s):
if s:
self.para()
for line in _split_lines(s):
if line.startswith('.'):
self._write('\\&' + line)
else:
self._write(line)
def linebreak(self):
if not self.last_wrote.endswith('\n'):
self._write('\n')
def para(self, bullet=None):
if not self.started:
if not bullet:
bullet = ' '
if not self.indent:
self.writeln(_macro('.PP'))
else:
assert(self.indent >= 2)
prefix = ' '*(self.indent-2) + bullet + ' '
self.writeln('.IP "%s" %d' % (prefix, self.indent))
self.started = True
def end_para(self):
self.linebreak()
self.started = False
def start_bullet(self):
self.indent += 3
self.para(bullet='\\[bu]')
def end_bullet(self):
self.indent -= 3
self.end_para()
w = Writer()
def _macro(name, *args):
if not name.startswith('.'):
raise ValueError('macro names must start with "."')
fixargs = []
for i in args:
i = str(i)
i = i.replace('\\', '')
i = i.replace('"', "'")
if (' ' in i) or not i:
i = '"%s"' % i
fixargs.append(i)
return ' '.join([name] + list(fixargs))
def macro(name, *args):
w.writeln(_macro(name, *args))
def _force_string(owner, tag):
if tag.string:
return tag.string
else:
out = ''
for i in tag:
if not (i.string or i.name in ['a', 'br']):
raise ValueError('"%s" tags must contain only strings: '
'got %r: %r' % (owner.name, tag.name, tag))
out += _force_string(owner, i)
return out
def _clean(s):
s = s.replace('\\', '\\\\')
return s
def _bitlist(tag):
if getattr(tag, 'contents', None) == None:
for i in _split_lines(str(tag)):
yield None,_clean(i)
else:
for e in tag:
name = getattr(e, 'name', None)
if name in ['a', 'br']:
name = None # just treat as simple text
s = _force_string(tag, e)
if name:
yield name,_clean(s)
else:
for i in _split_lines(s):
yield None,_clean(i)
def _bitlist_simple(tag):
for typ,text in _bitlist(tag):
if typ and not typ in ['em', 'strong', 'code']:
raise ValueError('unexpected tag %r inside %r' % (typ, tag.name))
yield text
def _text(bitlist):
out = ''
for typ,text in bitlist:
if not typ:
out += text
elif typ == 'em':
out += '\\fI%s\\fR' % text
elif typ in ['strong', 'code']:
out += '\\fB%s\\fR' % text
else:
raise ValueError('unexpected tag %r inside %r' % (typ, tag.name))
out = out.strip()
out = re.sub(re.compile(r'^\s+', re.M), '', out)
return out
def text(tag):
w.write(_text(_bitlist(tag)))
# This is needed because .BI (and .BR, .RB, etc) are weird little state
# machines that alternate between two fonts. So if someone says something
# like foo<b>chicken</b><b>wicken</b>dicken we have to convert that to
# .BI foo chickenwicken dicken
def _boldline(l):
out = ['']
last_bold = False
for typ,text in l:
nonzero = not not typ
if nonzero != last_bold:
last_bold = not last_bold
out.append('')
out[-1] += re.sub(r'\s+', ' ', text)
macro('.BI', *out)
def do_definition(tag):
w.end_para()
macro('.TP')
w.started = True
split = 0
pre = []
post = []
for typ,text in _bitlist(tag):
if split:
post.append((typ,text))
elif text.lstrip().startswith(': '):
split = 1
post.append((typ,text.lstrip()[2:].lstrip()))
else:
pre.append((typ,text))
_boldline(pre)
w.write(_text(post))
def do_list(tag):
for i in tag:
name = getattr(i, 'name', '').lower()
if not name and not str(i).strip():
pass
elif name != 'li':
raise ValueError('only <li> is allowed inside <ul>: got %r' % i)
else:
w.start_bullet()
for xi in i:
do(xi)
w.end_para()
w.end_bullet()
def do(tag):
name = getattr(tag, 'name', '').lower()
if not name:
text(tag)
elif name == 'h1':
macro('.SH', _force_string(tag, tag).upper())
w.started = True
elif name == 'h2':
macro('.SS', _force_string(tag, tag))
w.started = True
elif name.startswith('h') and len(name)==2:
raise ValueError('%r invalid - man page headers must be h1 or h2'
% name)
elif name == 'pre':
t = _force_string(tag.code, tag.code)
if t.strip():
macro('.RS', '+4n')
macro('.nf')
w.write(_clean(t).rstrip())
macro('.fi')
macro('.RE')
w.end_para()
elif name == 'p' or name == 'br':
g = re.match(re.compile(r'([^\n]*)\n +: +(.*)', re.S), str(tag))
if g:
# it's a definition list (which some versions of python-markdown
# don't support, including the one in Debian-lenny, so we can't
# enable that markdown extension). Fake it up.
do_definition(tag)
else:
text(tag)
w.end_para()
elif name == 'ul':
do_list(tag)
else:
raise ValueError('non-man-compatible html tag %r' % name)
PROD='Untitled'
VENDOR='Vendor Name'
SECTION='9'
GROUPNAME='User Commands'
DATE=''
AUTHOR=''
lines = []
if len(sys.argv) > 1:
for n in sys.argv[1:]:
lines += open(n).read().decode('utf8').split('\n')
else:
lines += sys.stdin.read().decode('utf8').split('\n')
# parse pandoc-style document headers (not part of markdown)
g = re.match(r'^%\s+(.*?)\((.*?)\)\s+(.*)$', lines[0])
if g:
PROD = g.group(1)
SECTION = g.group(2)
VENDOR = g.group(3)
lines.pop(0)
g = re.match(r'^%\s+(.*?)$', lines[0])
if g:
AUTHOR = g.group(1)
lines.pop(0)
g = re.match(r'^%\s+(.*?)$', lines[0])
if g:
DATE = g.group(1)
lines.pop(0)
g = re.match(r'^%\s+(.*?)$', lines[0])
if g:
GROUPNAME = g.group(1)
lines.pop(0)
inp = '\n'.join(lines)
if AUTHOR:
inp += ('\n# AUTHOR\n\n%s\n' % AUTHOR).replace('<', '\\<')
html = markdown.markdown(inp)
soup = BeautifulSoup(html, convertEntities=BeautifulSoup.HTML_ENTITIES)
macro('.TH', PROD.upper(), SECTION, DATE, VENDOR, GROUPNAME)
macro('.ad', 'l') # left justified
macro('.nh') # disable hyphenation
for e in soup:
do(e)

View File

@ -1,6 +1,6 @@
% sshuttle(8) Sshuttle 0.46
% sshuttle(8) Sshuttle %VERSION%
% Avery Pennarun <apenwarr@gmail.com>
% 2011-01-25
% %DATE%
# NAME
@ -71,6 +71,10 @@ entire subnet to the VPN.
are taken automatically from the server's routing
table.
--dns
: capture local DNS requests and forward to the remote DNS
server.
--python
: specify the name/path of the remote python interpreter.
The default is just `python`, which means to use the
@ -90,6 +94,10 @@ entire subnet to the VPN.
`0/0 -x 1.2.3.0/24` to forward everything except the
local subnet over the VPN, for example.
--exclude-from=*file*
: exclude the subnets specified in a file, one subnet per
line. Useful when you have lots of subnets to exclude.
-v, --verbose
: print more information about the session. This option
can be used more than once for increased verbosity. By

4
all.do
View File

@ -1,11 +1,11 @@
exec >&2
UI=
[ "$(uname)" = "Darwin" ] && UI=ui-macos/all
redo-ifchange sshuttle.8 $UI
redo-ifchange Documentation/all version/all $UI
echo
echo "What now?"
[ -z "$UI" ] || echo "- Try the MacOS GUI: open ui-macos/Sshuttle*.app"
echo "- Run sshuttle: ./sshuttle --dns -r HOSTNAME 0/0"
echo "- Read the README: less README.md"
echo "- Read the man page: less sshuttle.md"
echo "- Read the man page: less Documentation/sshuttle.md"

View File

@ -1,2 +1,2 @@
redo ui-macos/clean
redo ui-macos/clean Documentation/clean version/clean
rm -f *~ */*~ .*~ */.*~ *.8 *.tmp */*.tmp *.pyc */*.pyc

View File

@ -171,7 +171,9 @@ class FirewallClient:
def done(self):
self.pfile.close()
rv = self.p.wait()
if rv:
if rv == EXITCODE_NEEDS_REBOOT:
raise FatalNeedsReboot()
elif rv:
raise Fatal('cleanup: %r returned %d' % (self.argv, rv))

View File

@ -1,6 +1,6 @@
exec >&2
if pandoc </dev/null 2>/dev/null; then
pandoc -s -r markdown -w man -o $3 $1.md
pandoc -s -r markdown -w man -o $3 $2.md
else
echo "Warning: pandoc not installed; can't generate manpages."
redo-always

2
do
View File

@ -121,7 +121,7 @@ _do()
fi
[ ! -e "$DO_BUILT" ] || [ ! -d "$(dirname "$target")" ] ||
: >>"$target.did"
( _run_dofile "$base" "$ext" "$tmp.tmp" )
( _run_dofile "$target" "$base" "$tmp.tmp" )
rv=$?
if [ $rv != 0 ]; then
printf "do: %s%s\n" "$DO_DEPTH" \

View File

@ -1,4 +1,4 @@
import re, errno, socket, select, struct
import re, errno, socket, select, signal, struct
import compat.ssubprocess as ssubprocess
import helpers, ssyslog
from helpers import *
@ -6,6 +6,12 @@ from helpers import *
# python doesn't have a definition for this
IPPROTO_DIVERT = 254
# return values from sysctl_set
SUCCESS = 0
SAME = 1
FAILED = -1
NONEXIST = -2
def nonfatal(func, *args):
try:
@ -14,6 +20,14 @@ def nonfatal(func, *args):
log('error: %s\n' % e)
def _call(argv):
debug1('>> %s\n' % ' '.join(argv))
rv = ssubprocess.call(argv)
if rv:
raise Fatal('%r returned %d' % (argv, rv))
return rv
def ipt_chain_exists(name):
argv = ['iptables', '-t', 'nat', '-nL']
p = ssubprocess.Popen(argv, stdout = ssubprocess.PIPE)
@ -27,10 +41,7 @@ def ipt_chain_exists(name):
def ipt(*args):
argv = ['iptables', '-t', 'nat'] + list(args)
debug1('>> %s\n' % ' '.join(argv))
rv = ssubprocess.call(argv)
if rv:
raise Fatal('%r returned %d' % (argv, rv))
_call(argv)
_no_ttl_module = False
@ -135,6 +146,42 @@ def _fill_oldctls(prefix):
raise Fatal('%r returned no data' % (argv,))
KERNEL_FLAGS_PATH = '/Library/Preferences/SystemConfiguration/com.apple.Boot'
KERNEL_FLAGS_NAME = 'Kernel Flags'
def _defaults_read_kernel_flags():
argv = ['defaults', 'read', KERNEL_FLAGS_PATH, KERNEL_FLAGS_NAME]
debug1('>> %s\n' % ' '.join(argv))
p = ssubprocess.Popen(argv, stdout = ssubprocess.PIPE)
flagstr = p.stdout.read().strip()
rv = p.wait()
if rv:
raise Fatal('%r returned %d' % (argv, rv))
flags = flagstr and flagstr.split(' ') or []
return flags
def _defaults_write_kernel_flags(flags):
flagstr = ' '.join(flags)
argv = ['defaults', 'write', KERNEL_FLAGS_PATH, KERNEL_FLAGS_NAME,
flagstr]
_call(argv)
argv = ['plutil', '-convert', 'xml1', KERNEL_FLAGS_PATH + '.plist']
_call(argv)
def defaults_write_kernel_flag(name, val):
flags = _defaults_read_kernel_flags()
found = 0
for i in range(len(flags)):
if flags[i].startswith('%s=' % name):
found += 1
flags[i] = '%s=%s' % (name, val)
if not found:
flags.insert(0, '%s=%s' % (name, val))
_defaults_write_kernel_flags(flags)
def _sysctl_set(name, val):
argv = ['sysctl', '-w', '%s=%s' % (name, val)]
debug1('>> %s\n' % ' '.join(argv))
@ -150,20 +197,24 @@ def sysctl_set(name, val, permanent=False):
_fill_oldctls(PREFIX)
if not (name in _oldctls):
debug1('>> No such sysctl: %r\n' % name)
return False
return NONEXIST
oldval = _oldctls[name]
if val != oldval:
rv = _sysctl_set(name, val)
if rv==0 and permanent:
debug1('>> ...saving permanently in /etc/sysctl.conf\n')
f = open('/etc/sysctl.conf', 'a')
f.write('\n'
'# Added by sshuttle\n'
'%s=%s\n' % (name, val))
f.close()
else:
_changedctls.append(name)
return True
if val == oldval:
return SAME
rv = _sysctl_set(name, val)
if rv != 0:
return FAILED
if permanent:
debug1('>> ...saving permanently in /etc/sysctl.conf\n')
f = open('/etc/sysctl.conf', 'a')
f.write('\n'
'# Added by sshuttle\n'
'%s=%s\n' % (name, val))
f.close()
else:
_changedctls.append(name)
return SUCCESS
def _udp_unpack(p):
@ -201,10 +252,7 @@ def _handle_diversion(divertsock, dnsport):
def ipfw(*args):
argv = ['ipfw', '-q'] + list(args)
debug1('>> %s\n' % ' '.join(argv))
rv = ssubprocess.call(argv)
if rv:
raise Fatal('%r returned %d' % (argv, rv))
_call(argv)
def do_ipfw(port, dnsport, subnets):
@ -222,8 +270,14 @@ def do_ipfw(port, dnsport, subnets):
if subnets or dnsport:
sysctl_set('net.inet.ip.fw.enable', 1)
changed = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True)
if changed:
# This seems to be needed on MacOS 10.6 and 10.7. For more
# information, see:
# http://groups.google.com/group/sshuttle/browse_thread/thread/bc32562e17987b25/6d3aa2bb30a1edab
# and
# http://serverfault.com/questions/138622/transparent-proxying-leaves-sockets-with-syn-rcvd-in-macos-x-10-6-snow-leopard
changeflag = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True)
if changeflag == SUCCESS:
log("\n"
" WARNING: ONE-TIME NETWORK DISRUPTION:\n"
" =====================================\n"
@ -234,6 +288,21 @@ def do_ipfw(port, dnsport, subnets):
"ethernet port) NOW, then restart sshuttle. The fix is\n"
"permanent; you only have to do this once.\n\n")
sys.exit(1)
elif changeflag == FAILED:
# On MacOS 10.7, the scopedroute sysctl became read-only, so
# we have to fix it using a kernel boot parameter instead,
# which requires rebooting. For more, see:
# http://groups.google.com/group/sshuttle/browse_thread/thread/a42505ca33e1de80/e5e8f3e5a92d25f7
log('Updating kernel boot flags.\n')
defaults_write_kernel_flag('net.inet.ip.scopedroute', 0)
log("\n"
" YOU MUST REBOOT TO USE SSHUTTLE\n"
" ===============================\n"
"sshuttle has changed a MacOS kernel boot-time setting\n"
"to work around a bug in MacOS 10.7 Lion. You will need\n"
"to reboot before it takes effect. You only have to\n"
"do this once.\n\n")
sys.exit(EXITCODE_NEEDS_REBOOT)
ipfw('add', sport, 'check-state', 'ip',
'from', 'any', 'to', 'any')
@ -243,11 +312,11 @@ def do_ipfw(port, dnsport, subnets):
for swidth,sexclude,snet in sorted(subnets, reverse=True):
if sexclude:
ipfw('add', sport, 'skipto', xsport,
'log', 'tcp',
'tcp',
'from', 'any', 'to', '%s/%s' % (snet,swidth))
else:
ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port,
'log', 'tcp',
'tcp',
'from', 'any', 'to', '%s/%s' % (snet,swidth),
'not', 'ipttl', '42', 'keep-state', 'setup')
@ -289,12 +358,12 @@ def do_ipfw(port, dnsport, subnets):
for ip in nslist:
# relabel and then catch outgoing DNS requests
ipfw('add', sport, 'divert', sport,
'log', 'udp',
'udp',
'from', 'any', 'to', '%s/32' % ip, '53',
'not', 'ipttl', '42')
# relabel DNS responses
ipfw('add', sport, 'divert', sport,
'log', 'udp',
'udp',
'from', 'any', str(dnsport), 'to', 'any',
'not', 'ipttl', '42')
@ -398,6 +467,13 @@ def main(port, dnsport, syslog):
sys.stdout.write('READY\n')
sys.stdout.flush()
# don't disappear if our controlling terminal or stdout/stderr
# disappears; we still have to clean up.
signal.signal(signal.SIGHUP, signal.SIG_IGN)
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
signal.signal(signal.SIGTERM, signal.SIG_IGN)
signal.signal(signal.SIGINT, signal.SIG_IGN)
# ctrl-c shouldn't be passed along to me. When the main sshuttle dies,
# I'll die automatically.
os.setsid()

View File

@ -30,6 +30,11 @@ class Fatal(Exception):
pass
EXITCODE_NEEDS_REBOOT = 111
class FatalNeedsReboot(Fatal):
pass
def list_contains_any(l, sub):
for i in sub:
if i in l:

11
main.py Normal file → Executable file
View File

@ -57,12 +57,14 @@ dns capture local DNS requests and forward to the remote DNS server
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)
exclude-from= exclude the subnets in a file (whitespace separated)
v,verbose increase debug message verbosity
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)
D,daemon run in the background as a daemon
V,version print sshuttle's version number
syslog send log messages to syslog (default if you use --daemon)
pidfile= pidfile name (only if using --daemon) [./sshuttle.pid]
server (internal use only)
@ -72,6 +74,10 @@ hostwatch (internal use only)
o = options.Options(optspec)
(opt, flags, extra) = o.parse(sys.argv[2:])
if opt.version:
import version
print version.TAG
sys.exit(0)
if opt.daemon:
opt.syslog = 1
if opt.wrap:
@ -99,6 +105,8 @@ try:
for k,v in flags:
if k in ('-x','--exclude'):
excludes.append(v)
if k in ('-X', '--exclude-from'):
excludes += open(v).read().split()
remotename = opt.remote
if remotename == '' or remotename == '-':
remotename = None
@ -121,6 +129,9 @@ try:
parse_subnets(includes),
parse_subnets(excludes),
opt.syslog, opt.daemon, opt.pidfile))
except FatalNeedsReboot, e:
log('You must reboot before using sshuttle.\n')
sys.exit(EXITCODE_NEEDS_REBOOT)
except Fatal, e:
log('fatal: %s\n' % e)
sys.exit(99)

View File

@ -43,7 +43,12 @@ def _maskbits(netmask):
def _shl(n, bits):
return n * int(2**bits)
# we use our own implementation of left-shift because
# results may be different between older and newer versions
# of python for numbers like 1<<32. We use long() because
# int(2**32) doesn't work in older python, which has limited
# int sizes.
return n * long(2**bits)
def _list_routes():
@ -68,9 +73,11 @@ def _list_routes():
def list_routes():
l = []
for (ip,width) in _list_routes():
if not ip.startswith('0.') and not ip.startswith('127.'):
yield (ip,width)
l.append((ip,width))
return l
def _exc_dump():

View File

@ -2,12 +2,14 @@ exec >&2
redo-ifchange runpython.c
ARCHES=""
printf "Platforms: "
for d in /usr/libexec/gcc/darwin/*; do
PLAT=$(basename "$d")
[ "$PLAT" != "ppc64" ] || continue # fails for some reason on my Mac
ARCHES="$ARCHES -arch $PLAT"
printf "$PLAT "
done
if [ -d /usr/libexec/gcc/darwin ]; then
for d in /usr/libexec/gcc/darwin/*; do
PLAT=$(basename "$d")
[ "$PLAT" != "ppc64" ] || continue # fails for some reason on my Mac
ARCHES="$ARCHES -arch $PLAT"
printf "$PLAT "
done
fi
printf "\n"
gcc $ARCHES \
-Wall -o $3 runpython.c \

View File

@ -3,9 +3,9 @@ redo-ifchange sources.list
redo-ifchange Info.plist bits/runpython \
$(while read name newname; do echo "$name"; done <sources.list)
rm -rf "$1.app"
mkdir "$1.app" "$1.app/Contents"
cd "$1.app/Contents"
rm -rf "$2.app"
mkdir "$2.app" "$2.app/Contents"
cd "$2.app/Contents"
cp "$TOP/Info.plist" .
@ -18,11 +18,11 @@ cd "$TOP"
while read name newname; do
[ -z "$name" ] && continue
: "${newname:=$name}"
outname=$1.app/Contents/Resources/$newname
outname=$2.app/Contents/Resources/$newname
outdir=$(dirname "$outname")
[ -d "$outdir" ] || mkdir "$outdir"
cp "${name-$newname}" "$outname"
done <sources.list
cd "$1.app"
cd "$2.app"
redo-ifchange $(find . -type f)

View File

@ -1,5 +1,5 @@
exec >&2
IFS="
"
redo-ifchange $1.app
tar -czf $3 $1.app/
redo-ifchange $2.app
tar -czf $3 $2.app/

View File

@ -1,5 +1,5 @@
exec >&2
IFS="
"
redo-ifchange $1.app
zip -q -r $3 $1.app/
redo-ifchange $2.app
zip -q -r $3 $2.app/

View File

@ -1,2 +1,2 @@
redo-ifchange $1.xib
ibtool --compile $3 $1.xib
redo-ifchange $2.xib
ibtool --compile $3 $2.xib

View File

@ -78,6 +78,11 @@ class Runner:
if pid == self.pid:
if os.WIFEXITED(code):
self.rv = os.WEXITSTATUS(code)
if self.rv == 111:
NSRunAlertPanel('Sshuttle',
'Please restart your computer to finish '
'installing Sshuttle.',
'Restart Later', None, None)
else:
self.rv = -os.WSTOPSIG(code)
self.serverobj.setConnected_(False)
@ -87,7 +92,10 @@ class Runner:
return self.rv
def wait(self):
return self._try_wait(0)
rv = None
while rv is None:
self.gotdata(None)
rv = self._try_wait(os.WNOHANG)
def poll(self):
return self._try_wait(os.WNOHANG)

1
version/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
gitvars.pre export-subst

3
version/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/vars
/gitvars
/_version.py

1
version/__init__.py Normal file
View File

@ -0,0 +1 @@
from _version import COMMIT, TAG, DATE

3
version/_version.py.do Normal file
View File

@ -0,0 +1,3 @@
redo-ifchange vars
cat vars

2
version/all.do Normal file
View File

@ -0,0 +1,2 @@
redo-ifchange vars _version.py

3
version/clean.do Normal file
View File

@ -0,0 +1,3 @@
rm -f *~ .*~ *.pyc _version.py vars gitvars

28
version/gitvars.do Normal file
View File

@ -0,0 +1,28 @@
redo-ifchange gitvars.pre prodname
read PROD <prodname
exec >$3
# Fix each line from gitvars.pre where git may or may not have already
# substituted the variables. If someone generated a tarball with 'git archive',
# then the data will have been substituted already. If we're in a checkout of
# the git repo, then it won't, but we can just ask git to do the substitutions
# right now.
while read line; do
# Lines *may* be of the form: $Format: ... $
x=${line#\$Format:} # remove prefix
if [ "$x" != "$line" ]; then
# git didn't substitute it
redo-always # git this from the git repo
x=${x%\$} # remove trailing $
if [ "$x" = "%d" ]; then
tag=$(git describe --match="$PROD-*")
x="(tag: $tag)"
else
x=$(git log -1 --pretty=format:"$x")
fi
fi
echo "$x"
done <gitvars.pre
redo-stamp <$3

3
version/gitvars.pre Normal file
View File

@ -0,0 +1,3 @@
$Format:%H$
$Format:%d$
$Format:%ci$

1
version/prodname Normal file
View File

@ -0,0 +1 @@
sshuttle

40
version/vars.do Normal file
View File

@ -0,0 +1,40 @@
redo-ifchange gitvars prodname
read PROD <prodname
exec <gitvars
read COMMIT
read NAMES
read DATE
# the list of names is of the form:
# (x,y,tag: $PROD-####,tag: $PROD-####,a,b)
# The entries we want are the ones starting with "tag: $PROD-" since those
# refer to the right actual git tags.
names_to_tag()
{
x=${1#\(}
x=${x%\)}
cur=
while [ "$cur" != "$x" ]; do
x=${x# }
x=${x#tag: }
cur=${x%%,*}
tagpost=${cur#$PROD-}
if [ "$cur" != "$tagpost" ]; then
echo "$tagpost"
return 0
fi
x=${x#*,}
done
commitpost=${COMMIT#???????}
commitpre=${COMMIT%$commitpost}
echo "unknown-$commitpre"
}
sTAG=$(names_to_tag "$NAMES")
echo "COMMIT='$COMMIT'"
echo "TAG='$sTAG'"
echo "DATE='${DATE%% *}'"