diff --git a/client.html b/client.html
index d8f2f99..36b35cf 100644
--- a/client.html
+++ b/client.html
@@ -408,10 +408,10 @@
setStatus('Connecting...');
sigWs = new WebSocket(SIG_URL);
- sigWs.onopen = () => {
+ sigWs.onopen = async () => {
setStatus('Connected — setting up stream...');
- initVisitorName();
- setupPeerConnection();
+ await initVisitorName();
+ await setupPeerConnection();
};
sigWs.onmessage = async e => {
diff --git a/streaming-agent/signaling-server.js b/streaming-agent/signaling-server.js
index 516ed57..1f28ec1 100644
--- a/streaming-agent/signaling-server.js
+++ b/streaming-agent/signaling-server.js
@@ -248,7 +248,14 @@ function stopPacat(browserSessId) {
}
// ── WebSocket handler ─────────────────────────────────────────────────────────
-wss.on('connection', ws => {
+wss.on('connection', (ws, req) => {
+ // Capture the real visitor IP at connection time from nginx headers.
+ // ws._socket.remoteAddress would be nginx's local IP for everyone.
+ const forwarded = req.headers['x-real-ip'] ||
+ req.headers['x-forwarded-for'] || '';
+ ws._realIp = forwarded.split(',')[0].trim() ||
+ req.socket.remoteAddress.replace('::ffff:', '');
+
let role = null;
let agentSessionId = null;
let browserSessId = null;
@@ -280,7 +287,13 @@ wss.on('connection', ws => {
}
else if (msg.type === 'request_control') {
- visitorName = msg.visitorName;
+ // Use the server-assigned name only — never trust the client-supplied one.
+ // If get_ip hasn't been called yet, reject the request.
+ if (!ws._assignedName) {
+ console.log('[sig] request_control rejected — no server-assigned name yet');
+ return;
+ }
+ visitorName = ws._assignedName;
if (containerRestarting) return;
@@ -291,14 +304,17 @@ wss.on('connection', ws => {
if (controlQueue.find(e => e.ws === ws)) return;
+ // Prevent someone with the same IP as the current controller from
+ // queuing behind themselves (duplicate tab or same-IP reconnect).
+ if (currentController && currentController.visitorName === visitorName) {
+ console.log(`[sig] ${visitorName} already controlling, ignoring duplicate request_control`);
+ return;
+ }
+
const entry = { ws, visitorName, browserSessId };
if (!currentController) {
applyController(entry);
- } else if (currentController.visitorName === visitorName) {
- // Same identity already controlling (e.g. duplicate tab) — don't queue
- // behind yourself.
- return;
} else {
controlQueue.push(entry);
console.log(`[sig] ${visitorName} joined queue position ${controlQueue.length}`);
@@ -317,8 +333,11 @@ wss.on('connection', ws => {
}
else if (msg.type === 'get_ip') {
- const ip = ws._socket.remoteAddress.replace('::ffff:', '');
- const name = ipToName(ip);
+ const name = ipToName(ws._realIp);
+ // Store server-assigned name on the socket so request_control can use
+ // it exclusively — the client-supplied visitorName is never trusted.
+ ws._assignedName = name;
+ visitorName = name;
ws.send(JSON.stringify({ type: 'your_ip', ip: name }));
}