browser.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.Browser = void 0;
  7. const dns_txt_1 = __importDefault(require("./dns-txt"));
  8. const dns_equal_1 = __importDefault(require("./utils/dns-equal"));
  9. const events_1 = require("events");
  10. const service_types_1 = require("./service-types");
  11. const filter_service_1 = __importDefault(require("./utils/filter-service"));
  12. const filter_txt_1 = __importDefault(require("./utils/filter-txt"));
  13. const equal_txt_1 = __importDefault(require("./utils/equal-txt"));
  14. const TLD = '.local';
  15. const WILDCARD = '_services._dns-sd._udp' + TLD;
  16. class Browser extends events_1.EventEmitter {
  17. constructor(mdns, opts, onup) {
  18. super();
  19. this.onresponse = undefined;
  20. this.serviceMap = {};
  21. this.wildcard = false;
  22. this._services = [];
  23. if (typeof opts === 'function')
  24. return new Browser(mdns, null, opts);
  25. this.mdns = mdns;
  26. this.txt = new dns_txt_1.default(opts !== null && opts.txt != null ? opts.txt : undefined);
  27. if (opts === null || opts.type === undefined) {
  28. this.name = WILDCARD;
  29. this.wildcard = true;
  30. }
  31. else {
  32. this.name = (0, service_types_1.toString)({ name: opts.type, protocol: opts.protocol || 'tcp' }) + TLD;
  33. if (opts.name)
  34. this.name = opts.name + '.' + this.name;
  35. this.wildcard = false;
  36. }
  37. if (opts != null && opts.txt !== undefined)
  38. this.txtQuery = (0, filter_txt_1.default)(opts.txt);
  39. if (onup)
  40. this.on('up', onup);
  41. this.start();
  42. }
  43. start() {
  44. if (this.onresponse || this.name === undefined)
  45. return;
  46. var self = this;
  47. var nameMap = {};
  48. if (!this.wildcard)
  49. nameMap[this.name] = true;
  50. this.onresponse = (packet, rinfo) => {
  51. if (self.wildcard) {
  52. packet.answers.forEach((answer) => {
  53. if (answer.type !== 'PTR' || answer.name !== self.name || answer.name in nameMap)
  54. return;
  55. nameMap[answer.data] = true;
  56. self.mdns.query(answer.data, 'PTR');
  57. });
  58. }
  59. Object.keys(nameMap).forEach(function (name) {
  60. self.goodbyes(name, packet).forEach(self.removeService.bind(self));
  61. var matches = self.buildServicesFor(name, packet, self.txt, rinfo);
  62. if (matches.length === 0)
  63. return;
  64. matches.forEach((service) => {
  65. if (self.serviceMap[service.fqdn]) {
  66. self.updateService(service);
  67. return;
  68. }
  69. self.addService(service);
  70. });
  71. });
  72. };
  73. this.mdns.on('response', this.onresponse);
  74. this.update();
  75. }
  76. stop() {
  77. if (!this.onresponse)
  78. return;
  79. this.mdns.removeListener('response', this.onresponse);
  80. this.onresponse = undefined;
  81. }
  82. update() {
  83. this.mdns.query(this.name, 'PTR');
  84. }
  85. get services() {
  86. return this._services;
  87. }
  88. addService(service) {
  89. if ((0, filter_service_1.default)(service, this.txtQuery) === false)
  90. return;
  91. this._services.push(service);
  92. this.serviceMap[service.fqdn] = true;
  93. this.emit('up', service);
  94. }
  95. updateService(service) {
  96. var _a;
  97. if ((0, equal_txt_1.default)(service.txt, ((_a = this._services.find((s) => (0, dns_equal_1.default)(s.fqdn, service.fqdn))) === null || _a === void 0 ? void 0 : _a.txt) || {}))
  98. return;
  99. if (!(0, filter_service_1.default)(service, this.txtQuery)) {
  100. this.removeService(service.fqdn);
  101. return;
  102. }
  103. this._services = this._services.map(function (s) {
  104. if (!(0, dns_equal_1.default)(s.fqdn, service.fqdn))
  105. return s;
  106. return service;
  107. });
  108. this.emit('txt-update', service);
  109. }
  110. removeService(fqdn) {
  111. var service, index;
  112. this._services.some(function (s, i) {
  113. if ((0, dns_equal_1.default)(s.fqdn, fqdn)) {
  114. service = s;
  115. index = i;
  116. return true;
  117. }
  118. });
  119. if (!service || index === undefined)
  120. return;
  121. this._services.splice(index, 1);
  122. delete this.serviceMap[fqdn];
  123. this.emit('down', service);
  124. }
  125. goodbyes(name, packet) {
  126. return packet.answers.concat(packet.additionals)
  127. .filter((rr) => rr.type === 'PTR' && rr.ttl === 0 && (0, dns_equal_1.default)(rr.name, name))
  128. .map((rr) => rr.data);
  129. }
  130. buildServicesFor(name, packet, txt, referer) {
  131. var records = packet.answers.concat(packet.additionals).filter((rr) => rr.ttl > 0);
  132. return records
  133. .filter((rr) => rr.type === 'PTR' && (0, dns_equal_1.default)(rr.name, name))
  134. .map((ptr) => {
  135. const service = {
  136. addresses: [],
  137. subtypes: []
  138. };
  139. records.filter((rr) => {
  140. return (rr.type === 'PTR' && (0, dns_equal_1.default)(rr.data, ptr.data) && rr.name.includes('._sub'));
  141. }).forEach((rr) => {
  142. const types = (0, service_types_1.toType)(rr.name);
  143. service.subtypes.push(types.subtype);
  144. });
  145. records
  146. .filter((rr) => {
  147. return (rr.type === 'SRV' || rr.type === 'TXT') && (0, dns_equal_1.default)(rr.name, ptr.data);
  148. })
  149. .forEach((rr) => {
  150. if (rr.type === 'SRV') {
  151. var parts = rr.name.split('.');
  152. var name = parts[0];
  153. var types = (0, service_types_1.toType)(parts.slice(1, -1).join('.'));
  154. service.name = name;
  155. service.fqdn = rr.name;
  156. service.host = rr.data.target;
  157. service.referer = referer;
  158. service.port = rr.data.port;
  159. service.type = types.name;
  160. service.protocol = types.protocol;
  161. }
  162. else if (rr.type === 'TXT') {
  163. service.rawTxt = rr.data;
  164. service.txt = this.txt.decodeAll(rr.data);
  165. }
  166. });
  167. if (!service.name)
  168. return;
  169. records
  170. .filter((rr) => (rr.type === 'A' || rr.type === 'AAAA') && (0, dns_equal_1.default)(rr.name, service.host))
  171. .forEach((rr) => service.addresses.push(rr.data));
  172. return service;
  173. })
  174. .filter((rr) => !!rr);
  175. }
  176. }
  177. exports.Browser = Browser;
  178. exports.default = Browser;
  179. //# sourceMappingURL=browser.js.map