Beispiel #1
0
class TestMerkleMessage(TestAsServer):

    """
    Testing Merkle hashpiece messages for both:
    * Merkle BEP style
    * old Tribler <= 4.5.2 that did not use the Extention protocol (BEP 10).

    See BitTornado/BT1/Connecter.py
    """

    def setUp(self):
        """ override TestAsServer """
        TestAsServer.setUp(self)
        print >> sys.stderr, "test: Giving Session time to startup"
        time.sleep(5)
        print >> sys.stderr, "test: Session should have started up"

    def setUpPreSession(self):
        """ override TestAsServer """
        TestAsServer.setUpPreSession(self)
        self.config.set_overlay(False)
        self.config.set_megacache(False)

    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", repr(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", repr(hash1)
        print >> sys.stderr, "hash piece2", repr(hash2)
        f2 = open("piece1.bin", "wb")
        f2.write(piece2)
        f2.close()

    def setUpDownloadConfig(self):
        dscfg = DownloadStartupConfig()
        print >> sys.stderr, "test: Downloading to", self.config_path
        dscfg.set_dest_dir(self.config_path)
        dscfg.set_breakup_seed_bitfield(False)

        return dscfg

    def tearDown(self):
        TestAsServer.tearDown(self)
        try:
            os.remove('piece1.bin')
        except:
            pass

    def singtest_good_hashpiece_bepstyle(self):
        self.subtest_good_hashpiece(False)

    def singtest_good_hashpiece_oldstyle(self):
        self.subtest_good_hashpiece(True)

    def singtest_good_request_bepstyle(self):
        # Let Session download file first
        self.subtest_good_hashpiece(False)
        # Now connect as different peer and download
        print >> sys.stderr, "\n\ntest: test_good_request: STARTING"
        self._test_good_request()

    def singtest_bad_hashpiece_bepstyle(self):
        self.subtest_bad_hashpiece(False)

    def singtest_bad_hashpiece_oldstyle(self):
        self.subtest_bad_hashpiece(True)

    #
    # Good hashpiece message
    #
    def subtest_good_hashpiece(self, oldstyle):
        print >> sys.stderr, "test: Testing good hashpiece, oldstyle", oldstyle
        if oldstyle:
            self._test_good(self.create_good_hashpiece, oldstyle, self.create_good_tribler_extend_hs, infohash=self.infohash)
        else:
            options = '\x00\x00\x00\x00\x00\x10\x00\x00'
            self._test_good(self.create_good_hashpiece, oldstyle, self.create_good_nontribler_extend_hs, options=options, infohash=self.infohash)

    def _test_good(self, msg_gen_func, oldstyle, extend_hs_gen_func, options=None, infohash=None):
        if options is None and infohash is None:
            s = BTConnection('localhost', self.hisport)
        elif options is None:
            s = BTConnection('localhost', self.hisport, user_infohash=infohash)
        elif infohash is None:
            s = BTConnection('localhost', self.hisport, user_option_pattern=options)
        else:
            s = BTConnection('localhost', self.hisport, user_option_pattern=options, user_infohash=infohash)
        print >> sys.stderr, "test: test_good: Create EXTEND HS"
        msg = extend_hs_gen_func()
        print >> sys.stderr, "test: test_good: Sending EXTEND HS", repr(msg)
        s.send(msg)
        print >> sys.stderr, "test: test_good: Waiting for BT HS"
        s.read_handshake_medium_rare()

        # Tribler should send an EXTEND message back
        try:
            print >> sys.stderr, "test: Waiting for reply"
            s.s.settimeout(10.0)
            resp = s.recv()
            self.assert_(len(resp) > 0)
            print >> sys.stderr, "test: Got reply", getMessageName(resp[0])
            self.assert_(resp[0] == EXTEND)
            self.check_tribler_extend_hs(resp[1:])

            # 1. Pretend we're seeder: send BITFIELD and UNCHOKE
            msg = BITFIELD + self.seederbitfieldstr
            s.send(msg)
            msg = UNCHOKE
            s.send(msg)
            print >> sys.stderr, "test: Pretend we are seeder"
            while True:
                resp = s.recv()
                self.assert_(len(resp) > 0)
                print >> sys.stderr, "test: Got reply2", getMessageName(resp[0])
                self.assert_(resp[0] == REQUEST or resp[0] == INTERESTED or resp[0] == UNCHOKE or resp[0] == HAVE or resp[0] == NOT_INTERESTED)
                if resp[0] == REQUEST:
                    chunkid = self.check_request(resp)

                    # 2. Reply to REQUEST with HASHPIECE (oldstyle) or Tr_hashpiece
                    msg = msg_gen_func(oldstyle, chunkid)
                    s.send(msg)
                elif resp[0] == NOT_INTERESTED:
                    break

            # s.close()
        except socket.timeout:
            print >> sys.stderr, "test: Timeout, bad, peer didn't reply in time"
            self.assert_(False)

        destfn = os.path.join(self.config_path, "file2.wmv")
        sf = open(self.sourcefn, "rb")
        df = open(destfn, "rb")
        n = self.tdef.get_piece_length()
        while True:
            sdata = sf.read(n)
            if len(sdata) == 0:
                break
            ddata = df.read(n)
            self.assert_(sdata == ddata)

        time.sleep(3)
        s.close()

    def create_good_nontribler_extend_hs(self):
        """ Merkle BEP style """
        d = {}
        d['m'] = {'Tr_hashpiece': 250}
        d['p'] = self.mylistenport
        d['v'] = 'TestSweet 1.2.3.4'
        bd = bencode(d)
        return EXTEND + chr(0) + bd

    def create_good_tribler_extend_hs(self):
        """ old Tribler style """
        d = {}
        d['m'] = {'Tr_OVERLAYSWARM': 253}
        d['p'] = self.mylistenport
        d['v'] = 'Tribler 3.5.1'
        bd = bencode(d)
        return EXTEND + chr(0) + bd

    def check_tribler_extend_hs(self, data):
        self.assert_(data[0] == chr(0))
        d = bdecode(data[1:])
        self.assert_(isinstance(d, DictType))
        self.assert_('m' in d.keys())
        m = d['m']
        self.assert_(isinstance(m, DictType))
        self.assert_('Tr_hashpiece' in m.keys())
        val = m['Tr_hashpiece']
        self.assert_(isinstance(val, IntType))
        self.assert_(val == 250)

    def check_request(self, data):
        index = toint(data[1:5])
        begin = toint(data[5:9])
        length = toint(data[9:])
        return (index, begin, length)

    def create_good_hashpiece(self, oldstyle, chunkid):
        index, begin, length = chunkid
        if begin == 0:
            ohlist = self.tree.get_hashes_for_piece(index)
        else:
            ohlist = []

        chunk = self.read_chunk(index, begin, length)
        bohlist = bencode(ohlist)

        print >> sys.stderr, "test: create_good_hashpiece:", index, begin, length, "==len", len(chunk)

        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        if oldstyle:
            msg = HASHPIECE + payload
        else:
            # Offical: use the msg ID he defined in his handshake
            msg = EXTEND + HASHPIECE + payload
        return msg

    def read_chunk(self, index, begin, length):
        offset = index * self.tdef.get_piece_length() + begin
        f = open(self.sourcefn, "rb")
        f.seek(offset)
        chunk = f.read(length)
        f.close()
        return chunk

    #
    # Test whether Tribler sends good Tr_hashpiece on our requests
    #
    def _test_good_request(self):
        options = '\x00\x00\x00\x00\x00\x10\x00\x00'
        myid = Rand.rand_bytes(20)

        s = BTConnection('localhost', self.hisport, user_option_pattern=options, user_infohash=self.infohash, myid=myid)
        msg = self.create_good_nontribler_extend_hs()
        s.send(msg)
        s.read_handshake_medium_rare()

        # Tribler should send an EXTEND message back
        try:
            print >> sys.stderr, "test: Waiting for reply"
            s.s.settimeout(10.0)
            resp = s.recv()
            self.assert_(len(resp) > 0)
            print >> sys.stderr, "test: Got reply", getMessageName(resp[0])
            self.assert_(resp[0] == EXTEND)
            self.check_tribler_extend_hs(resp[1:])

            # 1. Pretend we're leecher: send INTERESTED
            msg = INTERESTED
            s.send(msg)
            print >> sys.stderr, "test: Pretend we are leecher"
            while True:
                resp = s.recv()
                self.assert_(len(resp) > 0)
                print >> sys.stderr, "test: Got reply2", getMessageName(resp[0])
                if resp[0] == EXTEND:
                    print >> sys.stderr, "test: Got EXTEND type", getMessageName(resp[1])
                self.assert_(resp[0] == UNCHOKE or resp[0] == BITFIELD or resp[0] == EXTEND or resp[0] == HAVE)
                if resp[0] == UNCHOKE:
                    # 2. Reply with REQUESTs
                    for index in range(0, self.numpieces):
                        plen = self.get_piece_length(index)

                        for begin in range(0, plen, 2 ** 14):
                            length = self.get_chunk_length(index, begin)
                            print >> sys.stderr, "RETRIEVE", index, begin, length
                            chunkid = (index, begin, length)
                            msg = self.create_request(chunkid)
                            s.send(msg)

                    # s.send(NOT_INTERESTED)

                elif resp[0] == EXTEND and resp[1] == HASHPIECE:
                    done = self.check_hashpiece(resp)
                    if done:
                        break
                elif resp[0] == BITFIELD:
                    self.check_bitfield(resp)

            # s.close()
        except socket.timeout:
            print >> sys.stderr, "test: Timeout, bad, peer didn't reply in time"
            self.assert_(False)

        time.sleep(3)
        s.close()

    def get_piece_length(self, index):
        if index == (self.numpieces - 1):
            plen = self.tdef.get_length() % self.tdef.get_piece_length()
        else:
            plen = self.tdef.get_piece_length()
        return plen

    def get_chunk_length(self, index, begin):
        plen = self.get_piece_length(index)
        length = 2 ** 14
        if index == (self.numpieces - 1):
            if (begin + 2 ** 14) > plen:
                length = plen - begin
        return length

    def create_request(self, chunkid):
        index, begin, length = chunkid
        return REQUEST + tobinary(index) + tobinary(begin) + tobinary(length)

    def check_hashpiece(self, resp):
        """ Merkle BEP style """
        print >> sys.stderr, "test: good_request: check_hashpiece"
        self.assert_(resp[0] == EXTEND)
        self.assert_(resp[1] == HASHPIECE)
        index = toint(resp[2:2 + 4])
        begin = toint(resp[6:6 + 4])
        ohlen = toint(resp[10:10 + 4])
        print >> sys.stderr, "test: good_request: check_hashpiece", index, begin, ohlen
        bohlist = resp[14:14 + ohlen]
        hisohlist = bdecode(bohlist)
        hischunk = resp[14 + ohlen:]

        if begin == 0:
            self.assert_(isinstance(hisohlist, ListType))
            for oh in hisohlist:
                self.assert_(isinstance(oh, ListType))
                self.assert_(len(oh) == 2)
                self.assert_(isinstance(oh[0], IntType))
                self.assert_(isinstance(oh[1], StringType))

            hisohlist.sort()
            print >> sys.stderr, "test: good_request: check_hashpiece", repr(hisohlist)
            myohlist = self.tree.get_hashes_for_piece(index)
            myohlist.sort()

            self.assert_(len(hisohlist) == len(myohlist))
            for i in range(0, len(hisohlist)):
                hisoh = hisohlist[i]
                myoh = myohlist[i]
                self.assert_(hisoh == myoh)
        else:
            self.assert_(len(hisohlist) == 0)

        mylength = self.get_chunk_length(index, begin)
        mychunk = self.read_chunk(index, begin, mylength)

        self.assert_(hischunk == mychunk)

        return index == self.numpieces - 1 and mylength != 2 ** 14

    def check_bitfield(self, data):
        self.assert_(data[0] == BITFIELD)
        bitmap = data[1:]
        self.assert_(len(bitmap) == 1)
        # Must have set_breakup_seed_bitfield() set to False
        self.assert_(bitmap == '\xc0')

    #
    # Bad EXTEND handshake message
    #
    def subtest_bad_hashpiece(self, oldstyle):
        if not oldstyle:
            # Test becomes equivalent to BT keep alive message (len 0, payload '')
            self._test_bad(self.create_empty, oldstyle)
        self._test_bad(self.create_ext_id_not_byte, oldstyle)
        self._test_bad(self.create_not_hashpiece, oldstyle)
        self._test_bad(self.create_not_index, oldstyle)
        self._test_bad(self.create_not_begin, oldstyle)
        self._test_bad(self.create_not_len_bohlist, oldstyle)
        self._test_bad(self.create_ohlist_not_bdecodable, oldstyle)
        self._test_bad(self.create_ohlist_wrong_no_hashes, oldstyle)
        self._test_bad(self.create_ohlist_wrong_no_root_hash, oldstyle)
        self._test_bad(self.create_ohlist_wrong_bad_offset, oldstyle)
        self._test_bad(self.create_ohlist_wrong_bad_hash, oldstyle)
        # TODO: need working peer kicking for that
        # self._test_bad(self.create_bad_chunk,oldstyle)

    #
    # Main test code for bad EXTEND handshake messages
    #
    def _test_bad(self, msg_gen_func, oldstyle):
        print >> sys.stderr, "test: test_BAD: Create EXTEND HS", repr(msg_gen_func), oldstyle
        if oldstyle:
            options = None
            exthsmsg = self.create_good_tribler_extend_hs()
        else:
            options = '\x00\x00\x00\x00\x00\x10\x00\x00'
            exthsmsg = self.create_good_nontribler_extend_hs()

        s = BTConnection('localhost', self.hisport, user_option_pattern=options, user_infohash=self.infohash)
        s.send(exthsmsg)
        s.read_handshake_medium_rare()

        # Tribler should send an EXTEND message back
        try:
            print >> sys.stderr, "test: Waiting for reply"
            s.s.settimeout(10.0)
            resp = s.recv()
            self.assert_(len(resp) > 0)
            print >> sys.stderr, "test: Got reply", getMessageName(resp[0])
            self.assert_(resp[0] == EXTEND)
            self.check_tribler_extend_hs(resp[1:])

            # 1. Pretend we're seeder: send BITFIELD and UNCHOKE
            msg = BITFIELD + self.seederbitfieldstr
            s.send(msg)
            msg = UNCHOKE
            s.send(msg)
            print >> sys.stderr, "test: Pretend we are seeder"
            while True:
                resp = s.recv()
                self.assert_(len(resp) > 0)
                print >> sys.stderr, "test: Got reply 2", getMessageName(resp[0])
                self.assert_(resp[0] == REQUEST or resp[0] == INTERESTED or resp[0] == UNCHOKE or resp[0] == HAVE or resp[0] == NOT_INTERESTED)
                if resp[0] == REQUEST:
                    chunkid = self.check_request(resp)

                    # 2. Reply to REQUEST with *bad* HASHPIECE
                    msg = msg_gen_func(chunkid)
                    if oldstyle:
                        if len(msg) == 1:
                            msg = ''
                        else:
                            msg = msg[1:]  # Strip EXTEND byte
                    s.send(msg)
                    break

            # s.close()
        except socket.timeout:
            print >> sys.stderr, "test: Timeout, bad, peer didn't reply in time"
            self.assert_(False)

        time.sleep(3)
        # Should have closed the connection
        try:
            s.send(UNCHOKE)
            self.assert_(False)
        except:
            print_exc()

        s.close()

    #
    # Bad message creators (all create Merkle BEP style, I strip first byte
    # later for oldstyle
    #
    def create_empty(self, chunkid):
        return EXTEND

    def create_ext_id_not_byte(self, chunkid):
        return EXTEND + 'Hallo kijkbuiskinderen'

    def create_not_hashpiece(self, chunkid):
        index, begin, length = chunkid
        ohlist = []
        bohlist = bencode(ohlist)
        chunk = self.read_chunk(index, begin, length)
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + chr(231) + payload

    def create_not_index(self, chunkid):
        payload = 'bla'
        return EXTEND + HASHPIECE + payload

    def create_not_begin(self, chunkid):
        index, begin, length = chunkid
        payload = tobinary(index) + 'bla'
        return EXTEND + HASHPIECE + payload

    def create_not_len_bohlist(self, chunkid):
        index, begin, length = chunkid
        payload = tobinary(index) + tobinary(begin) + 'bla'
        return EXTEND + HASHPIECE + payload

    def create_ohlist_not_bdecodable(self, chunkid):
        index, begin, length = chunkid
        bohlist = 'bla'
        chunk = '*' * (2 ** 14)
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + HASHPIECE + payload

    def create_ohlist_wrong_no_hashes(self, chunkid):
        index, begin, length = chunkid
        ohlist = [(0, '#' * 20), (1, '$' * 20)]  # should contain 3 for file2.wmv: own, sibling and root
        bohlist = bencode(ohlist)
        chunk = '*' * (2 ** 14)
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + HASHPIECE + payload

    def create_ohlist_wrong_no_root_hash(self, chunkid):
        index, begin, length = chunkid
        ohlist = self.tree.get_hashes_for_piece(index)
        newohlist = []
        # Remove root hash
        for oh in ohlist:
            if oh[0] != 0:
                newohlist.append(oh)
        ohlist = newohlist
        bohlist = bencode(ohlist)
        chunk = self.read_chunk(index, begin, length)
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + HASHPIECE + payload

    def create_ohlist_wrong_bad_offset(self, chunkid):
        index, begin, length = chunkid
        ohlist = self.tree.get_hashes_for_piece(index)
        ohlist[1][0] = 481
        bohlist = bencode(ohlist)
        chunk = self.read_chunk(index, begin, length)
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + HASHPIECE + payload

    def create_ohlist_wrong_bad_hash(self, chunkid):
        index, begin, length = chunkid
        ohlist = self.tree.get_hashes_for_piece(index)
        ohlist[1][1] = '$' * 20
        bohlist = bencode(ohlist)
        chunk = self.read_chunk(index, begin, length)
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + HASHPIECE + payload

    def create_bad_chunk(self, chunkid):
        index, begin, length = chunkid
        ohlist = self.tree.get_hashes_for_piece(index)
        bohlist = bencode(ohlist)
        chunk = '*' * length
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + HASHPIECE + payload
Beispiel #2
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", repr(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", repr(hash1)
        print >> sys.stderr, "hash piece2", repr(hash2)
        f2 = open("piece1.bin", "wb")
        f2.write(piece2)
        f2.close()
Beispiel #3
0
    else:
        flkey = 'files'
        flval = fs

        outpath = input['files'][0]['outpath']
        l = filename2pathlist(outpath)
        name = l[0]
        
    infodict =  { 'piece length':num2num(piece_length), flkey: flval, 
            'name': uniconvert(name,encoding),
            'name.utf-8': uniconvert(name,'utf-8')}
    
    if 'live' not in input:
        
        if input['createmerkletorrent']:
            merkletree = MerkleTree(piece_length,totalsize,None,pieces)
            root_hash = merkletree.get_root_hash()
            infodict.update( {'root hash': root_hash } )
        else:
            infodict.update( {'pieces': ''.join(pieces) } )
    else:
        # With source auth, live is a dict
        infodict['live'] = input['live']

    if len(subs) == 1:
        # Find and add playtime
        for file in input['files']:
            if file['inpath'] == f:
                if file['playtime'] is not None:
                    infodict['playtime'] = file['playtime']
Beispiel #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", repr(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", repr(hash1)
        print >> sys.stderr, "hash piece2", repr(hash2)
        f2 = open("piece1.bin", "wb")
        f2.write(piece2)
        f2.close()
Beispiel #5
0
class TestMerkleMessage(TestAsServer):

    """
    Testing Merkle hashpiece messages for both:
    * Merkle BEP style
    * old Tribler <= 4.5.2 that did not use the Extention protocol (BEP 10).

    See BitTornado/BT1/Connecter.py
    """

    def setUp(self):
        """ override TestAsServer """
        TestAsServer.setUp(self)
        print >> sys.stderr, "test: Giving Session time to startup"
        time.sleep(5)
        print >> sys.stderr, "test: Session should have started up"

    def setUpPreSession(self):
        """ override TestAsServer """
        TestAsServer.setUpPreSession(self)
        self.config.set_overlay(False)
        self.config.set_megacache(False)

    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", repr(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", repr(hash1)
        print >> sys.stderr, "hash piece2", repr(hash2)
        f2 = open("piece1.bin", "wb")
        f2.write(piece2)
        f2.close()

    def setUpDownloadConfig(self):
        dscfg = DownloadStartupConfig()
        print >> sys.stderr, "test: Downloading to", self.config_path
        dscfg.set_dest_dir(self.config_path)
        dscfg.set_breakup_seed_bitfield(False)

        return dscfg

    def tearDown(self):
        TestAsServer.tearDown(self)
        try:
            os.remove("piece1.bin")
        except:
            pass

    def singtest_good_hashpiece_bepstyle(self):
        self.subtest_good_hashpiece(False)

    def singtest_good_hashpiece_oldstyle(self):
        self.subtest_good_hashpiece(True)

    def singtest_good_request_bepstyle(self):
        # Let Session download file first
        self.subtest_good_hashpiece(False)
        # Now connect as different peer and download
        print >> sys.stderr, "\n\ntest: test_good_request: STARTING"
        self._test_good_request()

    def singtest_bad_hashpiece_bepstyle(self):
        self.subtest_bad_hashpiece(False)

    def singtest_bad_hashpiece_oldstyle(self):
        self.subtest_bad_hashpiece(True)

    #
    # Good hashpiece message
    #
    def subtest_good_hashpiece(self, oldstyle):
        print >> sys.stderr, "test: Testing good hashpiece, oldstyle", oldstyle
        if oldstyle:
            self._test_good(
                self.create_good_hashpiece, oldstyle, self.create_good_tribler_extend_hs, infohash=self.infohash
            )
        else:
            options = "\x00\x00\x00\x00\x00\x10\x00\x00"
            self._test_good(
                self.create_good_hashpiece,
                oldstyle,
                self.create_good_nontribler_extend_hs,
                options=options,
                infohash=self.infohash,
            )

    def _test_good(self, msg_gen_func, oldstyle, extend_hs_gen_func, options=None, infohash=None):
        if options is None and infohash is None:
            s = BTConnection("localhost", self.hisport)
        elif options is None:
            s = BTConnection("localhost", self.hisport, user_infohash=infohash)
        elif infohash is None:
            s = BTConnection("localhost", self.hisport, user_option_pattern=options)
        else:
            s = BTConnection("localhost", self.hisport, user_option_pattern=options, user_infohash=infohash)
        print >> sys.stderr, "test: test_good: Create EXTEND HS"
        msg = extend_hs_gen_func()
        print >> sys.stderr, "test: test_good: Sending EXTEND HS", repr(msg)
        s.send(msg)
        print >> sys.stderr, "test: test_good: Waiting for BT HS"
        s.read_handshake_medium_rare()

        # Tribler should send an EXTEND message back
        try:
            print >> sys.stderr, "test: Waiting for reply"
            s.s.settimeout(10.0)
            resp = s.recv()
            self.assert_(len(resp) > 0)
            print >> sys.stderr, "test: Got reply", getMessageName(resp[0])
            self.assert_(resp[0] == EXTEND)
            self.check_tribler_extend_hs(resp[1:])

            # 1. Pretend we're seeder: send BITFIELD and UNCHOKE
            msg = BITFIELD + self.seederbitfieldstr
            s.send(msg)
            msg = UNCHOKE
            s.send(msg)
            print >> sys.stderr, "test: Pretend we are seeder"
            while True:
                resp = s.recv()
                self.assert_(len(resp) > 0)
                print >> sys.stderr, "test: Got reply2", getMessageName(resp[0])
                self.assert_(
                    resp[0] == REQUEST
                    or resp[0] == INTERESTED
                    or resp[0] == UNCHOKE
                    or resp[0] == HAVE
                    or resp[0] == NOT_INTERESTED
                )
                if resp[0] == REQUEST:
                    chunkid = self.check_request(resp)

                    # 2. Reply to REQUEST with HASHPIECE (oldstyle) or Tr_hashpiece
                    msg = msg_gen_func(oldstyle, chunkid)
                    s.send(msg)
                elif resp[0] == NOT_INTERESTED:
                    break

            # s.close()
        except socket.timeout:
            print >> sys.stderr, "test: Timeout, bad, peer didn't reply in time"
            self.assert_(False)

        destfn = os.path.join(self.config_path, "file2.wmv")
        sf = open(self.sourcefn, "rb")
        df = open(destfn, "rb")
        n = self.tdef.get_piece_length()
        while True:
            sdata = sf.read(n)
            if len(sdata) == 0:
                break
            ddata = df.read(n)
            self.assert_(sdata == ddata)

        time.sleep(3)
        s.close()

    def create_good_nontribler_extend_hs(self):
        """ Merkle BEP style """
        d = {}
        d["m"] = {"Tr_hashpiece": 250}
        d["p"] = self.mylistenport
        d["v"] = "TestSweet 1.2.3.4"
        bd = bencode(d)
        return EXTEND + chr(0) + bd

    def create_good_tribler_extend_hs(self):
        """ old Tribler style """
        d = {}
        d["m"] = {"Tr_OVERLAYSWARM": 253}
        d["p"] = self.mylistenport
        d["v"] = "Tribler 3.5.1"
        bd = bencode(d)
        return EXTEND + chr(0) + bd

    def check_tribler_extend_hs(self, data):
        self.assert_(data[0] == chr(0))
        d = bdecode(data[1:])
        self.assert_(isinstance(d, DictType))
        self.assert_("m" in d.keys())
        m = d["m"]
        self.assert_(isinstance(m, DictType))
        self.assert_("Tr_hashpiece" in m.keys())
        val = m["Tr_hashpiece"]
        self.assert_(isinstance(val, IntType))
        self.assert_(val == 250)

    def check_request(self, data):
        index = toint(data[1:5])
        begin = toint(data[5:9])
        length = toint(data[9:])
        return (index, begin, length)

    def create_good_hashpiece(self, oldstyle, chunkid):
        index, begin, length = chunkid
        if begin == 0:
            ohlist = self.tree.get_hashes_for_piece(index)
        else:
            ohlist = []

        chunk = self.read_chunk(index, begin, length)
        bohlist = bencode(ohlist)

        print >> sys.stderr, "test: create_good_hashpiece:", index, begin, length, "==len", len(chunk)

        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        if oldstyle:
            msg = HASHPIECE + payload
        else:
            # Offical: use the msg ID he defined in his handshake
            msg = EXTEND + HASHPIECE + payload
        return msg

    def read_chunk(self, index, begin, length):
        offset = index * self.tdef.get_piece_length() + begin
        f = open(self.sourcefn, "rb")
        f.seek(offset)
        chunk = f.read(length)
        f.close()
        return chunk

    #
    # Test whether Tribler sends good Tr_hashpiece on our requests
    #
    def _test_good_request(self):
        options = "\x00\x00\x00\x00\x00\x10\x00\x00"
        myid = Rand.rand_bytes(20)

        s = BTConnection("localhost", self.hisport, user_option_pattern=options, user_infohash=self.infohash, myid=myid)
        msg = self.create_good_nontribler_extend_hs()
        s.send(msg)
        s.read_handshake_medium_rare()

        # Tribler should send an EXTEND message back
        try:
            print >> sys.stderr, "test: Waiting for reply"
            s.s.settimeout(10.0)
            resp = s.recv()
            self.assert_(len(resp) > 0)
            print >> sys.stderr, "test: Got reply", getMessageName(resp[0])
            self.assert_(resp[0] == EXTEND)
            self.check_tribler_extend_hs(resp[1:])

            # 1. Pretend we're leecher: send INTERESTED
            msg = INTERESTED
            s.send(msg)
            print >> sys.stderr, "test: Pretend we are leecher"
            while True:
                resp = s.recv()
                self.assert_(len(resp) > 0)
                print >> sys.stderr, "test: Got reply2", getMessageName(resp[0])
                if resp[0] == EXTEND:
                    print >> sys.stderr, "test: Got EXTEND type", getMessageName(resp[1])
                self.assert_(resp[0] == UNCHOKE or resp[0] == BITFIELD or resp[0] == EXTEND or resp[0] == HAVE)
                if resp[0] == UNCHOKE:
                    # 2. Reply with REQUESTs
                    for index in range(0, self.numpieces):
                        plen = self.get_piece_length(index)

                        for begin in range(0, plen, 2 ** 14):
                            length = self.get_chunk_length(index, begin)
                            print >> sys.stderr, "RETRIEVE", index, begin, length
                            chunkid = (index, begin, length)
                            msg = self.create_request(chunkid)
                            s.send(msg)

                    # s.send(NOT_INTERESTED)

                elif resp[0] == EXTEND and resp[1] == HASHPIECE:
                    done = self.check_hashpiece(resp)
                    if done:
                        break
                elif resp[0] == BITFIELD:
                    self.check_bitfield(resp)

            # s.close()
        except socket.timeout:
            print >> sys.stderr, "test: Timeout, bad, peer didn't reply in time"
            self.assert_(False)

        time.sleep(3)
        s.close()

    def get_piece_length(self, index):
        if index == (self.numpieces - 1):
            plen = self.tdef.get_length() % self.tdef.get_piece_length()
        else:
            plen = self.tdef.get_piece_length()
        return plen

    def get_chunk_length(self, index, begin):
        plen = self.get_piece_length(index)
        length = 2 ** 14
        if index == (self.numpieces - 1):
            if (begin + 2 ** 14) > plen:
                length = plen - begin
        return length

    def create_request(self, chunkid):
        index, begin, length = chunkid
        return REQUEST + tobinary(index) + tobinary(begin) + tobinary(length)

    def check_hashpiece(self, resp):
        """ Merkle BEP style """
        print >> sys.stderr, "test: good_request: check_hashpiece"
        self.assert_(resp[0] == EXTEND)
        self.assert_(resp[1] == HASHPIECE)
        index = toint(resp[2 : 2 + 4])
        begin = toint(resp[6 : 6 + 4])
        ohlen = toint(resp[10 : 10 + 4])
        print >> sys.stderr, "test: good_request: check_hashpiece", index, begin, ohlen
        bohlist = resp[14 : 14 + ohlen]
        hisohlist = bdecode(bohlist)
        hischunk = resp[14 + ohlen :]

        if begin == 0:
            self.assert_(isinstance(hisohlist, ListType))
            for oh in hisohlist:
                self.assert_(isinstance(oh, ListType))
                self.assert_(len(oh) == 2)
                self.assert_(isinstance(oh[0], IntType))
                self.assert_(isinstance(oh[1], StringType))

            hisohlist.sort()
            print >> sys.stderr, "test: good_request: check_hashpiece", repr(hisohlist)
            myohlist = self.tree.get_hashes_for_piece(index)
            myohlist.sort()

            self.assert_(len(hisohlist) == len(myohlist))
            for i in range(0, len(hisohlist)):
                hisoh = hisohlist[i]
                myoh = myohlist[i]
                self.assert_(hisoh == myoh)
        else:
            self.assert_(len(hisohlist) == 0)

        mylength = self.get_chunk_length(index, begin)
        mychunk = self.read_chunk(index, begin, mylength)

        self.assert_(hischunk == mychunk)

        return index == self.numpieces - 1 and mylength != 2 ** 14

    def check_bitfield(self, data):
        self.assert_(data[0] == BITFIELD)
        bitmap = data[1:]
        self.assert_(len(bitmap) == 1)
        # Must have set_breakup_seed_bitfield() set to False
        self.assert_(bitmap == "\xc0")

    #
    # Bad EXTEND handshake message
    #
    def subtest_bad_hashpiece(self, oldstyle):
        if not oldstyle:
            # Test becomes equivalent to BT keep alive message (len 0, payload '')
            self._test_bad(self.create_empty, oldstyle)
        self._test_bad(self.create_ext_id_not_byte, oldstyle)
        self._test_bad(self.create_not_hashpiece, oldstyle)
        self._test_bad(self.create_not_index, oldstyle)
        self._test_bad(self.create_not_begin, oldstyle)
        self._test_bad(self.create_not_len_bohlist, oldstyle)
        self._test_bad(self.create_ohlist_not_bdecodable, oldstyle)
        self._test_bad(self.create_ohlist_wrong_no_hashes, oldstyle)
        self._test_bad(self.create_ohlist_wrong_no_root_hash, oldstyle)
        self._test_bad(self.create_ohlist_wrong_bad_offset, oldstyle)
        self._test_bad(self.create_ohlist_wrong_bad_hash, oldstyle)
        # TODO: need working peer kicking for that
        # self._test_bad(self.create_bad_chunk,oldstyle)

    #
    # Main test code for bad EXTEND handshake messages
    #
    def _test_bad(self, msg_gen_func, oldstyle):
        print >> sys.stderr, "test: test_BAD: Create EXTEND HS", repr(msg_gen_func), oldstyle
        if oldstyle:
            options = None
            exthsmsg = self.create_good_tribler_extend_hs()
        else:
            options = "\x00\x00\x00\x00\x00\x10\x00\x00"
            exthsmsg = self.create_good_nontribler_extend_hs()

        s = BTConnection("localhost", self.hisport, user_option_pattern=options, user_infohash=self.infohash)
        s.send(exthsmsg)
        s.read_handshake_medium_rare()

        # Tribler should send an EXTEND message back
        try:
            print >> sys.stderr, "test: Waiting for reply"
            s.s.settimeout(10.0)
            resp = s.recv()
            self.assert_(len(resp) > 0)
            print >> sys.stderr, "test: Got reply", getMessageName(resp[0])
            self.assert_(resp[0] == EXTEND)
            self.check_tribler_extend_hs(resp[1:])

            # 1. Pretend we're seeder: send BITFIELD and UNCHOKE
            msg = BITFIELD + self.seederbitfieldstr
            s.send(msg)
            msg = UNCHOKE
            s.send(msg)
            print >> sys.stderr, "test: Pretend we are seeder"
            while True:
                resp = s.recv()
                self.assert_(len(resp) > 0)
                print >> sys.stderr, "test: Got reply 2", getMessageName(resp[0])
                self.assert_(
                    resp[0] == REQUEST
                    or resp[0] == INTERESTED
                    or resp[0] == UNCHOKE
                    or resp[0] == HAVE
                    or resp[0] == NOT_INTERESTED
                )
                if resp[0] == REQUEST:
                    chunkid = self.check_request(resp)

                    # 2. Reply to REQUEST with *bad* HASHPIECE
                    msg = msg_gen_func(chunkid)
                    if oldstyle:
                        if len(msg) == 1:
                            msg = ""
                        else:
                            msg = msg[1:]  # Strip EXTEND byte
                    s.send(msg)
                    break

            # s.close()
        except socket.timeout:
            print >> sys.stderr, "test: Timeout, bad, peer didn't reply in time"
            self.assert_(False)

        time.sleep(3)
        # Should have closed the connection
        try:
            s.send(UNCHOKE)
            self.assert_(False)
        except:
            print_exc()

        s.close()

    #
    # Bad message creators (all create Merkle BEP style, I strip first byte
    # later for oldstyle
    #
    def create_empty(self, chunkid):
        return EXTEND

    def create_ext_id_not_byte(self, chunkid):
        return EXTEND + "Hallo kijkbuiskinderen"

    def create_not_hashpiece(self, chunkid):
        index, begin, length = chunkid
        ohlist = []
        bohlist = bencode(ohlist)
        chunk = self.read_chunk(index, begin, length)
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + chr(231) + payload

    def create_not_index(self, chunkid):
        payload = "bla"
        return EXTEND + HASHPIECE + payload

    def create_not_begin(self, chunkid):
        index, begin, length = chunkid
        payload = tobinary(index) + "bla"
        return EXTEND + HASHPIECE + payload

    def create_not_len_bohlist(self, chunkid):
        index, begin, length = chunkid
        payload = tobinary(index) + tobinary(begin) + "bla"
        return EXTEND + HASHPIECE + payload

    def create_ohlist_not_bdecodable(self, chunkid):
        index, begin, length = chunkid
        bohlist = "bla"
        chunk = "*" * (2 ** 14)
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + HASHPIECE + payload

    def create_ohlist_wrong_no_hashes(self, chunkid):
        index, begin, length = chunkid
        ohlist = [(0, "#" * 20), (1, "$" * 20)]  # should contain 3 for file2.wmv: own, sibling and root
        bohlist = bencode(ohlist)
        chunk = "*" * (2 ** 14)
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + HASHPIECE + payload

    def create_ohlist_wrong_no_root_hash(self, chunkid):
        index, begin, length = chunkid
        ohlist = self.tree.get_hashes_for_piece(index)
        newohlist = []
        # Remove root hash
        for oh in ohlist:
            if oh[0] != 0:
                newohlist.append(oh)
        ohlist = newohlist
        bohlist = bencode(ohlist)
        chunk = self.read_chunk(index, begin, length)
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + HASHPIECE + payload

    def create_ohlist_wrong_bad_offset(self, chunkid):
        index, begin, length = chunkid
        ohlist = self.tree.get_hashes_for_piece(index)
        ohlist[1][0] = 481
        bohlist = bencode(ohlist)
        chunk = self.read_chunk(index, begin, length)
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + HASHPIECE + payload

    def create_ohlist_wrong_bad_hash(self, chunkid):
        index, begin, length = chunkid
        ohlist = self.tree.get_hashes_for_piece(index)
        ohlist[1][1] = "$" * 20
        bohlist = bencode(ohlist)
        chunk = self.read_chunk(index, begin, length)
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + HASHPIECE + payload

    def create_bad_chunk(self, chunkid):
        index, begin, length = chunkid
        ohlist = self.tree.get_hashes_for_piece(index)
        bohlist = bencode(ohlist)
        chunk = "*" * length
        payload = tobinary(index) + tobinary(begin) + tobinary(len(bohlist)) + bohlist + chunk
        return EXTEND + HASHPIECE + payload
Beispiel #6
0
def makeinfo(input, userabortflag, userprogresscallback):
    """ Calculate hashes and create torrent file's 'info' part """
    encoding = input['encoding']

    pieces = []
    sh = sha()
    done = 0
    fs = []
    totalsize = 0
    totalhashed = 0

    # 1. Determine which files should go into the torrent (=expand any dirs
    # specified by user in input['files']
    subs = []
    for file in input['files']:
        inpath = file['inpath']
        outpath = file['outpath']

        if DEBUG:
            print >> sys.stderr, "makeinfo: inpath", inpath, "outpath", outpath

        if os.path.isdir(inpath):
            dirsubs = subfiles(inpath)
            subs.extend(dirsubs)
        else:
            if outpath is None:
                subs.append(([os.path.basename(inpath)], inpath))
            else:
                subs.append((filename2pathlist(outpath, skipfirst=True), inpath))

    subs.sort()

    # 2. Calc total size
    newsubs = []
    for p, f in subs:
        if 'live' in input:
            size = input['files'][0]['length']
        else:
            size = os.path.getsize(f)
        totalsize += size
        newsubs.append((p, f, size))
    subs = newsubs

    # 3. Calc piece length from totalsize if not set
    if input['piece length'] == 0:
        if input['createmerkletorrent']:
            # used to be 15=32K, but this works better with slow python
            piece_length = 2 ** 18
        else:
            # Niels we want roughly between 1000-2000 pieces
            # This results in the following logic:

            # We start with 32K pieces
            piece_length = 2 ** 15

            while totalsize / piece_length > 2000:
                # too many piece, double piece_size
                piece_length *= 2
    else:
        piece_length = input['piece length']

    # 4. Read files and calc hashes, if not live
    if 'live' not in input:
        for p, f, size in subs:
            pos = 0

            h = open(f, 'rb')

            if input['makehash_md5']:
                hash_md5 = md5.new()
            if input['makehash_sha1']:
                hash_sha1 = sha()
            if input['makehash_crc32']:
                hash_crc32 = zlib.crc32('')

            while pos < size:
                a = min(size - pos, piece_length - done)

                # See if the user cancelled
                if userabortflag is not None and userabortflag.isSet():
                    return (None, None)

                readpiece = h.read(a)

                # See if the user cancelled
                if userabortflag is not None and userabortflag.isSet():
                    return (None, None)

                sh.update(readpiece)

                if input['makehash_md5']:
                    # Update MD5
                    hash_md5.update(readpiece)

                if input['makehash_crc32']:
                    # Update CRC32
                    hash_crc32 = zlib.crc32(readpiece, hash_crc32)

                if input['makehash_sha1']:
                    # Update SHA1
                    hash_sha1.update(readpiece)

                done += a
                pos += a
                totalhashed += a

                if done == piece_length:
                    pieces.append(sh.digest())
                    done = 0
                    sh = sha()

                if userprogresscallback is not None:
                    userprogresscallback(float(totalhashed) / float(totalsize))

            newdict = {'length': num2num(size),
                       'path': uniconvertl(p, encoding),
                       'path.utf-8': uniconvertl(p, 'utf-8')}

            # Find and add playtime
            for file in input['files']:
                if file['inpath'] == f:
                    if file['playtime'] is not None:
                        newdict['playtime'] = file['playtime']
                    break

            if input['makehash_md5']:
                newdict['md5sum'] = hash_md5.hexdigest()
            if input['makehash_crc32']:
                newdict['crc32'] = "%08X" % hash_crc32
            if input['makehash_sha1']:
                newdict['sha1'] = hash_sha1.digest()

            fs.append(newdict)

            h.close()

        if done > 0:
            pieces.append(sh.digest())

    # 5. Create info dict
    if len(subs) == 1:
        flkey = 'length'
        flval = num2num(totalsize)
        name = subs[0][0][0]
    else:
        flkey = 'files'
        flval = fs

        if 'name' in input:  # allow someone to overrule the default name if multifile
            name = input['name']
        else:
            outpath = input['files'][0]['outpath']
            l = filename2pathlist(outpath)
            name = l[0]

    infodict = {'piece length': num2num(piece_length), flkey: flval,
            'name': uniconvert(name, encoding),
            'name.utf-8': uniconvert(name, 'utf-8')}

    if 'live' not in input:

        if input['createmerkletorrent']:
            merkletree = MerkleTree(piece_length, totalsize, None, pieces)
            root_hash = merkletree.get_root_hash()
            infodict.update({'root hash': root_hash})
        else:
            infodict.update({'pieces': ''.join(pieces)})
    else:
        # With source auth, live is a dict
        infodict['live'] = input['live']

    if 'cs_keys' in input:
        # This is a closed swarm - add torrent keys
        infodict['cs_keys'] = input['cs_keys']

    if 'ns-metadata' in input:
        # This has P2P-Next metadata, store in info field to make it
        # immutable.
        infodict['ns-metadata'] = input['ns-metadata']

    if len(subs) == 1:
        # Find and add playtime
        for file in input['files']:
            if file['inpath'] == f:
                if file['playtime'] is not None:
                    infodict['playtime'] = file['playtime']

    return (infodict, piece_length)
Beispiel #7
0
        outpath = input['files'][0]['outpath']
        l = filename2pathlist(outpath)
        name = l[0]

    infodict = {
        'piece length': num2num(piece_length),
        flkey: flval,
        'name': uniconvert(name, encoding),
        'name.utf-8': uniconvert(name, 'utf-8')
    }

    if 'live' not in input:

        if input['createmerkletorrent']:
            merkletree = MerkleTree(piece_length, totalsize, None, pieces)
            root_hash = merkletree.get_root_hash()
            infodict.update({'root hash': root_hash})
        else:
            infodict.update({'pieces': ''.join(pieces)})
    else:
        # With source auth, live is a dict
        infodict['live'] = input['live']

    if len(subs) == 1:
        # Find and add playtime
        for file in input['files']:
            if file['inpath'] == f:
                if file['playtime'] is not None:
                    infodict['playtime'] = file['playtime']