diff --git a/packages/integration-tests/.aegir.js b/packages/integration-tests/.aegir.js index bbd789eb5c..61d0dffeaa 100644 --- a/packages/integration-tests/.aegir.js +++ b/packages/integration-tests/.aegir.js @@ -156,7 +156,7 @@ export default { await before.goLibp2pRelay?.proc.kill() await before.libp2pLimitedRelay?.stop() - // node-datachannel sometimes causes the process to hang + // Force exit after cleanup (WebRTC native modules may hold process open) process.exit(0) } } diff --git a/packages/transport-webrtc/package.json b/packages/transport-webrtc/package.json index 79f1d9aca3..8f9660124c 100644 --- a/packages/transport-webrtc/package.json +++ b/packages/transport-webrtc/package.json @@ -45,17 +45,18 @@ }, "dependencies": { "@chainsafe/is-ip": "^2.1.0", + "@chainsafe/libp2p-noise": "^17.0.0", "@libp2p/crypto": "^5.1.13", "@libp2p/interface": "^3.1.0", "@libp2p/interface-internal": "^3.0.9", "@libp2p/keychain": "^6.0.9", - "@chainsafe/libp2p-noise": "^17.0.0", "@libp2p/peer-id": "^6.0.4", "@libp2p/utils": "^7.0.9", "@multiformats/multiaddr": "^13.0.1", "@multiformats/multiaddr-matcher": "^3.0.1", "@peculiar/webcrypto": "^1.5.0", "@peculiar/x509": "^1.13.0", + "@roamhq/wrtc": "^0.9.1", "detect-browser": "^5.3.0", "get-port": "^7.1.0", "interface-datastore": "^9.0.1", @@ -65,7 +66,6 @@ "it-stream-types": "^2.0.2", "main-event": "^1.0.1", "multiformats": "^13.4.0", - "node-datachannel": "^0.29.0", "p-defer": "^4.0.1", "p-event": "^7.0.0", "p-timeout": "^7.0.0", @@ -74,6 +74,7 @@ "protons-runtime": "^5.6.0", "race-signal": "^2.0.0", "react-native-webrtc": "^124.0.6", + "stun": "^2.1.0", "uint8-varint": "^2.0.4", "uint8arraylist": "^2.4.8", "uint8arrays": "^5.1.0" diff --git a/packages/transport-webrtc/src/private-to-private/initiate-connection.ts b/packages/transport-webrtc/src/private-to-private/initiate-connection.ts index 2e8d895fdc..daa0093d04 100644 --- a/packages/transport-webrtc/src/private-to-private/initiate-connection.ts +++ b/packages/transport-webrtc/src/private-to-private/initiate-connection.ts @@ -68,20 +68,7 @@ export async function initiateConnection ({ rtcConfiguration, dataChannel, signa const messageStream = pbStream(stream).pb(Message) const peerConnection = new RTCPeerConnection(rtcConfiguration) - // make sure C++ peer connection is garbage collected - // https://github.com/murat-dogan/node-datachannel/issues/366#issuecomment-3228453155 - peerConnection.addEventListener('connectionstatechange', () => { - switch (peerConnection.connectionState) { - case 'closed': - peerConnection.close() - break - default: - break - } - }) - const muxerFactory = new DataChannelMuxerFactory({ - // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370 peerConnection, dataChannelOptions: dataChannel }) @@ -209,7 +196,6 @@ export async function initiateConnection ({ rtcConfiguration, dataChannel, signa return { remoteAddress: ma, - // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370 peerConnection, muxerFactory } diff --git a/packages/transport-webrtc/src/private-to-private/transport.ts b/packages/transport-webrtc/src/private-to-private/transport.ts index ace1163d89..02f9b52a83 100644 --- a/packages/transport-webrtc/src/private-to-private/transport.ts +++ b/packages/transport-webrtc/src/private-to-private/transport.ts @@ -199,19 +199,7 @@ export class WebRTCTransport implements Transport, Startable { async _onProtocol (stream: Stream, connection: Connection, signal: AbortSignal): Promise { const peerConnection = new RTCPeerConnection(await getRtcConfiguration(this.init.rtcConfiguration)) - // make sure C++ peer connection is garbage collected - // https://github.com/murat-dogan/node-datachannel/issues/366#issuecomment-3228453155 - peerConnection.addEventListener('connectionstatechange', () => { - switch (peerConnection.connectionState) { - case 'closed': - peerConnection.close() - break - default: - break - } - }) const muxerFactory = new DataChannelMuxerFactory({ - // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370 peerConnection, dataChannelOptions: this.init.dataChannel }) @@ -229,7 +217,6 @@ export class WebRTCTransport implements Transport, Startable { }) const webRTCConn = toMultiaddrConnection({ - // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370 peerConnection, remoteAddr: remoteAddress, metrics: this.metrics?.listenerEvents, @@ -246,7 +233,6 @@ export class WebRTCTransport implements Transport, Startable { }) // close the connection on shut down - // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370 this._closeOnShutdown(peerConnection, webRTCConn) } catch (err: any) { this.log.error('incoming signaling error - %e', err) diff --git a/packages/transport-webrtc/src/private-to-public/listener.ts b/packages/transport-webrtc/src/private-to-public/listener.ts index ed1c99abe6..f2991d4dfc 100644 --- a/packages/transport-webrtc/src/private-to-public/listener.ts +++ b/packages/transport-webrtc/src/private-to-public/listener.ts @@ -11,7 +11,6 @@ import { createDialerRTCPeerConnection } from './utils/get-rtcpeerconnection.js' import { stunListener } from './utils/stun-listener.js' import type { DataChannelOptions, TransportCertificate } from '../index.js' import type { WebRTCDirectTransportCertificateEvents } from './transport.js' -import type { DirectRTCPeerConnection } from './utils/get-rtcpeerconnection.js' import type { StunServer } from './utils/stun-listener.js' import type { PeerId, ListenerEvents, Listener, Upgrader, ComponentLogger, Logger, CounterGroup, Metrics, PrivateKey } from '@libp2p/interface' import type { Keychain } from '@libp2p/keychain' @@ -58,7 +57,7 @@ export class WebRTCDirectListener extends TypedEventEmitter impl private listeningMultiaddr?: Multiaddr private certificate: TransportCertificate private stunServer?: StunServer - private readonly connections: Map + private readonly connections: Map private readonly log: Logger private readonly init: WebRTCDirectListenerInit private readonly components: WebRTCDirectListenerComponents diff --git a/packages/transport-webrtc/src/private-to-public/utils/connect.ts b/packages/transport-webrtc/src/private-to-public/utils/connect.ts index 4538362a35..f99c3ed782 100644 --- a/packages/transport-webrtc/src/private-to-public/utils/connect.ts +++ b/packages/transport-webrtc/src/private-to-public/utils/connect.ts @@ -7,7 +7,7 @@ import { createStream } from '../../stream.js' import { isFirefox } from '../../util.js' import { generateNoisePrologue } from './generate-noise-prologue.js' import * as sdp from './sdp.js' -import type { DirectRTCPeerConnection } from './get-rtcpeerconnection.js' +import { extractRemoteFingerprint } from './get-rtcpeerconnection.js' import type { DataChannelOptions } from '../../index.js' import type { ComponentLogger, Connection, CounterGroup, Logger, PeerId, PrivateKey, Upgrader } from '@libp2p/interface' import type { Multiaddr } from '@multiformats/multiaddr' @@ -36,13 +36,9 @@ export interface ServerOptions extends ConnectOptions { const CONNECTION_STATE_CHANGE_EVENT = isFirefox ? 'iceconnectionstatechange' : 'connectionstatechange' -function isServer (options: ClientOptions | ServerOptions, peerConnection: any): peerConnection is DirectRTCPeerConnection { - return options.role === 'server' -} - export async function connect (peerConnection: RTCPeerConnection, muxerFactory: DataChannelMuxerFactory, ufrag: string, options: ClientOptions): Promise -export async function connect (peerConnection: DirectRTCPeerConnection, muxerFactory: DataChannelMuxerFactory, ufrag: string, options: ServerOptions): Promise -export async function connect (peerConnection: RTCPeerConnection | DirectRTCPeerConnection, muxerFactory: DataChannelMuxerFactory, ufrag: string, options: ClientOptions | ServerOptions): Promise { +export async function connect (peerConnection: RTCPeerConnection, muxerFactory: DataChannelMuxerFactory, ufrag: string, options: ServerOptions): Promise +export async function connect (peerConnection: RTCPeerConnection, muxerFactory: DataChannelMuxerFactory, ufrag: string, options: ClientOptions | ServerOptions): Promise { // create data channel for running the noise handshake. Once the data // channel is opened, the listener will initiate the noise handshake. This // is used to confirm the identity of the peer. @@ -91,10 +87,10 @@ export async function connect (peerConnection: RTCPeerConnection | DirectRTCPeer options.log.trace('%s handshake channel opened', options.role) - if (isServer(options, peerConnection)) { + if (options.role === 'server') { // now that the connection has been opened, add the remote's certhash to // it's multiaddr so we can complete the noise handshake - const remoteFingerprint = peerConnection.remoteFingerprint()?.value ?? '' + const remoteFingerprint = extractRemoteFingerprint(peerConnection) ?? '' options.remoteAddr = options.remoteAddr.encapsulate(sdp.fingerprint2Ma(remoteFingerprint)) } @@ -127,7 +123,6 @@ export async function connect (peerConnection: RTCPeerConnection | DirectRTCPeer // Creating the connection before completion of the noise // handshake ensures that the stream opening callback is set up const maConn = toMultiaddrConnection({ - // @ts-expect-error types are broken peerConnection, remoteAddr: options.remoteAddr, metrics: options.events, diff --git a/packages/transport-webrtc/src/private-to-public/utils/get-rtcpeerconnection.browser.ts b/packages/transport-webrtc/src/private-to-public/utils/get-rtcpeerconnection.browser.ts index f3891e2f4c..c16f81f2ab 100644 --- a/packages/transport-webrtc/src/private-to-public/utils/get-rtcpeerconnection.browser.ts +++ b/packages/transport-webrtc/src/private-to-public/utils/get-rtcpeerconnection.browser.ts @@ -1,6 +1,23 @@ import { DataChannelMuxerFactory } from '../../muxer.ts' import type { CreateDialerRTCPeerConnectionOptions } from './get-rtcpeerconnection.ts' +/** + * Helper to extract remote fingerprint from RTCPeerConnection + * Used by WebRTC Direct server to get remote certificate info + */ +export function extractRemoteFingerprint (pc: RTCPeerConnection): string | undefined { + if (pc.remoteDescription?.sdp == null) { + return undefined + } + + const match = pc.remoteDescription.sdp.match(/a=fingerprint:(\S+)\s+(\S+)/) + if (match != null) { + return match[2] // Return just the fingerprint hash, not the algorithm + } + + return undefined +} + export async function createDialerRTCPeerConnection (role: 'client' | 'server', ufrag: string, options: CreateDialerRTCPeerConnectionOptions = {}): Promise<{ peerConnection: RTCPeerConnection, muxerFactory: DataChannelMuxerFactory }> { // @ts-expect-error options type is wrong let certificate: RTCCertificate = options.certificate diff --git a/packages/transport-webrtc/src/private-to-public/utils/get-rtcpeerconnection.ts b/packages/transport-webrtc/src/private-to-public/utils/get-rtcpeerconnection.ts index 700d288e03..173a824619 100644 --- a/packages/transport-webrtc/src/private-to-public/utils/get-rtcpeerconnection.ts +++ b/packages/transport-webrtc/src/private-to-public/utils/get-rtcpeerconnection.ts @@ -1,92 +1,13 @@ import { Crypto } from '@peculiar/webcrypto' -import { PeerConnection } from 'node-datachannel' -import { RTCPeerConnection } from 'node-datachannel/polyfill' +import { RTCPeerConnection } from '../../webrtc/index.js' import { DEFAULT_ICE_SERVERS, MAX_MESSAGE_SIZE } from '../../constants.js' import { DataChannelMuxerFactory } from '../../muxer.ts' import { generateTransportCertificate } from './generate-certificates.js' import type { DataChannelOptions, TransportCertificate } from '../../index.js' import type { CounterGroup } from '@libp2p/interface' -import type { CertificateFingerprint } from 'node-datachannel' const crypto = new Crypto() -interface DirectRTCPeerConnectionInit extends RTCConfiguration { - ufrag: string - peerConnection: PeerConnection -} - -export class DirectRTCPeerConnection extends RTCPeerConnection { - private peerConnection: PeerConnection - private readonly ufrag: string - - constructor (init: DirectRTCPeerConnectionInit) { - super(init) - - this.peerConnection = init.peerConnection - this.ufrag = init.ufrag - - // make sure C++ peer connection is garbage collected - // https://github.com/murat-dogan/node-datachannel/issues/366#issuecomment-3228453155 - this.addEventListener('connectionstatechange', () => { - switch (this.connectionState) { - case 'closed': - this.peerConnection.close() - break - default: - break - } - }) - } - - async createOffer (): Promise { - // have to set ufrag before creating offer - if (this.connectionState === 'new') { - this.peerConnection?.setLocalDescription('offer', { - iceUfrag: this.ufrag, - icePwd: this.ufrag - }) - } - - return super.createOffer() - } - - async createAnswer (): Promise { - // have to set ufrag before creating answer - if (this.connectionState === 'new') { - this.peerConnection?.setLocalDescription('answer', { - iceUfrag: this.ufrag, - icePwd: this.ufrag - }) - } - - return super.createAnswer() - } - - remoteFingerprint (): CertificateFingerprint { - if (this.peerConnection == null) { - throw new Error('Invalid state: peer connection not set') - } - - return this.peerConnection.remoteFingerprint() - } -} - -function mapIceServers (iceServers?: RTCIceServer[]): string[] { - return iceServers - ?.map((server) => { - const urls = Array.isArray(server.urls) ? server.urls : [server.urls] - - return urls.map((url) => { - if (server.username != null && server.credential != null) { - const [protocol, rest] = url.split(/:(.*)/) - return `${protocol}:${server.username}:${server.credential}@${rest}` - } - return url - }) - }) - .flat() ?? [] -} - export interface CreateDialerRTCPeerConnectionOptions { rtcConfiguration?: RTCConfiguration | (() => RTCConfiguration | Promise) certificate?: TransportCertificate @@ -94,9 +15,24 @@ export interface CreateDialerRTCPeerConnectionOptions { dataChannel?: DataChannelOptions } -export async function createDialerRTCPeerConnection (role: 'client', ufrag: string, options?: CreateDialerRTCPeerConnectionOptions): Promise<{ peerConnection: globalThis.RTCPeerConnection, muxerFactory: DataChannelMuxerFactory }> -export async function createDialerRTCPeerConnection (role: 'server', ufrag: string, options?: CreateDialerRTCPeerConnectionOptions): Promise<{ peerConnection: DirectRTCPeerConnection, muxerFactory: DataChannelMuxerFactory }> -export async function createDialerRTCPeerConnection (role: 'client' | 'server', ufrag: string, options: CreateDialerRTCPeerConnectionOptions = {}): Promise<{ peerConnection: globalThis.RTCPeerConnection | DirectRTCPeerConnection, muxerFactory: DataChannelMuxerFactory }> { +/** + * Helper to extract remote fingerprint from RTCPeerConnection + * Used by WebRTC Direct server to get remote certificate info + */ +export function extractRemoteFingerprint (pc: RTCPeerConnection): string | undefined { + if (pc.remoteDescription?.sdp == null) { + return undefined + } + + const match = pc.remoteDescription.sdp.match(/a=fingerprint:(\S+)\s+(\S+)/) + if (match != null) { + return match[2] // Return just the fingerprint hash, not the algorithm + } + + return undefined +} + +export async function createDialerRTCPeerConnection (role: 'client' | 'server', ufrag: string, options: CreateDialerRTCPeerConnectionOptions = {}): Promise<{ peerConnection: RTCPeerConnection, muxerFactory: DataChannelMuxerFactory }> { if (options.certificate == null) { // ECDSA is preferred over RSA here. From our testing we find that P-256 // elliptic curve is supported by Pion, webrtc-rs, as well as Chromium @@ -114,22 +50,15 @@ export async function createDialerRTCPeerConnection (role: 'client' | 'server', const rtcConfig = typeof options.rtcConfiguration === 'function' ? await options.rtcConfiguration() : options.rtcConfiguration - const peerConnection = new DirectRTCPeerConnection({ + // @roamhq/wrtc uses standard browser-like RTCPeerConnection API + // Certificate is handled differently - wrtc auto-generates certificates + // We'll rely on SDP manipulation for ufrag (done in connect.ts via sdp.munge) + const peerConnection = new RTCPeerConnection({ ...rtcConfig, - ufrag, - peerConnection: new PeerConnection(`${role}-${Date.now()}`, { - disableFingerprintVerification: true, - disableAutoNegotiation: true, - certificatePemFile: options.certificate.pem, - keyPemFile: options.certificate.privateKey, - enableIceUdpMux: role === 'server', - maxMessageSize: MAX_MESSAGE_SIZE, - iceServers: mapIceServers(rtcConfig?.iceServers ?? DEFAULT_ICE_SERVERS.map(urls => ({ urls }))) - }) + iceServers: rtcConfig?.iceServers ?? DEFAULT_ICE_SERVERS.map(urls => ({ urls })) }) const muxerFactory = new DataChannelMuxerFactory({ - // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370 peerConnection, metrics: options.events, dataChannelOptions: options.dataChannel diff --git a/packages/transport-webrtc/src/private-to-public/utils/stun-listener.ts b/packages/transport-webrtc/src/private-to-public/utils/stun-listener.ts index dfbf65c195..9bb8033cf2 100644 --- a/packages/transport-webrtc/src/private-to-public/utils/stun-listener.ts +++ b/packages/transport-webrtc/src/private-to-public/utils/stun-listener.ts @@ -1,8 +1,11 @@ import { isIPv4 } from '@chainsafe/is-ip' -import { IceUdpMuxListener } from 'node-datachannel' +import dgram from 'node:dgram' +import stun from 'stun' import type { Logger } from '@libp2p/interface' import type { AddressInfo } from 'node:net' +const { isStunMessage, decode, createMessage, encode, constants } = stun + export interface StunServer { close(): Promise address(): AddressInfo @@ -12,28 +15,96 @@ export interface Callback { (ufrag: string, remoteHost: string, remotePort: number): void } +/** + * Creates a STUN server listener for WebRTC Direct. + * Uses the 'stun' package to handle STUN protocol messages. + */ export async function stunListener (host: string, port: number, log: Logger, cb: Callback): Promise { - const listener = new IceUdpMuxListener(port, host) - listener.onUnhandledStunRequest(request => { - if (request.ufrag == null) { + const socket = dgram.createSocket(isIPv4(host) ? 'udp4' : 'udp6') + + socket.on('message', (msg: Buffer, rinfo: dgram.RemoteInfo) => { + // Only process STUN messages + if (!isStunMessage(msg)) { + log.trace('received non-STUN packet from %s:%d', rinfo.address, rinfo.port) return } - - log.trace('incoming STUN packet from %s:%d %s', request.host, request.port, request.ufrag) - - cb(request.ufrag, request.host, request.port) + + try { + const request = decode(msg) + + // Extract ufrag from USERNAME attribute + // WebRTC Direct uses ICE username format: "localUfrag:remoteUfrag" or just "ufrag" + let ufrag: string | undefined + + if (request.attributes?.USERNAME != null) { + const username = request.attributes.USERNAME + // ICE username format is "ufrag:password" or just "ufrag" + ufrag = username.split(':')[0] + } + + if (ufrag == null || ufrag === '') { + log.trace('STUN request missing USERNAME/ufrag from %s:%d', rinfo.address, rinfo.port) + return + } + + log.trace('incoming STUN packet from %s:%d ufrag=%s', rinfo.address, rinfo.port, ufrag) + + // Send STUN response with XOR-MAPPED-ADDRESS + try { + const response = createMessage( + constants.STUN_BINDING_RESPONSE, + request.transactionId + ) + + response.addAttribute(constants.STUN_ATTR_XOR_MAPPED_ADDRESS, { + family: isIPv4(rinfo.address) ? 'IPv4' : 'IPv6', + port: rinfo.port, + address: rinfo.address + }) + + const responseBuffer = encode(response) + socket.send(responseBuffer, rinfo.port, rinfo.address, (err) => { + if (err != null) { + log.error('failed to send STUN response to %s:%d - %e', rinfo.address, rinfo.port, err) + } + }) + } catch (err) { + log.error('failed to create STUN response - %e', err) + } + + // Callback to initiate WebRTC connection + cb(ufrag, rinfo.address, rinfo.port) + } catch (err) { + log.error('failed to decode STUN message from %s:%d - %e', rinfo.address, rinfo.port, err) + } }) - + + socket.on('error', (err) => { + log.error('STUN listener error - %e', err) + }) + + // Bind the socket + await new Promise((resolve, reject) => { + socket.once('error', reject) + socket.bind(port, host, () => { + socket.removeListener('error', reject) + resolve() + }) + }) + + const actualAddress = socket.address() as AddressInfo + log('STUN listener bound to %s:%d', actualAddress.address, actualAddress.port) + return { close: async () => { - listener.stop() + await new Promise((resolve) => { + socket.close(() => { + resolve() + }) + }) }, address: () => { - return { - address: host, - family: isIPv4(host) ? 'IPv4' : 'IPv6', - port - } + return socket.address() as AddressInfo } } } diff --git a/packages/transport-webrtc/src/rtcpeerconnection-to-conn.ts b/packages/transport-webrtc/src/rtcpeerconnection-to-conn.ts index 07c7765093..fc213cde5e 100644 --- a/packages/transport-webrtc/src/rtcpeerconnection-to-conn.ts +++ b/packages/transport-webrtc/src/rtcpeerconnection-to-conn.ts @@ -23,9 +23,6 @@ class RTCPeerConnectionMultiaddrConnection extends AbstractMultiaddrConnection { if (this.peerConnection.connectionState === 'disconnected' || this.peerConnection.connectionState === 'failed' || this.peerConnection.connectionState === 'closed') { // nothing else to do but close the connection this.onTransportClosed() - - // only necessary with node-datachannel - // https://github.com/murat-dogan/node-datachannel/issues/366#issuecomment-3228453155 this.peerConnection.close() } } diff --git a/packages/transport-webrtc/src/types/stun.d.ts b/packages/transport-webrtc/src/types/stun.d.ts new file mode 100644 index 0000000000..8bdafa86dc --- /dev/null +++ b/packages/transport-webrtc/src/types/stun.d.ts @@ -0,0 +1,64 @@ +declare module 'stun' { + import type { Socket } from 'node:dgram' + + export interface StunAttribute { + type: number + value: any + } + + export interface StunMessageAttributes { + USERNAME?: string + MESSAGE_INTEGRITY?: Buffer + FINGERPRINT?: number + XOR_MAPPED_ADDRESS?: { + family: string + port: number + address: string + } + [key: string]: any + } + + export interface StunMessage { + type: number + transactionId: Buffer + attributes?: StunMessageAttributes + addAttribute(type: number, value: any): void + } + + export interface StunRequest extends StunMessage {} + export interface StunResponse extends StunMessage {} + + export interface StunServer { + on(event: 'bindingRequest', listener: (request: StunRequest, remote: any) => void): this + on(event: 'bindingIndication', listener: (message: StunMessage, remote: any) => void): this + on(event: 'bindingResponse', listener: (response: StunResponse, remote: any) => void): this + on(event: 'error', listener: (err: Error) => void): this + close(): void + } + + export const constants: { + STUN_BINDING_REQUEST: number + STUN_BINDING_RESPONSE: number + STUN_BINDING_INDICATION: number + STUN_ATTR_USERNAME: number + STUN_ATTR_MESSAGE_INTEGRITY: number + STUN_ATTR_FINGERPRINT: number + STUN_ATTR_XOR_MAPPED_ADDRESS: number + STUN_ATTR_MAPPED_ADDRESS: number + [key: string]: number + } + + export function isStunMessage(buf: Buffer): boolean + export function decode(buf: Buffer): StunMessage + export function encode(message: StunMessage): Buffer + export function createMessage(type: number, transactionId?: Buffer): StunMessage + export function createServer(socket?: Socket): StunServer + export function createTransaction(options: any, cb: (err: Error | null, res?: StunResponse) => void): void + export function request(url: string, options: any, cb: (err: Error | null, res?: StunResponse) => void): void + export function validateFingerprint(message: StunMessage): boolean + export function validateMessageIntegrity(message: StunMessage, key: Buffer): boolean + + export class StunError extends Error {} + export class StunMessageError extends StunError {} + export class StunResponseError extends StunError {} +} diff --git a/packages/transport-webrtc/src/util.ts b/packages/transport-webrtc/src/util.ts index 379c37a070..dc27cf0afc 100644 --- a/packages/transport-webrtc/src/util.ts +++ b/packages/transport-webrtc/src/util.ts @@ -4,7 +4,6 @@ import pTimeout from 'p-timeout' import { DATA_CHANNEL_DRAIN_TIMEOUT, DEFAULT_ICE_SERVERS, UFRAG_ALPHABET, UFRAG_PREFIX } from './constants.js' import type { LoggerOptions } from '@libp2p/interface' import type { Duplex, Source } from 'it-stream-types' -import type { PeerConnection } from 'node-datachannel' const browser = detect() export const isFirefox = ((browser != null) && browser.name === 'firefox') @@ -86,10 +85,6 @@ export interface AbortPromiseOptions { message?: string } -export function isPeerConnection (obj: any): obj is PeerConnection { - return typeof obj.state === 'function' -} - export async function getRtcConfiguration (config?: RTCConfiguration | (() => RTCConfiguration | Promise)): Promise { config = config ?? {} diff --git a/packages/transport-webrtc/src/webrtc/index.ts b/packages/transport-webrtc/src/webrtc/index.ts index 3f1bd1580e..820c6a1d39 100644 --- a/packages/transport-webrtc/src/webrtc/index.ts +++ b/packages/transport-webrtc/src/webrtc/index.ts @@ -1 +1,5 @@ -export { RTCSessionDescription, RTCIceCandidate, RTCPeerConnection } from 'node-datachannel/polyfill' +import wrtc from '@roamhq/wrtc' + +export const RTCSessionDescription = wrtc.RTCSessionDescription +export const RTCIceCandidate = wrtc.RTCIceCandidate +export const RTCPeerConnection = wrtc.RTCPeerConnection diff --git a/packages/transport-webrtc/test/maconn.spec.ts b/packages/transport-webrtc/test/maconn.spec.ts index 6905f5ca47..81c3f8a970 100644 --- a/packages/transport-webrtc/test/maconn.spec.ts +++ b/packages/transport-webrtc/test/maconn.spec.ts @@ -18,7 +18,6 @@ describe('Multiaddr Connection', () => { reset: () => {} }) const maConn = toMultiaddrConnection({ - // @ts-expect-error https://github.com/murat-dogan/node-datachannel/pull/370 peerConnection, remoteAddr, metrics,