示例#1
0
class TestMagnetMiniBitTorrent(TestAsServer, MagnetHelpers):
    """
    A MiniBitTorrent instance is used to connect to BitTorrent clients
    and download the info part from the metadata. 
    """
    def setUp(self):
        """ override TestAsServer """
        # listener for incoming connections from MiniBitTorrent
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server.bind(("localhost", LISTEN_PORT))
        self.server.listen(1)

        # the metadata that we want to transfer
        self.tdef = TorrentDef()
        self.tdef.add_content(os.path.join(os.getcwd(), "API", "file.wmv"))
        self.tdef.set_tracker("http://fake.net/announce")
        # we use a small piece length to obtain multiple pieces
        self.tdef.set_piece_length(1) 
        self.tdef.finalize()

        MagnetHelpers.__init__(self, self.tdef)

        # startup the client
        TestAsServer.setUp(self)
        print >>sys.stderr,time.asctime(),'-', "test: Giving MyLaunchMany time to startup"
        time.sleep(5)
        print >>sys.stderr,time.asctime(),'-', "test: MyLaunchMany should have started up"

    def create_good_url(self, infohash=None, title=None, tracker=None):
        url = "magnet:?xt=urn:btih:"
        if infohash:
            assert isinstance(infohash, str)
            url += hexlify(infohash)
        else:
            url += hexlify(self.tdef.get_infohash())
        if title:
            assert isinstance(title, str)
            url += "&dn=" + title
        if tracker:
            assert isinstance(tracker, str)
            url += "&tr=" + tracker
        return url

    def test_good_transfer(self):
        def torrentdef_retrieved(tdef):
            tags["retrieved"] = True
            tags["metainfo"] = tdef.get_metainfo()

        tags = {"retrieved":False}

        assert TorrentDef.retrieve_from_magnet(self.create_good_url(), torrentdef_retrieved)

        # supply fake addresses (regular dht obviously wont work here)
        for magnetlink in MagnetHandler.get_instance().get_magnets():
            magnetlink._swarm.add_potential_peers([("localhost", LISTEN_PORT)])

        # accept incoming connection
        self.server.settimeout(10.0)
        sock, address = self.server.accept()
        assert sock, "No incoming connection"

        # handshakes
        conn = BTConnection(address[0], address[1], opensock=sock, user_infohash=self.tdef.get_infohash())
        conn.send(self.create_good_extend_handshake())
        conn.read_handshake_medium_rare()
        metadata_id = self.read_extend_handshake(conn)

        # serve pieces
        for counter in xrange(len(self.metadata_list)):
            piece = self.read_extend_metadata_request(conn)
            assert 0 <= piece < len(self.metadata_list)
            conn.send(self.create_good_extend_metadata_reply(metadata_id, piece))

        # no more metadata request may be send and the connection must
        # be closed
        self.read_extend_metadata_close(conn)

        time.sleep(5)
        assert tags["retrieved"]
        assert tags["metainfo"]["info"] == self.tdef.get_metainfo()["info"]
