def __init__(self, file_name, info_dict=None): """Reads existing metainfo file, or writes a new one. Builds client, fetches peer list, and construct peers. """ self.logger = logging.getLogger('bt.torrent.Torrent') with open(file_name, 'r') as f: contents = f.read() self.info_dict = info_dict or bencode.bdecode( contents) # If read, bdecode self.info_hash = util.sha1_hash( bencode.bencode( self.info_dict['info']) # metainfo file is bencoded ) self.piece_length = self.info_dict['info']['piece length'] self.last_piece_length = self.length( ) % self.piece_length or self.piece_length pieces = self.info_dict['info']['pieces'] self.pieces_hashes = list(self._read_pieces_hashes(pieces)) self.num_pieces = len(self.pieces_hashes) assert len(self.pieces_hashes) == self.num_pieces # assert (self.num_pieces-1) * self.piece_length + self.last_piece_length \ # == file_length self.files = [f for f in self._create_files()] self.tmp_file = open('temp.tmp', 'w+') """ Data structure for easy lookup of piece rarity pieces[hash] has list of Peer instances with that piece Get rarity: len(pieces[hash]) """ self.pieces = [(Piece(self, i, self.pieces_hashes[i]), []) for i in range(self.num_pieces)] for p, _ in self.pieces: logging.debug('Piece {} has length {}.'.format( p.index, p.piece_length)) self._pieces_added = 0
def is_valid(self): """Return true if the hash of this entire piece is what we expected. """ self.torrent.tmp_file.seek(self.start_pos) # ... but we read only the # of bytes this piece actually has. f = self.torrent.tmp_file.read(self.piece_length) self.logger.debug('HASH: length of piece {} was {}'.format( self.index, len(f))) actual_hash = util.sha1_hash(f) return actual_hash == self.piece_hash
def _pieces_hashes(cls, string, piece_length): """Return array built from 20-byte SHA1 hashes of the string's pieces. """ output = "" current_pos = 0 num_bytes = len(string) while current_pos < num_bytes: if current_pos + piece_length > num_bytes: to_position = num_bytes else: to_position = current_pos + piece_length piece_hash = util.sha1_hash(string[current_pos:to_position]) output += piece_hash current_pos += piece_length return output
def __init__(self, ip, port, client, peer_id=None, conn=None): self.logger = logging.getLogger('bt.peer.Peer') self.ip = ip self.port = port self.client = client self.outstanding_requests = 0 if peer_id: self.peer_id = peer_id else: seed = self.ip + str(time.time()) self.peer_id = util.sha1_hash(self.ip) # Until handshake self.am_choking = True # Client choking remote self.am_interested = False # Client interested self.choking = True # Remote choking client self.interested = False # Remote interested if conn: self.conn = MsgConnection(self, conn) else: self.conn = MsgConnection(self)
def connect(self, port=6881): """Try to connect to tracker. Return tracker's response. """ params = { 'info_hash': util.sha1_hash(str( bencode.bencode(self.torrent.info_dict['info']) )), 'peer_id': self.client.peer_id, 'port': port, 'uploaded': 0, 'downloaded': 0, 'left': self.torrent.length(), 'event': 'started' } announce_url = self.torrent.info_dict['announce'] get_url = announce_url + "?" + urllib.urlencode(params) try: get_url=self._make_req(get_url) except Exception,e: print e return None
def _is_valid_piece(self, piece, index): piece_hash = util.sha1_hash(piece) expected_hash = self.client.torrent.pieces[index][0].piece_hash return piece_hash == expected_hash