feat(commands:peeringdb|Network): add peeringdb command

This commit is contained in:
Romain J 2021-04-23 00:47:58 +02:00
parent 2afd3af540
commit 2e7934148e
3 changed files with 122 additions and 72 deletions

View file

@ -13,7 +13,7 @@ from ipinfo.exceptions import RequestQuotaExceededError
from ipwhois import Net from ipwhois import Net
from ipwhois.asn import IPASN from ipwhois.asn import IPASN
from aiocache import cached from aiocache import cached, Cache
from aiocache.serializers import PickleSerializer from aiocache.serializers import PickleSerializer
from tuxbot.cogs.Network.functions.exceptions import ( from tuxbot.cogs.Network.functions.exceptions import (
@ -29,7 +29,12 @@ def _(x):
return x return x
@cached(ttl=15 * 60, serializer=PickleSerializer()) @cached(
ttl=24 * 3600,
serializer=PickleSerializer(),
cache=Cache.MEMORY,
namespace="network",
)
async def get_ip(loop, ip: str, inet: str = "") -> str: async def get_ip(loop, ip: str, inet: str = "") -> str:
_inet: socket.AddressFamily | int = 0 # pylint: disable=no-member _inet: socket.AddressFamily | int = 0 # pylint: disable=no-member
@ -52,7 +57,12 @@ async def get_ip(loop, ip: str, inet: str = "") -> str:
return await loop.run_in_executor(None, _get_ip, str(ip)) return await loop.run_in_executor(None, _get_ip, str(ip))
@cached(ttl=15 * 60, serializer=PickleSerializer()) @cached(
ttl=24 * 3600,
serializer=PickleSerializer(),
cache=Cache.MEMORY,
namespace="network",
)
async def get_hostname(loop, ip: str) -> str: async def get_hostname(loop, ip: str) -> str:
def _get_hostname(_ip: str): def _get_hostname(_ip: str):
try: try:
@ -71,11 +81,16 @@ async def get_hostname(loop, ip: str) -> str:
return "N/A" return "N/A"
@cached(ttl=15 * 60, serializer=PickleSerializer()) @cached(
async def get_ipwhois_result(loop, ip_address: str) -> NoReturn | dict: ttl=24 * 3600,
def _get_ipwhois_result(_ip_address: str) -> NoReturn | dict: serializer=PickleSerializer(),
cache=Cache.MEMORY,
namespace="network",
)
async def get_ipwhois_result(loop, ip: str) -> NoReturn | dict:
def _get_ipwhois_result(_ip: str) -> NoReturn | dict:
try: try:
net = Net(ip_address) net = Net(ip)
obj = IPASN(net) obj = IPASN(net)
return obj.lookup() return obj.lookup()
except ipwhois.exceptions.ASNRegistryError: except ipwhois.exceptions.ASNRegistryError:
@ -90,38 +105,48 @@ async def get_ipwhois_result(loop, ip_address: str) -> NoReturn | dict:
try: try:
return await asyncio.wait_for( return await asyncio.wait_for(
loop.run_in_executor(None, _get_ipwhois_result, str(ip_address)), loop.run_in_executor(None, _get_ipwhois_result, str(ip)),
timeout=0.200, timeout=0.200,
) )
except asyncio.exceptions.TimeoutError: except asyncio.exceptions.TimeoutError:
return {} return {}
@cached(ttl=15 * 60, serializer=PickleSerializer()) @cached(
async def get_ipinfo_result(apikey: str, ip_address: str) -> dict: ttl=24 * 3600,
serializer=PickleSerializer(),
cache=Cache.MEMORY,
namespace="network",
)
async def get_ipinfo_result(apikey: str, ip: str) -> dict:
try: try:
handler = ipinfo.getHandlerAsync( handler = ipinfo.getHandlerAsync(
apikey, request_options={"timeout": 7} apikey, request_options={"timeout": 7}
) )
return (await handler.getDetails(ip_address)).all return (await handler.getDetails(ip)).all
except RequestQuotaExceededError: except RequestQuotaExceededError:
return {} return {}
@cached(ttl=15 * 60, serializer=PickleSerializer()) @cached(
ttl=24 * 3600,
serializer=PickleSerializer(),
cache=Cache.MEMORY,
namespace="network",
)
async def get_crimeflare_result( async def get_crimeflare_result(
session: aiohttp.ClientSession, ip_address: str session: aiohttp.ClientSession, ip: str
) -> Optional[str]: ) -> Optional[str]:
try: try:
async with session.post( async with session.post(
"http://www.crimeflare.org:82/cgi-bin/cfsearch.cgi", "http://www.crimeflare.org:82/cgi-bin/cfsearch.cgi",
data=f"cfS={ip_address}", data=f"cfS={ip}",
timeout=aiohttp.ClientTimeout(total=15), timeout=aiohttp.ClientTimeout(total=15),
) as s: ) as s:
ip = re.search(r"(\d*\.\d*\.\d*\.\d*)", await s.text()) result = re.search(r"(\d*\.\d*\.\d*\.\d*)", await s.text())
if ip: if result:
return ip.group() return result.group()
except (aiohttp.ClientError, asyncio.exceptions.TimeoutError): except (aiohttp.ClientError, asyncio.exceptions.TimeoutError):
pass pass
@ -161,7 +186,12 @@ def merge_ipinfo_ipwhois(ipinfo_result: dict, ipwhois_result: dict) -> dict:
return output return output
@cached(ttl=15 * 60, serializer=PickleSerializer()) @cached(
ttl=24 * 3600,
serializer=PickleSerializer(),
cache=Cache.MEMORY,
namespace="network",
)
async def get_pydig_result( async def get_pydig_result(
loop, domain: str, query_type: str, dnssec: str | bool loop, domain: str, query_type: str, dnssec: str | bool
) -> list: ) -> list:
@ -187,48 +217,25 @@ async def get_pydig_result(
return [] return []
@cached(ttl=15 * 60, serializer=PickleSerializer()) @cached(
async def get_peeringdb_as_set_result( ttl=24 * 3600,
serializer=PickleSerializer(),
cache=Cache.MEMORY,
namespace="network",
)
async def get_peeringdb_net_result(
session: aiohttp.ClientSession, asn: str session: aiohttp.ClientSession, asn: str
) -> Optional[dict]: ) -> dict:
try: try:
async with session.get( async with session.get(
f"https://www.peeringdb.com/api/as_set/{asn}", f"https://peeringdb.com/api/net?asn={asn}",
timeout=aiohttp.ClientTimeout(total=5), timeout=aiohttp.ClientTimeout(total=8),
) as s: ) as s:
return await s.json() return await s.json()
except ( except (asyncio.exceptions.TimeoutError,):
aiohttp.ClientError,
aiohttp.ContentTypeError,
asyncio.exceptions.TimeoutError,
):
pass pass
return None return {"data": []}
@cached(ttl=15 * 60, serializer=PickleSerializer())
async def get_peeringdb_net_irr_as_set_result(
session: aiohttp.ClientSession, asn: str
) -> Optional[dict]:
try:
async with session.get(
f"https://www.peeringdb.com/api/net?irr_as_set={asn}",
timeout=aiohttp.ClientTimeout(total=10),
) as s:
json = await s.json()
for data in json:
if data["asn"] == int(asn):
return data
except (
aiohttp.ClientError,
aiohttp.ContentTypeError,
asyncio.exceptions.TimeoutError,
):
pass
return None
def check_ip_version_or_raise(version: str) -> bool | NoReturn: def check_ip_version_or_raise(version: str) -> bool | NoReturn:

View file

@ -1,6 +1,7 @@
import asyncio import asyncio
import logging import logging
import time import time
from datetime import datetime
from typing import Optional from typing import Optional
import aiohttp import aiohttp
@ -34,7 +35,7 @@ from tuxbot.core.utils.functions.extra import (
ContextPlus, ContextPlus,
command_extra, command_extra,
) )
from tuxbot.core.utils.functions.utils import shorten from tuxbot.core.utils.functions.utils import shorten, str_if_empty
from .config import NetworkConfig from .config import NetworkConfig
from .functions.utils import ( from .functions.utils import (
get_ip, get_ip,
@ -43,8 +44,7 @@ from .functions.utils import (
get_ipinfo_result, get_ipinfo_result,
get_ipwhois_result, get_ipwhois_result,
get_pydig_result, get_pydig_result,
# get_peeringdb_as_set_result, get_peeringdb_net_result,
# get_peeringdb_net_irr_as_set_result,
merge_ipinfo_ipwhois, merge_ipinfo_ipwhois,
check_query_type_or_raise, check_query_type_or_raise,
check_ip_version_or_raise, check_ip_version_or_raise,
@ -297,19 +297,58 @@ class Network(commands.Cog):
async def _peeringdb(self, ctx: ContextPlus, asn: ASConverter): async def _peeringdb(self, ctx: ContextPlus, asn: ASConverter):
check_asn_or_raise(str(asn)) check_asn_or_raise(str(asn))
return await ctx.send("Not implemented yet") data: dict = (
await get_peeringdb_net_result(self.bot.session, str(asn))
)["data"]
# peeringdb_as_set_result = await get_peeringdb_as_set_result( if not data:
# self.bot.session, str(asn) return await ctx.send(
# ) _(
# peeringdb_net_irr_as_set_result = ( "AS{asn} could not be found in PeeringDB's database.",
# await get_peeringdb_net_irr_as_set_result( ctx,
# self.bot.session, peeringdb_as_set_result["data"][0][asn] self.bot.config,
# ) ).format(asn=asn)
# )["data"] )
#
# data = peeringdb_net_irr_as_set_result data = data[0]
#
# self.bot.console.log(data) filtered = {
# "info_type": "Type",
# await ctx.send("done") "info_traffic": "Traffic",
"info_ratio": "Ratio",
"info_prefixes4": "Prefixes IPv4",
"info_prefixes6": "Prefixes IPv6",
}
filtered_link = {
"website": ("Site", "website"),
"looking_glass": ("Looking Glass", "looking_glass"),
"policy_general": ("Peering", "policy_url"),
}
e = discord.Embed(
title=f"{data['name']} ({str_if_empty(data['aka'], f'AS{asn}')})",
color=0x5858D7,
)
for key, name in filtered.items():
e.add_field(
name=name, value=f"```{str_if_empty(data.get(key), 'N/A')}```"
)
for key, names in filtered_link.items():
if data.get(key):
e.add_field(
name=names[0],
value=f"[{str_if_empty(data.get(key), 'N/A')}]"
f"({str_if_empty(data.get(names[1]), 'N/A')})",
)
if data["notes"]:
output = (await shorten(self.bot.session, data["notes"], 550))[1]
e.description = output["text"]
if data["created"]:
e.timestamp = datetime.strptime(
data["created"], "%Y-%m-%dT%H:%M:%SZ"
)
await ctx.send(embed=e)

View file

@ -1,6 +1,6 @@
import asyncio import asyncio
import functools import functools
from typing import Dict from typing import Dict, Optional
import aiohttp import aiohttp
from discord.ext import commands from discord.ext import commands
@ -81,3 +81,7 @@ def replace_in_list(value: list, search: str, replace: str) -> list:
clean.append(v) clean.append(v)
return clean return clean
def str_if_empty(value: Optional[str], replacement: str) -> str:
return value if value else replacement