mirror of
https://github.com/sshuttle/sshuttle.git
synced 2025-07-05 17:20:35 +02:00
Compare commits
1 Commits
sshuttle-0
...
dns
Author | SHA1 | Date | |
---|---|---|---|
9915d736fe |
3
Documentation/.gitignore
vendored
3
Documentation/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
*.8
|
|
||||||
/md-to-man
|
|
||||||
/*.md.tmp
|
|
@ -1,5 +0,0 @@
|
|||||||
/bin/ls *.md |
|
|
||||||
sed 's/\.md/.8/' |
|
|
||||||
xargs redo-ifchange
|
|
||||||
|
|
||||||
redo-always
|
|
@ -1 +0,0 @@
|
|||||||
rm -f *~ .*~ *.8 t/*.8 md-to-man *.tmp t/*.tmp
|
|
@ -1,2 +0,0 @@
|
|||||||
redo-ifchange md-to-man $2.md.tmp
|
|
||||||
. ./md-to-man $1 $2 $3
|
|
@ -1,3 +0,0 @@
|
|||||||
redo-ifchange ../version/vars $2.md
|
|
||||||
. ../version/vars
|
|
||||||
sed -e "s/%VERSION%/$TAG/" -e "s/%DATE%/$DATE/" $2.md
|
|
@ -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
|
|
@ -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)
|
|
14
README.md
14
README.md
@ -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
4
all.do
@ -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"
|
||||||
|
2
clean.do
2
clean.do
@ -1,2 +1,2 @@
|
|||||||
redo ui-macos/clean Documentation/clean
|
redo ui-macos/clean
|
||||||
rm -f *~ */*~ .*~ */.*~ *.8 *.tmp */*.tmp *.pyc */*.pyc
|
rm -f *~ */*~ .*~ */.*~ *.8 *.tmp */*.tmp *.pyc */*.pyc
|
||||||
|
217
client.py
217
client.py
@ -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:
|
||||||
|
@ -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
115
do
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
112
firewall.py
112
firewall.py
@ -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,15 +150,11 @@ 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)
|
rv = _sysctl_set(name, val)
|
||||||
if rv != 0:
|
if rv==0 and permanent:
|
||||||
return FAILED
|
|
||||||
if permanent:
|
|
||||||
debug1('>> ...saving permanently in /etc/sysctl.conf\n')
|
debug1('>> ...saving permanently in /etc/sysctl.conf\n')
|
||||||
f = open('/etc/sysctl.conf', 'a')
|
f = open('/etc/sysctl.conf', 'a')
|
||||||
f.write('\n'
|
f.write('\n'
|
||||||
@ -214,7 +163,7 @@ def sysctl_set(name, val, permanent=False):
|
|||||||
f.close()
|
f.close()
|
||||||
else:
|
else:
|
||||||
_changedctls.append(name)
|
_changedctls.append(name)
|
||||||
return SUCCESS
|
return True
|
||||||
|
|
||||||
|
|
||||||
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,11 +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)
|
|
||||||
|
|
||||||
# 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()
|
||||||
|
@ -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:
|
||||||
|
26
main.py
Executable file → Normal file
26
main.py
Executable file → Normal file
@ -54,6 +54,8 @@ 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)
|
||||||
@ -63,7 +65,6 @@ 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)
|
||||||
@ -73,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:
|
||||||
@ -115,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)
|
||||||
|
@ -130,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.
|
||||||
@ -145,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.
|
||||||
@ -173,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
2
ssh.py
@ -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 +
|
||||||
|
7
sshuttle
7
sshuttle
@ -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
|
||||||
|
@ -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
|
||||||
|
|
25
ssnet.py
25
ssnet.py
@ -42,10 +42,6 @@ cmd_to_name = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NET_ERRS = [errno.ECONNREFUSED, errno.ETIMEDOUT,
|
|
||||||
errno.EHOSTUNREACH, errno.ENETUNREACH,
|
|
||||||
errno.EHOSTDOWN, errno.ENETDOWN]
|
|
||||||
|
|
||||||
|
|
||||||
def _add(l, elem):
|
def _add(l, elem):
|
||||||
if not elem in l:
|
if not elem in l:
|
||||||
@ -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)
|
||||||
|
@ -1,15 +1,5 @@
|
|||||||
exec >&2
|
exec >&2
|
||||||
redo-ifchange runpython.c
|
redo-ifchange runpython.c
|
||||||
ARCHES=""
|
gcc -Wall -o $3 runpython.c \
|
||||||
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
|
|
||||||
printf "\n"
|
|
||||||
gcc $ARCHES \
|
|
||||||
-Wall -o $3 runpython.c \
|
|
||||||
-I/usr/include/python2.5 \
|
-I/usr/include/python2.5 \
|
||||||
-lpython2.5
|
-lpython2.5
|
||||||
|
@ -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)
|
||||||
|
@ -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/
|
||||||
|
@ -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/
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
redo-ifchange $2.xib
|
redo-ifchange $1.xib
|
||||||
ibtool --compile $3 $2.xib
|
ibtool --compile $3 $1.xib
|
||||||
|
@ -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)
|
||||||
|
@ -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('-'):
|
||||||
|
1
version/.gitattributes
vendored
1
version/.gitattributes
vendored
@ -1 +0,0 @@
|
|||||||
gitvars.pre export-subst
|
|
3
version/.gitignore
vendored
3
version/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
/vars
|
|
||||||
/gitvars
|
|
||||||
/_version.py
|
|
@ -1 +0,0 @@
|
|||||||
from _version import COMMIT, TAG, DATE
|
|
@ -1,3 +0,0 @@
|
|||||||
redo-ifchange vars
|
|
||||||
cat vars
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
redo-ifchange vars _version.py
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
rm -f *~ .*~ *.pyc _version.py vars gitvars
|
|
||||||
|
|
||||||
|
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||||||
$Format:%H$
|
|
||||||
$Format:%d$
|
|
||||||
$Format:%ci$
|
|
@ -1 +0,0 @@
|
|||||||
sshuttle
|
|
@ -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%% *}'"
|
|
Reference in New Issue
Block a user