openmedialibrary/oml/localnodes.py

145 lines
4.4 KiB
Python
Raw Normal View History

2014-05-12 12:57:47 +00:00
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
2014-05-16 08:06:11 +00:00
from __future__ import division
2014-05-12 12:57:47 +00:00
import json
2014-05-17 14:26:59 +00:00
import logging
import socket
2014-05-12 12:57:47 +00:00
import struct
2014-05-16 17:08:10 +00:00
import subprocess
2014-05-17 14:26:59 +00:00
import sys
import thread
from threading import Thread
2014-05-12 12:57:47 +00:00
2014-05-13 10:58:49 +00:00
from settings import preferences, server, USER_ID, sk
2014-05-17 22:18:32 +00:00
from utils import valid, get_public_ipv6
2014-05-12 12:57:47 +00:00
2014-05-17 14:26:59 +00:00
logger = logging.getLogger('oml.localnodes')
2014-05-12 12:57:47 +00:00
def can_connect(data):
try:
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s.settimeout(1)
s.connect((data['host'], data['port']))
s.close()
return True
except:
pass
return False
2014-05-16 17:08:10 +00:00
def get_interface():
interface = ''
if sys.platform == 'darwin':
2014-05-17 00:14:15 +00:00
#cmd = ['/usr/sbin/netstat', '-rn']
2014-05-16 17:08:10 +00:00
cmd = ['/sbin/route', '-n', 'get', 'default']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
interface = [[p.strip() for p in s.split(':', 1)] for s in stdout.strip().split('\n') if 'interface' in s]
if interface:
interface = '%%%s' % interface[0][1]
else:
interface = ''
return interface
2014-05-12 12:57:47 +00:00
class LocalNodes(Thread):
_active = True
_nodes = {}
_BROADCAST = "ff02::1"
_PORT = 9851
2014-05-12 23:43:27 +00:00
TTL = 1
2014-05-12 12:57:47 +00:00
def __init__(self, app):
self._app = app
Thread.__init__(self)
2014-05-14 23:28:49 +00:00
if not server['localnode_discovery']:
return
2014-05-12 12:57:47 +00:00
self.daemon = True
self.start()
def send(self):
2014-05-14 23:28:49 +00:00
if not server['localnode_discovery']:
return
2014-05-12 12:57:47 +00:00
message = json.dumps({
'username': preferences.get('username', 'anonymous'),
'host': self.host,
'port': server['node_port'],
2014-05-14 09:57:11 +00:00
'cert': server['cert']
2014-05-12 12:57:47 +00:00
})
2014-05-13 10:58:49 +00:00
sig = sk.sign(message, encoding='base64')
packet = json.dumps([sig, USER_ID, message])
2014-05-16 17:08:10 +00:00
ttl = struct.pack('@i', self.TTL)
address = self._BROADCAST + get_interface()
addrs = socket.getaddrinfo(address, self._PORT, socket.AF_INET6,socket.SOCK_DGRAM)
addr = addrs[0]
(family, socktype, proto, canonname, sockaddr) = addr
s = socket.socket(family, socktype, proto)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, ttl)
s.sendto(packet + '\0', sockaddr)
s.close()
2014-05-12 12:57:47 +00:00
def receive(self):
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', self._PORT))
group_bin = socket.inet_pton(socket.AF_INET6, self._BROADCAST) + '\0'*4
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group_bin)
while self._active:
data, addr = s.recvfrom(1024)
while data[-1] == '\0':
data = data[:-1] # Strip trailing \0's
2014-05-13 10:58:49 +00:00
data = self.verify(data)
2014-05-12 12:57:47 +00:00
if data:
2014-05-17 00:14:15 +00:00
#fixme use local link address
#print addr
2014-05-14 09:57:11 +00:00
if data['id'] != USER_ID:
if data['id'] not in self._nodes:
thread.start_new_thread(self.new_node, (data, ))
#else:
# print 'UPDATE NODE', data
self._nodes[data['id']] = data
2014-05-12 12:57:47 +00:00
2014-05-13 10:58:49 +00:00
def verify(self, data):
2014-05-12 12:57:47 +00:00
try:
2014-05-13 10:58:49 +00:00
packet = json.loads(data)
2014-05-12 12:57:47 +00:00
except:
return None
2014-05-13 10:58:49 +00:00
if len(packet) == 3:
sig, user_id, data = packet
if valid(user_id, data, sig):
message = json.loads(data)
message['id'] = user_id
2014-05-14 09:57:11 +00:00
for key in ['id', 'username', 'host', 'port', 'cert']:
2014-05-13 10:58:49 +00:00
if key not in message:
return None
return message
2014-05-12 12:57:47 +00:00
def get(self, user_id):
if user_id in self._nodes:
if can_connect(self._nodes[user_id]):
return self._nodes[user_id]
def new_node(self, data):
2014-05-17 14:26:59 +00:00
logger.debug('NEW NODE %s', data)
2014-05-12 12:57:47 +00:00
if can_connect(data):
self._nodes[data['id']] = data
with self._app.app_context():
import user.models
u = user.models.User.get_or_create(data['id'])
u.info['username'] = data['username']
u.info['local'] = data
u.save()
self.send()
def run(self):
2014-05-16 17:08:10 +00:00
self.host = get_public_ipv6()
self.send()
2014-05-12 12:57:47 +00:00
self.receive()
def join(self):
self._active = False
return Thread.join(self)