def StreamInit(self): """ Send an initial stream header """ self._owner.Connection.sendqueue = [] self.Stream = simplexml.NodeBuilder() self.Stream.dispatch = self.dispatch self.Stream._dispatch_depth = 2 self.Stream.stream_header_received = self._check_stream_start self.Stream.features = None self._metastream = Node('stream:stream') self._metastream.setNamespace(self._owner.Namespace) self._metastream.setAttr('version', '1.0') self._metastream.setAttr('xmlns:stream', NS_STREAMS) self._metastream.setAttr('to', self._owner.Server) if locale.getdefaultlocale()[0]: self._metastream.setAttr('xml:lang', locale.getdefaultlocale()[0].split('_')[0]) self._owner.send("%s%s>" % (XML_DECLARATION, str(self._metastream)[:-2]))
def StreamInit(self): """ Send an initial stream header """ self.Stream = simplexml.NodeBuilder() self.Stream.dispatch = self.dispatch self.Stream._dispatch_depth = 2 self.Stream.stream_header_received = self._check_stream_start self.Stream.features = self.old_features self._metastream = Node('stream:stream') self._metastream.setNamespace(self._owner.Namespace) self._metastream.setAttr('version', '1.0') self._metastream.setAttr('xmlns:stream', NS_STREAMS) self._metastream.setAttr('to', self._owner.Server) if locale.getdefaultlocale()[0]: self._metastream.setAttr('xml:lang', locale.getdefaultlocale()[0].split('_')[0]) self.restart = True self._owner.Connection.send_init(after_SASL=self.after_SASL)
class XMPPDispatcher(PlugIn): """ Handles XMPP stream and is the first who takes control over a fresh stanza Is plugged into NonBlockingClient but can be replugged to restart handled stream headers (used by SASL f.e.). """ def __init__(self): PlugIn.__init__(self) self.handlers = {} self._expected = {} self._defaultHandler = None self._pendingExceptions = [] self._eventHandler = None self._cycleHandlers = [] self._exported_methods=[self.RegisterHandler, self.RegisterDefaultHandler, self.RegisterEventHandler, self.UnregisterCycleHandler, self.RegisterCycleHandler, self.RegisterHandlerOnce, self.UnregisterHandler, self.RegisterProtocol, self.SendAndWaitForResponse, self.SendAndCallForResponse, self.getAnID, self.Event, self.send] # Let the dispatcher know if there is support for stream management self.sm = None # \ufddo -> \ufdef range c = u'\ufdd0' r = c.encode('utf8') while (c < u'\ufdef'): c = unichr(ord(c) + 1) r += '|' + c.encode('utf8') # \ufffe-\uffff, \u1fffe-\u1ffff, ..., \u10fffe-\u10ffff c = u'\ufffe' r += '|' + c.encode('utf8') r += '|' + unichr(ord(c) + 1).encode('utf8') while (c < u'\U0010fffe'): c = unichr(ord(c) + 0x10000) r += '|' + c.encode('utf8') r += '|' + unichr(ord(c) + 1).encode('utf8') self.invalid_chars_re = re.compile(r) def getAnID(self): global outgoingID outgoingID += 1 return repr(outgoingID) def dumpHandlers(self): """ Return set of user-registered callbacks in it's internal format. Used within the library to carry user handlers set over Dispatcher replugins """ return self.handlers def restoreHandlers(self, handlers): """ Restore user-registered callbacks structure from dump previously obtained via dumpHandlers. Used within the library to carry user handlers set over Dispatcher replugins. """ self.handlers = handlers def _init(self): """ Register default namespaces/protocols/handlers. Used internally """ # FIXME: inject dependencies, do not rely that they are defined by our # owner self.RegisterNamespace('unknown') self.RegisterNamespace(NS_STREAMS) self.RegisterNamespace(self._owner.defaultNamespace) self.RegisterProtocol('iq', Iq) self.RegisterProtocol('presence', Presence) self.RegisterProtocol('message', Message) self.RegisterDefaultHandler(self.returnStanzaHandler) self.RegisterEventHandler(self._owner._caller._event_dispatcher) self.on_responses = {} def plugin(self, owner): """ Plug the Dispatcher instance into Client class instance and send initial stream header. Used internally """ self._init() self._owner.lastErrNode = None self._owner.lastErr = None self._owner.lastErrCode = None if hasattr(self._owner, 'StreamInit'): self._owner.StreamInit() else: self.StreamInit() def plugout(self): """ Prepare instance to be destructed """ self.Stream.dispatch = None self.Stream.features = None self.Stream.destroy() self._owner = None self.Stream = None def StreamInit(self): """ Send an initial stream header """ self._owner.Connection.sendqueue = [] self.Stream = simplexml.NodeBuilder() self.Stream.dispatch = self.dispatch self.Stream._dispatch_depth = 2 self.Stream.stream_header_received = self._check_stream_start self.Stream.features = None self._metastream = Node('stream:stream') self._metastream.setNamespace(self._owner.Namespace) self._metastream.setAttr('version', '1.0') self._metastream.setAttr('xmlns:stream', NS_STREAMS) self._metastream.setAttr('to', self._owner.Server) if locale.getdefaultlocale()[0]: self._metastream.setAttr('xml:lang', locale.getdefaultlocale()[0].split('_')[0]) self._owner.send("%s%s>" % (XML_DECLARATION, str(self._metastream)[:-2])) def _check_stream_start(self, ns, tag, attrs): if ns != NS_STREAMS or tag!='stream': raise ValueError('Incorrect stream start: (%s,%s). Terminating.' % (tag, ns)) def replace_non_character(self, data): return re.sub(self.invalid_chars_re, u'\ufffd'.encode('utf-8'), data) def ProcessNonBlocking(self, data): """ Check incoming stream for data waiting :param data: data received from transports/IO sockets :return: 1) length of processed data if some data were processed; 2) '0' string if no data were processed but link is alive; 3) 0 (zero) if underlying connection is closed. """ # FIXME: # When an error occurs we disconnect the transport directly. Client's # disconnect method will never be called. # Is this intended? # also look at transports start_disconnect() data = self.replace_non_character(data) for handler in self._cycleHandlers: handler(self) if len(self._pendingExceptions) > 0: _pendingException = self._pendingExceptions.pop() raise _pendingException[0], _pendingException[1], _pendingException[2] try: self.Stream.Parse(data) # end stream:stream tag received if self.Stream and self.Stream.has_received_endtag(): self._owner.disconnect(self.Stream.streamError) return 0 except ExpatError: log.error('Invalid XML received from server. Forcing disconnect.') self._owner.Connection.disconnect() return 0 except ValueError, e: log.debug('ValueError: %s' % str(e)) self._owner.Connection.pollend() return 0 if len(self._pendingExceptions) > 0: _pendingException = self._pendingExceptions.pop() raise _pendingException[0], _pendingException[1], _pendingException[2] if len(data) == 0: return '0' return len(data)
class BOSHDispatcher(XMPPDispatcher): def PlugIn(self, owner, after_SASL=False, old_features=None): self.old_features = old_features self.after_SASL = after_SASL XMPPDispatcher.PlugIn(self, owner) def StreamInit(self): """ Send an initial stream header """ self.Stream = simplexml.NodeBuilder() self.Stream.dispatch = self.dispatch self.Stream._dispatch_depth = 2 self.Stream.stream_header_received = self._check_stream_start self.Stream.features = self.old_features self._metastream = Node('stream:stream') self._metastream.setNamespace(self._owner.Namespace) self._metastream.setAttr('version', '1.0') self._metastream.setAttr('xmlns:stream', NS_STREAMS) self._metastream.setAttr('to', self._owner.Server) if locale.getdefaultlocale()[0]: self._metastream.setAttr('xml:lang', locale.getdefaultlocale()[0].split('_')[0]) self.restart = True self._owner.Connection.send_init(after_SASL=self.after_SASL) def StreamTerminate(self): """ Send a stream terminator """ self._owner.Connection.send_terminator() def ProcessNonBlocking(self, data=None): if self.restart: fromstream = self._metastream fromstream.setAttr('from', fromstream.getAttr('to')) fromstream.delAttr('to') data = '%s%s>%s' % (XML_DECLARATION, str(fromstream)[:-2], data) self.restart = False return XMPPDispatcher.ProcessNonBlocking(self, data) def dispatch(self, stanza, session=None, direct=0): if stanza.getName() == 'body' and stanza.getNamespace() == NS_HTTP_BIND: stanza_attrs = stanza.getAttrs() if 'authid' in stanza_attrs: # should be only in init response # auth module expects id of stream in document attributes self.Stream._document_attrs['id'] = stanza_attrs['authid'] self._owner.Connection.handle_body_attrs(stanza_attrs) children = stanza.getChildren() if children: for child in children: # if child doesn't have any ns specified, simplexml (or expat) # thinks it's of parent's (BOSH body) namespace, so we have to # rewrite it to jabber:client if child.getNamespace() == NS_HTTP_BIND: child.setNamespace(self._owner.defaultNamespace) XMPPDispatcher.dispatch(self, child, session, direct) else: XMPPDispatcher.dispatch(self, stanza, session, direct)
def proccess_message(self, data, addr, running_in_thread=False): # data = json.loads(msg) if data['operation'] != 'DISCOVER': print("Data received: " + str(data)) if data['operation'] == 'DISCOVER': if data['join']: # addr = str(data['sender'][1]), int(data['sender'][2]) if addr != (self.node.ip, self.node.port): answer = { 'operation': 'CONTACT', 'sender': list(self.node), 'key': data['key'] } self.send_udp_msg(json.dumps(answer).encode(), addr) self.update(tuple(data['sender'])) print(f"{data['sender']} joined") else: if addr != (self.node.ip, self.node.port): ip, port = str(data['ip']), int(data['port']) server_addr = (self.node.ip, self.tcp_server_port) try: self.sendall(server_addr, ip, port) except: pass elif data['operation'] == 'CONTACT': contact = tuple(data['sender']) self.update(contact) self.lookup_node(self.node.ID) # A peer has to perform a method specified by other peer by RPC elif data['operation'] == 'EXECUTE': result = None if data['method'] == 'FIND_NODE': result = self.node.FIND_NODE(data['id']) elif data['method'] == 'FIND_VALUE': result = self.node.FIND_VALUE(data['id']) answer = {'operation': 'RESPONSE', 'result': result, 'key': data['key'], 'sender': [self.node.ID, self.node.ip, self.node.port] } self.sendall(answer, addr[1]) if 'sender' in data: self.update(tuple(data['sender'])) return elif data['method'] == 'PING': result = self.node.PING() elif data['method'] == 'STORE': key, value = data['store_key'], data['store_value'] publisher, sender = tuple(data['publisher']), tuple(data['sender']) result = self.node.STORE(key, value, publisher, sender, to_update=data['to_update']) elif data['method'] == 'LOOKUP': result = self.lookup_value(data["id"]) elif data['method'] == 'PUBLISH': node = self.node.asTuple() self.publish(data, node, node) if result is not None: answer = {'operation': 'RESPONSE', 'result': result, 'key': data['key'], 'sender': [self.node.ID, self.node.ip, self.node.port] } answer = utils.dumps_json(answer).encode() self.send_udp_msg(answer, addr) if 'sender' in data: self.update(tuple(data['sender'])) # A peer is requested to perform a RPC to other peer elif data['operation'] == 'RPC': msg = None if data['method'] == 'FIND_NODE': msg = utils.build_FIND_NODE_msg(data['id'], self.node) elif data['method'] == 'FIND_VALUE': msg = utils.build_FIND_VALUE_msg(data['id'], self.node) elif data['method'] == 'PING': msg = utils.build_PING_msg(self.node) elif data['method'] == 'STORE': msg = utils.build_STORE_msg(data['storeKey'], data['store_value'], self.node, self.node) if msg is not None: # The address of the remote peer wich it will be used as the target of the RPC addr = (data['ip'], data['port']) msg = utils.dumps_json(msg).encode() self.send_udp_msg(msg, addr) # The peer receives the answer of a RPC made before elif data['operation'] == 'RESPONSE': self.set_response(data['key'], data) if not Node.Equals(data['sender'], self.node): self.update(data['sender']) if running_in_thread: exit_thread()
def attend(client): while True: msg = self.recvall(client) if not msg: break # print(f'RECEIVED MSG {msg}') data = {'method': None} try: data = json.loads(msg) except: pass if data['method'] == 'PUBLISH': node = self.node.asTuple() self.publish(data, node, node) elif data['method'] == 'LOOKUP': answer = self.lookup_value(data['id']) founded, result, file_bytes = answer[0], answer[1], answer[2] if founded and result['value_type'] == 'file': if not file_bytes: file_bytes = self.recv_file() # try: client.sendall(file_bytes) # except: pass client.sendall(file_bytes) else: if not founded: result = None client.sendall(utils.dumps_json(result).encode() + b'\r\n\r\n') client.close() elif data['method'] == 'PING': client.send(b'PING') elif data['method'] == 'STORE': key, value = data['store_key'], data['store_value'] publisher, sender = tuple(data['publisher']), tuple(data['sender']) real_value = None if data['value_type'] == 'file': real_value = self.recv_file() self.node.STORE(key, value, publisher, sender, data['value_type'], real_value, data['to_update']) elif data['method'] == 'FIND_VALUE': founded, result, file_bytes = self.node.FIND_VALUE(data['id']) answer = {'operation': 'RESPONSE', 'result': (founded, result, file_bytes), 'key': data['key'], 'sender': [self.node.ID, self.node.ip, self.node.port] } # client.sendall(utils.dumps_json(answer).encode() + b'\r\n\r\n') answer = utils.dumps_json(answer) client.sendall(answer.encode() + b'\r\n\r\n') if founded and result['value_type'] == 'file': # files_bytes = utils.load_file(result['value']) self._send_file(file_bytes, data['sender'][1]) if not Node.Equals(data['sender'], self.node): self.update(data['sender']) elif data['method'] == 'UPDATE': self._update(data['store_key'], data['store_value'], data['publisher'], data['sender']) elif data['method'] == 'FIND_NODE': result = self.node.FIND_NODE(data['id']) answer = {'operation': 'RESPONSE', 'result': result, 'key': data['key'], 'sender': [self.node.ID, self.node.ip, self.node.port] } # client.sendall(utils.dumps_json(answer).encode() + b'\r\n\r\n') answer = utils.dumps_json(answer) client.sendall(answer.encode() + b'\r\n\r\n') if not Node.Equals(data['sender'], self.node): self.update(data['sender']) exit_thread()
def lookup_value(self, ID): to_query = self.node.get_n_closest(ID, self.node.alpha) pending = [] heapq.heapify(pending) myinfo = (self.node.ID ^ ID, self.node.asTuple()) enquired = [myinfo] alives = [myinfo] if len(to_query) == 0: founded, data, file_bytes = self.node.FIND_VALUE(ID) if founded: return (True, data, file_bytes) else: return (False, myinfo, None) else: founded, data, file_bytes = self.node.FIND_VALUE(ID) if founded: return (True, data, file_bytes) closest_node = heapq.nsmallest(1, to_query)[0][0] round = 1 while True: for d,n in to_query: k_closest, data = [], None if not Node.Equals(n, self.node): msg = utils.build_FIND_VALUE_msg(ID, self.node.asTuple()) sock = self.sendall(msg, n[1], close=False) # data = self.get_response(msg['key']) data = self.recvall(sock) utils.close_connection(sock) if data != None: data = json.loads(data) if data['result'][0]: return data['result'] # if data['result'][0]: return data k_closest = [(t[0], tuple(t[1])) for t in data['result'][1]] self.update(tuple(data['sender'])) if (d,n) not in enquired: enquired.append((d, n)) if data != None and (d,n) not in alives: alives.append((d, n)) for t in k_closest: if not t in enquired and not t in to_query and not t in pending: pending.append(t) to_query.clear() if pending == []: break c = heapq.nsmallest(1, pending)[0][0] top = self.node.alpha if c <= closest_node else self.node.k closest_node = min(closest_node, c) for _ in range(top): try: to_query.append(heapq.heappop(pending)) except: break round += 1 return (False, heapq.nsmallest(self.node.k, enquired), None)
def lookup_node(self, ID): ID = int(ID) to_query = self.node.get_n_closest(ID, self.node.alpha) pending = [] heapq.heapify(pending) myinfo = (self.node.ID ^ ID, self.node.asTuple()) enquired = [myinfo] alives = [myinfo] if len(to_query) == 0: return [(self.node.ID ^ ID, self.node.asTuple())] # return [] closest_node = heapq.nsmallest(1, to_query)[0][0] round = 1 while True: for d,n in to_query: k_closest, data = [], None if not Node.Equals(n, self.node): # k_closest = n[1].FIND_NODE(ID, self.node.k) msg = utils.build_FIND_NODE_msg(ID, self.node.asTuple()) ip, port = n[1], n[2] # self.send_udp_msg(utils.dumps_json(msg).encode(), (ip, port)) # # data = self.get_answer(msg['key']) # data = self.get_response(msg['key']) sock = self.sendall(msg, n[1], close=False) # data = self.get_response(msg['key']) data = self.recvall(sock) try: data = json.loads(data) except: data = None utils.close_connection(sock) if data: k_closest = [(t[0], tuple(t[1])) for t in data['result']] self.update(tuple(data['sender'])) if (d,n) not in enquired: enquired.append((d, n)) if data != None and (d,n) not in alives: alives.append((d, n)) for t in k_closest: if not t in enquired and not t in to_query and not t in pending: pending.append(t) to_query.clear() if pending == []: break c = heapq.nsmallest(1, pending)[0][0] # print(c) #print(closest_node) top = self.node.alpha if c <= closest_node else self.node.k closest_node = min(closest_node, c) for _ in range(top): try: to_query.append(heapq.heappop(pending)) except: break round += 1 # return heapq.nsmallest(self.node.k, enquired) return heapq.nsmallest(self.node.k, alives)
# break if __name__ == '__main__': import argparse parser = argparse.ArgumentParser() parser.add_argument('-uport', '--udp_port', type=int, default=8000) parser.add_argument('-id', '--id', type=int, default=-1) parser.add_argument('-tport', '--tcp_server_port', type=int, default=9000) parser.add_argument('-ip', '--ip', type=str, default=None) args = parser.parse_args() IP = args.ip if not IP: hostname = socket.gethostname() IP = socket.gethostbyname(hostname) import uuid ID = int(args.id) if ID == -1: ID = utils.get_key(uuid.uuid4().hex) node = Node(ID, IP, int(args.udp_port), B=160, k=20, alpha=3) peer = Peer(node, int(args.tcp_server_port)) threading._start_new_thread(peer.join, ()) threading._start_new_thread(peer.check_network, ()) threading._start_new_thread(peer.attend_clients, ()) threading._start_new_thread(peer.attend_new_nodes, ()) peer.serve()