@mega-yfue/eufy-sdk 0.5.0-beta.93 → 0.5.0-beta.95
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/dist/index.js
CHANGED
|
@@ -15193,6 +15193,8 @@ __export(codec_exports, {
|
|
|
15193
15193
|
buildLookupWithKeyPayload: () => buildLookupWithKeyPayload,
|
|
15194
15194
|
buildLookupWithKeyPayload2: () => buildLookupWithKeyPayload2,
|
|
15195
15195
|
buildRawCommandPayload: () => buildRawCommandPayload,
|
|
15196
|
+
buildRelayPacketPayload: () => buildRelayPacketPayload,
|
|
15197
|
+
buildRelayRequestPayload: () => buildRelayRequestPayload,
|
|
15196
15198
|
buildStringCommandPayload: () => buildStringCommandPayload,
|
|
15197
15199
|
buildStringPairCommandPayload: () => buildStringPairCommandPayload,
|
|
15198
15200
|
buildVoidCommandPayload: () => buildVoidCommandPayload,
|
|
@@ -15211,6 +15213,9 @@ __export(codec_exports, {
|
|
|
15211
15213
|
paddingP2PData: () => paddingP2PData,
|
|
15212
15214
|
parseDataFrameHeader: () => parseDataFrameHeader,
|
|
15213
15215
|
parseLookupAddr: () => parseLookupAddr,
|
|
15216
|
+
parseRelayList: () => parseRelayList,
|
|
15217
|
+
parseRelayPortAck: () => parseRelayPortAck,
|
|
15218
|
+
parseRelayTo: () => parseRelayTo,
|
|
15214
15219
|
readNullTerminatedString: () => readNullTerminatedString,
|
|
15215
15220
|
stringWithLength: () => stringWithLength
|
|
15216
15221
|
});
|
|
@@ -15219,7 +15224,12 @@ var MAGIC_WORD = "XZYH";
|
|
|
15219
15224
|
var RequestMessageType = {
|
|
15220
15225
|
LOCAL_LOOKUP: Buffer.from([241, 48]),
|
|
15221
15226
|
LOOKUP_WITH_KEY: Buffer.from([241, 38]),
|
|
15227
|
+
/** `ListReq_DSK`: the same body as a variant-2 lookup, answered with {@link ResponseMessageType.LIST_REQ_ACK}. */
|
|
15222
15228
|
LOOKUP_WITH_KEY2: Buffer.from([241, 106]),
|
|
15229
|
+
RLY_HELLO: Buffer.from([241, 112]),
|
|
15230
|
+
RLY_PORT: Buffer.from([241, 114]),
|
|
15231
|
+
RLY_REQ: Buffer.from([241, 128]),
|
|
15232
|
+
RLY_PKT: Buffer.from([241, 131]),
|
|
15223
15233
|
CHECK_CAM: Buffer.from([241, 65]),
|
|
15224
15234
|
CHECK_CAM2: Buffer.from([241, 66]),
|
|
15225
15235
|
PING: Buffer.from([241, 224]),
|
|
@@ -15231,14 +15241,24 @@ var RequestMessageType = {
|
|
|
15231
15241
|
var ResponseMessageType = {
|
|
15232
15242
|
LOCAL_LOOKUP_RESP: Buffer.from([241, 65]),
|
|
15233
15243
|
LOOKUP_ADDR: Buffer.from([241, 64]),
|
|
15234
|
-
|
|
15244
|
+
/** The relay servers a cloud lookup server names: `[count:1][pad:3]` then `count` address records. */
|
|
15245
|
+
LIST_REQ_ACK: Buffer.from([241, 105]),
|
|
15246
|
+
RLY_HELLO_ACK: Buffer.from([241, 113]),
|
|
15247
|
+
/** A relay's answer to RLY_PORT: `[mark:4 BE][port:2 BE][pad:2]`, the port being the one it relays on. */
|
|
15248
|
+
RLY_PORT_ACK: Buffer.from([241, 115]),
|
|
15249
|
+
RLY_REQ_ACK: Buffer.from([241, 129]),
|
|
15250
|
+
/** A relay endpoint to register with: one address record, then `[handle:4 BE]`. */
|
|
15251
|
+
RLY_TO: Buffer.from([241, 130]),
|
|
15235
15252
|
CAM_ID: Buffer.from([241, 66]),
|
|
15253
|
+
/** A lookup server's answer to a P2P request: `[result:1]` first, 0 when the request is accepted. */
|
|
15254
|
+
P2P_REQ_ACK: Buffer.from([241, 33]),
|
|
15236
15255
|
/**
|
|
15237
15256
|
* The device's own address record, sent in answer to CHECK_CAM ahead of CAM_ID: its 20-byte device id, one
|
|
15238
|
-
* record in `
|
|
15257
|
+
* record in `encodeAddress`'s shape naming the address it answers from, then 8 zero bytes (44-byte payload).
|
|
15239
15258
|
*/
|
|
15240
15259
|
CAM_ADDR: Buffer.from([241, 67]),
|
|
15241
|
-
|
|
15260
|
+
/** A relay reporting the path to the device ready: the device's 20-byte id, sent from the relay endpoint. */
|
|
15261
|
+
RLY_RDY: Buffer.from([241, 132]),
|
|
15242
15262
|
PING: Buffer.from([241, 224]),
|
|
15243
15263
|
PONG: Buffer.from([241, 225]),
|
|
15244
15264
|
DATA: Buffer.from([241, 208]),
|
|
@@ -15398,7 +15418,7 @@ function buildLocalLookupPayload() {
|
|
|
15398
15418
|
function buildLookupWithKeyPayload2(p2pDid, dskKey) {
|
|
15399
15419
|
return Buffer.concat([p2pDidToBuffer(p2pDid), Buffer.from(dskKey), Buffer.from([0, 0, 0, 0])]);
|
|
15400
15420
|
}
|
|
15401
|
-
function
|
|
15421
|
+
function encodeAddress(host, port) {
|
|
15402
15422
|
const out = Buffer.alloc(16);
|
|
15403
15423
|
out.writeUInt16BE(2, 0);
|
|
15404
15424
|
out.writeUInt16LE(port, 2);
|
|
@@ -15412,12 +15432,22 @@ function encodeSelfAddress(host, port) {
|
|
|
15412
15432
|
function buildLookupWithKeyPayload(p2pDid, selfHost, selfPort, dskKey) {
|
|
15413
15433
|
return Buffer.concat([
|
|
15414
15434
|
p2pDidToBuffer(p2pDid),
|
|
15415
|
-
|
|
15435
|
+
encodeAddress(selfHost, selfPort),
|
|
15416
15436
|
Buffer.from([2, 5, 1, 5]),
|
|
15417
15437
|
Buffer.from(dskKey),
|
|
15418
15438
|
Buffer.from([0, 0, 0, 0])
|
|
15419
15439
|
]);
|
|
15420
15440
|
}
|
|
15441
|
+
function buildRelayRequestPayload(p2pDid, relay, mark) {
|
|
15442
|
+
const tail = Buffer.alloc(4);
|
|
15443
|
+
tail.writeUInt32BE(mark >>> 0, 0);
|
|
15444
|
+
return Buffer.concat([p2pDidToBuffer(p2pDid), encodeAddress(relay.host, relay.port), tail]);
|
|
15445
|
+
}
|
|
15446
|
+
function buildRelayPacketPayload(handle, p2pDid) {
|
|
15447
|
+
const head = Buffer.alloc(4);
|
|
15448
|
+
head.writeUInt32BE(handle >>> 0, 0);
|
|
15449
|
+
return Buffer.concat([head, p2pDidToBuffer(p2pDid), Buffer.alloc(4)]);
|
|
15450
|
+
}
|
|
15421
15451
|
function buildCheckCamPayload(p2pDid) {
|
|
15422
15452
|
return Buffer.concat([p2pDidToBuffer(p2pDid), Buffer.from([0, 0, 0])]);
|
|
15423
15453
|
}
|
|
@@ -15517,10 +15547,30 @@ function parseDataFrameHeader(frame) {
|
|
|
15517
15547
|
type: frame.readUInt8(14)
|
|
15518
15548
|
};
|
|
15519
15549
|
}
|
|
15550
|
+
function readAddress(buf, offset) {
|
|
15551
|
+
return {
|
|
15552
|
+
host: `${buf[offset + 7]}.${buf[offset + 6]}.${buf[offset + 5]}.${buf[offset + 4]}`,
|
|
15553
|
+
port: buf.readUInt16LE(offset + 2)
|
|
15554
|
+
};
|
|
15555
|
+
}
|
|
15520
15556
|
function parseLookupAddr(msg) {
|
|
15521
|
-
|
|
15522
|
-
|
|
15523
|
-
|
|
15557
|
+
return readAddress(msg, 4);
|
|
15558
|
+
}
|
|
15559
|
+
function parseRelayList(msg) {
|
|
15560
|
+
const out = [];
|
|
15561
|
+
const count2 = msg.length > 4 ? msg[4] : 0;
|
|
15562
|
+
for (let i = 0; i < count2 && 8 + 16 * (i + 1) <= msg.length; i++) {
|
|
15563
|
+
const addr = readAddress(msg, 8 + 16 * i);
|
|
15564
|
+
if (addr.host !== "0.0.0.0")
|
|
15565
|
+
out.push(addr);
|
|
15566
|
+
}
|
|
15567
|
+
return out;
|
|
15568
|
+
}
|
|
15569
|
+
function parseRelayPortAck(msg) {
|
|
15570
|
+
return msg.length >= 10 ? { mark: msg.readUInt32BE(4), port: msg.readUInt16BE(8) } : void 0;
|
|
15571
|
+
}
|
|
15572
|
+
function parseRelayTo(msg) {
|
|
15573
|
+
return msg.length >= 24 ? { address: readAddress(msg, 4), handle: msg.readUInt32BE(20) } : void 0;
|
|
15524
15574
|
}
|
|
15525
15575
|
function readNullTerminatedString(data, encoding = "utf8") {
|
|
15526
15576
|
const end = data.indexOf(0);
|
|
@@ -16140,6 +16190,8 @@ import { setTimeout as sleep2 } from "node:timers/promises";
|
|
|
16140
16190
|
// dist/transport/p2p/p2p-session.js
|
|
16141
16191
|
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
16142
16192
|
import dgram from "node:dgram";
|
|
16193
|
+
import { resolve4 } from "node:dns/promises";
|
|
16194
|
+
import { isIP as isIP2 } from "node:net";
|
|
16143
16195
|
import { constants as constants2, createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, generateKeyPairSync, privateDecrypt as privateDecrypt2, randomBytes as randomBytes4 } from "node:crypto";
|
|
16144
16196
|
|
|
16145
16197
|
// dist/transport/p2p/live-trace.js
|
|
@@ -16153,8 +16205,10 @@ var LOCAL_LOOKUP_PORT = 32108;
|
|
|
16153
16205
|
var HEARTBEAT_MS = 5e3;
|
|
16154
16206
|
var PATH_SILENCE_MS = HEARTBEAT_MS * 3;
|
|
16155
16207
|
var LOOKUP_RETRY_MS = 1e3;
|
|
16208
|
+
var RELAY_AFTER_ACKED_ROUNDS = 24;
|
|
16209
|
+
var RELAY_PACKET_COPIES = 4;
|
|
16156
16210
|
var RECEIVE_BUFFER_BYTES = 4 * 1024 * 1024;
|
|
16157
|
-
var CONNECT_TIMEOUT_MS =
|
|
16211
|
+
var CONNECT_TIMEOUT_MS = 45e3;
|
|
16158
16212
|
var STATION_CHANNEL3 = 255;
|
|
16159
16213
|
var CMD_GATEWAYINFO = 1100;
|
|
16160
16214
|
var CMD_START_REALTIME_MEDIA = 1003;
|
|
@@ -16177,6 +16231,19 @@ var LIVE_START_RETRANSMIT_MS = 150;
|
|
|
16177
16231
|
var LIVE_START_ACK_DEADLINE_MS = 3e3;
|
|
16178
16232
|
var SEQUENCE_LOOKBACK = 32768;
|
|
16179
16233
|
var STALE_RETRANSMIT_DEPTH = 1024;
|
|
16234
|
+
async function resolveLookupServers(addresses) {
|
|
16235
|
+
const resolved = await Promise.all(addresses.map(async (addr) => {
|
|
16236
|
+
if (isIP2(addr.host))
|
|
16237
|
+
return addr;
|
|
16238
|
+
try {
|
|
16239
|
+
const [host] = await resolve4(addr.host);
|
|
16240
|
+
return host ? { host, port: addr.port } : void 0;
|
|
16241
|
+
} catch {
|
|
16242
|
+
return void 0;
|
|
16243
|
+
}
|
|
16244
|
+
}));
|
|
16245
|
+
return resolved.filter((addr) => addr !== void 0);
|
|
16246
|
+
}
|
|
16180
16247
|
var PUNCH_PROBE_SOCKETS = 7;
|
|
16181
16248
|
var REORDER_WAIT_MS = 250;
|
|
16182
16249
|
var VIDEO_REORDER_WAIT_MS = 700;
|
|
@@ -16297,6 +16364,17 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16297
16364
|
probeSockets = [];
|
|
16298
16365
|
/** Cloud lookup inputs resolved once for a connection. */
|
|
16299
16366
|
cloudLookup;
|
|
16367
|
+
/**
|
|
16368
|
+
* Lookup rounds since the lookup server acknowledged this connect's P2P request, or `undefined` while it
|
|
16369
|
+
* has not; see {@link RELAY_AFTER_ACKED_ROUNDS}.
|
|
16370
|
+
*/
|
|
16371
|
+
ackedRounds;
|
|
16372
|
+
/**
|
|
16373
|
+
* The relay handshake of a connect that has fallen back to one, by the step it waits on: the relay list
|
|
16374
|
+
* (`list`, answering ListReq_DSK), a relay's hello acknowledgement (`hello`), that relay's port (`port`),
|
|
16375
|
+
* then the relay-ready the device's registration produces (`request`, re-sending RLY_REQ meanwhile).
|
|
16376
|
+
*/
|
|
16377
|
+
relay;
|
|
16300
16378
|
/** Local outbound IPv4 reported inside LOOKUP_WITH_KEY requests. */
|
|
16301
16379
|
selfHost;
|
|
16302
16380
|
/**
|
|
@@ -16646,6 +16724,7 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16646
16724
|
this.connecting = true;
|
|
16647
16725
|
this.closed = false;
|
|
16648
16726
|
this.connectionGeneration += 1;
|
|
16727
|
+
this.ackedRounds = void 0;
|
|
16649
16728
|
this.resetInboundSequencing();
|
|
16650
16729
|
if (!this.level2KeyServes("set-payload")) {
|
|
16651
16730
|
this.level2Pending = this.cfg.resolveCipherKey !== void 0;
|
|
@@ -16674,6 +16753,13 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16674
16753
|
});
|
|
16675
16754
|
const { dskKey, cloudAddresses } = this.cfg;
|
|
16676
16755
|
this.cloudLookup = dskKey && cloudAddresses?.length ? { key: dskKey, addresses: cloudAddresses } : void 0;
|
|
16756
|
+
if (this.cloudLookup && cloudAddresses) {
|
|
16757
|
+
const generation = this.connectionGeneration;
|
|
16758
|
+
void resolveLookupServers(cloudAddresses).then((addresses) => {
|
|
16759
|
+
if (this.cloudLookup && !this.closed && generation === this.connectionGeneration)
|
|
16760
|
+
this.cloudLookup.addresses = addresses;
|
|
16761
|
+
});
|
|
16762
|
+
}
|
|
16677
16763
|
if (this.cloudLookup)
|
|
16678
16764
|
await this.startPunchProbes();
|
|
16679
16765
|
if (this.closed)
|
|
@@ -16688,7 +16774,7 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16688
16774
|
this.lookupTimer = setInterval(() => this.sendLookups(), LOOKUP_RETRY_MS);
|
|
16689
16775
|
this.connectTimer = setTimeout(() => {
|
|
16690
16776
|
if (!this.connected) {
|
|
16691
|
-
this.emit("error", new Error(`P2P connect timeout for ${this.cfg.stationSn}`));
|
|
16777
|
+
this.emit("error", new Error(`P2P connect timeout for ${this.cfg.stationSn} (${this.relayProgress()})`));
|
|
16692
16778
|
void this.close();
|
|
16693
16779
|
}
|
|
16694
16780
|
}, CONNECT_TIMEOUT_MS);
|
|
@@ -16766,6 +16852,84 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16766
16852
|
this.sendCloudLookup(socket, lookup2);
|
|
16767
16853
|
this.logger.debug(`[p2p] ${this.cfg.stationSn} sendLookups: cloud -> ${lookup2.addresses.map((a) => `${a.host}:${a.port}`).join(", ")} self=${this.selfHost ?? "unknown"}`);
|
|
16768
16854
|
}
|
|
16855
|
+
this.countAckedRound();
|
|
16856
|
+
this.sendRelay();
|
|
16857
|
+
}
|
|
16858
|
+
/** Step {@link ackedRounds} for one lookup round, and start the relay handshake once it passes its threshold. */
|
|
16859
|
+
countAckedRound() {
|
|
16860
|
+
if (this.ackedRounds === void 0 || this.relay || !this.cloudLookup)
|
|
16861
|
+
return;
|
|
16862
|
+
this.ackedRounds += 1;
|
|
16863
|
+
if (this.ackedRounds > RELAY_AFTER_ACKED_ROUNDS)
|
|
16864
|
+
this.relay = { step: "list", relays: [] };
|
|
16865
|
+
}
|
|
16866
|
+
/**
|
|
16867
|
+
* How far this connect got towards a relay: the handshake step it was waiting on (`list` meaning no lookup
|
|
16868
|
+
* server named a relay), `not started` where it had not fallen back yet, or `no cloud lookup` where it could
|
|
16869
|
+
* not.
|
|
16870
|
+
*/
|
|
16871
|
+
relayProgress() {
|
|
16872
|
+
return `relay: ${this.relay?.step ?? (this.cloudLookup ? "not started" : "no cloud lookup")}`;
|
|
16873
|
+
}
|
|
16874
|
+
/**
|
|
16875
|
+
* Send the relay handshake's message for the step it waits on, from the session's own socket.
|
|
16876
|
+
*
|
|
16877
|
+
* The order is the vendor client's: ListReq_DSK to the cloud lookup servers, RLY_HELLO to every relay they
|
|
16878
|
+
* list, RLY_PORT to the first of those relays that acknowledges, then RLY_REQ to the lookup servers naming
|
|
16879
|
+
* that relay's port and mark. The lookup server has the device register with the relay, and the relay
|
|
16880
|
+
* answers RLY_RDY.
|
|
16881
|
+
*/
|
|
16882
|
+
sendRelay() {
|
|
16883
|
+
const relay = this.relay;
|
|
16884
|
+
const lookup2 = this.cloudLookup;
|
|
16885
|
+
if (!relay || !lookup2 || this.connected)
|
|
16886
|
+
return;
|
|
16887
|
+
if (relay.step === "list") {
|
|
16888
|
+
const payload = buildLookupWithKeyPayload2(this.cfg.p2pDid, lookup2.key);
|
|
16889
|
+
for (const addr of lookup2.addresses)
|
|
16890
|
+
this.send(addr, RequestMessageType.LOOKUP_WITH_KEY2, payload);
|
|
16891
|
+
} else if (relay.step === "hello") {
|
|
16892
|
+
for (const addr of relay.relays)
|
|
16893
|
+
this.send(addr, RequestMessageType.RLY_HELLO);
|
|
16894
|
+
} else if (relay.step === "port" && relay.via) {
|
|
16895
|
+
this.send(relay.via, RequestMessageType.RLY_PORT);
|
|
16896
|
+
} else if (relay.step === "request" && relay.via && relay.mark !== void 0) {
|
|
16897
|
+
const payload = buildRelayRequestPayload(this.cfg.p2pDid, relay.via, relay.mark);
|
|
16898
|
+
for (const addr of lookup2.addresses)
|
|
16899
|
+
this.send(addr, RequestMessageType.RLY_REQ, payload);
|
|
16900
|
+
}
|
|
16901
|
+
}
|
|
16902
|
+
/** Advance the relay handshake on one of its answers, arriving on the session's own socket. */
|
|
16903
|
+
onRelayMessage(msg, rinfo) {
|
|
16904
|
+
const relay = this.relay;
|
|
16905
|
+
if (!relay || this.connected)
|
|
16906
|
+
return;
|
|
16907
|
+
if (hasHeader(msg, ResponseMessageType.LIST_REQ_ACK)) {
|
|
16908
|
+
if (relay.step !== "list" && relay.step !== "hello")
|
|
16909
|
+
return;
|
|
16910
|
+
for (const listed of parseRelayList(msg))
|
|
16911
|
+
if (!relay.relays.some((known) => known.host === listed.host && known.port === listed.port))
|
|
16912
|
+
relay.relays.push(listed);
|
|
16913
|
+
if (relay.relays.length)
|
|
16914
|
+
relay.step = "hello";
|
|
16915
|
+
} else if (hasHeader(msg, ResponseMessageType.RLY_HELLO_ACK)) {
|
|
16916
|
+
const listed = relay.relays.some((known) => known.host === rinfo.address && known.port === rinfo.port);
|
|
16917
|
+
if (relay.step !== "hello" || !listed)
|
|
16918
|
+
return;
|
|
16919
|
+
relay.via = { host: rinfo.address, port: rinfo.port };
|
|
16920
|
+
relay.step = "port";
|
|
16921
|
+
} else if (hasHeader(msg, ResponseMessageType.RLY_PORT_ACK)) {
|
|
16922
|
+
const ack = parseRelayPortAck(msg);
|
|
16923
|
+
if (relay.step !== "port" || !relay.via || !ack)
|
|
16924
|
+
return;
|
|
16925
|
+
relay.via = { host: relay.via.host, port: ack.port };
|
|
16926
|
+
relay.mark = ack.mark;
|
|
16927
|
+
relay.step = "request";
|
|
16928
|
+
} else {
|
|
16929
|
+
return;
|
|
16930
|
+
}
|
|
16931
|
+
this.logger.debug(`[p2p] ${this.cfg.stationSn} relay -> ${relay.step}`);
|
|
16932
|
+
this.sendRelay();
|
|
16769
16933
|
}
|
|
16770
16934
|
/** Register a bound socket's own port with each configured cloud lookup address. */
|
|
16771
16935
|
sendCloudLookup(socket, lookup2) {
|
|
@@ -16778,15 +16942,17 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16778
16942
|
* Route one inbound UDP datagram by its message type, tracing every non-DATA one and any type this session
|
|
16779
16943
|
* does not model.
|
|
16780
16944
|
*
|
|
16781
|
-
* An unmodelled type is not by itself evidence about a session that is failing.
|
|
16782
|
-
*
|
|
16783
|
-
*
|
|
16784
|
-
*
|
|
16785
|
-
*
|
|
16786
|
-
* as a cause.
|
|
16945
|
+
* An unmodelled type is not by itself evidence about a session that is failing. Correlate one against a
|
|
16946
|
+
* WORKING session before reading it as a cause.
|
|
16947
|
+
*
|
|
16948
|
+
* P2P_REQ_ACK is the lookup server acknowledging this connect's P2P request; the first one carrying result 0
|
|
16949
|
+
* starts the round count {@link RELAY_AFTER_ACKED_ROUNDS} measures.
|
|
16787
16950
|
*
|
|
16788
16951
|
* CAM_ADDR is recognised and dropped: it names the address the device is already answering from, and the
|
|
16789
|
-
* CAM_ID that follows it is what completes the connect.
|
|
16952
|
+
* CAM_ID that follows it is what completes the connect. RLY_TO is a relay endpoint, never the device: it is
|
|
16953
|
+
* answered with {@link RELAY_PACKET_COPIES} RLY_PKTs carrying its handle, and the RLY_RDY naming this
|
|
16954
|
+
* session's device completes the connect with the relay as the peer. The other relay answers advance the
|
|
16955
|
+
* handshake {@link sendRelay} drives, and are dropped outside it.
|
|
16790
16956
|
*/
|
|
16791
16957
|
onMessage(msg, rinfo, socket = this.socket) {
|
|
16792
16958
|
if (!socket)
|
|
@@ -16802,14 +16968,33 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16802
16968
|
if (hasHeader(msg, ResponseMessageType.LOCAL_LOOKUP_RESP)) {
|
|
16803
16969
|
if (!this.connected)
|
|
16804
16970
|
this.beginCheckCam({ host: rinfo.address, port: rinfo.port }, socket);
|
|
16805
|
-
} else if (hasHeader(msg, ResponseMessageType.LOOKUP_ADDR)
|
|
16971
|
+
} else if (hasHeader(msg, ResponseMessageType.LOOKUP_ADDR)) {
|
|
16806
16972
|
if (!this.connected) {
|
|
16807
16973
|
const addr = parseLookupAddr(msg);
|
|
16808
16974
|
this.logger.debug(`[p2p] ${this.cfg.stationSn} LOOKUP_ADDR -> ${addr.host}:${addr.port}`);
|
|
16809
16975
|
if (addr.host !== "0.0.0.0")
|
|
16810
16976
|
this.beginCheckCam(addr, socket);
|
|
16811
16977
|
}
|
|
16812
|
-
} else if (hasHeader(msg, ResponseMessageType.
|
|
16978
|
+
} else if (hasHeader(msg, ResponseMessageType.RLY_TO)) {
|
|
16979
|
+
const to = parseRelayTo(msg);
|
|
16980
|
+
if (!this.connected && to && to.address.host !== "0.0.0.0") {
|
|
16981
|
+
this.logger.debug(`[p2p] ${this.cfg.stationSn} RLY_TO -> ${to.address.host}:${to.address.port}`);
|
|
16982
|
+
const payload = buildRelayPacketPayload(to.handle, this.cfg.p2pDid);
|
|
16983
|
+
for (let i = 0; i < RELAY_PACKET_COPIES; i++)
|
|
16984
|
+
this.send(to.address, RequestMessageType.RLY_PKT, payload, socket);
|
|
16985
|
+
}
|
|
16986
|
+
} else if (hasHeader(msg, ResponseMessageType.RLY_RDY)) {
|
|
16987
|
+
if (msg.subarray(4, 24).equals(p2pDidToBuffer(this.cfg.p2pDid))) {
|
|
16988
|
+
this.logger.debug(`[p2p] ${this.cfg.stationSn} RLY_RDY from relay ${rinfo.address}:${rinfo.port}`);
|
|
16989
|
+
this.onConnected({ host: rinfo.address, port: rinfo.port }, socket);
|
|
16990
|
+
}
|
|
16991
|
+
} else if (hasHeader(msg, ResponseMessageType.P2P_REQ_ACK)) {
|
|
16992
|
+
if (!this.connected && this.ackedRounds === void 0 && msg.length > 4 && msg[4] === 0)
|
|
16993
|
+
this.ackedRounds = 0;
|
|
16994
|
+
} else if (hasHeader(msg, ResponseMessageType.LIST_REQ_ACK) || hasHeader(msg, ResponseMessageType.RLY_HELLO_ACK) || hasHeader(msg, ResponseMessageType.RLY_PORT_ACK) || hasHeader(msg, ResponseMessageType.RLY_REQ_ACK)) {
|
|
16995
|
+
if (socket === this.socket)
|
|
16996
|
+
this.onRelayMessage(msg, rinfo);
|
|
16997
|
+
} else if (hasHeader(msg, ResponseMessageType.CAM_ID)) {
|
|
16813
16998
|
this.onConnected({ host: rinfo.address, port: rinfo.port }, socket);
|
|
16814
16999
|
} else if (hasHeader(msg, ResponseMessageType.END)) {
|
|
16815
17000
|
if (fromConnectedPeer)
|
|
@@ -16897,6 +17082,7 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16897
17082
|
clearInterval(this.lookupTimer);
|
|
16898
17083
|
if (this.connectTimer)
|
|
16899
17084
|
clearTimeout(this.connectTimer);
|
|
17085
|
+
this.relay = void 0;
|
|
16900
17086
|
this.logger.debug(`[p2p] ${this.cfg.stationSn} connected ${addr.host}:${addr.port}`);
|
|
16901
17087
|
this.sendCommand(CMD_GATEWAYINFO);
|
|
16902
17088
|
this.send(addr, RequestMessageType.PING, this.lastPongData);
|
|
@@ -18076,6 +18262,7 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
18076
18262
|
clearInterval(this.heartbeatTimer);
|
|
18077
18263
|
if (this.connectTimer)
|
|
18078
18264
|
clearTimeout(this.connectTimer);
|
|
18265
|
+
this.relay = void 0;
|
|
18079
18266
|
if (this.audioRetransmitTimer)
|
|
18080
18267
|
clearInterval(this.audioRetransmitTimer);
|
|
18081
18268
|
this.audioRetransmitTimer = void 0;
|
|
@@ -22086,6 +22273,7 @@ var P2PCommandRouter = class _P2PCommandRouter {
|
|
|
22086
22273
|
*/
|
|
22087
22274
|
async openMediaSession(parentSn, channel, signal) {
|
|
22088
22275
|
const session = await this.openSession(_P2PCommandRouter.mediaSessionKey(parentSn, channel), parentSn);
|
|
22276
|
+
await this.awaitConnected(session, parentSn, signal);
|
|
22089
22277
|
let ready = await abortable(session.awaitLevel2Key(LEVEL2_GRACE_MS, "call"), signal);
|
|
22090
22278
|
if (!ready && session.repromptLevel2Key()) {
|
|
22091
22279
|
ready = await abortable(session.awaitLevel2Key(LEVEL2_GRACE_MS, "call"), signal);
|
|
@@ -22094,6 +22282,30 @@ var P2PCommandRouter = class _P2PCommandRouter {
|
|
|
22094
22282
|
throw new StationKeyUnavailableError(parentSn);
|
|
22095
22283
|
return session;
|
|
22096
22284
|
}
|
|
22285
|
+
/**
|
|
22286
|
+
* Wait for `session` to connect, for up to the session's own connect deadline, and throw
|
|
22287
|
+
* {@link StationUnreachableError} when it does not.
|
|
22288
|
+
*
|
|
22289
|
+
* `connect()` resolves once the lookups are under way, not once a path answers, so a session handed back by
|
|
22290
|
+
* {@link openSession} may still be connecting: a relay connect lands about 25 s in. A level-2 grace counted
|
|
22291
|
+
* before this would be spent on the connect rather than on the key.
|
|
22292
|
+
*/
|
|
22293
|
+
async awaitConnected(session, parentSn, signal) {
|
|
22294
|
+
const t0 = Date.now();
|
|
22295
|
+
let waitedMs = 0;
|
|
22296
|
+
if (!session.isConnected) {
|
|
22297
|
+
this.traceOnStation(session, { phase: "session-connect-wait", waitMs: P2P_STATION_WAITS.connect });
|
|
22298
|
+
while (!session.isConnected && Date.now() - t0 < P2P_STATION_WAITS.connect) {
|
|
22299
|
+
signal?.throwIfAborted();
|
|
22300
|
+
await sleep2(200);
|
|
22301
|
+
}
|
|
22302
|
+
waitedMs = Date.now() - t0;
|
|
22303
|
+
this.traceOnStation(session, session.isConnected ? { phase: "session-connected", waitedMs } : { phase: "session-unreachable", waitedMs });
|
|
22304
|
+
}
|
|
22305
|
+
signal?.throwIfAborted();
|
|
22306
|
+
if (!session.isConnected)
|
|
22307
|
+
throw new StationUnreachableError(parentSn, waitedMs);
|
|
22308
|
+
}
|
|
22097
22309
|
/** Open (or reuse) the session filed under `key`, dialling `parentSn`'s endpoint. */
|
|
22098
22310
|
async openSession(key, parentSn) {
|
|
22099
22311
|
return this.manager.acquire(key, async (register) => {
|
|
@@ -23143,20 +23355,7 @@ var P2PCommandRouter = class _P2PCommandRouter {
|
|
|
23143
23355
|
stationAdmin: typeof stationAdminId !== "string" ? "unstated" : stationAdminId === this.deps.mega.auth?.userId ? "self" : "other",
|
|
23144
23356
|
...stationModel ? { stationModel } : {}
|
|
23145
23357
|
});
|
|
23146
|
-
|
|
23147
|
-
let waitedMs = 0;
|
|
23148
|
-
if (!session.isConnected) {
|
|
23149
|
-
this.traceOnStation(session, { phase: "session-connect-wait", waitMs: P2P_STATION_WAITS.connect });
|
|
23150
|
-
while (!session.isConnected && Date.now() - t0 < P2P_STATION_WAITS.connect) {
|
|
23151
|
-
opts.signal?.throwIfAborted();
|
|
23152
|
-
await sleep2(200);
|
|
23153
|
-
}
|
|
23154
|
-
waitedMs = Date.now() - t0;
|
|
23155
|
-
this.traceOnStation(session, session.isConnected ? { phase: "session-connected", waitedMs } : { phase: "session-unreachable", waitedMs });
|
|
23156
|
-
}
|
|
23157
|
-
opts.signal?.throwIfAborted();
|
|
23158
|
-
if (!session.isConnected)
|
|
23159
|
-
throw new StationUnreachableError(parentSn, waitedMs);
|
|
23358
|
+
await this.awaitConnected(session, parentSn, opts.signal);
|
|
23160
23359
|
if (opts.waitLevel2) {
|
|
23161
23360
|
if (opts.waitLevel2 === "settle") {
|
|
23162
23361
|
await abortable(session.awaitLevel2Key(LEVEL2_SETTLE_MS, "session"), opts.signal);
|
|
@@ -25570,10 +25769,10 @@ var RtcCommandRouter = class {
|
|
|
25570
25769
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
25571
25770
|
|
|
25572
25771
|
// dist/transport/mqtt/broker-discovery.js
|
|
25573
|
-
import { resolve4 } from "node:dns/promises";
|
|
25772
|
+
import { resolve4 as resolve42 } from "node:dns/promises";
|
|
25574
25773
|
async function resolveBrokerIps(hostname) {
|
|
25575
25774
|
try {
|
|
25576
|
-
return await
|
|
25775
|
+
return await resolve42(hostname);
|
|
25577
25776
|
} catch {
|
|
25578
25777
|
return [];
|
|
25579
25778
|
}
|