Compare commits

..

1 Commits

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

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

213
client.py
View File

@ -175,67 +175,60 @@ class FirewallClient:
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:
@ -257,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:
@ -314,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)
@ -334,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:
@ -379,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:

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 "$base" "$ext" "$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 sys, os, socket, errno import sys, os, socket
logprefix = '' logprefix = ''
verbose = 0 verbose = 0

18
main.py
View 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)
@ -110,12 +112,26 @@ 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),

View File

@ -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
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

@ -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,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

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('-'):