示例#2
0
class TestQueryReplyActive(TestAsServer):
    """  
    Testing QUERY_REPLY message of Query extension V1 

    This test checks how the Tribler code responds to good and bad 
    QUERY_REPLY messages. I.e. the Tribler client initiates
    the dialback by connecting to us and sending a QUERY and we
    reply with good and bad messages.

    This test allows authoritative answers from superpeers.

    WARNING: Each of the test_ methods should be tested by running the TestCase 
    in a separate Python interpreter to prevent problems with our singleton 
    classes, e.g. SuperPeerDB, etc.
    """
    def setUpPreSession(self):
        """ override TestAsServer """
        print >> sys.stderr, "test: Pre Tribler Init"
        TestAsServer.setUpPreSession(self)
        print >> sys.stderr, "test: Pre Tribler Init: config_path", self.config_path
        # Enable remote querying
        self.config.set_remote_query(True)

    def setUpPostSession(self):
        """ override TestAsServer """
        TestAsServer.setUpPostSession(self)
        self.hispermid = str(self.his_keypair.pub().get_der())
        self.my_permid = str(self.my_keypair.pub().get_der())

        self.content_name = 'Hallo S22E44'
        self.tdef = TorrentDef()
        self.tdef.set_tracker('http://localhost:0/announce')
        self.tdef.set_piece_length(2**15)
        self.tdef.create_live(self.content_name, 2**16)
        self.tdef.finalize()

    def pretest_simple(self):
        self.pretest_q('SIMPLE hallo')

    def pretest_simpleplustorrents(self):
        self.pretest_q('SIMPLE+METADATA hallo')

    def pretest_q(self, query):
        # 1. First connect to Tribler
        self.openconn = OLConnection(self.my_keypair, 'localhost',
                                     self.hisport)
        sleep(3)

        # 2. Make Tribler send query
        self.query = query
        self.session.query_connected_peers(query,
                                           self.query_usercallback,
                                           max_peers_to_query=10)

    def query_usercallback(self, permid, query, hits):

        print >> sys.stderr, "test: query_usercallback:", ` permid `, ` query `, ` hits `

        self.assert_(query == self.query)
        self.assert_(permid == self.my_permid)
        self.check_good_qreply(hits)

        # TODO: if SIMPLE+METADATA: check torrent now in db.

    #
    # Good SIMPLE QUERY, builds on TestQueryReply code
    #
    def singtest_good_simple_reply(self):
        self.pretest_simple()
        self._test_qreply(self.create_good_simple_reply, True)

    #
    # Good SIMPLE+METADATA QUERY, builds on TestQueryReply code
    #
    def singtest_good_simpleplustorrents_reply(self):
        self.pretest_simpleplustorrents()
        self._test_qreply(self.create_good_simpleplustorrents_reply, True)

    #
    # Bad QUERY, builds on TestQueryReply code
    #
    def singtest_bad_not_bdecodable(self):
        self.pretest_simple()
        self._test_qreply(self.create_not_bdecodable, False)

    #
    # Bad SIMPLE+METADATA QUERY, builds on TestQueryReply code
    #
    def singtest_bad_not_bdecodable_torrentfile(self):
        self.pretest_simpleplustorrents()
        self._test_qreply(self.create_not_bdecodable_torrentfile, False)

    ### TODO: send different valid answers so consensus not reached

    #
    # Main test code
    #
    def _test_qreply(self, gen_qreply, good):
        print >> sys.stderr, "test: waiting for reply"
        s = self.openconn

        msg = s.recv()
        self.assert_(len(msg) > 0)
        print >> sys.stderr, "test: Received overlay message", getMessageName(
            msg[0])
        self.assert_(msg[0] == QUERY)
        id = self.check_rquery(msg[1:])

        resp = gen_qreply(id)
        print >> sys.stderr, "test: sending QUERY_REPLY"
        s.send(resp)
        if good:
            time.sleep(10)
            # the other side should not have closed the connection, as
            # this is all valid, so this should not throw an exception:
            s.send('bla')
            s.close()
        else:
            # the other side should not like this and close the connection
            self.assert_(len(s.recv()) == 0)
            s.close()

    def create_good_simple_reply_dict(self, id):
        r = {}
        r['content_name'] = self.content_name
        r['length'] = LENGTH
        r['leecher'] = LEECHERS
        r['seeder'] = SEEDERS
        r['category'] = CATEGORY
        # OLPROTO_PROTO_ELEVENTH
        # set later r['torrent_size'] = 42
        r['channel_permid'] = '$' * 83
        r['channel_name'] = 'Nitin Channel'

        d2 = {}
        d2[self.tdef.get_infohash()] = r

        d = {}
        d['id'] = id
        d['a'] = d2
        return d

    def create_good_simple_reply(self, id):
        d = self.create_good_simple_reply_dict(id)
        bmetainfo = bencode(self.tdef.get_metainfo())
        d['a'][self.tdef.get_infohash()]['torrent_size'] = len(bmetainfo)
        b = bencode(d)
        return QUERY_REPLY + b

    def create_good_simpleplustorrents_reply(self, id):
        d = self.create_good_simple_reply_dict(id)
        bmetainfo = bencode(self.tdef.get_metainfo())
        d['a'][self.tdef.get_infohash()]['torrent_size'] = len(bmetainfo)
        d['a'][self.tdef.get_infohash(
        )]['metatype'] = 'application/x-tribler-stream'
        d['a'][self.tdef.get_infohash()]['metadata'] = bmetainfo
        b = bencode(d)
        return QUERY_REPLY + b

    def check_good_qreply(self, hits):
        self.assert_(len(hits) == 1)
        self.assert_(hits.keys()[0] == self.tdef.get_infohash())
        hit = hits[self.tdef.get_infohash()]
        self.assert_(hit['content_name'] == self.content_name)
        self.assert_(hit['length'] == LENGTH)
        self.assert_(hit['leecher'] == LEECHERS)
        self.assert_(hit['seeder'] == SEEDERS)
        self.assert_(hit['category'] == CATEGORY)

        # OLPROTO_VERSION_ELEVENTH
        bmetainfo = bencode(self.tdef.get_metainfo())
        self.assert_(hit['torrent_size'] == len(bmetainfo))
        if self.query.startswith('SIMPLE+METADATA'):
            self.assert_(hit['metadata'] == bmetainfo)

    def create_not_bdecodable(self, id):
        return QUERY_REPLY + "bla"

    def create_not_bdecodable_torrentfile(self, id):
        d = self.create_good_simple_reply_dict(id)
        d['a'][self.tdef.get_infohash(
        )]['torrent_size'] = 3  # consistent with metadata. Should be named "metasize"
        d['a'][self.tdef.get_infohash()]['metadata'] = 'bla'
        b = bencode(d)
        return QUERY_REPLY + b

    def check_rquery(self, data):
        d = bdecode(data)
        self.assert_(type(d) == DictType)
        self.assert_(d.has_key('q'))
        q = d['q']
        self.assert_(type(q) == StringType)
        self.assert_(d.has_key('id'))
        id = d['id']
        self.assert_(type(id) == StringType)

        self.assert_(q == self.query)
        return d['id']
