From 8412203bf82bf5c30a48621415bf4c814144c167 Mon Sep 17 00:00:00 2001 From: Billie Hilton <587740+billiegoose@users.noreply.github.com> Date: Tue, 25 Jun 2024 19:08:38 -0400 Subject: [PATCH 1/3] add timestamp to handshake --- lib/connect.js | 10 ++++++++-- lib/messages.js | 9 +++++++-- lib/server.js | 7 ++++++- test/messages.js | 44 +++++++++++++++++++++++++++++++++++++++----- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/lib/connect.js b/lib/connect.js index 0f140ef5..af261376 100644 --- a/lib/connect.js +++ b/lib/connect.js @@ -81,7 +81,10 @@ module.exports = function connect (dht, publicKey, opts = {}) { relaySocket: null, relayClient: null, relayPaired: false, - relayKeepAlive: opts.relayKeepAlive || 5000 + relayKeepAlive: opts.relayKeepAlive || 5000, + + // Used by the server when tie-breaking between two connections from the same peer + timestamp: opts.timestamp ?? -1 } // If the raw stream receives an error signal pre connect (ie from the firewall hook), make sure @@ -374,7 +377,8 @@ async function connectThroughNode (c, address, socket) { secretStream: {}, relayThrough: c.relayThrough ? { publicKey: c.relayThrough, token: c.relayToken } - : null + : null, + timestamp: c.timestamp }) if (isDone(c)) return } @@ -410,6 +414,8 @@ async function connectThroughNode (c, address, socket) { serverAddress, payload } + c.encryptedSocket.userData ??= {} + c.encryptedSocket.userData.timestamp = payload.timestamp c.payload = new SecurePayload(hs.holepunchSecret) diff --git a/lib/messages.js b/lib/messages.js index 86d83ee4..e3684054 100644 --- a/lib/messages.js +++ b/lib/messages.js @@ -162,6 +162,7 @@ exports.noisePayload = { if (m.udx) udxInfo.preencode(state, m.udx) if (m.secretStream) secretStreamInfo.preencode(state, m.secretStream) if (m.relayThrough) relayThroughInfo.preencode(state, m.relayThrough) + if (m.timestamp >= 0) c.uint.preencode(state, m.timestamp) }, encode (state, m) { let flags = 0 @@ -172,6 +173,7 @@ exports.noisePayload = { if (m.udx) flags |= 8 if (m.secretStream) flags |= 16 if (m.relayThrough) flags |= 32 + if (m.timestamp >= 0) flags |= 64 c.uint.encode(state, 1) // version c.uint.encode(state, flags) @@ -184,6 +186,7 @@ exports.noisePayload = { if (m.udx) udxInfo.encode(state, m.udx) if (m.secretStream) secretStreamInfo.encode(state, m.secretStream) if (m.relayThrough) relayThroughInfo.encode(state, m.relayThrough) + if (m.timestamp >= 0) c.uint.encode(state, m.timestamp) }, decode (state) { const version = c.uint.decode(state) @@ -200,7 +203,8 @@ exports.noisePayload = { addresses6: [], udx: null, secretStream: null, - relayThrough: null + relayThrough: null, + timestamp: -1 } } @@ -215,7 +219,8 @@ exports.noisePayload = { addresses6: (flags & 4) !== 0 ? ipv6Array.decode(state) : [], udx: (flags & 8) !== 0 ? udxInfo.decode(state) : null, secretStream: (flags & 16) !== 0 ? secretStreamInfo.decode(state) : null, - relayThrough: (flags & 32) !== 0 ? relayThroughInfo.decode(state) : null + relayThrough: (flags & 32) !== 0 ? relayThroughInfo.decode(state) : null, + timestamp: (flags & 64) !== 0 ? c.uint.decode(state) : -1 } } } diff --git a/lib/server.js b/lib/server.js index cc898004..9a9957f7 100644 --- a/lib/server.js +++ b/lib/server.js @@ -320,6 +320,8 @@ module.exports = class Server extends EventEmitter { keepAlive: this.dht.connectionKeepAlive }) + hs.encryptedSocket.userData ??= {} + hs.encryptedSocket.userData.timestamp = remotePayload.timestamp this.onconnection(hs.encryptedSocket) } @@ -355,7 +357,8 @@ module.exports = class Server extends EventEmitter { secretStream: {}, relayThrough: relayThrough ? { publicKey: relayThrough, token: hs.relayToken } - : null + : null, + timestamp: Date.now() }) } catch (err) { safetyCatch(err) @@ -650,6 +653,8 @@ module.exports = class Server extends EventEmitter { hs.encryptedSocket = this.createSecretStream(false, rawStream, { handshake: h }) + hs.encryptedSocket.userData ??= {} + hs.encryptedSocket.userData.timestamp = remotePayload.timestamp this.onconnection(hs.encryptedSocket) }) diff --git a/test/messages.js b/test/messages.js index 3d09e349..039fc075 100644 --- a/test/messages.js +++ b/test/messages.js @@ -14,7 +14,8 @@ test('basic noise payload', function (t) { addresses6: [], udx: null, secretStream: null, - relayThrough: null + relayThrough: null, + timestamp: -1 } m.noisePayload.preencode(state, c) @@ -56,7 +57,8 @@ test('noise payload with holepunch and addresses', function (t) { addresses6: [], udx: null, secretStream: null, - relayThrough: null + relayThrough: null, + timestamp: -1 } m.noisePayload.preencode(state, c) @@ -87,7 +89,8 @@ test('noise payload only addresses', function (t) { addresses6: [], udx: null, secretStream: null, - relayThrough: null + relayThrough: null, + timestamp: -1 } m.noisePayload.preencode(state, c) @@ -118,7 +121,8 @@ test('noise payload ipv6', function (t) { }], udx: null, secretStream: null, - relayThrough: null + relayThrough: null, + timestamp: -1 } m.noisePayload.preencode(state, c) @@ -150,10 +154,40 @@ test('noise payload newer version', function (t) { addresses6: [], udx: null, secretStream: null, - relayThrough: null + relayThrough: null, + timestamp: -1 }) }) +test('noise payload timestamp', function (t) { + const state = { start: 0, end: 0, buffer: null } + + const c = { + version: 1, + error: 0, + firewall: 2, + holepunch: null, + addresses4: [], + addresses6: [], + udx: null, + secretStream: null, + relayThrough: null, + timestamp: Date.now() + } + + m.noisePayload.preencode(state, c) + + state.buffer = b4a.allocUnsafe(state.end) + m.noisePayload.encode(state, c) + + state.start = 0 + + const d = m.noisePayload.decode(state) + + t.is(state.start, state.end) + t.alike(d, c) +}) + test('basic holepunch payload', function (t) { const state = { start: 0, end: 0, buffer: null } From e7fc0ee75e96fd6ee95193b957390864661187e8 Mon Sep 17 00:00:00 2001 From: Billie Hilton <587740+billiegoose@users.noreply.github.com> Date: Wed, 26 Jun 2024 09:20:45 -0400 Subject: [PATCH 2/3] >= -> > --- lib/messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/messages.js b/lib/messages.js index e3684054..fb58ff0b 100644 --- a/lib/messages.js +++ b/lib/messages.js @@ -162,7 +162,7 @@ exports.noisePayload = { if (m.udx) udxInfo.preencode(state, m.udx) if (m.secretStream) secretStreamInfo.preencode(state, m.secretStream) if (m.relayThrough) relayThroughInfo.preencode(state, m.relayThrough) - if (m.timestamp >= 0) c.uint.preencode(state, m.timestamp) + if (m.timestamp > 0) c.uint.preencode(state, m.timestamp) }, encode (state, m) { let flags = 0 From b354fcfbd3104d216b6eba0dd4d5beb843ffcea1 Mon Sep 17 00:00:00 2001 From: Billie Hilton <587740+billiegoose@users.noreply.github.com> Date: Wed, 26 Jun 2024 09:44:19 -0400 Subject: [PATCH 3/3] don't use userData --- lib/connect.js | 3 +-- lib/server.js | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/connect.js b/lib/connect.js index af261376..f6a62e56 100644 --- a/lib/connect.js +++ b/lib/connect.js @@ -414,8 +414,7 @@ async function connectThroughNode (c, address, socket) { serverAddress, payload } - c.encryptedSocket.userData ??= {} - c.encryptedSocket.userData.timestamp = payload.timestamp + c.encryptedSocket.timestamp = payload.timestamp c.payload = new SecurePayload(hs.holepunchSecret) diff --git a/lib/server.js b/lib/server.js index 9a9957f7..469ef464 100644 --- a/lib/server.js +++ b/lib/server.js @@ -320,8 +320,7 @@ module.exports = class Server extends EventEmitter { keepAlive: this.dht.connectionKeepAlive }) - hs.encryptedSocket.userData ??= {} - hs.encryptedSocket.userData.timestamp = remotePayload.timestamp + hs.encryptedSocket.timestamp = remotePayload.timestamp this.onconnection(hs.encryptedSocket) } @@ -653,8 +652,7 @@ module.exports = class Server extends EventEmitter { hs.encryptedSocket = this.createSecretStream(false, rawStream, { handshake: h }) - hs.encryptedSocket.userData ??= {} - hs.encryptedSocket.userData.timestamp = remotePayload.timestamp + hs.encryptedSocket.timestamp = remotePayload.timestamp this.onconnection(hs.encryptedSocket) })