Last active
February 3, 2024 17:33
-
-
Save agostof/118d8320a75d9bd2d13a7ca42aed6aaf to your computer and use it in GitHub Desktop.
Fetches ISP CIDR ranges from IP addresses using ARIN RDAP, aiding network analysis, useful for setting up firewall rules.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# There are resources | |
# https://www.arin.net/resources/registry/whois/rdap/ | |
# + | |
# There are several ways to getting the CIDR that one IP belongs to. | |
# A quick way is by using whois for a given a IP. # The CIRD will be on the record: | |
# For example for IP Address 8.8.8.1: | |
# whois 8.8.8.1 | |
# ... LINES OMITTED ... | |
# NetRange: 8.8.8.0 - 8.8.8.255 | |
# CIDR: 8.8.8.0/24 | |
# ... LINES OMITTED ... | |
# For geting the CIDRs assigned to an organization, we can also use whois | |
# to extract the ARIN records. We would need to grab the arin entry from the record. | |
# We need to locate the NetName, OrgID, then grab registry URL for the entity. | |
# For example: | |
# whois 8.8.8.1 | |
# | |
# NetRange: 8.8.8.0 - 8.8.8.255 | |
# CIDR: 8.8.8.0/24 | |
# NetName: GOGL | |
# ... LINES OMITTED ... | |
# Ref: https://rdap.arin.net/registry/ip/8.8.8.0 | |
# ... | |
# OrgName: Google LLC | |
# OrgId: GOGL | |
# Ref: https://rdap.arin.net/registry/entity/GOGL | |
# | |
# On this example we would use `https://rdap.arin.net/registry/entity/GOGL` | |
# on the script below. | |
# | |
# The oher option is to run the `get_arin_rdap_url` included in this gist e.g. | |
# rdap_url = get_arin_rdap_url("8.8.8.8") | |
# | |
# You can also query multiple IPS based on the ip addresses of interest. | |
# Setting `ip_addresses_of_interest` to a list of IPs e.g. | |
# ip_addresses_of_interest = ['YOUR_IP', 'SOME_OTHER_IP', 'SOME_OTHER_IP2'] | |
# This will result on the script finding the entities who control those IPs. | |
# Please be judicious of the number of queries. Some rate limitting will might be needed. | |
isps = {'ATT Mobility':'https://rdap.arin.net/registry/entity/ATTMO-3', | |
'Digital Ocean':'https://rdap.arin.net/registry/entity/DO-13', | |
} | |
ip_addresses_of_interest = ['199.4.240.1', '8.8.8.8'] | |
import requests | |
#import urllib.request | |
import json | |
import time | |
from pprint import pprint | |
def parse_cidr(data, isp_name=None): | |
out = {} | |
handle = data['handle'] | |
if isp_name: | |
full_isp_name = f'{isp_name}:{handle}' | |
else: | |
full_isp_name = f'{isp_name}' | |
out[full_isp_name] = {'ipv4':[], 'ipv6':[]} | |
for i in data['networks']: | |
cidrs = i['cidr0_cidrs'] | |
for o in cidrs: | |
if 'v4prefix' in o: | |
out[full_isp_name]['ipv4'].append( "{v4prefix}/{length}".format(**o)) | |
elif 'v6prefix' in o: | |
out[full_isp_name]['ipv6'].append( "{v6prefix}/{length}".format(**o)) | |
else: | |
raise | |
return out | |
def query_arin_api(ip): | |
try: | |
url = f"https://rdap.arin.net/registry/ip/{ip}" | |
# using the requests module | |
arin_res = requests.get(url) | |
data = json.loads(arin_res.text) | |
return data | |
# to use without the reques module use this: | |
# with urllib.request.urlopen(url) as response: | |
# data = json.loads(response.read().decode()) | |
# return data | |
except Exception as e: | |
print(f"Error querying ARIN API for IP {ip}: {e}") | |
return None | |
def parse_arin_data(arin_data): | |
rdap_urls = [] | |
for entity in arin_data['entities']: | |
if 'registrant' not in entity['roles']: | |
# will keep only the registrant records | |
# skip the rest | |
continue | |
handle = entity['handle'] | |
entity_long_name = entity['vcardArray'][1][1][3] | |
for link in entity['links']: | |
if link['type'] == 'application/rdap+json': | |
rdap_url = link["href"] | |
#rdap_urls.append( f'{handle}@{link["href"]}' ) | |
#print(f'{entity_long_name},{handle},{rdap_url}') | |
#rdap_urls.append(link["href"]) | |
rdap_urls.append(f'{entity_long_name},{handle},{rdap_url}') | |
# just in case there are other records | |
out_name, out_handle, out_url = list(set(rdap_urls))[0].split(",") | |
return {'entity_long_name': out_name, 'handle': out_handle, 'rdap_url': out_url} | |
def get_arin_rdap_url(ip): | |
arin_data = query_arin_api(ip) | |
out = parse_arin_data(arin_data) | |
return out | |
for ip in ip_addresses_of_interest: | |
record = get_arin_rdap_url(ip) | |
isps[ record['entity_long_name']] = record['rdap_url'] | |
print(record) | |
results = {} | |
for isp_name, url in isps.items(): | |
isp_res = requests.get(url) | |
if isp_res.ok: | |
data = json.loads(isp_res.text) | |
results[isp_name] = data | |
else: | |
results[isp_name] = None | |
time.sleep(0.25) | |
out = {} | |
for isp_name,data in results.items(): | |
out.update(parse_cidr(data, isp_name)) | |
#pprint(parse_cidr(data)) | |
pprint(out) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment