mirror of
https://github.com/sshuttle/sshuttle.git
synced 2025-06-20 09:57:42 +02:00
Compare commits
17 Commits
master
...
sshuttle-0
Author | SHA1 | Date | |
---|---|---|---|
|
29d2e06bf5 | ||
|
bff1610050 | ||
|
cce6a9d96d | ||
|
5743f29ed6 | ||
|
42bc6d62db | ||
|
274ee854d4 | ||
|
12f6a52ec6 | ||
|
e737f4b944 | ||
|
d9f761a8a3 | ||
|
bd20841782 | ||
|
4c1a505e37 | ||
|
41d1f73dc2 | ||
|
cbc32ff8d8 | ||
|
6698992f4f | ||
|
e2c682084c | ||
|
89e914e9d1 | ||
|
2268e76771 |
3
Documentation/.gitignore
vendored
Normal file
3
Documentation/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.8
|
||||
/md-to-man
|
||||
/*.md.tmp
|
5
Documentation/all.do
Normal file
5
Documentation/all.do
Normal file
@ -0,0 +1,5 @@
|
||||
/bin/ls *.md |
|
||||
sed 's/\.md/.8/' |
|
||||
xargs redo-ifchange
|
||||
|
||||
redo-always
|
1
Documentation/clean.do
Normal file
1
Documentation/clean.do
Normal file
@ -0,0 +1 @@
|
||||
rm -f *~ .*~ *.8 t/*.8 md-to-man *.tmp t/*.tmp
|
2
Documentation/default.8.do
Normal file
2
Documentation/default.8.do
Normal file
@ -0,0 +1,2 @@
|
||||
redo-ifchange md-to-man $2.md.tmp
|
||||
. ./md-to-man $1 $2 $3
|
3
Documentation/default.md.tmp.do
Normal file
3
Documentation/default.md.tmp.do
Normal file
@ -0,0 +1,3 @@
|
||||
redo-ifchange ../version/vars $2.md
|
||||
. ../version/vars
|
||||
sed -e "s/%VERSION%/$TAG/" -e "s/%DATE%/$DATE/" $2.md
|
8
Documentation/md-to-man.do
Normal file
8
Documentation/md-to-man.do
Normal 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
278
Documentation/md2man.py
Executable 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)
|
@ -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
4
all.do
@ -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"
|
||||
|
2
clean.do
2
clean.do
@ -1,2 +1,2 @@
|
||||
redo ui-macos/clean
|
||||
redo ui-macos/clean Documentation/clean version/clean
|
||||
rm -f *~ */*~ .*~ */.*~ *.8 *.tmp */*.tmp *.pyc */*.pyc
|
||||
|
@ -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))
|
||||
|
||||
|
||||
|
@ -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
2
do
@ -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" \
|
||||
|
114
firewall.py
114
firewall.py
@ -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,11 +197,15 @@ 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:
|
||||
if val == oldval:
|
||||
return SAME
|
||||
|
||||
rv = _sysctl_set(name, val)
|
||||
if rv==0 and permanent:
|
||||
if rv != 0:
|
||||
return FAILED
|
||||
if permanent:
|
||||
debug1('>> ...saving permanently in /etc/sysctl.conf\n')
|
||||
f = open('/etc/sysctl.conf', 'a')
|
||||
f.write('\n'
|
||||
@ -163,7 +214,7 @@ def sysctl_set(name, val, permanent=False):
|
||||
f.close()
|
||||
else:
|
||||
_changedctls.append(name)
|
||||
return True
|
||||
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()
|
||||
|
@ -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
11
main.py
Normal file → Executable 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)
|
||||
|
11
server.py
11
server.py
@ -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():
|
||||
|
@ -2,12 +2,14 @@ exec >&2
|
||||
redo-ifchange runpython.c
|
||||
ARCHES=""
|
||||
printf "Platforms: "
|
||||
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 \
|
||||
|
@ -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)
|
||||
|
@ -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/
|
||||
|
@ -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/
|
||||
|
@ -1,2 +1,2 @@
|
||||
redo-ifchange $1.xib
|
||||
ibtool --compile $3 $1.xib
|
||||
redo-ifchange $2.xib
|
||||
ibtool --compile $3 $2.xib
|
||||
|
@ -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
1
version/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
gitvars.pre export-subst
|
3
version/.gitignore
vendored
Normal file
3
version/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/vars
|
||||
/gitvars
|
||||
/_version.py
|
1
version/__init__.py
Normal file
1
version/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from _version import COMMIT, TAG, DATE
|
3
version/_version.py.do
Normal file
3
version/_version.py.do
Normal file
@ -0,0 +1,3 @@
|
||||
redo-ifchange vars
|
||||
cat vars
|
||||
|
2
version/all.do
Normal file
2
version/all.do
Normal file
@ -0,0 +1,2 @@
|
||||
redo-ifchange vars _version.py
|
||||
|
3
version/clean.do
Normal file
3
version/clean.do
Normal file
@ -0,0 +1,3 @@
|
||||
rm -f *~ .*~ *.pyc _version.py vars gitvars
|
||||
|
||||
|
28
version/gitvars.do
Normal file
28
version/gitvars.do
Normal 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
3
version/gitvars.pre
Normal file
@ -0,0 +1,3 @@
|
||||
$Format:%H$
|
||||
$Format:%d$
|
||||
$Format:%ci$
|
1
version/prodname
Normal file
1
version/prodname
Normal file
@ -0,0 +1 @@
|
||||
sshuttle
|
40
version/vars.do
Normal file
40
version/vars.do
Normal 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%% *}'"
|
Loading…
x
Reference in New Issue
Block a user