feat: add wave 1 plugins (dns, encode, hash, defang, revshell, cidr)
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>
This commit is contained in:
78
plugins/cidr.py
Normal file
78
plugins/cidr.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""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}")
|
||||
Reference in New Issue
Block a user