mirror of
https://github.com/jlu5/SupyPlugins.git
synced 2025-05-06 11:33:41 -05:00
BirdLGGo: support optionally printing trace hop PTR
This commit is contained in:
parent
ebcbf5a92d
commit
f6932586fa
@ -47,11 +47,15 @@ def configure(advanced):
|
||||
conf.registerPlugin('BirdLGGo', True)
|
||||
|
||||
|
||||
class _traceHopFormat(registry.OnlySomeStrings):
|
||||
validStrings = ('ip', 'ptr', 'both')
|
||||
|
||||
BirdLGGo = conf.registerPlugin('BirdLGGo')
|
||||
conf.registerChannelValue(BirdLGGo, 'lgServer',
|
||||
registry.String("", _("""Looking glass server to query, including the scheme (http(s)://)""")))
|
||||
registry.String("", _("""Looking glass server to query, including the scheme (http(s)://).""")))
|
||||
conf.registerChannelValue(BirdLGGo, 'nodes',
|
||||
registry.SpaceSeparatedListOfStrings([], _("""List of nodes to query (space separated list)""")))
|
||||
|
||||
registry.SpaceSeparatedListOfStrings([], _("""List of nodes to query (space separated list).""")))
|
||||
conf.registerChannelValue(BirdLGGo, 'traceHopFormat',
|
||||
_traceHopFormat(_traceHopFormat.validStrings[0], _("""Whether the plugin should show IPs, reverse DNS (PTR records), or both for each hop in a traceroute.""")))
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=120:
|
||||
|
@ -5,9 +5,14 @@ import subprocess
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
|
||||
@dataclass
|
||||
class TraceHop:
|
||||
ip: str
|
||||
ptr: str
|
||||
|
||||
@dataclass
|
||||
class TraceResult:
|
||||
ips: List[str]
|
||||
hops: List[TraceHop]
|
||||
latency: str = field(default=None)
|
||||
notes: List[str] = field(default_factory=list)
|
||||
|
||||
@ -16,14 +21,14 @@ class TraceParseError(ValueError):
|
||||
|
||||
# bird-lg-go enables DNS lookups and sends one query by default, but we should be a bit more flexible with what we accept
|
||||
# This will grab IPs from whichever format is used, and report the first latency / error code of the last line if there are multiple queries
|
||||
_TRACEROUTE_RE = re.compile(r'\s*\d+\s+(?P<line>(?:[^() ]+ \((?P<IPdns>[0-9a-fA-F.:]+)\)|(?P<IPbare>[0-9a-fA-F.:]+)).*? (?P<latency>[0-9.]+ ms( \![A-Za-z0-9]+)?)|\*)')
|
||||
_TRACEROUTE_RE = re.compile(r'\s*\d+\s+(?P<line>(?:(?P<ptr>[^() ]+) \((?P<IPdns>[0-9a-fA-F.:]+)\)|(?P<IPbare>[0-9a-fA-F.:]+)).*? (?P<latency>[0-9.]+ ms( \![A-Za-z0-9]+)?)|\*)')
|
||||
def parse_traceroute(text):
|
||||
lines = text.strip().splitlines()
|
||||
if len(lines) < 2 or not lines[1].lstrip().startswith("1"):
|
||||
# Assume error condition if 2nd line doesn't start with "1" (first hop)
|
||||
raise TraceParseError(' '.join(lines) or "traceroute returned empty output")
|
||||
else:
|
||||
ips = []
|
||||
hops = []
|
||||
notes = []
|
||||
latency = None
|
||||
for line in lines[1:]:
|
||||
@ -33,14 +38,16 @@ def parse_traceroute(text):
|
||||
if not m:
|
||||
notes.append(line)
|
||||
continue
|
||||
ips.append(m.group("IPdns") or m.group("IPbare") or m.group("line"))
|
||||
ip = m.group("IPdns") or m.group("IPbare") or m.group("line")
|
||||
ptr = m.group("ptr") or ip
|
||||
hops.append(TraceHop(ip, ptr))
|
||||
latency = m.group("latency")
|
||||
|
||||
# bird-lg-go specific truncation
|
||||
if "hops not responding" in ''.join(notes):
|
||||
latency = None
|
||||
|
||||
return TraceResult(ips, latency, notes)
|
||||
return TraceResult(hops, latency, notes)
|
||||
|
||||
if __name__ == '__main__':
|
||||
proc = subprocess.run(['traceroute', *sys.argv[1:]], encoding='utf-8', stdout=subprocess.PIPE)
|
||||
|
@ -1,5 +1,9 @@
|
||||
from pprint import pprint
|
||||
import unittest
|
||||
from parsetrace import parse_traceroute, TraceResult
|
||||
from parsetrace import parse_traceroute, TraceResult, TraceHop
|
||||
|
||||
def _bareip(ip: str):
|
||||
return TraceHop(ip=ip, ptr=ip)
|
||||
|
||||
class ParseTraceTestCase(unittest.TestCase):
|
||||
maxDiff = None
|
||||
@ -14,14 +18,14 @@ class ParseTraceTestCase(unittest.TestCase):
|
||||
6 2001:4860:0:1::5737 (2001:4860:0:1::5737) 0.870 ms
|
||||
7 ord37s33-in-x0e.1e100.net (2607:f8b0:4009:809::200e) 46.306 ms
|
||||
"""
|
||||
self.assertEqual(TraceResult(ips=[
|
||||
"2605:4840:3::1",
|
||||
"2604:6600:2700:11::1",
|
||||
"2001:418:0:5000::ec0",
|
||||
"2001:504:0:4:0:1:5169:1",
|
||||
"*",
|
||||
"2001:4860:0:1::5737",
|
||||
"2607:f8b0:4009:809::200e"
|
||||
self.assertEqual(TraceResult(hops=[
|
||||
_bareip("2605:4840:3::1"),
|
||||
_bareip("2604:6600:2700:11::1"),
|
||||
TraceHop("2001:418:0:5000::ec0", "ce-0-7-0-2.r07.chcgil09.us.bb.gin.ntt.net"),
|
||||
TraceHop("2001:504:0:4:0:1:5169:1", "eqix-ch-200g-1.google.com"),
|
||||
_bareip("*"),
|
||||
_bareip("2001:4860:0:1::5737"),
|
||||
TraceHop("2607:f8b0:4009:809::200e", "ord37s33-in-x0e.1e100.net")
|
||||
], latency="46.306 ms"), parse_traceroute(s))
|
||||
|
||||
def testTracerouteMultiQuery(self):
|
||||
@ -32,12 +36,12 @@ class ParseTraceTestCase(unittest.TestCase):
|
||||
4 cloudflare.slix.net (149.112.13.27) 9.057 ms 9.060 ms 9.504 ms
|
||||
5 one.one.one.one (1.1.1.1) 8.567 ms 8.688 ms 8.764 ms
|
||||
"""
|
||||
self.assertEqual(TraceResult(ips=[
|
||||
'205.185.112.1',
|
||||
'172.18.0.29',
|
||||
'184.104.194.81',
|
||||
'149.112.13.27',
|
||||
'1.1.1.1'
|
||||
self.assertEqual(TraceResult(hops=[
|
||||
_bareip('205.185.112.1'),
|
||||
_bareip('172.18.0.29'),
|
||||
TraceHop('184.104.194.81', '100ge3-2.core1.slc1.he.net'),
|
||||
TraceHop('149.112.13.27', 'cloudflare.slix.net'),
|
||||
TraceHop('1.1.1.1', 'one.one.one.one')
|
||||
], latency="8.567 ms"), parse_traceroute(s))
|
||||
|
||||
def testTracerouteMultiQueryDifferentPaths(self):
|
||||
@ -56,20 +60,21 @@ class ParseTraceTestCase(unittest.TestCase):
|
||||
12 142.250.210.209 (142.250.210.209) 3.812 ms 108.170.252.18 (108.170.252.18) 3.763 ms 142.250.210.209 (142.250.210.209) 3.685 ms
|
||||
13 fra16s51-in-f14.1e100.net (142.250.185.174) 3.895 ms 3.871 ms 3.945 ms
|
||||
"""
|
||||
self.assertEqual(TraceResult(ips=[
|
||||
'172.31.1.1',
|
||||
'49.12.142.82',
|
||||
'*',
|
||||
'78.47.3.237',
|
||||
'85.10.239.169',
|
||||
'85.10.228.85',
|
||||
'213.239.245.250',
|
||||
'213.239.224.217',
|
||||
'142.250.169.172',
|
||||
'*',
|
||||
'142.250.226.148',
|
||||
'142.250.210.209',
|
||||
'142.250.185.174'], latency="3.895 ms"), parse_traceroute(s))
|
||||
self.assertEqual(TraceResult(hops=[
|
||||
TraceHop(ip='172.31.1.1', ptr='172.31.1.1'),
|
||||
TraceHop(ip='49.12.142.82', ptr='17476.your-cloud.host'),
|
||||
TraceHop(ip='*', ptr='*'),
|
||||
TraceHop(ip='78.47.3.237', ptr='static.237.3.47.78.clients.your-server.de'),
|
||||
TraceHop(ip='85.10.239.169', ptr='static.85.10.239.169.clients.your-server.de'),
|
||||
TraceHop(ip='85.10.228.85', ptr='static.85-10-228-85.clients.your-server.de'),
|
||||
TraceHop(ip='213.239.245.250', ptr='core1.fra.hetzner.com'),
|
||||
TraceHop(ip='213.239.224.217', ptr='core8.fra.hetzner.com'),
|
||||
TraceHop(ip='142.250.169.172', ptr='142.250.169.172'),
|
||||
TraceHop(ip='*', ptr='*'),
|
||||
TraceHop(ip='142.250.226.148', ptr='142.250.226.148'),
|
||||
TraceHop(ip='142.250.210.209', ptr='142.250.210.209'),
|
||||
TraceHop(ip='142.250.185.174', ptr='fra16s51-in-f14.1e100.net')],
|
||||
latency='3.895 ms'), parse_traceroute(s))
|
||||
|
||||
def testTracerouteTimedOut(self):
|
||||
s = """
|
||||
@ -105,15 +110,16 @@ traceroute to azure.microsoft.com (13.107.42.16), 30 hops max, 60 byte packets
|
||||
29 * * *
|
||||
30 * * *
|
||||
"""
|
||||
self.assertEqual(TraceResult(ips=[
|
||||
'172.31.1.1',
|
||||
'49.12.142.82',
|
||||
'*',
|
||||
'78.47.3.237',
|
||||
'85.10.248.221',
|
||||
'213.239.208.221',
|
||||
'213.239.224.238',
|
||||
'104.44.37.193'] + ["*"]*(30-9+1)), parse_traceroute(s))
|
||||
self.assertEqual(TraceResult(hops=[
|
||||
TraceHop(ip='172.31.1.1', ptr='172.31.1.1'),
|
||||
TraceHop(ip='49.12.142.82', ptr='17476.your-cloud.host'),
|
||||
TraceHop(ip='*', ptr='*'),
|
||||
TraceHop(ip='78.47.3.237', ptr='static.237.3.47.78.clients.your-server.de'),
|
||||
TraceHop(ip='85.10.248.221', ptr='static.85.10.248.221.clients.your-server.de'),
|
||||
TraceHop(ip='213.239.208.221', ptr='core11.nbg1.hetzner.com'),
|
||||
TraceHop(ip='213.239.224.238', ptr='core5.fra.hetzner.com'),
|
||||
TraceHop(ip='104.44.37.193', ptr='ae72-0.fra-96cbe-1b.ntwk.msn.net')] +
|
||||
[_bareip("*")]*(30-9+1)), parse_traceroute(s))
|
||||
|
||||
|
||||
def testTracerouteTimedOutTruncated(self):
|
||||
@ -131,14 +137,15 @@ traceroute to azure.microsoft.com (13.107.42.16), 30 hops max, 60 byte packets
|
||||
23 hops not responding.
|
||||
|
||||
"""
|
||||
self.assertEqual(TraceResult(ips=[
|
||||
'172.31.1.1',
|
||||
'49.12.142.82',
|
||||
'78.47.3.237',
|
||||
'85.10.248.217',
|
||||
'85.10.250.209',
|
||||
'213.239.245.254',
|
||||
'104.44.197.103'], notes=["23 hops not responding."]), parse_traceroute(s))
|
||||
self.assertEqual(TraceResult(hops=[
|
||||
TraceHop(ip='172.31.1.1', ptr='172.31.1.1'),
|
||||
TraceHop(ip='49.12.142.82', ptr='17476.your-cloud.host'),
|
||||
TraceHop(ip='78.47.3.237', ptr='static.237.3.47.78.clients.your-server.de'),
|
||||
TraceHop(ip='85.10.248.217', ptr='static.85.10.248.217.clients.your-server.de'),
|
||||
TraceHop(ip='85.10.250.209', ptr='core11.nbg1.hetzner.com'),
|
||||
TraceHop(ip='213.239.245.254', ptr='core1.fra.hetzner.com'),
|
||||
TraceHop(ip='104.44.197.103', ptr='hetzner.fra-96cbe-1a.ntwk.msn.net')],
|
||||
notes=["23 hops not responding."]), parse_traceroute(s))
|
||||
|
||||
def testTracerouteNoDNS(self):
|
||||
s = """
|
||||
@ -152,15 +159,15 @@ traceroute to irc.hackint.dn42 (172.20.66.67), 30 hops max, 60 byte packets
|
||||
7 172.23.96.1 165.631 ms 182.696 ms 182.695 ms
|
||||
8 172.20.66.67 182.695 ms 167.117 ms 187.094 ms"""
|
||||
|
||||
self.assertEqual(TraceResult(ips=[
|
||||
'172.20.229.114',
|
||||
'172.20.229.113',
|
||||
'172.20.229.123',
|
||||
'*',
|
||||
'*',
|
||||
'172.20.129.169',
|
||||
'172.23.96.1',
|
||||
'172.20.66.67'], latency="182.695 ms"), parse_traceroute(s))
|
||||
self.assertEqual(TraceResult(hops=[
|
||||
_bareip('172.20.229.114'),
|
||||
_bareip('172.20.229.113'),
|
||||
_bareip('172.20.229.123'),
|
||||
_bareip('*'),
|
||||
_bareip('*'),
|
||||
_bareip('172.20.129.169'),
|
||||
_bareip('172.23.96.1'),
|
||||
_bareip('172.20.66.67')], latency="182.695 ms"), parse_traceroute(s))
|
||||
|
||||
def testTracerouteNoDNSv6(self):
|
||||
s = """traceroute to map.dn42 (fd42:4242:2189:e9::1), 30 hops max, 80 byte packets
|
||||
@ -170,19 +177,19 @@ traceroute to irc.hackint.dn42 (172.20.66.67), 30 hops max, 60 byte packets
|
||||
4 fd42:4242:2189:e9::1 208.400 ms 208.398 ms 208.543 ms
|
||||
"""
|
||||
|
||||
self.assertEqual(TraceResult(ips=[
|
||||
'fd86:bad:11b7:34::1',
|
||||
'fd86:bad:11b7:22::1',
|
||||
'fd42:4242:2189:ef::1',
|
||||
'fd42:4242:2189:e9::1'], latency="208.400 ms"), parse_traceroute(s))
|
||||
self.assertEqual(TraceResult(hops=[
|
||||
_bareip('fd86:bad:11b7:34::1'),
|
||||
_bareip('fd86:bad:11b7:22::1'),
|
||||
_bareip('fd42:4242:2189:ef::1'),
|
||||
_bareip('fd42:4242:2189:e9::1')], latency="208.400 ms"), parse_traceroute(s))
|
||||
|
||||
def testTracerouteError(self):
|
||||
s = """traceroute to 192.168.123.123 (192.168.123.123), 30 hops max, 60 byte packets
|
||||
1 192.168.123.1 (192.168.123.1) 3079.329 ms !H 3079.304 ms !H 3079.299 ms !H
|
||||
"""
|
||||
|
||||
self.assertEqual(TraceResult(ips=[
|
||||
'192.168.123.1'], latency="3079.329 ms !H"), parse_traceroute(s))
|
||||
self.assertEqual(TraceResult(hops=[
|
||||
_bareip('192.168.123.1')], latency="3079.329 ms !H"), parse_traceroute(s))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -65,6 +65,7 @@ class BirdLGGo(callbacks.Plugin):
|
||||
else:
|
||||
return resp["result"]
|
||||
|
||||
|
||||
@wrap(['something'])
|
||||
def traceroute(self, irc, msg, args, target):
|
||||
"""<target>
|
||||
@ -78,18 +79,28 @@ class BirdLGGo(callbacks.Plugin):
|
||||
"args": target
|
||||
}
|
||||
results = self.lg_post_request(irc, msg, query)
|
||||
hop_display_mode = self.registryValue("traceHopFormat", network=irc.network, channel=msg.channel)
|
||||
|
||||
def _format_tracehop(hop):
|
||||
if hop_display_mode == 'ip' or hop.ip == hop.ptr:
|
||||
return hop.ip
|
||||
elif hop_display_mode == 'ptr':
|
||||
return hop.ptr
|
||||
elif hop_display_mode == 'both':
|
||||
return f'{hop.ptr}[{hop.ip}]'
|
||||
irc.error("Unknown traceHopFormat setting %r" % hop_display_mode)
|
||||
|
||||
for result in results:
|
||||
parsed_result = parsetrace.parse_traceroute(result["data"])
|
||||
server = result["server"]
|
||||
|
||||
ips = " ".join(parsed_result.ips)
|
||||
hops = " ".join(map(_format_tracehop, parsed_result.hops))
|
||||
latency = parsed_result.latency or "(timed out)"
|
||||
notes = ""
|
||||
if parsed_result.notes:
|
||||
notes = "- " + ", ".join(parsed_result.notes)
|
||||
|
||||
irc.reply(f"{server} -> {target}: {latency} | {ips} {notes}")
|
||||
irc.reply(f"{server} -> {target}: {latency} | {hops} {notes}")
|
||||
|
||||
@wrap(['something'])
|
||||
def showroute(self, irc, msg, args, target):
|
||||
|
Loading…
x
Reference in New Issue
Block a user