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.
Files changed (32) hide show
  1. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/PKG-INFO +1 -1
  2. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/pyproject.toml +1 -1
  3. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/formatters.py +0 -2
  4. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/ieee/catalog.py +2 -2
  5. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/ieee/ieee_registry.py +4 -4
  6. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/ieee/index.py +1 -1
  7. etherlyzer-0.9.2/src/etherlyzer/terminal/cli.py +78 -0
  8. etherlyzer-0.9.2/src/etherlyzer/terminal/commands/__init__.py +8 -0
  9. etherlyzer-0.9.2/src/etherlyzer/terminal/commands/format.py +96 -0
  10. etherlyzer-0.5.4/src/etherlyzer/terminal/commands/identify.py → etherlyzer-0.9.2/src/etherlyzer/terminal/commands/inspect.py +36 -12
  11. etherlyzer-0.9.2/src/etherlyzer/terminal/commands/registries.py +55 -0
  12. etherlyzer-0.9.2/src/etherlyzer/terminal/commands/sync.py +35 -0
  13. etherlyzer-0.5.4/src/etherlyzer/terminal/commands/normalize.py → etherlyzer-0.9.2/src/etherlyzer/terminal/commands/validate.py +33 -16
  14. etherlyzer-0.9.2/src/etherlyzer/terminal/commands/whois.py +91 -0
  15. etherlyzer-0.5.4/src/etherlyzer/terminal/cli.py +0 -61
  16. etherlyzer-0.5.4/src/etherlyzer/terminal/commands/__init__.py +0 -8
  17. etherlyzer-0.5.4/src/etherlyzer/terminal/commands/bulk.py +0 -56
  18. etherlyzer-0.5.4/src/etherlyzer/terminal/commands/format.py +0 -42
  19. etherlyzer-0.5.4/src/etherlyzer/terminal/commands/info.py +0 -43
  20. etherlyzer-0.5.4/src/etherlyzer/terminal/commands/update.py +0 -25
  21. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/LICENSE +0 -0
  22. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/README.md +0 -0
  23. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/__init__.py +0 -0
  24. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/etherlyzer.env +0 -0
  25. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/exporter.py +0 -0
  26. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/ieee/__init__.py +0 -0
  27. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/ieee/csv_parser.py +0 -0
  28. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/terminal/__init__.py +0 -0
  29. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/terminal/utility.py +0 -0
  30. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/util/__init__.py +0 -0
  31. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/util/dirs.py +0 -0
  32. {etherlyzer-0.5.4 → etherlyzer-0.9.2}/src/etherlyzer/util/knobs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: etherlyzer
3
- Version: 0.5.4
3
+ Version: 0.9.2
4
4
  Summary: Fast offline lookup and classification of Ethernet-related identifiers.
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "etherlyzer"
3
- version = "0.5.4"
3
+ version = "0.9.2"
4
4
  description = "Fast offline lookup and classification of Ethernet-related identifiers."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -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
- print(f"Cache for {self.name} expires at: {expires_humanized} UTC")
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,8 @@
1
+ from . import (
2
+ whois,
3
+ format,
4
+ registries,
5
+ validate,
6
+ inspect,
7
+ sync,
8
+ )
@@ -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.mac)
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.mac)
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.mac)
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 entry in other_bloks:
66
- if entry.registry == "MA-L":
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 entry.registry == "MA-M":
70
- total_addresses += 20 ** 2
71
- total_addresses_mam += 20 ** 2
72
- elif entry.registry == "MA-S":
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 entry.registry == "IAB":
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("identify", help="Search vendors, protocols or assignments")
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
- parser.add_argument("mac", help="Search string", type=str)
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 = 1
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
- "validize",
53
- help="Validates and normalizes MAC addresses (this command does not perform any sort of typo-correction - use `format` for that purpose).",
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.add_argument(
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
- parser.add_argument(
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="Show linenumbers corresponding to each MAC address (kind of only useful when validizing in bulk)",
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="Only return MAC's that can be normalized without errors.",
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="Displays the original MAC addresses after the normalized MAC address on each line.",
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="Displays an OK or ERR before each line depending on whether the outout succeeded normalization.",
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,8 +0,0 @@
1
- from . import (
2
- bulk,
3
- format,
4
- info,
5
- normalize,
6
- identify,
7
- update,
8
- )
@@ -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