text: # Copyright (c) 2013-2020 Philip Hane
  # All rights reserved.
  #
  # Redistribution and use in source and binary forms, with or without
  # modification, are permitted provided that the following conditions are met:
  #
  # 1. Redistributions of source code must retain the above copyright notice,
  #    this list of conditions and the following disclaimer.
  # 2. Redistributions in binary form must reproduce the above copyright notice,
  #    this list of conditions and the following disclaimer in the documentation
  #    and/or other materials provided with the distribution.
  #
  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  # POSSIBILITY OF SUCH DAMAGE.
  
  # CLI python script interface for ipwhois.utils lookups.
  
  import argparse
  from collections import OrderedDict
  import json
  from ipwhois.utils import (ipv4_lstrip_zeros, calculate_cidr, get_countries,
                             ipv4_is_defined, ipv6_is_defined,
                             ipv4_generate_random, ipv6_generate_random,
                             unique_everseen, unique_addresses)
  
  # CLI ANSI rendering
  ANSI = {
      'end': '\033[0m',
      'b': '\033[1m',
      'ul': '\033[4m',
      'red': '\033[31m',
      'green': '\033[32m',
      'yellow': '\033[33m',
      'cyan': '\033[36m'
  }
  
  # Setup the arg parser.
  parser = argparse.ArgumentParser(
      description='ipwhois utilities CLI interface'
  )
  parser.add_argument(
      '--ipv4_lstrip_zeros',
      type=str,
      nargs=1,
      metavar='"IP ADDRESS"',
      help='Strip leading zeros in each octet of an IPv4 address.'
  )
  parser.add_argument(
      '--calculate_cidr',
      type=str,
      nargs=2,
      metavar='"IP ADDRESS"',
      help='Calculate a CIDR range(s) from a start and end IP address.'
  )
  parser.add_argument(
      '--get_countries',
      action='store_true',
      help='Output a dictionary containing ISO_3166-1 country codes to names.'
  )
  parser.add_argument(
      '--get_country',
      type=str,
      nargs=1,
      metavar='"COUNTRY CODE"',
      help='Output the ISO_3166-1 name for a country code.'
  )
  parser.add_argument(
      '--ipv4_is_defined',
      type=str,
      nargs=1,
      metavar='"IP ADDRESS"',
      help='Check if an IPv4 address is defined (in a reserved address range).'
  )
  parser.add_argument(
      '--ipv6_is_defined',
      type=str,
      nargs=1,
      metavar='"IP ADDRESS"',
      help='Check if an IPv6 address is defined (in a reserved address range).'
  )
  parser.add_argument(
      '--ipv4_generate_random',
      type=int,
      nargs=1,
      metavar='TOTAL',
      help='Generate random, unique IPv4 addresses that are not defined (can be '
           'looked up using ipwhois).'
  )
  parser.add_argument(
      '--ipv6_generate_random',
      type=int,
      nargs=1,
      metavar='TOTAL',
      help='Generate random, unique IPv6 addresses that are not defined (can be '
           'looked up using ipwhois).'
  )
  parser.add_argument(
      '--unique_everseen',
      type=json.loads,
      nargs=1,
      metavar='"ITERABLE"',
      help='List unique elements from input iterable, preserving the order.'
  )
  parser.add_argument(
      '--unique_addresses',
      type=str,
      nargs=1,
      metavar='"FILE PATH"',
      help='Search an input file, extracting, counting, and summarizing '
           'IPv4/IPv6 addresses/networks.'
  )
  
  # Output options
  group = parser.add_argument_group('Output options')
  group.add_argument(
      '--colorize',
      action='store_true',
      help='If set, colorizes the output using ANSI. Should work in most '
           'platform consoles.'
  )
  
  # Get the args
  script_args = parser.parse_args()
  
  if script_args.ipv4_lstrip_zeros:
  
      print(ipv4_lstrip_zeros(address=script_args.ipv4_lstrip_zeros[0]))
  
  elif script_args.calculate_cidr:
  
      try:
  
          result = calculate_cidr(
              start_address=script_args.calculate_cidr[0],
              end_address=script_args.calculate_cidr[1]
          )
  
          print('{0}Found {1} CIDR blocks for ({2}, {3}){4}:\n{5}'.format(
              ANSI['green'] if script_args.colorize else '',
              len(result),
              script_args.calculate_cidr[0],
              script_args.calculate_cidr[1],
              ANSI['end'] if script_args.colorize else '',
              '\n'.join(result)
          ))
  
      except Exception as e:
  
          print('{0}Error{1}: {2}'.format(ANSI['red'], ANSI['end'], str(e)))
  
  elif script_args.get_countries:
  
      try:
  
          result = get_countries()
  
          print('{0}Found {1} countries{2}:\n{3}'.format(
              ANSI['green'] if script_args.colorize else '',
              len(result),
              ANSI['end'] if script_args.colorize else '',
              '\n'.join(['{0}: {1}'.format(k, v) for k, v in (
                  OrderedDict(sorted(result.items())).iteritems())])
          ))
  
      except Exception as e:
  
          print('{0}Error{1}: {2}'.format(ANSI['red'], ANSI['end'], str(e)))
  
  elif script_args.get_country:
  
      try:
  
          countries = get_countries()
          result = countries[script_args.get_country[0].upper()]
  
          print('{0}Match found for country code ({1}){2}:\n{3}'.format(
              ANSI['green'] if script_args.colorize else '',
              script_args.get_country[0],
              ANSI['end'] if script_args.colorize else '',
              result
          ))
  
      except Exception as e:
  
          print('{0}Error{1}: {2}'.format(ANSI['red'], ANSI['end'], str(e)))
  
  elif script_args.ipv4_is_defined:
  
      try:
  
          result = ipv4_is_defined(address=script_args.ipv4_is_defined[0])
  
          if result[0]:
              print('{0}{1} is defined{2}:\n{3}'.format(
                  ANSI['green'] if script_args.colorize else '',
                  script_args.ipv4_is_defined[0],
                  ANSI['end'] if script_args.colorize else '',
                  'Name: {0}\nRFC: {1}'.format(result[1], result[2])
              ))
          else:
              print('{0}{1} is not defined{2}'.format(
                  ANSI['yellow'] if script_args.colorize else '',
                  script_args.ipv4_is_defined[0],
                  ANSI['end'] if script_args.colorize else ''
              ))
  
      except Exception as e:
  
          print('{0}Error{1}: {2}'.format(ANSI['red'], ANSI['end'], str(e)))
  
  elif script_args.ipv6_is_defined:
  
      try:
  
          result = ipv6_is_defined(address=script_args.ipv6_is_defined[0])
  
          if result[0]:
              print('{0}{1} is defined{2}:\n{3}'.format(
                  ANSI['green'] if script_args.colorize else '',
                  script_args.ipv6_is_defined[0],
                  ANSI['end'] if script_args.colorize else '',
                  'Name: {0}\nRFC: {1}'.format(result[1], result[2])
              ))
          else:
              print('{0}{1} is not defined{2}'.format(
                  ANSI['yellow'] if script_args.colorize else '',
                  script_args.ipv6_is_defined[0],
                  ANSI['end'] if script_args.colorize else ''
              ))
  
      except Exception as e:
  
          print('{0}Error{1}: {2}'.format(ANSI['red'], ANSI['end'], str(e)))
  
  elif script_args.ipv4_generate_random:
  
      try:
  
          result = ipv4_generate_random(total=script_args.ipv4_generate_random[0])
  
          for random_ip in result:
  
              print(random_ip)
  
      except Exception as e:
  
          print('{0}Error{1}: {2}'.format(ANSI['red'], ANSI['end'], str(e)))
  
  elif script_args.ipv6_generate_random:
  
      try:
  
          result = ipv6_generate_random(total=script_args.ipv6_generate_random[0])
  
          for random_ip in result:
  
              print(random_ip)
  
      except Exception as e:
  
          print('{0}Error{1}: {2}'.format(ANSI['red'], ANSI['end'], str(e)))
  
  elif script_args.unique_everseen:
  
      try:
  
          result = list(unique_everseen(iterable=script_args.unique_everseen[0]))
  
          print('{0}Unique everseen{1}:\n{2}'.format(
              ANSI['green'] if script_args.colorize else '',
              ANSI['end'] if script_args.colorize else '',
              result
          ))
  
      except Exception as e:
  
          print('{0}Error{1}: {2}'.format(ANSI['red'], ANSI['end'], str(e)))
  
  elif script_args.unique_addresses:
  
      try:
  
          result = unique_addresses(file_path=script_args.unique_addresses[0])
  
          tmp = []
          for k, v in sorted(result.items(), key=lambda kv: int(kv[1]['count']),
                             reverse=True):
              tmp.append('{0}{1}{2}: Count: {3}, Ports: {4}'.format(
                  ANSI['b'] if script_args.colorize else '',
                  k,
                  ANSI['end'] if script_args.colorize else '',
                  v['count'],
                  json.dumps(v['ports'])
              ))
  
          print('{0}Found {1} unique addresses{2}:\n{3}'.format(
              ANSI['green'] if script_args.colorize else '',
              len(result),
              ANSI['end'] if script_args.colorize else '',
              '\n'.join(tmp)
          ))
  
      except Exception as e:
  
          print('{0}Error{1}: {2}'.format(ANSI['red'], ANSI['end'], str(e)))
  