mirror of
https://github.com/meshcore-dev/meshcore_py.git
synced 2026-04-20 22:13:49 +00:00
G7: R03 — pre-register binary request before send() to close race window
Why: send_binary_req() registered the pending request with the reader AFTER send() returned. If a BINARY_RESPONSE arrives between send() returning and registration (reachable for TCP-companion proxies), the reader logs "No tracked request found" and the caller's wait_for_event times out. Fix: pre-register a placeholder keyed by object id before send(), then swap it for the real tag from MSG_SENT. On send() failure, the placeholder is cleaned up. Refs: Forensics report finding R03
This commit is contained in:
parent
aed7db21b3
commit
168e613ed7
1 changed files with 24 additions and 11 deletions
|
|
@ -243,18 +243,31 @@ class CommandHandlerBase:
|
|||
logger.debug(f"Binary request to {dst_bytes.hex()}")
|
||||
data = b"\x32" + dst_bytes + request_type.value.to_bytes(1, "little", signed=False) + (data if data else b"")
|
||||
|
||||
result = await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
||||
|
||||
# Register the request with the reader if we have both reader and request_type
|
||||
if (result.type == EventType.MSG_SENT and
|
||||
self._reader is not None and
|
||||
request_type is not None):
|
||||
|
||||
exp_tag = result.payload["expected_ack"].hex()
|
||||
# Use provided timeout or fallback to suggested timeout (with 5s default)
|
||||
actual_timeout = timeout if timeout is not None and timeout > 0 else result.payload.get("suggested_timeout", 4000) / 800.0
|
||||
# Pre-register a placeholder binary request before send() to close the race
|
||||
# window where a BINARY_RESPONSE could arrive between send() returning and
|
||||
# registration. The placeholder tag is patched to the real tag once MSG_SENT
|
||||
# returns. If send() fails, the placeholder is cleaned up.
|
||||
placeholder_tag = None
|
||||
if self._reader is not None and request_type is not None:
|
||||
placeholder_tag = f"_pending_{id(data)}"
|
||||
actual_timeout = timeout if timeout is not None and timeout > 0 else self.default_timeout
|
||||
actual_timeout = min_timeout if actual_timeout < min_timeout else actual_timeout
|
||||
self._reader.register_binary_request(pubkey_prefix.hex(), exp_tag, request_type, actual_timeout, context=context)
|
||||
self._reader.register_binary_request(pubkey_prefix.hex(), placeholder_tag, request_type, actual_timeout, context=context)
|
||||
|
||||
result = await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
||||
|
||||
# Patch the placeholder tag with the real tag from MSG_SENT, or clean up on failure
|
||||
if placeholder_tag is not None and self._reader is not None:
|
||||
# Remove the placeholder entry
|
||||
self._reader.pending_binary_requests.pop(placeholder_tag, None)
|
||||
if (result.type == EventType.MSG_SENT and
|
||||
request_type is not None):
|
||||
exp_tag = result.payload["expected_ack"].hex()
|
||||
# Use suggested_timeout from the result if available
|
||||
actual_timeout = timeout if timeout is not None and timeout > 0 else result.payload.get("suggested_timeout", 4000) / 800.0
|
||||
actual_timeout = min_timeout if actual_timeout < min_timeout else actual_timeout
|
||||
# Register with the real tag
|
||||
self._reader.register_binary_request(pubkey_prefix.hex(), exp_tag, request_type, actual_timeout, context=context)
|
||||
|
||||
return result
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue