hap-nodejs
Version:
HAP-NodeJS is a Node.js implementation of HomeKit Accessory Server.
726 lines • 35.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ResolvedAdvertiser = exports.AvahiAdvertiser = exports.DBusInvokeError = exports.BonjourHAPAdvertiser = exports.CiaoAdvertiser = exports.AdvertiserEvent = exports.PairingFeatureFlag = exports.StatusFlag = void 0;
var tslib_1 = require("tslib");
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="../../@types/bonjour-hap.d.ts" />
var ciao_1 = tslib_1.__importDefault(require("@homebridge/ciao"));
var dbus_native_1 = tslib_1.__importDefault(require("@homebridge/dbus-native"));
var assert_1 = tslib_1.__importDefault(require("assert"));
var bonjour_hap_1 = tslib_1.__importDefault(require("bonjour-hap"));
var crypto_1 = tslib_1.__importDefault(require("crypto"));
var debug_1 = tslib_1.__importDefault(require("debug"));
var events_1 = require("events");
var promise_utils_1 = require("./util/promise-utils");
var debug = (0, debug_1.default)("HAP-NodeJS:Advertiser");
/**
* This enum lists all bitmasks for all known status flags.
* When the bit for the given bitmask is set, it represents the state described by the name.
*
* @group Advertiser
*/
var StatusFlag;
(function (StatusFlag) {
StatusFlag[StatusFlag["NOT_PAIRED"] = 1] = "NOT_PAIRED";
StatusFlag[StatusFlag["NOT_JOINED_WIFI"] = 2] = "NOT_JOINED_WIFI";
StatusFlag[StatusFlag["PROBLEM_DETECTED"] = 4] = "PROBLEM_DETECTED";
})(StatusFlag || (exports.StatusFlag = StatusFlag = {}));
/**
* This enum lists all bitmasks for all known pairing feature flags.
* When the bit for the given bitmask is set, it represents the state described by the name.
*
* @group Advertiser
*/
var PairingFeatureFlag;
(function (PairingFeatureFlag) {
PairingFeatureFlag[PairingFeatureFlag["SUPPORTS_HARDWARE_AUTHENTICATION"] = 1] = "SUPPORTS_HARDWARE_AUTHENTICATION";
PairingFeatureFlag[PairingFeatureFlag["SUPPORTS_SOFTWARE_AUTHENTICATION"] = 2] = "SUPPORTS_SOFTWARE_AUTHENTICATION";
})(PairingFeatureFlag || (exports.PairingFeatureFlag = PairingFeatureFlag = {}));
/**
* @group Advertiser
*/
var AdvertiserEvent;
(function (AdvertiserEvent) {
/**
* Emitted if the underlying mDNS advertisers signals, that the service name
* was automatically changed due to some naming conflicts on the network.
*/
AdvertiserEvent["UPDATED_NAME"] = "updated-name";
})(AdvertiserEvent || (exports.AdvertiserEvent = AdvertiserEvent = {}));
/**
* Advertiser uses mdns to broadcast the presence of an Accessory to the local network.
*
* Note that as of iOS 9, an accessory can only pair with a single client. Instead of pairing your
* accessories with multiple iOS devices in your home, Apple intends for you to use Home Sharing.
* To support this requirement, we provide the ability to be "discoverable" or not (via a "service flag" on the
* mdns payload).
*
* @group Advertiser
*/
var CiaoAdvertiser = /** @class */ (function (_super) {
tslib_1.__extends(CiaoAdvertiser, _super);
function CiaoAdvertiser(accessoryInfo, responderOptions, serviceOptions) {
var _this = _super.call(this) || this;
_this.accessoryInfo = accessoryInfo;
_this.setupHash = CiaoAdvertiser.computeSetupHash(accessoryInfo);
_this.responder = ciao_1.default.getResponder(tslib_1.__assign({}, responderOptions));
_this.advertisedService = _this.responder.createService(tslib_1.__assign({ name: _this.accessoryInfo.displayName, type: "hap" /* ServiceType.HAP */, txt: CiaoAdvertiser.createTxt(accessoryInfo, _this.setupHash) }, serviceOptions));
_this.advertisedService.on("name-change" /* ServiceEvent.NAME_CHANGED */, _this.emit.bind(_this, "updated-name" /* AdvertiserEvent.UPDATED_NAME */));
debug("Preparing Advertiser for '".concat(_this.accessoryInfo.displayName, "' using ciao backend!"));
return _this;
}
CiaoAdvertiser.prototype.initPort = function (port) {
this.advertisedService.updatePort(port);
};
CiaoAdvertiser.prototype.startAdvertising = function () {
debug("Starting to advertise '".concat(this.accessoryInfo.displayName, "' using ciao backend!"));
return this.advertisedService.advertise();
};
CiaoAdvertiser.prototype.updateAdvertisement = function (silent) {
var txt = CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash);
debug("Updating txt record (txt: %o, silent: %d)", txt, silent);
this.advertisedService.updateTxt(txt, silent);
};
CiaoAdvertiser.prototype.destroy = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
// advertisedService.destroy(); is called implicitly via the shutdown call
return [4 /*yield*/, this.responder.shutdown()];
case 1:
// advertisedService.destroy(); is called implicitly via the shutdown call
_a.sent();
this.removeAllListeners();
return [2 /*return*/];
}
});
});
};
CiaoAdvertiser.createTxt = function (accessoryInfo, setupHash) {
var statusFlags = [];
if (!accessoryInfo.paired()) {
statusFlags.push(1 /* StatusFlag.NOT_PAIRED */);
}
return {
"c#": accessoryInfo.getConfigVersion(), // current configuration number
ff: CiaoAdvertiser.ff(), // pairing feature flags
id: accessoryInfo.username, // device id
md: accessoryInfo.model, // model name
pv: CiaoAdvertiser.protocolVersion, // protocol version
"s#": 1, // current state number (must be 1)
sf: CiaoAdvertiser.sf.apply(CiaoAdvertiser, tslib_1.__spreadArray([], tslib_1.__read(statusFlags), false)), // status flags
ci: accessoryInfo.category,
sh: setupHash,
};
};
CiaoAdvertiser.computeSetupHash = function (accessoryInfo) {
var hash = crypto_1.default.createHash("sha512");
hash.update(accessoryInfo.setupID + accessoryInfo.username.toUpperCase());
return hash.digest().slice(0, 4).toString("base64");
};
CiaoAdvertiser.ff = function () {
var flags = [];
for (var _i = 0; _i < arguments.length; _i++) {
flags[_i] = arguments[_i];
}
var value = 0;
flags.forEach(function (flag) { return value |= flag; });
return value;
};
CiaoAdvertiser.sf = function () {
var flags = [];
for (var _i = 0; _i < arguments.length; _i++) {
flags[_i] = arguments[_i];
}
var value = 0;
flags.forEach(function (flag) { return value |= flag; });
return value;
};
CiaoAdvertiser.protocolVersion = "1.1";
CiaoAdvertiser.protocolVersionService = "1.1.0";
return CiaoAdvertiser;
}(events_1.EventEmitter));
exports.CiaoAdvertiser = CiaoAdvertiser;
/**
* Advertiser base on the legacy "bonjour-hap" library.
*
* @group Advertiser
*/
var BonjourHAPAdvertiser = /** @class */ (function (_super) {
tslib_1.__extends(BonjourHAPAdvertiser, _super);
function BonjourHAPAdvertiser(accessoryInfo, responderOptions, serviceOptions) {
var _this = _super.call(this) || this;
_this.destroyed = false;
_this.accessoryInfo = accessoryInfo;
_this.setupHash = CiaoAdvertiser.computeSetupHash(accessoryInfo);
_this.serviceOptions = serviceOptions;
_this.bonjour = (0, bonjour_hap_1.default)(responderOptions);
debug("Preparing Advertiser for '".concat(_this.accessoryInfo.displayName, "' using bonjour-hap backend!"));
return _this;
}
BonjourHAPAdvertiser.prototype.initPort = function (port) {
this.port = port;
};
BonjourHAPAdvertiser.prototype.startAdvertising = function () {
(0, assert_1.default)(!this.destroyed, "Can't advertise on a destroyed bonjour instance!");
if (this.port == null) {
throw new Error("Tried starting bonjour-hap advertisement without initializing port!");
}
debug("Starting to advertise '".concat(this.accessoryInfo.displayName, "' using bonjour-hap backend!"));
if (this.advertisement) {
this.destroy();
}
var hostname = this.accessoryInfo.username.replace(/:/ig, "_") + ".local";
this.advertisement = this.bonjour.publish(tslib_1.__assign({ name: this.accessoryInfo.displayName, type: "hap", port: this.port, txt: CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash), host: hostname, addUnsafeServiceEnumerationRecord: true }, this.serviceOptions));
return (0, promise_utils_1.PromiseTimeout)(1);
};
BonjourHAPAdvertiser.prototype.updateAdvertisement = function (silent) {
var txt = CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash);
debug("Updating txt record (txt: %o, silent: %d)", txt, silent);
if (this.advertisement) {
this.advertisement.updateTxt(txt, silent);
}
};
BonjourHAPAdvertiser.prototype.destroy = function () {
var _this = this;
if (this.advertisement) {
this.advertisement.stop(function () {
_this.advertisement.destroy();
_this.advertisement = undefined;
_this.bonjour.destroy();
});
}
else {
this.bonjour.destroy();
}
};
return BonjourHAPAdvertiser;
}(events_1.EventEmitter));
exports.BonjourHAPAdvertiser = BonjourHAPAdvertiser;
function messageBusConnectionResult(bus) {
return new Promise(function (resolve, reject) {
var errorHandler = function (error) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
bus.connection.removeListener("connect", connectHandler);
reject(error);
};
var connectHandler = function () {
bus.connection.removeListener("error", errorHandler);
resolve();
};
bus.connection.once("connect", connectHandler);
bus.connection.once("error", errorHandler);
});
}
/**
* @group Advertiser
*/
var DBusInvokeError = /** @class */ (function (_super) {
tslib_1.__extends(DBusInvokeError, _super);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function DBusInvokeError(errorObject) {
var _this = _super.call(this) || this;
Object.setPrototypeOf(_this, DBusInvokeError.prototype);
_this.name = "DBusInvokeError";
_this.errorName = errorObject.name;
if (Array.isArray(errorObject.message) && errorObject.message.length === 1) {
_this.message = errorObject.message[0];
}
else {
_this.message = errorObject.message.toString();
}
return _this;
}
return DBusInvokeError;
}(Error));
exports.DBusInvokeError = DBusInvokeError;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function dbusInvoke(bus, destination, path, dbusInterface, member, others) {
return new Promise(function (resolve, reject) {
var command = tslib_1.__assign({ destination: destination, path: path, interface: dbusInterface, member: member }, (others || {}));
bus.invoke(command, function (err, result) {
if (err) {
reject(new DBusInvokeError(err));
}
else {
resolve(result);
}
});
});
}
/**
* AvahiServerState.
*
* Refer to https://github.com/lathiat/avahi/blob/fd482a74625b8db8547b8cfca3ee3d3c6c721423/avahi-common/defs.h#L220-L227.
*
* @group Advertiser
*/
var AvahiServerState;
(function (AvahiServerState) {
// noinspection JSUnusedGlobalSymbols
AvahiServerState[AvahiServerState["INVALID"] = 0] = "INVALID";
AvahiServerState[AvahiServerState["REGISTERING"] = 1] = "REGISTERING";
AvahiServerState[AvahiServerState["RUNNING"] = 2] = "RUNNING";
AvahiServerState[AvahiServerState["COLLISION"] = 3] = "COLLISION";
AvahiServerState[AvahiServerState["FAILURE"] = 4] = "FAILURE";
})(AvahiServerState || (AvahiServerState = {}));
/**
* Advertiser based on the Avahi D-Bus library.
* For (very crappy) docs on the interface, see the XML files at: https://github.com/lathiat/avahi/tree/master/avahi-daemon.
*
* Refer to https://github.com/lathiat/avahi/blob/fd482a74625b8db8547b8cfca3ee3d3c6c721423/avahi-common/defs.h#L120-L155 for a
* rough API usage guide of Avahi.
*
* @group Advertiser
*/
var AvahiAdvertiser = /** @class */ (function (_super) {
tslib_1.__extends(AvahiAdvertiser, _super);
function AvahiAdvertiser(accessoryInfo) {
var _this = _super.call(this) || this;
_this.accessoryInfo = accessoryInfo;
_this.setupHash = CiaoAdvertiser.computeSetupHash(accessoryInfo);
debug("Preparing Advertiser for '".concat(_this.accessoryInfo.displayName, "' using Avahi backend!"));
_this.bus = dbus_native_1.default.systemBus();
_this.stateChangeHandler = _this.handleStateChangedEvent.bind(_this);
return _this;
}
AvahiAdvertiser.prototype.createTxt = function () {
return Object
.entries(CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash))
.map(function (el) { return Buffer.from(el[0] + "=" + el[1]); });
};
AvahiAdvertiser.prototype.initPort = function (port) {
this.port = port;
};
AvahiAdvertiser.prototype.startAdvertising = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _a, _b, error_1;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
if (this.port == null) {
throw new Error("Tried starting Avahi advertisement without initializing port!");
}
if (!this.bus) {
throw new Error("Tried to start Avahi advertisement on a destroyed advertiser!");
}
debug("Starting to advertise '".concat(this.accessoryInfo.displayName, "' using Avahi backend!"));
_a = this;
return [4 /*yield*/, AvahiAdvertiser.avahiInvoke(this.bus, "/", "Server", "EntryGroupNew")];
case 1:
_a.path = (_c.sent());
return [4 /*yield*/, AvahiAdvertiser.avahiInvoke(this.bus, this.path, "EntryGroup", "AddService", {
body: [
-1, // interface
-1, // protocol
0, // flags
this.accessoryInfo.displayName, // name
"_hap._tcp", // type
"", // domain
"", // host
this.port, // port
this.createTxt(), // txt
],
signature: "iiussssqaay",
})];
case 2:
_c.sent();
return [4 /*yield*/, AvahiAdvertiser.avahiInvoke(this.bus, this.path, "EntryGroup", "Commit")];
case 3:
_c.sent();
_c.label = 4;
case 4:
_c.trys.push([4, 7, , 8]);
if (!!this.avahiServerInterface) return [3 /*break*/, 6];
_b = this;
return [4 /*yield*/, AvahiAdvertiser.avahiInterface(this.bus, "Server")];
case 5:
_b.avahiServerInterface = _c.sent();
this.avahiServerInterface.on("StateChanged", this.stateChangeHandler);
_c.label = 6;
case 6: return [3 /*break*/, 8];
case 7:
error_1 = _c.sent();
// We have some problem on Synology https://github.com/homebridge/HAP-NodeJS/issues/993
console.warn("Failed to create listener for avahi-daemon server state. The system will not be notified about restarts of avahi-daemon " +
"and will therefore stay undiscoverable in those instances. Error message: " + error_1);
if (error_1.stack) {
debug("Detailed error: " + error_1.stack);
}
return [3 /*break*/, 8];
case 8: return [2 /*return*/];
}
});
});
};
/**
* Event handler for the `StateChanged` event of the `org.freedesktop.Avahi.Server` DBus interface.
*
* This is called once the state of the running avahi-daemon changes its running state.
* @param state - The state the server changed into {@see AvahiServerState}.
*/
AvahiAdvertiser.prototype.handleStateChangedEvent = function (state) {
if (state === 2 /* AvahiServerState.RUNNING */ && this.path) {
debug("Found Avahi daemon to have restarted!");
this.startAdvertising()
.catch(function (reason) { return console.error("Could not (re-)create mDNS advertisement. The HAP-Server won't be discoverable: " + reason); });
}
};
AvahiAdvertiser.prototype.updateAdvertisement = function (silent) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var error_2;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.bus) {
throw new Error("Tried to update Avahi advertisement on a destroyed advertiser!");
}
if (!this.path) {
debug("Tried to update advertisement without a valid `path`!");
return [2 /*return*/];
}
debug("Updating txt record (txt: %o, silent: %d)", CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash), silent);
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, AvahiAdvertiser.avahiInvoke(this.bus, this.path, "EntryGroup", "UpdateServiceTxt", {
body: [-1, -1, 0, this.accessoryInfo.displayName, "_hap._tcp", "", this.createTxt()],
signature: "iiusssaay",
})];
case 2:
_a.sent();
return [3 /*break*/, 4];
case 3:
error_2 = _a.sent();
console.error("Failed to update avahi advertisement: " + error_2);
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
});
};
AvahiAdvertiser.prototype.destroy = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var error_3;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.bus) {
throw new Error("Tried to destroy Avahi advertisement on a destroyed advertiser!");
}
if (!this.path) return [3 /*break*/, 5];
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, AvahiAdvertiser.avahiInvoke(this.bus, this.path, "EntryGroup", "Free")];
case 2:
_a.sent();
return [3 /*break*/, 4];
case 3:
error_3 = _a.sent();
// Typically, this fails if e.g. avahi service was stopped in the meantime.
debug("Destroying Avahi advertisement failed: " + error_3);
return [3 /*break*/, 4];
case 4:
this.path = undefined;
_a.label = 5;
case 5:
if (this.avahiServerInterface) {
this.avahiServerInterface.removeListener("StateChanged", this.stateChangeHandler);
this.avahiServerInterface = undefined;
}
this.bus.connection.stream.destroy();
this.bus = undefined;
return [2 /*return*/];
}
});
});
};
AvahiAdvertiser.isAvailable = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var bus, error_4, version, error_5;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
bus = dbus_native_1.default.systemBus();
_a.label = 1;
case 1:
_a.trys.push([1, , 9, 10]);
_a.label = 2;
case 2:
_a.trys.push([2, 4, , 5]);
return [4 /*yield*/, messageBusConnectionResult(bus)];
case 3:
_a.sent();
return [3 /*break*/, 5];
case 4:
error_4 = _a.sent();
debug("Avahi/DBus classified unavailable due to missing dbus interface!");
return [2 /*return*/, false];
case 5:
_a.trys.push([5, 7, , 8]);
return [4 /*yield*/, this.avahiInvoke(bus, "/", "Server", "GetVersionString")];
case 6:
version = _a.sent();
debug("Detected Avahi over DBus interface running version '%s'.", version);
return [3 /*break*/, 8];
case 7:
error_5 = _a.sent();
debug("Avahi/DBus classified unavailable due to missing avahi interface!");
return [2 /*return*/, false];
case 8: return [2 /*return*/, true];
case 9:
bus.connection.stream.destroy();
return [7 /*endfinally*/];
case 10: return [2 /*return*/];
}
});
});
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
AvahiAdvertiser.avahiInvoke = function (bus, path, dbusInterface, member, others) {
return dbusInvoke(bus, "org.freedesktop.Avahi", path, "org.freedesktop.Avahi.".concat(dbusInterface), member, others);
};
AvahiAdvertiser.avahiInterface = function (bus, dbusInterface) {
return new Promise(function (resolve, reject) {
bus
.getService("org.freedesktop.Avahi")
.getInterface("/", "org.freedesktop.Avahi." + dbusInterface, function (error, iface) {
if (error || !iface) {
reject(error !== null && error !== void 0 ? error : new Error("Interface not present!"));
}
else {
resolve(iface);
}
});
});
};
return AvahiAdvertiser;
}(events_1.EventEmitter));
exports.AvahiAdvertiser = AvahiAdvertiser;
var RESOLVED_PERMISSIONS_ERRORS = [
"org.freedesktop.DBus.Error.AccessDenied",
"org.freedesktop.DBus.Error.AuthFailed",
"org.freedesktop.DBus.Error.InteractiveAuthorizationRequired",
];
/**
* Advertiser based on the systemd-resolved D-Bus library.
* For docs on the interface, see: https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html
*
* @group Advertiser
*/
var ResolvedAdvertiser = /** @class */ (function (_super) {
tslib_1.__extends(ResolvedAdvertiser, _super);
function ResolvedAdvertiser(accessoryInfo) {
var _this = _super.call(this) || this;
_this.accessoryInfo = accessoryInfo;
_this.setupHash = CiaoAdvertiser.computeSetupHash(accessoryInfo);
_this.bus = dbus_native_1.default.systemBus();
debug("Preparing Advertiser for '".concat(_this.accessoryInfo.displayName, "' using systemd-resolved backend!"));
return _this;
}
ResolvedAdvertiser.prototype.createTxt = function () {
return Object
.entries(CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash))
.map(function (el) { return [el[0].toString(), Buffer.from(el[1].toString())]; });
};
ResolvedAdvertiser.prototype.initPort = function (port) {
this.port = port;
};
ResolvedAdvertiser.prototype.startAdvertising = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _a, error_6;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
if (this.port == null) {
throw new Error("Tried starting systemd-resolved advertisement without initializing port!");
}
if (!this.bus) {
throw new Error("Tried to start systemd-resolved advertisement on a destroyed advertiser!");
}
debug("Starting to advertise '".concat(this.accessoryInfo.displayName, "' using systemd-resolved backend!"));
_b.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
_a = this;
return [4 /*yield*/, ResolvedAdvertiser.managerInvoke(this.bus, "RegisterService", {
body: [
this.accessoryInfo.displayName, // name
this.accessoryInfo.displayName, // name_template
"_hap._tcp", // type
this.port, // service_port
0, // service_priority
0, // service_weight
[this.createTxt()], // txt_datas
],
signature: "sssqqqaa{say}",
})];
case 2:
_a.path = _b.sent();
return [3 /*break*/, 4];
case 3:
error_6 = _b.sent();
if (error_6 instanceof DBusInvokeError) {
if (RESOLVED_PERMISSIONS_ERRORS.includes(error_6.errorName)) {
error_6.message = "Permissions issue. See https://homebridge.io/w/mDNS-Options for more info. ".concat(error_6.message);
}
}
throw error_6;
case 4: return [2 /*return*/];
}
});
});
};
ResolvedAdvertiser.prototype.updateAdvertisement = function (silent) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.bus) {
throw new Error("Tried to update systemd-resolved advertisement on a destroyed advertiser!");
}
debug("Updating txt record (txt: %o, silent: %d)", CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash), silent);
// Currently, systemd-resolved has no way to update an existing record.
return [4 /*yield*/, this.stopAdvertising()];
case 1:
// Currently, systemd-resolved has no way to update an existing record.
_a.sent();
return [4 /*yield*/, this.startAdvertising()];
case 2:
_a.sent();
return [2 /*return*/];
}
});
});
};
ResolvedAdvertiser.prototype.stopAdvertising = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var error_7;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.bus) {
throw new Error("Tried to destroy systemd-resolved advertisement on a destroyed advertiser!");
}
if (!this.path) return [3 /*break*/, 5];
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, ResolvedAdvertiser.managerInvoke(this.bus, "UnregisterService", {
body: [this.path],
signature: "o",
})];
case 2:
_a.sent();
return [3 /*break*/, 4];
case 3:
error_7 = _a.sent();
// Typically, this fails if e.g. systemd-resolved service was stopped in the meantime.
debug("Destroying systemd-resolved advertisement failed: " + error_7);
return [3 /*break*/, 4];
case 4:
this.path = undefined;
_a.label = 5;
case 5: return [2 /*return*/];
}
});
});
};
ResolvedAdvertiser.prototype.destroy = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.bus) {
throw new Error("Tried to destroy systemd-resolved advertisement on a destroyed advertiser!");
}
return [4 /*yield*/, this.stopAdvertising()];
case 1:
_a.sent();
this.bus.connection.stream.destroy();
this.bus = undefined;
return [2 /*return*/];
}
});
});
};
ResolvedAdvertiser.isAvailable = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var bus, error_8, error_9, mdnsStatus, error_10;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
bus = dbus_native_1.default.systemBus();
_a.label = 1;
case 1:
_a.trys.push([1, , 12, 13]);
_a.label = 2;
case 2:
_a.trys.push([2, 4, , 5]);
return [4 /*yield*/, messageBusConnectionResult(bus)];
case 3:
_a.sent();
return [3 /*break*/, 5];
case 4:
error_8 = _a.sent();
debug("systemd-resolved/DBus classified unavailable due to missing dbus interface!");
return [2 /*return*/, false];
case 5:
_a.trys.push([5, 7, , 8]);
// Ensure that systemd-resolved is accessible.
return [4 /*yield*/, this.managerInvoke(bus, "ResolveHostname", {
body: [0, "127.0.0.1", 0, 0],
signature: "isit",
})];
case 6:
// Ensure that systemd-resolved is accessible.
_a.sent();
debug("Detected systemd-resolved over DBus interface running version.");
return [3 /*break*/, 8];
case 7:
error_9 = _a.sent();
debug("systemd-resolved/DBus classified unavailable due to missing systemd-resolved interface!");
return [2 /*return*/, false];
case 8:
_a.trys.push([8, 10, , 11]);
return [4 /*yield*/, this.resolvedInvoke(bus, "org.freedesktop.DBus.Properties", "Get", {
body: ["org.freedesktop.resolve1.Manager", "MulticastDNS"],
signature: "ss",
})];
case 9:
mdnsStatus = _a.sent();
if (mdnsStatus[0][0].type !== "s") {
throw new Error("Invalid type for MulticastDNS");
}
if (mdnsStatus[1][0] !== "yes") {
debug("systemd-resolved/DBus classified unavailable because MulticastDNS is not enabled!");
return [2 /*return*/, false];
}
return [3 /*break*/, 11];
case 10:
error_10 = _a.sent();
debug("systemd-resolved/DBus classified unavailable due to failure checking system status: " + error_10);
return [2 /*return*/, false];
case 11: return [2 /*return*/, true];
case 12:
bus.connection.stream.destroy();
return [7 /*endfinally*/];
case 13: return [2 /*return*/];
}
});
});
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ResolvedAdvertiser.resolvedInvoke = function (bus, dbusInterface, member, others) {
return dbusInvoke(bus, "org.freedesktop.resolve1", "/org/freedesktop/resolve1", dbusInterface, member, others);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ResolvedAdvertiser.managerInvoke = function (bus, member, others) {
return this.resolvedInvoke(bus, "org.freedesktop.resolve1.Manager", member, others);
};
return ResolvedAdvertiser;
}(events_1.EventEmitter));
exports.ResolvedAdvertiser = ResolvedAdvertiser;
//# sourceMappingURL=Advertiser.js.map