browser.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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. const receiveTime = Date.now();
  60. Object.keys(nameMap).forEach(function (name) {
  61. self.goodbyes(name, packet).forEach(self.removeService.bind(self));
  62. var matches = self.buildServicesFor(name, packet, self.txt, rinfo, receiveTime);
  63. if (matches.length === 0)
  64. return;
  65. matches.forEach((service) => {
  66. const existingService = self._services.find((s) => (0, dns_equal_1.default)(s.fqdn, service.fqdn));
  67. if (existingService) {
  68. self.updateServiceSrv(existingService, service);
  69. self.updateServiceTxt(existingService, service);
  70. return;
  71. }
  72. self.addService(service);
  73. });
  74. });
  75. };
  76. this.mdns.on('response', this.onresponse);
  77. this.update();
  78. }
  79. stop() {
  80. if (!this.onresponse)
  81. return;
  82. this.mdns.removeListener('response', this.onresponse);
  83. this.onresponse = undefined;
  84. }
  85. update() {
  86. this.mdns.query(this.name, 'PTR');
  87. }
  88. expire() {
  89. const currentTime = Date.now();
  90. this._services = this._services.filter((service) => {
  91. if (!service.ttl || service.lastSeen === undefined)
  92. return true;
  93. const expireTime = service.lastSeen + service.ttl * 1000;
  94. if (expireTime < currentTime) {
  95. this.emit('down', service);
  96. return false;
  97. }
  98. return true;
  99. });
  100. }
  101. get services() {
  102. return this._services;
  103. }
  104. addService(service) {
  105. if ((0, filter_service_1.default)(service, this.txtQuery) === false)
  106. return;
  107. this._services.push(service);
  108. this.serviceMap[service.fqdn] = true;
  109. this.emit('up', service);
  110. }
  111. updateServiceSrv(existingService, newService) {
  112. if (existingService.name !== newService.name
  113. || existingService.host !== newService.host
  114. || existingService.port !== newService.port
  115. || existingService.type !== newService.type
  116. || existingService.protocol !== newService.protocol) {
  117. this.replaceService(newService);
  118. this.emit('srv-update', newService, existingService);
  119. }
  120. }
  121. updateServiceTxt(existingService, service) {
  122. if ((0, equal_txt_1.default)(service.txt, (existingService === null || existingService === void 0 ? void 0 : existingService.txt) || {}))
  123. return;
  124. if (!(0, filter_service_1.default)(service, this.txtQuery)) {
  125. this.removeService(service.fqdn);
  126. return;
  127. }
  128. this.replaceService(service);
  129. this.emit('txt-update', service, existingService);
  130. }
  131. replaceService(service) {
  132. this._services = this._services.map((s) => {
  133. if (!(0, dns_equal_1.default)(s.fqdn, service.fqdn))
  134. return s;
  135. return service;
  136. });
  137. }
  138. removeService(fqdn) {
  139. var service, index;
  140. this._services.some(function (s, i) {
  141. if ((0, dns_equal_1.default)(s.fqdn, fqdn)) {
  142. service = s;
  143. index = i;
  144. return true;
  145. }
  146. });
  147. if (!service || index === undefined)
  148. return;
  149. this._services.splice(index, 1);
  150. delete this.serviceMap[fqdn];
  151. this.emit('down', service);
  152. }
  153. goodbyes(name, packet) {
  154. return packet.answers.concat(packet.additionals)
  155. .filter((rr) => rr.type === 'PTR' && rr.ttl === 0 && (0, dns_equal_1.default)(rr.name, name))
  156. .map((rr) => rr.data);
  157. }
  158. buildServicesFor(name, packet, txt, referer, receiveTime) {
  159. var records = packet.answers.concat(packet.additionals).filter((rr) => rr.ttl > 0);
  160. return records
  161. .filter((rr) => rr.type === 'PTR' && (0, dns_equal_1.default)(rr.name, name))
  162. .map((ptr) => {
  163. const service = {
  164. addresses: [],
  165. subtypes: [],
  166. ttl: ptr.ttl,
  167. lastSeen: receiveTime
  168. };
  169. records.filter((rr) => {
  170. return (rr.type === 'PTR' && (0, dns_equal_1.default)(rr.data, ptr.data) && rr.name.includes('._sub'));
  171. }).forEach((rr) => {
  172. const types = (0, service_types_1.toType)(rr.name);
  173. service.subtypes.push(types.subtype);
  174. });
  175. records
  176. .filter((rr) => {
  177. return (rr.type === 'SRV' || rr.type === 'TXT') && (0, dns_equal_1.default)(rr.name, ptr.data);
  178. })
  179. .forEach((rr) => {
  180. if (rr.type === 'SRV') {
  181. var parts = rr.name.split('.');
  182. var name = parts[0];
  183. var types = (0, service_types_1.toType)(parts.slice(1, -1).join('.'));
  184. service.name = name;
  185. service.fqdn = rr.name;
  186. service.host = rr.data.target;
  187. service.referer = referer;
  188. service.port = rr.data.port;
  189. service.type = types.name;
  190. service.protocol = types.protocol;
  191. }
  192. else if (rr.type === 'TXT') {
  193. service.rawTxt = rr.data;
  194. service.txt = this.txt.decodeAll(rr.data);
  195. }
  196. });
  197. if (!service.name)
  198. return;
  199. records
  200. .filter((rr) => (rr.type === 'A' || rr.type === 'AAAA') && (0, dns_equal_1.default)(rr.name, service.host))
  201. .forEach((rr) => service.addresses.push(rr.data));
  202. return service;
  203. })
  204. .filter((rr) => !!rr);
  205. }
  206. }
  207. exports.Browser = Browser;
  208. exports.default = Browser;
  209. //# sourceMappingURL=browser.js.map