class TestQueryReplyActive(TestAsServer):

    """  
    Testing QUERY_REPLY message of Query extension V1 

    This test checks how the Tribler code responds to good and bad 
    QUERY_REPLY messages. I.e. the Tribler client initiates
    the dialback by connecting to us and sending a QUERY and we
    reply with good and bad messages.

    This test allows authoritative answers from superpeers.

    WARNING: Each of the test_ methods should be tested by running the TestCase 
    in a separate Python interpreter to prevent problems with our singleton 
    classes, e.g. SuperPeerDB, etc.
    """

    def setUpPreSession(self):
        """ override TestAsServer """
        print >> sys.stderr,"test: Pre Tribler Init"
        TestAsServer.setUpPreSession(self)
        print >> sys.stderr,"test: Pre Tribler Init: config_path",self.config_path
        # Enable remote querying
        self.config.set_remote_query(True)

    def setUpPostSession(self):
        """ override TestAsServer """
        TestAsServer.setUpPostSession(self)
        self.hispermid = str(self.his_keypair.pub().get_der())
        self.my_permid = str(self.my_keypair.pub().get_der())

        self.content_name = 'Hallo S22E44'
        self.tdef = TorrentDef()
        self.tdef.set_tracker('http://localhost:0/announce')
        self.tdef.set_piece_length(2 ** 15)
        self.tdef.create_live(self.content_name,2 ** 16)
        self.tdef.finalize()


    def pretest_simple(self):
        self.pretest_q('SIMPLE hallo')

    def pretest_simpleplustorrents(self):
        self.pretest_q('SIMPLE+METADATA hallo')

    def pretest_q(self,query):
        # 1. First connect to Tribler
        self.openconn = OLConnection(self.my_keypair,'localhost',self.hisport)
        sleep(3)
        
        # 2. Make Tribler send query
        self.query = query
        self.session.query_connected_peers(query,self.query_usercallback,max_peers_to_query=10)

    def query_usercallback(self,permid,query,hits):
        
        print >>sys.stderr,"test: query_usercallback:",`permid`,`query`,`hits`
        
        self.assert_(query == self.query)
        self.assert_(permid == self.my_permid)
        self.check_good_qreply(hits)
        
        # TODO: if SIMPLE+METADATA: check torrent now in db.
        

    #
    # Good SIMPLE QUERY, builds on TestQueryReply code
    #    
    def singtest_good_simple_reply(self):
        self.pretest_simple()
        self._test_qreply(self.create_good_simple_reply,True)

    #
    # Good SIMPLE+METADATA QUERY, builds on TestQueryReply code
    #    
    def singtest_good_simpleplustorrents_reply(self):
        self.pretest_simpleplustorrents()
        self._test_qreply(self.create_good_simpleplustorrents_reply,True)


    #
    # Bad QUERY, builds on TestQueryReply code
    #    
    def singtest_bad_not_bdecodable(self):
        self.pretest_simple()
        self._test_qreply(self.create_not_bdecodable,False)

    #
    # Bad SIMPLE+METADATA QUERY, builds on TestQueryReply code
    #    
    def singtest_bad_not_bdecodable_torrentfile(self):
        self.pretest_simpleplustorrents()
        self._test_qreply(self.create_not_bdecodable_torrentfile,False)


    ### TODO: send different valid answers so consensus not reached

    #
    # Main test code
    #
    def _test_qreply(self,gen_qreply,good):
        print >> sys.stderr,"test: waiting for reply"
        s = self.openconn

        msg = s.recv()
        self.assert_(len(msg) > 0)
        print >> sys.stderr,"test: Received overlay message",getMessageName(msg[0])
        self.assert_(msg[0] == QUERY)
        id = self.check_rquery(msg[1:])
        
        resp = gen_qreply(id)
        print >> sys.stderr,"test: sending QUERY_REPLY"
        s.send(resp)
        if good:
            time.sleep(10)
            # the other side should not have closed the connection, as
            # this is all valid, so this should not throw an exception:
            s.send('bla')
            s.close()
        else:
            # the other side should not like this and close the connection
            self.assert_(len(s.recv())==0)
            s.close()


    def create_good_simple_reply_dict(self,id):
        r = {}
        r['content_name'] = self.content_name
        r['length'] = LENGTH
        r['leecher'] = LEECHERS
        r['seeder'] = SEEDERS
        r['category'] = CATEGORY
        # OLPROTO_PROTO_ELEVENTH
        # set later r['torrent_size'] = 42
        r['channel_permid'] = '$' * 83
        r['channel_name'] = 'Nitin Channel' 
        
        d2 = {}
        d2[self.tdef.get_infohash()] = r
        
        d = {}
        d['id'] = id
        d['a'] = d2
        return d
        
    def create_good_simple_reply(self,id):
        d = self.create_good_simple_reply_dict(id)
        bmetainfo = bencode(self.tdef.get_metainfo())
        d['a'][self.tdef.get_infohash()]['torrent_size'] = len(bmetainfo) 
        b = bencode(d)
        return QUERY_REPLY+b

    def create_good_simpleplustorrents_reply(self,id):
        d = self.create_good_simple_reply_dict(id)
        bmetainfo = bencode(self.tdef.get_metainfo())
        d['a'][self.tdef.get_infohash()]['torrent_size'] = len(bmetainfo)
        d['a'][self.tdef.get_infohash()]['metatype'] = 'application/x-tribler-stream' 
        d['a'][self.tdef.get_infohash()]['metadata'] = bmetainfo 
        b = bencode(d)
        return QUERY_REPLY+b

    

    def check_good_qreply(self,hits):
        self.assert_(len(hits) == 1)
        self.assert_(hits.keys()[0] == self.tdef.get_infohash())
        hit = hits[self.tdef.get_infohash()]
        self.assert_(hit['content_name'] == self.content_name)
        self.assert_(hit['length'] == LENGTH)
        self.assert_(hit['leecher'] == LEECHERS)
        self.assert_(hit['seeder'] == SEEDERS)
        self.assert_(hit['category'] ==  CATEGORY)
    
        # OLPROTO_VERSION_ELEVENTH
        bmetainfo = bencode(self.tdef.get_metainfo())
        self.assert_(hit['torrent_size'] == len(bmetainfo))
        if self.query.startswith('SIMPLE+METADATA'):
            self.assert_(hit['metadata'] == bmetainfo)

    def create_not_bdecodable(self,id):
        return QUERY_REPLY+"bla"

    def create_not_bdecodable_torrentfile(self,id):
        d = self.create_good_simple_reply_dict(id)
        d['a'][self.tdef.get_infohash()]['torrent_size'] = 3 # consistent with metadata. Should be named "metasize"
        d['a'][self.tdef.get_infohash()]['metadata'] = 'bla'
        b = bencode(d)
        return QUERY_REPLY+b

    def check_rquery(self,data):
        d = bdecode(data)
        self.assert_(type(d) == DictType)
        self.assert_(d.has_key('q'))
        q = d['q']
        self.assert_(type(q) == StringType)
        self.assert_(d.has_key('id'))
        id = d['id']
        self.assert_(type(id) == StringType)

        self.assert_(q == self.query)
        return d['id']
