def rpc_find_node(self, sender, public_key, nodeid, key): log.info("finding neighbors of %i in local table", int(nodeid.hex(), 16)) source = Node(nodeid, sender[0], sender[1], public_key=public_key) self.welcomeIfNewNode(source) node = Node(key) neighbors = self.router.findNeighbors(node, exclude=source) return list(map(tuple, neighbors))
def rpc_find_value(self, sender, public_key, nodeid, key): source = Node(nodeid, sender[0], sender[1], public_key=public_key) self.welcomeIfNewNode(source) value = self.storage.get(key, None) if value is None: return self.rpc_find_node(sender, public_key, nodeid, key) return {'value': value}
def getNodeList(self): """ Get the node list in the response. If there's no value, this should be set. """ nodelist = self.response[1] or [] return [Node(*nodeple) for nodeple in nodelist]
def setUp(self): self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) self.a = genkeys( '06391888e37a48cef1ded85a375490df4f9b2c74f7723e88c954a055f3d2685a') self.b = genkeys( '91f7021a9e8c65ca873747ae24de08e0a7acf58159a8aa6548910fe152dab3d8') self.evil = genkeys( 'c5cb6d3ac7d644df8c72b613d57e4c47df6107989e584863b86bde47df704464') self.off = genkeys( '8ddaf072b9108444e189773e2ddcb4cbd2a76bbf3db448e55d0bfc131409a197') self.a_net = Network(sk=self.a['sk'], network_port=13321, keyname='a', wipe_certs=True, loop=self.loop, daemon=DaemonMock()) self.b_net = Network(sk=self.b['sk'], network_port=14321, keyname='b', wipe_certs=True, loop=self.loop, daemon=DaemonMock()) self.evil_net = Network(sk=self.evil['sk'], network_port=15321, keyname='evil', wipe_certs=True, loop=self.loop, daemon=DaemonMock()) self.off_node = Node(digest(self.off['vk']), ip='127.0.0.1', port=16321, public_key=self.off['curve_key'])
def rpc_store(self, sender, public_key, nodeid, key, value): source = Node(nodeid, sender[0], sender[1], public_key=public_key) self.welcomeIfNewNode(source) log.debug("got a store request from %s, storing '%s'='%s'", sender, key.hex(), value) self.storage[key] = value return True
def __init__(self, ksize=20, alpha=3, node_id=None, discovery_mode='neighborhood', loop=None, max_peers=64, network_port=None, public_ip=None, event_sock=None, *args, **kwargs): """ Create a server instance. This will start listening on the given port. Args: ksize (int): The k parameter from the paper alpha (int): The ALPHA parameter from the paper node_id: The id for this node on the network. """ self.loop = loop if loop else asyncio.get_event_loop() asyncio.set_event_loop(self.loop) self.vkcache = {} self.ksize = ksize self.alpha = alpha self.transport = None self.protocol = None self.refresh_loop = None self.save_state_loop = None self.event_sock = event_sock self.max_peers = max_peers self.network_port = network_port self.heartbeat_port = self.network_port+HEARTBEAT_PORT_OFFSET self.ironhouse = Ironhouse(auth_port=self.network_port+AUTH_PORT_OFFSET, *args, **kwargs) self.node = Node( node_id=digest(self.ironhouse.vk), public_key=self.ironhouse.public_key, ip=public_ip or os.getenv('HOST_IP', '127.0.0.1'), port=self.network_port ) self.setup_stethoscope() self.ironhouse.setup_secure_server() self.listen() self.saveStateRegularly('state.tmp')
async def _lookup_ip(self, cmd, url, vk, *args, **kwargs): ip, node = None, None try: node, cached = await self.dht.network.lookup_ip(vk) # NOTE while secure, this is a more loose connection policy self.log.fatal('{} resolves for {}'.format(os.getenv('HOST_IP'), node)) if node and not cached: ip = node.ip if type(node) == Node else node.split(':')[0] public_key = self.dht.network.ironhouse.vk2pk(vk) authorization = await self.dht.network.ironhouse.authenticate( public_key, ip) self.log.fatal('{} -> {} is {}'.format(os.getenv('HOST_IP'), node, authorization)) if authorization != 'authorized': node = None else: n = Node(node_id=digest(vk), public_key=public_key, ip=ip, port=self.dht.network.network_port) self.dht.network.protocol.router.addContact(n) self.dht.network.connect_to_neighbor(n) self.log.fatal([ item[0] for item in self.dht.network.bootstrappableNeighbors() ]) except Exception as e: delim_line = '!' * 64 err_msg = '\n\n' + delim_line + '\n' + delim_line err_msg += '\n ERROR CAUGHT IN LOOKUP FUNCTION {}\ncalled \w args={}\nand kwargs={}\n'\ .format(args, kwargs) err_msg += '\nError Message: ' err_msg += '\n\n{}'.format(traceback.format_exc()) err_msg += '\n' + delim_line + '\n' + delim_line self.log.error(err_msg) if node is None: kwargs = cmd.kwargs callback = ReactorCommand.create_callback( callback=StateInput.LOOKUP_FAILED, **kwargs) self.log.debug( "Sending callback failure to mainthread {}".format(callback)) self.socket.send(callback.serialize()) # TODO -- send callback to SM saying hey i couldnt lookup this vk return # Send interpolated command back through pipeline ip = node.ip if type(node) == Node else node new_url = IPUtils.interpolate_url(url, ip) kwargs = cmd.kwargs kwargs['url'] = new_url new_cmd = ReactorCommand.create_cmd(envelope=cmd.envelope, **kwargs) self._execute_cmd(new_cmd)
async def bootstrap_node(self, addr): result = await self.protocol.ping(addr, self.node.public_key, self.node.id) if result[0]: node_id, public_key = result[1] node = Node(node_id, ip=addr[0], port=addr[1], public_key=public_key) authorized = await self.authenticate(node) if authorized == True: return node return None
def mknode(node_id=None, ip=None, port=None, intid=None): """ Make a node. Created a random id if not specified. """ if intid is not None: node_id = pack('>l', intid) if not node_id: randbits = str(random.getrandbits(255)) node_id = hashlib.sha1(randbits.encode()).digest() return Node(node_id, ip, port)
async def _refresh_table(self): """ Refresh buckets that haven't had any lookups in the last hour (per section 2.3 of the paper). """ log.debug("Refreshing routing table") ds = [] for node_id in self.protocol.getRefreshIDs(): node = Node(node_id=node_id) nearest = self.protocol.router.findNeighbors(node, self.alpha) spider = NodeSpiderCrawl(self.protocol, node, nearest, self.ksize, self.alpha) ds.append(spider.find()) # do our crawling await asyncio.gather(*ds)
def welcomeIfNewNode(self, node): """ Given a new node, send it all the keys/values it should be storing, then add it to the routing table. @param node: A new node that just joined (or that we just found out about). Process: For each key in storage, get k closest nodes. If newnode is closer than the furtherst in that list, and the node for this network is closer than the closest in that list, then store the key/value on the new node (per section 2.5 of the paper) """ if not node: log.warning('This node is not welcomed.') return if not self.router.isNewNode(node): log.debug( "Skipping node {} that already exists in routing table".format( node)) return log.info("never seen %s before, adding to router", node) for key, value in self.storage.items(): keynode = Node(digest(key)) neighbors = self.router.findNeighbors(keynode) if len(neighbors) > 0: last = neighbors[-1].distanceTo(keynode) newNodeClose = node.distanceTo(keynode) < last first = neighbors[0].distanceTo(keynode) thisNodeClosest = self.sourceNode.distanceTo(keynode) < first if len(neighbors) == 0 or (newNodeClose and thisNodeClosest): asyncio.ensure_future(self.callStore(node, key, value)) self.router.addContact(node) self.network.connect_to_neighbor(node)
def rpc_ping(self, sender, public_key, nodeid): source = Node(nodeid, sender[0], sender[1], public_key=public_key) self.welcomeIfNewNode(source) return self.sourceNode.id, self.sourceNode.public_key
def __init__(self, sourceID, ksize=20): self.router = RoutingTable(self, ksize, Node(sourceID)) self.storage = {} self.sourceID = sourceID