ipwhois/scripts/ipwhois_cli.py
# 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.IPWhois lookups.
import argparse
import json
from os import path
from ipwhois import IPWhois
from ipwhois.hr import (HR_ASN, HR_RDAP, HR_RDAP_COMMON, HR_WHOIS,
HR_WHOIS_NIR)
try: # pragma: no cover
from urllib.request import (ProxyHandler,
build_opener)
except ImportError: # pragma: no cover
from urllib2 import (ProxyHandler,
build_opener)
# 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'
}
# Color definitions for sub lines
COLOR_DEPTH = {
'0': ANSI['green'],
'1': ANSI['yellow'],
'2': ANSI['red'],
'3': ANSI['cyan']
}
# Line formatting, keys ending in C are colorized versions.
LINES = {
'1': '>> ',
'2': '>> >>> ',
'3': '>> >>> >>>> ',
'4': '>> >>> >>>> >>>>> ',
'1C': '{0}>>{1} '.format(COLOR_DEPTH['0'], ANSI['end']),
'2C': '{0}>>{1} >>>{2} '.format(
COLOR_DEPTH['0'], COLOR_DEPTH['1'], ANSI['end']
),
'3C': '{0}>>{1} >>>{2} >>>>{3} '.format(
COLOR_DEPTH['0'], COLOR_DEPTH['1'], COLOR_DEPTH['2'], ANSI['end']
),
'4C': '{0}>>{1} >>>{2} >>>>{3} >>>>>{4} '.format(
COLOR_DEPTH['0'], COLOR_DEPTH['1'], COLOR_DEPTH['2'], COLOR_DEPTH['3'],
ANSI['end']
),
}
# Setup the arg parser.
parser = argparse.ArgumentParser(
description='ipwhois CLI interface'
)
parser.add_argument(
'--whois',
action='store_true',
help='Retrieve whois data via legacy Whois (port 43) instead of RDAP '
'(default).'
)
parser.add_argument(
'--exclude_nir',
action='store_true',
help='Disable NIR whois lookups (JPNIC, KRNIC). This is the opposite of '
'the ipwhois inc_nir, in order to enable inc_nir by default in the '
'CLI.',
default=False
)
parser.add_argument(
'--json',
action='store_true',
help='Output results in JSON format.',
default=False
)
# Output options
group = parser.add_argument_group('Output options')
group.add_argument(
'--hr',
action='store_true',
help='If set, returns results with human readable key translations.'
)
group.add_argument(
'--show_name',
action='store_true',
help='If this and --hr are set, the key name is shown in parentheses after'
'its short value'
)
group.add_argument(
'--colorize',
action='store_true',
help='If set, colorizes the output using ANSI. Should work in most '
'platform consoles.'
)
# IPWhois settings (common)
group = parser.add_argument_group('IPWhois settings')
group.add_argument(
'--timeout',
type=int,
default=5,
metavar='TIMEOUT',
help='The default timeout for socket connections in seconds.'
)
group.add_argument(
'--proxy_http',
type=str,
nargs=1,
default='',
metavar='"PROXY_HTTP"',
help='The proxy HTTP address passed to request.ProxyHandler. User auth '
'can be passed like "http://user:pass@192.168.0.1:80"',
required=False
)
group.add_argument(
'--proxy_https',
type=str,
nargs=1,
default='',
metavar='"PROXY_HTTPS"',
help='The proxy HTTPS address passed to request.ProxyHandler. User auth'
'can be passed like "https://user:pass@192.168.0.1:443"',
required=False
)
# Common (RDAP & Legacy Whois)
group = parser.add_argument_group('Common settings (RDAP & Legacy Whois)')
group.add_argument(
'--inc_raw',
action='store_true',
help='Include the raw whois results in the output.'
)
group.add_argument(
'--retry_count',
type=int,
default=3,
metavar='RETRY_COUNT',
help='The number of times to retry in case socket errors, timeouts, '
'connection resets, etc. are encountered.'
)
group.add_argument(
'--asn_methods',
type=str,
nargs=1,
default='dns,whois,http',
metavar='"ASN_METHODS"',
help='List of ASN lookup types to attempt, in order. '
'Defaults to all [\'dns\', \'whois\', \'http\'].'
)
group.add_argument(
'--extra_org_map',
type=json.loads,
nargs=1,
default='{"DNIC": "arin"}',
metavar='"EXTRA_ORG_MAP"',
help='Dictionary mapping org handles to RIRs. This is for limited cases '
'where ARIN REST (ASN fallback HTTP lookup) does not show an RIR as '
'the org handle e.g., DNIC (which is now the built in ORG_MAP) e.g., '
'{\\"DNIC\\": \\"arin\\"}. Valid RIR values are (note the '
'case-sensitive - this is meant to match the REST result): '
'\'ARIN\', \'RIPE\', \'apnic\', \'lacnic\', \'afrinic\''
)
group.add_argument(
'--skip_asn_description',
action='store_true',
help='Don\'t run an additional query when pulling ASN information via dns '
'(to get the ASN description). This is the opposite of the ipwhois '
'get_asn_description argument, in order to enable '
'get_asn_description by default in the CLI.',
default=False
)
# RDAP
group = parser.add_argument_group('RDAP settings')
group.add_argument(
'--depth',
type=int,
default=0,
metavar='COLOR_DEPTH',
help='If not --whois, how many levels deep to run RDAP queries when '
'additional referenced objects are found.'
)
group.add_argument(
'--excluded_entities',
type=str,
nargs=1,
default=None,
metavar='"EXCLUDED_ENTITIES"',
help='If not --whois, a comma delimited list of entity handles to not '
'perform lookups.'
)
group.add_argument(
'--bootstrap',
action='store_true',
help='If not --whois, performs lookups via ARIN bootstrap rather than '
'lookups based on ASN data. ASN lookups are not performed and no '
'output for any of the asn* fields is provided.'
)
group.add_argument(
'--rate_limit_timeout',
type=int,
default=120,
metavar='RATE_LIMIT_TIMEOUT',
help='If not --whois, the number of seconds to wait before retrying when '
'a rate limit notice is returned via rdap+json.'
)
# Legacy Whois
group = parser.add_argument_group('Legacy Whois settings')
group.add_argument(
'--get_referral',
action='store_true',
help='If --whois, retrieve referral whois information, if available.'
)
group.add_argument(
'--extra_blacklist',
type=str,
nargs=1,
default='',
metavar='"EXTRA_BLACKLIST"',
help='If --whois, A list of blacklisted whois servers in addition to the '
'global BLACKLIST.'
)
group.add_argument(
'--ignore_referral_errors',
action='store_true',
help='If --whois, ignore and continue when an exception is encountered on '
'referral whois lookups.'
)
group.add_argument(
'--field_list',
type=str,
nargs=1,
default='',
metavar='"FIELD_LIST"',
help='If --whois, a list of fields to parse: '
'[\'name\', \'handle\', \'description\', \'country\', \'state\', '
'\'city\', \'address\', \'postal_code\', \'emails\', \'created\', '
'\'updated\']'
)
# NIR (National Internet Registry -- JPNIC, KRNIC)
group = parser.add_argument_group('NIR (National Internet Registry) settings')
group.add_argument(
'--nir_field_list',
type=str,
nargs=1,
default='',
metavar='"NIR_FIELD_LIST"',
help='If not --exclude_nir, a list of fields to parse: '
'[\'name\', \'handle\', \'country\', \'address\', \'postal_code\', '
'\'nameservers\', \'created\', \'updated\', \'contact_admin\', '
'\'contact_tech\']'
)
# Input (required)
group = parser.add_argument_group('Input (Required)')
group.add_argument(
'--addr',
type=str,
nargs=1,
metavar='"IP"',
help='An IPv4 or IPv6 address as a string.',
required=True
)
# Get the args
script_args = parser.parse_args()
# Get the current working directory.
CUR_DIR = path.dirname(__file__)
def generate_output(line='0', short=None, name=None, value=None,
is_parent=False, colorize=True):
"""
The function for formatting CLI output results.
Args:
line (:obj:`str`): The line number (0-4). Determines indentation.
Defaults to '0'.
short (:obj:`str`): The optional abbreviated name for a field.
See hr.py for values.
name (:obj:`str`): The optional name for a field. See hr.py for values.
value (:obj:`str`): The field data (required).
is_parent (:obj:`bool`): Set to True if the field value has sub-items
(dicts/lists). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI colors.
Defaults to True.
Returns:
str: The generated output.
"""
# TODO: so ugly
output = '{0}{1}{2}{3}{4}{5}{6}{7}\n'.format(
LINES['{0}{1}'.format(line, 'C' if colorize else '')] if (
line in LINES.keys()) else '',
COLOR_DEPTH[line] if (colorize and line in COLOR_DEPTH) else '',
ANSI['b'],
short if short is not None else (
name if (name is not None) else ''
),
'' if (name is None or short is None) else ' ({0})'.format(
name),
'' if (name is None and short is None) else ': ',
ANSI['end'] if colorize else '',
'' if is_parent else value
)
return output
class IPWhoisCLI:
"""
The CLI wrapper class for outputting formatted IPWhois results.
Args:
addr (:obj:`str`/:obj:`int`/:obj:`IPv4Address`/:obj:`IPv6Address`):
An IPv4 or IPv6 address
timeout (:obj:`int`): The default timeout for socket connections in
seconds. Defaults to 5.
proxy_http (:obj:`urllib.request.OpenerDirector`): The request for
proxy HTTP support or None.
proxy_https (:obj:`urllib.request.OpenerDirector`): The request for
proxy HTTPS support or None.
"""
def __init__(
self,
addr,
timeout,
proxy_http,
proxy_https
):
self.addr = addr
self.timeout = timeout
handler_dict = None
if proxy_http is not None:
handler_dict = {'http': proxy_http}
if proxy_https is not None:
if handler_dict is None:
handler_dict = {'https': proxy_https}
else:
handler_dict['https'] = proxy_https
if handler_dict is None:
self.opener = None
else:
handler = ProxyHandler(handler_dict)
self.opener = build_opener(handler)
self.obj = IPWhois(address=self.addr,
timeout=self.timeout,
proxy_opener=self.opener)
def generate_output_header(self, query_type='RDAP'):
"""
The function for generating the CLI output header.
Args:
query_type (:obj:`str`): The IPWhois query type. Defaults to
'RDAP'.
Returns:
str: The generated output.
"""
output = '\n{0}{1}{2} query for {3}:{4}\n\n'.format(
ANSI['ul'],
ANSI['b'],
query_type,
self.obj.address_str,
ANSI['end']
)
return output
def generate_output_newline(self, line='0', colorize=True):
"""
The function for generating a CLI output new line.
Args:
line (:obj:`str`): The line number (0-4). Determines indentation.
Defaults to '0'.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
Returns:
str: The generated output.
"""
return generate_output(
line=line,
is_parent=True,
colorize=colorize
)
def generate_output_asn(self, json_data=None, hr=True, show_name=False,
colorize=True):
"""
The function for generating CLI output ASN results.
Args:
json_data (:obj:`dict`): The data to process. Defaults to None.
hr (:obj:`bool`): Enable human readable key translations. Defaults
to True.
show_name (:obj:`bool`): Show human readable name (default is to
only show short). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
Returns:
str: The generated output.
"""
if json_data is None:
json_data = {}
keys = {'asn', 'asn_cidr', 'asn_country_code', 'asn_date',
'asn_registry', 'asn_description'}.intersection(json_data)
output = ''
for key in keys:
output += generate_output(
line='0',
short=HR_ASN[key]['_short'] if hr else key,
name=HR_ASN[key]['_name'] if (hr and show_name) else None,
value=(json_data[key] if (
json_data[key] is not None and
len(json_data[key]) > 0 and
json_data[key] != 'NA') else 'None'),
colorize=colorize
)
return output
def generate_output_entities(self, json_data=None, hr=True,
show_name=False, colorize=True):
"""
The function for generating CLI output RDAP entity results.
Args:
json_data (:obj:`dict`): The data to process. Defaults to None.
hr (:obj:`bool`): Enable human readable key translations. Defaults
to True.
show_name (:obj:`bool`): Show human readable name (default is to
only show short). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
Returns:
str: The generated output.
"""
output = ''
short = HR_RDAP['entities']['_short'] if hr else 'entities'
name = HR_RDAP['entities']['_name'] if (hr and show_name) else None
output += generate_output(
line='0',
short=short,
name=name,
is_parent=False if (json_data is None or
json_data['entities'] is None) else True,
value='None' if (json_data is None or
json_data['entities'] is None) else None,
colorize=colorize
)
if json_data is not None:
for ent in json_data['entities']:
output += generate_output(
line='1',
value=ent,
colorize=colorize
)
return output
def generate_output_events(self, source, key, val, line='2', hr=True,
show_name=False, colorize=True):
"""
The function for generating CLI output RDAP events results.
Args:
source (:obj:`str`): The parent key 'network' or 'objects'
(required).
key (:obj:`str`): The event key 'events' or 'events_actor'
(required).
val (:obj:`dict`): The event dictionary (required).
line (:obj:`str`): The line number (0-4). Determines indentation.
Defaults to '0'.
hr (:obj:`bool`): Enable human readable key translations. Defaults
to True.
show_name (:obj:`bool`): Show human readable name (default is to
only show short). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
Returns:
str: The generated output.
"""
output = generate_output(
line=line,
short=HR_RDAP[source][key]['_short'] if hr else key,
name=HR_RDAP[source][key]['_name'] if (hr and show_name) else None,
is_parent=False if (val is None or
len(val) == 0) else True,
value='None' if (val is None or
len(val) == 0) else None,
colorize=colorize
)
if val is not None:
count = 0
for item in val:
try:
action = item['action']
except KeyError:
action = None
try:
timestamp = item['timestamp']
except KeyError:
timestamp = None
try:
actor = item['actor']
except KeyError:
actor = None
if count > 0:
output += generate_output(
line=str(int(line)+1),
is_parent=True,
colorize=colorize
)
output += generate_output(
line=str(int(line)+1),
short=HR_RDAP_COMMON[key]['action'][
'_short'] if hr else 'action',
name=HR_RDAP_COMMON[key]['action'][
'_name'] if (hr and show_name) else None,
value=action,
colorize=colorize
)
output += generate_output(
line=str(int(line)+1),
short=HR_RDAP_COMMON[key]['timestamp'][
'_short'] if hr else 'timestamp',
name=HR_RDAP_COMMON[key]['timestamp'][
'_name'] if (hr and show_name) else None,
value=timestamp,
colorize=colorize
)
output += generate_output(
line=str(int(line)+1),
short=HR_RDAP_COMMON[key]['actor'][
'_short'] if hr else 'actor',
name=HR_RDAP_COMMON[key]['actor'][
'_name'] if (hr and show_name) else None,
value=actor,
colorize=colorize
)
count += 1
return output
def generate_output_list(self, source, key, val, line='2', hr=True,
show_name=False, colorize=True):
"""
The function for generating CLI output RDAP list results.
Args:
source (:obj:`str`): The parent key 'network' or 'objects'
(required).
key (:obj:`str`): The event key 'events' or 'events_actor'
(required).
val (:obj:`dict`): The event dictionary (required).
line (:obj:`str`): The line number (0-4). Determines indentation.
Defaults to '0'.
hr (:obj:`bool`): Enable human readable key translations. Defaults
to True.
show_name (:obj:`bool`): Show human readable name (default is to
only show short). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
Returns:
str: The generated output.
"""
output = generate_output(
line=line,
short=HR_RDAP[source][key]['_short'] if hr else key,
name=HR_RDAP[source][key]['_name'] if (hr and show_name) else None,
is_parent=False if (val is None or
len(val) == 0) else True,
value='None' if (val is None or
len(val) == 0) else None,
colorize=colorize
)
if val is not None:
for item in val:
output += generate_output(
line=str(int(line)+1),
value=item,
colorize=colorize
)
return output
def generate_output_notices(self, source, key, val, line='1', hr=True,
show_name=False, colorize=True):
"""
The function for generating CLI output RDAP notices results.
Args:
source (:obj:`str`): The parent key 'network' or 'objects'
(required).
key (:obj:`str`): The event key 'events' or 'events_actor'
(required).
val (:obj:`dict`): The event dictionary (required).
line (:obj:`str`): The line number (0-4). Determines indentation.
Defaults to '0'.
hr (:obj:`bool`): Enable human readable key translations. Defaults
to True.
show_name (:obj:`bool`): Show human readable name (default is to
only show short). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
Returns:
str: The generated output.
"""
output = generate_output(
line=line,
short=HR_RDAP[source][key]['_short'] if hr else key,
name=HR_RDAP[source][key]['_name'] if (hr and show_name) else None,
is_parent=False if (val is None or
len(val) == 0) else True,
value='None' if (val is None or
len(val) == 0) else None,
colorize=colorize
)
if val is not None:
count = 0
for item in val:
title = item['title']
description = item['description']
links = item['links']
if count > 0:
output += generate_output(
line=str(int(line)+1),
is_parent=True,
colorize=colorize
)
output += generate_output(
line=str(int(line)+1),
short=HR_RDAP_COMMON[key]['title']['_short'] if hr else (
'title'),
name=HR_RDAP_COMMON[key]['title']['_name'] if (
hr and show_name) else None,
value=title,
colorize=colorize
)
output += generate_output(
line=str(int(line)+1),
short=HR_RDAP_COMMON[key]['description'][
'_short'] if hr else 'description',
name=HR_RDAP_COMMON[key]['description'][
'_name'] if (hr and show_name) else None,
value=description.replace(
'\n',
'\n{0}'.format(generate_output(line='3'))
),
colorize=colorize
)
output += self.generate_output_list(
source=source,
key='links',
val=links,
line=str(int(line)+1),
hr=hr,
show_name=show_name,
colorize=colorize
)
count += 1
return output
def generate_output_network(self, json_data=None, hr=True, show_name=False,
colorize=True):
"""
The function for generating CLI output RDAP network results.
Args:
json_data (:obj:`dict`): The data to process. Defaults to None.
hr (:obj:`bool`): Enable human readable key translations. Defaults
to True.
show_name (:obj:`bool`): Show human readable name (default is to
only show short). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
Returns:
str: The generated output.
"""
if json_data is None:
json_data = {}
output = generate_output(
line='0',
short=HR_RDAP['network']['_short'] if hr else 'network',
name=HR_RDAP['network']['_name'] if (hr and show_name) else None,
is_parent=True,
colorize=colorize
)
for key, val in json_data['network'].items():
if key in ['links', 'status']:
output += self.generate_output_list(
source='network',
key=key,
val=val,
line='1',
hr=hr,
show_name=show_name,
colorize=colorize
)
elif key in ['notices', 'remarks']:
output += self.generate_output_notices(
source='network',
key=key,
val=val,
line='1',
hr=hr,
show_name=show_name,
colorize=colorize
)
elif key == 'events':
output += self.generate_output_events(
source='network',
key=key,
val=val,
line='1',
hr=hr,
show_name=show_name,
colorize=colorize
)
elif key not in ['raw']:
output += generate_output(
line='1',
short=HR_RDAP['network'][key]['_short'] if hr else key,
name=HR_RDAP['network'][key]['_name'] if (
hr and show_name) else None,
value=val,
colorize=colorize
)
return output
def generate_output_objects(self, json_data=None, hr=True, show_name=False,
colorize=True):
"""
The function for generating CLI output RDAP object results.
Args:
json_data (:obj:`dict`): The data to process. Defaults to None.
hr (:obj:`bool`): Enable human readable key translations. Defaults
to True.
show_name (:obj:`bool`): Show human readable name (default is to
only show short). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
Returns:
str: The generated output.
"""
if json_data is None:
json_data = {}
output = generate_output(
line='0',
short=HR_RDAP['objects']['_short'] if hr else 'objects',
name=HR_RDAP['objects']['_name'] if (hr and show_name) else None,
is_parent=True,
colorize=colorize
)
count = 0
for obj_name, obj in json_data['objects'].items():
if count > 0:
output += self.generate_output_newline(
line='1',
colorize=colorize
)
count += 1
output += generate_output(
line='1',
short=obj_name,
is_parent=True,
colorize=colorize
)
for key, val in obj.items():
if key in ['links', 'entities', 'roles', 'status']:
output += self.generate_output_list(
source='objects',
key=key,
val=val,
line='2',
hr=hr,
show_name=show_name,
colorize=colorize
)
elif key in ['notices', 'remarks']:
output += self.generate_output_notices(
source='objects',
key=key,
val=val,
line='2',
hr=hr,
show_name=show_name,
colorize=colorize
)
elif key == 'events':
output += self.generate_output_events(
source='objects',
key=key,
val=val,
line='2',
hr=hr,
show_name=show_name,
colorize=colorize
)
elif key == 'contact':
output += generate_output(
line='2',
short=HR_RDAP['objects']['contact'][
'_short'] if hr else 'contact',
name=HR_RDAP['objects']['contact']['_name'] if (
hr and show_name) else None,
is_parent=False if (val is None or
len(val) == 0) else True,
value='None' if (val is None or
len(val) == 0) else None,
colorize=colorize
)
if val is not None:
for k, v in val.items():
if k in ['phone', 'address', 'email']:
output += generate_output(
line='3',
short=HR_RDAP['objects']['contact'][k][
'_short'] if hr else k,
name=HR_RDAP['objects']['contact'][k][
'_name'] if (
hr and show_name) else None,
is_parent=False if (
val is None or
len(val) == 0
) else True,
value='None' if (val is None or
len(val) == 0) else None,
colorize=colorize
)
if v is not None:
for item in v:
i_type = ', '.join(item['type']) if (
isinstance(item['type'], list)
) else item['type']
i_type = i_type if (
i_type is not None and
len(i_type) > 0) else ''
i_value = item['value'].replace(
'\n',
'\n{0}'.format(
generate_output(
line='4',
is_parent=True,
colorize=colorize
).replace('\n', ''))
)
tmp_out = '{0}{1}{2}'.format(
i_type,
': ' if i_type != '' else '',
i_value
)
output += generate_output(
line='4',
value=tmp_out,
colorize=colorize
)
else:
output += generate_output(
line='3',
short=HR_RDAP['objects']['contact'][k][
'_short'] if hr else k,
name=HR_RDAP['objects']['contact'][k][
'_name'] if (
hr and show_name) else None,
value=v,
colorize=colorize
)
elif key not in ['raw']:
output += generate_output(
line='2',
short=HR_RDAP['objects'][key]['_short'] if hr else key,
name=HR_RDAP['objects'][key]['_name'] if (
hr and show_name) else None,
value=val,
colorize=colorize
)
return output
def lookup_rdap(self, hr=True, show_name=False, colorize=True, **kwargs):
"""
The function for wrapping IPWhois.lookup_rdap() and generating
formatted CLI output.
Args:
hr (:obj:`bool`): Enable human readable key translations. Defaults
to True.
show_name (:obj:`bool`): Show human readable name (default is to
only show short). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
kwargs: Arguments to pass to IPWhois.lookup_rdap().
Returns:
str: The generated output.
"""
# Perform the RDAP lookup
ret = self.obj.lookup_rdap(**kwargs)
if script_args.json:
output = json.dumps(ret)
else:
# Header
output = self.generate_output_header(query_type='RDAP')
# ASN
output += self.generate_output_asn(
json_data=ret, hr=hr, show_name=show_name, colorize=colorize
)
output += self.generate_output_newline(colorize=colorize)
# Entities
output += self.generate_output_entities(
json_data=ret, hr=hr, show_name=show_name, colorize=colorize
)
output += self.generate_output_newline(colorize=colorize)
# Network
output += self.generate_output_network(
json_data=ret, hr=hr, show_name=show_name, colorize=colorize
)
output += self.generate_output_newline(colorize=colorize)
# Objects
output += self.generate_output_objects(
json_data=ret, hr=hr, show_name=show_name, colorize=colorize
)
output += self.generate_output_newline(colorize=colorize)
if 'nir' in ret:
# NIR
output += self.generate_output_nir(
json_data=ret, hr=hr, show_name=show_name,
colorize=colorize
)
output += self.generate_output_newline(colorize=colorize)
return output
def generate_output_whois_nets(self, json_data=None, hr=True,
show_name=False, colorize=True):
"""
The function for generating CLI output Legacy Whois networks results.
Args:
json_data (:obj:`dict`): The data to process. Defaults to None.
hr (:obj:`bool`): Enable human readable key translations. Defaults
to True.
show_name (:obj:`bool`): Show human readable name (default is to
only show short). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
Returns:
str: The generated output.
"""
if json_data is None:
json_data = {}
output = generate_output(
line='0',
short=HR_WHOIS['nets']['_short'] if hr else 'nets',
name=HR_WHOIS['nets']['_name'] if (hr and show_name) else None,
is_parent=True,
colorize=colorize
)
count = 0
for net in json_data['nets']:
if count > 0:
output += self.generate_output_newline(
line='1',
colorize=colorize
)
count += 1
output += generate_output(
line='1',
short=net['handle'],
is_parent=True,
colorize=colorize
)
for key, val in net.items():
if val and '\n' in val:
output += generate_output(
line='2',
short=HR_WHOIS['nets'][key]['_short'] if hr else key,
name=HR_WHOIS['nets'][key]['_name'] if (
hr and show_name) else None,
is_parent=False if (val is None or
len(val) == 0) else True,
value='None' if (val is None or
len(val) == 0) else None,
colorize=colorize
)
for v in val.split('\n'):
output += generate_output(
line='3',
value=v,
colorize=colorize
)
else:
output += generate_output(
line='2',
short=HR_WHOIS['nets'][key]['_short'] if hr else key,
name=HR_WHOIS['nets'][key]['_name'] if (
hr and show_name) else None,
value=val,
colorize=colorize
)
return output
def generate_output_whois_referral(self, json_data=None, hr=True,
show_name=False, colorize=True):
"""
The function for generating CLI output Legacy Whois referral results.
Args:
json_data (:obj:`dict`): The data to process. Defaults to None.
hr (:obj:`bool`): Enable human readable key translations. Defaults
to True.
show_name (:obj:`bool`): Show human readable name (default is to
only show short). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
Returns:
str: The generated output.
"""
if json_data is None:
json_data = {}
output = generate_output(
line='0',
short=HR_WHOIS['referral']['_short'] if hr else 'referral',
name=HR_WHOIS['referral']['_name'] if (hr and show_name) else None,
is_parent=False if json_data['referral'] is None else True,
value='None' if json_data['referral'] is None else None,
colorize=colorize
)
if json_data['referral']:
for key, val in json_data['referral'].items():
if val and '\n' in val:
output += generate_output(
line='1',
short=HR_WHOIS['nets'][key]['_short'] if hr else key,
name=HR_WHOIS['nets'][key]['_name'] if (
hr and show_name) else None,
is_parent=False if (val is None or
len(val) == 0) else True,
value='None' if (val is None or
len(val) == 0) else None,
colorize=colorize
)
for v in val.split('\n'):
output += generate_output(
line='2',
value=v,
colorize=colorize
)
else:
output += generate_output(
line='1',
short=HR_WHOIS['nets'][key]['_short'] if hr else key,
name=HR_WHOIS['nets'][key]['_name'] if (
hr and show_name) else None,
value=val,
colorize=colorize
)
return output
def generate_output_nir(self, json_data=None, hr=True, show_name=False,
colorize=True):
"""
The function for generating CLI output NIR network results.
Args:
json_data (:obj:`dict`): The data to process. Defaults to None.
hr (:obj:`bool`): Enable human readable key translations. Defaults
to True.
show_name (:obj:`bool`): Show human readable name (default is to
only show short). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
Returns:
str: The generated output.
"""
if json_data is None:
json_data = {}
output = generate_output(
line='0',
short=HR_WHOIS_NIR['nets']['_short'] if hr else 'nir_nets',
name=HR_WHOIS_NIR['nets']['_name'] if (hr and show_name) else None,
is_parent=True,
colorize=colorize
)
count = 0
if json_data['nir']:
for net in json_data['nir']['nets']:
if count > 0:
output += self.generate_output_newline(
line='1',
colorize=colorize
)
count += 1
output += generate_output(
line='1',
short=net['handle'],
is_parent=True,
colorize=colorize
)
for key, val in net.items():
if val and (isinstance(val, dict) or '\n' in val or
key == 'nameservers'):
output += generate_output(
line='2',
short=(
HR_WHOIS_NIR['nets'][key]['_short'] if (
hr) else key
),
name=HR_WHOIS_NIR['nets'][key]['_name'] if (
hr and show_name) else None,
is_parent=False if (val is None or
len(val) == 0) else True,
value='None' if (val is None or
len(val) == 0) else None,
colorize=colorize
)
if key == 'contacts':
for k, v in val.items():
if v:
output += generate_output(
line='3',
is_parent=False if (
len(v) == 0) else True,
name=k,
colorize=colorize
)
for contact_key, contact_val in v.items():
if v is not None:
tmp_out = '{0}{1}{2}'.format(
contact_key,
': ',
contact_val
)
output += generate_output(
line='4',
value=tmp_out,
colorize=colorize
)
elif key == 'nameservers':
for v in val:
output += generate_output(
line='3',
value=v,
colorize=colorize
)
else:
for v in val.split('\n'):
output += generate_output(
line='3',
value=v,
colorize=colorize
)
else:
output += generate_output(
line='2',
short=(
HR_WHOIS_NIR['nets'][key]['_short'] if (
hr) else key
),
name=HR_WHOIS_NIR['nets'][key]['_name'] if (
hr and show_name) else None,
value=val,
colorize=colorize
)
else:
output += 'None'
return output
def lookup_whois(self, hr=True, show_name=False, colorize=True, **kwargs):
"""
The function for wrapping IPWhois.lookup_whois() and generating
formatted CLI output.
Args:
hr (:obj:`bool`): Enable human readable key translations. Defaults
to True.
show_name (:obj:`bool`): Show human readable name (default is to
only show short). Defaults to False.
colorize (:obj:`bool`): Colorize the console output with ANSI
colors. Defaults to True.
kwargs: Arguments to pass to IPWhois.lookup_whois().
Returns:
str: The generated output.
"""
# Perform the RDAP lookup
ret = self.obj.lookup_whois(**kwargs)
if script_args.json:
output = json.dumps(ret)
else:
# Header
output = self.generate_output_header(query_type='Legacy Whois')
# ASN
output += self.generate_output_asn(
json_data=ret, hr=hr, show_name=show_name, colorize=colorize
)
output += self.generate_output_newline(colorize=colorize)
# Network
output += self.generate_output_whois_nets(
json_data=ret, hr=hr, show_name=show_name, colorize=colorize
)
output += self.generate_output_newline(colorize=colorize)
# Referral
output += self.generate_output_whois_referral(
json_data=ret, hr=hr, show_name=show_name, colorize=colorize
)
output += self.generate_output_newline(colorize=colorize)
if 'nir' in ret:
# NIR
output += self.generate_output_nir(
json_data=ret, hr=hr, show_name=show_name,
colorize=colorize
)
output += self.generate_output_newline(colorize=colorize)
return output
if script_args.addr:
results = IPWhoisCLI(
addr=script_args.addr[0],
timeout=script_args.timeout,
proxy_http=script_args.proxy_http if (
script_args.proxy_http and len(script_args.proxy_http) > 0
) else None,
proxy_https=script_args.proxy_https if (
script_args.proxy_https and len(script_args.proxy_https) > 0
) else None
)
if script_args.whois:
print(results.lookup_whois(
hr=script_args.hr,
show_name=script_args.show_name,
colorize=script_args.colorize,
inc_raw=script_args.inc_raw,
retry_count=script_args.retry_count,
get_referral=script_args.get_referral,
extra_blacklist=script_args.extra_blacklist.split(',') if (
script_args.extra_blacklist and
len(script_args.extra_blacklist) > 0) else None,
ignore_referral_errors=script_args.ignore_referral_errors,
field_list=script_args.field_list.split(',') if (
script_args.field_list and
len(script_args.field_list) > 0) else None,
extra_org_map=script_args.extra_org_map,
inc_nir=(not script_args.exclude_nir),
nir_field_list=script_args.nir_field_list.split(',') if (
script_args.nir_field_list and
len(script_args.nir_field_list) > 0) else None,
asn_methods=script_args.asn_methods.split(',') if (
script_args.asn_methods and
len(script_args.asn_methods) > 0) else None,
get_asn_description=(not script_args.skip_asn_description)
))
else:
print(results.lookup_rdap(
hr=script_args.hr,
show_name=script_args.show_name,
colorize=script_args.colorize,
inc_raw=script_args.inc_raw,
retry_count=script_args.retry_count,
depth=script_args.depth,
excluded_entities=script_args.excluded_entities.split(',') if (
script_args.excluded_entities and
len(script_args.excluded_entities) > 0) else None,
bootstrap=script_args.bootstrap,
rate_limit_timeout=script_args.rate_limit_timeout,
extra_org_map=script_args.extra_org_map,
inc_nir=(not script_args.exclude_nir),
nir_field_list=script_args.nir_field_list.split(',') if (
script_args.nir_field_list and
len(script_args.nir_field_list) > 0) else None,
asn_methods=script_args.asn_methods.split(',') if (
script_args.asn_methods and
len(script_args.asn_methods) > 0) else None,
get_asn_description=(not script_args.skip_asn_description)
))