def import_from_file(hosts_path=None, file_path=None): """Import entries from a text file :param hosts_path: Path to the hosts file to update :param file_path: Path to the file containing the hosts entries to import :return: A dict containing the result and user message to output """ if hosts_path and not os.path.exists(hosts_path): return { 'result': 'failed', 'message': 'Cannot read hosts file: {0}'.format(hosts_path) } if not os.path.exists(file_path): return { 'result': 'failed', 'message': 'Cannot read import file: {0}'.format(file_path) } else: hosts = Hosts(path=hosts_path) pre_count = len(hosts.entries) import_file_output = hosts.import_file(import_file_path=file_path) post_count = len(hosts.entries) write_result = import_file_output.get('write_result') message = 'New entries:\t{0}\nTotal entries:\t{1}\n'.format( post_count - pre_count, write_result.get('total_written')) return {'result': import_file_output.get('result'), 'message': message}
def test_file_import_fails_when_not_readable(tmpdir): """ Test import fails if file to import is not readable """ hosts_file = tmpdir.mkdir("etc").join("hosts") hosts_file.write("82.132.132.132\texample.com\texample") hosts_entries = Hosts(path=hosts_file.strpath) result = hosts_entries.import_file('/invalid_file') assert result.get('result') == 'failed'
def test_import_file_returns_duplicate_correctly(tmpdir): """ Test that adding an entry that exists will return a duplicate count of 1 and a write count of 2 (where existing 82.132.132.132 is written along with new 10.10.10.10 entry) """ hosts_file = tmpdir.mkdir("etc").join("hosts") hosts_file.write("82.132.132.132\texample.com\texample\n") import_file = tmpdir.mkdir("input").join("infile") import_file.write("10.10.10.10\thello.com\n82.132.132.132\texample.com\texample\n") hosts_entries = Hosts(path=hosts_file.strpath) feedback = hosts_entries.import_file(import_file_path=import_file.strpath) add_result = feedback.get('add_result') write_result = feedback.get('write_result') assert add_result.get('duplicate_count') == 1 assert write_result.get('ipv4_entries_written') == 2
def test_import_file_increments_invalid_counter(tmpdir): """ Test that correct counters values are returned when a text file of host entries is imported Existing host file has: 1 ipv4 entry Import file has: 2 ipv4 entries plus 1 invalid entry Add should return 2 Dedupe will find a single duplicate Add will return 1 as invalid Write will write 1 new entry plus the existing entry (1 + 1 = 2) """ hosts_file = tmpdir.mkdir("etc").join("hosts") hosts_file.write("82.132.132.132\texample.com\texample\n") import_file = tmpdir.mkdir("input").join("infile") import_file.write("example\n\n10.10.10.10\thello.com\n82.132.132.132\texample.com\texample\n") hosts_entries = Hosts(path=hosts_file.strpath) import_file_result = hosts_entries.import_file(import_file.strpath) assert not import_file_result.get('result') == 'failed' import_file_write_result = import_file_result.get('write_result') assert import_file_result.get('invalid_count') == 1 assert import_file_write_result.get('ipv4_entries_written') == 2 assert import_file_write_result.get('total_written') == 2
class MulticastAnnouncerListener: def __init__(self, **kwargs): self.MCAST_GROUP = '224.1.1.1' self.MCAST_PORT = 4180 self.IS_ALL_GROUPS = True self.blacklisted_interfaces = ['lo', 'lo0'] self.blacklisted_ips = [] self.localSubnets = [] self.ips = {} self.logfile = kwargs['l'] self.hostsfile = kwargs['o'] self.input_hostsfile = kwargs['i'] self.seperator = kwargs['s'] self.verbose = kwargs['v'] self.name = kwargs['nickname'] self.blacklist = str(kwargs['bl']).split(",") self.blacklisted_subnets = [] self.log = logging.getLogger(__name__) syslog = logging.StreamHandler() formatter = logging.Formatter("%(message)s") syslog.setFormatter(formatter) self.log.setLevel(logging.DEBUG) self.log.addHandler(syslog) self.log = logging.LoggerAdapter(self.log, {'app_name': 'muCast'}) if self.input_hostsfile and not self.hostsfile: self.log.error( '[ERROR] You can only import a hosts file if you are also writing a hosts file via -o' ) os._exit(1) if self.hostsfile: self.hosts = Hosts(path=self.hostsfile) else: self.hosts = False if os.path.exists(self.input_hostsfile) and os.path.isfile( self.input_hostsfile) and self.input_hostsfile: imported = self.hosts.import_file(self.input_hostsfile) self.log.debug('[ OK ] Imported hosts file: {}'.format( self.input_hostsfile)) elif self.input_hostsfile: self.log.error( '[ERROR] The hosts file to import {} does not exist or no permission has been given to read it' .format(self.input_hostsfile)) os._exit(1) if not self.logfile: self.log.debug("[ OK ] Writing to stdout") else: self.log.debug('[ OK ] Writing to logfile: {}'.format( self.logfile)) sys.stdout.flush() sys.stderr.flush() self.blacklistedSubnets() localSubnets = threading.Thread(target=self.getLocalSubnets, args=()).start() receive = threading.Thread(target=self.receive, args=()).start() def getLocalSubnets(self): while True: blacklisted_ips = [] localSubnets = [] for inter in netifaces.interfaces(): if inter not in self.blacklisted_interfaces: interface = netifaces.ifaddresses(inter) for address in interface: blacklisted_ips.append(interface[address][0]['addr']) try: bits = None ip_addr = None if 'netmask' in interface[address][0].keys(): netmask = interface[address][0]['netmask'] bits = IPAddress(netmask).netmask_bits() if 'addr' in interface[address][0].keys(): ip_addr = interface[address][0]['addr'] cidr = "{}/{}".format(ip_addr, bits) localSubnets.append( ipaddress.ip_network(cidr, False)) except Exception as e: #if self.verbose: self.log.error("[LISTENER - getLocalSubnets() - (Try/Catch statement)]: {}".format(e)) pass self.blacklisted_ips = blacklisted_ips self.localSubnets = localSubnets time.sleep(1) def blacklistedSubnets(self): for subnet in self.blacklist: try: self.blacklisted_subnets.append(IPNetwork(str(subnet))) except: pass def receive(self): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if self.IS_ALL_GROUPS: sock.bind(('', self.MCAST_PORT)) else: socket.bind((self.MCAST_GROUP, self.MCAST_PORT)) mreq = struct.pack("4sl", socket.inet_aton(self.MCAST_GROUP), socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) while True: recv = sock.recv(10240).decode("utf-8") self.parseResponse(recv) def parseResponse(self, recv): try: nickname = recv.split(":")[0] address = ipaddress.ip_address(recv.split(":")[1]) packet_id = recv.split(":")[2] timestamp = recv.split(":")[3] if self.verbose: self.log.debug( "[VERBOSE] Packet {} from {} with content {} received at {} ({} difference in ms)" .format(packet_id, nickname, address, timestamp, ((time.time() - float(timestamp)) / 1000))) for subnet in self.localSubnets: subnet = IPNetwork(str(subnet)) ip = IPAddress(str(address)) is_blacklisted = False for b_subnet in self.blacklisted_subnets: if ip in b_subnet: is_blacklisted = True if ip in subnet and nickname != self.name and not is_blacklisted: self.ips[nickname] = address if self.logfile: self.writeLogFile() if self.hosts: self.writeHostsFile(recv) self.log.info( codecs.decode(("{}{}{}".format(address, self.seperator, nickname)), 'unicode_escape')) except Exception as e: if self.verbose and "does not appear to be an IPv4 or IPv6 address" not in str( e): self.log.error("[LISTENER - parseResponse()]: {}".format(e)) else: pass def writeLogFile(self): with open(self.logfile, 'w') as file: file_content = "" for nickname in self.ips: ip = self.ips[nickname] file_content += "{}{}{}\n".format(ip, self.seperator, nickname) file.write(codecs.decode(file_content, 'unicode_escape')) file.close() def writeHostsFile(self, recv): try: nickname = recv.split(":")[0] address = ipaddress.ip_address(recv.split(":")[1]) packet_id = recv.split(":")[2] timestamp = recv.split(":")[3] ip_type = ipaddress.ip_address(address) self.hosts.remove_all_matching(name=nickname) if isinstance(ip_type, ipaddress.IPv4Address): new_entry = HostsEntry(entry_type='ipv4', address=str(address), names=[nickname]) elif isinstance(ip_type, ipaddress.IPv6Address) and self.ipv6: new_entry = HostsEntry(entry_type='ipv6', address=str(address), names=[nickname]) else: new_entry = HostsEntry(entry_type='blank', address=str(address), names=[nickname]) self.hosts.add([new_entry]) self.hosts.write() except Exception as e: if self.verbose: self.log.error("[LISTENER - writeHostsFile()]: {}".format(e)) else: pass