Skip to content

Commit 30b5e6a

Browse files
committed
documentation and better variable names
1 parent 7c4a487 commit 30b5e6a

5 files changed

Lines changed: 74 additions & 49 deletions

File tree

nettacker/core/lib/socket.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,21 @@ def tcp_connect_send_and_receive(self, host, port, timeout):
9595
}
9696

9797
def tcp_and_udp_scan(self, host, port: int, timeout=5):
98+
"""
99+
Probe a port with the nmap-service-probes engine to fingerprint the running service.
100+
101+
Tries plain TCP first, falls back to TLS if the port rejects plaintext, then falls
102+
back to UDP. Returns a dict with "service", "ssl_flag", and "log" keys, or None if
103+
the port could not be identified as open.
104+
105+
Args:
106+
host: target host/IP
107+
port: target port
108+
timeout: per-probe timeout in seconds
109+
110+
Returns:
111+
A result dict, or None if the port appears closed/unresponsive.
112+
"""
98113
# `timeout` follows the same convention as every other method here
99114
# (tcp_connect_only, tcp_connect_send_and_receive, socket_icmp): it's
100115
# in seconds, as set by the module yaml (e.g. "timeout: 3"). The

nettacker/probing/engine.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
)
2727

2828

29-
def Interpret(value: bytes | str, endian: str) -> int:
29+
def interpret(value: bytes | str, endian: str) -> int:
3030
"""
3131
Interpret up to 8 bytes as unsigned integer
3232
endian: '>' = big-endian, '<' = little-endian
@@ -40,7 +40,7 @@ def Interpret(value: bytes | str, endian: str) -> int:
4040
return int.from_bytes(value, byteorder=byteorder, signed=False)
4141

4242

43-
def Printable(value: bytes | str) -> str:
43+
def printable(value: bytes | str) -> str:
4444
"""
4545
Make a string printable:
4646
- Remove NULLs
@@ -71,7 +71,7 @@ def apply_subst(match_obj, regex_match):
7171
return value.replace(old, new)
7272

7373

74-
def expand_SUBST(template: str, regex_match):
74+
def expand_subst(template: str, regex_match):
7575
while True:
7676
m = SUBST_RE.search(template)
7777
if not m:
@@ -82,24 +82,24 @@ def expand_SUBST(template: str, regex_match):
8282
return template
8383

8484

85-
def expand_I(template: str, match):
85+
def expand_i(template: str, match):
8686
def repl(m):
8787
idx = int(m.group(1))
8888
endian = m.group(2)
8989
try:
9090
captured = match.group(idx)
9191
except IndexError:
9292
return ""
93-
return str(Interpret(captured, endian))
93+
return str(interpret(captured, endian))
9494

9595
return I_RE.sub(repl, template)
9696

9797

98-
def expand_P(template: str, regex_match):
98+
def expand_p(template: str, regex_match):
9999
def repl(m):
100100
i = int(m.group(1))
101101
try:
102-
return Printable(regex_match.group(i))
102+
return printable(regex_match.group(i))
103103
except IndexError:
104104
return ""
105105

@@ -123,14 +123,16 @@ def repl(m):
123123

124124

125125
def expand_template(template: str, regex_match):
126-
template = expand_SUBST(template, regex_match)
127-
template = expand_P(template, regex_match)
128-
template = expand_I(template, regex_match)
126+
template = expand_subst(template, regex_match)
127+
template = expand_p(template, regex_match)
128+
template = expand_i(template, regex_match)
129129
template = expand_place(template, regex_match)
130130
return template
131131

132132

