From 3412885f86ebc678e668b1ad5f413f3580c1493e Mon Sep 17 00:00:00 2001 From: supersuryaansh Date: Fri, 13 Feb 2026 23:46:41 +0530 Subject: [PATCH] add holepunching on lan --- lib/connect.js | 22 +--------------------- lib/server.js | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/lib/connect.js b/lib/connect.js index 3751c6cb..23afbf83 100644 --- a/lib/connect.js +++ b/lib/connect.js @@ -1,7 +1,7 @@ const NoiseSecretStream = require('@hyperswarm/secret-stream') const b4a = require('b4a') const relay = require('blind-relay') -const { isReserved, isBogon } = require('bogon') +const { isReserved, isPrivate, isBogon } = require('bogon') const safetyCatch = require('safety-catch') const unslab = require('unslab') const Semaphore = require('./semaphore') @@ -230,26 +230,6 @@ async function holepunch(c, opts) { return } - // TODO: would be better to just try local addrs in the background whilst continuing with other strategies... - if (c.lan && relayed && clientAddress.host === serverAddress.host) { - const serverAddresses = payload.addresses4.filter(onlyNonReserved) - - if (serverAddresses.length > 0) { - const myAddresses = Holepuncher.localAddresses(c.dht.io.serverSocket) - const addr = Holepuncher.matchAddress(myAddresses, serverAddresses) || serverAddresses[0] - - const socket = c.dht.io.serverSocket - try { - await c.dht.ping(addr) - } catch { - maybeDestroyEncryptedSocket(c, HOLEPUNCH_ABORTED()) - return - } - c.onsocket(socket, addr.port, addr.host) - return - } - } - if (!remoteHolepunchable) { maybeDestroyEncryptedSocket(c, CANNOT_HOLEPUNCH()) return diff --git a/lib/server.js b/lib/server.js index fd86e6cb..52165b2b 100644 --- a/lib/server.js +++ b/lib/server.js @@ -388,9 +388,21 @@ module.exports = class Server extends EventEmitter { } if (remotePayload.firewall === FIREWALL.OPEN || direct) { + let connectAddr = clientAddress + // When on the same LAN, the client's external address requires NAT hairpinning + // which most routers don't support. Use the client's LAN address instead. + if (!direct && clientAddress.host === serverAddress.host && !isPrivate(clientAddress.host)) { + const clientLanAddrs = remotePayload.addresses4.filter(onlyPrivateHosts) + if (clientLanAddrs.length > 0) { + const myAddresses = Holepuncher.localAddresses(this.dht.io.serverSocket) + const lanAddr = Holepuncher.matchAddress(myAddresses, clientLanAddrs) + if (lanAddr) connectAddr = lanAddr + } + } + const sock = direct ? socket : this.dht.socket this.dht.stats.punches.open++ - hs.onsocket(sock, clientAddress.port, clientAddress.host) + hs.onsocket(sock, connectAddr.port, connectAddr.host) return hs } @@ -411,15 +423,16 @@ module.exports = class Server extends EventEmitter { if (hs.relayToken === null) hs.rawStream.destroy() } - if (!direct && clientAddress.host === serverAddress.host) { + if (!direct && clientAddress.host === serverAddress.host && !isPrivate(clientAddress.host)) { const clientAddresses = remotePayload.addresses4.filter(onlyPrivateHosts) - if (clientAddresses.length > 0 && this._shareLocalAddress) { - const myAddresses = await this._localAddresses() + if (clientAddresses.length > 0) { + const myAddresses = Holepuncher.localAddresses(this.dht.io.serverSocket) const addr = Holepuncher.matchAddress(myAddresses, clientAddresses) if (addr) { - hs.prepunching = setTimeout(onabort, HANDSHAKE_INITIAL_TIMEOUT) + this.dht.stats.punches.open++ + hs.onsocket(this.dht.socket, addr.port, addr.host) return hs } }