broadcast mdns on each local interface with the matching local ip
This commit is contained in:
parent
6f5deada62
commit
ad03b7ef2e
2 changed files with 47 additions and 79 deletions
|
@ -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:
|
||||||
|
|
52
oml/utils.py
52
oml/utils.py
|
@ -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('.')]
|
||||||
|
|
Loading…
Reference in a new issue