def append_from_file(self, input_file_str): linelist = file_to_list(input_file_str) for line in linelist: result = [word.strip() for word in line.split(' ')] if len(result) != 3: self.__table['ERROR'] = self.addrflag('MALFORMED', 'ENTRY') msg = 'Input \'{}{}{}\' from file \'{}{}{}\' is malformed. Unable to add entry to table.\n'.format(K.color.bold.WHT, line, K.NRM, K.color.bold.WHT, input_file_str, K.NRM) log(logstat.ERR, funcname(), msg) else: if result[2] == DNS_table.flag.NS.value and self.__ts_hostname == '__NONE__': self.__ts_hostname = result[0] msg = 'ts_hostname assigned as \'{}{}{}\'.'.format(K.color.bold.WHT, result[0], K.NRM) log(logstat.LOG, funcname(), msg) elif result[2] == DNS_table.flag.A.value: self.__table[result[0]] = self.addrflag(result[1], result[2]) msg = '{}\'{} : ({}, {}){}\' added to table from file {}\'{}\'{}.'.format(K.color.bold.WHT, result[0], result[1], result[2], K.NRM, K.color.bold.WHT, input_file_str, K.NRM) log(logstat.LOG, funcname(), msg) print('')
def is_valid_hostname(hostname): """Uses gethostbyname to determine if hostname valid, or not Args: hostname: str desired hostname to verify Returns: str representing the IP address of the validated hostname; if hostname is invalid, returns None Raises: EnvironmentError if hostname is invalid (gethostbyname fails) """ try: ipaddr = gethostbyname(hostname) except EnvironmentError: msg = 'Host by name \'{}{}{}\' is not available.\n'.format(K.color.bold.WHT, ls_hostname, K.NRM) log(logstat.ERR, funcname(), msg) return None msg = 'Verified hostname and IP address ({}{}{} : {}{}{})\n'.format(K.color.CYN, hostname, K.NRM, K.color.CYN, ipaddr, K.NRM) log(logstat.OK, funcname(), msg) return ipaddr
def main(argv): """Main function, where client function is called Args: Command line arguments (as per sys.argv) argv[1] - ts2_listen_port desired port number for ts2 program argv[2] - input_file (OPTIONAL) desired name of input file of entries for ts2 program Returns: Exit status, by default, 0 upon exit Raises: KeyboardInterrupt if user terminates program with (Ctrl + c) before completion SystemExit causes program to exit upon KeyboardInterrupt """ (ts2_portno, input_file_str) = check_args(argv) print('') table = DNS_table() table.append_from_file(input_file_str) try: msg = 'Starting TS1 server. Hit (Ctrl + c) to quit.\n' log(logstat.LOG, funcname(), msg) start_ts2(ts2_portno, table) except KeyboardInterrupt, SystemExit: print('') msg = 'User terminated program before completion.\n' log(logstat.LOG, funcname(), msg)
def main(argv): """Main function, where client function is called Args: Command line arguments (as per sys.argv) argv[1] - ls_hostname desired hostname for LS machine argv[2] - ls_listen_port desired port number for LS machine argv[3] - input_file_name (OPTIONAL) desired name of input file (queried hostnames) argv[4] - output_file_name (OPTIONAL) desired name of output file (resolved host names) Returns: Exit status, by default, 0 upon exit Raises: KeyboardInterrupt if user terminates program with (Ctrl + c) before completion SystemExit causes program to exit upon KeyboardInterrupt """ (ls_info, file_str) = check_args(argv) # ls_info[0] is LS's hostname # ls_info[1] is LS's port number # file_str[0] is the query input file string # file_str[1] is the query output file string print('') # read the input file into a list hostname_list = file_to_list(file_str[0]) if len(hostname_list) > 0: resolved_list = [] # if the hostname list has at least one element, proceed to LS server try: msg = 'Starting client routine. Hit (Ctrl + c) to quit.\n' log(logstat.LOG, funcname(), msg) resolved_list = query_ls(ls_info, hostname_list) except KeyboardInterrupt, SystemExit: print('') msg = 'User terminated program before completion.\n' log(logstat.LOG, funcname(), msg) if len(resolved_list) > 0: # if the resolved_list has at least one element, write to file write_to_file_from_list(file_str[1], resolved_list, 'w')
def append_from_str(self, input_str): result = str_to_list(input_str, ' ') if len(result) != 3: self.__table['ERROR'] = self.addrflag('MALFORMED', 'ENTRY') else: if result[2] == DNS_table.flag.NS.value and self.__ts_hostname == '__NONE__': self.__ts_hostname = result[0] msg = 'ts_hostname assigned as \'{}{}{}\'.'.format(K.color.bold.WHT, result[0], K.NRM) log(logstat.LOG, funcname(), msg) elif result[2] == DNS_table.flag.A.value: self.__table[result[0]] = self.addrflag(result[1], result[2]) msg = '{}\'{} : ({}, {}){}\' added to table from string {}\'{}\'{}.'.format(K.color.bold.WHT, result[0], result[1], result[2], K.NRM, K.color.bold.WHT, input_str, K.NRM) log(logstat.LOG, funcname(), msg)
def has_hostname(self, query): found = False q = query.lower() for key, value in self.__table.iteritems(): if key.lower() == q: found = True msg = 'Found queried hostname \'{}{}{}\' with value \'{}({}, {}){}\' in table.'.format(K.color.bold.WHT, query, K.NRM, K.color.bold.WHT, value[0], value[1], K.NRM) log(logstat.OK, funcname(), msg) break if not found: msg = 'Unable to find hostname \'{}{}{}\' in table.'.format(K.color.bold.WHT, query, K.NRM) log(logstat.LOG, funcname(), msg) return found
def main(argv): """Main function, where LS function is called Args: Command line arguments (as per sys.argv) argv[1] - ls_listen_port desired port number for LS program argv[2] - ts1_hostname hostname for desired TS1 server argv[3] - ts1_listen_port port number for desired TS1 server, corresponds to ts1_hostname argv[4] - ts2_hostname hostname for desired TS2 server argv[5] - ts2_listen_port port number for desired TS2 server, corresponds to ts2_hostname Returns: Exit status, by default, 0 upon exit Raises: KeyboardInterrupt if user terminates program with (Ctrl + c) before completion SystemExit causes program to exit upon KeyboardInterrupt """ (ls_portno, ts1_info, ts2_info) = check_args(argv) # ts1_info[0] is TS1's hostname # ts1_info[1] is TS1's port number # ts2_info[0] is TS2's hostname # ts2_info[1] is TS2's port number print('') try: msg = 'Starting LS server. Hit (Ctrl + c) to quit.\n' log(logstat.LOG, funcname(), msg) start_ls(ls_portno, ts1_info, ts2_info) except KeyboardInterrupt, SystemExit: print('') msg = 'User terminated program before completion.\n' log(logstat.LOG, funcname(), msg)
def udp_socket_open(): """Opens a UDP (datagram) socket, with a call to setsockopt (so the hostname can be immediately reused after a disconnect) Args: (none) Returns: An open UDP (datagram) socket Raises: EnvironmentError if socket fails to open """ sock = 0 msg = '' try: sock = socket(AF_INET, SOCK_DGRAM) except EnvironmentError: msg = 'Socket open error.\n' log(logstat.ERR, funcname(), msg) exit() sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) msg = 'Opened new datagram socket.\n' log(logstat.OK, funcname(), msg) hostname = gethostname() ipaddr = gethostbyname(hostname) msg = 'Hostname is \'{}{}{}\'.'.format(K.color.bold.WHT, hostname, K.NRM) log(logstat.LOG, funcname(), msg) msg = 'IP address is \'{}{}{}\'.\n'.format(K.color.bold.WHT, ipaddr, K.NRM) log(logstat.LOG, funcname(), msg) return sock
def start_ls(ls_portno, ts1_info, ts2_info): """Starts the LS server routine by opening a socket so that it can receive client queries -- also opens a socket for the TS1 and TS2 servers respectively so that it can forward the client queries to the TS servers, and await any potential replies from them. Routine runs indefintely unless connection is severed or client quits with KeyboardInterrupt. Args: ls_portno: int desired port number for LS server ts1_info: tuple consists of (ts1_hostname, ts1_portno) ts2_info: tuple consists of (ts2_hostname, ts2_portno) Returns: (none) Raises: (none) """ ts1_hostname = ts1_info[0] ts1_portno = ts1_info[1] ts2_hostname = ts2_info[0] ts2_portno = ts2_info[1] ls_binding = ('', ls_portno) ls_sock = udp_socket_open() ls_sock.bind(ls_binding) ts1_ipaddr = is_valid_hostname(ts1_hostname) ts1_binding = (ts1_hostname, ts1_portno) ts1_sock = udp_socket_open() ts1_sock.setblocking(False) ts2_ipaddr = is_valid_hostname(ts2_hostname) ts2_binding = (ts2_hostname, ts2_portno) ts2_sock = udp_socket_open() ts2_sock.setblocking(False) ts_sockets = [ts1_sock, ts2_sock] resolved_sockets = [] query = '' data_in = '' data_out = '' msg_in = '' msg_out = '' msg_log = '' timeout_val = DEFAULT_TS_TIMEOUT_VALUE while True: # receive data from client, decode for logging (data_in, (client_ipaddr, client_portno)) = ls_sock.recvfrom(BUFFER_SIZE) client_binding = (client_ipaddr, client_portno) query = data_in.decode('utf-8') # retrieve client hostname from client_ipaddr client_hostname = gethostbyaddr(client_ipaddr)[0] msg_log = logstr(client_hostname, client_ipaddr, query) log(logstat.IN, funcname(), msg_log) # send data to TS1 ts1_sock.sendto(data_in, ts1_binding) msg_log = logstr(ts1_hostname, ts1_ipaddr, query) log(logstat.OUT, funcname(), msg_log) # send data to TS2 ts2_sock.sendto(data_in, ts2_binding) msg_log = logstr(ts2_hostname, ts2_ipaddr, query) log(logstat.OUT, funcname(), msg_log) # use select with timeout_val to determine which socket to recv from resolved_sockets, _, _ = select(ts_sockets, [], [], timeout_val) if resolved_sockets: # if TS1 or TS2 received data, we proceed here for ts in resolved_sockets: hostname = '' ipaddr = '' # receive incoming data from TS socket # data received from TS socket will be sent to client data_in = ts.recv(BUFFER_SIZE) data_out = data_in # decode message for logging msg_in = data_in.decode('utf-8') msg_out = msg_in # log incoming data from TS socket if ts is ts1_sock: hostname = ts1_hostname ipaddr = ts1_ipaddr elif ts is ts2_sock: hostname = ts2_hostname ipaddr = ts2_ipaddr msg_log = logstr(hostname, ipaddr, msg_in) log(logstat.IN, funcname(), msg_log) else: # TS1 and TS2 did not receive data after timeout_val seconds, log it msg_log = '[Connection timeout]' log(logstat.LOG, funcname(), msg_log) # prepare 'HOST NOT FOUND' message for client msg_out = '{} - {}'.format(query, DNS_table.flag.HOST_NOT_FOUND.value) # encode prepared message, will send to client shortly data_out = msg_out.encode('utf-8') # send outgoing data to client ls_sock.sendto(data_out, client_binding) msg_log = logstr(client_hostname, client_ipaddr, msg_out) log(logstat.OUT, funcname(), msg_log) print('')
def start_ts2(ts2_portno, table): """Starts the TS2 server routine by opening a socket so that it can receive queries from LS. The query is used to run a look-up for a hostname mapping within table, and a reply is sent if the query is resolved. Args: ts2_portno: int desired port number for TS2 server table: DNS_table table containing (hostname : addrflag) mappings Returns: (none) Raises: (none) """ ts2_binding = ('', ts2_portno) ts2_sock = udp_socket_open() ts2_hostname = gethostname() ts2_sock.bind(ts2_binding) query = '' data_in = '' data_out = '' msg_out = '' msg_log = '' while True: # receive data from LS, decode for logging data_in, (ls_ipaddr, ls_portno) = ts2_sock.recvfrom(BUFFER_SIZE) ls_binding = (ls_ipaddr, ls_portno) query = data_in.decode('utf-8') # retrieve ls_hostname from ls_ipaddr ls_hostname = gethostbyaddr(ls_ipaddr)[0] msg_log = logstr(ls_hostname, ls_ipaddr, query) log(logstat.IN, funcname(), msg_log) # search table for query if table.has_hostname(query): # if query is resolved, reply to LS # prepare outgoing data to LS # original specificaton, as per PDF msg_out = '{} {} {}'.format(query, table.ipaddr(query), table.flagtype(query)) """ # new specification, as mentioned by professor msg_out = '{} {} {} {}'.format(query, table.ipaddr(query), table.flagtype(query), ts2_hostname) """ # send outgoing data to LS data_out = msg_out.encode('utf-8') ts2_sock.sendto(data_out, ls_binding) # log outgoing data to LS msg_log = logstr(ls_hostname, ls_ipaddr, msg_out) log(logstat.OUT, funcname(), msg_log) else: # if query is not resolved, notify user log(logstat.LOG, funcname(), 'No outgoing data will be sent for this query.') print('')
def query_ls(ls_info, hostname_list): """Starts the client routine by opening a socket so that it can communicate with the LS server, and send it the contents of hostname_list. Client will then receive replies from LS; each reply will be appended to a resolved_list and returned from this function. Args: ls_info: tuple consists of ls_hostname (LS hostname) and ls_portno (LS port number) hostname_list: list consists of hostnames (each a [str]) to query, which LS server will receive from client Returns: a [str] of replies from LS Raises: socket.timeout if no data is received from LS by timeout_val seconds. """ ls_hostname = ls_info[0] ls_portno = ls_info[1] ls_ipaddr = is_valid_hostname(ls_hostname) ls_binding = (ls_hostname, ls_portno) timeout_val = DEFAULT_TS_TIMEOUT_VALUE * 2 ls_sock = udp_socket_open() ls_sock.settimeout(timeout_val) resolved_list = [] if ls_ipaddr: # if ls_hostname yields a valid ip address data_in = '' msg_in = '' msg_log = '' for query in hostname_list: # log query msg_log = 'Querying hostname \'{}{}{}\'...'.format( K.color.bold.WHT, query, K.NRM) log(logstat.LOG, funcname(), msg_log) # send encoded query to LS ls_sock.sendto(query.encode('utf-8'), ls_binding) msg_log = logstr(ls_hostname, ls_ipaddr, query) log(logstat.OUT, funcname(), msg_log) # upon the next recv call, # we are expecting the query results from LS # receive data from LS, decode it try: data_in = ls_sock.recv(BUFFER_SIZE) except timeout: # LS is given a maximum of 10 sec to respond -- # since TS1 and TS2 have a max of 5 secs to respond, # RTT must be accounted for msg_log = '[Connection timeout]' log(logstat.LOG, funcname(), msg_log + '\n') msg_in = data_in.decode('utf-8') msg_log = logstr(ls_hostname, ls_ipaddr, msg_in) log(logstat.IN, funcname(), msg_log) # append query result to resolved_list resolved_list.append(msg_in) msg_log = 'Added \'{}{}{}\' to resolved_list.'.format( K.color.bold.WHT, msg_in, K.NRM) log(logstat.LOG, funcname(), msg_log) print('') return resolved_list
resolved_list = [] # if the hostname list has at least one element, proceed to LS server try: msg = 'Starting client routine. Hit (Ctrl + c) to quit.\n' log(logstat.LOG, funcname(), msg) resolved_list = query_ls(ls_info, hostname_list) except KeyboardInterrupt, SystemExit: print('') msg = 'User terminated program before completion.\n' log(logstat.LOG, funcname(), msg) if len(resolved_list) > 0: # if the resolved_list has at least one element, write to file write_to_file_from_list(file_str[1], resolved_list, 'w') else: # if the hostname list is empty, notify the user log( logstat.ERR, funcname(), 'No data was read from \'{}{}{}\'.\n'.format( K.color.bold.WHT, file_str[0], K.NRM)) print('') return EX_OK if __name__ == '__main__': # Program execution begins here. retval = main(argv)