network-core 0.5.2__tar.gz → 0.5.4__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 (33) hide show
  1. {network_core-0.5.2 → network_core-0.5.4}/PKG-INFO +1 -1
  2. {network_core-0.5.2 → network_core-0.5.4}/network_core/__init__.py +1 -0
  3. {network_core-0.5.2 → network_core-0.5.4}/network_core/analysis.py +6 -3
  4. network_core-0.5.4/network_core/bin/process_pcap +0 -0
  5. network_core-0.5.4/network_core/pcap.py +99 -0
  6. {network_core-0.5.2 → network_core-0.5.4}/network_core.egg-info/PKG-INFO +1 -1
  7. {network_core-0.5.2 → network_core-0.5.4}/network_core.egg-info/SOURCES.txt +2 -0
  8. {network_core-0.5.2 → network_core-0.5.4}/network_core.egg-info/top_level.txt +1 -0
  9. {network_core-0.5.2 → network_core-0.5.4}/pyproject.toml +5 -2
  10. {network_core-0.5.2 → network_core-0.5.4}/network_core/conn.py +0 -0
  11. {network_core-0.5.2 → network_core-0.5.4}/network_core/connOps.py +0 -0
  12. {network_core-0.5.2 → network_core-0.5.4}/network_core/dataModels.py +0 -0
  13. {network_core-0.5.2 → network_core-0.5.4}/network_core/http/__init__.py +0 -0
  14. {network_core-0.5.2 → network_core-0.5.4}/network_core/http/httpExtract/__init__.py +0 -0
  15. {network_core-0.5.2 → network_core-0.5.4}/network_core/http/httpExtract/helpers.py +0 -0
  16. {network_core-0.5.2 → network_core-0.5.4}/network_core/http/httpExtract/parser.py +0 -0
  17. {network_core-0.5.2 → network_core-0.5.4}/network_core/http/httpExtract/pdh.py +0 -0
  18. {network_core-0.5.2 → network_core-0.5.4}/network_core/http/httpExtract/pdml_parser.py +0 -0
  19. {network_core-0.5.2 → network_core-0.5.4}/network_core/http/httpIO.py +0 -0
  20. {network_core-0.5.2 → network_core-0.5.4}/network_core/sni/__init__.py +0 -0
  21. {network_core-0.5.2 → network_core-0.5.4}/network_core/sni/clientHello.py +0 -0
  22. {network_core-0.5.2 → network_core-0.5.4}/network_core/sni/constants.py +0 -0
  23. {network_core-0.5.2 → network_core-0.5.4}/network_core/sni/crypto.py +0 -0
  24. {network_core-0.5.2 → network_core-0.5.4}/network_core/sni/helpers.py +0 -0
  25. {network_core-0.5.2 → network_core-0.5.4}/network_core/sni/parsers.py +0 -0
  26. {network_core-0.5.2 → network_core-0.5.4}/network_core/utils/__init__.py +0 -0
  27. {network_core-0.5.2 → network_core-0.5.4}/network_core/utils/csvIO.py +0 -0
  28. {network_core-0.5.2 → network_core-0.5.4}/network_core/utils/dt.py +0 -0
  29. {network_core-0.5.2 → network_core-0.5.4}/network_core/utils/jsonIO.py +0 -0
  30. {network_core-0.5.2 → network_core-0.5.4}/network_core/utils/pcapIO.py +0 -0
  31. {network_core-0.5.2 → network_core-0.5.4}/network_core.egg-info/dependency_links.txt +0 -0
  32. {network_core-0.5.2 → network_core-0.5.4}/network_core.egg-info/requires.txt +0 -0
  33. {network_core-0.5.2 → network_core-0.5.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: network_core
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: Core networking utilities and data models
5
5
  Author: Your Name
6
6
  Requires-Python: >=3.9
@@ -16,3 +16,4 @@ from .analysis import (
16
16
  conn_features,
17
17
  nan_to_none,
18
18
  )
19
+ from .pcap import process_pcap, process_pcap_auto, process_pcap_tree
@@ -300,8 +300,11 @@ def conn_features(
300
300
 
301
301
  ub = _burst_stats(lengths, timestamps, directions, direction=0, gap_ms=500)
302
302
 
303
- small_ups = up_len[up_len < 200]
304
- idle_up_med = float(np.median(small_ups)) if len(small_ups) >= 3 else float("nan")
303
+ # Median size (bytes) of upload packets < 200 B — captures the ACK/header
304
+ # packet baseline. Agents send large bursts separated by pure ACK traffic
305
+ # (66 B), while chat sessions mix in more small-but-nonzero request packets.
306
+ small_ups = up_len[up_len < 200]
307
+ up_small_pkt_med = float(np.median(small_ups)) if len(small_ups) >= 3 else float("nan")
305
308
 
306
309
  pattern = _classify_pattern(
307
310
  n_pkts=n_pkts,
@@ -378,7 +381,7 @@ def conn_features(
378
381
  "up_interburst_cv": ub["interburst_cv"],
379
382
  "up_interburst_p95_ms": ub["interburst_p95_ms"],
380
383
  "ttfb_ms": ttfb_ms,
381
- "idle_up_med": idle_up_med,
384
+ "up_small_pkt_med": up_small_pkt_med,
382
385
  "traffic_pattern": pattern,
383
386
  }
384
387
 
@@ -0,0 +1,99 @@
1
+ """Wrapper around the bundled process_pcap Go binary."""
2
+
3
+ import os
4
+ import signal
5
+ import subprocess
6
+ from pathlib import Path
7
+
8
+ _BIN = Path(__file__).parent / "bin" / "process_pcap"
9
+
10
+
11
+ def _ensure_binary() -> Path:
12
+ if not _BIN.exists():
13
+ raise RuntimeError(
14
+ f"process_pcap binary not found at {_BIN}. "
15
+ "Run `make build` in the network_core repo root."
16
+ )
17
+ return _BIN
18
+
19
+
20
+ def process_pcap(pcap_path: str, flows_path: str, packets_path: str) -> None:
21
+ """Run the Go flow extractor on a single pcap file.
22
+
23
+ Writes {flows_path} and {packets_path} as flat CSVs.
24
+ Removes stale outputs before running so partial prior runs never linger.
25
+ Blocks until the Go process exits.
26
+ """
27
+ pcap = Path(pcap_path)
28
+ if not pcap.exists():
29
+ raise FileNotFoundError(f"pcap not found: {pcap_path}")
30
+ if pcap.suffix not in {".pcap", ".pcapng"}:
31
+ raise ValueError(f"Expected .pcap or .pcapng, got: {pcap.name}")
32
+
33
+ Path(flows_path).unlink(missing_ok=True)
34
+ Path(packets_path).unlink(missing_ok=True)
35
+
36
+ result = subprocess.run(
37
+ [str(_ensure_binary()), str(pcap), flows_path, packets_path],
38
+ capture_output=True,
39
+ text=True,
40
+ )
41
+ if result.returncode != 0:
42
+ raise RuntimeError(
43
+ f"process_pcap failed (exit {result.returncode}):\n{result.stderr.strip()}"
44
+ )
45
+
46
+
47
+ def _derive_output_paths(pcap_path: str) -> tuple[str, str]:
48
+ """Return (flows_path, packets_path) derived from a pcap file path."""
49
+ p = Path(pcap_path)
50
+ stem = p.stem
51
+ return str(p.parent / f"{stem}Flows.csv"), str(p.parent / f"{stem}Packets.csv")
52
+
53
+
54
+ def process_pcap_auto(pcap_path: str) -> tuple[str, str]:
55
+ """Run process_pcap with output paths derived from the pcap filename.
56
+
57
+ Returns (flows_path, packets_path).
58
+ """
59
+ flows_path, packets_path = _derive_output_paths(pcap_path)
60
+ process_pcap(pcap_path, flows_path, packets_path)
61
+ return flows_path, packets_path
62
+
63
+
64
+ def _iter_pcaps(root: str):
65
+ """Yield (pcap_path, flows_path, packets_path) for every pcap under root."""
66
+ p = Path(root)
67
+ if p.is_dir():
68
+ for child in p.iterdir():
69
+ yield from _iter_pcaps(str(child))
70
+ elif p.suffix in {".pcap", ".pcapng"}:
71
+ flows, packets = _derive_output_paths(str(p))
72
+ yield str(p.resolve()), str(Path(flows).resolve()), str(Path(packets).resolve())
73
+
74
+
75
+ def process_pcap_tree(root: str, force: bool = False) -> None:
76
+ """Run process_pcap on every pcap file found recursively under root.
77
+
78
+ Skips files whose output CSVs already exist unless force=True.
79
+ Runs in a new process group so Ctrl-C cleanly kills the subprocess.
80
+ """
81
+ for pcap_path, flows_path, packets_path in _iter_pcaps(root):
82
+ if not force and Path(flows_path).exists() and Path(packets_path).exists():
83
+ print(f"Skipping {Path(pcap_path).name} (outputs already exist)")
84
+ continue
85
+
86
+ Path(flows_path).unlink(missing_ok=True)
87
+ Path(packets_path).unlink(missing_ok=True)
88
+
89
+ proc = subprocess.Popen(
90
+ [str(_ensure_binary()), pcap_path, flows_path, packets_path],
91
+ preexec_fn=os.setsid,
92
+ )
93
+ try:
94
+ proc.wait()
95
+ except KeyboardInterrupt:
96
+ os.killpg(proc.pid, signal.SIGTERM)
97
+ raise
98
+ if proc.returncode != 0:
99
+ raise RuntimeError(f"process_pcap failed (exit {proc.returncode}) on {pcap_path}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: network_core
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: Core networking utilities and data models
5
5
  Author: Your Name
6
6
  Requires-Python: >=3.9
@@ -4,11 +4,13 @@ network_core/analysis.py
4
4
  network_core/conn.py
5
5
  network_core/connOps.py
6
6
  network_core/dataModels.py
7
+ network_core/pcap.py
7
8
  network_core.egg-info/PKG-INFO
8
9
  network_core.egg-info/SOURCES.txt
9
10
  network_core.egg-info/dependency_links.txt
10
11
  network_core.egg-info/requires.txt
11
12
  network_core.egg-info/top_level.txt
13
+ network_core/bin/process_pcap
12
14
  network_core/http/__init__.py
13
15
  network_core/http/httpIO.py
14
16
  network_core/http/httpExtract/__init__.py
@@ -1,2 +1,3 @@
1
1
  dist
2
+ go_src
2
3
  network_core
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "network_core"
3
- version = "0.5.2"
3
+ version = "0.5.4"
4
4
  description = "Core networking utilities and data models"
5
5
  authors = [{ name = "Your Name" }]
6
6
  readme = "README.md"
@@ -22,4 +22,7 @@ requires = ["setuptools>=61.0"]
22
22
  build-backend = "setuptools.build_meta"
23
23
 
24
24
  [tool.setuptools.packages.find]
25
- where = ["."]
25
+ where = ["."]
26
+
27
+ [tool.setuptools.package-data]
28
+ network_core = ["bin/process_pcap"]
File without changes