def sendto(self, identity, data, timeout=30): '''Send a single data object to the remote peer in datagram mode.''' values = yield self.get(guid=H(identity)) for value in map(lambda x: x.value, values): try: value = int(value) except: print 'invalid non-integer value=%r' % (value) continue seq = dht._seq = dht._seq + 1 net = self.net request = Message(name='Datagram:Request', src=net.node, dest=value, seq=seq, sock=hasattr(self, 'identity') and self.identity or None, peer=identity, value=str(data)) if value == net.node.guid: yield net.put(request, timeout=timeout) elif self.isSuperNode: yield net.put(Message(name='Route:Request', src=net.node, dest=value, payload=request), timeout=timeout) else: yield net.send(Message(name='Proxy:Request', src=net.node, payload=request), node=self.client.neighbors[0]) raise StopIteration(None)
def get(self, guid, maxvalues=16, Kp=None, timeout=5): '''Invoke the get method on the connected DHT node if this is a client.''' if self.server or not self.neighbors: if _debug: print 'client.get not a client with valid connections' raise StopIteration([]) net = self.net seq = dht._seq = dht._seq + 1 request = Message(name='Get:Request', seq=seq, src=net.node, dest=guid, maxvalues=maxvalues, hash=Kp and H(str(Kp)) or None) yield net.send(Message(name='Proxy:Request', src=net.node, payload=request), node=self.neighbors[0], timeout=5) response = yield net.get(timeout=timeout, criteria=lambda x: x.seq == seq and x.name == 'Get:Response') # wait for response result = [(v.value, k.nonce, v.Kp, k.expires) for k, v in zip( response.get('keyss', [None] * len(response['vals'])), response['vals']) ] if response else [] raise StopIteration( result ) # don't use response.values as it is a built-in method of base class dict of Message.
def pinghandler(self): '''Respond to Ping:Request.''' while True: msg = yield self.net.get(lambda x: x.name == 'Ping:Request') if _debug: print 'received ping request' yield self.net.send(msg=Message(name='Ping:Response'), node=msg.remote)
def put(self, guid, value, nonce, expires, Ks=None, put=True, timeout=30): '''Forward the put request to the connected DHT node.''' if self.server or not self.neighbors: # this is a server, or doesn't have valid connections if _debug: print 'client.put not a client with valid connections' raise StopIteration(False) net = self.net seq = dht._seq = dht._seq + 1 request = Message(name='Put:Request', date=time.time(), seq=seq, src=net.node, dest=guid, nonce=nonce, expires=expires, put=put, \ value=str(value) if put else None, hash=H(str(value)), Kp=Ks and dht.extractPublicKey(Ks) or None, \ sigma=dht.sign(Ks, H(str(guid) + str(value) + str(nonce) + str(expires))) if Ks else None) yield net.send(Message(name='Proxy:Request', src=net.node, payload=request), node=self.neighbors[0], timeout=5) response = yield net.get(timeout=timeout, criteria=lambda x: x.seq == seq and x.name == 'Put:Response') # wait for response raise StopIteration(response and response.result)
def parse(self, data, addr, type): '''Parse a message from given remote (host, port) and return parsed Message and remote Node. Returns None as message if can't be parsed.''' if len(data) < Hsize: return (None, None) guid, data = dht.bin2int(data[0:Hsize]), data[Hsize:] node = Node(ip=addr[0], port=addr[1], type=type, guid=guid) try: msg = Message(raw=data) except: return (None, None) return (msg, node)
def connect(self, identity, timeout=30): '''Connect to the given identity. It returns a Socket object on success or None on error.''' values = yield self.get(guid=H(identity)) if _debug: print 'connect() found values=%r' % (values) for value in map(lambda x: x.value, values): try: value = int(value) except: print 'invalid non-integer value=%r' % (value) continue sock = Socket(sock=self, peer=(identity, value), server=False) seq = dht._seq = dht._seq + 1 net = self.net request = Message(name='Connect:Request', src=net.node, dest=value, seq=seq, sock=hasattr(self, 'identity') and self.identity or None, peer=identity) if value == net.node.guid: yield net.put(request, timeout=5) elif self.isSuperNode: yield net.put(Message(name='Route:Request', src=net.node, dest=value, payload=request), timeout=5) else: yield net.send(Message(name='Proxy:Request', src=net.node, payload=request), node=self.client.neighbors[0]) response = yield net.get( timeout=timeout, criteria=lambda x: x.seq == seq and x.name == 'Connect:Response') # wait for response if response: raise StopIteration(sock) else: sock.close() raise StopIteration(None)
def requesthandler(msg): p = msg.payload response = None if self.server: # only if a server if p.name == 'Put:Request': result = yield dht.put(net, p.dest, p.value, p.nonce, p.expires, p.Ks, p.put) response = Message(name='Put:Response', seq=p.seq, result=result) elif p.name == 'Get:Request': result = yield dht.get(net, p.dest, p.maxvalues, p.Kp) response = Message(name='Get:Response', seq=p.seq, guid=p.guid, vals=result) if response: yield self.net.send(Message(name='Proxy:Response', src=net.node, payload=response), node=msg.src, timeout=5)
def accept(self, timeout=None): '''Accept an incoming connection. It returns a Socket object on success or None on error.''' net = self.net msg = yield net.get(timeout=timeout, criteria=lambda x: x.name == 'Connect:Request' and x.peer == self.identity) # wait for request if _debug: print 'accept msg=%r' % (msg) if not msg: raise StopIteration(None) sock = Socket(sock=self, peer=(msg.sock, msg.src.guid), server=True) yield net.send(Message(name='Connect:Response', seq=msg.seq, result=True), node=msg.src) raise StopIteration(sock)
def discoverhandler(self, timeout=3): '''Respond to a Discover:Request message, for both multicast and unicast.''' while True: msg = yield self.net.get(lambda x: x.name == 'Discover:Request') if _debug: print 'received discover request' if msg.remote.hostport == self.net.node.hostport: if _debug: print 'discoverhandler() ignoring our own packet' continue # don't compare Node but only hostport. Ignore if our packet. if msg.multicast: # wait randomly before replying to multicast discover if _debug: print 'discoverhandler() wait before responding to multicast' response = yield self.net.get( lambda x: x.name == 'Discover:Response' and x.multicast, timeout=(random.random() + 0.5) * timeout) if response: # someone else sent a response, we don't have to send anymore continue neighbors = ([self.net.node, self.net.nodetcp] if self.server else []) + self.neighbors if not msg.multicast or neighbors: response = Message(name='Discover:Response', neighbors=neighbors) dest = (msg.remote if not msg.multicast else self.net.nodemcast) yield self.net.send(msg=response, node=dest)
def bootstrap(self, timeout=5, interval=30): '''A generator to perform bootstrap function.''' candidates = self.candidates[:] # a copy of list of candidates while True: if _debug: print self.net.name, 'bootstrap server=', self.server, 'neighbors=', len( self.neighbors), 'candidates=', len(candidates) if not self.server and not self.neighbors and candidates: # more candidates but no more neighbors node = candidates.pop(0) if _debug: print 'bootstrap trying node=', repr(node) if node.type == socket.SOCK_DGRAM and isMulticast(node.ip): yield self.net.send(Message(name='Discover:Request'), node=node) msg = yield self.net.get( lambda x: x.name == 'Discover:Response' and x. multicast, timeout=timeout) else: if not isIPv4(node.ip): # is a IP address? node = Node(ip=socket.gethostbyname(node.ip), port=node.port, type=node.type, guid=node.guid) yield self.net.send(Message(name='Discover:Request'), node=node) msg = yield self.net.get( lambda x: x.name == 'Discover:Response' and not x. multicast, timeout=timeout) if msg: added = False for node in msg.neighbors: if node.hostport == msg.remote.hostport: # whether msg.remote exists in msg.neighbors, which means remote is a server and we are already connected. if _debug: print 'received neighbor', repr(node) self.neighbors.insert( 0, node) # put this as most preferred neighbor. added = True else: if _debug: print 'received candidate', repr(node) candidates.append( node) # put this as the next candidate if added: yield self.net.put( Message(name='Discover:Indication', node=self.node, neighbors=self.neighbors) ) # indicate change in client. else: if _debug: print 'bootstrap did not receive response.' elif not self.server and self.neighbors: # perform neighbor refresh yield dht.randomsleep(timeout) result = yield self.net.send(Message(name='Ping:Request'), node=self.neighbors[0], timeout=timeout) if not result: # no response received, remove the neighbor del self.neighbors[0] yield self.net.put( Message(name='Discover:Indication', node=self.node, neighbors=self.neighbors) ) # indicate change in client. elif not self.server and not self.neighbors and not candidates: candidates = self.candidates[:] yield dht.randomsleep(timeout) else: # just wait before trying again. yield dht.randomsleep(interval)