Beispiel #1
0
    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
Beispiel #2
0
 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)
Beispiel #3
0
 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.
Beispiel #4
0
    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
Beispiel #5
0
 def close(self):
     '''Close the bound socket'''
     if hasattr(self, 'identity'):
         result = yield self.remove(guid=H(self.identity),
                                    value=self.net.node.guid,
                                    nonce=self.nonce,
                                    expires=self.expires)
         del self.identity, self.nonce, self.expires
     raise StopIteration(None)
Beispiel #6
0
 def bind(self, identity, interval=3600):
     '''Bind the server socket to the given identity.'''
     if hasattr(self, 'identity'): raise Exception('socket already bound')
     self.identity, self.nonce, self.expires = identity, dht.randomNonce(
     ), time.time() + interval
     result = yield self.put(guid=H(identity),
                             value=self.net.node.guid,
                             nonce=self.nonce,
                             expires=self.expires)
     raise StopIteration(result)
Beispiel #7
0
 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)
Beispiel #8
0
 def send(self, msg, node, timeout=None):
     '''Send some msg to dest node (Node), and if timeout is specified then return a success (True)
     or failure (False) within that timeout. Otherwise, the function may return immediately.'''
     try:
         start = time.time()
         if node.type == socket.SOCK_DGRAM and timeout is not None:  # no ack required for tcp
             msg['ack'] = True  # require a NetworkAck
         data = dht.int2bin(self.node.guid) + str(
             msg)  # TODO: this assumes guid is same for all transports.
         if _debug and msg.name[:4] != 'Hash':
             print self.name, 'sending %d bytes %s=>%s: %r' % (
                 len(data), self.node.hostport, node.hostport, msg)
         if node.type == socket.SOCK_DGRAM:
             self.udp.sendto(data, (node.ip, node.port))
         else:
             if node in self.tcpc:
                 sock = self.tcpc[node]
             else:
                 sock = socket.socket(type=socket.SOCK_STREAM)
                 sock.setblocking(0)
                 try:
                     if _debug: print 'connecting to %s' % (node.hostport, )
                     sock.connect((node.ip, node.port))
                 except (socket.timeout, socket.error):
                     yield multitask.sleep(2.0)
                     ret = select.select((), (sock, ), (), 0)
                     if len(ret[1]) == 0:
                         if _debug:
                             print 'connection timedout to %s' % (
                                 node.hostport, )
                         raise multitask.Timeout, 'Cannot connect to the destination'
                 self.tcpc[node] = sock
                 multitask.sleep()
                 multitask.add(self.tcphandler(sock, (node.ip, node.port)))
             data = struct.pack('!H',
                                len(data)) + data  # put a length first.
             sock.send(data)
         if msg.ack:
             hash = H(
                 data
             )  # hash property to associate the ack to the data request.
             ack = yield self.get(
                 lambda x: x.name == 'Ack:Indication' and x.hash == hash,
                 timeout=(timeout - (time.time() - start)))
             if _debug: 'received ack %r' % (ack)
             if ack is None: raise StopIteration(False)  # no ack received
         raise StopIteration(True)
     except (multitask.Timeout, socket.error):
         raise StopIteration(False)  # timeout in sendto or get
Beispiel #9
0
 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)
Beispiel #10
0
 def udpreceiver(self, maxsize=16386, timeout=None, interval=30):
     '''A UDP receiver task which also performs network ack.'''
     while True:
         data, addr = yield multitask.recvfrom(self.udp,
                                               maxsize,
                                               timeout=timeout)
         msg, remote = self.parse(data, addr, self.udp.type)
         if not msg:
             continue  # ignore invalid messages. TODO: handle non-p2p message
         if _debug and msg.name[:4] != 'Hash':
             print self.name, 'udp-received %s=>%s: %r' % (
                 remote.hostport, self.node.hostport, msg)
         if 'ack' in msg and msg.name != 'Ack:Indication':  # the remote requires an ack. send one.
             del msg['ack']  # remove the ack
             ack = dht.Message(
                 name='Ack:Indication',
                 hash=H(data))  # hash of original received packet
             yield self.send(msg=ack,
                             node=remote)  # send the NetworkAck message
         msg['remote'] = remote  # put remote as an attribute in msg before putting on queue.
         yield self.put(
             msg
         )  # put the parsed msg so that other interested party may get it.
Beispiel #11
0
 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