def __init__(self, Ks, cert): '''Construct a new network object. The application must invoke bind() before using any other function on this object. The private key Ks and certificate cert must be supplied to construct a network object.''' Network.count = Network.count + 1 self.name = 'Network[%d]' % (Network.count) self.queue = multitask.SmartQueue( ) # module's default queue for dispatching and receiving Message events self.qsip = multitask.SmartQueue() # queue for SIP messages self.qstun = multitask.SmartQueue() # queue for STUN-related messages self.Ks, self.cert = Ks, cert self.udp, self.tcp, self.mcast = createSockets(preferred=(ADDRESS, PORT)) self.tcp.listen(5) self.tcpc = dict( ) # table of client connections from Node to connected socket if any. ip, port = getlocaladdr(self.udp) ignore, ptcp = getlocaladdr(self.tcp) self.node = Node(ip=ip, port=port, type=socket.SOCK_DGRAM, guid=H(ip + ':' + str(port))) # TODO: construct this using H(Kp) self.nodetcp = Node(ip=ip, port=ptcp, type=socket.SOCK_STREAM, guid=self.node.guid) self.nodemcast = Node(ip=ADDRESS, port=PORT, type=socket.SOCK_DGRAM, guid=self.node.guid) self.gen = self.gentcp = self.genmcast = None
def start(self, net=None, servers=None): '''Start the p2p node as ordinary node. Create a network object if none.''' if self.net is None: self.net = net or Network( Ks=crypto.generateRSA()[0], cert=None, port=self.port) self.net.start() # convert from serevrs ip:port list to Node list if servers: servers = [ Node(ip=ip, port=port, type=socket.SOCK_DGRAM, guid=H(ip + ':' + str(port))) for ip, port in servers ] if _debug: print 'using servers=', servers self.client = Client(self.net, server=self.server).start(servers) if self.server: if self.router is None: self.router = dht.Router(self.net).start() if self.storage is None: self.storage = dht.Storage(self.net, self.router).start() if not self.router.initialized: self.router.initialized = True if not self._gens: for gen in [self.handler()]: multitask.add(gen) self._gens.append(gen) return self
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 start(self, servers=[]): '''Start the client with the given set of optional servers list.''' if not self._gens: guid = H(ADDRESS + ':' + str(PORT)) try: bs = [ Node(ip=socket.gethostbyname(BOOTSTRAP), port=PORT, type=socket.SOCK_STREAM, guid=guid) ] except: bs = [] self.candidates = [self.net.nodemcast] + servers + bs if _debug: print 'Client.start candidates=', self.candidates self.neighbors = [] self._gens = [ self.discoverhandler(), self.bootstrap(), self.clienthandler() ] # , self.pinghandler() for gen in self._gens: multitask.add(gen) return self
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)