示例#4
0
    def setUpPostSession(self):
        """ override TestAsServer """
        TestAsServer.setUpPostSession(self)

        # Let Tribler start downloading an non-functioning torrent, so
        # we can talk to a normal download engine.
        self.tdef = TorrentDef()
        self.sourcefn = os.path.join(os.getcwd(),"API","file2.wmv")
        self.tdef.add_content(self.sourcefn)
        self.tdef.set_create_merkle_torrent(True)
        self.tdef.set_tracker("http://127.0.0.1:12/announce")
        self.tdef.finalize()

        self.torrentfn = os.path.join(self.session.get_state_dir(),"gen.torrent")
        self.tdef.save(self.torrentfn)
        
        dscfg = self.setUpDownloadConfig()
        
        self.session.start_download(self.tdef,dscfg)

        self.infohash = self.tdef.get_infohash()
        self.mylistenport = 4810
        
        self.numpieces = (self.tdef.get_length()+self.tdef.get_piece_length()-1) / self.tdef.get_piece_length()
        b = Bitfield(self.numpieces)
        for i in range(self.numpieces):
            b[i] = True
        self.assert_(b.complete())
        self.seederbitfieldstr = b.tostring()

        #piece_hashes = ['\x01\x02\x03\x04\x05\x06\x07\x08\x07\x06\x05\x04\x03\x02\x01\x00\x01\x02\x03\x04' ] * npieces
        # Construct Merkle tree
        tdef2 = TorrentDef()
        tdef2.add_content(self.sourcefn)
        tdef2.set_create_merkle_torrent(False)
        tdef2.set_tracker("http://127.0.0.1:12/announce")
        tdef2.set_piece_length(self.tdef.get_piece_length())
        tdef2.finalize()
        metainfo = tdef2.get_metainfo()
        
        piecesstr = metainfo['info']['pieces']
        print >>sys.stderr,"test: pieces has len",len(piecesstr)
        piece_hashes = []
        for i in range(0,len(piecesstr),20):
            hash = piecesstr[i:i+20]
            print >>sys.stderr,"test: piece",i/20,"hash",`hash`
            piece_hashes.append(hash)
            
        print >>sys.stderr,"test: Putting",len(piece_hashes),"into MerkleTree, size",self.tdef.get_piece_length(),tdef2.get_piece_length()
        
        self.tree = MerkleTree(self.tdef.get_piece_length(),self.tdef.get_length(),None,piece_hashes)
        
        
        f = open(self.sourcefn,"rb")
        piece1 = f.read(2 ** 18)
        piece2 = f.read(2 ** 18)
        print >>sys.stderr,"read piece1",len(piece1)
        print >>sys.stderr,"read piece2",len(piece2)
        f.close()
        hash1 = sha(piece1).digest()
        hash2 = sha(piece2).digest()
        print >>sys.stderr,"hash piece1",`hash1`
        print >>sys.stderr,"hash piece2",`hash2`
        f2 = open("piece1.bin","wb")
        f2.write(piece2)
        f2.close()