2014-05-12 12:57:47 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# vi:si:et:sw=4:sts=4:ts=4
|
2014-09-02 22:32:44 +00:00
|
|
|
|
2014-05-17 14:26:59 +00:00
|
|
|
import socket
|
2014-05-12 12:57:47 +00:00
|
|
|
|
2016-03-14 13:31:56 +00:00
|
|
|
from zeroconf import (
|
|
|
|
get_all_addresses,
|
|
|
|
ServiceBrowser, ServiceInfo, ServiceStateChange, Zeroconf
|
|
|
|
)
|
|
|
|
from tornado.ioloop import PeriodicCallback
|
|
|
|
|
|
|
|
import settings
|
2014-05-18 03:01:24 +00:00
|
|
|
import state
|
2015-11-26 00:26:10 +00:00
|
|
|
from tor_request import get_opener
|
2016-03-14 13:31:56 +00:00
|
|
|
from utils import get_local_ipv4
|
2014-05-12 12:57:47 +00:00
|
|
|
|
2014-09-01 10:30:09 +00:00
|
|
|
import logging
|
2015-11-29 14:56:38 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
2014-05-17 14:26:59 +00:00
|
|
|
|
2016-03-14 13:31:56 +00:00
|
|
|
|
2014-05-12 12:57:47 +00:00
|
|
|
def can_connect(data):
|
|
|
|
try:
|
2015-11-26 00:26:10 +00:00
|
|
|
opener = get_opener(data['id'])
|
|
|
|
headers = {
|
|
|
|
'User-Agent': settings.USER_AGENT,
|
|
|
|
'X-Node-Protocol': settings.NODE_PROTOCOL,
|
|
|
|
'Accept-Encoding': 'gzip',
|
|
|
|
}
|
2014-05-22 14:20:40 +00:00
|
|
|
if ':' in data['host']:
|
2015-11-26 00:26:10 +00:00
|
|
|
url = 'https://[{host}]:{port}'.format(**data)
|
2014-05-22 14:20:40 +00:00
|
|
|
else:
|
2015-11-26 00:26:10 +00:00
|
|
|
url = 'https://{host}:{port}'.format(**data)
|
|
|
|
opener.addheaders = list(zip(headers.keys(), headers.values()))
|
|
|
|
opener.timeout = 1
|
|
|
|
r = opener.open(url)
|
|
|
|
version = r.headers.get('X-Node-Protocol', None)
|
|
|
|
if version != settings.NODE_PROTOCOL:
|
2016-01-23 16:49:34 +00:00
|
|
|
logger.debug('version does not match local: %s remote %s (%s)', settings.NODE_PROTOCOL, version, data['id'])
|
2015-11-26 00:26:10 +00:00
|
|
|
return False
|
|
|
|
c = r.read()
|
2014-05-12 12:57:47 +00:00
|
|
|
return True
|
|
|
|
except:
|
2015-12-01 10:51:58 +00:00
|
|
|
pass
|
2016-01-24 09:13:03 +00:00
|
|
|
#logger.debug('failed to connect to local node %s', data, exc_info=True)
|
2014-05-12 12:57:47 +00:00
|
|
|
return False
|
|
|
|
|
2016-03-14 13:31:56 +00:00
|
|
|
class LocalNodes(dict):
|
|
|
|
service_type = '_oml._tcp.local.'
|
|
|
|
local_info = None
|
|
|
|
local_ips = None
|
2016-01-02 09:36:48 +00:00
|
|
|
|
2016-03-14 13:31:56 +00:00
|
|
|
def __init__(self):
|
|
|
|
if not settings.server.get('localnode_discovery'):
|
2014-05-14 23:28:49 +00:00
|
|
|
return
|
2016-03-14 13:31:56 +00:00
|
|
|
self.setup()
|
|
|
|
self._ip_changed = PeriodicCallback(self._update_if_ip_changed, 60000)
|
|
|
|
|
|
|
|
def setup(self):
|
|
|
|
self.local_ips = sorted(get_all_addresses(socket.AF_INET))
|
|
|
|
self.zeroconf = Zeroconf()
|
|
|
|
self.register_service()
|
|
|
|
self.browse()
|
|
|
|
|
|
|
|
def _update_if_ip_changed(self):
|
|
|
|
local_ips = sorted(get_all_addresses(socket.AF_INET))
|
|
|
|
if local_ips != self.local_ips:
|
|
|
|
self.close()
|
|
|
|
self.setup()
|
|
|
|
|
|
|
|
def browse(self):
|
|
|
|
self.browser = ServiceBrowser(self.zeroconf, self.service_type, handlers=[self.on_service_state_change])
|
|
|
|
|
|
|
|
def register_service(self):
|
|
|
|
if self.local_info:
|
|
|
|
self.zeroconf.unregister_service(self.local_info)
|
|
|
|
self.local_info = None
|
|
|
|
local_ip = get_local_ipv4()
|
|
|
|
if local_ip:
|
|
|
|
local_name = socket.gethostname().partition('.')[0] + '.local.'
|
|
|
|
|
|
|
|
port = settings.server['node_port']
|
|
|
|
desc = {
|
|
|
|
'username': settings.preferences.get('username', 'anonymous'),
|
|
|
|
}
|
|
|
|
self.local_info = ServiceInfo(self.service_type,
|
|
|
|
'%s.%s' % (settings.USER_ID, self.service_type),
|
|
|
|
socket.inet_aton(local_ip), port, 0, 0,
|
|
|
|
desc, local_name)
|
|
|
|
self.zeroconf.register_service(self.local_info)
|
|
|
|
|
|
|
|
def __del__(self):
|
|
|
|
self.close()
|
|
|
|
|
|
|
|
def close(self):
|
|
|
|
if self.local_info:
|
|
|
|
self.zeroconf.unregister_service(self.local_info)
|
|
|
|
self.local_info = None
|
|
|
|
if self.zeroconf:
|
2014-09-01 10:30:09 +00:00
|
|
|
try:
|
2016-03-14 13:31:56 +00:00
|
|
|
self.zeroconf.close()
|
2014-09-01 10:30:09 +00:00
|
|
|
except:
|
2016-03-14 13:31:56 +00:00
|
|
|
logger.debug('exception closing zeroconf', exc_info=True)
|
|
|
|
self.zeroconf = None
|
|
|
|
for id in list(self):
|
|
|
|
self.pop(id, None)
|
|
|
|
|
|
|
|
def on_service_state_change(self, zeroconf, service_type, name, state_change):
|
|
|
|
id = name.split('.')[0]
|
|
|
|
if id == settings.USER_ID:
|
2014-05-24 09:39:45 +00:00
|
|
|
return
|
2016-03-14 13:31:56 +00:00
|
|
|
if state_change is ServiceStateChange.Added:
|
|
|
|
info = zeroconf.get_service_info(service_type, name)
|
|
|
|
if info:
|
|
|
|
self[id] = {
|
|
|
|
'id': id,
|
|
|
|
'host': socket.inet_ntoa(info.address),
|
|
|
|
'port': info.port
|
|
|
|
}
|
|
|
|
if info.properties:
|
|
|
|
for key, value in info.properties.items():
|
|
|
|
key = key.decode()
|
|
|
|
self[id][key] = value.decode()
|
|
|
|
logger.debug('add localnode: %s', self[id])
|
|
|
|
if state.tasks:
|
|
|
|
state.tasks.queue('addlocalinfo', self[id])
|
|
|
|
elif state_change is ServiceStateChange.Removed:
|
|
|
|
logger.debug('remove localnode: %s', id)
|
|
|
|
self.pop(id, None)
|
|
|
|
if state.tasks:
|
|
|
|
state.tasks.queue('removelocalinfo', id)
|
2014-09-01 10:30:09 +00:00
|
|
|
|
2014-05-24 09:39:45 +00:00
|
|
|
def get(self, user_id):
|
2016-03-14 13:31:56 +00:00
|
|
|
if user_id in self:
|
|
|
|
if can_connect(self[user_id]):
|
|
|
|
return self[user_id]
|