class MainMenu(CommandMenu): def __init__(self, version, interface, gateway_ip, gateway_mac, netmask): super().__init__() self.prompt = '({}Main{}) >>> '.format(IO.Style.BRIGHT, IO.Style.RESET_ALL) self.parser.add_subparser('hosts', self._hosts_handler) self.parser.add_subparser('clear', self._clear_handler) scan_parser = self.parser.add_subparser('scan', self._scan_handler) scan_parser.add_parameterized_flag('--range', 'iprange') limit_parser = self.parser.add_subparser('limit', self._limit_handler) limit_parser.add_parameter('id') limit_parser.add_parameter('rate') block_parser = self.parser.add_subparser('block', self._block_handler) block_parser.add_parameter('id') free_parser = self.parser.add_subparser('free', self._free_handler) free_parser.add_parameter('id') add_parser = self.parser.add_subparser('add', self._add_handler) add_parser.add_parameter('ip') add_parser.add_parameterized_flag('--mac', 'mac') self.parser.add_subparser('help', self._help_handler) self.parser.add_subparser('?', self._help_handler) self.parser.add_subparser('quit', self._quit_handler) self.parser.add_subparser('exit', self._quit_handler) self.version = version # application version self.interface = interface # specified IPv4 interface self.gateway_ip = gateway_ip self.gateway_mac = gateway_mac self.netmask = netmask # range of IP address calculated from gateway IP and netmask self.iprange = list( netaddr.IPNetwork('{}/{}'.format(self.gateway_ip, self.netmask))) self.host_scanner = HostScanner(self.interface, self.iprange) self.arp_spoofer = ARPSpoofer(self.interface, self.gateway_ip, self.gateway_mac) self.limiter = Limiter(self.interface) # holds discovered hosts self.hosts = [] self._print_help_reminder() # start the spoof thread self.arp_spoofer.start() def interrupt_handler(self, ctrl_c=True): if ctrl_c: IO.spacer() IO.ok('cleaning up... stand by...') self.arp_spoofer.stop() for host in self.hosts: self._free_host(host) def _scan_handler(self, args): """ Handles 'scan' command-line argument (Re)scans for hosts on the network """ if args.iprange: try: if '-' in args.iprange: iprange = list( netaddr.iter_iprange(*args.iprange.split('-'))) else: iprange = list(netaddr.IPNetwork(args.iprange)) except netaddr.core.AddrFormatError: IO.error('ip range invalid.') return else: iprange = None for host in self.hosts: self._free_host(host) IO.spacer() self.hosts = self.host_scanner.scan(iprange) IO.ok('{}{}{} hosts discovered.'.format(IO.Fore.LIGHTYELLOW_EX, len(self.hosts), IO.Style.RESET_ALL)) IO.spacer() def _hosts_handler(self, args): """ Handles 'hosts' command-line argument Displays discovered hosts """ table_data = [[ '{}ID{}'.format(IO.Style.BRIGHT, IO.Style.RESET_ALL), '{}IP-Address{}'.format(IO.Style.BRIGHT, IO.Style.RESET_ALL), '{}MAC-Address{}'.format(IO.Style.BRIGHT, IO.Style.RESET_ALL), '{}Hostname{}'.format(IO.Style.BRIGHT, IO.Style.RESET_ALL), '{}Status{}'.format(IO.Style.BRIGHT, IO.Style.RESET_ALL) ]] for i, host in enumerate(self.hosts): table_data.append([ '{}{}{}'.format(IO.Fore.LIGHTYELLOW_EX, i, IO.Style.RESET_ALL), host.ip, host.mac, host.name if host.name is not None else '', host.pretty_status() ]) table = SingleTable(table_data, 'Hosts') if not table.ok: IO.error( 'table does not fit terminal. resize or decrease font size.') return IO.spacer() IO.print(table.table) IO.spacer() def _limit_handler(self, args): """ Handles 'limit' command-line argument Limits bandwith of host to specified rate """ hosts = self._get_hosts_by_ids(args.id) rate = args.rate if hosts is not None and len(hosts) > 0: for host in hosts: if not host.spoofed: self.arp_spoofer.add(host) if netutils.validate_netrate_string(rate): self.limiter.limit(host, rate) else: IO.error('limit rate is invalid.') return IO.ok('{}{}{} limited{} to {}.'.format(IO.Fore.LIGHTYELLOW_EX, host.ip, IO.Fore.LIGHTRED_EX, IO.Style.RESET_ALL, rate)) def _block_handler(self, args): """ Handles 'block' command-line argument Blocks internet communication for host """ hosts = self._get_hosts_by_ids(args.id) if hosts is not None and len(hosts) > 0: for host in hosts: if not host.spoofed: self.arp_spoofer.add(host) self.limiter.block(host) IO.ok('{}{}{} blocked{}.'.format(IO.Fore.LIGHTYELLOW_EX, host.ip, IO.Fore.RED, IO.Style.RESET_ALL)) def _free_handler(self, args): """ Handles 'free' command-line argument Frees the host from all limitations """ hosts = self._get_hosts_by_ids(args.id) if hosts is not None and len(hosts) > 0: for host in hosts: self._free_host(host) def _add_handler(self, args): """ Handles 'add' command-line argument Adds custom host to host list """ ip = args.ip if not netutils.validate_ip_address(ip): IO.error('invalid ip address.') return if args.mac: mac = args.mac if not netutils.validate_mac_address(mac): IO.error('invalid mac address.') return else: mac = netutils.get_mac_by_ip(self.interface, ip) if mac is None: IO.error( 'unable to resolve mac address. specify manually (--mac).') return name = None try: host_info = socket.gethostbyaddr(ip) name = None if host_info is None else host_info[0] except socket.herror: pass host = Host(ip, mac, name) if host in self.hosts: IO.error('host does already exist.') return self.hosts.append(host) IO.ok('host added.') def _clear_handler(self, args): """ Handler for the 'clear' command-line argument Clears the terminal window and re-prints the banner """ IO.clear() IO.print(get_main_banner(self.version)) self._print_help_reminder() def _help_handler(self, args): """ Handles 'help' command-line argument Prints help message including commands and usage """ spaces = ' ' * 30 IO.print(""" {y}scan (--range [IP range]){r}{}scans for online hosts on your network. {s}required to find the hosts you want to limit. {b}{s}e.g.: scan {s} scan --range 192.168.178.1-192.168.178.50 {s} scan --range 192.168.178.1/24{r} {y}hosts{r}{}lists all scanned hosts. {s}contains host information, including IDs. {y}limit [ID1,ID2,...] [rate]{r}{}limits bandwith of host(s) (uload/dload). {b}{s}e.g.: limit 4 100kbit {s} limit 2,3,4 1gbit {s} limit all 200kbit{r} {y}block [ID1,ID2,...]{r}{}blocks internet access of host(s). {b}{s}e.g.: block 3,2 {s} block all{r} {y}free [ID1,ID2,...]{r}{}unlimits/unblocks host(s). {b}{s}e.g.: free 3 {s} free all{r} {y}add [IP] (--mac [MAC]){r}{}adds custom host to host list. {s}mac resolved automatically. {b}{s}e.g.: add 192.168.178.24 {s} add 192.168.1.50 --mac 1c:fc:bc:2d:a6:37{r} {y}clear{r}{}clears the terminal window. {y}quit{r}{}quits the application. """.format(spaces[len('scan (--range [IP range])'):], spaces[len('hosts'):], spaces[len('limit [ID1,ID2,...] [rate]'):], spaces[len('block [ID1,ID2,...]'):], spaces[len('free [ID1,ID2,...]'):], spaces[len('add [IP] (--mac [MAC])'):], spaces[len('clear'):], spaces[len('quit'):], y=IO.Fore.LIGHTYELLOW_EX, r=IO.Style.RESET_ALL, b=IO.Style.BRIGHT, s=spaces)) def _quit_handler(self, args): self.interrupt_handler(False) self.stop() def _print_help_reminder(self): IO.print( 'type {Y}help{R} or {Y}?{R} to show command information.'.format( Y=IO.Fore.LIGHTYELLOW_EX, R=IO.Style.RESET_ALL)) def _get_hosts_by_ids(self, ids_string): if ids_string == 'all': return self.hosts.copy() try: ids = [int(x) for x in ids_string.split(',')] except ValueError: IO.error('\'{}\' are invalid IDs.'.format(ids_string)) return hosts = [] for id_ in ids: if len(self.hosts) == 0 or id_ not in range(len(self.hosts)): IO.error('no host with id {}{}{}.'.format( IO.Fore.LIGHTYELLOW_EX, id_, IO.Style.RESET_ALL)) return if self.hosts[id_] not in hosts: hosts.append(self.hosts[id_]) return hosts def _free_host(self, host): """ Stops ARP spoofing and unlimits host """ if host.spoofed: self.arp_spoofer.remove(host) self.limiter.unlimit(host) IO.ok('{}{}{} freed.'.format(IO.Fore.LIGHTYELLOW_EX, host.ip, IO.Style.RESET_ALL))
class MainMenu(CommandMenu): def __init__(self, version, interface, gateway_ip, gateway_mac, netmask): super().__init__() self.prompt = '({}Main{}) >>> '.format(IO.Style.BRIGHT, IO.Style.RESET_ALL) self.parser.add_subparser('scan', self._scan_handler) self.parser.add_subparser('hosts', self._hosts_handler) self.parser.add_subparser('clear', self._clear_handler) limit_parser = self.parser.add_subparser('limit', self._limit_handler) limit_parser.add_parameter('id') limit_parser.add_parameter('rate') block_parser = self.parser.add_subparser('block', self._block_handler) block_parser.add_parameter('id') free_parser = self.parser.add_subparser('free', self._free_handler) free_parser.add_parameter('id') self.parser.add_subparser('help', self._help_handler) self.parser.add_subparser('?', self._help_handler) self.version = version # application version self.interface = interface # specified IPv4 interface self.gateway_ip = gateway_ip self.gateway_mac = gateway_mac self.netmask = netmask # range of IP address calculated from gateway IP and netmask self.iprange = [ str(x) for x in netaddr.IPNetwork('{}/{}'.format( self.gateway_ip, self.netmask)) ] self.host_scanner = HostScanner(self.interface, self.iprange) self.arp_spoofer = ARPSpoofer(self.interface, self.gateway_ip, self.gateway_mac) self.limiter = Limiter(self.interface) # holds discovered hosts self.hosts = [] self._print_help_reminder() # start the spoof thread self.arp_spoofer.start() def interrupt_handler(self): IO.spacer() IO.ok('cleaning up... stand by...') self.arp_spoofer.stop() for host in self.hosts: self._free_host(host) def _scan_handler(self, args): """ Handles 'scan' command-line argument (Re)scans for hosts on the network """ for host in self.hosts: self._free_host(host) IO.spacer() self.hosts = self.host_scanner.scan() IO.ok('{}{}{} hosts discovered.'.format(IO.Fore.LIGHTYELLOW_EX, len(self.hosts), IO.Style.RESET_ALL)) IO.spacer() def _hosts_handler(self, args): """ Handles 'hosts' command-line argument Displays discovered hosts """ table_data = [[ '{}ID{}'.format(IO.Style.BRIGHT, IO.Style.RESET_ALL), '{}IP-Address{}'.format(IO.Style.BRIGHT, IO.Style.RESET_ALL), '{}MAC-Address{}'.format(IO.Style.BRIGHT, IO.Style.RESET_ALL), '{}Hostname{}'.format(IO.Style.BRIGHT, IO.Style.RESET_ALL), '{}Status{}'.format(IO.Style.BRIGHT, IO.Style.RESET_ALL) ]] for i, host in enumerate(self.hosts): table_data.append([ '{}{}{}'.format(IO.Fore.LIGHTYELLOW_EX, i, IO.Style.RESET_ALL), host.ip, host.mac, host.name if host.name is not None else '', host.pretty_status() ]) table = SingleTable(table_data, 'Hosts') if not table.ok: IO.error( 'table does not fit terminal. resize or decrease font size.') return IO.spacer() IO.print(table.table) IO.spacer() def _limit_handler(self, args): """ Handles 'limit' command-line argument Limits bandwith of host to specified rate """ host = self._get_host_by_id(args.id) rate = args.rate if host is not None: if not host.spoofed: self.arp_spoofer.add(host) if netutils.validate_netrate_string(rate): self.limiter.limit(host, rate) else: IO.error('limit rate is invalid.') return IO.ok('{}{}{} limited{} to {}.'.format(IO.Fore.LIGHTYELLOW_EX, host.ip, IO.Fore.LIGHTRED_EX, IO.Style.RESET_ALL, rate)) def _block_handler(self, args): """ Handles 'block' command-line argument Blocks internet communication for host """ host = self._get_host_by_id(args.id) if host is not None: if not host.spoofed: self.arp_spoofer.add(host) self.limiter.block(host) IO.ok('{}{}{} blocked{}.'.format(IO.Fore.LIGHTYELLOW_EX, host.ip, IO.Fore.RED, IO.Style.RESET_ALL)) def _free_handler(self, args): """ Handles 'free' command-line argument Frees the host from all limitations """ host = self._get_host_by_id(args.id) if host is not None: self._free_host(host) def _clear_handler(self, args): """ Handler for the 'clear' command-line argument Clears the terminal window and re-prints the banner """ IO.clear() IO.print(get_main_banner(self.version)) self._print_help_reminder() def _help_handler(self, args): """ Handles 'help' command-line argument Prints help message including commands and usage """ spaces = ' ' * 20 IO.print(""" {y}scan{r}{}scans for online hosts on your network. {s}required to find the hosts you want to limit. {y}hosts{r}{}lists all scanned hosts. {s}contains host information, including IDs. {y}limit [ID] [rate]{r}{}limits bandwith of host (uload/dload). {b}{s}e.g.: limit 4 100kbit {s} limit 2 1gbit {s} limit 5 500tbit{r} {y}block [ID]{r}{}blocks internet access of host. {b}{s}e.g.: block 3{r} {y}free [ID]{r}{}unlimits/unblocks host. {b}{s}e.g.: free 3{r} {y}clear{r}{}clears the terminal window. """.format(spaces[len('scan'):], spaces[len('hosts'):], spaces[len('limit [ID] [rate]'):], spaces[len('block [ID]'):], spaces[len('free [ID]'):], spaces[len('clear'):], y=IO.Fore.LIGHTYELLOW_EX, r=IO.Style.RESET_ALL, b=IO.Style.BRIGHT, s=spaces)) def _print_help_reminder(self): IO.print( 'type {Y}help{R} or {Y}?{R} to show command information.'.format( Y=IO.Fore.LIGHTYELLOW_EX, R=IO.Style.RESET_ALL)) def _get_host_by_id(self, id_): try: identifier = int(id_) except ValueError: IO.error('identifier is not an integer.') return if len(self.hosts) == 0 or identifier not in range(len(self.hosts)): IO.error('no host with id {}{}{}.'.format(IO.Fore.LIGHTYELLOW_EX, identifier, IO.Style.RESET_ALL)) return return self.hosts[identifier] def _free_host(self, host): """ Stops ARP spoofing and unlimits host """ if host.spoofed: self.arp_spoofer.remove(host) self.limiter.unlimit(host) IO.ok('{}{}{} freed.'.format(IO.Fore.LIGHTYELLOW_EX, host.ip, IO.Style.RESET_ALL))