Skip to content

Commit 58a9e15

Browse files
author
Your Name
committed
fix(core): reject unreachable tcp endpoints during discovery
1 parent 039c640 commit 58a9e15

2 files changed

Lines changed: 52 additions & 10 deletions

File tree

src/kagan/core/ipc/discovery.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import json
66
import logging
7+
import socket
78
from dataclasses import dataclass
89
from typing import TYPE_CHECKING, Any
910

@@ -82,6 +83,14 @@ def _is_process_alive(pid: int) -> bool:
8283
return pid_exists(pid)
8384

8485

86+
def _is_tcp_endpoint_reachable(address: str, port: int) -> bool:
87+
try:
88+
with socket.create_connection((address, port), timeout=0.25):
89+
return True
90+
except OSError:
91+
return False
92+
93+
8594
def discover_core_endpoint() -> CoreEndpoint | None:
8695
"""Discover a running Kagan core by reading runtime files.
8796
@@ -113,6 +122,18 @@ def discover_core_endpoint() -> CoreEndpoint | None:
113122
if pid is not None and not _is_process_alive(pid):
114123
logger.info("Core process (PID %d) is no longer running; stale endpoint", pid)
115124
return None
125+
if normalized_transport == "tcp":
126+
raw_port = data.get("port")
127+
if not isinstance(raw_port, int) or raw_port <= 0:
128+
logger.warning("Malformed TCP endpoint file at %s", endpoint_path)
129+
return None
130+
if not _is_tcp_endpoint_reachable(str(address), raw_port):
131+
logger.info(
132+
"Core endpoint tcp://%s:%s is unreachable; treating runtime metadata as stale",
133+
address,
134+
raw_port,
135+
)
136+
return None
116137

117138
token = _read_text(get_core_token_path())
118139

tests/core/unit/test_core_ipc_discovery.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,19 @@
1111
from pathlib import Path
1212

1313

14-
def _write_runtime_files(runtime_dir: Path, *, owner_pid: int) -> None:
14+
def _write_runtime_files(
15+
runtime_dir: Path,
16+
*,
17+
owner_pid: int,
18+
transport: str = "socket",
19+
address: str = "/tmp/kagan-core.sock",
20+
port: int | None = None,
21+
) -> None:
1522
runtime_dir.mkdir(parents=True, exist_ok=True)
16-
(runtime_dir / "endpoint.json").write_text(
17-
json.dumps(
18-
{
19-
"transport": "socket",
20-
"address": "/tmp/kagan-core.sock",
21-
}
22-
),
23-
encoding="utf-8",
24-
)
23+
endpoint_payload: dict[str, object] = {"transport": transport, "address": address}
24+
if port is not None:
25+
endpoint_payload["port"] = port
26+
(runtime_dir / "endpoint.json").write_text(json.dumps(endpoint_payload), encoding="utf-8")
2527
(runtime_dir / "token").write_text("lease-token", encoding="utf-8")
2628
(runtime_dir / "core.lease.json").write_text(
2729
json.dumps(
@@ -67,3 +69,22 @@ def test_discover_core_endpoint_rejects_stale_dead_lease(monkeypatch, tmp_path:
6769
endpoint = discover_core_endpoint()
6870

6971
assert endpoint is None
72+
73+
74+
def test_discover_core_endpoint_rejects_unreachable_tcp_endpoint(
75+
monkeypatch, tmp_path: Path
76+
) -> None:
77+
runtime_dir = tmp_path / "core-runtime"
78+
_write_runtime_files(
79+
runtime_dir,
80+
owner_pid=os.getpid(),
81+
transport="tcp",
82+
address="127.0.0.1",
83+
port=54321,
84+
)
85+
monkeypatch.setenv("KAGAN_CORE_RUNTIME_DIR", str(runtime_dir))
86+
monkeypatch.setattr("kagan.core.ipc.discovery._is_tcp_endpoint_reachable", lambda *_: False)
87+
88+
endpoint = discover_core_endpoint()
89+
90+
assert endpoint is None

0 commit comments

Comments
 (0)