diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/signaling.py')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/api/signaling.py | 22 |
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({ |