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