broadcast mdns on each local interface with the matching local ip

This commit is contained in:
j 2016-03-24 15:15:15 +01:00
parent 6f5deada62
commit ad03b7ef2e
2 changed files with 47 additions and 79 deletions

View file

@ -2,10 +2,9 @@
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
import socket import socket
import time
import netifaces
from zeroconf import ( from zeroconf import (
get_all_addresses,
ServiceBrowser, ServiceInfo, ServiceStateChange, Zeroconf ServiceBrowser, ServiceInfo, ServiceStateChange, Zeroconf
) )
from tornado.ioloop import PeriodicCallback from tornado.ioloop import PeriodicCallback
@ -13,7 +12,6 @@ from tornado.ioloop import PeriodicCallback
import settings import settings
import state import state
from tor_request import get_opener from tor_request import get_opener
from utils import get_local_ipv4
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -45,6 +43,14 @@ def can_connect(data):
#logger.debug('failed to connect to local node %s', data, exc_info=True) #logger.debug('failed to connect to local node %s', data, exc_info=True)
return False return False
def get_broadcast_interfaces():
return list(set(
addr['addr']
for iface in netifaces.interfaces()
for addr in netifaces.ifaddresses(iface).get(socket.AF_INET, [])
if addr.get('netmask') != '255.255.255.255' and addr.get('broadcast')
))
class LocalNodes(dict): class LocalNodes(dict):
service_type = '_oml._tcp.local.' service_type = '_oml._tcp.local.'
local_info = None local_info = None
@ -57,57 +63,71 @@ class LocalNodes(dict):
self._ip_changed = PeriodicCallback(self._update_if_ip_changed, 60000) self._ip_changed = PeriodicCallback(self._update_if_ip_changed, 60000)
def setup(self): def setup(self):
self.local_ips = sorted(get_all_addresses(socket.AF_INET)) self.local_ips = get_broadcast_interfaces()
self.zeroconf = Zeroconf() self.zeroconf = {ip: Zeroconf(interfaces=[ip]) for ip in self.local_ips}
time.sleep(0.02)
self.register_service() self.register_service()
self.browse() self.browse()
def _update_if_ip_changed(self): def _update_if_ip_changed(self):
local_ips = sorted(get_all_addresses(socket.AF_INET)) local_ips = get_broadcast_interfaces()
if local_ips != self.local_ips: if local_ips != self.local_ips:
self.close() self.close()
self.setup() self.setup()
def browse(self): def browse(self):
self.browser = ServiceBrowser(self.zeroconf, self.service_type, handlers=[self.on_service_state_change]) self.browser = {
ip: ServiceBrowser(self.zeroconf[ip], self.service_type, handlers=[self.on_service_state_change])
for ip in self.zeroconf
}
def register_service(self): def register_service(self):
if self.local_info: if self.local_info:
self.zeroconf.unregister_service(self.local_info) for local_ip, local_info in self.local_info:
self.zeroconf[local_ip].unregister_service(local_info)
self.local_info = None self.local_info = None
local_ip = get_local_ipv4()
if local_ip:
local_name = socket.gethostname().partition('.')[0] + '.local.'
port = settings.server['node_port'] local_name = socket.gethostname().partition('.')[0] + '.local.'
desc = { port = settings.server['node_port']
'username': settings.preferences.get('username', 'anonymous'), desc = {
} 'username': settings.preferences.get('username', 'anonymous')
self.local_info = ServiceInfo(self.service_type, }
'%s.%s' % (settings.USER_ID, self.service_type), self.local_info = []
socket.inet_aton(local_ip), port, 0, 0, for i, local_ip in enumerate(get_broadcast_interfaces()):
desc, local_name) if i:
self.zeroconf.register_service(self.local_info) name = '%s-%s [%s].%s' % (desc['username'], i, settings.USER_ID, self.service_type)
else:
name = '%s [%s].%s' % (desc['username'], settings.USER_ID, self.service_type)
local_info = ServiceInfo(self.service_type, name,
socket.inet_aton(local_ip), port, 0, 0, desc, local_name)
self.zeroconf[local_ip].register_service(local_info)
self.local_info.append((local_ip, local_info))
def __del__(self): def __del__(self):
self.close() self.close()
def close(self): def close(self):
if self.local_info: if self.local_info:
self.zeroconf.unregister_service(self.local_info) for local_ip, local_info in self.local_info:
try:
self.zeroconf[local_ip].unregister_service(local_info)
except:
logger.debug('exception closing zeroconf', exc_info=True)
self.local_info = None self.local_info = None
if self.zeroconf: if self.zeroconf:
try: for local_ip in self.zeroconf:
self.zeroconf.close() try:
except: self.zeroconf[local_ip].close()
logger.debug('exception closing zeroconf', exc_info=True) except:
logger.debug('exception closing zeroconf', exc_info=True)
self.zeroconf = None self.zeroconf = None
for id in list(self): for id in list(self):
self.pop(id, None) self.pop(id, None)
def on_service_state_change(self, zeroconf, service_type, name, state_change): def on_service_state_change(self, zeroconf, service_type, name, state_change):
id = name.split('.')[0] if not '[' in name:
id = name.split('.')[0]
else:
id = name.split('[')[1].split(']')[0]
if id == settings.USER_ID: if id == settings.USER_ID:
return return
if state_change is ServiceStateChange.Added: if state_change is ServiceStateChange.Added:

View file

@ -218,58 +218,6 @@ def get_interface():
interface = '' interface = ''
return interface return interface
def get_local_ipv4():
ip = None
if sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
cmd = ['/sbin/route', '-n', 'get', 'default']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
stdout = stdout.decode('utf-8')
interface = [[p.strip() for p in s.split(':', 1)]
for s in stdout.strip().split('\n') if 'interface' in s]
if interface:
interface = interface[0][1]
cmd = ['ifconfig', interface]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
stdout = stdout.decode('utf-8')
ips = [l for l in stdout.split('\n') if 'inet ' in l]
if ips:
ip = ips[0].strip().split(' ')[1]
elif sys.platform.startswith('linux'):
cmd = ['ip', 'route', 'show']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
stdout = stdout.decode('utf-8')
local = [l for l in stdout.split('\n') if 'default' in l]
if local:
dev = local[0].split(' ')[4]
local_ip = [l for l in stdout.split('\n')
if dev in l and not 'default' in l and 'src' in l]
if local_ip:
local_ip = [p for p in local_ip[0].split(' ')[1:] if '.' in p]
if local_ip:
ip = local_ip[0]
if not ip:
cmd = ['ip', 'addr', 'show']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
stdout = stdout.decode('utf-8')
parts = stdout.split(' ')
local_ip = [p for p in parts if dev in p]
if local_ip:
local_ip = re.compile('inet (\d+\.\d+\.\d+.\d+)').findall(local_ip[0])
if local_ip:
ip = local_ip[0]
if not ip:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 53))
return s.getsockname()[0]
except:
pass
return ip
def update_dict(root, data): def update_dict(root, data):
for key in data: for key in data:
keys = [part.replace('\0', '.') for part in key.replace('\\.', '\0').split('.')] keys = [part.replace('\0', '.') for part in key.replace('\\.', '\0').split('.')]