コード例 #1
0
    def announce(self, peer_id, port, event=None, num_wanted=-1):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.stream = UDPStream(self.socket)

        try:
            self.stream.connect((self.host, self.port))
        except socket.error:
            raise TrackerFailure('Could not connect to tracker')

        self.stream.read_until_close(self.data_received, self.data_received)
        logging.debug('Announcing to UDP tracker...')

        try:
            response = yield self.send_request(
                action=1,
                structure='!20s20sQQQIIIiH',
                arguments=[
                    self.torrent.info_hash(),
                    peer_id,
                    self.torrent.downloaded,
                    self.torrent.remaining,
                    self.torrent.uploaded,
                    self.events[event],
                    0,
                    0,
                    -1,
                    port
                ]
            )

            logging.debug('Tracker responded with %s', repr(response))

            raise Return(response)
        except RuntimeError:
            raise TrackerFailure('Tracker did not respond after 8 attempts')
コード例 #2
0
class UDPTracker(object):
    events = {
        None: 0,
        'none': 0,
        'completed': 1,
        'started': 2,
        'stopped': 3
    }

    def __init__(self, host, port, torrent, tier=0):
        self.host = host
        self.port = port

        self.torrent = torrent
        self.tier = tier

        self.connection_id = None
        self.connection_id_age = datetime.min
        self.requesting_connection_id = False

        self.pending_retries = defaultdict(int)
        self.pending_futures = {}
        self.pending_timers = {}

        self.socket = None
        self.stream = None

    @property
    def url(self):
        return 'udp://{self.host}:{self.port}'.format(self=self)

    def data_received(self, data):
        if not data:
            return

        logging.debug('Received some data')

        action, transaction_id = struct.unpack('!II', data[:8])

        if transaction_id in self.pending_retries:
            del self.pending_retries[transaction_id]

        if transaction_id in self.pending_timers:
            handle = self.pending_timers[transaction_id]
            IOLoop.instance().remove_timeout(handle)
            del self.pending_timers[transaction_id]

        if action == 0:  # CONNECT
            logging.debug('Received a CONNECT response for transaction %d', transaction_id)
            result = self.receive_connect(data[8:])
        elif action == 1:  # ANNOUNCE
            logging.debug('Received an ANNOUNCE response for transaction %d', transaction_id)
            result = self.receive_announce(data[8:])
        elif action == 2:  # SCRAPE
            logging.debug('Received a SCRAPE response for transaction %d', transaction_id)
            result = None
        elif action == 3:  # ERROR
            logging.debug('Received an ERROR response for transaction %d', transaction_id)
            raise Exception(data[8:])

        self.pending_futures[transaction_id].set_result(result)
        del self.pending_futures[transaction_id]

    def request_connection_id(self):
        self.connection_id = 0x41727101980

        return self.send_request(0)

    def receive_connect(self, data):
        self.connection_id = struct.unpack('!Q', data)[0]
        self.connection_id_age = datetime.now()

        logging.debug('Received a connection id: %d', self.connection_id)

    def receive_announce(self, data):
        interval, leechers, seeders = struct.unpack('!III', data[:12])
        num_peers = seeders + leechers
        peers = []

        for chunk in utils.grouper(6, data[12:12 + 6 * num_peers]):
            peers.append(Peer(*utils.unpack_peer_address(''.join(chunk))))

        return TrackerResponse(peers, interval)

    @coroutine
    def send_request(self, action, structure=None, arguments=None, transaction_id=None, attempt=1):
        logging.debug('Sending a type %d message', action)

        if action != 0 and datetime.now() - self.connection_id_age > timedelta(minutes=1):
            logging.debug('Connection id is too old or has not been established. Doing that before sending the actual message...')

            self.requesting_connection_id = True
            yield self.request_connection_id()

        if transaction_id is None:
            transaction_id = random.getrandbits(32)
            logging.debug('No transaction id was given. Generated a new one: %s', transaction_id)

        data = struct.pack('!QII', self.connection_id, action, transaction_id)

        if structure is not None and arguments is not None:
            data += struct.pack(structure, *arguments)

        if transaction_id not in self.pending_retries:
            self.pending_futures[transaction_id] = Future()

        self.pending_retries[transaction_id] += 1
        count = self.pending_retries[transaction_id]

        if count > 8:
            logging.error('Tracker did not respond to transaction %d after 8 tries', transaction_id)
            raise RuntimeError('Request was retried 8 times with no response')

        if transaction_id in self.pending_timers:
            IOLoop.instance().remove_timeout(self.pending_timers[transaction_id])

        retry_request = lambda: self.send_request(action, structure, arguments, transaction_id, attempt=attempt + 1)
        self.pending_timers[transaction_id] = IOLoop.instance().add_timeout(timedelta(seconds=15 * 2 ** count), retry_request)

        logging.debug('Current transaction has already been sent %d times.', self.pending_retries[transaction_id] - 1)
        logging.debug('Resending in %d seconds.', 15 * 2 ** count)

        self.stream.write(data)
        result = yield self.pending_futures[transaction_id]

        raise Return(result)

    @coroutine
    def announce(self, peer_id, port, event=None, num_wanted=-1):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.stream = UDPStream(self.socket)

        try:
            self.stream.connect((self.host, self.port))
        except socket.error:
            raise TrackerFailure('Could not connect to tracker')

        self.stream.read_until_close(self.data_received, self.data_received)
        logging.debug('Announcing to UDP tracker...')

        try:
            response = yield self.send_request(
                action=1,
                structure='!20s20sQQQIIIiH',
                arguments=[
                    self.torrent.info_hash(),
                    peer_id,
                    self.torrent.downloaded,
                    self.torrent.remaining,
                    self.torrent.uploaded,
                    self.events[event],
                    0,
                    0,
                    -1,
                    port
                ]
            )

            logging.debug('Tracker responded with %s', repr(response))

            raise Return(response)
        except RuntimeError:
            raise TrackerFailure('Tracker did not respond after 8 attempts')