aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/api/signaling.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/signaling.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/signaling.py22
1 files changed, 20 insertions, 2 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/signaling.py b/packages/meshbay-hub/src/meshbay_hub/api/signaling.py
index a8feae8..b4be3f2 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/signaling.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/signaling.py
@@ -34,6 +34,9 @@ log = logging.getLogger(__name__)
router = APIRouter(prefix="/v1/nodes", tags=["signaling"])
_webrtc_answers: dict[str, asyncio.Future] = {}
+# peer_id → the node the offer was relayed to. An answer is only accepted
+# from that node (see handle_webrtc_answer).
+_answer_owner: dict[str, str] = {}
class WebRTCOfferRequest(BaseModel):
@@ -133,6 +136,7 @@ async def webrtc_offer(
peer_id = str(uuid.uuid4())
answer_future: asyncio.Future = asyncio.get_event_loop().create_future()
_webrtc_answers[peer_id] = answer_future
+ _answer_owner[peer_id] = node_id
_pending_per_user[current_user.id] = _pending_per_user.get(current_user.id, 0) + 1
try:
@@ -157,6 +161,7 @@ async def webrtc_offer(
)
finally:
_webrtc_answers.pop(peer_id, None)
+ _answer_owner.pop(peer_id, None)
remaining = _pending_per_user.get(current_user.id, 1) - 1
if remaining > 0:
_pending_per_user[current_user.id] = remaining
@@ -164,13 +169,26 @@ async def webrtc_offer(
_pending_per_user.pop(current_user.id, None)
-def handle_webrtc_answer(msg: dict) -> None:
- """Called from the node WebSocket message loop when a webrtc_answer arrives."""
+def handle_webrtc_answer(msg: dict, node_id: str) -> None:
+ """Called from the node WebSocket message loop when a webrtc_answer arrives.
+
+ `node_id` is the socket this arrived on, and the answer is accepted only for
+ a `peer_id` the hub issued to **that** node. The answer carries the SDP the
+ browser then connects to, so without the check any connected node could
+ resolve any pending offer and stand in for the node the client asked for.
+ That it had not happened rested on a uuid4 being unguessable, which is a
+ reason it was hard, not a reason it was refused.
+ """
peer_id = msg.get("peer_id")
if not peer_id:
log.warning("webrtc_answer without peer_id")
return
+ if _answer_owner.get(peer_id) != node_id:
+ log.warning("Node %s answered an offer it was never sent (peer=%s)",
+ (node_id or "?")[:8], str(peer_id)[:8])
+ return
+
future = _webrtc_answers.get(peer_id)
if future and not future.done():
future.set_result({