Compare commits

..

1 Commits

Author SHA1 Message Date
9915d736fe Enhanced DNS support. Initial version. 2011-05-12 14:37:19 +10:00
38 changed files with 275 additions and 750 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +0,0 @@
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

View File

@ -1,278 +0,0 @@
#!/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

@ -63,19 +63,7 @@ This is how you use it:
on your client machine. You'll need root or sudo on your client machine. You'll need root or sudo
access, and python needs to be installed. access, and python needs to be installed.
- The most basic use of sshuttle looks like: - <tt>./sshuttle -r username@sshserver 0.0.0.0/0 -vv</tt>
<tt>./sshuttle -r username@sshserver 0.0.0.0/0 -vv</tt>
- There is a shortcut for 0.0.0.0/0 for those that value
their wrists
<tt>./sshuttle -r username@sshserver 0/0 -vv</tt>
- If you would also like your DNS queries to be proxied
through the DNS server of the server you are connect to:
<tt>./sshuttle --dns -vvr username@sshserver 0/0</tt>
The above is probably what you want to use to prevent
local network attacks such as Firesheep and friends.
(You may be prompted for one or more passwords; first, the (You may be prompted for one or more passwords; first, the
local password to become root using either sudo or su, and local password to become root using either sudo or su, and

4
all.do
View File

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

View File

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

217
client.py
View File

@ -171,73 +171,64 @@ class FirewallClient:
def done(self): def done(self):
self.pfile.close() self.pfile.close()
rv = self.p.wait() rv = self.p.wait()
if rv == EXITCODE_NEEDS_REBOOT: if rv:
raise FatalNeedsReboot()
elif rv:
raise Fatal('cleanup: %r returned %d' % (self.argv, rv)) raise Fatal('cleanup: %r returned %d' % (self.argv, rv))
def onaccept(listener, mux, handlers): def unpack_dns_name(buf, off):
global _extra_fd name = ''
try: while True:
sock,srcip = listener.accept() # get the next octet from buffer
except socket.error, e: n = ord(buf[off])
if e.args[0] in [errno.EMFILE, errno.ENFILE]:
debug1('Rejected incoming connection: too many open files!\n') # zero octet terminates name
# free up an fd so we can eat the connection if n == 0:
os.close(_extra_fd) off += 1
try: break
sock,srcip = listener.accept()
sock.close() # top two bits on
finally: # => a 2 octect pointer to another part of the buffer
_extra_fd = os.open('/dev/null', os.O_RDONLY) elif (n & 0xc0) == 0xc0:
return ptr = struct.unpack('>H', buf[off:off+2])[0] & 0x3fff
off = ptr
# an octet representing the number of bytes to process.
else: else:
raise off += 1
dstip = original_dst(sock) name = name + buf[off:off+n] + '.'
debug1('Accept: %s:%r -> %s:%r.\n' % (srcip[0],srcip[1], off += n
dstip[0],dstip[1]))
if dstip[1] == listener.getsockname()[1] and islocal(dstip[0]):
debug1("-- ignored: that's my address!\n")
sock.close()
return
chan = mux.next_channel()
if not chan:
log('warning: too many open channels. Discarded connection.\n')
sock.close()
return
mux.send(chan, ssnet.CMD_CONNECT, '%s,%s' % dstip)
outwrap = MuxWrapper(mux, chan)
handlers.append(Proxy(SockWrapper(sock, sock), outwrap))
return name.strip('.'), off
dnsreqs = {} class dnspkt:
def dns_done(chan, data): def unpack(self, buf, off):
peer,sock,timeout = dnsreqs.get(chan) or (None,None,None) l = len(buf)
debug3('dns_done: channel=%r peer=%r\n' % (chan, peer))
if peer:
del dnsreqs[chan]
debug3('doing sendto %r\n' % (peer,))
sock.sendto(data, peer)
(self.id, self.op, self.qdcount, self.ancount, self.nscount, self.arcount) = struct.unpack("!HHHHHH",buf[off:off+12])
off += 12
def ondns(listener, mux, handlers): self.q = []
pkt,peer = listener.recvfrom(4096) for i in range(self.qdcount):
now = time.time() qname, off = unpack_dns_name(buf, off)
if pkt: qtype, qclass = struct.unpack('!HH', buf[off:off+4])
debug1('DNS request from %r: %d bytes\n' % (peer, len(pkt))) off += 4
chan = mux.next_channel() self.q.append( (qname,qtype,qclass) )
dnsreqs[chan] = peer,listener,now+30
mux.send(chan, ssnet.CMD_DNS_REQ, pkt)
mux.channels[chan] = lambda cmd,data: dns_done(chan,data)
for chan,(peer,sock,timeout) in dnsreqs.items():
if timeout < now:
del dnsreqs[chan]
debug3('Remaining DNS requests: %d\n' % len(dnsreqs))
return off
def match_q_domain(self, domain):
l = len(domain)
for qname,qtype,qclass in self.q:
if qname[-l:] == domain:
if l==len(qname):
return True
elif qname[-l-1] == '.':
return True
return False
def _main(listener, fw, ssh_cmd, remotename, python, latency_control, def _main(listener, fw, ssh_cmd, remotename, python, latency_control,
dnslistener, seed_hosts, auto_nets, dnslistener, dnsforwarder, dns_domains, dns_to,
seed_hosts, auto_nets,
syslog, daemon): syslog, daemon):
handlers = [] handlers = []
if helpers.verbose >= 1: if helpers.verbose >= 1:
@ -259,14 +250,7 @@ def _main(listener, fw, ssh_cmd, remotename, python, latency_control,
handlers.append(mux) handlers.append(mux)
expected = 'SSHUTTLE0001' expected = 'SSHUTTLE0001'
try: try:
v = 'x'
while v and v != '\0':
v = serversock.recv(1)
v = 'x'
while v and v != '\0':
v = serversock.recv(1)
initstring = serversock.recv(len(expected)) initstring = serversock.recv(len(expected))
except socket.error, e: except socket.error, e:
if e.args[0] == errno.ECONNRESET: if e.args[0] == errno.ECONNRESET:
@ -316,10 +300,102 @@ def _main(listener, fw, ssh_cmd, remotename, python, latency_control,
fw.sethostip(name, ip) fw.sethostip(name, ip)
mux.got_host_list = onhostlist mux.got_host_list = onhostlist
handlers.append(Handler([listener], lambda: onaccept(listener, mux, handlers))) def onaccept():
global _extra_fd
try:
sock,srcip = listener.accept()
except socket.error, e:
if e.args[0] in [errno.EMFILE, errno.ENFILE]:
debug1('Rejected incoming connection: too many open files!\n')
# free up an fd so we can eat the connection
os.close(_extra_fd)
try:
sock,srcip = listener.accept()
sock.close()
finally:
_extra_fd = os.open('/dev/null', os.O_RDONLY)
return
else:
raise
dstip = original_dst(sock)
debug1('Accept: %s:%r -> %s:%r.\n' % (srcip[0],srcip[1],
dstip[0],dstip[1]))
if dstip[1] == listener.getsockname()[1] and islocal(dstip[0]):
debug1("-- ignored: that's my address!\n")
sock.close()
return
chan = mux.next_channel()
if not chan:
log('warning: too many open channels. Discarded connection.\n')
sock.close()
return
mux.send(chan, ssnet.CMD_CONNECT, '%s,%s' % dstip)
outwrap = MuxWrapper(mux, chan)
handlers.append(Proxy(SockWrapper(sock, sock), outwrap))
handlers.append(Handler([listener], onaccept))
dnsreqs = {}
dnsforwards = {}
def dns_done(chan, data):
peer,timeout = dnsreqs.get(chan) or (None,None)
debug3('dns_done: channel=%r peer=%r\n' % (chan, peer))
if peer:
del dnsreqs[chan]
debug3('doing sendto %r\n' % (peer,))
dnslistener.sendto(data, peer)
def ondns():
pkt,peer = dnslistener.recvfrom(4096)
now = time.time()
if pkt:
debug1('DNS request from %r: %d bytes\n' % (peer, len(pkt)))
dns = dnspkt()
dns.unpack(pkt, 0)
match=False
if dns_domains is not None:
for domain in dns_domains:
if dns.match_q_domain(domain):
match=True
break
if match:
debug3("We need to redirect this request remotely\n")
chan = mux.next_channel()
dnsreqs[chan] = peer,now+30
mux.send(chan, ssnet.CMD_DNS_REQ, pkt)
mux.channels[chan] = lambda cmd,data: dns_done(chan,data)
else:
debug3("We need to forward this request locally\n")
dnsforwarder.sendto(pkt, dns_to)
dnsforwards[dns.id] = peer,now+30
for chan,(peer,timeout) in dnsreqs.items():
if timeout < now:
del dnsreqs[chan]
for chan,(peer,timeout) in dnsforwards.items():
if timeout < now:
del dnsforwards[chan]
debug3('Remaining DNS requests: %d\n' % len(dnsreqs))
debug3('Remaining DNS forwards: %d\n' % len(dnsforwards))
if dnslistener: if dnslistener:
handlers.append(Handler([dnslistener], lambda: ondns(dnslistener, mux, handlers))) handlers.append(Handler([dnslistener], ondns))
def ondnsforward():
debug1("We got a response.\n")
pkt,server = dnsforwarder.recvfrom(4096)
now = time.time()
if server[0] != dns_to[0] or server[1] != dns_to[1]:
debug1("Ooops. The response came from the wrong server. Ignoring\n")
else:
dns = dnspkt()
dns.unpack(pkt, 0)
chan=dns.id
peer,timeout = dnsforwards.get(chan) or (None,None)
debug3('dns_done: channel=%r peer=%r\n' % (chan, peer))
if peer:
del dnsforwards[chan]
debug3('doing sendto %r\n' % (peer,))
dnslistener.sendto(pkt, peer)
if dnsforwarder:
handlers.append(Handler([dnsforwarder], ondnsforward))
if seed_hosts != None: if seed_hosts != None:
debug1('seed_hosts: %r\n' % seed_hosts) debug1('seed_hosts: %r\n' % seed_hosts)
@ -336,7 +412,8 @@ def _main(listener, fw, ssh_cmd, remotename, python, latency_control,
mux.callback() mux.callback()
def main(listenip, ssh_cmd, remotename, python, latency_control, dns, def main(listenip, ssh_cmd, remotename, python, latency_control,
dns, dns_domains, dns_to,
seed_hosts, auto_nets, seed_hosts, auto_nets,
subnets_include, subnets_exclude, syslog, daemon, pidfile): subnets_include, subnets_exclude, syslog, daemon, pidfile):
if syslog: if syslog:
@ -381,15 +458,21 @@ def main(listenip, ssh_cmd, remotename, python, latency_control, dns,
dnsip = dnslistener.getsockname() dnsip = dnslistener.getsockname()
debug1('DNS listening on %r.\n' % (dnsip,)) debug1('DNS listening on %r.\n' % (dnsip,))
dnsport = dnsip[1] dnsport = dnsip[1]
dnsforwarder = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
dnsforwarder.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
dnsforwarder.setsockopt(socket.SOL_IP, socket.IP_TTL, 42)
else: else:
dnsport = 0 dnsport = 0
dnslistener = None dnslistener = None
dnsforwarder = None
fw = FirewallClient(listenip[1], subnets_include, subnets_exclude, dnsport) fw = FirewallClient(listenip[1], subnets_include, subnets_exclude, dnsport)
try: try:
return _main(listener, fw, ssh_cmd, remotename, return _main(listener, fw, ssh_cmd, remotename,
python, latency_control, dnslistener, python, latency_control,
dnslistener, dnsforwarder, dns_domains, dns_to,
seed_hosts, auto_nets, syslog, daemon) seed_hosts, auto_nets, syslog, daemon)
finally: finally:
try: try:

View File

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

115
do
View File

@ -8,14 +8,14 @@
# #
# By default, no output coloring. # By default, no output coloring.
green="" GREEN=""
bold="" BOLD=""
plain="" PLAIN=""
if [ -n "$TERM" -a "$TERM" != "dumb" ] && tty <&2 >/dev/null 2>&1; then if [ -n "$TERM" -a "$TERM" != "dumb" ] && tty <&2 >/dev/null 2>&1; then
green="$(printf '\033[32m')" GREEN="$(printf '\033[32m')"
bold="$(printf '\033[1m')" BOLD="$(printf '\033[1m')"
plain="$(printf '\033[m')" PLAIN="$(printf '\033[m')"
fi fi
_dirsplit() _dirsplit()
@ -24,13 +24,6 @@ _dirsplit()
dir=${1%$base} dir=${1%$base}
} }
dirname()
(
_dirsplit "$1"
dir=${dir%/}
echo "${dir:-.}"
)
_dirsplit "$0" _dirsplit "$0"
export REDO=$(cd "${dir:-.}" && echo "$PWD/$base") export REDO=$(cd "${dir:-.}" && echo "$PWD/$base")
@ -61,105 +54,87 @@ fi
_find_dofile_pwd() _find_dofile_pwd()
{ {
dofile=default.$1.do DOFILE=default.$1.do
while :; do while :; do
dofile=default.${dofile#default.*.} DOFILE=default.${DOFILE#default.*.}
[ -e "$dofile" -o "$dofile" = default.do ] && break [ -e "$DOFILE" -o "$DOFILE" = default.do ] && break
done done
ext=${dofile#default} EXT=${DOFILE#default}
ext=${ext%.do} EXT=${EXT%.do}
base=${1%$ext} BASE=${1%$EXT}
} }
_find_dofile() _find_dofile()
{ {
local prefix= PREFIX=
while :; do while :; do
_find_dofile_pwd "$1" _find_dofile_pwd "$1"
[ -e "$dofile" ] && break [ -e "$DOFILE" ] && break
[ "$PWD" = "/" ] && break [ "$PWD" = "/" ] && break
target=${PWD##*/}/$target TARGET=${PWD##*/}/$TARGET
tmp=${PWD##*/}/$tmp PREFIX=${PWD##*/}/$PREFIX
prefix=${PWD##*/}/$prefix
cd .. cd ..
done done
base=$prefix$base BASE=$PREFIX$BASE
} }
_run_dofile() _run_dofile()
{ {
export DO_DEPTH="$DO_DEPTH " export DO_DEPTH="$DO_DEPTH "
export REDO_TARGET=$PWD/$target export REDO_TARGET=$PWD/$TARGET
local line1
set -e set -e
read line1 <"$PWD/$dofile" read line1 <"$PWD/$DOFILE"
cmd=${line1#"#!/"} cmd=${line1#"#!/"}
if [ "$cmd" != "$line1" ]; then if [ "$cmd" != "$line1" ]; then
/$cmd "$PWD/$dofile" "$@" >"$tmp.tmp2" /$cmd "$PWD/$DOFILE" "$@" >"$TARGET.tmp2"
else else
:; . "$PWD/$dofile" >"$tmp.tmp2" . "$PWD/$DOFILE" >"$TARGET.tmp2"
fi fi
} }
_do() _do()
{ {
local dir=$1 target=$2 tmp=$3 DIR=$1
if [ ! -e "$target" ] || [ -d "$target" -a ! -e "$target.did" ]; then TARGET=$2
if [ ! -e "$TARGET" ] || [ -e "$TARGET/." -a ! -e "$TARGET.did" ]; then
printf '%sdo %s%s%s%s\n' \ printf '%sdo %s%s%s%s\n' \
"$green" "$DO_DEPTH" "$bold" "$dir$target" "$plain" >&2 "$GREEN" "$DO_DEPTH" "$BOLD" "$DIR$TARGET" "$PLAIN" >&2
echo "$PWD/$target" >>"$DO_BUILT" echo "$PWD/$TARGET" >>"$DO_BUILT"
dofile=$target.do DOFILE=$TARGET.do
base=$target BASE=$TARGET
ext= EXT=
[ -e "$target.do" ] || _find_dofile "$target" [ -e "$TARGET.do" ] || _find_dofile "$TARGET"
if [ ! -e "$dofile" ]; then if [ ! -e "$DOFILE" ]; then
echo "do: $target: no .do file" >&2 echo "do: $TARGET: no .do file" >&2
return 1 return 1
fi fi
[ ! -e "$DO_BUILT" ] || [ ! -d "$(dirname "$target")" ] || [ ! -e "$DO_BUILD" ] || : >>"$TARGET.did"
: >>"$target.did" ( _run_dofile "$BASE" "$EXT" "$TARGET.tmp" )
( _run_dofile "$target" "$base" "$tmp.tmp" ) RV=$?
rv=$? if [ $RV != 0 ]; then
if [ $rv != 0 ]; then
printf "do: %s%s\n" "$DO_DEPTH" \ printf "do: %s%s\n" "$DO_DEPTH" \
"$dir$target: got exit code $rv" >&2 "$DIR$TARGET: got exit code $RV" >&2
rm -f "$tmp.tmp" "$tmp.tmp2" rm -f "$TARGET.tmp" "$TARGET.tmp2"
return $rv return $RV
fi fi
mv "$tmp.tmp" "$target" 2>/dev/null || mv "$TARGET.tmp" "$TARGET" 2>/dev/null ||
! test -s "$tmp.tmp2" || ! test -s "$TARGET.tmp2" ||
mv "$tmp.tmp2" "$target" 2>/dev/null mv "$TARGET.tmp2" "$TARGET" 2>/dev/null
rm -f "$tmp.tmp2" rm -f "$TARGET.tmp2"
else else
echo "do $DO_DEPTH$target exists." >&2 echo "do $DO_DEPTH$TARGET exists." >&2
fi fi
} }
# Make corrections for directories that don't actually exist yet.
_dir_shovel()
{
local dir base
xdir=$1 xbase=$2 xbasetmp=$2
while [ ! -d "$xdir" -a -n "$xdir" ]; do
_dirsplit "${xdir%/}"
xbasetmp=${base}__$xbase
xdir=$dir xbase=$base/$xbase
echo "xbasetmp='$xbasetmp'" >&2
done
}
redo() redo()
{ {
for i in "$@"; do for i in "$@"; do
_dirsplit "$i" _dirsplit "$i"
_dir_shovel "$dir" "$base" ( cd "$dir" && _do "$dir" "$base" ) || return 1
dir=$xdir base=$xbase basetmp=$xbasetmp
( cd "$dir" && _do "$dir" "$base" "$basetmp" ) || return 1
done done
} }

View File

@ -1,4 +1,4 @@
import re, errno, socket, select, signal, struct import re, errno, socket, select, struct
import compat.ssubprocess as ssubprocess import compat.ssubprocess as ssubprocess
import helpers, ssyslog import helpers, ssyslog
from helpers import * from helpers import *
@ -6,12 +6,6 @@ from helpers import *
# python doesn't have a definition for this # python doesn't have a definition for this
IPPROTO_DIVERT = 254 IPPROTO_DIVERT = 254
# return values from sysctl_set
SUCCESS = 0
SAME = 1
FAILED = -1
NONEXIST = -2
def nonfatal(func, *args): def nonfatal(func, *args):
try: try:
@ -20,14 +14,6 @@ def nonfatal(func, *args):
log('error: %s\n' % e) 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): def ipt_chain_exists(name):
argv = ['iptables', '-t', 'nat', '-nL'] argv = ['iptables', '-t', 'nat', '-nL']
p = ssubprocess.Popen(argv, stdout = ssubprocess.PIPE) p = ssubprocess.Popen(argv, stdout = ssubprocess.PIPE)
@ -41,7 +27,10 @@ def ipt_chain_exists(name):
def ipt(*args): def ipt(*args):
argv = ['iptables', '-t', 'nat'] + list(args) argv = ['iptables', '-t', 'nat'] + list(args)
_call(argv) debug1('>> %s\n' % ' '.join(argv))
rv = ssubprocess.call(argv)
if rv:
raise Fatal('%r returned %d' % (argv, rv))
_no_ttl_module = False _no_ttl_module = False
@ -146,42 +135,6 @@ def _fill_oldctls(prefix):
raise Fatal('%r returned no data' % (argv,)) 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): def _sysctl_set(name, val):
argv = ['sysctl', '-w', '%s=%s' % (name, val)] argv = ['sysctl', '-w', '%s=%s' % (name, val)]
debug1('>> %s\n' % ' '.join(argv)) debug1('>> %s\n' % ' '.join(argv))
@ -197,24 +150,20 @@ def sysctl_set(name, val, permanent=False):
_fill_oldctls(PREFIX) _fill_oldctls(PREFIX)
if not (name in _oldctls): if not (name in _oldctls):
debug1('>> No such sysctl: %r\n' % name) debug1('>> No such sysctl: %r\n' % name)
return NONEXIST return False
oldval = _oldctls[name] oldval = _oldctls[name]
if val == oldval: if val != oldval:
return SAME rv = _sysctl_set(name, val)
if rv==0 and permanent:
rv = _sysctl_set(name, val) debug1('>> ...saving permanently in /etc/sysctl.conf\n')
if rv != 0: f = open('/etc/sysctl.conf', 'a')
return FAILED f.write('\n'
if permanent: '# Added by sshuttle\n'
debug1('>> ...saving permanently in /etc/sysctl.conf\n') '%s=%s\n' % (name, val))
f = open('/etc/sysctl.conf', 'a') f.close()
f.write('\n' else:
'# Added by sshuttle\n' _changedctls.append(name)
'%s=%s\n' % (name, val)) return True
f.close()
else:
_changedctls.append(name)
return SUCCESS
def _udp_unpack(p): def _udp_unpack(p):
@ -252,7 +201,10 @@ def _handle_diversion(divertsock, dnsport):
def ipfw(*args): def ipfw(*args):
argv = ['ipfw', '-q'] + list(args) argv = ['ipfw', '-q'] + list(args)
_call(argv) debug1('>> %s\n' % ' '.join(argv))
rv = ssubprocess.call(argv)
if rv:
raise Fatal('%r returned %d' % (argv, rv))
def do_ipfw(port, dnsport, subnets): def do_ipfw(port, dnsport, subnets):
@ -270,14 +222,8 @@ def do_ipfw(port, dnsport, subnets):
if subnets or dnsport: if subnets or dnsport:
sysctl_set('net.inet.ip.fw.enable', 1) sysctl_set('net.inet.ip.fw.enable', 1)
changed = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True)
# This seems to be needed on MacOS 10.6 and 10.7. For more if changed:
# 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" log("\n"
" WARNING: ONE-TIME NETWORK DISRUPTION:\n" " WARNING: ONE-TIME NETWORK DISRUPTION:\n"
" =====================================\n" " =====================================\n"
@ -288,21 +234,6 @@ def do_ipfw(port, dnsport, subnets):
"ethernet port) NOW, then restart sshuttle. The fix is\n" "ethernet port) NOW, then restart sshuttle. The fix is\n"
"permanent; you only have to do this once.\n\n") "permanent; you only have to do this once.\n\n")
sys.exit(1) 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', ipfw('add', sport, 'check-state', 'ip',
'from', 'any', 'to', 'any') 'from', 'any', 'to', 'any')
@ -312,11 +243,11 @@ def do_ipfw(port, dnsport, subnets):
for swidth,sexclude,snet in sorted(subnets, reverse=True): for swidth,sexclude,snet in sorted(subnets, reverse=True):
if sexclude: if sexclude:
ipfw('add', sport, 'skipto', xsport, ipfw('add', sport, 'skipto', xsport,
'tcp', 'log', 'tcp',
'from', 'any', 'to', '%s/%s' % (snet,swidth)) 'from', 'any', 'to', '%s/%s' % (snet,swidth))
else: else:
ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port, ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port,
'tcp', 'log', 'tcp',
'from', 'any', 'to', '%s/%s' % (snet,swidth), 'from', 'any', 'to', '%s/%s' % (snet,swidth),
'not', 'ipttl', '42', 'keep-state', 'setup') 'not', 'ipttl', '42', 'keep-state', 'setup')
@ -358,12 +289,12 @@ def do_ipfw(port, dnsport, subnets):
for ip in nslist: for ip in nslist:
# relabel and then catch outgoing DNS requests # relabel and then catch outgoing DNS requests
ipfw('add', sport, 'divert', sport, ipfw('add', sport, 'divert', sport,
'udp', 'log', 'udp',
'from', 'any', 'to', '%s/32' % ip, '53', 'from', 'any', 'to', '%s/32' % ip, '53',
'not', 'ipttl', '42') 'not', 'ipttl', '42')
# relabel DNS responses # relabel DNS responses
ipfw('add', sport, 'divert', sport, ipfw('add', sport, 'divert', sport,
'udp', 'log', 'udp',
'from', 'any', str(dnsport), 'to', 'any', 'from', 'any', str(dnsport), 'to', 'any',
'not', 'ipttl', '42') 'not', 'ipttl', '42')
@ -467,13 +398,6 @@ def main(port, dnsport, syslog):
sys.stdout.write('READY\n') sys.stdout.write('READY\n')
sys.stdout.flush() 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, # ctrl-c shouldn't be passed along to me. When the main sshuttle dies,
# I'll die automatically. # I'll die automatically.
os.setsid() os.setsid()

View File

@ -1,4 +1,4 @@
import sys, os, socket, errno import sys, os, socket
logprefix = '' logprefix = ''
verbose = 0 verbose = 0
@ -30,11 +30,6 @@ class Fatal(Exception):
pass pass
EXITCODE_NEEDS_REBOOT = 111
class FatalNeedsReboot(Fatal):
pass
def list_contains_any(l, sub): def list_contains_any(l, sub):
for i in sub: for i in sub:
if i in l: if i in l:

29
main.py Executable file → Normal file
View File

@ -54,17 +54,17 @@ l,listen= transproxy to this ip address and port number [127.0.0.1:0]
H,auto-hosts scan for remote hostnames and update local /etc/hosts H,auto-hosts scan for remote hostnames and update local /etc/hosts
N,auto-nets automatically determine subnets to route N,auto-nets automatically determine subnets to route
dns capture local DNS requests and forward to the remote DNS server dns capture local DNS requests and forward to the remote DNS server
dns-domains= comma seperated list of DNS domains for DNS forwarding
dns-to= forward any DNS requests that don't match domains to this address
python= path to python interpreter on the remote server python= path to python interpreter on the remote server
r,remote= ssh hostname (and optional username) of remote sshuttle server r,remote= ssh hostname (and optional username) of remote sshuttle server
x,exclude= exclude this subnet (can be used more than once) x,exclude= 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 v,verbose increase debug message verbosity
e,ssh-cmd= the command to use to connect to the remote [ssh] 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) seed-hosts= with -H, use these hostnames for initial scan (comma-separated)
no-latency-control sacrifice latency to improve bandwidth benchmarks no-latency-control sacrifice latency to improve bandwidth benchmarks
wrap= restart counting channel numbers after this number (for testing) wrap= restart counting channel numbers after this number (for testing)
D,daemon run in the background as a daemon 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) syslog send log messages to syslog (default if you use --daemon)
pidfile= pidfile name (only if using --daemon) [./sshuttle.pid] pidfile= pidfile name (only if using --daemon) [./sshuttle.pid]
server (internal use only) server (internal use only)
@ -74,10 +74,6 @@ hostwatch (internal use only)
o = options.Options(optspec) o = options.Options(optspec)
(opt, flags, extra) = o.parse(sys.argv[2:]) (opt, flags, extra) = o.parse(sys.argv[2:])
if opt.version:
import version
print version.TAG
sys.exit(0)
if opt.daemon: if opt.daemon:
opt.syslog = 1 opt.syslog = 1
if opt.wrap: if opt.wrap:
@ -105,8 +101,6 @@ try:
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)
if k in ('-X', '--exclude-from'):
excludes += open(v).read().split()
remotename = opt.remote remotename = opt.remote
if remotename == '' or remotename == '-': if remotename == '' or remotename == '-':
remotename = None remotename = None
@ -118,20 +112,31 @@ try:
sh = [] sh = []
else: else:
sh = None sh = None
if opt.dns and opt.dns_domains:
dns_domains = opt.dns_domains.split(",")
if opt.dns_to:
addr,colon,port = opt.dns_to.rpartition(":")
if colon == ":":
dns_to = ( addr, int(port) )
else:
dns_to = ( port, 53 )
else:
o.fatal('--dns-to=ip is required with --dns-domains=list')
else:
dns_domains = None
dns_to = None
sys.exit(client.main(parse_ipport(opt.listen or '0.0.0.0:0'), sys.exit(client.main(parse_ipport(opt.listen or '0.0.0.0:0'),
opt.ssh_cmd, opt.ssh_cmd,
remotename, remotename,
opt.python, opt.python,
opt.latency_control, opt.latency_control,
opt.dns, opt.dns, dns_domains, dns_to,
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))
except FatalNeedsReboot, e:
log('You must reboot before using sshuttle.\n')
sys.exit(EXITCODE_NEEDS_REBOOT)
except Fatal, e: except Fatal, e:
log('fatal: %s\n' % e) log('fatal: %s\n' % e)
sys.exit(99) sys.exit(99)

View File

@ -43,12 +43,7 @@ def _maskbits(netmask):
def _shl(n, bits): def _shl(n, bits):
# we use our own implementation of left-shift because return n * int(2**bits)
# 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(): def _list_routes():
@ -73,11 +68,9 @@ def _list_routes():
def list_routes(): def list_routes():
l = []
for (ip,width) in _list_routes(): for (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.'):
l.append((ip,width)) yield (ip,width)
return l
def _exc_dump(): def _exc_dump():
@ -137,7 +130,7 @@ class DnsProxy(Handler):
try: try:
self.sock.send(self.request) self.sock.send(self.request)
except socket.error, e: except socket.error, e:
if e.args[0] in ssnet.NET_ERRS: if e.args[0] in [errno.ECONNREFUSED, errno.EHOSTUNREACH]:
# might have been spurious; try again. # might have been spurious; try again.
# Note: these errors sometimes are reported by recv(), # Note: these errors sometimes are reported by recv(),
# and sometimes by send(). We have to catch both. # and sometimes by send(). We have to catch both.
@ -152,7 +145,7 @@ class DnsProxy(Handler):
try: try:
data = self.sock.recv(4096) data = self.sock.recv(4096)
except socket.error, e: except socket.error, e:
if e.args[0] in ssnet.NET_ERRS: if e.args[0] in [errno.ECONNREFUSED, errno.EHOSTUNREACH]:
# might have been spurious; try again. # might have been spurious; try again.
# Note: these errors sometimes are reported by recv(), # Note: these errors sometimes are reported by recv(),
# and sometimes by send(). We have to catch both. # and sometimes by send(). We have to catch both.
@ -180,7 +173,7 @@ def main():
debug1(' %s/%d\n' % r) debug1(' %s/%d\n' % r)
# synchronization header # synchronization header
sys.stdout.write('\0\0SSHUTTLE0001') sys.stdout.write('SSHUTTLE0001')
sys.stdout.flush() sys.stdout.flush()
handlers = [] handlers = []

2
ssh.py
View File

@ -85,7 +85,7 @@ def connect(ssh_cmd, rhostport, python, stderr, options):
pycmd = "'%s' -c '%s'" % (python, pyscript) pycmd = "'%s' -c '%s'" % (python, pyscript)
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 "\"$P\" -c '%s'") % pyscript
argv = (sshl + argv = (sshl +
portl + portl +
ipv6flag + ipv6flag +

View File

@ -1,10 +1,5 @@
#!/bin/sh #!/bin/sh
EXE=$0 DIR=$(dirname "$0")
for i in 1 2 3 4 5 6 7 8 9 10; do
[ -L "$EXE" ] || break
EXE=$(readlink "$EXE")
done
DIR=$(dirname "$EXE")
if python2 -V 2>/dev/null; then if python2 -V 2>/dev/null; then
exec python2 "$DIR/main.py" python2 "$@" exec python2 "$DIR/main.py" python2 "$@"
else else

View File

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

View File

@ -40,11 +40,7 @@ cmd_to_name = {
CMD_DNS_REQ: 'DNS_REQ', CMD_DNS_REQ: 'DNS_REQ',
CMD_DNS_RESPONSE: 'DNS_RESPONSE', CMD_DNS_RESPONSE: 'DNS_RESPONSE',
} }
NET_ERRS = [errno.ECONNREFUSED, errno.ETIMEDOUT,
errno.EHOSTUNREACH, errno.ENETUNREACH,
errno.EHOSTDOWN, errno.ENETDOWN]
def _add(l, elem): def _add(l, elem):
@ -128,12 +124,6 @@ class SockWrapper:
return # already connected return # already connected
self.rsock.setblocking(False) self.rsock.setblocking(False)
debug3('%r: trying connect to %r\n' % (self, self.connect_to)) debug3('%r: trying connect to %r\n' % (self, self.connect_to))
if socket.inet_aton(self.connect_to[0])[0] == '\0':
self.seterr(Exception("Can't connect to %r: "
"IP address starts with zero\n"
% (self.connect_to,)))
self.connect_to = None
return
try: try:
self.rsock.connect(self.connect_to) self.rsock.connect(self.connect_to)
# connected successfully (Linux) # connected successfully (Linux)
@ -152,21 +142,12 @@ class SockWrapper:
debug3('%r: fixed connect result: %s\n' % (self, e)) debug3('%r: fixed connect result: %s\n' % (self, e))
if e.args[0] in [errno.EINPROGRESS, errno.EALREADY]: if e.args[0] in [errno.EINPROGRESS, errno.EALREADY]:
pass # not connected yet pass # not connected yet
elif e.args[0] == 0:
# connected successfully (weird Linux bug?)
# Sometimes Linux seems to return EINVAL when it isn't
# invalid. This *may* be caused by a race condition
# between connect() and getsockopt(SO_ERROR) (ie. it
# finishes connecting in between the two, so there is no
# longer an error). However, I'm not sure of that.
#
# I did get at least one report that the problem went away
# when we added this, however.
self.connect_to = None
elif e.args[0] == errno.EISCONN: elif e.args[0] == errno.EISCONN:
# connected successfully (BSD) # connected successfully (BSD)
self.connect_to = None self.connect_to = None
elif e.args[0] in NET_ERRS + [errno.EACCES, errno.EPERM]: elif e.args[0] in [errno.ECONNREFUSED, errno.ETIMEDOUT,
errno.EHOSTUNREACH, errno.ENETUNREACH,
errno.EACCES, errno.EPERM]:
# a "normal" kind of error # a "normal" kind of error
self.connect_to = None self.connect_to = None
self.seterr(e) self.seterr(e)

View File

@ -1,17 +1,5 @@
exec >&2 exec >&2
redo-ifchange runpython.c redo-ifchange runpython.c
ARCHES="" gcc -Wall -o $3 runpython.c \
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 \
-I/usr/include/python2.5 \ -I/usr/include/python2.5 \
-lpython2.5 -lpython2.5

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,6 @@ import my
configchange_callback = setconnect_callback = None configchange_callback = setconnect_callback = None
objc_validator = objc.signature('@@:N^@o^@')
def config_changed(): def config_changed():
@ -40,7 +39,7 @@ class SshuttleNet(NSObject):
def setSubnet_(self, v): def setSubnet_(self, v):
self._k_subnet = v self._k_subnet = v
config_changed() config_changed()
@objc_validator @objc.accessor
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
@ -50,7 +49,7 @@ class SshuttleNet(NSObject):
def setWidth_(self, v): def setWidth_(self, v):
self._k_width = v self._k_width = v
config_changed() config_changed()
@objc_validator @objc.accessor
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
@ -119,7 +118,7 @@ class SshuttleServer(NSObject):
self._k_host = v self._k_host = v
self.setTitle_(None) self.setTitle_(None)
config_changed() config_changed()
@objc_validator @objc.accessor
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('-'):

View File

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

3
version/.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,28 +0,0 @@
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

View File

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

View File

@ -1 +0,0 @@
sshuttle

View File

@ -1,40 +0,0 @@
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%% *}'"