hap-nodejs 0.12.3-beta.2 → 0.12.3-beta.21
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/README.md +1 -0
- package/dist/accessories/AirConditioner_accessory.js +24 -24
- package/dist/accessories/AirConditioner_accessory.js.map +1 -1
- package/dist/accessories/AppleTVRemote_accessory.js +23 -23
- package/dist/accessories/AppleTVRemote_accessory.js.map +1 -1
- package/dist/accessories/Camera_accessory.js +292 -373
- package/dist/accessories/Camera_accessory.js.map +1 -1
- package/dist/accessories/Fan_accessory.js +15 -21
- package/dist/accessories/Fan_accessory.js.map +1 -1
- package/dist/accessories/GarageDoorOpener_accessory.js +12 -12
- package/dist/accessories/GarageDoorOpener_accessory.js.map +1 -1
- package/dist/accessories/Light-AdaptiveLighting_accessory.js +31 -21
- package/dist/accessories/Light-AdaptiveLighting_accessory.js.map +1 -1
- package/dist/accessories/Light_accessory.js +45 -48
- package/dist/accessories/Light_accessory.js.map +1 -1
- package/dist/accessories/Lock_accessory.js +11 -11
- package/dist/accessories/Lock_accessory.js.map +1 -1
- package/dist/accessories/MotionSensor_accessory.js +8 -8
- package/dist/accessories/MotionSensor_accessory.js.map +1 -1
- package/dist/accessories/Outlet_accessory.js +10 -10
- package/dist/accessories/Outlet_accessory.js.map +1 -1
- package/dist/accessories/SmartSpeaker_accessory.js +11 -11
- package/dist/accessories/SmartSpeaker_accessory.js.map +1 -1
- package/dist/accessories/Sprinkler_accessory.js +19 -19
- package/dist/accessories/Sprinkler_accessory.js.map +1 -1
- package/dist/accessories/TV_accessory.js +17 -17
- package/dist/accessories/TV_accessory.js.map +1 -1
- package/dist/accessories/TemperatureSensor_accessory.js +6 -6
- package/dist/accessories/TemperatureSensor_accessory.js.map +1 -1
- package/dist/accessories/Wi-FiRouter_accessory.js +3 -3
- package/dist/accessories/Wi-FiRouter_accessory.js.map +1 -1
- package/dist/accessories/Wi-FiSatellite_accessory.js +4 -4
- package/dist/accessories/Wi-FiSatellite_accessory.js.map +1 -1
- package/dist/accessories/gstreamer-audioProducer.js +36 -47
- package/dist/accessories/gstreamer-audioProducer.js.map +1 -1
- package/dist/accessories/types.js +2 -2
- package/dist/accessories/types.js.map +1 -1
- package/dist/index.d.ts +0 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -28
- package/dist/index.js.map +1 -1
- package/dist/lib/Accessory.d.ts +1 -58
- package/dist/lib/Accessory.d.ts.map +1 -1
- package/dist/lib/Accessory.js +747 -1149
- package/dist/lib/Accessory.js.map +1 -1
- package/dist/lib/Advertiser.d.ts +1 -2
- package/dist/lib/Advertiser.d.ts.map +1 -1
- package/dist/lib/Advertiser.js +392 -524
- package/dist/lib/Advertiser.js.map +1 -1
- package/dist/lib/Bridge.js +6 -10
- package/dist/lib/Bridge.js.map +1 -1
- package/dist/lib/Characteristic.d.ts +2 -133
- package/dist/lib/Characteristic.d.ts.map +1 -1
- package/dist/lib/Characteristic.js +1467 -669
- package/dist/lib/Characteristic.js.map +1 -1
- package/dist/lib/HAPServer.d.ts +0 -10
- package/dist/lib/HAPServer.d.ts.map +1 -1
- package/dist/lib/HAPServer.js +216 -280
- package/dist/lib/HAPServer.js.map +1 -1
- package/dist/lib/Service.d.ts +1 -51
- package/dist/lib/Service.d.ts.map +1 -1
- package/dist/lib/Service.js +474 -322
- package/dist/lib/Service.js.map +1 -1
- package/dist/lib/camera/RTPProxy.js +112 -104
- package/dist/lib/camera/RTPProxy.js.map +1 -1
- package/dist/lib/camera/RTPStreamManagement.d.ts +0 -65
- package/dist/lib/camera/RTPStreamManagement.d.ts.map +1 -1
- package/dist/lib/camera/RTPStreamManagement.js +255 -278
- package/dist/lib/camera/RTPStreamManagement.js.map +1 -1
- package/dist/lib/camera/RecordingManagement.js +318 -381
- package/dist/lib/camera/RecordingManagement.js.map +1 -1
- package/dist/lib/camera/index.d.ts +0 -1
- package/dist/lib/camera/index.d.ts.map +1 -1
- package/dist/lib/camera/index.js +1 -2
- package/dist/lib/camera/index.js.map +1 -1
- package/dist/lib/controller/AdaptiveLightingController.d.ts +19 -3
- package/dist/lib/controller/AdaptiveLightingController.d.ts.map +1 -1
- package/dist/lib/controller/AdaptiveLightingController.js +217 -218
- package/dist/lib/controller/AdaptiveLightingController.js.map +1 -1
- package/dist/lib/controller/CameraController.d.ts +0 -4
- package/dist/lib/controller/CameraController.d.ts.map +1 -1
- package/dist/lib/controller/CameraController.js +189 -256
- package/dist/lib/controller/CameraController.js.map +1 -1
- package/dist/lib/controller/DoorbellController.js +38 -39
- package/dist/lib/controller/DoorbellController.js.map +1 -1
- package/dist/lib/controller/RemoteController.d.ts +0 -14
- package/dist/lib/controller/RemoteController.d.ts.map +1 -1
- package/dist/lib/controller/RemoteController.js +340 -415
- package/dist/lib/controller/RemoteController.js.map +1 -1
- package/dist/lib/controller/index.js +1 -1
- package/dist/lib/datastream/DataStreamManagement.js +56 -57
- package/dist/lib/datastream/DataStreamManagement.js.map +1 -1
- package/dist/lib/datastream/DataStreamParser.js +259 -304
- package/dist/lib/datastream/DataStreamParser.js.map +1 -1
- package/dist/lib/datastream/DataStreamServer.d.ts +0 -5
- package/dist/lib/datastream/DataStreamServer.d.ts.map +1 -1
- package/dist/lib/datastream/DataStreamServer.js +252 -269
- package/dist/lib/datastream/DataStreamServer.js.map +1 -1
- package/dist/lib/datastream/index.js +1 -1
- package/dist/lib/definitions/CharacteristicDefinitions.d.ts +1 -106
- package/dist/lib/definitions/CharacteristicDefinitions.d.ts.map +1 -1
- package/dist/lib/definitions/CharacteristicDefinitions.js +2000 -2995
- package/dist/lib/definitions/CharacteristicDefinitions.js.map +1 -1
- package/dist/lib/definitions/ServiceDefinitions.d.ts +0 -32
- package/dist/lib/definitions/ServiceDefinitions.d.ts.map +1 -1
- package/dist/lib/definitions/ServiceDefinitions.js +820 -1147
- package/dist/lib/definitions/ServiceDefinitions.js.map +1 -1
- package/dist/lib/definitions/generate-definitions.js +383 -679
- package/dist/lib/definitions/generate-definitions.js.map +1 -1
- package/dist/lib/definitions/generator-configuration.js +29 -29
- package/dist/lib/definitions/generator-configuration.js.map +1 -1
- package/dist/lib/definitions/index.js +1 -1
- package/dist/lib/model/AccessoryInfo.js +101 -136
- package/dist/lib/model/AccessoryInfo.js.map +1 -1
- package/dist/lib/model/ControllerStorage.js +86 -89
- package/dist/lib/model/ControllerStorage.js.map +1 -1
- package/dist/lib/model/HAPStorage.js +15 -16
- package/dist/lib/model/HAPStorage.js.map +1 -1
- package/dist/lib/model/IdentifierCache.js +49 -49
- package/dist/lib/model/IdentifierCache.js.map +1 -1
- package/dist/lib/tv/AccessControlManagement.js +40 -44
- package/dist/lib/tv/AccessControlManagement.js.map +1 -1
- package/dist/lib/util/checkName.d.ts +2 -1
- package/dist/lib/util/checkName.d.ts.map +1 -1
- package/dist/lib/util/checkName.js +7 -11
- package/dist/lib/util/checkName.js.map +1 -1
- package/dist/lib/util/clone.js +5 -27
- package/dist/lib/util/clone.js.map +1 -1
- package/dist/lib/util/color-utils.js +8 -12
- package/dist/lib/util/color-utils.js.map +1 -1
- package/dist/lib/util/eventedhttp.d.ts.map +1 -1
- package/dist/lib/util/eventedhttp.js +301 -409
- package/dist/lib/util/eventedhttp.js.map +1 -1
- package/dist/lib/util/hapCrypto.js +31 -32
- package/dist/lib/util/hapCrypto.js.map +1 -1
- package/dist/lib/util/hapStatusError.js +9 -12
- package/dist/lib/util/hapStatusError.js.map +1 -1
- package/dist/lib/util/net-utils.js +32 -53
- package/dist/lib/util/net-utils.js.map +1 -1
- package/dist/lib/util/once.js +3 -8
- package/dist/lib/util/once.js.map +1 -1
- package/dist/lib/util/promise-utils.js +8 -13
- package/dist/lib/util/promise-utils.js.map +1 -1
- package/dist/lib/util/request-util.js +2 -3
- package/dist/lib/util/request-util.js.map +1 -1
- package/dist/lib/util/time.js +5 -5
- package/dist/lib/util/time.js.map +1 -1
- package/dist/lib/util/tlv.d.ts +0 -27
- package/dist/lib/util/tlv.d.ts.map +1 -1
- package/dist/lib/util/tlv.js +71 -113
- package/dist/lib/util/tlv.js.map +1 -1
- package/dist/lib/util/uuid.d.ts +0 -9
- package/dist/lib/util/uuid.d.ts.map +1 -1
- package/dist/lib/util/uuid.js +15 -33
- package/dist/lib/util/uuid.js.map +1 -1
- package/dist/types.d.ts +0 -35
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +10 -10
- package/dist/BridgedCore.d.ts +0 -2
- package/dist/BridgedCore.d.ts.map +0 -1
- package/dist/BridgedCore.js +0 -43
- package/dist/BridgedCore.js.map +0 -1
- package/dist/Core.d.ts +0 -2
- package/dist/Core.d.ts.map +0 -1
- package/dist/Core.js +0 -52
- package/dist/Core.js.map +0 -1
- package/dist/lib/AccessoryLoader.d.ts +0 -28
- package/dist/lib/AccessoryLoader.d.ts.map +0 -1
- package/dist/lib/AccessoryLoader.js +0 -166
- package/dist/lib/AccessoryLoader.js.map +0 -1
- package/dist/lib/camera/Camera.d.ts +0 -43
- package/dist/lib/camera/Camera.d.ts.map +0 -1
- package/dist/lib/camera/Camera.js +0 -36
- package/dist/lib/camera/Camera.js.map +0 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DataStreamConnection = exports.HDSConnectionError = exports.HDSConnectionErrorType = exports.DataStreamConnectionEvent = exports.DataStreamServer = exports.DataStreamServerEvent = exports.MessageType = exports.HDSProtocolError = exports.HDSProtocolSpecificErrorReason = exports.HDSStatus = exports.Topics = exports.Protocols = void 0;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
6
|
+
const crypto_1 = tslib_1.__importDefault(require("crypto"));
|
|
7
|
+
const debug_1 = tslib_1.__importDefault(require("debug"));
|
|
8
|
+
const events_1 = require("events");
|
|
9
|
+
const net_1 = tslib_1.__importDefault(require("net"));
|
|
10
|
+
const hapCrypto = tslib_1.__importStar(require("../util/hapCrypto"));
|
|
11
|
+
const DataStreamParser_1 = require("./DataStreamParser");
|
|
12
|
+
const debug = (0, debug_1.default)("HAP-NodeJS:DataStream:Server");
|
|
13
13
|
/**
|
|
14
14
|
* @group HomeKit Data Streams (HDS)
|
|
15
15
|
*/
|
|
@@ -70,21 +70,19 @@ var HDSProtocolSpecificErrorReason;
|
|
|
70
70
|
* E.g. it may be used to encode a {@link HDSStatus.PROTOCOL_SPECIFIC_ERROR} in the {@link Protocols.DATA_SEND} protocol.
|
|
71
71
|
* @group HomeKit Data Streams (HDS)
|
|
72
72
|
*/
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
class HDSProtocolError extends Error {
|
|
74
|
+
reason;
|
|
75
75
|
/**
|
|
76
76
|
* Initializes a new `HDSProtocolError`
|
|
77
77
|
* @param reason - The {@link HDSProtocolSpecificErrorReason}.
|
|
78
78
|
* Values MUST NOT be {@link HDSProtocolSpecificErrorReason.NORMAL}.
|
|
79
79
|
*/
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
constructor(reason) {
|
|
81
|
+
super("HDSProtocolError: " + reason);
|
|
82
82
|
(0, assert_1.default)(reason !== 0 /* HDSProtocolSpecificErrorReason.NORMAL */, "Cannot initialize a HDSProtocolError with NORMAL!");
|
|
83
|
-
|
|
84
|
-
return _this;
|
|
83
|
+
this.reason = reason;
|
|
85
84
|
}
|
|
86
|
-
|
|
87
|
-
}(Error));
|
|
85
|
+
}
|
|
88
86
|
exports.HDSProtocolError = HDSProtocolError;
|
|
89
87
|
var ServerState;
|
|
90
88
|
(function (ServerState) {
|
|
@@ -130,16 +128,19 @@ var DataStreamServerEvent;
|
|
|
130
128
|
* @group HomeKit Data Streams (HDS)
|
|
131
129
|
*/
|
|
132
130
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
131
|
+
class DataStreamServer extends events_1.EventEmitter {
|
|
132
|
+
static version = "1.0";
|
|
133
|
+
state = 0 /* ServerState.UNINITIALIZED */;
|
|
134
|
+
static accessoryToControllerInfo = Buffer.from("HDS-Read-Encryption-Key");
|
|
135
|
+
static controllerToAccessoryInfo = Buffer.from("HDS-Write-Encryption-Key");
|
|
136
|
+
tcpServer;
|
|
137
|
+
tcpPort;
|
|
138
|
+
preparedSessions = [];
|
|
139
|
+
connections = [];
|
|
140
|
+
removeListenersOnceClosed = false;
|
|
141
|
+
internalEventEmitter = new events_1.EventEmitter(); // used for message event and message request handlers
|
|
142
|
+
constructor() {
|
|
143
|
+
super();
|
|
143
144
|
}
|
|
144
145
|
/**
|
|
145
146
|
* Registers a new event handler to handle incoming event messages.
|
|
@@ -150,10 +151,10 @@ var DataStreamServer = /** @class */ (function (_super) {
|
|
|
150
151
|
* @param event - name of the event (also referred to as topic. See {@link Topics} for some known ones)
|
|
151
152
|
* @param handler - function to be called for every occurring event
|
|
152
153
|
*/
|
|
153
|
-
|
|
154
|
+
onEventMessage(protocol, event, handler) {
|
|
154
155
|
this.internalEventEmitter.on(protocol + "-e-" + event, handler);
|
|
155
156
|
return this;
|
|
156
|
-
}
|
|
157
|
+
}
|
|
157
158
|
/**
|
|
158
159
|
* Removes a registered event handler.
|
|
159
160
|
*
|
|
@@ -161,10 +162,10 @@ var DataStreamServer = /** @class */ (function (_super) {
|
|
|
161
162
|
* @param event - name of the event (also referred to as topic. See {@link Topics} for some known ones)
|
|
162
163
|
* @param handler - registered event handler
|
|
163
164
|
*/
|
|
164
|
-
|
|
165
|
+
removeEventHandler(protocol, event, handler) {
|
|
165
166
|
this.internalEventEmitter.removeListener(protocol + "-e-" + event, handler);
|
|
166
167
|
return this;
|
|
167
|
-
}
|
|
168
|
+
}
|
|
168
169
|
/**
|
|
169
170
|
* Registers a new request handler to handle incoming request messages.
|
|
170
171
|
* The handler is only called for a connection if for the give protocol no ProtocolHandler
|
|
@@ -174,10 +175,10 @@ var DataStreamServer = /** @class */ (function (_super) {
|
|
|
174
175
|
* @param request - name of the request (also referred to as topic. See {@link Topics} for some known ones)
|
|
175
176
|
* @param handler - function to be called for every occurring request
|
|
176
177
|
*/
|
|
177
|
-
|
|
178
|
+
onRequestMessage(protocol, request, handler) {
|
|
178
179
|
this.internalEventEmitter.on(protocol + "-r-" + request, handler);
|
|
179
180
|
return this;
|
|
180
|
-
}
|
|
181
|
+
}
|
|
181
182
|
/**
|
|
182
183
|
* Removes a registered request handler.
|
|
183
184
|
*
|
|
@@ -185,27 +186,26 @@ var DataStreamServer = /** @class */ (function (_super) {
|
|
|
185
186
|
* @param request - name of the request (also referred to as topic. See {@link Topics} for some known ones)
|
|
186
187
|
* @param handler - registered request handler
|
|
187
188
|
*/
|
|
188
|
-
|
|
189
|
+
removeRequestHandler(protocol, request, handler) {
|
|
189
190
|
this.internalEventEmitter.removeListener(protocol + "-r-" + request, handler);
|
|
190
191
|
return this;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
var _this = this;
|
|
192
|
+
}
|
|
193
|
+
prepareSession(connection, controllerKeySalt, callback) {
|
|
194
194
|
debug("Preparing for incoming HDS connection from %s", connection.sessionID);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
195
|
+
const accessoryKeySalt = crypto_1.default.randomBytes(32);
|
|
196
|
+
const salt = Buffer.concat([controllerKeySalt, accessoryKeySalt]);
|
|
197
|
+
const accessoryToControllerEncryptionKey = hapCrypto.HKDF("sha512", salt, connection.encryption.sharedSecret, DataStreamServer.accessoryToControllerInfo, 32);
|
|
198
|
+
const controllerToAccessoryEncryptionKey = hapCrypto.HKDF("sha512", salt, connection.encryption.sharedSecret, DataStreamServer.controllerToAccessoryInfo, 32);
|
|
199
|
+
const preparedSession = {
|
|
200
200
|
connection: connection,
|
|
201
201
|
accessoryToControllerEncryptionKey: accessoryToControllerEncryptionKey,
|
|
202
202
|
controllerToAccessoryEncryptionKey: controllerToAccessoryEncryptionKey,
|
|
203
203
|
accessoryKeySalt: accessoryKeySalt,
|
|
204
|
-
connectTimeout: setTimeout(
|
|
204
|
+
connectTimeout: setTimeout(() => this.timeoutPreparedSession(preparedSession), 10000),
|
|
205
205
|
};
|
|
206
206
|
preparedSession.connectTimeout.unref();
|
|
207
207
|
this.preparedSessions.push(preparedSession);
|
|
208
|
-
this.checkTCPServerEstablished(preparedSession,
|
|
208
|
+
this.checkTCPServerEstablished(preparedSession, (error) => {
|
|
209
209
|
if (error) {
|
|
210
210
|
callback(error);
|
|
211
211
|
}
|
|
@@ -213,17 +213,16 @@ var DataStreamServer = /** @class */ (function (_super) {
|
|
|
213
213
|
callback(undefined, preparedSession);
|
|
214
214
|
}
|
|
215
215
|
});
|
|
216
|
-
}
|
|
217
|
-
|
|
216
|
+
}
|
|
217
|
+
timeoutPreparedSession(preparedSession) {
|
|
218
218
|
debug("Prepared HDS session timed out out since no connection was opened for 10 seconds (%s)", preparedSession.connection.sessionID);
|
|
219
|
-
|
|
219
|
+
const index = this.preparedSessions.indexOf(preparedSession);
|
|
220
220
|
if (index >= 0) {
|
|
221
221
|
this.preparedSessions.splice(index, 1);
|
|
222
222
|
}
|
|
223
223
|
this.checkCloseable();
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
var _this = this;
|
|
224
|
+
}
|
|
225
|
+
checkTCPServerEstablished(preparedSession, callback) {
|
|
227
226
|
switch (this.state) {
|
|
228
227
|
case 0 /* ServerState.UNINITIALIZED */:
|
|
229
228
|
debug("Starting up TCP server.");
|
|
@@ -245,34 +244,34 @@ var DataStreamServer = /** @class */ (function (_super) {
|
|
|
245
244
|
break;
|
|
246
245
|
case 3 /* ServerState.CLOSING */:
|
|
247
246
|
debug("TCP socket is currently closing. Trying again when server is fully closed and opening a new one then.");
|
|
248
|
-
this.tcpServer.once("close",
|
|
247
|
+
this.tcpServer.once("close", () => setTimeout(() => this.checkTCPServerEstablished(preparedSession, callback), 10));
|
|
249
248
|
break;
|
|
250
249
|
}
|
|
251
|
-
}
|
|
252
|
-
|
|
250
|
+
}
|
|
251
|
+
listening(preparedSession, callback) {
|
|
253
252
|
this.state = 2 /* ServerState.LISTENING */;
|
|
254
|
-
|
|
253
|
+
const address = this.tcpServer.address();
|
|
255
254
|
if (address && typeof address !== "string") { // address is only typeof string when listening to a pipe or unix socket
|
|
256
255
|
this.tcpPort = address.port;
|
|
257
256
|
preparedSession.port = address.port;
|
|
258
257
|
debug("TCP server is now listening for new data stream connections on port %s", address.port);
|
|
259
258
|
callback();
|
|
260
259
|
}
|
|
261
|
-
}
|
|
262
|
-
|
|
260
|
+
}
|
|
261
|
+
onConnection(socket) {
|
|
263
262
|
debug("[%s] New DataStream connection was established", socket.remoteAddress);
|
|
264
263
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
265
|
-
|
|
264
|
+
const connection = new DataStreamConnection(socket);
|
|
266
265
|
connection.on("identification" /* DataStreamConnectionEvent.IDENTIFICATION */, this.handleSessionIdentification.bind(this, connection));
|
|
267
266
|
connection.on("handle-message-globally" /* DataStreamConnectionEvent.HANDLE_MESSAGE_GLOBALLY */, this.handleMessageGlobally.bind(this, connection));
|
|
268
267
|
connection.on("closed" /* DataStreamConnectionEvent.CLOSED */, this.connectionClosed.bind(this, connection));
|
|
269
268
|
this.connections.push(connection);
|
|
270
269
|
this.emit("connection-opened" /* DataStreamServerEvent.CONNECTION_OPENED */, connection);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
for (
|
|
275
|
-
|
|
270
|
+
}
|
|
271
|
+
handleSessionIdentification(connection, firstFrame, callback) {
|
|
272
|
+
let identifiedSession = undefined;
|
|
273
|
+
for (let i = 0; i < this.preparedSessions.length; i++) {
|
|
274
|
+
const preparedSession = this.preparedSessions[i];
|
|
276
275
|
// if we successfully decrypt the first frame with this key we know to which session this connection belongs
|
|
277
276
|
if (connection.decryptHDSFrame(firstFrame, preparedSession.controllerToAccessoryEncryptionKey)) {
|
|
278
277
|
identifiedSession = preparedSession;
|
|
@@ -282,7 +281,7 @@ var DataStreamServer = /** @class */ (function (_super) {
|
|
|
282
281
|
callback(identifiedSession);
|
|
283
282
|
if (identifiedSession) {
|
|
284
283
|
debug("[%s] Connection was successfully identified (linked with sessionId: %s)", connection.remoteAddress, identifiedSession.connection.sessionID);
|
|
285
|
-
|
|
284
|
+
const index = this.preparedSessions.indexOf(identifiedSession);
|
|
286
285
|
if (index >= 0) {
|
|
287
286
|
this.preparedSessions.splice(index, 1);
|
|
288
287
|
}
|
|
@@ -296,12 +295,11 @@ var DataStreamServer = /** @class */ (function (_super) {
|
|
|
296
295
|
debug("[%s] Could not identify connection. Terminating.", connection.remoteAddress);
|
|
297
296
|
connection.close(); // disconnecting since first message was not a valid hello
|
|
298
297
|
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
var _a;
|
|
298
|
+
}
|
|
299
|
+
handleMessageGlobally(connection, message) {
|
|
302
300
|
assert_1.default.notStrictEqual(message.type, 3 /* MessageType.RESPONSE */); // responses can't physically get here
|
|
303
|
-
|
|
304
|
-
|
|
301
|
+
let separator = "";
|
|
302
|
+
const args = [];
|
|
305
303
|
if (message.type === 1 /* MessageType.EVENT */) {
|
|
306
304
|
separator = "-e-";
|
|
307
305
|
}
|
|
@@ -310,9 +308,9 @@ var DataStreamServer = /** @class */ (function (_super) {
|
|
|
310
308
|
args.push(message.id);
|
|
311
309
|
}
|
|
312
310
|
args.push(message.message);
|
|
313
|
-
|
|
311
|
+
let hadListeners;
|
|
314
312
|
try {
|
|
315
|
-
hadListeners =
|
|
313
|
+
hadListeners = this.internalEventEmitter.emit(message.protocol + separator + message.topic, connection, ...args);
|
|
316
314
|
}
|
|
317
315
|
catch (error) {
|
|
318
316
|
hadListeners = true;
|
|
@@ -322,8 +320,8 @@ var DataStreamServer = /** @class */ (function (_super) {
|
|
|
322
320
|
if (!hadListeners) {
|
|
323
321
|
debug("[%s] WARNING no handler was found for message: %o", connection.remoteAddress, message);
|
|
324
322
|
}
|
|
325
|
-
}
|
|
326
|
-
|
|
323
|
+
}
|
|
324
|
+
connectionClosed(connection) {
|
|
327
325
|
debug("[%s] DataStream connection closed", connection.remoteAddress);
|
|
328
326
|
this.connections.splice(this.connections.indexOf(connection), 1);
|
|
329
327
|
this.emit("connection-closed" /* DataStreamServerEvent.CONNECTION_CLOSED */, connection);
|
|
@@ -331,49 +329,34 @@ var DataStreamServer = /** @class */ (function (_super) {
|
|
|
331
329
|
if (this.state === 3 /* ServerState.CLOSING */ && this.removeListenersOnceClosed && this.connections.length === 0) {
|
|
332
330
|
this.removeAllListeners(); // see this.destroy()
|
|
333
331
|
}
|
|
334
|
-
}
|
|
335
|
-
|
|
332
|
+
}
|
|
333
|
+
checkCloseable() {
|
|
336
334
|
if (this.connections.length === 0 && this.preparedSessions.length === 0 && this.state < 3 /* ServerState.CLOSING */) {
|
|
337
335
|
debug("Last connection disconnected. Closing the server now.");
|
|
338
336
|
this.state = 3 /* ServerState.CLOSING */;
|
|
339
337
|
this.tcpServer.close();
|
|
340
338
|
}
|
|
341
|
-
}
|
|
339
|
+
}
|
|
342
340
|
/**
|
|
343
341
|
* This method will fully stop the DataStreamServer
|
|
344
342
|
*/
|
|
345
|
-
|
|
346
|
-
var e_1, _a;
|
|
343
|
+
destroy() {
|
|
347
344
|
if (this.state > 0 /* ServerState.UNINITIALIZED */ && this.state < 3 /* ServerState.CLOSING */) {
|
|
348
345
|
this.tcpServer.close();
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
var connection = _c.value;
|
|
352
|
-
connection.close();
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
356
|
-
finally {
|
|
357
|
-
try {
|
|
358
|
-
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
359
|
-
}
|
|
360
|
-
finally { if (e_1) throw e_1.error; }
|
|
346
|
+
for (const connection of this.connections) {
|
|
347
|
+
connection.close();
|
|
361
348
|
}
|
|
362
349
|
}
|
|
363
350
|
this.state = 3 /* ServerState.CLOSING */;
|
|
364
351
|
this.removeListenersOnceClosed = true;
|
|
365
352
|
this.internalEventEmitter.removeAllListeners();
|
|
366
|
-
}
|
|
367
|
-
|
|
353
|
+
}
|
|
354
|
+
closed() {
|
|
368
355
|
this.tcpServer = undefined;
|
|
369
356
|
this.tcpPort = undefined;
|
|
370
357
|
this.state = 0 /* ServerState.UNINITIALIZED */;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
DataStreamServer.accessoryToControllerInfo = Buffer.from("HDS-Read-Encryption-Key");
|
|
374
|
-
DataStreamServer.controllerToAccessoryInfo = Buffer.from("HDS-Write-Encryption-Key");
|
|
375
|
-
return DataStreamServer;
|
|
376
|
-
}(events_1.EventEmitter));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
377
360
|
exports.DataStreamServer = DataStreamServer;
|
|
378
361
|
/**
|
|
379
362
|
* @group HomeKit Data Streams (HDS)
|
|
@@ -408,15 +391,13 @@ var HDSConnectionErrorType;
|
|
|
408
391
|
/**
|
|
409
392
|
* @group HomeKit Data Streams (HDS)
|
|
410
393
|
*/
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
return HDSConnectionError;
|
|
419
|
-
}(Error));
|
|
394
|
+
class HDSConnectionError extends Error {
|
|
395
|
+
type;
|
|
396
|
+
constructor(message, type) {
|
|
397
|
+
super(message);
|
|
398
|
+
this.type = type;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
420
401
|
exports.HDSConnectionError = HDSConnectionError;
|
|
421
402
|
/**
|
|
422
403
|
* DataStream connection which holds any necessary state information, encryption and decryption keys, manages
|
|
@@ -425,59 +406,69 @@ exports.HDSConnectionError = HDSConnectionError;
|
|
|
425
406
|
* @group HomeKit Data Streams (HDS)
|
|
426
407
|
*/
|
|
427
408
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
409
|
+
class DataStreamConnection extends events_1.EventEmitter {
|
|
410
|
+
static MAX_PAYLOAD_LENGTH = 0b11111111111111111111;
|
|
411
|
+
socket;
|
|
412
|
+
connection; // reference to the hap connection. is present when state > UNIDENTIFIED
|
|
413
|
+
remoteAddress;
|
|
414
|
+
/*
|
|
415
|
+
Since our DataStream server does only listen on one port and this port is supplied to every client
|
|
416
|
+
which wants to connect, we do not really know which client is who when we receive a tcp connection.
|
|
417
|
+
Thus, we find the correct PreparedDataStreamSession object by testing the encryption keys of all available
|
|
418
|
+
prepared sessions. Then we can reference this hds connection with the correct hap connection and mark it as identified.
|
|
419
|
+
*/
|
|
420
|
+
state = 0 /* ConnectionState.UNIDENTIFIED */;
|
|
421
|
+
accessoryToControllerEncryptionKey;
|
|
422
|
+
controllerToAccessoryEncryptionKey;
|
|
423
|
+
accessoryToControllerNonce;
|
|
424
|
+
accessoryToControllerNonceBuffer;
|
|
425
|
+
controllerToAccessoryNonce;
|
|
426
|
+
controllerToAccessoryNonceBuffer;
|
|
427
|
+
frameBuffer; // used to store incomplete HDS frames
|
|
428
|
+
hapConnectionClosedListener;
|
|
429
|
+
protocolHandlers = {}; // used to store protocolHandlers identified by their protocol name
|
|
430
|
+
responseHandlers = {}; // used to store responseHandlers indexed by their respective requestId
|
|
431
|
+
responseTimers = {}; // used to store response timeouts indexed by their respective requestId
|
|
432
|
+
helloTimer;
|
|
433
|
+
constructor(socket) {
|
|
434
|
+
super();
|
|
435
|
+
this.socket = socket;
|
|
436
|
+
this.remoteAddress = socket.remoteAddress;
|
|
437
|
+
this.socket.setNoDelay(true); // disable Nagle algorithm
|
|
438
|
+
this.socket.setKeepAlive(true);
|
|
439
|
+
this.accessoryToControllerNonce = 0;
|
|
440
|
+
this.accessoryToControllerNonceBuffer = Buffer.alloc(8);
|
|
441
|
+
this.controllerToAccessoryNonce = 0;
|
|
442
|
+
this.controllerToAccessoryNonceBuffer = Buffer.alloc(8);
|
|
443
|
+
this.hapConnectionClosedListener = this.onHAPSessionClosed.bind(this);
|
|
444
|
+
this.addProtocolHandler("control" /* Protocols.CONTROL */, {
|
|
445
|
+
requestHandler: {
|
|
446
|
+
["hello" /* Topics.HELLO */]: this.handleHello.bind(this),
|
|
447
|
+
},
|
|
456
448
|
});
|
|
457
|
-
|
|
458
|
-
debug("[%s] Hello message did not arrive in time. Killing the connection",
|
|
459
|
-
|
|
449
|
+
this.helloTimer = setTimeout(() => {
|
|
450
|
+
debug("[%s] Hello message did not arrive in time. Killing the connection", this.remoteAddress);
|
|
451
|
+
this.close();
|
|
460
452
|
}, 10000);
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
453
|
+
this.socket.on("data", this.onSocketData.bind(this));
|
|
454
|
+
this.socket.on("error", this.onSocketError.bind(this));
|
|
455
|
+
this.socket.on("close", this.onSocketClose.bind(this));
|
|
464
456
|
// this is to mitigate the event emitter "memory leak warning".
|
|
465
457
|
// e.g. with HSV there might be multiple cameras subscribing to the CLOSE event. one subscription for
|
|
466
458
|
// every active recording stream on a camera. The default limit of 10 might be easily reached.
|
|
467
459
|
// Setting a high limit isn't the prefect solution, but will avoid false positives but ensures that
|
|
468
460
|
// a warning is still be printed if running long enough.
|
|
469
|
-
|
|
470
|
-
return _this;
|
|
461
|
+
this.setMaxListeners(100);
|
|
471
462
|
}
|
|
472
463
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
473
|
-
|
|
464
|
+
handleHello(id, message) {
|
|
474
465
|
// that hello is indeed the _first_ message received is verified in onSocketData(...)
|
|
475
466
|
debug("[%s] Received hello message from client: %o", this.remoteAddress, message);
|
|
476
467
|
clearTimeout(this.helloTimer);
|
|
477
468
|
this.helloTimer = undefined;
|
|
478
469
|
this.state = 2 /* ConnectionState.READY */;
|
|
479
470
|
this.sendResponse("control" /* Protocols.CONTROL */, "hello" /* Topics.HELLO */, id);
|
|
480
|
-
}
|
|
471
|
+
}
|
|
481
472
|
/**
|
|
482
473
|
* Registers a new protocol handler to handle incoming messages.
|
|
483
474
|
* The same protocol cannot be registered multiple times.
|
|
@@ -485,25 +476,25 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
485
476
|
* @param protocol - name of the protocol to register the handler for
|
|
486
477
|
* @param protocolHandler - object to be registered as protocol handler
|
|
487
478
|
*/
|
|
488
|
-
|
|
479
|
+
addProtocolHandler(protocol, protocolHandler) {
|
|
489
480
|
if (this.protocolHandlers[protocol] !== undefined) {
|
|
490
481
|
return false;
|
|
491
482
|
}
|
|
492
483
|
this.protocolHandlers[protocol] = protocolHandler;
|
|
493
484
|
return true;
|
|
494
|
-
}
|
|
485
|
+
}
|
|
495
486
|
/**
|
|
496
487
|
* Removes a protocol handler if it is registered.
|
|
497
488
|
*
|
|
498
489
|
* @param protocol - name of the protocol to unregister the handler for
|
|
499
490
|
* @param protocolHandler - object which will be unregistered
|
|
500
491
|
*/
|
|
501
|
-
|
|
502
|
-
|
|
492
|
+
removeProtocolHandler(protocol, protocolHandler) {
|
|
493
|
+
const current = this.protocolHandlers[protocol];
|
|
503
494
|
if (current === protocolHandler) {
|
|
504
495
|
delete this.protocolHandlers[protocol];
|
|
505
496
|
}
|
|
506
|
-
}
|
|
497
|
+
}
|
|
507
498
|
/**
|
|
508
499
|
* Sends a new event message to the connected client.
|
|
509
500
|
*
|
|
@@ -512,14 +503,15 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
512
503
|
* @param message - message dictionary which gets sent along the event
|
|
513
504
|
*/
|
|
514
505
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
515
|
-
|
|
516
|
-
if (message === void 0) { message = {}; }
|
|
506
|
+
sendEvent(protocol, event, message = {}) {
|
|
517
507
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
518
|
-
|
|
508
|
+
const header = {};
|
|
519
509
|
header.protocol = protocol;
|
|
520
510
|
header.event = event;
|
|
521
|
-
this.
|
|
522
|
-
|
|
511
|
+
if (this.state === 2 /* ConnectionState.READY */) {
|
|
512
|
+
this.sendHDSFrame(header, message);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
523
515
|
/**
|
|
524
516
|
* Sends a new request message to the connected client.
|
|
525
517
|
*
|
|
@@ -530,31 +522,29 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
530
522
|
* arrive in time or the status and the message dictionary from the response
|
|
531
523
|
*/
|
|
532
524
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (message === void 0) { message = {}; }
|
|
536
|
-
var requestId;
|
|
525
|
+
sendRequest(protocol, request, message = {}, callback) {
|
|
526
|
+
let requestId;
|
|
537
527
|
do { // generate unused requestId
|
|
538
528
|
// currently writing int64 to data stream is not really supported, so 32-bit int will be the max
|
|
539
529
|
requestId = Math.floor(Math.random() * 4294967295);
|
|
540
530
|
} while (this.responseHandlers[requestId] !== undefined);
|
|
541
531
|
this.responseHandlers[requestId] = callback;
|
|
542
|
-
this.responseTimers[requestId] = setTimeout(
|
|
532
|
+
this.responseTimers[requestId] = setTimeout(() => {
|
|
543
533
|
// we did not receive a response => close socket
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
delete
|
|
547
|
-
delete
|
|
534
|
+
this.close();
|
|
535
|
+
const handler = this.responseHandlers[requestId];
|
|
536
|
+
delete this.responseHandlers[requestId];
|
|
537
|
+
delete this.responseTimers[requestId];
|
|
548
538
|
// handler should be able to clean up their stuff
|
|
549
539
|
handler(new Error("timeout"), undefined, {});
|
|
550
540
|
}, 10000); // 10s timer
|
|
551
541
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
552
|
-
|
|
542
|
+
const header = {};
|
|
553
543
|
header.protocol = protocol;
|
|
554
544
|
header.request = request;
|
|
555
545
|
header.id = new DataStreamParser_1.Int64(requestId);
|
|
556
546
|
this.sendHDSFrame(header, message);
|
|
557
|
-
}
|
|
547
|
+
}
|
|
558
548
|
/**
|
|
559
549
|
* Send a new response message to a received request message to the client.
|
|
560
550
|
*
|
|
@@ -564,43 +554,40 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
564
554
|
* @param status - status indication if the request was successful. A status of zero indicates success.
|
|
565
555
|
* @param message - message dictionary which gets sent along the response
|
|
566
556
|
*/
|
|
567
|
-
|
|
557
|
+
sendResponse(protocol, response, id, status = HDSStatus.SUCCESS,
|
|
568
558
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
569
|
-
message) {
|
|
570
|
-
if (status === void 0) { status = HDSStatus.SUCCESS; }
|
|
571
|
-
if (message === void 0) { message = {}; }
|
|
559
|
+
message = {}) {
|
|
572
560
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
573
|
-
|
|
561
|
+
const header = {};
|
|
574
562
|
header.protocol = protocol;
|
|
575
563
|
header.response = response;
|
|
576
564
|
header.id = new DataStreamParser_1.Int64(id);
|
|
577
565
|
header.status = new DataStreamParser_1.Int64(status);
|
|
578
566
|
this.sendHDSFrame(header, message);
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
var _this = this;
|
|
567
|
+
}
|
|
568
|
+
onSocketData(data) {
|
|
582
569
|
if (this.state >= 3 /* ConnectionState.CLOSING */) {
|
|
583
570
|
return;
|
|
584
571
|
}
|
|
585
|
-
|
|
586
|
-
|
|
572
|
+
let frameIndex = 0;
|
|
573
|
+
const frames = this.decodeHDSFrames(data);
|
|
587
574
|
if (frames.length === 0) { // not enough data
|
|
588
575
|
return;
|
|
589
576
|
}
|
|
590
577
|
if (this.state === 0 /* ConnectionState.UNIDENTIFIED */) {
|
|
591
578
|
// at the beginning we are only interested in trying to decrypt the first frame in order to test decryption keys
|
|
592
|
-
|
|
593
|
-
this.emit("identification" /* DataStreamConnectionEvent.IDENTIFICATION */, firstFrame,
|
|
579
|
+
const firstFrame = frames[frameIndex++];
|
|
580
|
+
this.emit("identification" /* DataStreamConnectionEvent.IDENTIFICATION */, firstFrame, (identifiedSession) => {
|
|
594
581
|
if (identifiedSession) {
|
|
595
582
|
// horray, we found our connection
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
583
|
+
this.connection = identifiedSession.connection;
|
|
584
|
+
this.accessoryToControllerEncryptionKey = identifiedSession.accessoryToControllerEncryptionKey;
|
|
585
|
+
this.controllerToAccessoryEncryptionKey = identifiedSession.controllerToAccessoryEncryptionKey;
|
|
586
|
+
this.state = 1 /* ConnectionState.EXPECTING_HELLO */;
|
|
600
587
|
// below listener is removed in .close()
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
debug("[%s] Registering CLOSED handler to HAP connection. Connection currently has %d close handlers!",
|
|
588
|
+
this.connection.setMaxListeners(this.connection.getMaxListeners() + 1);
|
|
589
|
+
this.connection.on("closed" /* HAPConnectionEvent.CLOSED */, this.hapConnectionClosedListener); // register close listener
|
|
590
|
+
debug("[%s] Registering CLOSED handler to HAP connection. Connection currently has %d close handlers!", this.remoteAddress, this.connection.listeners("closed" /* HAPConnectionEvent.CLOSED */).length);
|
|
604
591
|
}
|
|
605
592
|
});
|
|
606
593
|
if (this.state === 0 /* ConnectionState.UNIDENTIFIED */) {
|
|
@@ -615,9 +602,9 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
615
602
|
return;
|
|
616
603
|
}
|
|
617
604
|
}
|
|
618
|
-
|
|
605
|
+
const messages = this.decodePayloads(frames); // decode contents of payload
|
|
619
606
|
if (this.state === 1 /* ConnectionState.EXPECTING_HELLO */) {
|
|
620
|
-
|
|
607
|
+
const firstMessage = messages[0];
|
|
621
608
|
if (firstMessage.protocol !== "control" /* Protocols.CONTROL */ || firstMessage.type !== 2 /* MessageType.REQUEST */ || firstMessage.topic !== "hello" /* Topics.HELLO */) {
|
|
622
609
|
// first message is not the expected hello request
|
|
623
610
|
debug("[%s] First message received was not the expected hello message. Instead got: %o", this.remoteAddress, firstMessage);
|
|
@@ -625,15 +612,15 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
625
612
|
return;
|
|
626
613
|
}
|
|
627
614
|
}
|
|
628
|
-
messages.forEach(
|
|
615
|
+
messages.forEach(message => {
|
|
629
616
|
if (message.type === 3 /* MessageType.RESPONSE */) {
|
|
630
617
|
// protocol and topic are currently not tested here; just assumed they are correct;
|
|
631
618
|
// probably they are as the requestId is unique per connection no matter what protocol is used
|
|
632
|
-
|
|
633
|
-
|
|
619
|
+
const responseHandler = this.responseHandlers[message.id];
|
|
620
|
+
const responseTimer = this.responseTimers[message.id];
|
|
634
621
|
if (responseTimer) {
|
|
635
622
|
clearTimeout(responseTimer);
|
|
636
|
-
delete
|
|
623
|
+
delete this.responseTimers[message.id];
|
|
637
624
|
}
|
|
638
625
|
if (!responseHandler) {
|
|
639
626
|
// we got a response to a request we did not send; we ignore it for now, since nobody will be hurt
|
|
@@ -644,88 +631,88 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
644
631
|
responseHandler(undefined, message.status, message.message);
|
|
645
632
|
}
|
|
646
633
|
catch (error) {
|
|
647
|
-
debug("[%s] Error occurred while dispatching response handler for HDS message: %o",
|
|
634
|
+
debug("[%s] Error occurred while dispatching response handler for HDS message: %o", this.remoteAddress, message);
|
|
648
635
|
debug(error.stack);
|
|
649
636
|
}
|
|
650
|
-
delete
|
|
637
|
+
delete this.responseHandlers[message.id];
|
|
651
638
|
}
|
|
652
639
|
else {
|
|
653
|
-
|
|
640
|
+
const handler = this.protocolHandlers[message.protocol];
|
|
654
641
|
if (handler === undefined) {
|
|
655
642
|
// send message to the server to check if there are some global handlers for it
|
|
656
|
-
|
|
643
|
+
this.emit("handle-message-globally" /* DataStreamConnectionEvent.HANDLE_MESSAGE_GLOBALLY */, message);
|
|
657
644
|
return;
|
|
658
645
|
}
|
|
659
646
|
if (message.type === 1 /* MessageType.EVENT */) {
|
|
660
|
-
|
|
647
|
+
let eventHandler;
|
|
661
648
|
if (!handler.eventHandler || !(eventHandler = handler.eventHandler[message.topic])) {
|
|
662
|
-
debug("[%s] WARNING no event handler was found for message: %o",
|
|
649
|
+
debug("[%s] WARNING no event handler was found for message: %o", this.remoteAddress, message);
|
|
663
650
|
return;
|
|
664
651
|
}
|
|
665
652
|
try {
|
|
666
653
|
eventHandler(message.message);
|
|
667
654
|
}
|
|
668
655
|
catch (error) {
|
|
669
|
-
debug("[%s] Error occurred while dispatching event handler for HDS message: %o",
|
|
656
|
+
debug("[%s] Error occurred while dispatching event handler for HDS message: %o", this.remoteAddress, message);
|
|
670
657
|
debug(error.stack);
|
|
671
658
|
}
|
|
672
659
|
}
|
|
673
660
|
else if (message.type === 2 /* MessageType.REQUEST */) {
|
|
674
|
-
|
|
661
|
+
let requestHandler;
|
|
675
662
|
if (!handler.requestHandler || !(requestHandler = handler.requestHandler[message.topic])) {
|
|
676
|
-
debug("[%s] WARNING no request handler was found for message: %o",
|
|
663
|
+
debug("[%s] WARNING no request handler was found for message: %o", this.remoteAddress, message);
|
|
677
664
|
return;
|
|
678
665
|
}
|
|
679
666
|
try {
|
|
680
667
|
requestHandler(message.id, message.message);
|
|
681
668
|
}
|
|
682
669
|
catch (error) {
|
|
683
|
-
debug("[%s] Error occurred while dispatching request handler for HDS message: %o",
|
|
670
|
+
debug("[%s] Error occurred while dispatching request handler for HDS message: %o", this.remoteAddress, message);
|
|
684
671
|
debug(error.stack);
|
|
685
672
|
}
|
|
686
673
|
}
|
|
687
674
|
else {
|
|
688
|
-
debug("[%s] Encountered unknown message type with id %d",
|
|
675
|
+
debug("[%s] Encountered unknown message type with id %d", this.remoteAddress, message.type);
|
|
689
676
|
}
|
|
690
677
|
}
|
|
691
678
|
});
|
|
692
|
-
}
|
|
693
|
-
|
|
679
|
+
}
|
|
680
|
+
decodeHDSFrames(data) {
|
|
694
681
|
if (this.frameBuffer !== undefined) {
|
|
695
682
|
data = Buffer.concat([this.frameBuffer, data]);
|
|
696
683
|
this.frameBuffer = undefined;
|
|
697
684
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
for (
|
|
685
|
+
const totalBufferLength = data.length;
|
|
686
|
+
const frames = [];
|
|
687
|
+
for (let frameBegin = 0; frameBegin < totalBufferLength;) {
|
|
701
688
|
if (frameBegin + 4 > totalBufferLength) {
|
|
702
689
|
// we don't have enough data in the buffer for the next header
|
|
703
690
|
this.frameBuffer = data.slice(frameBegin);
|
|
704
691
|
break;
|
|
705
692
|
}
|
|
706
|
-
|
|
707
|
-
|
|
693
|
+
const payloadType = data.readUInt8(frameBegin); // type defining structure of payload; 8-bit; currently expected to be 1
|
|
694
|
+
const payloadLength = data.readUIntBE(frameBegin + 1, 3); // read 24-bit big-endian uint length field
|
|
708
695
|
if (payloadLength > DataStreamConnection.MAX_PAYLOAD_LENGTH) {
|
|
709
696
|
debug("[%s] Connection send payload with size bigger than the maximum allow for data stream", this.remoteAddress);
|
|
710
697
|
this.close();
|
|
711
698
|
return [];
|
|
712
699
|
}
|
|
713
|
-
|
|
700
|
+
const remainingBufferLength = totalBufferLength - frameBegin - 4; // subtract 4 for payloadType (1-byte) and payloadLength (3-byte)
|
|
714
701
|
// check if the data from this frame is already there (payload + 16-byte authTag)
|
|
715
702
|
if (payloadLength + 16 > remainingBufferLength) {
|
|
716
703
|
// Frame is fragmented, so we wait until we receive more
|
|
717
704
|
this.frameBuffer = data.slice(frameBegin);
|
|
718
705
|
break;
|
|
719
706
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
707
|
+
const payloadBegin = frameBegin + 4;
|
|
708
|
+
const authTagBegin = payloadBegin + payloadLength;
|
|
709
|
+
const header = data.slice(frameBegin, payloadBegin); // header is also authenticated using authTag
|
|
710
|
+
const cipheredPayload = data.slice(payloadBegin, authTagBegin);
|
|
711
|
+
const plaintextPayload = Buffer.alloc(payloadLength);
|
|
712
|
+
const authTag = data.slice(authTagBegin, authTagBegin + 16);
|
|
726
713
|
frameBegin = authTagBegin + 16; // move to next frame
|
|
727
714
|
if (payloadType === 1) {
|
|
728
|
-
|
|
715
|
+
const hdsFrame = {
|
|
729
716
|
header: header,
|
|
730
717
|
cipheredPayload: cipheredPayload,
|
|
731
718
|
authTag: authTag,
|
|
@@ -737,13 +724,13 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
737
724
|
}
|
|
738
725
|
}
|
|
739
726
|
return frames;
|
|
740
|
-
}
|
|
727
|
+
}
|
|
741
728
|
/**
|
|
742
729
|
* @private file-private API
|
|
743
730
|
*/
|
|
744
|
-
|
|
731
|
+
decryptHDSFrame(frame, keyOverwrite) {
|
|
745
732
|
hapCrypto.writeUInt64LE(this.controllerToAccessoryNonce, this.controllerToAccessoryNonceBuffer, 0); // update nonce buffer
|
|
746
|
-
|
|
733
|
+
const key = keyOverwrite || this.controllerToAccessoryEncryptionKey;
|
|
747
734
|
try {
|
|
748
735
|
frame.plaintextPayload = hapCrypto.chacha20_poly1305_decryptAndVerify(key, this.controllerToAccessoryNonceBuffer, frame.header, frame.cipheredPayload, frame.authTag);
|
|
749
736
|
this.controllerToAccessoryNonce++; // we had a successful encryption, increment the nonce
|
|
@@ -753,31 +740,30 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
753
740
|
// frame decryption or authentication failed. Could happen when our guess for a PreparedDataStreamSession is wrong
|
|
754
741
|
return false;
|
|
755
742
|
}
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
var payload = frame.plaintextPayload;
|
|
743
|
+
}
|
|
744
|
+
decodePayloads(frames) {
|
|
745
|
+
const messages = [];
|
|
746
|
+
frames.forEach(frame => {
|
|
747
|
+
const payload = frame.plaintextPayload;
|
|
762
748
|
if (!payload) {
|
|
763
749
|
throw new HDSConnectionError("Reached illegal state. Encountered HDSFrame with wasn't decrypted yet!", 1 /* HDSConnectionErrorType.ILLEGAL_STATE */);
|
|
764
750
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
751
|
+
const headerLength = payload.readUInt8(0);
|
|
752
|
+
const messageLength = payload.length - headerLength - 1;
|
|
753
|
+
const headerBegin = 1;
|
|
754
|
+
const messageBegin = headerBegin + headerLength;
|
|
755
|
+
const headerPayload = new DataStreamParser_1.DataStreamReader(payload.slice(headerBegin, headerBegin + headerLength));
|
|
756
|
+
const messagePayload = new DataStreamParser_1.DataStreamReader(payload.slice(messageBegin, messageBegin + messageLength));
|
|
771
757
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
772
|
-
|
|
758
|
+
let headerDictionary;
|
|
773
759
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
774
|
-
|
|
760
|
+
let messageDictionary;
|
|
775
761
|
try {
|
|
776
762
|
headerDictionary = DataStreamParser_1.DataStreamParser.decode(headerPayload);
|
|
777
763
|
headerPayload.finished();
|
|
778
764
|
}
|
|
779
765
|
catch (error) {
|
|
780
|
-
debug("[%s] Failed to decode header payload: %s",
|
|
766
|
+
debug("[%s] Failed to decode header payload: %s", this.remoteAddress, error.message);
|
|
781
767
|
return;
|
|
782
768
|
}
|
|
783
769
|
try {
|
|
@@ -785,14 +771,14 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
785
771
|
messagePayload.finished();
|
|
786
772
|
}
|
|
787
773
|
catch (error) {
|
|
788
|
-
debug("[%s] Failed to decode message payload: %s (header: %o)",
|
|
774
|
+
debug("[%s] Failed to decode message payload: %s (header: %o)", this.remoteAddress, error.message, headerDictionary);
|
|
789
775
|
return;
|
|
790
776
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
777
|
+
let type;
|
|
778
|
+
const protocol = headerDictionary.protocol;
|
|
779
|
+
let topic;
|
|
780
|
+
let id = undefined;
|
|
781
|
+
let status = undefined;
|
|
796
782
|
if (headerDictionary.event !== undefined) {
|
|
797
783
|
type = 1 /* MessageType.EVENT */;
|
|
798
784
|
topic = headerDictionary.event;
|
|
@@ -809,10 +795,10 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
809
795
|
status = headerDictionary.status;
|
|
810
796
|
}
|
|
811
797
|
else {
|
|
812
|
-
debug("[%s] Encountered unknown payload header format: %o (message: %o)",
|
|
798
|
+
debug("[%s] Encountered unknown payload header format: %o (message: %o)", this.remoteAddress, headerDictionary, messageDictionary);
|
|
813
799
|
return;
|
|
814
800
|
}
|
|
815
|
-
|
|
801
|
+
const message = {
|
|
816
802
|
type: type,
|
|
817
803
|
protocol: protocol,
|
|
818
804
|
topic: topic,
|
|
@@ -823,30 +809,30 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
823
809
|
messages.push(message);
|
|
824
810
|
});
|
|
825
811
|
return messages;
|
|
826
|
-
}
|
|
812
|
+
}
|
|
827
813
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
828
|
-
|
|
814
|
+
sendHDSFrame(header, message) {
|
|
829
815
|
if (this.state >= 3 /* ConnectionState.CLOSING */) {
|
|
830
816
|
throw new HDSConnectionError("Cannot send message on closing/closed socket!", 2 /* HDSConnectionErrorType.CLOSED_SOCKET */);
|
|
831
817
|
}
|
|
832
|
-
|
|
833
|
-
|
|
818
|
+
const headerWriter = new DataStreamParser_1.DataStreamWriter();
|
|
819
|
+
const messageWriter = new DataStreamParser_1.DataStreamWriter();
|
|
834
820
|
DataStreamParser_1.DataStreamParser.encode(header, headerWriter);
|
|
835
821
|
DataStreamParser_1.DataStreamParser.encode(message, messageWriter);
|
|
836
|
-
|
|
822
|
+
const payloadHeaderBuffer = Buffer.alloc(1);
|
|
837
823
|
payloadHeaderBuffer.writeUInt8(headerWriter.length(), 0);
|
|
838
|
-
|
|
824
|
+
const payloadBuffer = Buffer.concat([payloadHeaderBuffer, headerWriter.getData(), messageWriter.getData()]);
|
|
839
825
|
if (payloadBuffer.length > DataStreamConnection.MAX_PAYLOAD_LENGTH) {
|
|
840
826
|
throw new HDSConnectionError("Tried sending payload with length larger than the maximum allowed for data stream", 3 /* HDSConnectionErrorType.MAX_PAYLOAD_LENGTH */);
|
|
841
827
|
}
|
|
842
|
-
|
|
828
|
+
const frameTypeBuffer = Buffer.alloc(1);
|
|
843
829
|
frameTypeBuffer.writeUInt8(1, 0);
|
|
844
|
-
|
|
830
|
+
let frameLengthBuffer = Buffer.alloc(4);
|
|
845
831
|
frameLengthBuffer.writeUInt32BE(payloadBuffer.length, 0);
|
|
846
832
|
frameLengthBuffer = frameLengthBuffer.slice(1, 4); // a bit hacky but the only real way to write 24-bit int in node
|
|
847
|
-
|
|
833
|
+
const frameHeader = Buffer.concat([frameTypeBuffer, frameLengthBuffer]);
|
|
848
834
|
hapCrypto.writeUInt64LE(this.accessoryToControllerNonce++, this.accessoryToControllerNonceBuffer);
|
|
849
|
-
|
|
835
|
+
const encrypted = hapCrypto.chacha20_poly1305_encryptAndSeal(this.accessoryToControllerEncryptionKey, this.accessoryToControllerNonceBuffer, frameHeader, payloadBuffer);
|
|
850
836
|
this.socket.write(Buffer.concat([frameHeader, encrypted.ciphertext, encrypted.authTag]));
|
|
851
837
|
/* Useful for debugging outgoing packages and detecting encoding errors
|
|
852
838
|
console.log("SENT DATA: " + payloadBuffer.toString("hex"));
|
|
@@ -859,37 +845,34 @@ var DataStreamConnection = /** @class */ (function (_super) {
|
|
|
859
845
|
const sentMessage = this.decodePayloads([frame])[0];
|
|
860
846
|
console.log("Sent message: " + JSON.stringify(sentMessage, null, 4));
|
|
861
847
|
//*/
|
|
862
|
-
}
|
|
863
|
-
|
|
848
|
+
}
|
|
849
|
+
close() {
|
|
864
850
|
if (this.state >= 3 /* ConnectionState.CLOSING */) {
|
|
865
851
|
return; // connection is already closing/closed
|
|
866
852
|
}
|
|
867
853
|
this.state = 3 /* ConnectionState.CLOSING */;
|
|
868
854
|
this.socket.end();
|
|
869
|
-
}
|
|
870
|
-
|
|
855
|
+
}
|
|
856
|
+
isConsideredClosed() {
|
|
871
857
|
return this.state >= 3 /* ConnectionState.CLOSING */;
|
|
872
|
-
}
|
|
873
|
-
|
|
858
|
+
}
|
|
859
|
+
onHAPSessionClosed() {
|
|
874
860
|
// If the hap connection is closed it is probably also a good idea to close the data stream connection
|
|
875
861
|
debug("[%s] HAP connection disconnected. Also closing DataStream connection now.", this.remoteAddress);
|
|
876
862
|
this.close();
|
|
877
|
-
}
|
|
878
|
-
|
|
863
|
+
}
|
|
864
|
+
onSocketError(error) {
|
|
879
865
|
debug("[%s] Encountered socket error: %s", this.remoteAddress, error.message);
|
|
880
866
|
// onSocketClose will be called next
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
var _a, _b;
|
|
867
|
+
}
|
|
868
|
+
onSocketClose() {
|
|
884
869
|
// this instance is now considered completely dead
|
|
885
870
|
this.state = 4 /* ConnectionState.CLOSED */;
|
|
886
871
|
this.emit("closed" /* DataStreamConnectionEvent.CLOSED */);
|
|
887
|
-
|
|
888
|
-
|
|
872
|
+
this.connection?.removeListener("closed" /* HAPConnectionEvent.CLOSED */, this.hapConnectionClosedListener);
|
|
873
|
+
this.connection?.setMaxListeners(this.connection.getMaxListeners() - 1);
|
|
889
874
|
this.removeAllListeners();
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
return DataStreamConnection;
|
|
893
|
-
}(events_1.EventEmitter));
|
|
875
|
+
}
|
|
876
|
+
}
|
|
894
877
|
exports.DataStreamConnection = DataStreamConnection;
|
|
895
878
|
//# sourceMappingURL=DataStreamServer.js.map
|