示例#1
0
class Session:

    def __init__(self, host_tcp_port, host_address, mode):
        self.mode = mode
        self.locally_reserved_ports = []
        self.local_udp_port = 0
        self.client_ip = ""
        self.client_udp_port = 0
        self.client_address = (self.client_ip, self.client_udp_port)
        self.host_ip = host_address
        self.host_tcp_port = host_tcp_port
        self.host_udp_port = 0
        self.host_address = (self.host_ip, self.host_udp_port)
        self.server_udp_socket = socket(AF_INET, SOCK_DGRAM)
        self.server_tcp_socket = socket(AF_INET, SOCK_STREAM)
        self.tcp_connection_socket = socket(AF_INET, SOCK_STREAM)
        self.multipart_message_buffer = ""
        self.multipart_message_handling_over = True
        self.features = Features()
        if mode == 'client':
            self.   add_multipart()
            self.init_connections()
            #self.udp_client_socket = socket(AF_INET, SOCK_DGRAM)
            self.start_udp_communication()
        if mode == 'proxy':
            self.init_connections()
            self.start_proxy_service()
            self.client_ip = ('', 0)


    def init_connections(self):
        if self.mode == 'client':
            self.scan_available_port_and_bind(type='udpserver')
            # udp client has no neet to bind
            # self.scan_available_port_and_bind(type='')
            self.scan_available_port_and_bind(type='tcpclient')
            message = self.build_handshake_message()
            self.exchange_port_information()
        elif self.mode == 'proxy':
            self.scan_available_port_and_bind(type='tcpclient')
            self.scan_available_port_and_bind(type='tcpserver')
            self.scan_available_port_and_bind(type='udpserver')

    def start_proxy_service(self):
        self.wait_tcp_connection_and_init()
        self.start_udp_proxy()

    def wait_tcp_connection_and_init(self):

        print("Waiting for tcp connection..")
        print("Proxy supports only one client at the time")
        # listen to clients, backlog argument specifies the max no. of queued conns
        self.server_tcp_socket.listen(20)

        while True:
            try:
                print('\nWaiting to receive message from client')
                self.client_tcp_socket, address = self.server_tcp_socket.accept()
                client_ip, client_port = address
                data = self.client_tcp_socket.recv(70)
                # modify self.client address so that the port is same as used by udp
                clients_message_in_parts = data.decode("utf-8").split(" ")
                self.client_udp_port = clients_message_in_parts[1]
                self.client_address = (client_ip, int(self.client_udp_port))

                print(" ".join(clients_message_in_parts))
                modified_message_in_parts = clients_message_in_parts
                modified_message_in_parts[1] = " " + str(self.local_udp_port) + " "
                print("in parts: {}".format(modified_message_in_parts))

                modified_message = " ".join(modified_message_in_parts).encode('utf-8')
                modified_message_as_bytes  = bytes("".join(modified_message_in_parts).encode('utf-8'))


                print("clients udp port: {}".format(self.client_udp_port))
                if data:
                    print("\nData: {}.".format(data))
                    print("Client type: {}".format(type(self.client_tcp_socket)))
                    print("Client address: {}".format(self.client_ip))
                    try:
                        print("\nHost address: {}:{}".format(self.host_ip, self.host_tcp_port))
                        self.tcp_connection_socket.sendto(modified_message_as_bytes, (self.host_ip, self.host_tcp_port))
                        print("sent: {}\nto: {}".format(modified_message_as_bytes, (self.host_ip, self.host_tcp_port)))
                        server_response = self.tcp_connection_socket.recv(1024)
                        print(server_response)
                    except Exception as e:
                        print(str(e))
                        #print('sent %s bytes back to %s' % (data, address))
                    hosts_response_in_parts = server_response.decode("utf-8").split(" ")
                    print("host_response_in_parts: {}".format(server_response))
                    print("self.host_udp_port = hosts_response_in_parts[1]")
                    print("self.host_udp_port = {}".format(hosts_response_in_parts[1]))
                    self.host_udp_port = int(hosts_response_in_parts[1].strip('\x00'))

                    # save hosts upd address as a tuple
                    self.host_address = (self.host_ip, self.host_udp_port)
                    print("self.host_address: {}.".format(self.host_address))
                    print("response:")
                    print("".join(hosts_response_in_parts))


                    modified_response_in_parts = hosts_response_in_parts
                    modified_response_in_parts[1] = str(self.local_udp_port)
                    modified_response_as_bytes = bytes(" ".join(modified_response_in_parts).encode('utf-8'))
                    self.client_tcp_socket.send(modified_response_as_bytes)

                self.client_tcp_socket.close()

            except ValueError as e:
                print(str(e))
            if (server_response == b'Invalid command.\r\n'):
                print("Invalid command.")
            else:
                break

    def start_udp_proxy(self):
        EOM = False
        while not EOM:
            print("Listening udp traffic to forward..\n")
            try:
                msg_received, sender_address = self.server_udp_socket.recvfrom(70)
                print("Message received")
                print("from: {}".format(sender_address))
                self.pretty_print_packed_msg(msg_received)
                print("client: {}".format(self.client_address))
                print("host: {}".format(self.host_address))

                print(sender_address == self.host_address or sender_address == gethostbyname(self.host_ip))
                if sender_address == self.client_address:
                    self.server_udp_socket.sendto(msg_received, self.host_address)
                    print("sent to: {}.\n".format(self.host_address))
                elif sender_address == self.host_address or sender_address == (str(gethostbyname(self.host_ip)),self.host_udp_port):
                    EOM = struct.unpack('!??HH64s', msg_received)[0]
                    self.server_udp_socket.sendto(msg_received, self.client_address)
                    print("sent to: {}.".format(self.client_address))
            except Exception as e:
                print(str(e))
        print("Last message forwarded. Closing proxy.")


    def start_udp_communication(self):
        """
        starts udp transaction for exchanging questions/answers
        :return: none
        """
        opening_statement = "Ekki-ekki-ekki-ekki-PTANG.".encode('utf-8')
        packed_statement = struct.pack('!??HH64s', True, True, len(opening_statement), 0, opening_statement)
        self.send_udp_message_to_host(packed_statement)
        print("sent: {}".format(opening_statement))

        # listen and response
        EOM = False
        while not EOM:
            print('listening..')
            if EOM:
                break
            try:
                msg_received, addr = self.server_udp_socket.recvfrom(70)
                unpacked_msg = struct.unpack('!??HH64s', msg_received)
                print("\nReceived:")
                self.pretty_print_packed_msg(msg_received)
                EOM = unpacked_msg[0]
                ACK = unpacked_msg[1]
                content_length = unpacked_msg[2]
                remaining_content = unpacked_msg[3]

                # print("need to handle multipart messags: {}".format(remaining_content > 0))
                # print("need to handle remaining content: {}".format(not self.multipart_message_handling_over))
                question = str(unpacked_msg[4].decode('utf-8'))

                if remaining_content > 0:
                    print("remaining content was larger than 0. So we have to handle multipart messages")
                    self.multipart_message_handling_over = False

                if not self.multipart_message_handling_over:
                    print("multipart handling is not over")
                    complete_question = self.handle_multipart_message(question, content_length, remaining_content)
                    print("complete question: {}".format(complete_question))

                    if complete_question:
                        self.answer_to_question(complete_question, EOM)
                elif self.multipart_message_buffer == "":
                    self.answer_to_question(question, EOM)

            except KeyboardInterrupt as KI:
                print(msg_received)
                if (input('Q?') == 'y'):
                    sys.exit(1)

            except Exception as e:
                print("ex. "+str(e))

    def answer_to_question(self, question, EOM):

        answer_to_question = answer(question)
        # tell the user what's happening
        print("----------------------------------------")
        print("Question received: {}".format(question))
        print("Answer: {}".format(answer_to_question))

        if len(answer_to_question) > 64:
            print("handle sending of a multipart message")
            first_part_of_the_answer = answer_to_question[:64].encode('utf-8')
            second_part_of_the_answer = answer_to_question[64:].encode('utf-8')

            print(first_part_of_the_answer)
            print(second_part_of_the_answer)

            first_answer_packed = struct.pack('!??HH64s', EOM, True, len(first_part_of_the_answer), len(second_part_of_the_answer), first_part_of_the_answer)
            second_answer_packed = struct.pack('!??HH64s', EOM, True, len(second_part_of_the_answer), 0, second_part_of_the_answer)
            self.send_udp_message_to_host(first_answer_packed)
            print("Sent: ")
            self.pretty_print_packed_msg(first_answer_packed)
            self.send_udp_message_to_host(second_answer_packed)
            print("Sent: ")
            self.pretty_print_packed_msg(second_answer_packed)

        else:

            byte_answer = answer_to_question.encode('utf-8')
            packed_answer = struct.pack('!??HH64s', EOM, True, len(byte_answer), 0, byte_answer)
            print("Sent:")
            self.pretty_print_packed_msg(packed_answer)

            self.send_udp_message_to_host(packed_answer)

    def scan_available_port_and_bind(self, type):
        """
        :param socket: socket to bind
        :param host:  host to bind the socket
        :param type: what mode to bind the socket
        :return: port
        """
        for port in range(100):

            port_to_try = 10000 + port

            try:
                if type == 'udpserver':
                    print("Bind {} to port {}:{}".format(self.server_udp_socket, gethostname(), port_to_try))
                    self.server_udp_socket.bind(('', port_to_try))
                    self.server_udp_socket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
                    self.local_udp_port = port_to_try
                    if port_to_try in self.locally_reserved_ports:
                        port_to_try += 1
                    print('udp server binded')

                elif type == 'tcpserver':
                    print("Bind {} to port {}:{}".format(self.server_tcp_socket, gethostname(), port_to_try))
                    self.server_tcp_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
                    self.server_tcp_socket.bind(('', port_to_try))
                    if port_to_try in self.locally_reserved_ports:
                        port_to_try += 1
                    self.host_tcp_port = port_to_try
                    print('tcp server binded')

                elif type == 'tcpclient':
                    self.tcp_connection_socket.connect((self.host_ip, self.host_tcp_port))
                    print("Bind {} to port {}:{}".format(self.tcp_connection_socket, self.host_ip, self.host_tcp_port))
                    self.host_tcp_port = port_to_try
                    print('tcpclient init here')

                available_port = port_to_try
                return available_port
                break
            except Exception as e:
                print(str(e))

    def build_handshake_message(self):

        # builds the init message which is sent with tcp to negotiate extra features
        #:return: built message in bytes encoded to 'utf-8'

        features = ('{} {} {} {}'.format(self.features.get_multipart_status()[1],
                                         self.features.get_confidentiality_status()[1],
                                         self.features.get_integrity_status()[1],
                                         self.features.get_availability_status()[1]))

        features = features.replace('None', '').strip(" ")
        print("features: '{}'".format(features))
        message = 'HELO {} {}'.format(self.local_udp_port, features)
        if message.endswith(' '):
            message.rstrip(' ')
        message += "\r\n"
        print(message)
        return bytes(message.encode('utf-8'))

    def exchange_port_information(self):
        """
        TODO: ADD EXTRA FEATURES
        Get hosts port for udp-connection
        """
        # craft the message and send it
        self.send_tcp_message(self.build_handshake_message())

        # receive answer and parse port from it
        received = self.tcp_connection_socket.recv(1024)
        try:
            self.host_udp_port = int(received.decode('utf-8').split(' ')[1].split('\r')[0])
        except ValueError as VE:
            print('uusiksi portin parse')

        # tell the user what's happening
        print("Received msg: {}.".format(received))
        print("Host listens on port: {}.".format(self.host_udp_port))
        print("Clients UDP port: {} has been sent.\n\
               Closing TCP-socket..".format(self.local_udp_port))
        self.host_address = self.host_ip, self.host_udp_port
        # close connection
        self.tcp_connection_socket.close()

    def send_tcp_message(self, message):
        if type(message) != bytes:
            message = message.encode('utf-8')
        try:
            self.tcp_connection_socket.send(message)
            print(message)
            print("send_tcp_messag: {}.".format(self.tcp_connection_socket.getsockname()))
        except IOError as e:
            if e.errno == errno.EPIPE:
                self.tcp_connection_socket.connect((self.host_ip, self.host_tcp_port))
        print('message sent')

    def send_udp_message_to_host(self, message):
        """
        :param message: message to sent
        :param sock: UDP socket to use
        """
        self.server_udp_socket.sendto(message, (self.host_ip, self.host_udp_port))

        self.pretty_print_packed_msg(message)
        print("Sent to {} from {}".format(self.host_address, self.server_udp_socket.getsockname()))

    def add_multipart(self):
        if input('Add multipart feature? [y/n]: ') == 'y':
            self.features.set_multipart_status(True)


    def handle_multipart_message(self, message, content_length, remaining_content):
        print("\nhandling multipart message:")
        self.multipart_message_buffer = self.multipart_message_buffer + message
        print("\t\tbuffer: {}.".format(self.multipart_message_buffer))
        print("\t\tbuffer length: {}.".format(len(self.multipart_message_buffer)))
        self.multipart_message_handling_over = False

        # print("message: {}.".format(message))
        # print("content length: {}".format(content_length))
        # print("remaining content: {}".format(remaining_content))
        # print("length of buffer: {}".format(len(self.multipart_message_buffer)))

        if remaining_content == 0:
            complete_multipart_message = self.multipart_message_buffer
            self.multipart_message_buffer = ""
            self.multipart_message_handling_over = True
            print("multipart message compeleted: {}".format(complete_multipart_message))
            return complete_multipart_message

    def handle_remaining_multipart_content(self, msg):
        print("\nHandling remaining content")
        print("Buffer: {}".format(self.question_buffer))
        print("Partial question: {}".format(msg))
        question_to_return = self.question_buffer[0] + msg
        self.question_buffer[0] = None
        print("remaining content parsed together: {}".format(question_to_return))
        print("question buffer after handling remaining content\n{}.".format(self.question_buffer))
        return question_to_return

    def data_remaining(self, data_remaining):
        if data_remaining > 0:
            return True
        else:
            return False

    def pretty_print_packed_msg(self,msg):
        unpacked = struct.unpack('!??HH64s', msg)
        print("\nMessage unpacked:")
        print("\t\tEOM:\t\t\t{}".format(unpacked[0]))
        print("\t\tACK:\t\t\t{}".format(unpacked[1]))
        print("\t\tContent length:\t\t{}".format(unpacked[2]))
        print("\t\tRemaining content:\t{}".format(unpacked[3]))
        print("\t\tContent:\t\t{}\n".format(unpacked[4].decode('utf-8').strip("\x00")))