def check_crc(inputstring, poly):
    left_index = 0
    right_index = len(poly)
    first_operand = inputstring[left_index:right_index]
    xored = BitArray(bin='0000')
    while right_index <= len(inputstring):
        xored = first_operand ^ poly
        new_left = 0
        while new_left < len(xored) and xored[new_left] == 0:
            new_left += 1
        needed_bits = len(poly) - (len(xored) - new_left)
        if right_index + needed_bits > len(inputstring):
            break
        xored = xored[new_left:len(xored)]
        xored.append(inputstring[right_index:right_index + needed_bits])
        first_operand = xored
        right_index = right_index + needed_bits
        left_index = right_index - len(poly)
    return xored.all(0)
 def testAll(self):
     a = BitArray('0b0000101')
     self.assertTrue(a.all(1, [0, 2]))
     self.assertTrue(a.all(False, [-1, -2, -3, -4]))
示例#3
0
class Client:
    def __init__(self, ip_addr, port, filename):

        atexit.register(self.exit_handler)

        #list of peers (and connection info) that this client is connected to
        self.connection_list = list()
        self.listen_list = list()
        self.metainfo = Metainfo(filename)
        self.filename = filename
        self.ip_addr = ip_addr
        self.port = port
        self.peer_id = os.urandom(20)
        self.info_hash = self.metainfo.info_hash
        self.uploaded = 0
        self.downloaded = 0
        #if client has file, set left to 0 and bitfield to full

        self.tracker_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tracker_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                       1)
        self.tracker_socket.connect((socket.gethostbyname('localhost'), 6969))
        self.bitfield = BitArray(self.metainfo.num_pieces)

        if (self.check_for_file()):
            self.bitfield.set(True)
            self.left = 0

        else:
            self.bitfield.set(False)
            self.left = self.metainfo.file_length
        self.send_GET_request(0)

        self.listening_socket = socket.socket(socket.AF_INET,
                                              socket.SOCK_STREAM)
        self.listening_socket.setsockopt(socket.SOL_SOCKET,
                                         socket.SO_REUSEADDR, 1)
        self.listening_socket.bind(
            (socket.gethostbyname(self.ip_addr), self.port))
        self.listening_socket.listen(5)

        # while True:
        # 	(opentracker_socket, opentracker_addr) = self.socket.accept()
        # 	response = socket.recv(4096)
        # 	print(response)

        tracker_response = self.tracker_socket.recv(1024)
        print("Response: ", tracker_response)
        status = re.split(" ", str(tracker_response)[2:-1])
        if (status[1] != "200"):
            print("ERROR")

        else:
            print("parsing tracker response...")
            self.response = tracker_response
            self.handle_tracker_response()

        listening_thread = threading.Thread(target=self.listen_for_handshake)
        listening_thread.daemon = True
        listening_thread.start()

    def leech(self):
        while True:
            time.sleep(.2)
            if self.bitfield.all(True):
                print(self.bitfield.all(True))
                break
            #determine which piece to look for
            index = self.next_piece()
            #see who on peer list has the piece
            for connection in self.connection_list:
                if connection.has_piece(index):
                    #request the piece from the peer
                    print("Requesting piece ", str(index), " from ",
                          connection.peer_port)
                    self.request_piece(index, connection)
                    break
        #stop when bitfield is full
        print("done")
        self.reassemble_file()
        return
        #close connections but keep listening

    def send_have_message(self, index):
        have_message = bytearray(9)
        have_message[0:4] = struct.pack('>i', int(5))
        have_message[4] = struct.pack('>i', int(4))
        have_message[5:9] = struct.pack('>i', int(index))
        for listener in listen_list:
            print("Sending have message ", have_message, "to ", listener)
            socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            socket.connect(listener)
            socket.send(have_message)
            socket.close()
        return

    def check_for_file(self):
        if os.path.exists(self.filename):
            print("FILE EXISTS")
            #piece length 65536
            #file size 4641991

            #split file into temporary chunks
            f = open(self.filename, "rb")
            for i in range(self.metainfo.num_pieces - 1):
                temp_filename = "temp-" + str(i) + self.filename
                fout = open(temp_filename, 'wb')
                b = bytearray()
                b = f.read(self.metainfo.piece_length)
                if (b):
                    fout.write(b)
                piece_hash = hashlib.sha1()
                piece_hash.update(b)
                #print(i)
                #print(piece_hash.hexdigest())
                #print(self.metainfo.get_piece_hash(i))

            temp_filename = "temp-" + str(self.metainfo.num_pieces -
                                          1) + self.filename
            fout = open(temp_filename, 'w+b')
            while True:
                b = f.read(1)
                if (b):
                    fout.write(b)
                else:
                    break

            return True

        else:
            print("FILE DOESN'T EXIST")

            requesting_thread = threading.Thread(target=self.leech)
            requesting_thread.daemon = True
            requesting_thread.start()

            return False

        #if file is in local directory start running in seeder state
        #else if file is not in local directory run in leecher state

    def send_GET_request(self, event):
        get_request = bytearray(map(ord, "GET /announce?info_hash="))
        get_request.extend(
            map(ord,
                urllib.parse.quote_plus(self.metainfo.info_hash.digest())))
        get_request.extend(map(ord, "&peer_id="))
        get_request.extend(map(ord, urllib.parse.quote_plus(self.peer_id)))
        get_request.extend(map(ord, "&port="))
        get_request.extend(bytes(str(self.port), "ascii"))
        #ignore key
        get_request.extend(map(ord, "&uploaded="))
        get_request.extend(bytes(str(self.uploaded), "ascii"))
        get_request.extend(map(ord, "&downloaded"))
        get_request.extend(bytes(str(self.downloaded), "ascii"))
        get_request.extend(map(ord, "&left"))
        get_request.extend(bytes(str(self.left), "ascii"))
        get_request.extend(map(ord, "&compact=1"))
        if event == 0:
            get_request.extend(map(ord, "&event=started HTTP/1.1\r\n\r\n"))
        elif event == 1:
            get_request.extend(map(ord, "&event=completed HTTP 1.1\r\n\r\n"))
        elif event == 2:
            get_request.extend(map(ord, "&event=stopped HTTP/1.1\r\n\r\n"))
        else:
            get_request.extend(map(ord, " HTTP/1.1\r\n\r\n"))
        print("GET request: ", get_request)

        #send HTTP request to tracker
        self.tracker_socket.send(get_request)

        return get_request

    def handle_tracker_response(self):
        print("Raw Response: ", self.response)
        #decoded_response = self.response.decode('utf-8')
        #print("Decoded Response: ", decoded_response)
        split_decoded = self.response.split(b'\r\n\r\n')
        response_dict = decode(split_decoded[1])
        print("Tracker response: ", response_dict)

        #check for failure reason
        if b'failure reason' in response_dict:
            print(response_dict[b'failure reason'].decode('utf-8'))

        else:
            self.interval = response_dict[b'interval']
            #self.tracker_id = response_dict[b'tracker id']
            self.complete = response_dict[b'complete']
            self.incomplete = response_dict[b'incomplete']

            #parse list of peers
            peerlist = list()
            unparsed_peers = response_dict[b'peers']
            print(unparsed_peers)

            #add peers to list of tuples (IP, port)
            for x in range(len(unparsed_peers) // 6):
                ip = socket.inet_ntoa(unparsed_peers[x * 6:x * 6 + 4])
                port = int.from_bytes(unparsed_peers[x * 6 + 4:x * 6 + 6],
                                      byteorder='big')
                #print("Reading peer ", ip, port)
                peerlist.append((ip, port))

            print(peerlist)
            self.peer_list = peerlist

            #for each peer in peer list, check if connected
            for (IP, port) in self.peer_list:
                #print(self.ip_addr, IP, self.ip_addr != IP)
                #print(self.port, port, self.port != port)
                if (IP != self.ip_addr) or (port != self.port):
                    for connection in self.connection_list:
                        if (IP == connection.peer_ip_addr) and (
                                port == connection.peer_port):
                            break
                    else:
                        #if not connected, initiate handshake with peer
                        print("Connect to peer ", IP, port)
                        handshake_thread = threading.Thread(
                            target=self.initiate_handshaking, args=(IP, port))
                        handshake_thread.daemon = True
                        handshake_thread.start()

                        handshake_thread.join()

    def initiate_handshaking(self, IP, port):
        handshake_message = self.generate_handshake_msg()
        new_connection = Connection(self, IP, port,
                                    BitArray(self.metainfo.num_pieces))
        #send start of handshake
        new_connection.sock.send(handshake_message)
        print("Handshake initiated ", handshake_message)
        handshake_response = new_connection.sock.recv(1024)

        #if handshake response is valid, save connection
        if handshake_response[0] == 18 and handshake_response[
                1:19] == b'URTorrent protocol' and handshake_response[
                    19:
                    27] == b'\x00\x00\x00\x00\x00\x00\x00\x00' and handshake_response[
                        27:47] == self.info_hash.digest():
            new_connection.peer_id = handshake_response[47:68]
            print("Received valid handshake response ", handshake_response)
            self.connection_list.append(new_connection)

            new_connection.start()

    def listen_to_peer(self, peer, address):
        while True:
            try:
                message = peer.recv(1024)
                if message:
                    print("received message")
                    #check messsage type
                    message_prefix = struct.unpack('>i', message[0:4])[0]
                    print("Message Prefix = ", message_prefix)
                    message_id = message[4]
                    print("Message ID = ", message_id)
                    if message_id == 6:
                        print("Request message received")
                        index = message[5:9]
                        begin = message[9:13]
                        length = message[13:17]
                        #request message
                        #check that peer is not choked
                        #send requested chunk
                        length_prefix = 9 + 65536
                        piece_payload = bytearray(b'\x07')
                        piece_payload.extend(index)
                        piece_payload.extend(begin)
                        print("Sending piece Message: ", piece_payload)
                        int_index = struct.unpack('>i', index)[0]
                        filename = "temp-" + str(
                            int_index) + self.metainfo.filename
                        f = open(filename, 'rb')
                        b = bytearray()
                        if int_index < self.metainfo.num_pieces - 1:
                            b = f.read(65536)
                        else:
                            b = f.read(self.metainfo.file_length %
                                       self.metainfo.piece_length)
                            #print("Last piece data: ", b)

                        piece_payload.extend(b)
                        piece_message = bytearray(
                            struct.pack('>i', len(piece_payload)))
                        piece_message.extend(piece_payload)
                        peer.send(piece_message)
                        self.uploaded += 1

                    elif message_id == 8:
                        #cancel message
                        print("Cancel Message")
                    elif message_id == 4:
                        #have message
                        print("Have Message")
                        #self.bitfield.bin[]
                    elif message_id == 5:
                        #bitfield message
                        #check that bitfield is correct length
                        if (len(self.bitfield.bin) == len(message[5:])):
                            print("Received bitfield of ", message[5:])
                        #self.peer_bitfield.bin = message[5:6+len(self.peer_bitfield)]
                        #print(self.peer_bitfield)
                    #default block size is 16384
                    elif message_id == 7:
                        #piece message
                        index = message[5:9]
                        begin = message[9:13]
                        piece = message[13:]
                        #save piece
                    else:
                        print("Invalid Message")

                    time.sleep(.1)
                else:
                    print("No data received")
                    time.sleep(1)
            except Exception as e:
                print(e)
                return False

    def next_piece(self):
        #find missing piece
        #print("Choose piece")
        while True:
            j = randint(0, self.metainfo.num_pieces - 1)
            if self.bitfield.bin[j] == '0':
                #print("Chose next piece index = ", j)
                return j
        else:
            return 0

    def request_piece(self, index, peer_connection):
        piece_request = bytearray(18)
        piece_request[0:4] = struct.pack('>i', int(13))
        piece_request[4] = 6
        piece_request[5:9] = struct.pack('>i', int(index))
        piece_request[9:13] = struct.pack('>i', int(0))
        if index < self.metainfo.num_pieces - 1:
            piece_request[13:17] = struct.pack('>i',
                                               int(self.metainfo.piece_length))
        else:
            print("Last Piece")
            file_size = self.metainfo.file_length
            last_piece_size = file_size % self.metainfo.num_pieces
            #print(last_piece_size)
            piece_request[13:17] = struct.pack('>i', int(last_piece_size))

        print("Sending piece request: ", piece_request)
        peer_connection.sock.send(piece_request)
        return 0

    #generate handshake message
    def generate_handshake_msg(self):
        handshake = bytearray(b'\x12')
        handshake.extend(map(ord, "URTorrent protocol"))
        handshake.extend(bytearray(8))
        handshake.extend(self.metainfo.info_hash.digest())
        handshake.extend(self.peer_id)
        #print("Handshake message: ", handshake)
        return handshake

    def reassemble_file(self):
        print("Reassembling File...")
        f = open(self.filename, 'ab')

        for i in range(self.metainfo.num_pieces):
            piece_filename = "temp-" + str(i) + self.filename
            piece = open(piece_filename, 'rb')
            b = bytearray()
            b = piece.read(65536)
            if not b:
                while True:
                    k = piece.read(1)
                    if k:
                        b.extend(k)
                    else:
                        break

            f.write(b)

    def exit_handler(self):
        self.send_GET_request(2)
        print("Client closing")
        self.tracker_socket.close()
        self.listening_socket.close()
        print("Cleaning up temporary files")
        for fl in os.listdir():
            if "temp" in fl:
                os.remove(fl)
        print("Quitting...")

    def listen_for_handshake(self):
        #listen for handshakes
        i = 0
        while True:

            i = i + 1

            try:
                peer_connection, address = self.listening_socket.accept()
                print(i)
                buf = peer_connection.recv(1024)
                #print(len(buf))
                #if message is handshake

                if buf[0] == 18 and buf[1:19] == b'URTorrent protocol' and buf[
                        19:27] == b'\x00\x00\x00\x00\x00\x00\x00\x00' and buf[
                            27:47] == self.info_hash.digest():
                    #if len(buf) > 0 and "URTorrent" in str(buf):
                    #ip = connection_socket.getsockname()[0]
                    #port = connection_socket.getsockname()[1]
                    #connection_socket.close()
                    print("Received valid handshake", buf)
                    #self.listen_list.append()

                    new_listener = (peer_connection.getsockname()[0], address)

                    self.listen_list.append(new_listener)
                    #print(self.listen_list[0])
                    peer_connection.send(self.generate_handshake_msg())

                    time.sleep(.1)

                    #send bitfield message
                    bitfield_message = bytearray(1 + self.metainfo.num_pieces)
                    bitfield_message[0:4] = struct.pack(
                        '>i', int(1 + self.metainfo.num_pieces))
                    bitfield_message[4] = 5
                    bitfield_message[5:] = self.bitfield
                    print("Sending bitfield message: ", bitfield_message)
                    peer_connection.send(bitfield_message)

                    #split off thread to listen for piece requests on this socket
                    peer_connection.settimeout(120)
                    listen_thread = threading.Thread(
                        target=self.listen_to_peer,
                        args=(peer_connection, address))
                    listen_thread.daemon = True

                    listen_thread.start()

                #	listen_thread.join()
                #listen = Listen(self)
                #listen.start()
                #self.listen_list[0].start()

            except Exception as exc:
                print(str(exc))
                peer_connection.close()
                break
            except KeyboardInterrupt:
                print("Closing")
                peer_connection.close()
                break