etherlyzer 0.5.4__tar.gz → 0.9.2__tar.gz
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.
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/PKG-INFO +1 -1
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/pyproject.toml +1 -1
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/formatters.py +0 -2
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/ieee/catalog.py +2 -2
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/ieee/ieee_registry.py +4 -4
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/ieee/index.py +1 -1
- etherlyzer-0.9.2/src/etherlyzer/terminal/cli.py +78 -0
- etherlyzer-0.9.2/src/etherlyzer/terminal/commands/__init__.py +8 -0
- etherlyzer-0.9.2/src/etherlyzer/terminal/commands/format.py +96 -0
- etherlyzer-0.5.4/src/etherlyzer/terminal/commands/identify.py → etherlyzer-0.9.2/src/etherlyzer/terminal/commands/inspect.py +36 -12
- etherlyzer-0.9.2/src/etherlyzer/terminal/commands/registries.py +55 -0
- etherlyzer-0.9.2/src/etherlyzer/terminal/commands/sync.py +35 -0
- etherlyzer-0.5.4/src/etherlyzer/terminal/commands/normalize.py → etherlyzer-0.9.2/src/etherlyzer/terminal/commands/validate.py +33 -16
- etherlyzer-0.9.2/src/etherlyzer/terminal/commands/whois.py +91 -0
- etherlyzer-0.5.4/src/etherlyzer/terminal/cli.py +0 -61
- etherlyzer-0.5.4/src/etherlyzer/terminal/commands/__init__.py +0 -8
- etherlyzer-0.5.4/src/etherlyzer/terminal/commands/bulk.py +0 -56
- etherlyzer-0.5.4/src/etherlyzer/terminal/commands/format.py +0 -42
- etherlyzer-0.5.4/src/etherlyzer/terminal/commands/info.py +0 -43
- etherlyzer-0.5.4/src/etherlyzer/terminal/commands/update.py +0 -25
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/LICENSE +0 -0
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/README.md +0 -0
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/__init__.py +0 -0
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/etherlyzer.env +0 -0
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/exporter.py +0 -0
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/ieee/__init__.py +0 -0
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/ieee/csv_parser.py +0 -0
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/terminal/__init__.py +0 -0
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/terminal/utility.py +0 -0
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/util/__init__.py +0 -0
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/util/dirs.py +0 -0
- {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/util/knobs.py +0 -0
|
@@ -66,8 +66,6 @@ class MACFormatter:
|
|
|
66
66
|
if fix_typos:
|
|
67
67
|
mac = cls.fix_typos(mac)
|
|
68
68
|
mac = cls.normalize(mac)
|
|
69
|
-
if len(mac) > 12:
|
|
70
|
-
raise ValueError(f"Malformed MAC address is of length: {len(mac)}!")
|
|
71
69
|
mac = separator.join(mac[i: i + block_size] for i in range(0, len(mac), block_size))
|
|
72
70
|
if case == "upper":
|
|
73
71
|
return mac.upper()
|
|
@@ -116,10 +116,10 @@ class Catalog:
|
|
|
116
116
|
def get_registry(registry_name: str) -> IEEERegistry | None:
|
|
117
117
|
return Catalog.IEEE_REGISTRIES.get(registry_name, None)
|
|
118
118
|
|
|
119
|
-
def get_all_registries(self, check_for_updates=True) -> list[IEEERegistry]:
|
|
119
|
+
def get_all_registries(self, check_for_updates=True,inform_if_synced_about_expiration=False) -> list[IEEERegistry]:
|
|
120
120
|
for registry in self.IEEE_REGISTRIES.values():
|
|
121
121
|
# If not checking for updates, we still need a present dataset.
|
|
122
|
-
if (check_for_updates and registry.need_updates()) or not self.db_is_initialized():
|
|
122
|
+
if (check_for_updates and registry.need_updates(inform_if_synced_about_expiration)) or not self.db_is_initialized():
|
|
123
123
|
|
|
124
124
|
status = registry.save()
|
|
125
125
|
if status is False:
|
|
@@ -68,7 +68,6 @@ class IEEERegistry:
|
|
|
68
68
|
address_bits: int | None = None
|
|
69
69
|
address_count: int | None = None
|
|
70
70
|
|
|
71
|
-
|
|
72
71
|
update_interval: datetime.timedelta = datetime.timedelta(days=1)
|
|
73
72
|
last_retrieved: datetime.datetime = None # noqa; set in post-init
|
|
74
73
|
|
|
@@ -117,7 +116,7 @@ class IEEERegistry:
|
|
|
117
116
|
self.save()
|
|
118
117
|
return IEEERegistryReader.load(self.filepath, self.model)
|
|
119
118
|
|
|
120
|
-
def need_updates(self):
|
|
119
|
+
def need_updates(self, inform_if_synced_about_expiration=False):
|
|
121
120
|
expires = self.last_retrieved + self.update_interval
|
|
122
121
|
expires_humanized = expires.replace(microsecond=0)
|
|
123
122
|
now = datetime.datetime.now(datetime.UTC)
|
|
@@ -127,8 +126,9 @@ class IEEERegistry:
|
|
|
127
126
|
print(f"Cache for {self.name} not found.")
|
|
128
127
|
elif expires <= now and etherlyzer_knobs.show_sync_messages:
|
|
129
128
|
print(f"Cache for {self.name} has expired.")
|
|
130
|
-
elif etherlyzer_knobs.show_sync_messages:
|
|
131
|
-
|
|
129
|
+
elif etherlyzer_knobs.show_sync_messages and inform_if_synced_about_expiration:
|
|
130
|
+
namelen = len("EtherType") - len(self.name)
|
|
131
|
+
print(f"Cache for {self.name} expires at: {' '*namelen} {expires_humanized} UTC")
|
|
132
132
|
|
|
133
133
|
return f_not_exists or expires <= now
|
|
134
134
|
|
|
@@ -191,7 +191,7 @@ class IEEEIndex:
|
|
|
191
191
|
return ieee_entries
|
|
192
192
|
|
|
193
193
|
@staticmethod
|
|
194
|
-
def get_single(indextype: VendorIndex | MACIndex | IdentifierIndex | ProtocolIndex, mac: str):
|
|
194
|
+
def get_single(indextype: VendorIndex | MACIndex | IdentifierIndex | ProtocolIndex, mac: str) -> IEEEEntry | None:
|
|
195
195
|
ieee_entry = indextype.lookup(mac)
|
|
196
196
|
if ieee_entry:
|
|
197
197
|
return ieee_entry
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from etherlyzer import __version__
|
|
6
|
+
from etherlyzer.terminal.commands import (
|
|
7
|
+
format,
|
|
8
|
+
inspect,
|
|
9
|
+
registries,
|
|
10
|
+
sync,
|
|
11
|
+
validate,
|
|
12
|
+
whois,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
prog="etherlyzer",
|
|
19
|
+
description=(
|
|
20
|
+
"EtherLyzer - Ethernet identifier lookup, validation, formatting, "
|
|
21
|
+
"and IEEE registry analysis toolkit."
|
|
22
|
+
),
|
|
23
|
+
epilog=r"""
|
|
24
|
+
Examples:
|
|
25
|
+
etherlyzer whois 00:11:22:33:44:55
|
|
26
|
+
etherlyzer inspect 00:11:22:33:44:55
|
|
27
|
+
etherlyzer validate 00-11-22-33-44-55
|
|
28
|
+
etherlyzer format 001122334455
|
|
29
|
+
etherlyzer registries
|
|
30
|
+
etherlyzer sync
|
|
31
|
+
|
|
32
|
+
Run `etherlyzer <command> --help` for command-specific options and examples.
|
|
33
|
+
""",
|
|
34
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
parser.add_argument(
|
|
38
|
+
"-v",
|
|
39
|
+
"--version",
|
|
40
|
+
action="version",
|
|
41
|
+
version=f"EtherLyzer {__version__}",
|
|
42
|
+
help="Show the installed EtherLyzer version and exit.",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
subparsers = parser.add_subparsers(
|
|
46
|
+
title="Commands",
|
|
47
|
+
metavar="COMMAND",
|
|
48
|
+
required=True,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
whois.register(subparsers)
|
|
52
|
+
validate.register(subparsers)
|
|
53
|
+
format.register(subparsers)
|
|
54
|
+
inspect.register(subparsers)
|
|
55
|
+
registries.register(subparsers)
|
|
56
|
+
sync.register(subparsers)
|
|
57
|
+
|
|
58
|
+
return parser
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def main(argv: list[str] | None = None) -> int:
|
|
62
|
+
parser = build_parser()
|
|
63
|
+
args = parser.parse_args(argv)
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
return args.func(args)
|
|
67
|
+
|
|
68
|
+
except KeyboardInterrupt:
|
|
69
|
+
print("\nInterrupted.")
|
|
70
|
+
return 130
|
|
71
|
+
|
|
72
|
+
except Exception as exc:
|
|
73
|
+
print(f"Error: {exc}")
|
|
74
|
+
return 1
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from etherlyzer.formatters import MACFormatter
|
|
6
|
+
from etherlyzer.terminal.utility import read_multiline
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def run(args) -> int:
|
|
10
|
+
if args.bulk:
|
|
11
|
+
macs = read_multiline(linetype="MAC addresses")
|
|
12
|
+
else:
|
|
13
|
+
macs = [args.mac]
|
|
14
|
+
for mac in macs:
|
|
15
|
+
print(
|
|
16
|
+
MACFormatter.format_custom(
|
|
17
|
+
mac=mac,
|
|
18
|
+
separator=args.separator,
|
|
19
|
+
block_size=args.block_size,
|
|
20
|
+
case=args.casing,
|
|
21
|
+
fix_typos=args.fix_typos,
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
return 0
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def register(subparsers):
|
|
29
|
+
parser = subparsers.add_parser(
|
|
30
|
+
"format",
|
|
31
|
+
help="Correct and normalize MAC address formatting.",
|
|
32
|
+
description=(
|
|
33
|
+
"Format MAC addresses using a selected separator, block size, and casing. "
|
|
34
|
+
"Optional typo correction can repair supported character substitutions. "
|
|
35
|
+
"This command formats input without performing MAC address validation."
|
|
36
|
+
),
|
|
37
|
+
epilog=r"""
|
|
38
|
+
Examples:
|
|
39
|
+
etherlyzer format 001122334455
|
|
40
|
+
etherlyzer format 00-11-22-33-44-55 -p :
|
|
41
|
+
etherlyzer format 001122334455 -p . -s 4
|
|
42
|
+
etherlyzer format 00:11:22:33:44:55 -c upper
|
|
43
|
+
etherlyzer format O0:1i:22:33:44:s5 --fix-typos
|
|
44
|
+
etherlyzer format -b -p . -s 4 --fix-typos
|
|
45
|
+
""",
|
|
46
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
source = parser.add_mutually_exclusive_group(required=True)
|
|
50
|
+
|
|
51
|
+
source.add_argument(
|
|
52
|
+
"mac",
|
|
53
|
+
nargs="?",
|
|
54
|
+
help="MAC address to format.",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
source.add_argument(
|
|
58
|
+
"-b",
|
|
59
|
+
"--bulk",
|
|
60
|
+
action="store_true",
|
|
61
|
+
help="Read multiple MAC addresses interactively.",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"-p",
|
|
66
|
+
"--separator",
|
|
67
|
+
type=str,
|
|
68
|
+
default=":",
|
|
69
|
+
help="Output separator: ':', '-', '.', or empty (default: ':').",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
parser.add_argument(
|
|
73
|
+
"-s",
|
|
74
|
+
"--block-size",
|
|
75
|
+
type=int,
|
|
76
|
+
default=2,
|
|
77
|
+
help="Number of hexadecimal characters per block (default: 2).",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
parser.add_argument(
|
|
81
|
+
"-c",
|
|
82
|
+
"--casing",
|
|
83
|
+
type=str,
|
|
84
|
+
choices=["lower", "upper"],
|
|
85
|
+
default="lower",
|
|
86
|
+
help="Output hexadecimal casing (default: lower).",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
parser.add_argument(
|
|
90
|
+
"-f",
|
|
91
|
+
"--fix-typos",
|
|
92
|
+
action="store_true",
|
|
93
|
+
help="Correct supported typo-like character substitutions before formatting.",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
parser.set_defaults(func=run)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import argparse
|
|
1
2
|
import re
|
|
2
3
|
|
|
3
4
|
from etherlyzer.formatters import MACFormatter
|
|
@@ -10,11 +11,11 @@ def run(args):
|
|
|
10
11
|
registry = Catalog()
|
|
11
12
|
index = IEEEIndex.from_registries(registry.get_all_registries(check_for_updates=False))
|
|
12
13
|
if args.type.lower() == RegCategory.MAC.value:
|
|
13
|
-
entry = index.get_single(index.mac_index, args.
|
|
14
|
+
entry = index.get_single(index.mac_index, args.value)
|
|
14
15
|
elif args.type.lower() == RegCategory.PROTOCOL.value:
|
|
15
|
-
entry = index.get_single(index.protocol_index, args.
|
|
16
|
+
entry = index.get_single(index.protocol_index, args.value)
|
|
16
17
|
elif args.type.lower() == RegCategory.IDENTIFIER.value:
|
|
17
|
-
entry = index.get_single(index.identifier_index, args.
|
|
18
|
+
entry = index.get_single(index.identifier_index, args.value)
|
|
18
19
|
else:
|
|
19
20
|
print(f"No valid type ({args.type}) selected.")
|
|
20
21
|
return 1
|
|
@@ -62,17 +63,17 @@ def run(args):
|
|
|
62
63
|
total_addresses_mam = 0
|
|
63
64
|
total_addresses_mas = 0
|
|
64
65
|
total_addresses_iab = 0
|
|
65
|
-
for
|
|
66
|
-
if
|
|
66
|
+
for ent in other_bloks:
|
|
67
|
+
if ent.registry == "MA-L":
|
|
67
68
|
total_addresses += 2 ** 24
|
|
68
69
|
total_addresses_mal += 2 ** 24
|
|
69
|
-
elif
|
|
70
|
-
total_addresses +=
|
|
71
|
-
total_addresses_mam +=
|
|
72
|
-
elif
|
|
70
|
+
elif ent.registry == "MA-M":
|
|
71
|
+
total_addresses += 2 ** 20
|
|
72
|
+
total_addresses_mam += 2 ** 20
|
|
73
|
+
elif ent.registry == "MA-S":
|
|
73
74
|
total_addresses += 2 ** 12
|
|
74
75
|
total_addresses_mas += 2 ** 12
|
|
75
|
-
elif
|
|
76
|
+
elif ent.registry == "IAB":
|
|
76
77
|
total_addresses += 2 ** 12
|
|
77
78
|
total_addresses_iab += 2 ** 12
|
|
78
79
|
print(f"")
|
|
@@ -102,7 +103,23 @@ def run(args):
|
|
|
102
103
|
|
|
103
104
|
|
|
104
105
|
def register(subparsers):
|
|
105
|
-
parser = subparsers.add_parser(
|
|
106
|
+
parser = subparsers.add_parser(
|
|
107
|
+
"inspect",
|
|
108
|
+
help="Inspect an IEEE assignment and display registry, organization, range, and allocation details.",
|
|
109
|
+
description=(
|
|
110
|
+
"Inspect a single IEEE assignment and display detailed registry metadata, "
|
|
111
|
+
"organization information, address range, and related vendor allocations."
|
|
112
|
+
),
|
|
113
|
+
epilog=r"""
|
|
114
|
+
Examples:
|
|
115
|
+
etherlyzer inspect 00:11:22:33:44:55
|
|
116
|
+
etherlyzer inspect -t mac 00:11:22:33:44:55
|
|
117
|
+
etherlyzer inspect -t protocol 0800
|
|
118
|
+
etherlyzer inspect -t identifier <identifier>
|
|
119
|
+
""",
|
|
120
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
121
|
+
)
|
|
122
|
+
|
|
106
123
|
parser.add_argument(
|
|
107
124
|
"-t",
|
|
108
125
|
"--type",
|
|
@@ -110,6 +127,13 @@ def register(subparsers):
|
|
|
110
127
|
choices=list(RegCategory),
|
|
111
128
|
default="mac",
|
|
112
129
|
metavar="{mac|protocol|identifier}",
|
|
130
|
+
help="IEEE registry category to search (default: mac).",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
parser.add_argument(
|
|
134
|
+
"value",
|
|
135
|
+
type=str,
|
|
136
|
+
help="IEEE assignment or identifier to inspect.",
|
|
113
137
|
)
|
|
114
|
-
|
|
138
|
+
|
|
115
139
|
parser.set_defaults(func=run)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from etherlyzer.ieee.catalog import Catalog
|
|
6
|
+
from etherlyzer import __version__
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def run(args):
|
|
10
|
+
manager = Catalog()
|
|
11
|
+
|
|
12
|
+
print()
|
|
13
|
+
|
|
14
|
+
print(f"EtherLyzer {__version__}")
|
|
15
|
+
print()
|
|
16
|
+
|
|
17
|
+
print("Registries")
|
|
18
|
+
|
|
19
|
+
for registry in manager.get_all_registries(check_for_updates=False):
|
|
20
|
+
print(
|
|
21
|
+
f"Name : {registry.name} ({registry.full_name})"
|
|
22
|
+
f"\nLegacy Name : {registry.legacy_name}"
|
|
23
|
+
f"\nCategory : {registry.category}"
|
|
24
|
+
f"\nDescription : {registry.description}"
|
|
25
|
+
f"\nLegacy : {registry.legacy}"
|
|
26
|
+
f"\nFile : {registry.filepath}"
|
|
27
|
+
f"\nSource URL : {registry.url}"
|
|
28
|
+
f"\nPrefix Bits : {registry.prefix_bits}"
|
|
29
|
+
f"\nLast Retrieved : {registry.last_retrieved}"
|
|
30
|
+
f"\nUpdate Interval : {registry.update_interval}"
|
|
31
|
+
)
|
|
32
|
+
print()
|
|
33
|
+
|
|
34
|
+
print()
|
|
35
|
+
|
|
36
|
+
return 0
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def register(subparsers):
|
|
40
|
+
parser = subparsers.add_parser(
|
|
41
|
+
"registries",
|
|
42
|
+
help="Show local IEEE registry metadata and synchronization status.",
|
|
43
|
+
description=(
|
|
44
|
+
"Display metadata for the locally configured IEEE registries, "
|
|
45
|
+
"including category, source, prefix size, retrieval time, and "
|
|
46
|
+
"update interval."
|
|
47
|
+
),
|
|
48
|
+
epilog="""
|
|
49
|
+
Example:
|
|
50
|
+
etherlyzer registries
|
|
51
|
+
""",
|
|
52
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
parser.set_defaults(func=run)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from etherlyzer.ieee.catalog import Catalog
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def run(args):
|
|
10
|
+
manager = Catalog()
|
|
11
|
+
start = time.perf_counter()
|
|
12
|
+
print("Synchronizing IEEE registries...\n")
|
|
13
|
+
manager.get_all_registries(check_for_updates=True,inform_if_synced_about_expiration=True)
|
|
14
|
+
elapsed = time.perf_counter() - start
|
|
15
|
+
print()
|
|
16
|
+
print(f"Synchronization completed in {elapsed:.2f} seconds.")
|
|
17
|
+
return 0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def register(subparsers):
|
|
21
|
+
parser = subparsers.add_parser(
|
|
22
|
+
"sync",
|
|
23
|
+
help="Synchronize the local IEEE registry database with upstream sources.",
|
|
24
|
+
description=(
|
|
25
|
+
"Check upstream IEEE registry sources and refresh the local "
|
|
26
|
+
"database when updates are available."
|
|
27
|
+
),
|
|
28
|
+
epilog="""
|
|
29
|
+
Example:
|
|
30
|
+
etherlyzer sync
|
|
31
|
+
""",
|
|
32
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
parser.set_defaults(func=run)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
1
3
|
from etherlyzer.terminal.utility import read_multiline
|
|
2
4
|
from etherlyzer.formatters import MACFormatter
|
|
3
5
|
|
|
@@ -6,9 +8,9 @@ def run(args):
|
|
|
6
8
|
if args.bulk:
|
|
7
9
|
macs = read_multiline(linetype="MAC addresses")
|
|
8
10
|
else:
|
|
9
|
-
macs = args.mac
|
|
11
|
+
macs = [args.mac]
|
|
10
12
|
|
|
11
|
-
linenumber =
|
|
13
|
+
linenumber = 0
|
|
12
14
|
padding = len(str(len(macs))) + 2
|
|
13
15
|
normalized_count = 0
|
|
14
16
|
invalid_count = 0
|
|
@@ -47,53 +49,68 @@ def run(args):
|
|
|
47
49
|
return 0
|
|
48
50
|
|
|
49
51
|
|
|
52
|
+
|
|
50
53
|
def register(subparsers):
|
|
51
54
|
parser = subparsers.add_parser(
|
|
52
|
-
"
|
|
53
|
-
help="
|
|
55
|
+
"validate",
|
|
56
|
+
help="Validate and normalize MAC addresses with optional diagnostics and filtering.",
|
|
57
|
+
description=(
|
|
58
|
+
"Validate and normalize MAC addresses without typo correction. "
|
|
59
|
+
"Malformed input is reported rather than corrected; use `format` "
|
|
60
|
+
"for corrective formatting."
|
|
61
|
+
),
|
|
62
|
+
epilog=r"""
|
|
63
|
+
Examples:
|
|
64
|
+
etherlyzer validate 00:11:22:33:44:55
|
|
65
|
+
etherlyzer validate -b
|
|
66
|
+
etherlyzer validate -b --strict
|
|
67
|
+
etherlyzer validate -b --linenumbers --show-info
|
|
68
|
+
etherlyzer validate -b --show-originals
|
|
69
|
+
""",
|
|
70
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
54
71
|
)
|
|
55
72
|
|
|
56
|
-
parser.
|
|
73
|
+
source = parser.add_mutually_exclusive_group(required=True)
|
|
74
|
+
|
|
75
|
+
source.add_argument(
|
|
57
76
|
"mac",
|
|
58
77
|
nargs="?",
|
|
59
|
-
help="MAC address to normalize",
|
|
78
|
+
help="MAC address to validate and normalize.",
|
|
60
79
|
)
|
|
61
80
|
|
|
62
|
-
|
|
81
|
+
source.add_argument(
|
|
63
82
|
"-b",
|
|
64
83
|
"--bulk",
|
|
65
84
|
action="store_true",
|
|
66
|
-
help="Read multiple MAC addresses interactively",
|
|
85
|
+
help="Read multiple MAC addresses interactively.",
|
|
67
86
|
)
|
|
87
|
+
|
|
68
88
|
parser.add_argument(
|
|
69
89
|
"-l",
|
|
70
90
|
"--linenumbers",
|
|
71
91
|
action="store_true",
|
|
72
|
-
help="
|
|
73
|
-
dest="linenumbers"
|
|
92
|
+
help="Prefix output lines with their corresponding input line number.",
|
|
74
93
|
)
|
|
75
94
|
|
|
76
95
|
parser.add_argument(
|
|
77
96
|
"-s",
|
|
78
97
|
"--strict",
|
|
79
|
-
dest="strict",
|
|
80
98
|
action="store_true",
|
|
81
|
-
help="
|
|
99
|
+
help="Suppress invalid entries and output only successfully normalized MAC addresses.",
|
|
82
100
|
)
|
|
83
101
|
|
|
84
102
|
parser.add_argument(
|
|
85
103
|
"-o",
|
|
86
104
|
"--show-originals",
|
|
87
|
-
dest="show_originals",
|
|
88
105
|
action="store_true",
|
|
89
|
-
help="
|
|
106
|
+
help="Show the original input alongside each normalized MAC address.",
|
|
90
107
|
)
|
|
108
|
+
|
|
91
109
|
parser.add_argument(
|
|
92
110
|
"-i",
|
|
93
111
|
"--show-info",
|
|
94
|
-
dest="show_info",
|
|
95
112
|
action="store_true",
|
|
96
|
-
help="
|
|
113
|
+
help="Prefix each result with OK or ERR to indicate validation status.",
|
|
97
114
|
)
|
|
98
115
|
|
|
99
116
|
parser.set_defaults(
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from etherlyzer.ieee.index import IEEEIndex
|
|
7
|
+
from etherlyzer.ieee.catalog import Catalog
|
|
8
|
+
from etherlyzer.terminal.utility import read_multiline
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run(args):
|
|
12
|
+
index = IEEEIndex.from_registries(
|
|
13
|
+
Catalog().get_all_registries()
|
|
14
|
+
)
|
|
15
|
+
if args.mac:
|
|
16
|
+
entry = index.get_single(indextype=index.mac_index, mac=args.mac)
|
|
17
|
+
if entry is None:
|
|
18
|
+
print("No entry found.")
|
|
19
|
+
return 1
|
|
20
|
+
print(
|
|
21
|
+
entry.registry,
|
|
22
|
+
entry.assignment,
|
|
23
|
+
entry.organization_name,
|
|
24
|
+
sep="\t",
|
|
25
|
+
)
|
|
26
|
+
return 0
|
|
27
|
+
|
|
28
|
+
elif args.interactive:
|
|
29
|
+
data = read_multiline()
|
|
30
|
+
entries = IEEEIndex.get_bulk(
|
|
31
|
+
ieee_index=index.mac_index,
|
|
32
|
+
path_or_text=data,
|
|
33
|
+
)
|
|
34
|
+
elif args.file:
|
|
35
|
+
entries = IEEEIndex.get_bulk(
|
|
36
|
+
ieee_index=index.mac_index,
|
|
37
|
+
path_or_text=args.file,
|
|
38
|
+
)
|
|
39
|
+
else:
|
|
40
|
+
pass
|
|
41
|
+
return 1
|
|
42
|
+
if entries:
|
|
43
|
+
for entry in entries:
|
|
44
|
+
print(
|
|
45
|
+
entry.registry,
|
|
46
|
+
entry.assignment,
|
|
47
|
+
entry.organization_name,
|
|
48
|
+
sep="\t",
|
|
49
|
+
)
|
|
50
|
+
else:
|
|
51
|
+
print("No entries found.")
|
|
52
|
+
|
|
53
|
+
return 0
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def register(subparsers):
|
|
57
|
+
parser = subparsers.add_parser(
|
|
58
|
+
"whois",
|
|
59
|
+
help="Identify IEEE vendors and organizations for MAC addresses.",
|
|
60
|
+
description="Identify IEEE vendors and organizations for one or more MAC addresses.",
|
|
61
|
+
epilog="""
|
|
62
|
+
Examples:
|
|
63
|
+
etherlyzer whois 00:11:22:33:44:55
|
|
64
|
+
etherlyzer whois -f .\\macs.txt
|
|
65
|
+
etherlyzer whois -i
|
|
66
|
+
""",
|
|
67
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
68
|
+
)
|
|
69
|
+
source = parser.add_mutually_exclusive_group(required=True)
|
|
70
|
+
|
|
71
|
+
source.add_argument(
|
|
72
|
+
"mac",
|
|
73
|
+
nargs="?",
|
|
74
|
+
help="MAC address to identify",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
source.add_argument(
|
|
78
|
+
"-f",
|
|
79
|
+
"--file",
|
|
80
|
+
type=Path,
|
|
81
|
+
help="Read MAC addresses from a file",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
source.add_argument(
|
|
85
|
+
"-i",
|
|
86
|
+
"--interactive",
|
|
87
|
+
action="store_true",
|
|
88
|
+
help="Read multiple MAC addresses interactively",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
parser.set_defaults(func=run)
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import argparse
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
from etherlyzer.terminal.commands import info, format, normalize, identify, update, bulk
|
|
7
|
-
from etherlyzer.util.dirs import DATA_PLATFORM_DIR
|
|
8
|
-
from etherlyzer.util.knobs import etherlyzer_knobs
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def build_parser() -> argparse.ArgumentParser:
|
|
12
|
-
parser = argparse.ArgumentParser(
|
|
13
|
-
prog="etherlyzer",
|
|
14
|
-
description="EtherLyzer - Ethernet lookup and analysis toolkit",
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
parser.add_argument(
|
|
18
|
-
"-v",
|
|
19
|
-
"--version",
|
|
20
|
-
action="version",
|
|
21
|
-
version="EtherLyzer 0.1.0",
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
subparsers = parser.add_subparsers(
|
|
25
|
-
title="Commands",
|
|
26
|
-
required=True,
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
bulk.register(subparsers)
|
|
30
|
-
normalize.register(subparsers)
|
|
31
|
-
format.register(subparsers)
|
|
32
|
-
update.register(subparsers)
|
|
33
|
-
info.register(subparsers)
|
|
34
|
-
identify.register(subparsers)
|
|
35
|
-
|
|
36
|
-
return parser
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def main(argv: list[str] | None = None) -> int:
|
|
40
|
-
parser = build_parser()
|
|
41
|
-
|
|
42
|
-
args = parser.parse_args(argv)
|
|
43
|
-
|
|
44
|
-
if not hasattr(args, "func"):
|
|
45
|
-
parser.print_help()
|
|
46
|
-
return 1
|
|
47
|
-
|
|
48
|
-
try:
|
|
49
|
-
return args.func(args)
|
|
50
|
-
|
|
51
|
-
except KeyboardInterrupt:
|
|
52
|
-
print("Interrupted.")
|
|
53
|
-
return 130
|
|
54
|
-
|
|
55
|
-
except Exception as exc:
|
|
56
|
-
print(f"Error: {exc}")
|
|
57
|
-
return 1
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if __name__ == "__main__":
|
|
61
|
-
raise SystemExit(main())
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
from etherlyzer.ieee.index import IEEEIndex
|
|
6
|
-
from etherlyzer.ieee.catalog import Catalog
|
|
7
|
-
from etherlyzer.terminal.utility import read_multiline
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def run(args):
|
|
11
|
-
index = IEEEIndex.from_registries(
|
|
12
|
-
Catalog().get_all_registries()
|
|
13
|
-
)
|
|
14
|
-
if args.interactive:
|
|
15
|
-
data = read_multiline()
|
|
16
|
-
entries = (IEEEIndex.get_bulk(
|
|
17
|
-
ieee_index=index.mac_index,
|
|
18
|
-
path_or_text=data,
|
|
19
|
-
))
|
|
20
|
-
else: # elif args.file:
|
|
21
|
-
entries = (IEEEIndex.get_bulk(
|
|
22
|
-
ieee_index=index.mac_index,
|
|
23
|
-
path_or_text=Path(args.file),
|
|
24
|
-
))
|
|
25
|
-
|
|
26
|
-
for entry in entries:
|
|
27
|
-
print(
|
|
28
|
-
entry.registry,
|
|
29
|
-
entry.assignment,
|
|
30
|
-
entry.organization_name,
|
|
31
|
-
sep="\t",
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
return 0
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def register(subparsers):
|
|
38
|
-
parser = subparsers.add_parser(
|
|
39
|
-
"bulk",
|
|
40
|
-
help="Bulk lookup",
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
parser.add_argument(
|
|
44
|
-
"file",
|
|
45
|
-
nargs="?",
|
|
46
|
-
help="Input file",
|
|
47
|
-
)
|
|
48
|
-
parser.add_argument(
|
|
49
|
-
"-i",
|
|
50
|
-
"--interactive",
|
|
51
|
-
dest="interactive",
|
|
52
|
-
action="store_true",
|
|
53
|
-
help="Read multiple MAC addresses interactively",
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
parser.set_defaults(func=run)
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from etherlyzer.formatters import MACFormatter
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def run(args) -> int:
|
|
7
|
-
print(
|
|
8
|
-
MACFormatter.format_custom(
|
|
9
|
-
mac=args.mac,
|
|
10
|
-
separator=args.separator,
|
|
11
|
-
block_size=args.block_size,
|
|
12
|
-
case=args.casing,
|
|
13
|
-
fix_typos=args.fix_typos,
|
|
14
|
-
)
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
return 0
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def register(subparsers):
|
|
21
|
-
parser = subparsers.add_parser(
|
|
22
|
-
"format",
|
|
23
|
-
help="Format MAC address",
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
parser.add_argument("mac", nargs="?")
|
|
27
|
-
parser.add_argument("-p", "--separator", type=str, default=":", help="Separator for mac address")
|
|
28
|
-
parser.add_argument("-b", "--block-size", type=int, default=2, help="Block size for mac address")
|
|
29
|
-
parser.add_argument(
|
|
30
|
-
"-c",
|
|
31
|
-
"--casing", type=str, default="lower", help="Use upper case (lower is default)"
|
|
32
|
-
)
|
|
33
|
-
parser.add_argument(
|
|
34
|
-
"-f",
|
|
35
|
-
"--fix-typos",
|
|
36
|
-
action="store_true",
|
|
37
|
-
default=False,
|
|
38
|
-
dest="fix_typos",
|
|
39
|
-
help="Fix typo-candidates like o/O/ø/Ø instead of 0, 1 instead of i/I et cetera (see docs for specifics).",
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
parser.set_defaults(func=run)
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from etherlyzer.ieee.catalog import Catalog
|
|
4
|
-
from etherlyzer import __version__
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def run(args):
|
|
8
|
-
manager = Catalog()
|
|
9
|
-
|
|
10
|
-
print()
|
|
11
|
-
|
|
12
|
-
print(f"EtherLyzer {__version__}")
|
|
13
|
-
print()
|
|
14
|
-
|
|
15
|
-
print("Registries")
|
|
16
|
-
|
|
17
|
-
for registry in manager.get_all_registries(check_for_updates=False):
|
|
18
|
-
print(
|
|
19
|
-
f"IEEE Name: {registry.name} ({registry.full_name})"
|
|
20
|
-
f"\nLegacy Name: {registry.legacy_name}"
|
|
21
|
-
f"\nCategory: {registry.category}"
|
|
22
|
-
f"\nDescription: {registry.description}"
|
|
23
|
-
f"\nIs Legacy: {registry.legacy}"
|
|
24
|
-
f"\nFilepath: {registry.filepath}"
|
|
25
|
-
f"\nURL: {registry.url}"
|
|
26
|
-
f"\nPrefix bits: {registry.prefix_bits}"
|
|
27
|
-
f"\nLast retrieved: {registry.last_retrieved}"
|
|
28
|
-
f"\nUpdate interval: {registry.update_interval}"
|
|
29
|
-
)
|
|
30
|
-
print()
|
|
31
|
-
|
|
32
|
-
print()
|
|
33
|
-
|
|
34
|
-
return 0
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def register(subparsers):
|
|
38
|
-
parser = subparsers.add_parser(
|
|
39
|
-
"info",
|
|
40
|
-
help="Show application information",
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
parser.set_defaults(func=run)
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import time
|
|
4
|
-
|
|
5
|
-
from etherlyzer.ieee.catalog import Catalog
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def run(args):
|
|
9
|
-
manager = Catalog()
|
|
10
|
-
start = time.perf_counter()
|
|
11
|
-
print("Synchronizing IEEE registries...\n")
|
|
12
|
-
updated = manager.get_all_registries(check_for_updates=True)
|
|
13
|
-
elapsed = time.perf_counter() - start
|
|
14
|
-
print()
|
|
15
|
-
print(f"Finished in {elapsed:.2f} seconds.")
|
|
16
|
-
return 0
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def register(subparsers):
|
|
20
|
-
parser = subparsers.add_parser(
|
|
21
|
-
"update",
|
|
22
|
-
help="Synchronize IEEE registries",
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
parser.set_defaults(func=run)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|