All pure stdlib, zero external dependencies: - dns: raw UDP resolver with A/AAAA/MX/NS/TXT/CNAME/PTR/SOA - encode: base64, hex, URL, ROT13 encode/decode - hash: md5/sha1/sha256/sha512 generation + type identification - defang: IOC defanging/refanging for safe sharing - revshell: reverse shell one-liners for 11 languages - cidr: subnet calculator with IP membership check Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
79 lines
2.3 KiB
Python
79 lines
2.3 KiB
Python
"""Plugin: CIDR/subnet calculator."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import ipaddress
|
|
|
|
from derp.plugin import command
|
|
|
|
|
|
@command("cidr", help="Subnet info: !cidr <network> | !cidr contains <net> <ip>")
|
|
async def cmd_cidr(bot, message):
|
|
"""Calculate subnet properties or check IP membership.
|
|
|
|
!cidr 192.168.1.0/24
|
|
!cidr contains 10.0.0.0/8 10.1.2.3
|
|
"""
|
|
parts = message.text.split()
|
|
if len(parts) < 2:
|
|
await bot.reply(message, "Usage: !cidr <network> | !cidr contains <net> <ip>")
|
|
return
|
|
|
|
subcmd = parts[1].lower()
|
|
|
|
if subcmd == "contains" and len(parts) >= 4:
|
|
await _contains(bot, message, parts[2], parts[3])
|
|
elif subcmd == "contains":
|
|
await bot.reply(message, "Usage: !cidr contains <network> <ip>")
|
|
else:
|
|
await _info(bot, message, parts[1])
|
|
|
|
|
|
async def _info(bot, message, network_str: str) -> None:
|
|
"""Show subnet information."""
|
|
try:
|
|
net = ipaddress.ip_network(network_str, strict=False)
|
|
except ValueError:
|
|
await bot.reply(message, f"Invalid network: {network_str}")
|
|
return
|
|
|
|
if isinstance(net, ipaddress.IPv4Network):
|
|
host_count = net.num_addresses - 2 if net.prefixlen < 31 else net.num_addresses
|
|
parts = [
|
|
f"net:{net.network_address}/{net.prefixlen}",
|
|
f"range:{net[0]}-{net[-1]}",
|
|
f"hosts:{host_count}",
|
|
f"mask:{net.netmask}",
|
|
f"wildcard:{net.hostmask}",
|
|
]
|
|
if net.prefixlen < 31:
|
|
parts.append(f"broadcast:{net.broadcast_address}")
|
|
else:
|
|
parts = [
|
|
f"net:{net.network_address}/{net.prefixlen}",
|
|
f"range:{net[0]}-{net[-1]}",
|
|
f"hosts:{net.num_addresses}",
|
|
]
|
|
|
|
await bot.reply(message, " | ".join(parts))
|
|
|
|
|
|
async def _contains(bot, message, network_str: str, ip_str: str) -> None:
|
|
"""Check if an IP belongs to a network."""
|
|
try:
|
|
net = ipaddress.ip_network(network_str, strict=False)
|
|
except ValueError:
|
|
await bot.reply(message, f"Invalid network: {network_str}")
|
|
return
|
|
|
|
try:
|
|
addr = ipaddress.ip_address(ip_str)
|
|
except ValueError:
|
|
await bot.reply(message, f"Invalid IP: {ip_str}")
|
|
return
|
|
|
|
if addr in net:
|
|
await bot.reply(message, f"{addr} is in {net}")
|
|
else:
|
|
await bot.reply(message, f"{addr} is NOT in {net}")
|