werift 0.24.1 → 0.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/dtls/src/context/transport.d.ts +2 -2
- package/lib/dtls/src/context/transport.js +2 -2
- package/lib/dtls/src/context/transport.js.map +1 -1
- package/lib/dtls/src/socket.d.ts +3 -2
- package/lib/dtls/src/socket.js +2 -2
- package/lib/dtls/src/socket.js.map +1 -1
- package/lib/ice/src/ice.d.ts +20 -0
- package/lib/ice/src/ice.js +259 -57
- package/lib/ice/src/ice.js.map +1 -1
- package/lib/ice/src/iceBase.d.ts +22 -1
- package/lib/ice/src/iceBase.js +30 -1
- package/lib/ice/src/iceBase.js.map +1 -1
- package/lib/ice/src/stun/protocol.d.ts +2 -2
- package/lib/ice/src/stun/protocol.js +17 -3
- package/lib/ice/src/stun/protocol.js.map +1 -1
- package/lib/ice/src/stun/tcpProtocol.d.ts +2 -2
- package/lib/ice/src/stun/tcpProtocol.js +13 -3
- package/lib/ice/src/stun/tcpProtocol.js.map +1 -1
- package/lib/ice/src/stun/transaction.d.ts +41 -4
- package/lib/ice/src/stun/transaction.js +193 -24
- package/lib/ice/src/stun/transaction.js.map +1 -1
- package/lib/ice/src/turn/protocol.d.ts +3 -3
- package/lib/ice/src/turn/protocol.js +31 -6
- package/lib/ice/src/turn/protocol.js.map +1 -1
- package/lib/ice/src/types/model.d.ts +27 -1
- package/lib/ice/src/types/model.js.map +1 -1
- package/lib/ice-server/src/stun/message.js +8 -0
- package/lib/ice-server/src/stun/message.js.map +1 -1
- package/lib/index.mjs +435 -104
- package/lib/webrtc/src/peerConnection.d.ts +2 -2
- package/lib/webrtc/src/secureTransportManager.d.ts +1 -1
- package/lib/webrtc/src/secureTransportManager.js +12 -0
- package/lib/webrtc/src/secureTransportManager.js.map +1 -1
- package/lib/webrtc/src/transport/dtls.d.ts +1 -1
- package/package.json +1 -1
|
@@ -96,7 +96,15 @@ class StunProtocol {
|
|
|
96
96
|
message.messageClass === const_1.classes.ERROR) &&
|
|
97
97
|
this.transactionsKeys.includes(message.transactionIdHex)) {
|
|
98
98
|
const transaction = this.transactions[message.transactionIdHex];
|
|
99
|
-
|
|
99
|
+
// Re-parse with integrity key so unauthenticated responses are dropped.
|
|
100
|
+
const verified = transaction.integrityKey
|
|
101
|
+
? (0, message_1.parseMessage)(data, transaction.integrityKey)
|
|
102
|
+
: message;
|
|
103
|
+
if (!verified) {
|
|
104
|
+
log("STUN response failed MESSAGE-INTEGRITY check");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
transaction.responseReceived(verified, addr);
|
|
100
108
|
}
|
|
101
109
|
else if (message.messageClass === const_1.classes.REQUEST) {
|
|
102
110
|
this.onRequestReceived.execute(message, addr, data);
|
|
@@ -125,7 +133,7 @@ class StunProtocol {
|
|
|
125
133
|
}
|
|
126
134
|
await this.transport.send(data, addr);
|
|
127
135
|
}
|
|
128
|
-
async request(request, addr, integrityKey,
|
|
136
|
+
async request(request, addr, integrityKey, retransmissionsOrOptions, onRequestSent) {
|
|
129
137
|
// """
|
|
130
138
|
// Execute a STUN transaction and return the response.
|
|
131
139
|
// """
|
|
@@ -135,7 +143,13 @@ class StunProtocol {
|
|
|
135
143
|
request.addMessageIntegrity(integrityKey);
|
|
136
144
|
request.addFingerprint();
|
|
137
145
|
}
|
|
138
|
-
|
|
146
|
+
// Match DNS family to the bound socket so STUN hostname targets resolve correctly.
|
|
147
|
+
const socketType = this.transport
|
|
148
|
+
.socketType;
|
|
149
|
+
const family = socketType === "udp6" ? 6 : 4;
|
|
150
|
+
const resolvedAddr = await (0, transaction_1.resolveRequestAddress)(addr, family);
|
|
151
|
+
const options = (0, transaction_1.buildTransactionOptions)(integrityKey, retransmissionsOrOptions, onRequestSent);
|
|
152
|
+
const transaction = new transaction_1.Transaction(request, resolvedAddr, this, options);
|
|
139
153
|
this.transactions[request.transactionIdHex] = transaction;
|
|
140
154
|
try {
|
|
141
155
|
return await transaction.run();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../../../ice/src/stun/protocol.ts"],"names":[],"mappings":";;;AAAA,8CAA+D;AAM/D,mCAAkC;AAClC,uCAAuD;AACvD,+
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../../../ice/src/stun/protocol.ts"],"names":[],"mappings":"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","sourcesContent":["import { Event, UdpTransport, debug } from \"../imports/common\";\n\nimport type { Address, InterfaceAddresses } from \"../../../common/src/network\";\nimport type { Candidate } from \"../candidate\";\n\nimport type { Protocol, TransactionRequestOptions } from \"../types/model\";\nimport { classes } from \"./const\";\nimport { type Message, parseMessage } from \"./message\";\nimport {\n Transaction,\n buildTransactionOptions,\n resolveRequestAddress,\n} from \"./transaction\";\n\nconst log = debug(\"werift-ice : packages/ice/src/stun/protocol.ts\");\n\nexport class StunProtocol implements Protocol {\n static readonly type = \"stun\";\n readonly type = StunProtocol.type;\n transport!: UdpTransport;\n transactions: { [key: string]: Transaction } = {};\n get transactionsKeys() {\n return Object.keys(this.transactions);\n }\n localCandidate?: Candidate;\n sentMessage?: Message;\n localIp?: string;\n\n readonly onRequestReceived = new Event<[Message, Address, Buffer]>();\n readonly onDataReceived = new Event<[Buffer]>();\n\n constructor() {}\n\n connectionMade = async (\n useIpv4: boolean,\n portRange?: [number, number],\n interfaceAddresses?: InterfaceAddresses,\n ) => {\n if (useIpv4) {\n this.transport = await UdpTransport.init(\"udp4\", {\n portRange,\n interfaceAddresses,\n });\n } else {\n this.transport = await UdpTransport.init(\"udp6\", {\n portRange,\n interfaceAddresses,\n });\n }\n\n this.transport.onData = (data, addr) => {\n this.datagramReceived(data, addr);\n };\n };\n\n private datagramReceived(data: Buffer, addr: Address) {\n try {\n const message = parseMessage(data);\n if (!message) {\n if (this.localCandidate) {\n this.onDataReceived.execute(data);\n }\n return;\n }\n // log(\"parseMessage\", addr, message.toJSON());\n if (\n (message.messageClass === classes.RESPONSE ||\n message.messageClass === classes.ERROR) &&\n this.transactionsKeys.includes(message.transactionIdHex)\n ) {\n const transaction = this.transactions[message.transactionIdHex];\n // Re-parse with integrity key so unauthenticated responses are dropped.\n const verified = transaction.integrityKey\n ? parseMessage(data, transaction.integrityKey)\n : message;\n if (!verified) {\n log(\"STUN response failed MESSAGE-INTEGRITY check\");\n return;\n }\n transaction.responseReceived(verified, addr);\n } else if (message.messageClass === classes.REQUEST) {\n this.onRequestReceived.execute(message, addr, data);\n }\n } catch (error) {\n log(\"datagramReceived error\", error);\n }\n }\n\n getExtraInfo(): Address {\n const { address: host, port } = this.transport.address;\n return [host, port];\n }\n\n async sendStun(message: Message, addr: Address) {\n if (this.transport.closed) {\n return;\n }\n\n const data = message.bytes;\n await this.transport.send(data, addr).catch(() => {\n log(\"sendStun failed\", addr, message);\n });\n }\n\n async sendData(data: Buffer, addr: Address) {\n if (this.transport.closed) {\n return;\n }\n\n await this.transport.send(data, addr);\n }\n\n async request(\n request: Message,\n addr: Address,\n integrityKey?: Buffer,\n retransmissionsOrOptions?: number | TransactionRequestOptions,\n onRequestSent?: (attempt: number) => void,\n ) {\n // \"\"\"\n // Execute a STUN transaction and return the response.\n // \"\"\"\n if (this.transactionsKeys.includes(request.transactionIdHex))\n throw new Error(\"already request ed\");\n\n if (integrityKey) {\n request.addMessageIntegrity(integrityKey);\n request.addFingerprint();\n }\n\n // Match DNS family to the bound socket so STUN hostname targets resolve correctly.\n const socketType = (this.transport as unknown as { socketType?: string })\n .socketType;\n const family: 4 | 6 = socketType === \"udp6\" ? 6 : 4;\n const resolvedAddr = await resolveRequestAddress(addr, family);\n\n const options = buildTransactionOptions(\n integrityKey,\n retransmissionsOrOptions,\n onRequestSent,\n );\n const transaction: Transaction = new Transaction(\n request,\n resolvedAddr,\n this,\n options,\n );\n this.transactions[request.transactionIdHex] = transaction;\n\n try {\n return await transaction.run();\n } catch (e) {\n throw e;\n } finally {\n delete this.transactions[request.transactionIdHex];\n }\n }\n\n async close() {\n Object.values(this.transactions).forEach((transaction) => {\n transaction.cancel();\n });\n await this.transport.close();\n this.onRequestReceived.complete();\n this.onDataReceived.complete();\n }\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type AddressInfo, type Socket } from "node:net";
|
|
2
2
|
import type { Candidate } from "../candidate";
|
|
3
3
|
import { type Address, Event } from "../imports/common";
|
|
4
|
-
import type { Protocol } from "../types/model";
|
|
4
|
+
import type { Protocol, TransactionRequestOptions } from "../types/model";
|
|
5
5
|
import { type Message } from "./message";
|
|
6
6
|
import { Transaction } from "./transaction";
|
|
7
7
|
type SocketEntry = {
|
|
@@ -30,7 +30,7 @@ declare abstract class BaseTcpProtocol implements Protocol {
|
|
|
30
30
|
private sendFrame;
|
|
31
31
|
sendStun(message: Message, addr: Address): Promise<void>;
|
|
32
32
|
sendData(data: Buffer, addr: Address): Promise<void>;
|
|
33
|
-
request(request: Message, addr: Address, integrityKey?: Buffer,
|
|
33
|
+
request(request: Message, addr: Address, integrityKey?: Buffer, retransmissionsOrOptions?: number | TransactionRequestOptions, onRequestSent?: (attempt: number) => void): Promise<[Message, readonly [string, number]]>;
|
|
34
34
|
pruneForSelection(remoteAddr?: Address): Promise<void>;
|
|
35
35
|
get activeSocketCount(): number;
|
|
36
36
|
get address(): AddressInfo;
|
|
@@ -146,7 +146,15 @@ class BaseTcpProtocol {
|
|
|
146
146
|
if ((message.messageClass === const_1.classes.RESPONSE ||
|
|
147
147
|
message.messageClass === const_1.classes.ERROR) &&
|
|
148
148
|
this.transactions[message.transactionIdHex]) {
|
|
149
|
-
this.transactions[message.transactionIdHex]
|
|
149
|
+
const transaction = this.transactions[message.transactionIdHex];
|
|
150
|
+
const verified = transaction.integrityKey
|
|
151
|
+
? (0, message_1.parseMessage)(data, transaction.integrityKey)
|
|
152
|
+
: message;
|
|
153
|
+
if (!verified) {
|
|
154
|
+
log("STUN response failed MESSAGE-INTEGRITY check");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
transaction.responseReceived(verified, addr);
|
|
150
158
|
}
|
|
151
159
|
else if (message.messageClass === const_1.classes.REQUEST) {
|
|
152
160
|
this.onRequestReceived.execute(message, addr, data);
|
|
@@ -174,7 +182,7 @@ class BaseTcpProtocol {
|
|
|
174
182
|
async sendData(data, addr) {
|
|
175
183
|
await this.sendFrame(data, addr);
|
|
176
184
|
}
|
|
177
|
-
async request(request, addr, integrityKey,
|
|
185
|
+
async request(request, addr, integrityKey, retransmissionsOrOptions, onRequestSent) {
|
|
178
186
|
if (this.transactions[request.transactionIdHex]) {
|
|
179
187
|
throw new Error("already requested");
|
|
180
188
|
}
|
|
@@ -182,7 +190,9 @@ class BaseTcpProtocol {
|
|
|
182
190
|
request.addMessageIntegrity(integrityKey);
|
|
183
191
|
request.addFingerprint();
|
|
184
192
|
}
|
|
185
|
-
const
|
|
193
|
+
const resolvedAddr = await (0, transaction_1.resolveRequestAddress)(addr);
|
|
194
|
+
const options = (0, transaction_1.buildTransactionOptions)(integrityKey, retransmissionsOrOptions, onRequestSent);
|
|
195
|
+
const transaction = new transaction_1.Transaction(request, resolvedAddr, this, options);
|
|
186
196
|
this.transactions[request.transactionIdHex] = transaction;
|
|
187
197
|
try {
|
|
188
198
|
return await transaction.run();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tcpProtocol.js","sourceRoot":"","sources":["../../../../../ice/src/stun/tcpProtocol.ts"],"names":[],"mappings":"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","sourcesContent":["import { type AddressInfo, type Socket, connect, createServer } from \"node:net\";\n\nimport type { Candidate } from \"../candidate\";\nimport { type Address, Event, debug } from \"../imports/common\";\nimport type { Protocol } from \"../types/model\";\nimport { classes } from \"./const\";\nimport { type Message, parseMessage } from \"./message\";\nimport { encodeTcpFrame, splitTcpFrames } from \"./tcpFrame\";\nimport { Transaction } from \"./transaction\";\n\nconst log = debug(\"werift-ice:packages/ice/src/stun/tcpProtocol.ts\");\n\ntype SocketEntry = {\n socket: Socket;\n buffer: Buffer;\n remoteAddr?: Address;\n};\n\nfunction socketKey(addr: Address) {\n return `${addr[0]}:${addr[1]}`;\n}\n\nfunction addressFromSocket(socket: Socket) {\n if (!socket.remoteAddress || !socket.remotePort) {\n return;\n }\n return [socket.remoteAddress, socket.remotePort] as Address;\n}\n\nasync function waitForListening(\n server: ReturnType<typeof createServer>,\n host: string,\n port?: number,\n) {\n return await new Promise<void>((resolve, reject) => {\n const onError = (error: Error) => {\n server.off(\"listening\", onListening);\n reject(error);\n };\n const onListening = () => {\n server.off(\"error\", onError);\n resolve();\n };\n\n server.once(\"error\", onError);\n server.once(\"listening\", onListening);\n server.listen({\n host,\n port: port ?? 0,\n exclusive: true,\n });\n });\n}\n\nabstract class BaseTcpProtocol implements Protocol {\n static readonly type = \"tcp\";\n readonly type = BaseTcpProtocol.type;\n transactions: { [key: string]: Transaction } = {};\n localCandidate?: Candidate;\n sentMessage?: Message;\n localIp?: string;\n\n readonly onRequestReceived = new Event<[Message, Address, Buffer]>();\n readonly onDataReceived = new Event<[Buffer]>();\n\n protected readonly sockets = new Map<string, SocketEntry>();\n\n abstract connectionMade(...args: any[]): Promise<void>;\n protected abstract getSocket(addr: Address): Promise<SocketEntry>;\n\n protected rememberSocket(entry: SocketEntry) {\n const remoteAddr = entry.remoteAddr;\n if (!remoteAddr) {\n return;\n }\n\n this.sockets.set(socketKey(remoteAddr), entry);\n }\n\n protected forgetSocket(remoteAddr?: Address) {\n if (!remoteAddr) {\n return;\n }\n this.sockets.delete(socketKey(remoteAddr));\n }\n\n protected registerSocket(socket: Socket, remoteAddr?: Address) {\n const entry: SocketEntry = {\n socket,\n buffer: Buffer.alloc(0),\n remoteAddr,\n };\n\n if (remoteAddr) {\n this.rememberSocket(entry);\n }\n\n socket.on(\"data\", (data) => {\n entry.buffer = Buffer.concat([entry.buffer, data]);\n const { frames, rest } = splitTcpFrames(entry.buffer);\n entry.buffer = rest;\n\n const socketAddr = entry.remoteAddr ?? addressFromSocket(socket);\n if (!entry.remoteAddr && socketAddr) {\n entry.remoteAddr = socketAddr;\n this.rememberSocket(entry);\n }\n if (!socketAddr) {\n return;\n }\n\n for (const frame of frames) {\n if (frame.length === 0) {\n continue;\n }\n this.handleFrame(frame, socketAddr);\n }\n });\n socket.on(\"close\", () => {\n this.forgetSocket(entry.remoteAddr);\n });\n socket.on(\"error\", (error) => {\n log(\"tcp socket error\", error);\n });\n\n return entry;\n }\n\n private handleFrame(data: Buffer, addr: Address) {\n try {\n const message = parseMessage(data);\n if (!message) {\n this.onDataReceived.execute(data);\n return;\n }\n\n if (\n (message.messageClass === classes.RESPONSE ||\n message.messageClass === classes.ERROR) &&\n this.transactions[message.transactionIdHex]\n ) {\n this.transactions[message.transactionIdHex].responseReceived(\n message,\n addr,\n );\n } else if (message.messageClass === classes.REQUEST) {\n this.onRequestReceived.execute(message, addr, data);\n }\n } catch (error) {\n log(\"tcp frame parse error\", error);\n }\n }\n\n private async sendFrame(data: Buffer, addr: Address) {\n const entry = await this.getSocket(addr);\n await new Promise<void>((resolve, reject) => {\n entry.socket.write(encodeTcpFrame(data), (error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n }\n\n async sendStun(message: Message, addr: Address) {\n await this.sendFrame(message.bytes, addr);\n }\n\n async sendData(data: Buffer, addr: Address) {\n await this.sendFrame(data, addr);\n }\n\n async request(\n request: Message,\n addr: Address,\n integrityKey?: Buffer,\n retransmissions?: number,\n onRequestSent?: (attempt: number) => void,\n ) {\n if (this.transactions[request.transactionIdHex]) {\n throw new Error(\"already requested\");\n }\n\n if (integrityKey) {\n request.addMessageIntegrity(integrityKey);\n request.addFingerprint();\n }\n\n const transaction = new Transaction(\n request,\n addr,\n this,\n retransmissions,\n onRequestSent,\n );\n this.transactions[request.transactionIdHex] = transaction;\n\n try {\n return await transaction.run();\n } finally {\n delete this.transactions[request.transactionIdHex];\n }\n }\n\n async pruneForSelection(remoteAddr?: Address) {\n for (const [key, entry] of this.sockets.entries()) {\n if (remoteAddr && key === socketKey(remoteAddr)) {\n continue;\n }\n entry.socket.destroy();\n this.sockets.delete(key);\n }\n }\n\n get activeSocketCount() {\n return this.sockets.size;\n }\n\n get address() {\n return {} as AddressInfo;\n }\n\n async close() {\n Object.values(this.transactions).forEach((transaction) => {\n transaction.cancel();\n });\n\n await this.pruneForSelection();\n this.onRequestReceived.complete();\n this.onDataReceived.complete();\n }\n}\n\nexport class TcpActiveProtocol extends BaseTcpProtocol {\n private readonly pendingSockets = new Map<string, Promise<SocketEntry>>();\n\n async connectionMade(localIp: string) {\n this.localIp = localIp;\n }\n\n protected async getSocket(addr: Address) {\n const key = socketKey(addr);\n const existing = this.sockets.get(key);\n if (existing) {\n return existing;\n }\n\n const pending = this.pendingSockets.get(key);\n if (pending) {\n return await pending;\n }\n\n const connecting = new Promise<SocketEntry>((resolve, reject) => {\n const socket = connect({\n host: addr[0],\n port: addr[1],\n localAddress: this.localIp,\n });\n const entry = this.registerSocket(socket, addr);\n\n const onError = (error: Error) => {\n socket.off(\"connect\", onConnect);\n reject(error);\n };\n const onConnect = () => {\n socket.off(\"error\", onError);\n resolve(entry);\n };\n\n socket.once(\"error\", onError);\n socket.once(\"connect\", onConnect);\n });\n\n this.pendingSockets.set(key, connecting);\n\n try {\n return await connecting;\n } finally {\n this.pendingSockets.delete(key);\n }\n }\n}\n\nexport class TcpPassiveProtocol extends BaseTcpProtocol {\n private server = createServer((socket) => {\n this.registerSocket(socket, addressFromSocket(socket));\n });\n\n async connectionMade(localIp: string, portRange?: [number, number]) {\n this.localIp = localIp;\n\n if (portRange) {\n let lastError: Error | undefined;\n for (let port = portRange[0]; port <= portRange[1]; port++) {\n try {\n await waitForListening(this.server, localIp, port);\n return;\n } catch (error) {\n lastError = error as Error;\n }\n }\n throw lastError ?? new Error(\"tcp port not found\");\n }\n\n await waitForListening(this.server, localIp);\n }\n\n protected async getSocket(addr: Address) {\n const entry = this.sockets.get(socketKey(addr));\n if (!entry) {\n throw new Error(\"tcp passive connection not established\");\n }\n return entry;\n }\n\n get listeningPort() {\n const address = this.server.address();\n if (!address || typeof address === \"string\") {\n throw new Error(\"tcp passive protocol is not listening\");\n }\n return address.port;\n }\n\n override async close() {\n await super.close();\n await new Promise<void>((resolve) => {\n this.server.close(() => resolve());\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tcpProtocol.js","sourceRoot":"","sources":["../../../../../ice/src/stun/tcpProtocol.ts"],"names":[],"mappings":"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","sourcesContent":["import { type AddressInfo, type Socket, connect, createServer } from \"node:net\";\n\nimport type { Candidate } from \"../candidate\";\nimport { type Address, Event, debug } from \"../imports/common\";\nimport type { Protocol, TransactionRequestOptions } from \"../types/model\";\nimport { classes } from \"./const\";\nimport { type Message, parseMessage } from \"./message\";\nimport { encodeTcpFrame, splitTcpFrames } from \"./tcpFrame\";\nimport {\n Transaction,\n buildTransactionOptions,\n resolveRequestAddress,\n} from \"./transaction\";\n\nconst log = debug(\"werift-ice:packages/ice/src/stun/tcpProtocol.ts\");\n\ntype SocketEntry = {\n socket: Socket;\n buffer: Buffer;\n remoteAddr?: Address;\n};\n\nfunction socketKey(addr: Address) {\n return `${addr[0]}:${addr[1]}`;\n}\n\nfunction addressFromSocket(socket: Socket) {\n if (!socket.remoteAddress || !socket.remotePort) {\n return;\n }\n return [socket.remoteAddress, socket.remotePort] as Address;\n}\n\nasync function waitForListening(\n server: ReturnType<typeof createServer>,\n host: string,\n port?: number,\n) {\n return await new Promise<void>((resolve, reject) => {\n const onError = (error: Error) => {\n server.off(\"listening\", onListening);\n reject(error);\n };\n const onListening = () => {\n server.off(\"error\", onError);\n resolve();\n };\n\n server.once(\"error\", onError);\n server.once(\"listening\", onListening);\n server.listen({\n host,\n port: port ?? 0,\n exclusive: true,\n });\n });\n}\n\nabstract class BaseTcpProtocol implements Protocol {\n static readonly type = \"tcp\";\n readonly type = BaseTcpProtocol.type;\n transactions: { [key: string]: Transaction } = {};\n localCandidate?: Candidate;\n sentMessage?: Message;\n localIp?: string;\n\n readonly onRequestReceived = new Event<[Message, Address, Buffer]>();\n readonly onDataReceived = new Event<[Buffer]>();\n\n protected readonly sockets = new Map<string, SocketEntry>();\n\n abstract connectionMade(...args: any[]): Promise<void>;\n protected abstract getSocket(addr: Address): Promise<SocketEntry>;\n\n protected rememberSocket(entry: SocketEntry) {\n const remoteAddr = entry.remoteAddr;\n if (!remoteAddr) {\n return;\n }\n\n this.sockets.set(socketKey(remoteAddr), entry);\n }\n\n protected forgetSocket(remoteAddr?: Address) {\n if (!remoteAddr) {\n return;\n }\n this.sockets.delete(socketKey(remoteAddr));\n }\n\n protected registerSocket(socket: Socket, remoteAddr?: Address) {\n const entry: SocketEntry = {\n socket,\n buffer: Buffer.alloc(0),\n remoteAddr,\n };\n\n if (remoteAddr) {\n this.rememberSocket(entry);\n }\n\n socket.on(\"data\", (data) => {\n entry.buffer = Buffer.concat([entry.buffer, data]);\n const { frames, rest } = splitTcpFrames(entry.buffer);\n entry.buffer = rest;\n\n const socketAddr = entry.remoteAddr ?? addressFromSocket(socket);\n if (!entry.remoteAddr && socketAddr) {\n entry.remoteAddr = socketAddr;\n this.rememberSocket(entry);\n }\n if (!socketAddr) {\n return;\n }\n\n for (const frame of frames) {\n if (frame.length === 0) {\n continue;\n }\n this.handleFrame(frame, socketAddr);\n }\n });\n socket.on(\"close\", () => {\n this.forgetSocket(entry.remoteAddr);\n });\n socket.on(\"error\", (error) => {\n log(\"tcp socket error\", error);\n });\n\n return entry;\n }\n\n private handleFrame(data: Buffer, addr: Address) {\n try {\n const message = parseMessage(data);\n if (!message) {\n this.onDataReceived.execute(data);\n return;\n }\n\n if (\n (message.messageClass === classes.RESPONSE ||\n message.messageClass === classes.ERROR) &&\n this.transactions[message.transactionIdHex]\n ) {\n const transaction = this.transactions[message.transactionIdHex];\n const verified = transaction.integrityKey\n ? parseMessage(data, transaction.integrityKey)\n : message;\n if (!verified) {\n log(\"STUN response failed MESSAGE-INTEGRITY check\");\n return;\n }\n transaction.responseReceived(verified, addr);\n } else if (message.messageClass === classes.REQUEST) {\n this.onRequestReceived.execute(message, addr, data);\n }\n } catch (error) {\n log(\"tcp frame parse error\", error);\n }\n }\n\n private async sendFrame(data: Buffer, addr: Address) {\n const entry = await this.getSocket(addr);\n await new Promise<void>((resolve, reject) => {\n entry.socket.write(encodeTcpFrame(data), (error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n }\n\n async sendStun(message: Message, addr: Address) {\n await this.sendFrame(message.bytes, addr);\n }\n\n async sendData(data: Buffer, addr: Address) {\n await this.sendFrame(data, addr);\n }\n\n async request(\n request: Message,\n addr: Address,\n integrityKey?: Buffer,\n retransmissionsOrOptions?: number | TransactionRequestOptions,\n onRequestSent?: (attempt: number) => void,\n ) {\n if (this.transactions[request.transactionIdHex]) {\n throw new Error(\"already requested\");\n }\n\n if (integrityKey) {\n request.addMessageIntegrity(integrityKey);\n request.addFingerprint();\n }\n\n const resolvedAddr = await resolveRequestAddress(addr);\n const options = buildTransactionOptions(\n integrityKey,\n retransmissionsOrOptions,\n onRequestSent,\n );\n const transaction = new Transaction(request, resolvedAddr, this, options);\n this.transactions[request.transactionIdHex] = transaction;\n\n try {\n return await transaction.run();\n } finally {\n delete this.transactions[request.transactionIdHex];\n }\n }\n\n async pruneForSelection(remoteAddr?: Address) {\n for (const [key, entry] of this.sockets.entries()) {\n if (remoteAddr && key === socketKey(remoteAddr)) {\n continue;\n }\n entry.socket.destroy();\n this.sockets.delete(key);\n }\n }\n\n get activeSocketCount() {\n return this.sockets.size;\n }\n\n get address() {\n return {} as AddressInfo;\n }\n\n async close() {\n Object.values(this.transactions).forEach((transaction) => {\n transaction.cancel();\n });\n\n await this.pruneForSelection();\n this.onRequestReceived.complete();\n this.onDataReceived.complete();\n }\n}\n\nexport class TcpActiveProtocol extends BaseTcpProtocol {\n private readonly pendingSockets = new Map<string, Promise<SocketEntry>>();\n\n async connectionMade(localIp: string) {\n this.localIp = localIp;\n }\n\n protected async getSocket(addr: Address) {\n const key = socketKey(addr);\n const existing = this.sockets.get(key);\n if (existing) {\n return existing;\n }\n\n const pending = this.pendingSockets.get(key);\n if (pending) {\n return await pending;\n }\n\n const connecting = new Promise<SocketEntry>((resolve, reject) => {\n const socket = connect({\n host: addr[0],\n port: addr[1],\n localAddress: this.localIp,\n });\n const entry = this.registerSocket(socket, addr);\n\n const onError = (error: Error) => {\n socket.off(\"connect\", onConnect);\n reject(error);\n };\n const onConnect = () => {\n socket.off(\"error\", onError);\n resolve(entry);\n };\n\n socket.once(\"error\", onError);\n socket.once(\"connect\", onConnect);\n });\n\n this.pendingSockets.set(key, connecting);\n\n try {\n return await connecting;\n } finally {\n this.pendingSockets.delete(key);\n }\n }\n}\n\nexport class TcpPassiveProtocol extends BaseTcpProtocol {\n private server = createServer((socket) => {\n this.registerSocket(socket, addressFromSocket(socket));\n });\n\n async connectionMade(localIp: string, portRange?: [number, number]) {\n this.localIp = localIp;\n\n if (portRange) {\n let lastError: Error | undefined;\n for (let port = portRange[0]; port <= portRange[1]; port++) {\n try {\n await waitForListening(this.server, localIp, port);\n return;\n } catch (error) {\n lastError = error as Error;\n }\n }\n throw lastError ?? new Error(\"tcp port not found\");\n }\n\n await waitForListening(this.server, localIp);\n }\n\n protected async getSocket(addr: Address) {\n const entry = this.sockets.get(socketKey(addr));\n if (!entry) {\n throw new Error(\"tcp passive connection not established\");\n }\n return entry;\n }\n\n get listeningPort() {\n const address = this.server.address();\n if (!address || typeof address === \"string\") {\n throw new Error(\"tcp passive protocol is not listening\");\n }\n return address.port;\n }\n\n override async close() {\n await super.close();\n await new Promise<void>((resolve) => {\n this.server.close(() => resolve());\n });\n }\n}\n"]}
|
|
@@ -1,20 +1,57 @@
|
|
|
1
1
|
import { type Address } from "../imports/common";
|
|
2
|
-
import type { Protocol } from "../types/model";
|
|
2
|
+
import type { Protocol, TransactionRequestOptions } from "../types/model";
|
|
3
3
|
import type { Message } from "./message";
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a request target to a concrete IP before creating a Transaction so
|
|
6
|
+
* response source-address checks match the UDP peer address (hostname ≠ IP).
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveRequestAddress(addr: Address, family?: 0 | 4 | 6): Promise<Address>;
|
|
9
|
+
/**
|
|
10
|
+
* Normalize legacy positional args and the options-object form into one shape.
|
|
11
|
+
* Existing callers pass `(retransmissions?, onRequestSent?)`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function normalizeTransactionOptions(retransmissionsOrOptions?: number | TransactionRequestOptions, onRequestSent?: (attempt: number) => void): TransactionRequestOptions;
|
|
14
|
+
/** Compare ICE transport addresses (host, port). */
|
|
15
|
+
export declare function addressEquals(a: Address, b: Address): boolean;
|
|
4
16
|
export declare class Transaction {
|
|
5
17
|
private request;
|
|
6
18
|
private addr;
|
|
7
19
|
private protocol;
|
|
8
|
-
private retransmissions?;
|
|
9
|
-
private onRequestSent?;
|
|
10
20
|
private timeoutDelay;
|
|
11
21
|
ended: boolean;
|
|
12
22
|
private tries;
|
|
13
23
|
private readonly triesMax;
|
|
14
24
|
private readonly onResponse;
|
|
15
|
-
|
|
25
|
+
private readonly onRequestSent?;
|
|
26
|
+
private readonly signal?;
|
|
27
|
+
/** Remote address this transaction was sent to; responses must match. */
|
|
28
|
+
readonly expectedAddr: Address;
|
|
29
|
+
/**
|
|
30
|
+
* When set, protocol layers re-parse the wire response with this key so
|
|
31
|
+
* MESSAGE-INTEGRITY failures are rejected before responseReceived.
|
|
32
|
+
*/
|
|
33
|
+
readonly integrityKey?: Buffer;
|
|
34
|
+
private waitTimer?;
|
|
35
|
+
private waitResolve?;
|
|
36
|
+
private onAbort?;
|
|
37
|
+
constructor(request: Message, addr: Address, protocol: Protocol, retransmissionsOrOptions?: number | TransactionRequestOptions, onRequestSent?: (attempt: number) => void);
|
|
38
|
+
/**
|
|
39
|
+
* Accept a matching authenticated non-error response from the expected
|
|
40
|
+
* remote address. Wrong address, missing MESSAGE-INTEGRITY (when required),
|
|
41
|
+
* or non-success class is rejected without completing the transaction
|
|
42
|
+
* (wrong address / unauthenticated responses are ignored so we keep waiting).
|
|
43
|
+
*/
|
|
16
44
|
responseReceived: (message: Message, addr: Address) => void;
|
|
17
45
|
run: () => Promise<[Message, readonly [string, number]]>;
|
|
46
|
+
private attachAbortListener;
|
|
47
|
+
private failWithTimeout;
|
|
48
|
+
private clearWait;
|
|
49
|
+
private wait;
|
|
18
50
|
private retry;
|
|
19
51
|
cancel(): void;
|
|
20
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Build Transaction options for Protocol.request, folding integrityKey into
|
|
55
|
+
* options so response path can re-verify MESSAGE-INTEGRITY.
|
|
56
|
+
*/
|
|
57
|
+
export declare function buildTransactionOptions(integrityKey: Buffer | undefined, retransmissionsOrOptions?: number | TransactionRequestOptions, onRequestSent?: (attempt: number) => void): TransactionRequestOptions;
|
|
@@ -1,12 +1,51 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Transaction = void 0;
|
|
4
|
+
exports.resolveRequestAddress = resolveRequestAddress;
|
|
5
|
+
exports.normalizeTransactionOptions = normalizeTransactionOptions;
|
|
6
|
+
exports.addressEquals = addressEquals;
|
|
7
|
+
exports.buildTransactionOptions = buildTransactionOptions;
|
|
8
|
+
const node_dns_1 = require("node:dns");
|
|
9
|
+
const node_net_1 = require("node:net");
|
|
4
10
|
const common_1 = require("../imports/common");
|
|
5
11
|
const exceptions_1 = require("../exceptions");
|
|
6
12
|
const const_1 = require("./const");
|
|
7
13
|
const log = (0, common_1.debug)("werift-ice:packages/ice/src/stun/transaction.ts");
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a request target to a concrete IP before creating a Transaction so
|
|
16
|
+
* response source-address checks match the UDP peer address (hostname ≠ IP).
|
|
17
|
+
*/
|
|
18
|
+
async function resolveRequestAddress(addr, family = 0) {
|
|
19
|
+
if ((0, node_net_1.isIP)(addr[0])) {
|
|
20
|
+
return addr;
|
|
21
|
+
}
|
|
22
|
+
const looked = await node_dns_1.promises.lookup(addr[0], { family });
|
|
23
|
+
return [looked.address, addr[1]];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Normalize legacy positional args and the options-object form into one shape.
|
|
27
|
+
* Existing callers pass `(retransmissions?, onRequestSent?)`.
|
|
28
|
+
*/
|
|
29
|
+
function normalizeTransactionOptions(retransmissionsOrOptions, onRequestSent) {
|
|
30
|
+
if (retransmissionsOrOptions !== null &&
|
|
31
|
+
typeof retransmissionsOrOptions === "object") {
|
|
32
|
+
return retransmissionsOrOptions;
|
|
33
|
+
}
|
|
34
|
+
// After the object branch, only number | undefined remains (legacy positional API).
|
|
35
|
+
const retransmissions = typeof retransmissionsOrOptions === "number"
|
|
36
|
+
? retransmissionsOrOptions
|
|
37
|
+
: undefined;
|
|
38
|
+
return {
|
|
39
|
+
retransmissions,
|
|
40
|
+
onRequestSent,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** Compare ICE transport addresses (host, port). */
|
|
44
|
+
function addressEquals(a, b) {
|
|
45
|
+
return a[0] === b[0] && a[1] === b[1];
|
|
46
|
+
}
|
|
8
47
|
class Transaction {
|
|
9
|
-
constructor(request, addr, protocol,
|
|
48
|
+
constructor(request, addr, protocol, retransmissionsOrOptions, onRequestSent) {
|
|
10
49
|
Object.defineProperty(this, "request", {
|
|
11
50
|
enumerable: true,
|
|
12
51
|
configurable: true,
|
|
@@ -25,62 +64,122 @@ class Transaction {
|
|
|
25
64
|
writable: true,
|
|
26
65
|
value: protocol
|
|
27
66
|
});
|
|
28
|
-
Object.defineProperty(this, "
|
|
67
|
+
Object.defineProperty(this, "timeoutDelay", {
|
|
68
|
+
enumerable: true,
|
|
69
|
+
configurable: true,
|
|
70
|
+
writable: true,
|
|
71
|
+
value: void 0
|
|
72
|
+
});
|
|
73
|
+
Object.defineProperty(this, "ended", {
|
|
74
|
+
enumerable: true,
|
|
75
|
+
configurable: true,
|
|
76
|
+
writable: true,
|
|
77
|
+
value: false
|
|
78
|
+
});
|
|
79
|
+
Object.defineProperty(this, "tries", {
|
|
80
|
+
enumerable: true,
|
|
81
|
+
configurable: true,
|
|
82
|
+
writable: true,
|
|
83
|
+
value: 0
|
|
84
|
+
});
|
|
85
|
+
Object.defineProperty(this, "triesMax", {
|
|
86
|
+
enumerable: true,
|
|
87
|
+
configurable: true,
|
|
88
|
+
writable: true,
|
|
89
|
+
value: void 0
|
|
90
|
+
});
|
|
91
|
+
Object.defineProperty(this, "onResponse", {
|
|
29
92
|
enumerable: true,
|
|
30
93
|
configurable: true,
|
|
31
94
|
writable: true,
|
|
32
|
-
value:
|
|
95
|
+
value: new common_1.Event()
|
|
33
96
|
});
|
|
34
97
|
Object.defineProperty(this, "onRequestSent", {
|
|
35
98
|
enumerable: true,
|
|
36
99
|
configurable: true,
|
|
37
100
|
writable: true,
|
|
38
|
-
value:
|
|
101
|
+
value: void 0
|
|
102
|
+
});
|
|
103
|
+
Object.defineProperty(this, "signal", {
|
|
104
|
+
enumerable: true,
|
|
105
|
+
configurable: true,
|
|
106
|
+
writable: true,
|
|
107
|
+
value: void 0
|
|
39
108
|
});
|
|
40
|
-
|
|
109
|
+
/** Remote address this transaction was sent to; responses must match. */
|
|
110
|
+
Object.defineProperty(this, "expectedAddr", {
|
|
41
111
|
enumerable: true,
|
|
42
112
|
configurable: true,
|
|
43
113
|
writable: true,
|
|
44
|
-
value:
|
|
114
|
+
value: void 0
|
|
45
115
|
});
|
|
46
|
-
|
|
116
|
+
/**
|
|
117
|
+
* When set, protocol layers re-parse the wire response with this key so
|
|
118
|
+
* MESSAGE-INTEGRITY failures are rejected before responseReceived.
|
|
119
|
+
*/
|
|
120
|
+
Object.defineProperty(this, "integrityKey", {
|
|
47
121
|
enumerable: true,
|
|
48
122
|
configurable: true,
|
|
49
123
|
writable: true,
|
|
50
|
-
value:
|
|
124
|
+
value: void 0
|
|
51
125
|
});
|
|
52
|
-
Object.defineProperty(this, "
|
|
126
|
+
Object.defineProperty(this, "waitTimer", {
|
|
53
127
|
enumerable: true,
|
|
54
128
|
configurable: true,
|
|
55
129
|
writable: true,
|
|
56
|
-
value: 0
|
|
130
|
+
value: void 0
|
|
57
131
|
});
|
|
58
|
-
Object.defineProperty(this, "
|
|
132
|
+
Object.defineProperty(this, "waitResolve", {
|
|
59
133
|
enumerable: true,
|
|
60
134
|
configurable: true,
|
|
61
135
|
writable: true,
|
|
62
136
|
value: void 0
|
|
63
137
|
});
|
|
64
|
-
Object.defineProperty(this, "
|
|
138
|
+
Object.defineProperty(this, "onAbort", {
|
|
65
139
|
enumerable: true,
|
|
66
140
|
configurable: true,
|
|
67
141
|
writable: true,
|
|
68
|
-
value:
|
|
142
|
+
value: void 0
|
|
69
143
|
});
|
|
144
|
+
/**
|
|
145
|
+
* Accept a matching authenticated non-error response from the expected
|
|
146
|
+
* remote address. Wrong address, missing MESSAGE-INTEGRITY (when required),
|
|
147
|
+
* or non-success class is rejected without completing the transaction
|
|
148
|
+
* (wrong address / unauthenticated responses are ignored so we keep waiting).
|
|
149
|
+
*/
|
|
70
150
|
Object.defineProperty(this, "responseReceived", {
|
|
71
151
|
enumerable: true,
|
|
72
152
|
configurable: true,
|
|
73
153
|
writable: true,
|
|
74
154
|
value: (message, addr) => {
|
|
75
|
-
if (this.onResponse.length
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
155
|
+
if (this.ended || this.onResponse.length === 0) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// RFC 7675 / ICE: only responses from the request's transport address.
|
|
159
|
+
if (!addressEquals(this.expectedAddr, addr)) {
|
|
160
|
+
log("ignore STUN response from unexpected address", addr, "expected", this.expectedAddr);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// RFC 7675 authenticated consent: integrityKey requires MESSAGE-INTEGRITY.
|
|
164
|
+
// Wire HMAC is verified by protocol layers via parseMessage(data, key);
|
|
165
|
+
// this presence check is defense-in-depth if responseReceived is called
|
|
166
|
+
// with a constructed Message that skipped the wire re-parse path.
|
|
167
|
+
if (this.integrityKey) {
|
|
168
|
+
const hasIntegrity = message.attributesKeys.includes("MESSAGE-INTEGRITY") ||
|
|
169
|
+
message.attributesKeys.includes("MESSAGE-INTEGRITY-SHA256");
|
|
170
|
+
if (!hasIntegrity) {
|
|
171
|
+
log("ignore unauthenticated STUN response (MESSAGE-INTEGRITY required)");
|
|
172
|
+
return;
|
|
82
173
|
}
|
|
83
174
|
}
|
|
175
|
+
if (message.messageClass === const_1.classes.RESPONSE) {
|
|
176
|
+
this.onResponse.execute(message, addr);
|
|
177
|
+
this.onResponse.complete();
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// ERROR class or other non-success
|
|
181
|
+
this.onResponse.error(new exceptions_1.TransactionFailed(message, addr));
|
|
182
|
+
}
|
|
84
183
|
}
|
|
85
184
|
});
|
|
86
185
|
Object.defineProperty(this, "run", {
|
|
@@ -89,6 +188,10 @@ class Transaction {
|
|
|
89
188
|
writable: true,
|
|
90
189
|
value: async () => {
|
|
91
190
|
try {
|
|
191
|
+
if (this.signal?.aborted) {
|
|
192
|
+
throw new exceptions_1.TransactionTimeout();
|
|
193
|
+
}
|
|
194
|
+
this.attachAbortListener();
|
|
92
195
|
this.retry().catch((e) => {
|
|
93
196
|
log("retry failed", e);
|
|
94
197
|
});
|
|
@@ -113,24 +216,90 @@ class Transaction {
|
|
|
113
216
|
this.protocol.sendStun(this.request, this.addr).catch((e) => {
|
|
114
217
|
log("send stun failed", e);
|
|
115
218
|
});
|
|
116
|
-
await
|
|
219
|
+
await this.wait(this.timeoutDelay);
|
|
117
220
|
if (this.ended) {
|
|
118
221
|
break;
|
|
119
222
|
}
|
|
120
223
|
this.timeoutDelay *= 2;
|
|
121
224
|
this.tries++;
|
|
122
225
|
}
|
|
123
|
-
if (this.tries >= this.triesMax) {
|
|
226
|
+
if (this.tries >= this.triesMax && !this.ended) {
|
|
124
227
|
log(`retry failed times:${this.tries} maxLimit:${this.triesMax}`);
|
|
125
|
-
this.
|
|
228
|
+
this.failWithTimeout();
|
|
126
229
|
}
|
|
127
230
|
}
|
|
128
231
|
});
|
|
129
|
-
|
|
232
|
+
const options = normalizeTransactionOptions(retransmissionsOrOptions, onRequestSent);
|
|
233
|
+
// triesMax = initial send + retransmissions
|
|
234
|
+
this.triesMax = 1 + (options.retransmissions ?? const_1.RETRY_MAX);
|
|
235
|
+
// responseTimeout is independent of retransmission count (RFC 7675 / 8445)
|
|
236
|
+
this.timeoutDelay = options.responseTimeout ?? const_1.RETRY_RTO;
|
|
237
|
+
this.onRequestSent = options.onRequestSent;
|
|
238
|
+
this.signal = options.signal;
|
|
239
|
+
this.expectedAddr = addr;
|
|
240
|
+
this.integrityKey = options.integrityKey;
|
|
241
|
+
}
|
|
242
|
+
attachAbortListener() {
|
|
243
|
+
if (!this.signal) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
this.onAbort = () => {
|
|
247
|
+
this.failWithTimeout();
|
|
248
|
+
};
|
|
249
|
+
this.signal.addEventListener("abort", this.onAbort, { once: true });
|
|
250
|
+
}
|
|
251
|
+
failWithTimeout() {
|
|
252
|
+
if (this.ended) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
this.ended = true;
|
|
256
|
+
this.clearWait();
|
|
257
|
+
if (this.onResponse.length > 0) {
|
|
258
|
+
this.onResponse.error(new exceptions_1.TransactionTimeout());
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
clearWait() {
|
|
262
|
+
if (this.waitTimer !== undefined) {
|
|
263
|
+
clearTimeout(this.waitTimer);
|
|
264
|
+
this.waitTimer = undefined;
|
|
265
|
+
}
|
|
266
|
+
const resolve = this.waitResolve;
|
|
267
|
+
this.waitResolve = undefined;
|
|
268
|
+
resolve?.();
|
|
269
|
+
}
|
|
270
|
+
wait(ms) {
|
|
271
|
+
return new Promise((resolve) => {
|
|
272
|
+
if (this.ended || this.signal?.aborted) {
|
|
273
|
+
resolve();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
this.waitResolve = resolve;
|
|
277
|
+
this.waitTimer = setTimeout(() => {
|
|
278
|
+
this.waitTimer = undefined;
|
|
279
|
+
this.waitResolve = undefined;
|
|
280
|
+
resolve();
|
|
281
|
+
}, ms);
|
|
282
|
+
});
|
|
130
283
|
}
|
|
131
284
|
cancel() {
|
|
132
285
|
this.ended = true;
|
|
286
|
+
this.clearWait();
|
|
287
|
+
if (this.signal && this.onAbort) {
|
|
288
|
+
this.signal.removeEventListener("abort", this.onAbort);
|
|
289
|
+
this.onAbort = undefined;
|
|
290
|
+
}
|
|
133
291
|
}
|
|
134
292
|
}
|
|
135
293
|
exports.Transaction = Transaction;
|
|
294
|
+
/**
|
|
295
|
+
* Build Transaction options for Protocol.request, folding integrityKey into
|
|
296
|
+
* options so response path can re-verify MESSAGE-INTEGRITY.
|
|
297
|
+
*/
|
|
298
|
+
function buildTransactionOptions(integrityKey, retransmissionsOrOptions, onRequestSent) {
|
|
299
|
+
const options = normalizeTransactionOptions(retransmissionsOrOptions, onRequestSent);
|
|
300
|
+
if (integrityKey && !options.integrityKey) {
|
|
301
|
+
options.integrityKey = integrityKey;
|
|
302
|
+
}
|
|
303
|
+
return options;
|
|
304
|
+
}
|
|
136
305
|
//# sourceMappingURL=transaction.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../../../../ice/src/stun/transaction.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Address, Event, debug } from \"../imports/common\";\n\nimport { TransactionFailed, TransactionTimeout } from \"../exceptions\";\nimport type { Protocol } from \"../types/model\";\nimport { RETRY_MAX, RETRY_RTO, classes } from \"./const\";\nimport type { Message } from \"./message\";\n\nconst log = debug(\"werift-ice:packages/ice/src/stun/transaction.ts\");\n\nexport class Transaction {\n private timeoutDelay = RETRY_RTO;\n ended = false;\n private tries = 0;\n private readonly triesMax: number;\n private readonly onResponse = new Event<[Message, Address]>();\n\n constructor(\n private request: Message,\n private addr: Address,\n private protocol: Protocol,\n private retransmissions?: number,\n private onRequestSent?: (attempt: number) => void,\n ) {\n this.triesMax = 1 + (this.retransmissions ?? RETRY_MAX);\n }\n\n responseReceived = (message: Message, addr: Address) => {\n if (this.onResponse.length > 0) {\n if (message.messageClass === classes.RESPONSE) {\n this.onResponse.execute(message, addr);\n this.onResponse.complete();\n } else {\n this.onResponse.error(new TransactionFailed(message, addr));\n }\n }\n };\n\n run = async () => {\n try {\n this.retry().catch((e) => {\n log(\"retry failed\", e);\n });\n const res = await this.onResponse.asPromise();\n return res;\n } catch (error) {\n throw error;\n } finally {\n this.cancel();\n }\n };\n\n private retry = async () => {\n while (this.tries < this.triesMax && !this.ended) {\n this.onRequestSent?.(this.tries);\n this.protocol.sendStun(this.request, this.addr).catch((e) => {\n log(\"send stun failed\", e);\n });\n await new Promise((r) => setTimeout(r, this.timeoutDelay));\n if (this.ended) {\n break;\n }\n this.timeoutDelay *= 2;\n this.tries++;\n }\n if (this.tries >= this.triesMax) {\n log(`retry failed times:${this.tries} maxLimit:${this.triesMax}`);\n this.onResponse.error(new TransactionTimeout());\n }\n };\n\n cancel() {\n this.ended = true;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../../../../ice/src/stun/transaction.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as dns } from \"node:dns\";\nimport { isIP } from \"node:net\";\n\nimport { type Address, Event, debug } from \"../imports/common\";\n\nimport { TransactionFailed, TransactionTimeout } from \"../exceptions\";\nimport type { Protocol, TransactionRequestOptions } from \"../types/model\";\nimport { RETRY_MAX, RETRY_RTO, classes } from \"./const\";\nimport type { Message } from \"./message\";\n\nconst log = debug(\"werift-ice:packages/ice/src/stun/transaction.ts\");\n\n/**\n * Resolve a request target to a concrete IP before creating a Transaction so\n * response source-address checks match the UDP peer address (hostname ≠ IP).\n */\nexport async function resolveRequestAddress(\n addr: Address,\n family: 0 | 4 | 6 = 0,\n): Promise<Address> {\n if (isIP(addr[0])) {\n return addr;\n }\n const looked = await dns.lookup(addr[0], { family });\n return [looked.address, addr[1]];\n}\n\n/**\n * Normalize legacy positional args and the options-object form into one shape.\n * Existing callers pass `(retransmissions?, onRequestSent?)`.\n */\nexport function normalizeTransactionOptions(\n retransmissionsOrOptions?: number | TransactionRequestOptions,\n onRequestSent?: (attempt: number) => void,\n): TransactionRequestOptions {\n if (\n retransmissionsOrOptions !== null &&\n typeof retransmissionsOrOptions === \"object\"\n ) {\n return retransmissionsOrOptions;\n }\n // After the object branch, only number | undefined remains (legacy positional API).\n const retransmissions =\n typeof retransmissionsOrOptions === \"number\"\n ? retransmissionsOrOptions\n : undefined;\n return {\n retransmissions,\n onRequestSent,\n };\n}\n\n/** Compare ICE transport addresses (host, port). */\nexport function addressEquals(a: Address, b: Address): boolean {\n return a[0] === b[0] && a[1] === b[1];\n}\n\nexport class Transaction {\n private timeoutDelay: number;\n ended = false;\n private tries = 0;\n private readonly triesMax: number;\n private readonly onResponse = new Event<[Message, Address]>();\n private readonly onRequestSent?: (attempt: number) => void;\n private readonly signal?: AbortSignal;\n /** Remote address this transaction was sent to; responses must match. */\n readonly expectedAddr: Address;\n /**\n * When set, protocol layers re-parse the wire response with this key so\n * MESSAGE-INTEGRITY failures are rejected before responseReceived.\n */\n readonly integrityKey?: Buffer;\n private waitTimer?: ReturnType<typeof setTimeout>;\n private waitResolve?: () => void;\n private onAbort?: () => void;\n\n constructor(\n private request: Message,\n private addr: Address,\n private protocol: Protocol,\n retransmissionsOrOptions?: number | TransactionRequestOptions,\n onRequestSent?: (attempt: number) => void,\n ) {\n const options = normalizeTransactionOptions(\n retransmissionsOrOptions,\n onRequestSent,\n );\n // triesMax = initial send + retransmissions\n this.triesMax = 1 + (options.retransmissions ?? RETRY_MAX);\n // responseTimeout is independent of retransmission count (RFC 7675 / 8445)\n this.timeoutDelay = options.responseTimeout ?? RETRY_RTO;\n this.onRequestSent = options.onRequestSent;\n this.signal = options.signal;\n this.expectedAddr = addr;\n this.integrityKey = options.integrityKey;\n }\n\n /**\n * Accept a matching authenticated non-error response from the expected\n * remote address. Wrong address, missing MESSAGE-INTEGRITY (when required),\n * or non-success class is rejected without completing the transaction\n * (wrong address / unauthenticated responses are ignored so we keep waiting).\n */\n responseReceived = (message: Message, addr: Address) => {\n if (this.ended || this.onResponse.length === 0) {\n return;\n }\n\n // RFC 7675 / ICE: only responses from the request's transport address.\n if (!addressEquals(this.expectedAddr, addr)) {\n log(\n \"ignore STUN response from unexpected address\",\n addr,\n \"expected\",\n this.expectedAddr,\n );\n return;\n }\n\n // RFC 7675 authenticated consent: integrityKey requires MESSAGE-INTEGRITY.\n // Wire HMAC is verified by protocol layers via parseMessage(data, key);\n // this presence check is defense-in-depth if responseReceived is called\n // with a constructed Message that skipped the wire re-parse path.\n if (this.integrityKey) {\n const hasIntegrity =\n message.attributesKeys.includes(\"MESSAGE-INTEGRITY\") ||\n message.attributesKeys.includes(\"MESSAGE-INTEGRITY-SHA256\");\n if (!hasIntegrity) {\n log(\n \"ignore unauthenticated STUN response (MESSAGE-INTEGRITY required)\",\n );\n return;\n }\n }\n\n if (message.messageClass === classes.RESPONSE) {\n this.onResponse.execute(message, addr);\n this.onResponse.complete();\n } else {\n // ERROR class or other non-success\n this.onResponse.error(new TransactionFailed(message, addr));\n }\n };\n\n run = async () => {\n try {\n if (this.signal?.aborted) {\n throw new TransactionTimeout();\n }\n this.attachAbortListener();\n this.retry().catch((e) => {\n log(\"retry failed\", e);\n });\n const res = await this.onResponse.asPromise();\n return res;\n } catch (error) {\n throw error;\n } finally {\n this.cancel();\n }\n };\n\n private attachAbortListener() {\n if (!this.signal) {\n return;\n }\n this.onAbort = () => {\n this.failWithTimeout();\n };\n this.signal.addEventListener(\"abort\", this.onAbort, { once: true });\n }\n\n private failWithTimeout() {\n if (this.ended) {\n return;\n }\n this.ended = true;\n this.clearWait();\n if (this.onResponse.length > 0) {\n this.onResponse.error(new TransactionTimeout());\n }\n }\n\n private clearWait() {\n if (this.waitTimer !== undefined) {\n clearTimeout(this.waitTimer);\n this.waitTimer = undefined;\n }\n const resolve = this.waitResolve;\n this.waitResolve = undefined;\n resolve?.();\n }\n\n private wait(ms: number): Promise<void> {\n return new Promise((resolve) => {\n if (this.ended || this.signal?.aborted) {\n resolve();\n return;\n }\n this.waitResolve = resolve;\n this.waitTimer = setTimeout(() => {\n this.waitTimer = undefined;\n this.waitResolve = undefined;\n resolve();\n }, ms);\n });\n }\n\n private retry = async () => {\n while (this.tries < this.triesMax && !this.ended) {\n this.onRequestSent?.(this.tries);\n this.protocol.sendStun(this.request, this.addr).catch((e) => {\n log(\"send stun failed\", e);\n });\n await this.wait(this.timeoutDelay);\n if (this.ended) {\n break;\n }\n this.timeoutDelay *= 2;\n this.tries++;\n }\n if (this.tries >= this.triesMax && !this.ended) {\n log(`retry failed times:${this.tries} maxLimit:${this.triesMax}`);\n this.failWithTimeout();\n }\n };\n\n cancel() {\n this.ended = true;\n this.clearWait();\n if (this.signal && this.onAbort) {\n this.signal.removeEventListener(\"abort\", this.onAbort);\n this.onAbort = undefined;\n }\n }\n}\n\n/**\n * Build Transaction options for Protocol.request, folding integrityKey into\n * options so response path can re-verify MESSAGE-INTEGRITY.\n */\nexport function buildTransactionOptions(\n integrityKey: Buffer | undefined,\n retransmissionsOrOptions?: number | TransactionRequestOptions,\n onRequestSent?: (attempt: number) => void,\n): TransactionRequestOptions {\n const options = normalizeTransactionOptions(\n retransmissionsOrOptions,\n onRequestSent,\n );\n if (integrityKey && !options.integrityKey) {\n options.integrityKey = integrityKey;\n }\n return options;\n}\n"]}
|