mirror of
https://github.com/sshuttle/sshuttle.git
synced 2024-11-29 11:23:09 +01:00
da774f3f46
This should hopefully let us run even on python 2.3 on really old servers.
278 lines
7.0 KiB
Python
278 lines
7.0 KiB
Python
import time, socket, re, select, errno
|
|
if not globals().get('skip_imports'):
|
|
import compat.ssubprocess as ssubprocess
|
|
import helpers
|
|
from helpers import *
|
|
|
|
POLL_TIME = 60*15
|
|
NETSTAT_POLL_TIME = 30
|
|
CACHEFILE=os.path.expanduser('~/.sshuttle.hosts')
|
|
|
|
|
|
_nmb_ok = True
|
|
_smb_ok = True
|
|
hostnames = {}
|
|
queue = {}
|
|
null = open('/dev/null', 'rb+')
|
|
|
|
|
|
def _is_ip(s):
|
|
return re.match(r'\d+\.\d+\.\d+\.\d+$', s)
|
|
|
|
|
|
def write_host_cache():
|
|
tmpname = '%s.%d.tmp' % (CACHEFILE, os.getpid())
|
|
try:
|
|
f = open(tmpname, 'wb')
|
|
for name,ip in sorted(hostnames.items()):
|
|
f.write('%s,%s\n' % (name, ip))
|
|
f.close()
|
|
os.rename(tmpname, CACHEFILE)
|
|
finally:
|
|
try:
|
|
os.unlink(tmpname)
|
|
except:
|
|
pass
|
|
|
|
|
|
def read_host_cache():
|
|
try:
|
|
f = open(CACHEFILE)
|
|
except IOError, e:
|
|
if e.errno == errno.ENOENT:
|
|
return
|
|
else:
|
|
raise
|
|
for line in f:
|
|
words = line.strip().split(',')
|
|
if len(words) == 2:
|
|
(name,ip) = words
|
|
name = re.sub(r'[^-\w]', '-', name).strip()
|
|
ip = re.sub(r'[^0-9.]', '', ip).strip()
|
|
if name and ip:
|
|
found_host(name, ip)
|
|
|
|
|
|
def found_host(hostname, ip):
|
|
hostname = re.sub(r'\..*', '', hostname)
|
|
hostname = re.sub(r'[^-\w]', '_', hostname)
|
|
if (ip.startswith('127.') or ip.startswith('255.')
|
|
or hostname == 'localhost'):
|
|
return
|
|
oldip = hostnames.get(hostname)
|
|
if oldip != ip:
|
|
hostnames[hostname] = ip
|
|
debug1('Found: %s: %s\n' % (hostname, ip))
|
|
sys.stdout.write('%s,%s\n' % (hostname, ip))
|
|
write_host_cache()
|
|
|
|
|
|
def _check_etc_hosts():
|
|
debug2(' > hosts\n')
|
|
for line in open('/etc/hosts'):
|
|
line = re.sub(r'#.*', '', line)
|
|
words = line.strip().split()
|
|
if not words:
|
|
continue
|
|
ip = words[0]
|
|
names = words[1:]
|
|
if _is_ip(ip):
|
|
debug3('< %s %r\n' % (ip, names))
|
|
for n in names:
|
|
check_host(n)
|
|
found_host(n, ip)
|
|
|
|
|
|
def _check_revdns(ip):
|
|
debug2(' > rev: %s\n' % ip)
|
|
try:
|
|
r = socket.gethostbyaddr(ip)
|
|
debug3('< %s\n' % r[0])
|
|
check_host(r[0])
|
|
found_host(r[0], ip)
|
|
except socket.herror, e:
|
|
pass
|
|
|
|
|
|
def _check_dns(hostname):
|
|
debug2(' > dns: %s\n' % hostname)
|
|
try:
|
|
ip = socket.gethostbyname(hostname)
|
|
debug3('< %s\n' % ip)
|
|
check_host(ip)
|
|
found_host(hostname, ip)
|
|
except socket.gaierror, e:
|
|
pass
|
|
|
|
|
|
def _check_netstat():
|
|
debug2(' > netstat\n')
|
|
argv = ['netstat', '-n']
|
|
try:
|
|
p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null)
|
|
content = p.stdout.read()
|
|
p.wait()
|
|
except OSError, e:
|
|
log('%r failed: %r\n' % (argv, e))
|
|
return
|
|
|
|
for ip in re.findall(r'\d+\.\d+\.\d+\.\d+', content):
|
|
debug3('< %s\n' % ip)
|
|
check_host(ip)
|
|
|
|
|
|
def _check_smb(hostname):
|
|
return
|
|
global _smb_ok
|
|
if not _smb_ok:
|
|
return
|
|
argv = ['smbclient', '-U', '%', '-L', hostname]
|
|
debug2(' > smb: %s\n' % hostname)
|
|
try:
|
|
p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null)
|
|
lines = p.stdout.readlines()
|
|
p.wait()
|
|
except OSError, e:
|
|
log('%r failed: %r\n' % (argv, e))
|
|
_smb_ok = False
|
|
return
|
|
|
|
lines.reverse()
|
|
|
|
# junk at top
|
|
while lines:
|
|
line = lines.pop().strip()
|
|
if re.match(r'Server\s+', line):
|
|
break
|
|
|
|
# server list section:
|
|
# Server Comment
|
|
# ------ -------
|
|
while lines:
|
|
line = lines.pop().strip()
|
|
if not line or re.match(r'-+\s+-+', line):
|
|
continue
|
|
if re.match(r'Workgroup\s+Master', line):
|
|
break
|
|
words = line.split()
|
|
hostname = words[0].lower()
|
|
debug3('< %s\n' % hostname)
|
|
check_host(hostname)
|
|
|
|
# workgroup list section:
|
|
# Workgroup Master
|
|
# --------- ------
|
|
while lines:
|
|
line = lines.pop().strip()
|
|
if re.match(r'-+\s+', line):
|
|
continue
|
|
if not line:
|
|
break
|
|
words = line.split()
|
|
(workgroup, hostname) = (words[0].lower(), words[1].lower())
|
|
debug3('< group(%s) -> %s\n' % (workgroup, hostname))
|
|
check_host(hostname)
|
|
check_workgroup(workgroup)
|
|
|
|
if lines:
|
|
assert(0)
|
|
|
|
|
|
def _check_nmb(hostname, is_workgroup, is_master):
|
|
return
|
|
global _nmb_ok
|
|
if not _nmb_ok:
|
|
return
|
|
argv = ['nmblookup'] + ['-M']*is_master + ['--', hostname]
|
|
debug2(' > n%d%d: %s\n' % (is_workgroup, is_master, hostname))
|
|
try:
|
|
p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null)
|
|
lines = p.stdout.readlines()
|
|
rv = p.wait()
|
|
except OSError, e:
|
|
log('%r failed: %r\n' % (argv, e))
|
|
_nmb_ok = False
|
|
return
|
|
if rv:
|
|
log('%r returned %d\n' % (argv, rv))
|
|
return
|
|
for line in lines:
|
|
m = re.match(r'(\d+\.\d+\.\d+\.\d+) (\w+)<\w\w>\n', line)
|
|
if m:
|
|
g = m.groups()
|
|
(ip, name) = (g[0], g[1].lower())
|
|
debug3('< %s -> %s\n' % (name, ip))
|
|
if is_workgroup:
|
|
_enqueue(_check_smb, ip)
|
|
else:
|
|
found_host(name, ip)
|
|
check_host(name)
|
|
|
|
|
|
def check_host(hostname):
|
|
if _is_ip(hostname):
|
|
_enqueue(_check_revdns, hostname)
|
|
else:
|
|
_enqueue(_check_dns, hostname)
|
|
_enqueue(_check_smb, hostname)
|
|
_enqueue(_check_nmb, hostname, False, False)
|
|
|
|
|
|
def check_workgroup(hostname):
|
|
_enqueue(_check_nmb, hostname, True, False)
|
|
_enqueue(_check_nmb, hostname, True, True)
|
|
|
|
|
|
def _enqueue(op, *args):
|
|
t = (op,args)
|
|
if queue.get(t) == None:
|
|
queue[t] = 0
|
|
|
|
|
|
def _stdin_still_ok(timeout):
|
|
r,w,x = select.select([sys.stdin.fileno()], [], [], timeout)
|
|
if r:
|
|
b = os.read(sys.stdin.fileno(), 4096)
|
|
if not b:
|
|
return False
|
|
return True
|
|
|
|
|
|
def hw_main(seed_hosts):
|
|
if helpers.verbose >= 2:
|
|
helpers.logprefix = 'HH: '
|
|
else:
|
|
helpers.logprefix = 'hostwatch: '
|
|
|
|
read_host_cache()
|
|
|
|
_enqueue(_check_etc_hosts)
|
|
_enqueue(_check_netstat)
|
|
check_host('localhost')
|
|
check_host(socket.gethostname())
|
|
check_workgroup('workgroup')
|
|
check_workgroup('-')
|
|
for h in seed_hosts:
|
|
check_host(h)
|
|
|
|
while 1:
|
|
now = time.time()
|
|
for t,last_polled in queue.items():
|
|
(op,args) = t
|
|
if not _stdin_still_ok(0):
|
|
break
|
|
maxtime = POLL_TIME
|
|
if op == _check_netstat:
|
|
maxtime = NETSTAT_POLL_TIME
|
|
if now - last_polled > maxtime:
|
|
queue[t] = time.time()
|
|
op(*args)
|
|
try:
|
|
sys.stdout.flush()
|
|
except IOError:
|
|
break
|
|
|
|
# FIXME: use a smarter timeout based on oldest last_polled
|
|
if not _stdin_still_ok(1):
|
|
break
|