133-
class result:
133+
class Result:
134+
"""Version fields (template, product, CPE, ...) extracted from a matched signature."""
135+
134136
def __init__(
135137
self,
136138
version_template=None,
@@ -185,16 +187,16 @@ def get_probes_for_sslport(self):
185187
specific.append(p)
186188
return specific
187189

188-
def Match_response(self, response, signature):
190+
def match_response(self, response, signature):
189191
if response is None:
190-
return {"status": False, "result": result()}
192+
return {"status": False, "result": Result()}
191193

192194
if isinstance(response, str):
193195
response = response.encode("latin-1", errors="ignore")
194196
regex = signature.regex
195197
match = regex.search(response)
196198
if not match:
197-
return {"status": False, "result": result()}
199+
return {"status": False, "result": Result()}
198200

199201
version_ = signature.version_details
200202
version_template = product = info = hostname = None
@@ -220,7 +222,7 @@ def Match_response(self, response, signature):
220222

221223
return {
222224
"status": True,
223-
"result": result(
225+
"result": Result(
224226
version_template=version_template,
225227
product=product,
226228
info=info,
@@ -233,8 +235,8 @@ def Match_response(self, response, signature):
233235
),
234236
}
235237

236-
def check_match_service(self, Signatures, service) -> bool:
237-
for sig_ in Signatures:
238+
def check_match_service(self, signatures, service) -> bool:
239+
for sig_ in signatures:
238240
if sig_.service == service:
239241
return True
240242
return False
@@ -266,22 +268,22 @@ def probe_sequentially(self):
266268
self.port,
267269
probe.probe_string,
268270
probe.totalwaits,
269-
probe.tcpwrapperdms,
271+
probe.tcpwrapped_ms,
270272
)
271273
else:
272274
response = tcp_probe_ssl(
273275
self.host,
274276
self.port,
275277
probe.probe_string,
276278
probe.totalwaits,
277-
probe.tcpwrapperdms,
279+
probe.tcpwrapped_ms,
278280
)
279281
else:
280282
response = udp_probe(self.host, self.port, probe.probe_string, probe.totalwaits)
281283

282284
if response is None:
283285
response = tcp_probe(
284-
self.host, self.port, probe.probe_string, probe.totalwaits, probe.tcpwrapperdms
286+
self.host, self.port, probe.probe_string, probe.totalwaits, probe.tcpwrapped_ms
285287
)
286288

287289
if not response or response.get("raw_bytes") is None:
@@ -290,8 +292,8 @@ def probe_sequentially(self):
290292
raw_response = response.get("raw_bytes")
291293

292294
# 1. Check Primary Signatures
293-
for signature in probe.Signatures:
294-
matched_data = self.Match_response(raw_response, signature)
295+
for signature in probe.signatures:
296+
matched_data = self.match_response(raw_response, signature)
295297
res_ = matched_data["result"]
296298
if matched_data["status"]:
297299
if signature.sig_type == "match":
@@ -344,8 +346,8 @@ def probe_sequentially(self):
344346
if not fallback_probe:
345347
continue
346348

347-
for signature in fallback_probe.Signatures:
348-
matched_data = self.Match_response(raw_response, signature)
349+
for signature in fallback_probe.signatures:
350+
matched_data = self.match_response(raw_response, signature)
349351
res_ = matched_data["result"]
350352
if matched_data["status"]:
351353
if signature.sig_type == "match":

nettacker/probing/loader.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
log = logger.get_logger()
99

1010

11-
class version_details:
11+
class VersionDetails:
12+
"""Nmap-style version templates extracted from a matched signature."""
13+
1214
def __init__(
1315
self,
1416
raw,
@@ -35,6 +37,8 @@ def __init__(
3537

3638

3739
class Signature:
40+
"""A single match/softmatch regex rule belonging to a probe."""
41+
3842
def __init__(
3943
self,
4044
service,
@@ -53,6 +57,8 @@ def __init__(
5357

5458

5559
class Probe:
60+
"""An nmap-service-probes probe: the payload to send plus its signatures."""
61+
5662
def __init__(
5763
self,
5864
name,
@@ -65,26 +71,27 @@ def __init__(
6571
fallbacks=None,
6672
probe_string="",
6773
no_payload=False,
68-
Signatures=None,
74+
signatures=None,
6975
):
7076
self.name = name
7177
self.protocol = protocol
7278
self.totalwaits = totalwaits
73-
self.tcpwrapperdms = tcpwrappedms
79+
self.tcpwrapped_ms = tcpwrappedms
7480
self.rarity = rarity
7581
self.ports = ports or []
7682
self.sslports = sslports or []
7783
self.fallbacks = fallbacks or []
7884
self.probe_string = probe_string
7985
self.no_payload = no_payload
80-
self.Signatures = Signatures or []
86+
self.signatures = signatures or []
8187

8288

8389
_PROBES_CACHE = None
8490
_probes_by_name = {}
8591

8692

8793
def load_probes_from_yaml():
94+
"""Parse Config.path.probes_yaml_file into Probe objects, caching the result."""
8895
global _PROBES_CACHE
8996
global _probes_by_name
9097

@@ -129,7 +136,7 @@ def load_probes_from_yaml():
129136
log.verbose_info(f"Probe signature failed to compile: {pattern!r} ({e})")
130137
continue
131138
v = s.get("version", {}) or {}
132-
version = version_details(
139+
version = VersionDetails(
133140
raw=v.get("raw", ""),
134141
version_template=v.get("version_template", ""),
135142
product=v.get("product", ""),
@@ -163,7 +170,7 @@ def load_probes_from_yaml():
163170
fallbacks=fallbacks,
164171
probe_string=probe_string,
165172
no_payload=no_payload,
166-
Signatures=signatures,
173+
signatures=signatures,
167174
)
168175
_probes_by_name[name] = probe
169176

@@ -172,6 +179,7 @@ def load_probes_from_yaml():
172179

173180

174181
def build_probes_from_yaml():
182+
"""Return the cached probes dict, loading it from YAML on first use."""
175183
if not _probes_by_name:
176184
load_probes_from_yaml()
177185
return _probes_by_name

tests/probing/test_loader.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def test_loads_probes_from_configured_path(self, tmp_path, monkeypatch):
5858
assert "NULL" in probes
5959
assert probes["NULL"].protocol == "tcp"
6060
assert probes["NULL"].ports == [22]
61-
assert len(probes["NULL"].Signatures) == 1
61+
assert len(probes["NULL"].signatures) == 1
6262

6363
def test_null_fallback_is_always_appended(self, tmp_path, monkeypatch):
6464
probes_file = tmp_path / "probes.yaml"
@@ -77,15 +77,15 @@ def test_broken_regex_signature_is_skipped_not_fatal(self, tmp_path, monkeypatch
7777
probes = load_probes_from_yaml()
7878

7979
assert "BROKEN" in probes
80-
assert probes["BROKEN"].Signatures == []
80+
assert probes["BROKEN"].signatures == []
8181

8282
def test_compiled_signature_regex_is_usable(self, tmp_path, monkeypatch):
8383
probes_file = tmp_path / "probes.yaml"
8484
probes_file.write_text(PROBE_YAML)
8585
monkeypatch.setattr(loader_module.Config.path, "probes_yaml_file", probes_file)
8686

8787
probes = load_probes_from_yaml()
88-
sig = probes["NULL"].Signatures[0]
88+
sig = probes["NULL"].signatures[0]
8989
assert sig.regex.search(b"SSH-2.0\r\n")
9090

9191
def test_second_call_uses_cache(self, tmp_path, monkeypatch):

0 commit comments

Comments
 (0)