ipilot 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ipilot/__init__.py ADDED
File without changes
ipilot/args.py ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/local/env python3
2
+
3
+ """MODULE: Provides CLI arguments to the application."""
4
+
5
+ import argparse
6
+
7
+ from ipilot.query_normalisation import get_public_ip
8
+
9
+
10
+ def parse_args() -> argparse.Namespace: # pragma: no cover
11
+ """Get arguments from user via the command line.
12
+
13
+ Args:
14
+ None
15
+
16
+ Returns:
17
+ argparse.Namespace: parsed arguments
18
+ """
19
+ parser = argparse.ArgumentParser(
20
+ description="Query information about an IP address or domain name."
21
+ )
22
+ parser.add_argument(
23
+ "-q",
24
+ "--query",
25
+ help="IP/domain name to query (default: current public IP)",
26
+ default=get_public_ip(),
27
+ )
28
+ parser.add_argument(
29
+ "-p",
30
+ "--prefixes",
31
+ help="show advertised prefixes",
32
+ action="store_true",
33
+ )
34
+ parser.add_argument(
35
+ "-n",
36
+ "--noheader",
37
+ help="do not print header",
38
+ action="store_true",
39
+ )
40
+ return parser.parse_args()
ipilot/ip_info.py ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """MODULE: Provides functions to call various APIs to retrieve IP/prefix information."""
4
+
5
+ import ipaddress
6
+
7
+ import requests
8
+
9
+ from typing import Optional
10
+
11
+
12
+ def get_ip_information(ipv4_address: ipaddress.IPv4Address) -> Optional[dict]:
13
+ """Retrieves information about a given IPv4 address from IP-API.com.
14
+
15
+ Args:
16
+ ipv4_address (ipaddress.IPv4Address): IPv4 address to query
17
+
18
+ Returns:
19
+ Optional[dict]: API response
20
+ """
21
+ api_endpoint: str = f"http://ip-api.com/json/{ipv4_address}"
22
+ try:
23
+ resp: requests.Response = requests.get(api_endpoint, timeout=10)
24
+ resp.raise_for_status()
25
+ ret: dict | None = resp.json() if resp.json().get("status") == "success" else None
26
+ except (requests.exceptions.JSONDecodeError, requests.exceptions.HTTPError):
27
+ ret = None
28
+ return ret
29
+
30
+
31
+ def get_autonomous_system_number(as_info: str) -> str:
32
+ """Parses AS number from provided AS information.
33
+
34
+ Args:
35
+ as_info (str): AS information
36
+
37
+ Returns:
38
+ str: AS number
39
+ """
40
+ as_number: str = as_info.split(" ")[0]
41
+ return as_number
42
+
43
+
44
+ def get_prefix_information(autonomous_system: str) -> Optional[list]:
45
+ """Retrieves prefix information about a given autonomous system.
46
+
47
+ Args:
48
+ autonomous_system (str): autonomous system to query, e.g. AS123
49
+
50
+ Returns:
51
+ Optional[list]: API response
52
+ """
53
+ api_endpoint: str = f"https://api.hackertarget.com/aslookup/?q={str(autonomous_system)}"
54
+ try:
55
+ resp: requests.Response = requests.get(api_endpoint, timeout=10)
56
+ resp.raise_for_status()
57
+ except requests.exceptions.HTTPError:
58
+ return None
59
+ prefixes: list[str] = resp.text.split("\n")
60
+ prefixes.pop(0)
61
+ return prefixes
ipilot/main.py ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/local/bin/python3
2
+
3
+ """MODULE: Main application module."""
4
+
5
+ import sys
6
+
7
+ from ipilot.args import parse_args
8
+ from ipilot.ip_info import (
9
+ get_autonomous_system_number,
10
+ get_ip_information,
11
+ get_prefix_information,
12
+ )
13
+ from ipilot.print_table import generate_prefix_string, print_table
14
+ from ipilot.query_normalisation import is_ip_address, resolve_domain_name
15
+
16
+ HEADER = """-----------------------------------------------
17
+ | IP Address Information Lookup Tool (iPilot) |
18
+ | By Luke Tainton (@luketainton) |
19
+ -----------------------------------------------\n"""
20
+
21
+
22
+ def main() -> None:
23
+ """Main function.
24
+
25
+ Args:
26
+ None
27
+
28
+ Returns:
29
+ None
30
+ """
31
+ args = parse_args()
32
+ if not args.noheader:
33
+ print(HEADER)
34
+
35
+ # Set IP to passed IP address, or resolve passed domain name to IPv4
36
+ ip_address = resolve_domain_name(args.query) if not is_ip_address(args.query) else args.query
37
+
38
+ # If not given an IPv4, and can't resolve to IPv4, then throw error and exit
39
+ if not ip_address:
40
+ print("ERROR: could not resolve query to IPv4 address.")
41
+ sys.exit(1)
42
+
43
+ # Get information from API
44
+ ip_info: dict | None = get_ip_information(ip_address)
45
+ if not ip_info:
46
+ print("ERROR: could not retrieve IP information from API.")
47
+ sys.exit(1)
48
+ as_number: str = get_autonomous_system_number(ip_info["as"])
49
+
50
+ # Assemble list for table generation
51
+ country: str = ip_info["country"]
52
+ region: str = ip_info["regionName"]
53
+ city: str = ip_info["city"]
54
+ table_data: list = [
55
+ ["IP Address", ip_info["query"]],
56
+ ["Organization", ip_info["org"]],
57
+ ["Location", f"{country}/{region}/{city}"],
58
+ ["Timezone", ip_info["timezone"]],
59
+ ["Internet Service Provider", ip_info["isp"]],
60
+ ["Autonomous System", as_number],
61
+ ]
62
+
63
+ # If wanted, get prefix information
64
+ if args.prefixes:
65
+ prefix_info = get_prefix_information(as_number)
66
+ if not prefix_info:
67
+ print("ERROR: could not retrieve prefix information from API.")
68
+ sys.exit(1)
69
+ else:
70
+ table_data.append(["Prefixes", generate_prefix_string(prefix_info)])
71
+
72
+ print_table(table_data)
73
+
74
+
75
+ if __name__ == "__main__":
76
+ main()
ipilot/print_table.py ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """MODULE: Provides functions for preparing, then printing, retrieved data."""
4
+
5
+ from tabulate import tabulate
6
+
7
+
8
+ def generate_prefix_string(prefixes: list) -> str | None:
9
+ """Generate a string that spilts prefixes into rows of 4."""
10
+ num_per_row = 4
11
+ try:
12
+ ret: str = ""
13
+ for i in range(0, len(prefixes), num_per_row):
14
+ ret += ", ".join(prefixes[i : i + num_per_row]) + "\n"
15
+ return ret
16
+ except AttributeError:
17
+ return None
18
+
19
+
20
+ def print_table(table_data) -> None: # pragma: no cover
21
+ """Print table generated by tabulate."""
22
+ print(tabulate(table_data))
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """MODULE: Provides functions that ensure an IP address is
4
+ available to query the APIs for."""
5
+
6
+ import ipaddress
7
+ import socket
8
+
9
+ import requests
10
+
11
+
12
+ def is_ip_address(query: str) -> bool:
13
+ """Verifies if a given query is a valid IPv4 address."""
14
+ try:
15
+ ipaddress.ip_address(query)
16
+ return True
17
+ except ValueError:
18
+ return False
19
+
20
+
21
+ def resolve_domain_name(domain_name: str) -> ipaddress.IPv4Address | None:
22
+ """Resolve a domain name via DNS or return None."""
23
+ try:
24
+ result: str = socket.gethostbyname(domain_name)
25
+ ip_address: ipaddress.IPv4Address = ipaddress.IPv4Address(result)
26
+ return ip_address
27
+ except (socket.gaierror, ipaddress.AddressValueError):
28
+ return None
29
+
30
+
31
+ def get_public_ip() -> ipaddress.IPv4Address:
32
+ """Get the user's current public IPv4 address."""
33
+ ip_address: str = requests.get("https://api.ipify.org", timeout=10).text
34
+ return ipaddress.IPv4Address(ip_address)
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.1
2
+ Name: ipilot
3
+ Version: 0.0.0
4
+ Summary: IP Information Lookup Tool
5
+ Author: Luke Tainton
6
+ Author-email: luke@tainton.uk
7
+ Requires-Python: >=3.8,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.8
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Dist: astroid (==3.2.4)
16
+ Requires-Dist: attrs (==24.3.0)
17
+ Requires-Dist: certifi (==2024.12.14)
18
+ Requires-Dist: charset-normalizer (==3.4.1)
19
+ Requires-Dist: click (==8.1.8)
20
+ Requires-Dist: dill (==0.3.9)
21
+ Requires-Dist: exceptiongroup (==1.2.2)
22
+ Requires-Dist: idna (==3.10)
23
+ Requires-Dist: iniconfig (==2.0.0)
24
+ Requires-Dist: lazy-object-proxy (==1.10.0)
25
+ Requires-Dist: mccabe (==0.7.0)
26
+ Requires-Dist: mypy-extensions (==1.0.0)
27
+ Requires-Dist: packaging (==24.2)
28
+ Requires-Dist: pathspec (==0.12.1)
29
+ Requires-Dist: platformdirs (==4.3.6)
30
+ Requires-Dist: pluggy (==1.5.0)
31
+ Requires-Dist: py (==1.11.0)
32
+ Requires-Dist: pyparsing (==3.1.4)
33
+ Requires-Dist: requests (==2.32.3)
34
+ Requires-Dist: six (==1.17.0)
35
+ Requires-Dist: tabulate (==0.9.0)
36
+ Requires-Dist: tomli (==2.2.1)
37
+ Requires-Dist: tomlkit (==0.13.2)
38
+ Requires-Dist: urllib3 (==2.2.3)
39
+ Requires-Dist: wrapt (==1.17.0)
40
+ Description-Content-Type: text/markdown
41
+
42
+ # iPilot [![CI](https://github.com/luketainton/pypilot/actions/workflows/ci.yml/badge.svg)](https://github.com/luketainton/pypilot/actions/workflows/ci.yml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=luketainton_pypilot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=luketainton_pypilot)
43
+
44
+ ## Description
45
+ IP Information Lookup Tool
46
+
47
+ ## How to install
48
+ `pip install ipilot`
49
+
50
+ ## How to use
51
+ `ipilot --help`
52
+
@@ -0,0 +1,10 @@
1
+ ipilot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ ipilot/args.py,sha256=xtjr6B1XH9wUswMDK5cpTDWRrNA7Ld3RWmvNrW0F104,954
3
+ ipilot/ip_info.py,sha256=NAy3Wdo92YhlKP1PCjXOsO7a4IwAPKyQP-9KyVb4l2E,1787
4
+ ipilot/main.py,sha256=tiY9ZGNyN9cFamREDZPgImCs3wi4G_j-Io1ZQ1XI4zQ,2259
5
+ ipilot/print_table.py,sha256=3QP3mdN5_0n4JVZ_S3r1Rn4FgmoHkWcZhmaM3MCp-WU,634
6
+ ipilot/query_normalisation.py,sha256=9nOYhVwvr8eOfWfmE8_CptHpRSwCKXH9YDyKzUqUWe8,980
7
+ ipilot-0.0.0.dist-info/METADATA,sha256=NvYyjD7Stv4_sg3-754mno4BTHhbB-hxoyj38ykWvS4,1877
8
+ ipilot-0.0.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
9
+ ipilot-0.0.0.dist-info/entry_points.txt,sha256=2Ve389A36UooJzr-7C1hdB99TlYR0aoBkrL4TDrgRqg,40
10
+ ipilot-0.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.9.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ app=ipilot.main:main
3
+