matterbridge 3.1.6-dev-20250720-88d6141 → 3.1.6

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.
Files changed (236) hide show
  1. package/CHANGELOG.md +6 -4
  2. package/README-DOCKER.md +35 -0
  3. package/dist/cli.d.ts +26 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +91 -2
  6. package/dist/cli.js.map +1 -0
  7. package/dist/cliEmitter.d.ts +34 -0
  8. package/dist/cliEmitter.d.ts.map +1 -0
  9. package/dist/cliEmitter.js +30 -0
  10. package/dist/cliEmitter.js.map +1 -0
  11. package/dist/clusters/export.d.ts +2 -0
  12. package/dist/clusters/export.d.ts.map +1 -0
  13. package/dist/clusters/export.js +2 -0
  14. package/dist/clusters/export.js.map +1 -0
  15. package/dist/defaultConfigSchema.d.ts +28 -0
  16. package/dist/defaultConfigSchema.d.ts.map +1 -0
  17. package/dist/defaultConfigSchema.js +24 -0
  18. package/dist/defaultConfigSchema.js.map +1 -0
  19. package/dist/deviceManager.d.ts +112 -0
  20. package/dist/deviceManager.d.ts.map +1 -0
  21. package/dist/deviceManager.js +94 -1
  22. package/dist/deviceManager.js.map +1 -0
  23. package/dist/devices/batteryStorage.d.ts +48 -0
  24. package/dist/devices/batteryStorage.d.ts.map +1 -0
  25. package/dist/devices/batteryStorage.js +48 -1
  26. package/dist/devices/batteryStorage.js.map +1 -0
  27. package/dist/devices/dishwasher.d.ts +91 -0
  28. package/dist/devices/dishwasher.d.ts.map +1 -0
  29. package/dist/devices/dishwasher.js +78 -3
  30. package/dist/devices/dishwasher.js.map +1 -0
  31. package/dist/devices/evse.d.ts +75 -0
  32. package/dist/devices/evse.d.ts.map +1 -0
  33. package/dist/devices/evse.js +74 -10
  34. package/dist/devices/evse.js.map +1 -0
  35. package/dist/devices/export.d.ts +11 -0
  36. package/dist/devices/export.d.ts.map +1 -0
  37. package/dist/devices/export.js +2 -0
  38. package/dist/devices/export.js.map +1 -0
  39. package/dist/devices/extractorHood.d.ts +46 -0
  40. package/dist/devices/extractorHood.d.ts.map +1 -0
  41. package/dist/devices/extractorHood.js +42 -0
  42. package/dist/devices/extractorHood.js.map +1 -0
  43. package/dist/devices/heatPump.d.ts +47 -0
  44. package/dist/devices/heatPump.d.ts.map +1 -0
  45. package/dist/devices/heatPump.js +50 -2
  46. package/dist/devices/heatPump.js.map +1 -0
  47. package/dist/devices/laundryDryer.d.ts +87 -0
  48. package/dist/devices/laundryDryer.d.ts.map +1 -0
  49. package/dist/devices/laundryDryer.js +83 -6
  50. package/dist/devices/laundryDryer.js.map +1 -0
  51. package/dist/devices/laundryWasher.d.ts +242 -0
  52. package/dist/devices/laundryWasher.d.ts.map +1 -0
  53. package/dist/devices/laundryWasher.js +91 -7
  54. package/dist/devices/laundryWasher.js.map +1 -0
  55. package/dist/devices/roboticVacuumCleaner.d.ts +112 -0
  56. package/dist/devices/roboticVacuumCleaner.d.ts.map +1 -0
  57. package/dist/devices/roboticVacuumCleaner.js +93 -7
  58. package/dist/devices/roboticVacuumCleaner.js.map +1 -0
  59. package/dist/devices/solarPower.d.ts +40 -0
  60. package/dist/devices/solarPower.d.ts.map +1 -0
  61. package/dist/devices/solarPower.js +38 -0
  62. package/dist/devices/solarPower.js.map +1 -0
  63. package/dist/devices/waterHeater.d.ts +111 -0
  64. package/dist/devices/waterHeater.d.ts.map +1 -0
  65. package/dist/devices/waterHeater.js +82 -2
  66. package/dist/devices/waterHeater.js.map +1 -0
  67. package/dist/dgram/dgram.d.ts +140 -0
  68. package/dist/dgram/dgram.d.ts.map +1 -0
  69. package/dist/dgram/dgram.js +348 -0
  70. package/dist/dgram/dgram.js.map +1 -0
  71. package/dist/dgram/mdns.d.ts +288 -0
  72. package/dist/dgram/mdns.d.ts.map +1 -0
  73. package/dist/dgram/mdns.js +840 -0
  74. package/dist/dgram/mdns.js.map +1 -0
  75. package/dist/dgram/multicast.d.ts +65 -0
  76. package/dist/dgram/multicast.d.ts.map +1 -0
  77. package/dist/dgram/multicast.js +170 -0
  78. package/dist/dgram/multicast.js.map +1 -0
  79. package/dist/dgram/unicast.d.ts +56 -0
  80. package/dist/dgram/unicast.d.ts.map +1 -0
  81. package/dist/dgram/unicast.js +91 -0
  82. package/dist/dgram/unicast.js.map +1 -0
  83. package/dist/frontend.d.ts +304 -0
  84. package/dist/frontend.d.ts.map +1 -0
  85. package/dist/frontend.js +435 -21
  86. package/dist/frontend.js.map +1 -0
  87. package/dist/globalMatterbridge.d.ts +59 -0
  88. package/dist/globalMatterbridge.d.ts.map +1 -0
  89. package/dist/globalMatterbridge.js +47 -0
  90. package/dist/globalMatterbridge.js.map +1 -0
  91. package/dist/helpers.d.ts +48 -0
  92. package/dist/helpers.d.ts.map +1 -0
  93. package/dist/helpers.js +53 -0
  94. package/dist/helpers.js.map +1 -0
  95. package/dist/index.d.ts +33 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +30 -1
  98. package/dist/index.js.map +1 -0
  99. package/dist/logger/export.d.ts +2 -0
  100. package/dist/logger/export.d.ts.map +1 -0
  101. package/dist/logger/export.js +1 -0
  102. package/dist/logger/export.js.map +1 -0
  103. package/dist/matter/behaviors.d.ts +2 -0
  104. package/dist/matter/behaviors.d.ts.map +1 -0
  105. package/dist/matter/behaviors.js +2 -0
  106. package/dist/matter/behaviors.js.map +1 -0
  107. package/dist/matter/clusters.d.ts +2 -0
  108. package/dist/matter/clusters.d.ts.map +1 -0
  109. package/dist/matter/clusters.js +2 -0
  110. package/dist/matter/clusters.js.map +1 -0
  111. package/dist/matter/devices.d.ts +2 -0
  112. package/dist/matter/devices.d.ts.map +1 -0
  113. package/dist/matter/devices.js +2 -0
  114. package/dist/matter/devices.js.map +1 -0
  115. package/dist/matter/endpoints.d.ts +2 -0
  116. package/dist/matter/endpoints.d.ts.map +1 -0
  117. package/dist/matter/endpoints.js +2 -0
  118. package/dist/matter/endpoints.js.map +1 -0
  119. package/dist/matter/export.d.ts +5 -0
  120. package/dist/matter/export.d.ts.map +1 -0
  121. package/dist/matter/export.js +3 -0
  122. package/dist/matter/export.js.map +1 -0
  123. package/dist/matter/types.d.ts +3 -0
  124. package/dist/matter/types.d.ts.map +1 -0
  125. package/dist/matter/types.js +3 -0
  126. package/dist/matter/types.js.map +1 -0
  127. package/dist/matterbridge.d.ts +460 -0
  128. package/dist/matterbridge.d.ts.map +1 -0
  129. package/dist/matterbridge.js +810 -52
  130. package/dist/matterbridge.js.map +1 -0
  131. package/dist/matterbridgeAccessoryPlatform.d.ts +42 -0
  132. package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -0
  133. package/dist/matterbridgeAccessoryPlatform.js +36 -0
  134. package/dist/matterbridgeAccessoryPlatform.js.map +1 -0
  135. package/dist/matterbridgeBehaviors.d.ts +1351 -0
  136. package/dist/matterbridgeBehaviors.d.ts.map +1 -0
  137. package/dist/matterbridgeBehaviors.js +68 -5
  138. package/dist/matterbridgeBehaviors.js.map +1 -0
  139. package/dist/matterbridgeDeviceTypes.d.ts +709 -0
  140. package/dist/matterbridgeDeviceTypes.d.ts.map +1 -0
  141. package/dist/matterbridgeDeviceTypes.js +579 -15
  142. package/dist/matterbridgeDeviceTypes.js.map +1 -0
  143. package/dist/matterbridgeDynamicPlatform.d.ts +42 -0
  144. package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -0
  145. package/dist/matterbridgeDynamicPlatform.js +36 -0
  146. package/dist/matterbridgeDynamicPlatform.js.map +1 -0
  147. package/dist/matterbridgeEndpoint.d.ts +1328 -0
  148. package/dist/matterbridgeEndpoint.d.ts.map +1 -0
  149. package/dist/matterbridgeEndpoint.js +1200 -43
  150. package/dist/matterbridgeEndpoint.js.map +1 -0
  151. package/dist/matterbridgeEndpointHelpers.d.ts +3198 -0
  152. package/dist/matterbridgeEndpointHelpers.d.ts.map +1 -0
  153. package/dist/matterbridgeEndpointHelpers.js +322 -12
  154. package/dist/matterbridgeEndpointHelpers.js.map +1 -0
  155. package/dist/matterbridgePlatform.d.ts +310 -0
  156. package/dist/matterbridgePlatform.d.ts.map +1 -0
  157. package/dist/matterbridgePlatform.js +233 -0
  158. package/dist/matterbridgePlatform.js.map +1 -0
  159. package/dist/matterbridgeTypes.d.ts +195 -0
  160. package/dist/matterbridgeTypes.d.ts.map +1 -0
  161. package/dist/matterbridgeTypes.js +25 -0
  162. package/dist/matterbridgeTypes.js.map +1 -0
  163. package/dist/pluginManager.d.ts +270 -0
  164. package/dist/pluginManager.d.ts.map +1 -0
  165. package/dist/pluginManager.js +249 -3
  166. package/dist/pluginManager.js.map +1 -0
  167. package/dist/shelly.d.ts +174 -0
  168. package/dist/shelly.d.ts.map +1 -0
  169. package/dist/shelly.js +168 -7
  170. package/dist/shelly.js.map +1 -0
  171. package/dist/storage/export.d.ts +2 -0
  172. package/dist/storage/export.d.ts.map +1 -0
  173. package/dist/storage/export.js +1 -0
  174. package/dist/storage/export.js.map +1 -0
  175. package/dist/update.d.ts +59 -0
  176. package/dist/update.d.ts.map +1 -0
  177. package/dist/update.js +54 -0
  178. package/dist/update.js.map +1 -0
  179. package/dist/utils/colorUtils.d.ts +117 -0
  180. package/dist/utils/colorUtils.d.ts.map +1 -0
  181. package/dist/utils/colorUtils.js +263 -2
  182. package/dist/utils/colorUtils.js.map +1 -0
  183. package/dist/utils/commandLine.d.ts +59 -0
  184. package/dist/utils/commandLine.d.ts.map +1 -0
  185. package/dist/utils/commandLine.js +54 -0
  186. package/dist/utils/commandLine.js.map +1 -0
  187. package/dist/utils/copyDirectory.d.ts +33 -0
  188. package/dist/utils/copyDirectory.d.ts.map +1 -0
  189. package/dist/utils/copyDirectory.js +38 -1
  190. package/dist/utils/copyDirectory.js.map +1 -0
  191. package/dist/utils/createDirectory.d.ts +34 -0
  192. package/dist/utils/createDirectory.d.ts.map +1 -0
  193. package/dist/utils/createDirectory.js +33 -0
  194. package/dist/utils/createDirectory.js.map +1 -0
  195. package/dist/utils/createZip.d.ts +39 -0
  196. package/dist/utils/createZip.d.ts.map +1 -0
  197. package/dist/utils/createZip.js +47 -2
  198. package/dist/utils/createZip.js.map +1 -0
  199. package/dist/utils/deepCopy.d.ts +32 -0
  200. package/dist/utils/deepCopy.d.ts.map +1 -0
  201. package/dist/utils/deepCopy.js +39 -0
  202. package/dist/utils/deepCopy.js.map +1 -0
  203. package/dist/utils/deepEqual.d.ts +54 -0
  204. package/dist/utils/deepEqual.d.ts.map +1 -0
  205. package/dist/utils/deepEqual.js +72 -1
  206. package/dist/utils/deepEqual.js.map +1 -0
  207. package/dist/utils/error.d.ts +44 -0
  208. package/dist/utils/error.d.ts.map +1 -0
  209. package/dist/utils/error.js +41 -0
  210. package/dist/utils/error.js.map +1 -0
  211. package/dist/utils/export.d.ts +12 -0
  212. package/dist/utils/export.d.ts.map +1 -0
  213. package/dist/utils/export.js +1 -0
  214. package/dist/utils/export.js.map +1 -0
  215. package/dist/utils/hex.d.ts +49 -0
  216. package/dist/utils/hex.d.ts.map +1 -0
  217. package/dist/utils/hex.js +58 -0
  218. package/dist/utils/hex.js.map +1 -0
  219. package/dist/utils/isvalid.d.ts +103 -0
  220. package/dist/utils/isvalid.d.ts.map +1 -0
  221. package/dist/utils/isvalid.js +101 -0
  222. package/dist/utils/isvalid.js.map +1 -0
  223. package/dist/utils/network.d.ts +74 -0
  224. package/dist/utils/network.d.ts.map +1 -0
  225. package/dist/utils/network.js +81 -5
  226. package/dist/utils/network.js.map +1 -0
  227. package/dist/utils/spawn.d.ts +33 -0
  228. package/dist/utils/spawn.d.ts.map +1 -0
  229. package/dist/utils/spawn.js +40 -0
  230. package/dist/utils/spawn.js.map +1 -0
  231. package/dist/utils/wait.d.ts +56 -0
  232. package/dist/utils/wait.d.ts.map +1 -0
  233. package/dist/utils/wait.js +62 -9
  234. package/dist/utils/wait.js.map +1 -0
  235. package/npm-shrinkwrap.json +2 -2
  236. package/package.json +2 -1
@@ -0,0 +1,840 @@
1
+ /**
2
+ * @description This file contains the class Mdns.
3
+ * @file mdns.ts
4
+ * @author Luca Liguori
5
+ * @created 2025-03-22
6
+ * @version 1.0.0
7
+ * @license Apache-2.0
8
+ *
9
+ * Copyright 2025, 2026, 2027 Luca Liguori.
10
+ *
11
+ * Licensed under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License.
13
+ * You may obtain a copy of the License at
14
+ *
15
+ * http://www.apache.org/licenses/LICENSE-2.0
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License.
22
+ */
23
+ // AnsiLogger imports
24
+ import { BLUE, CYAN, db, er, GREEN, idn, MAGENTA, nf, rs } from 'node-ansi-logger';
25
+ // Matterbridge imports
26
+ import { MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_IPV6_ADDRESS, MDNS_MULTICAST_PORT, Multicast } from './multicast.js';
27
+ // Node.js imports
28
+ export var DnsRecordType;
29
+ (function (DnsRecordType) {
30
+ DnsRecordType[DnsRecordType["A"] = 1] = "A";
31
+ DnsRecordType[DnsRecordType["NS"] = 2] = "NS";
32
+ DnsRecordType[DnsRecordType["MD"] = 3] = "MD";
33
+ DnsRecordType[DnsRecordType["MF"] = 4] = "MF";
34
+ DnsRecordType[DnsRecordType["CNAME"] = 5] = "CNAME";
35
+ DnsRecordType[DnsRecordType["SOA"] = 6] = "SOA";
36
+ DnsRecordType[DnsRecordType["MB"] = 7] = "MB";
37
+ DnsRecordType[DnsRecordType["MG"] = 8] = "MG";
38
+ DnsRecordType[DnsRecordType["MR"] = 9] = "MR";
39
+ DnsRecordType[DnsRecordType["NULL"] = 10] = "NULL";
40
+ DnsRecordType[DnsRecordType["WKS"] = 11] = "WKS";
41
+ DnsRecordType[DnsRecordType["PTR"] = 12] = "PTR";
42
+ DnsRecordType[DnsRecordType["HINFO"] = 13] = "HINFO";
43
+ DnsRecordType[DnsRecordType["MINFO"] = 14] = "MINFO";
44
+ DnsRecordType[DnsRecordType["MX"] = 15] = "MX";
45
+ DnsRecordType[DnsRecordType["TXT"] = 16] = "TXT";
46
+ DnsRecordType[DnsRecordType["RP"] = 17] = "RP";
47
+ DnsRecordType[DnsRecordType["AFSDB"] = 18] = "AFSDB";
48
+ DnsRecordType[DnsRecordType["X25"] = 19] = "X25";
49
+ DnsRecordType[DnsRecordType["ISDN"] = 20] = "ISDN";
50
+ DnsRecordType[DnsRecordType["RT"] = 21] = "RT";
51
+ DnsRecordType[DnsRecordType["NSAP"] = 22] = "NSAP";
52
+ DnsRecordType[DnsRecordType["NSAP_PTR"] = 23] = "NSAP_PTR";
53
+ DnsRecordType[DnsRecordType["SIG"] = 24] = "SIG";
54
+ DnsRecordType[DnsRecordType["KEY"] = 25] = "KEY";
55
+ DnsRecordType[DnsRecordType["PX"] = 26] = "PX";
56
+ DnsRecordType[DnsRecordType["GPOS"] = 27] = "GPOS";
57
+ DnsRecordType[DnsRecordType["AAAA"] = 28] = "AAAA";
58
+ DnsRecordType[DnsRecordType["LOC"] = 29] = "LOC";
59
+ DnsRecordType[DnsRecordType["NXT"] = 30] = "NXT";
60
+ DnsRecordType[DnsRecordType["EID"] = 31] = "EID";
61
+ DnsRecordType[DnsRecordType["NIMLOC"] = 32] = "NIMLOC";
62
+ DnsRecordType[DnsRecordType["SRV"] = 33] = "SRV";
63
+ DnsRecordType[DnsRecordType["ATMA"] = 34] = "ATMA";
64
+ DnsRecordType[DnsRecordType["NAPTR"] = 35] = "NAPTR";
65
+ DnsRecordType[DnsRecordType["KX"] = 36] = "KX";
66
+ DnsRecordType[DnsRecordType["CERT"] = 37] = "CERT";
67
+ DnsRecordType[DnsRecordType["A6"] = 38] = "A6";
68
+ DnsRecordType[DnsRecordType["DNAME"] = 39] = "DNAME";
69
+ DnsRecordType[DnsRecordType["SINK"] = 40] = "SINK";
70
+ DnsRecordType[DnsRecordType["OPT"] = 41] = "OPT";
71
+ DnsRecordType[DnsRecordType["APL"] = 42] = "APL";
72
+ DnsRecordType[DnsRecordType["DS"] = 43] = "DS";
73
+ DnsRecordType[DnsRecordType["SSHFP"] = 44] = "SSHFP";
74
+ DnsRecordType[DnsRecordType["IPSECKEY"] = 45] = "IPSECKEY";
75
+ DnsRecordType[DnsRecordType["RRSIG"] = 46] = "RRSIG";
76
+ DnsRecordType[DnsRecordType["NSEC"] = 47] = "NSEC";
77
+ DnsRecordType[DnsRecordType["DNSKEY"] = 48] = "DNSKEY";
78
+ DnsRecordType[DnsRecordType["DHCID"] = 49] = "DHCID";
79
+ DnsRecordType[DnsRecordType["NSEC3"] = 50] = "NSEC3";
80
+ DnsRecordType[DnsRecordType["NSEC3PARAM"] = 51] = "NSEC3PARAM";
81
+ DnsRecordType[DnsRecordType["TLSA"] = 52] = "TLSA";
82
+ DnsRecordType[DnsRecordType["SMIMEA"] = 53] = "SMIMEA";
83
+ DnsRecordType[DnsRecordType["HIP"] = 55] = "HIP";
84
+ DnsRecordType[DnsRecordType["NINFO"] = 56] = "NINFO";
85
+ DnsRecordType[DnsRecordType["RKEY"] = 57] = "RKEY";
86
+ DnsRecordType[DnsRecordType["TALINK"] = 58] = "TALINK";
87
+ DnsRecordType[DnsRecordType["CDS"] = 59] = "CDS";
88
+ DnsRecordType[DnsRecordType["CDNSKEY"] = 60] = "CDNSKEY";
89
+ DnsRecordType[DnsRecordType["OPENPGPKEY"] = 61] = "OPENPGPKEY";
90
+ DnsRecordType[DnsRecordType["CSYNC"] = 62] = "CSYNC";
91
+ DnsRecordType[DnsRecordType["ZONEMD"] = 63] = "ZONEMD";
92
+ DnsRecordType[DnsRecordType["SVCB"] = 64] = "SVCB";
93
+ DnsRecordType[DnsRecordType["HTTPS"] = 65] = "HTTPS";
94
+ DnsRecordType[DnsRecordType["SPF"] = 99] = "SPF";
95
+ DnsRecordType[DnsRecordType["UINFO"] = 100] = "UINFO";
96
+ DnsRecordType[DnsRecordType["UID"] = 101] = "UID";
97
+ DnsRecordType[DnsRecordType["GID"] = 102] = "GID";
98
+ DnsRecordType[DnsRecordType["UNSPEC"] = 103] = "UNSPEC";
99
+ DnsRecordType[DnsRecordType["NID"] = 104] = "NID";
100
+ DnsRecordType[DnsRecordType["L32"] = 105] = "L32";
101
+ DnsRecordType[DnsRecordType["L64"] = 106] = "L64";
102
+ DnsRecordType[DnsRecordType["LP"] = 107] = "LP";
103
+ DnsRecordType[DnsRecordType["EUI48"] = 108] = "EUI48";
104
+ DnsRecordType[DnsRecordType["EUI64"] = 109] = "EUI64";
105
+ DnsRecordType[DnsRecordType["TKEY"] = 249] = "TKEY";
106
+ DnsRecordType[DnsRecordType["TSIG"] = 250] = "TSIG";
107
+ DnsRecordType[DnsRecordType["IXFR"] = 251] = "IXFR";
108
+ DnsRecordType[DnsRecordType["AXFR"] = 252] = "AXFR";
109
+ DnsRecordType[DnsRecordType["MAILB"] = 253] = "MAILB";
110
+ DnsRecordType[DnsRecordType["MAILA"] = 254] = "MAILA";
111
+ DnsRecordType[DnsRecordType["ANY"] = 255] = "ANY";
112
+ DnsRecordType[DnsRecordType["URI"] = 256] = "URI";
113
+ DnsRecordType[DnsRecordType["CAA"] = 257] = "CAA";
114
+ DnsRecordType[DnsRecordType["AVC"] = 258] = "AVC";
115
+ DnsRecordType[DnsRecordType["DOA"] = 259] = "DOA";
116
+ DnsRecordType[DnsRecordType["AMTRELAY"] = 260] = "AMTRELAY";
117
+ DnsRecordType[DnsRecordType["ZONEVERSION"] = 261] = "ZONEVERSION";
118
+ // 262-32767 are unassigned/reserved
119
+ DnsRecordType[DnsRecordType["TA"] = 32768] = "TA";
120
+ DnsRecordType[DnsRecordType["DLV"] = 32769] = "DLV";
121
+ })(DnsRecordType || (DnsRecordType = {}));
122
+ export var DnsClass;
123
+ (function (DnsClass) {
124
+ DnsClass[DnsClass["IN"] = 1] = "IN";
125
+ DnsClass[DnsClass["CH"] = 3] = "CH";
126
+ DnsClass[DnsClass["HS"] = 4] = "HS";
127
+ DnsClass[DnsClass["ANY"] = 255] = "ANY";
128
+ })(DnsClass || (DnsClass = {}));
129
+ export var DnsClassFlag;
130
+ (function (DnsClassFlag) {
131
+ DnsClassFlag[DnsClassFlag["FLUSH"] = 32768] = "FLUSH";
132
+ // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
133
+ DnsClassFlag[DnsClassFlag["QU"] = 32768] = "QU";
134
+ })(DnsClassFlag || (DnsClassFlag = {}));
135
+ export class Mdns extends Multicast {
136
+ deviceQueries = new Map();
137
+ deviceResponses = new Map();
138
+ /**
139
+ * Creates an instance of the Mdns class.
140
+ *
141
+ * @param {string} name - The internal name of the mDNS server for the logs.
142
+ * @param {string} multicastAddress - The multicast address for mDNS (i.e. 224.0.0.251 for udp4 or ff02::fb for udp6).
143
+ * @param {number} multicastPort - The port for mDNS (i.e. 5353).
144
+ * @param {('udp4' | 'udp6')} socketType - The type of socket to create (either 'udp4' or 'udp6').
145
+ * @param {boolean} [reuseAddr] - Whether to reuse the address. Defaults to true.
146
+ * @param {string} [interfaceName] - The optional name of the network interface to use.
147
+ * @param {string} [interfaceAddress] - The optional IP address of the network interface to use.
148
+ */
149
+ constructor(name, multicastAddress, multicastPort, socketType, reuseAddr = true, interfaceName, interfaceAddress) {
150
+ super(name, multicastAddress, multicastPort, socketType, reuseAddr, interfaceName, interfaceAddress);
151
+ }
152
+ onQuery(rinfo, _query) {
153
+ this.log.debug(`mDNS query received from ${BLUE}${rinfo.family}${db} ${BLUE}${rinfo.address}${db}:${BLUE}${rinfo.port}${db}`);
154
+ }
155
+ onResponse(rinfo, _response) {
156
+ this.log.debug(`mDNS response received from ${BLUE}${rinfo.family}${db} ${BLUE}${rinfo.address}${db}:${BLUE}${rinfo.port}${db}`);
157
+ }
158
+ onMessage(msg, rinfo) {
159
+ this.log.info(`Dgram mDNS server received a mDNS message from ${BLUE}${rinfo.family}${nf} ${BLUE}${rinfo.address}${nf}:${BLUE}${rinfo.port}${nf}`);
160
+ try {
161
+ const result = this.decodeMdnsMessage(msg);
162
+ if (result.qr === 0) {
163
+ this.deviceQueries.set(rinfo.address, { rinfo, query: result });
164
+ this.onQuery(rinfo, result);
165
+ }
166
+ else {
167
+ const ptr = result.answers?.find((record) => record.name === '_shelly._tcp.local' && record.type === 12 /* DnsRecordType.PTR */) ||
168
+ result.answers?.find((record) => record.name === '_http._tcp.local' && record.type === 12 /* DnsRecordType.PTR */) ||
169
+ result.answers?.find((record) => record.type === 12 /* DnsRecordType.PTR */);
170
+ this.deviceResponses.set(rinfo.address, { rinfo, response: result, dataPTR: ptr?.data });
171
+ this.onResponse(rinfo, result);
172
+ }
173
+ this.logMdnsMessage(result);
174
+ }
175
+ catch (error) {
176
+ this.log.error(`Error decoding mDNS message: ${error instanceof Error ? error.message : error}`);
177
+ }
178
+ }
179
+ /**
180
+ * Decodes an mDNS message, including the header, question section, answer section,
181
+ * authority section, and additional section.
182
+ *
183
+ * @param {Buffer} msg - The raw mDNS message buffer.
184
+ * @returns {MdnsMessage} An object representing the decoded mDNS message.
185
+ * @throws Error if the message is too short.
186
+ */
187
+ decodeMdnsMessage(msg) {
188
+ if (msg.length < 12) {
189
+ throw new Error('mDNS message too short');
190
+ }
191
+ const id = msg.readUInt16BE(0);
192
+ const flags = msg.readUInt16BE(2);
193
+ const qr = (flags & 0x8000) >> 15; // Bit 15: 0=query, 1=response.
194
+ const opcode = (flags & 0x7800) >> 11; // Bits 14-11.
195
+ const aa = Boolean(flags & 0x0400); // Bit 10.
196
+ const tc = Boolean(flags & 0x0200); // Bit 9.
197
+ const rd = Boolean(flags & 0x0100); // Bit 8.
198
+ const ra = Boolean(flags & 0x0080); // Bit 7.
199
+ const z = (flags & 0x0070) >> 4; // Bits 6-4.
200
+ const rcode = flags & 0x000f; // Bits 3-0.
201
+ const qdCount = msg.readUInt16BE(4);
202
+ const anCount = msg.readUInt16BE(6);
203
+ const nsCount = msg.readUInt16BE(8);
204
+ const arCount = msg.readUInt16BE(10);
205
+ const mdnsMessage = {
206
+ id,
207
+ qr,
208
+ opcode,
209
+ aa,
210
+ tc,
211
+ rd,
212
+ ra,
213
+ z,
214
+ rcode,
215
+ qdCount,
216
+ anCount,
217
+ nsCount,
218
+ arCount,
219
+ questions: [],
220
+ answers: [],
221
+ authorities: [],
222
+ additionals: [],
223
+ };
224
+ let offset = 12;
225
+ // Decode the question section.
226
+ for (let i = 0; i < qdCount; i++) {
227
+ const qnameResult = this.decodeDnsName(msg, offset);
228
+ const qname = qnameResult.name;
229
+ offset = qnameResult.newOffset;
230
+ const qtype = msg.readUInt16BE(offset);
231
+ offset += 2;
232
+ const qclass = msg.readUInt16BE(offset);
233
+ offset += 2;
234
+ mdnsMessage.questions?.push({ name: qname, type: qtype, class: qclass });
235
+ }
236
+ // Decode the answer section.
237
+ for (let i = 0; i < anCount; i++) {
238
+ const rrResult = this.decodeResourceRecord(msg, offset);
239
+ mdnsMessage.answers?.push(rrResult.record);
240
+ offset = rrResult.newOffset;
241
+ }
242
+ // Decode the authority (NS) section.
243
+ for (let i = 0; i < nsCount; i++) {
244
+ const rrResult = this.decodeResourceRecord(msg, offset);
245
+ mdnsMessage.authorities?.push(rrResult.record);
246
+ offset = rrResult.newOffset;
247
+ }
248
+ // Decode the additional records section.
249
+ for (let i = 0; i < arCount; i++) {
250
+ const rrResult = this.decodeResourceRecord(msg, offset);
251
+ mdnsMessage.additionals?.push(rrResult.record);
252
+ offset = rrResult.newOffset;
253
+ }
254
+ return mdnsMessage;
255
+ }
256
+ /**
257
+ * Decodes a DNS name from a buffer, handling compression.
258
+ *
259
+ * @param {Buffer} msg - The full mDNS message buffer.
260
+ * @param {number} offset - The offset at which the DNS name starts.
261
+ * @returns {{ name: string; newOffset: number }} An object with the decoded name and the new offset.
262
+ * @throws Error if the offset exceeds the buffer length or too many iterations are performed.
263
+ */
264
+ decodeDnsName(msg, offset) {
265
+ const labels = [];
266
+ let jumped = false;
267
+ let originalOffset = offset;
268
+ let iterations = 0; // Prevent infinite loops
269
+ while (true) {
270
+ // Safety guard: prevent infinite loops in malformed messages.
271
+ if (iterations++ > 1000) {
272
+ throw new Error('Too many iterations while decoding DNS name. Possible malformed message.');
273
+ }
274
+ // Check that offset is within buffer bounds.
275
+ if (offset >= msg.length) {
276
+ throw new Error('Offset exceeds buffer length while decoding DNS name.');
277
+ }
278
+ const len = msg.readUInt8(offset);
279
+ if (len === 0) {
280
+ offset++;
281
+ break;
282
+ }
283
+ // Check for pointer (first two bits are 11)
284
+ if ((len & 0xc0) === 0xc0) {
285
+ // Ensure the pointer has two bytes available.
286
+ if (offset + 1 >= msg.length) {
287
+ throw new Error('Incomplete pointer encountered while decoding DNS name.');
288
+ }
289
+ const pointer = ((len & 0x3f) << 8) | msg.readUInt8(offset + 1);
290
+ if (!jumped) {
291
+ originalOffset = offset + 2;
292
+ }
293
+ offset = pointer;
294
+ jumped = true;
295
+ continue;
296
+ }
297
+ offset++;
298
+ // Check that the label length doesn't go beyond the buffer.
299
+ if (offset + len > msg.length) {
300
+ throw new Error('Label length exceeds buffer bounds while decoding DNS name.');
301
+ }
302
+ labels.push(msg.toString('utf8', offset, offset + len));
303
+ offset += len;
304
+ }
305
+ return { name: labels.join('.'), newOffset: jumped ? originalOffset : offset };
306
+ }
307
+ /**
308
+ * Encodes a domain name into the DNS label format.
309
+ *
310
+ * For example, "example.local" becomes:
311
+ * [7] "example" [5] "local" [0]
312
+ *
313
+ * @param {string} name - The domain name to encode.
314
+ * @returns {Buffer} The encoded domain name as a Buffer.
315
+ */
316
+ encodeDnsName(name) {
317
+ const labels = name.split('.');
318
+ const buffers = labels.map((label) => {
319
+ const lenBuf = Buffer.alloc(1);
320
+ lenBuf.writeUInt8(label.length, 0);
321
+ return Buffer.concat([lenBuf, Buffer.from(label)]);
322
+ });
323
+ // Append the null byte to terminate the name.
324
+ return Buffer.concat([...buffers, Buffer.from([0])]);
325
+ }
326
+ /**
327
+ * Decodes a DNS resource record.
328
+ *
329
+ * @param {Buffer} msg - The full mDNS message buffer.
330
+ * @param {number} offset - The offset at which the resource record starts.
331
+ * @returns {{ record: MdnsRecord; newOffset: number }} An object containing the decoded record and the new offset.
332
+ */
333
+ decodeResourceRecord(msg, offset) {
334
+ // Decode the NAME field (which may be compressed)
335
+ const nameResult = this.decodeDnsName(msg, offset);
336
+ const name = nameResult.name;
337
+ offset = nameResult.newOffset;
338
+ // Read TYPE (16 bits), CLASS (16 bits), TTL (32 bits), and RDLENGTH (16 bits)
339
+ const type = msg.readUInt16BE(offset);
340
+ offset += 2;
341
+ const rrclass = msg.readUInt16BE(offset);
342
+ offset += 2;
343
+ const ttl = msg.readUInt32BE(offset);
344
+ offset += 4;
345
+ const rdlength = msg.readUInt16BE(offset);
346
+ offset += 2;
347
+ let data = '';
348
+ if (type === 12 /* DnsRecordType.PTR */) {
349
+ // PTR record (type 12): decode its RDATA as a domain name.
350
+ const ptrResult = this.decodeDnsName(msg, offset);
351
+ data = ptrResult.name;
352
+ offset += rdlength;
353
+ }
354
+ else if (type === 16 /* DnsRecordType.TXT */) {
355
+ // TXT record: may consist of one or more length-prefixed strings.
356
+ const txtStrings = [];
357
+ const end = offset + rdlength;
358
+ while (offset < end) {
359
+ const txtLen = msg[offset];
360
+ offset++;
361
+ const txt = msg.slice(offset, offset + txtLen).toString('utf8');
362
+ txtStrings.push(txt);
363
+ offset += txtLen;
364
+ }
365
+ data = txtStrings.join(', ');
366
+ }
367
+ else if (type === 33 /* DnsRecordType.SRV */) {
368
+ // SRV record (type === 33): consists of 2 bytes for priority, 2 for weight, 2 for port, followed by the target domain name.
369
+ const priority = msg.readUInt16BE(offset);
370
+ const weight = msg.readUInt16BE(offset + 2);
371
+ const port = msg.readUInt16BE(offset + 4);
372
+ offset += 6;
373
+ const srvTargetResult = this.decodeDnsName(msg, offset);
374
+ data = JSON.stringify({
375
+ priority,
376
+ weight,
377
+ port,
378
+ target: srvTargetResult.name,
379
+ });
380
+ offset = srvTargetResult.newOffset;
381
+ }
382
+ else if (type === 1 /* DnsRecordType.A */) {
383
+ // A record (type 1): an IPv4 address stored in 4 bytes.
384
+ const ipBytes = msg.slice(offset, offset + 4);
385
+ data = Array.from(ipBytes).join('.');
386
+ offset += 4;
387
+ }
388
+ else if (type === 28 /* DnsRecordType.AAAA */) {
389
+ // AAAA record (type 28): IPv6 address stored in 16 bytes.
390
+ const ipBytes = msg.slice(offset, offset + 16);
391
+ // Convert the 16 bytes into an IPv6 address string (colon-separated)
392
+ const ipv6Parts = [];
393
+ for (let i = 0; i < 16; i += 2) {
394
+ ipv6Parts.push(ipBytes.readUInt16BE(i).toString(16));
395
+ }
396
+ data = ipv6Parts.join(':');
397
+ offset += 16;
398
+ }
399
+ else if (type === 47 /* DnsRecordType.NSEC */) {
400
+ // NSEC record: RDATA consists of:
401
+ // - Next Domain Name (in DNS label format)
402
+ // - Type Bit Maps (variable length)
403
+ const { name: nextDomain, newOffset } = this.decodeDnsName(msg, offset);
404
+ const nextDomainLength = newOffset - offset;
405
+ offset = newOffset;
406
+ // Calculate the remaining length for the type bit maps.
407
+ const bitmapLength = rdlength - nextDomainLength;
408
+ const bitmapData = msg.slice(offset, offset + bitmapLength);
409
+ const types = [];
410
+ let bitmapOffset = 0;
411
+ while (bitmapOffset < bitmapData.length) {
412
+ const windowBlock = bitmapData[bitmapOffset];
413
+ const windowLength = bitmapData[bitmapOffset + 1];
414
+ bitmapOffset += 2;
415
+ for (let i = 0; i < windowLength; i++) {
416
+ const octet = bitmapData[bitmapOffset + i];
417
+ for (let bit = 0; bit < 8; bit++) {
418
+ if (octet & (0x80 >> bit)) {
419
+ const typeCode = windowBlock * 256 + i * 8 + bit;
420
+ types.push(this.dnsTypeToString(typeCode));
421
+ }
422
+ }
423
+ }
424
+ bitmapOffset += windowLength;
425
+ }
426
+ data = JSON.stringify({
427
+ nextDomain,
428
+ types,
429
+ });
430
+ offset += bitmapLength;
431
+ }
432
+ else {
433
+ // Fall back
434
+ data = msg.slice(offset, offset + rdlength).toString('hex');
435
+ offset += rdlength;
436
+ }
437
+ return {
438
+ record: { name, type, class: rrclass, ttl, data },
439
+ newOffset: offset,
440
+ };
441
+ }
442
+ /**
443
+ * Sends a DNS query with multiple questions.
444
+ *
445
+ * @param {Array<{ name: string; type: number; class: number; unicastResponse?: boolean }>} questions - Array of questions
446
+ *
447
+ * @remarks
448
+ * Each question should have a name (e.g., "_http._tcp.local"), type (e.g., DnsRecordType.PTR), class (e.g., DnsClass.IN),
449
+ * and an optional unicastResponse flag (this will add the DnsClassFlag.QU flag to the query).
450
+ */
451
+ sendQuery(questions) {
452
+ const header = Buffer.alloc(12);
453
+ header.writeUInt16BE(0, 0); // ID
454
+ header.writeUInt16BE(0, 2); // Flags
455
+ header.writeUInt16BE(questions.length, 4); // QDCOUNT
456
+ header.writeUInt16BE(0, 6); // ANCOUNT
457
+ header.writeUInt16BE(0, 8); // NSCOUNT
458
+ header.writeUInt16BE(0, 10); // ARCOUNT
459
+ const questionBuffers = questions.map(({ name, type: qtype, class: qclass, unicastResponse = false }) => {
460
+ const qname = this.encodeDnsName(name);
461
+ const qfields = Buffer.alloc(4);
462
+ qfields.writeUInt16BE(qtype, 0);
463
+ qfields.writeUInt16BE(unicastResponse ? qclass | 32768 /* DnsClassFlag.QU */ : qclass, 2);
464
+ return Buffer.concat([qname, qfields]);
465
+ });
466
+ const query = Buffer.concat([header, ...questionBuffers]);
467
+ const decoded = this.decodeMdnsMessage(query);
468
+ this.logMdnsMessage(decoded);
469
+ this.socket.send(query, 0, query.length, this.multicastPort, this.multicastAddress, (error) => {
470
+ if (error) {
471
+ this.log.error(`Dgram mDNS server failed to send query message: ${error.message}`);
472
+ this.emit('error', error);
473
+ }
474
+ else {
475
+ const names = questions
476
+ .map((q) => `- name ${MAGENTA}${q.name}${db} type ${MAGENTA}${this.dnsTypeToString(q.type)}${db} class ${MAGENTA}${this.dnsQuestionClassToString(q.class)}${db} unicastResponse ${MAGENTA}${q.unicastResponse}${db}`)
477
+ .join('\n');
478
+ this.log.debug(`Dgram mDNS server sent query message:\n${names}`);
479
+ this.emit('sent', query, this.multicastAddress, this.multicastPort);
480
+ }
481
+ });
482
+ }
483
+ /**
484
+ * Constructs an mDNS response packet and sends it to the multicast address and port.
485
+ *
486
+ * @param {string} name - The domain name being responded to (e.g., "example.local").
487
+ * @param {number} rtype - The response type (e.g., 1 for A, 28 for AAAA, etc.).
488
+ * @param {number} rclass - The response class (typically 1 for IN).
489
+ * @param {number} ttl - The time-to-live for the answer record.
490
+ * @param {Buffer} rdata - The resource data for the response (e.g., 4 bytes for an A record IPv4 address).
491
+ *
492
+ * @example
493
+ * const ptrRdata = mdnsIpv4.encodeDnsName('matterbridge._http._tcp.local');
494
+ * mdnsIpv4.sendResponse('_http._tcp.local', DnsRecordType.PTR, DnsClass.IN, 120, ptrRdata);
495
+ */
496
+ sendResponse(name, rtype, rclass, ttl, rdata) {
497
+ // Create a 12-byte DNS header.
498
+ const header = Buffer.alloc(12);
499
+ header.writeUInt16BE(0, 0); // ID is set to 0 in mDNS.
500
+ // Set flags: QR (response) bit and AA (authoritative answer) bit.
501
+ header.writeUInt16BE(0x8400, 2);
502
+ header.writeUInt16BE(0, 4); // QDCOUNT: 0 questions in response.
503
+ header.writeUInt16BE(1, 6); // ANCOUNT: 1 answer record.
504
+ header.writeUInt16BE(0, 8); // NSCOUNT: 0 authority records.
505
+ header.writeUInt16BE(0, 10); // ARCOUNT: 0 additional records.
506
+ // Encode the domain name in DNS label format.
507
+ const aname = this.encodeDnsName(name);
508
+ // Prepare the fixed part of the answer record:
509
+ // - 2 bytes for qtype,
510
+ // - 2 bytes for qclass,
511
+ // - 4 bytes for TTL,
512
+ // - 2 bytes for RDLENGTH (length of the rdata).
513
+ const answerFixed = Buffer.alloc(10);
514
+ answerFixed.writeUInt16BE(rtype, 0); // Record type.
515
+ answerFixed.writeUInt16BE(rclass, 2); // Record class.
516
+ answerFixed.writeUInt32BE(ttl, 4); // Time-to-live.
517
+ answerFixed.writeUInt16BE(rdata.length, 8); // RDLENGTH.
518
+ // Concatenate the answer: encoded name, fixed fields, and resource data.
519
+ const answer = Buffer.concat([aname, answerFixed, rdata]);
520
+ // Concatenate header and answer to form the complete mDNS response packet.
521
+ const response = Buffer.concat([header, answer]);
522
+ // Send the response packet via the socket.
523
+ this.socket.send(response, 0, response.length, this.multicastPort, this.multicastAddress, (error) => {
524
+ if (error) {
525
+ this.log.error(`Dgram mDNS server failed to send response message for ${MAGENTA}${name}${er} type ${MAGENTA}${this.dnsTypeToString(rtype)}${er} class ${MAGENTA}${this.dnsResponseClassToString(rclass)}${er} ttl ${MAGENTA}${ttl}${er}: ${error instanceof Error ? error.message : error}`);
526
+ this.emit('error', error);
527
+ }
528
+ else {
529
+ this.log.debug(`Dgram mDNS server sent response message for ${MAGENTA}${name}${db} type ${MAGENTA}${this.dnsTypeToString(rtype)}${db} class ${MAGENTA}${this.dnsResponseClassToString(rclass)}${db} ttl ${MAGENTA}${ttl}${db}`);
530
+ this.emit('sent', response, this.multicastAddress, this.multicastPort);
531
+ }
532
+ });
533
+ }
534
+ /**
535
+ * Converts a DNS record type numeric value to its string representation.
536
+ *
537
+ * @param {number} type - The numeric DNS record type.
538
+ * @returns {string} The string representation of the record type.
539
+ */
540
+ dnsTypeToString(type) {
541
+ const typeMap = {
542
+ [1 /* DnsRecordType.A */]: 'A',
543
+ [2 /* DnsRecordType.NS */]: 'NS',
544
+ [3 /* DnsRecordType.MD */]: 'MD',
545
+ [4 /* DnsRecordType.MF */]: 'MF',
546
+ [5 /* DnsRecordType.CNAME */]: 'CNAME',
547
+ [6 /* DnsRecordType.SOA */]: 'SOA',
548
+ [7 /* DnsRecordType.MB */]: 'MB',
549
+ [8 /* DnsRecordType.MG */]: 'MG',
550
+ [9 /* DnsRecordType.MR */]: 'MR',
551
+ [10 /* DnsRecordType.NULL */]: 'NULL',
552
+ [11 /* DnsRecordType.WKS */]: 'WKS',
553
+ [12 /* DnsRecordType.PTR */]: 'PTR',
554
+ [13 /* DnsRecordType.HINFO */]: 'HINFO',
555
+ [14 /* DnsRecordType.MINFO */]: 'MINFO',
556
+ [15 /* DnsRecordType.MX */]: 'MX',
557
+ [16 /* DnsRecordType.TXT */]: 'TXT',
558
+ [17 /* DnsRecordType.RP */]: 'RP',
559
+ [18 /* DnsRecordType.AFSDB */]: 'AFSDB',
560
+ [19 /* DnsRecordType.X25 */]: 'X25',
561
+ [20 /* DnsRecordType.ISDN */]: 'ISDN',
562
+ [21 /* DnsRecordType.RT */]: 'RT',
563
+ [22 /* DnsRecordType.NSAP */]: 'NSAP',
564
+ [23 /* DnsRecordType.NSAP_PTR */]: 'NSAP_PTR',
565
+ [24 /* DnsRecordType.SIG */]: 'SIG',
566
+ [25 /* DnsRecordType.KEY */]: 'KEY',
567
+ [26 /* DnsRecordType.PX */]: 'PX',
568
+ [27 /* DnsRecordType.GPOS */]: 'GPOS',
569
+ [28 /* DnsRecordType.AAAA */]: 'AAAA',
570
+ [29 /* DnsRecordType.LOC */]: 'LOC',
571
+ [30 /* DnsRecordType.NXT */]: 'NXT',
572
+ [31 /* DnsRecordType.EID */]: 'EID',
573
+ [32 /* DnsRecordType.NIMLOC */]: 'NIMLOC',
574
+ [33 /* DnsRecordType.SRV */]: 'SRV',
575
+ [34 /* DnsRecordType.ATMA */]: 'ATMA',
576
+ [35 /* DnsRecordType.NAPTR */]: 'NAPTR',
577
+ [36 /* DnsRecordType.KX */]: 'KX',
578
+ [37 /* DnsRecordType.CERT */]: 'CERT',
579
+ [38 /* DnsRecordType.A6 */]: 'A6',
580
+ [39 /* DnsRecordType.DNAME */]: 'DNAME',
581
+ [40 /* DnsRecordType.SINK */]: 'SINK',
582
+ [41 /* DnsRecordType.OPT */]: 'OPT',
583
+ [42 /* DnsRecordType.APL */]: 'APL',
584
+ [43 /* DnsRecordType.DS */]: 'DS',
585
+ [44 /* DnsRecordType.SSHFP */]: 'SSHFP',
586
+ [45 /* DnsRecordType.IPSECKEY */]: 'IPSECKEY',
587
+ [46 /* DnsRecordType.RRSIG */]: 'RRSIG',
588
+ [47 /* DnsRecordType.NSEC */]: 'NSEC',
589
+ [48 /* DnsRecordType.DNSKEY */]: 'DNSKEY',
590
+ [49 /* DnsRecordType.DHCID */]: 'DHCID',
591
+ [50 /* DnsRecordType.NSEC3 */]: 'NSEC3',
592
+ [51 /* DnsRecordType.NSEC3PARAM */]: 'NSEC3PARAM',
593
+ [52 /* DnsRecordType.TLSA */]: 'TLSA',
594
+ [53 /* DnsRecordType.SMIMEA */]: 'SMIMEA',
595
+ [55 /* DnsRecordType.HIP */]: 'HIP',
596
+ [56 /* DnsRecordType.NINFO */]: 'NINFO',
597
+ [57 /* DnsRecordType.RKEY */]: 'RKEY',
598
+ [58 /* DnsRecordType.TALINK */]: 'TALINK',
599
+ [59 /* DnsRecordType.CDS */]: 'CDS',
600
+ [60 /* DnsRecordType.CDNSKEY */]: 'CDNSKEY',
601
+ [61 /* DnsRecordType.OPENPGPKEY */]: 'OPENPGPKEY',
602
+ [62 /* DnsRecordType.CSYNC */]: 'CSYNC',
603
+ [63 /* DnsRecordType.ZONEMD */]: 'ZONEMD',
604
+ [64 /* DnsRecordType.SVCB */]: 'SVCB',
605
+ [65 /* DnsRecordType.HTTPS */]: 'HTTPS',
606
+ [99 /* DnsRecordType.SPF */]: 'SPF',
607
+ [100 /* DnsRecordType.UINFO */]: 'UINFO',
608
+ [101 /* DnsRecordType.UID */]: 'UID',
609
+ [102 /* DnsRecordType.GID */]: 'GID',
610
+ [103 /* DnsRecordType.UNSPEC */]: 'UNSPEC',
611
+ [104 /* DnsRecordType.NID */]: 'NID',
612
+ [105 /* DnsRecordType.L32 */]: 'L32',
613
+ [106 /* DnsRecordType.L64 */]: 'L64',
614
+ [107 /* DnsRecordType.LP */]: 'LP',
615
+ [108 /* DnsRecordType.EUI48 */]: 'EUI48',
616
+ [109 /* DnsRecordType.EUI64 */]: 'EUI64',
617
+ [249 /* DnsRecordType.TKEY */]: 'TKEY',
618
+ [250 /* DnsRecordType.TSIG */]: 'TSIG',
619
+ [251 /* DnsRecordType.IXFR */]: 'IXFR',
620
+ [252 /* DnsRecordType.AXFR */]: 'AXFR',
621
+ [253 /* DnsRecordType.MAILB */]: 'MAILB',
622
+ [254 /* DnsRecordType.MAILA */]: 'MAILA',
623
+ [255 /* DnsRecordType.ANY */]: 'ANY',
624
+ [256 /* DnsRecordType.URI */]: 'URI',
625
+ [257 /* DnsRecordType.CAA */]: 'CAA',
626
+ [258 /* DnsRecordType.AVC */]: 'AVC',
627
+ [259 /* DnsRecordType.DOA */]: 'DOA',
628
+ [260 /* DnsRecordType.AMTRELAY */]: 'AMTRELAY',
629
+ [261 /* DnsRecordType.ZONEVERSION */]: 'ZONEVERSION',
630
+ [32768 /* DnsRecordType.TA */]: 'TA',
631
+ [32769 /* DnsRecordType.DLV */]: 'DLV',
632
+ };
633
+ return typeMap[type] ?? `TYPE${type}`;
634
+ }
635
+ /**
636
+ * Converts a DNS response class numeric value to its string representation.
637
+ *
638
+ * @param {number} cls - The numeric DNS class.
639
+ * @returns {string} The string representation of the DNS class.
640
+ */
641
+ dnsResponseClassToString(cls) {
642
+ const isFlush = !!(cls & 32768 /* DnsClassFlag.FLUSH */);
643
+ const baseClass = cls & 0x7fff;
644
+ let classStr;
645
+ switch (baseClass) {
646
+ case 1 /* DnsClass.IN */:
647
+ classStr = 'IN';
648
+ break;
649
+ case 3 /* DnsClass.CH */:
650
+ classStr = 'CH';
651
+ break;
652
+ case 4 /* DnsClass.HS */:
653
+ classStr = 'HS';
654
+ break;
655
+ case 255 /* DnsClass.ANY */:
656
+ classStr = 'ANY';
657
+ break;
658
+ default:
659
+ classStr = `CLASS${baseClass}`;
660
+ }
661
+ return isFlush ? `${classStr}|FLUSH` : classStr;
662
+ }
663
+ /**
664
+ * Converts a DNS question class to a human-readable string.
665
+ * Adds support for mDNS QU (unicast-response) bit.
666
+ *
667
+ * @param {number} cls - The numeric question class.
668
+ * @returns {string} The string representation, e.g. "IN|QU"
669
+ */
670
+ dnsQuestionClassToString(cls) {
671
+ const isQU = !!(cls & 32768 /* DnsClassFlag.QU */);
672
+ const baseClass = cls & 0x7fff;
673
+ let classStr;
674
+ switch (baseClass) {
675
+ case 1 /* DnsClass.IN */:
676
+ classStr = 'IN';
677
+ break;
678
+ case 3 /* DnsClass.CH */:
679
+ classStr = 'CH';
680
+ break;
681
+ case 4 /* DnsClass.HS */:
682
+ classStr = 'HS';
683
+ break;
684
+ case 255 /* DnsClass.ANY */:
685
+ classStr = 'ANY';
686
+ break;
687
+ default:
688
+ classStr = `CLASS${baseClass}`;
689
+ }
690
+ return isQU ? `${classStr}|QU` : classStr;
691
+ }
692
+ /**
693
+ * Logs the decoded mDNS message header.
694
+ *
695
+ * @param {MdnsMessage} msg - The mDNS message header object.
696
+ */
697
+ logMdnsMessage(msg) {
698
+ this.log.info(`Decoded mDNS message: ID ${MAGENTA}${msg.id}${nf}, QR ${GREEN}${msg.qr === 0 ? 'Query' : 'Response'}${nf}, OPCODE ${MAGENTA}${msg.opcode}${nf}, AA ${MAGENTA}${msg.aa}${nf}, TC ${MAGENTA}${msg.tc}${nf}, RD ${MAGENTA}${msg.rd}${nf}, RA ${MAGENTA}${msg.ra}${nf}, Z ${MAGENTA}${msg.z}${nf}, RCODE ${MAGENTA}${msg.rcode}${nf}, QDCount ${MAGENTA}${msg.qdCount}${nf}, ANCount ${MAGENTA}${msg.anCount}${nf}, NSCount ${MAGENTA}${msg.nsCount}${nf}, ARCount ${MAGENTA}${msg.arCount}${nf}`);
699
+ msg.questions?.forEach((question) => {
700
+ this.log.info(`Question: ${CYAN}${question.name}${nf} type ${idn}${this.dnsTypeToString(question.type)}${rs}${nf} class ${CYAN}${this.dnsQuestionClassToString(question.class)}${nf}`);
701
+ });
702
+ msg.answers?.forEach((answer) => {
703
+ this.log.info(`Answer: ${CYAN}${answer.name}${nf} type ${idn}${this.dnsTypeToString(answer.type)}${rs}${nf} class ${CYAN}${this.dnsResponseClassToString(answer.class)}${nf} ttl ${CYAN}${answer.ttl}${nf} data ${CYAN}${answer.data}${nf}`);
704
+ });
705
+ msg.authorities?.forEach((authority) => {
706
+ this.log.info(`Authority: ${CYAN}${authority.name}${nf} type ${idn}${this.dnsTypeToString(authority.type)}${rs}${nf} class ${CYAN}${this.dnsResponseClassToString(authority.class)}${nf} ttl ${CYAN}${authority.ttl}${nf} data ${CYAN}${authority.data}${nf}`);
707
+ });
708
+ msg.additionals?.forEach((additional) => {
709
+ this.log.info(`Additional: ${CYAN}${additional.name}${nf} type ${idn}${this.dnsTypeToString(additional.type)}${rs}${nf} class ${CYAN}${this.dnsResponseClassToString(additional.class)}${nf} ttl ${CYAN}${additional.ttl}${nf} data ${CYAN}${additional.data}${nf}`);
710
+ });
711
+ this.log.info(`---\n`);
712
+ }
713
+ /**
714
+ * Logs the discovered devices from the mDNS queries and responses.
715
+ */
716
+ logDevices() {
717
+ this.log.info(`Discovered query devices: ${MAGENTA}${this.deviceQueries.size}${nf}`);
718
+ // Collect devices into an array
719
+ const deviceQueryArray = Array.from(this.deviceQueries.entries());
720
+ // Sort the array by numeric value of the IP address
721
+ deviceQueryArray.sort(([addressA], [addressB]) => {
722
+ const partsA = addressA.split('.').map(Number);
723
+ const partsB = addressB.split('.').map(Number);
724
+ for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
725
+ const diff = (partsA[i] || 0) - (partsB[i] || 0);
726
+ if (diff !== 0)
727
+ return diff;
728
+ }
729
+ // istanbul ignore next
730
+ return 0;
731
+ });
732
+ // Log the sorted devices
733
+ deviceQueryArray.forEach(([rinfo, response]) => {
734
+ this.log.info(`- ${MAGENTA}${rinfo}${nf} family ${BLUE}${response.rinfo.family}${nf} address ${BLUE}${response.rinfo.address}${nf} port ${BLUE}${response.rinfo.port}${nf}`);
735
+ });
736
+ this.log.info(`Discovered response devices: ${MAGENTA}${this.deviceResponses.size}${nf}`);
737
+ // Collect devices into an array
738
+ const deviceResponseArray = Array.from(this.deviceResponses.entries());
739
+ // Sort the array by numeric value of the IP address
740
+ deviceResponseArray.sort(([addressA], [addressB]) => {
741
+ const partsA = addressA.split(/[:.]/).map((part) => parseInt(part, 16));
742
+ const partsB = addressB.split(/[:.]/).map((part) => parseInt(part, 16));
743
+ for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
744
+ const diff = (partsA[i] || 0) - (partsB[i] || 0);
745
+ if (diff !== 0)
746
+ return diff;
747
+ }
748
+ // istanbul ignore next
749
+ return 0;
750
+ });
751
+ // Log the sorted devices
752
+ deviceResponseArray.forEach(([rinfo, response]) => {
753
+ this.log.info(`- ${MAGENTA}${rinfo}${nf} family ${BLUE}${response.rinfo.family}${nf} address ${BLUE}${response.rinfo.address}${nf} port ${BLUE}${response.rinfo.port}${nf} PTR ${GREEN}${response.dataPTR}${nf}`);
754
+ });
755
+ }
756
+ }
757
+ // CLI entry point for manual testing: node dist/dgram/mdns.js --mdns-udp4 --mdns-udp6 --mdns-query
758
+ // istanbul ignore next if
759
+ if (process.argv.includes('--mdns-udp4') || process.argv.includes('--mdns-udp6')) {
760
+ // const mdnsIpv4 = new Mdns('mDNS Server udp4', MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_PORT, 'udp4', true, 'Loopback Pseudo-Interface 1', '127.0.0.1');
761
+ // const mdnsIpv6 = new Mdns('mDNS Server udp6', MDNS_MULTICAST_IPV6_ADDRESS, MDNS_MULTICAST_PORT, 'udp6', true, 'Loopback Pseudo-Interface 1', '::1');
762
+ const mdnsIpv4 = new Mdns('mDNS Server udp4', MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_PORT, 'udp4', true, undefined, '0.0.0.0');
763
+ const mdnsIpv6 = new Mdns('mDNS Server udp6', MDNS_MULTICAST_IPV6_ADDRESS, MDNS_MULTICAST_PORT, 'udp6', true, undefined, '::');
764
+ mdnsIpv6.listNetworkInterfaces();
765
+ /**
766
+ * Cleanup and log device information before exiting.
767
+ */
768
+ function cleanupAndLogAndExit() {
769
+ if (process.argv.includes('--mdns-udp4'))
770
+ mdnsIpv4.stop();
771
+ if (process.argv.includes('--mdns-udp6'))
772
+ mdnsIpv6.stop();
773
+ if (process.argv.includes('--mdns-udp4'))
774
+ mdnsIpv4.logDevices();
775
+ if (process.argv.includes('--mdns-udp6'))
776
+ mdnsIpv6.logDevices();
777
+ // eslint-disable-next-line n/no-process-exit
778
+ process.exit(0);
779
+ }
780
+ /**
781
+ * Queries mDNS services over UDP IPv4 and sends a response for a specific service instance.
782
+ * This function sends a query for Shelly, HTTP, and services, and responds with the appropriate PTR records.
783
+ */
784
+ const queryUdp4 = () => {
785
+ mdnsIpv4.sendQuery([
786
+ { name: '_matter._tcp.local', type: 12 /* DnsRecordType.PTR */, class: 1 /* DnsClass.IN */, unicastResponse: false },
787
+ { name: '_shelly._tcp.local', type: 12 /* DnsRecordType.PTR */, class: 1 /* DnsClass.IN */, unicastResponse: false },
788
+ { name: '_http._tcp.local', type: 12 /* DnsRecordType.PTR */, class: 1 /* DnsClass.IN */, unicastResponse: false },
789
+ { name: '_services._dns-sd._udp.local', type: 12 /* DnsRecordType.PTR */, class: 1 /* DnsClass.IN */, unicastResponse: false },
790
+ ]);
791
+ const ptrRdata = mdnsIpv4.encodeDnsName('matterbridge._http._tcp.local');
792
+ mdnsIpv4.sendResponse('_http._tcp.local', 12 /* DnsRecordType.PTR */, 1 /* DnsClass.IN */, 120, ptrRdata);
793
+ };
794
+ /**
795
+ * Queries mDNS services over UDP IPv6 and sends a response for a specific service instance.
796
+ * This function sends a query for Shelly, HTTP, and services, and responds with the appropriate PTR records.
797
+ */
798
+ const queryUdp6 = () => {
799
+ mdnsIpv6.sendQuery([
800
+ { name: '_matter._tcp.local', type: 12 /* DnsRecordType.PTR */, class: 1 /* DnsClass.IN */, unicastResponse: true },
801
+ { name: '_shelly._tcp.local', type: 12 /* DnsRecordType.PTR */, class: 1 /* DnsClass.IN */, unicastResponse: true },
802
+ { name: '_http._tcp.local', type: 12 /* DnsRecordType.PTR */, class: 1 /* DnsClass.IN */, unicastResponse: true },
803
+ { name: '_services._dns-sd._udp.local', type: 12 /* DnsRecordType.PTR */, class: 1 /* DnsClass.IN */, unicastResponse: true },
804
+ ]);
805
+ const ptrRdata = mdnsIpv6.encodeDnsName('matterbridge._http._tcp.local');
806
+ mdnsIpv6.sendResponse('_http._tcp.local', 12 /* DnsRecordType.PTR */, 1 /* DnsClass.IN */, 120, ptrRdata);
807
+ };
808
+ // Handle Ctrl+C (SIGINT) to stop and log devices
809
+ process.on('SIGINT', () => {
810
+ cleanupAndLogAndExit();
811
+ });
812
+ if (process.argv.includes('--mdns-udp4')) {
813
+ mdnsIpv4.start();
814
+ mdnsIpv4.on('ready', (address) => {
815
+ mdnsIpv4.log.info(`mdnsIpv4 server ready on ${address.family} ${address.address}:${address.port}`);
816
+ if (!process.argv.includes('--mdns-query'))
817
+ return; // Skip querying if --mdns-query is not specified
818
+ queryUdp4();
819
+ setInterval(() => {
820
+ queryUdp4();
821
+ }, 10000).unref();
822
+ });
823
+ }
824
+ if (process.argv.includes('--mdns-udp6')) {
825
+ mdnsIpv6.start();
826
+ mdnsIpv6.on('ready', (address) => {
827
+ mdnsIpv6.log.info(`mdnsIpv6 server ready on ${address.family} ${address.address}:${address.port}`);
828
+ if (!process.argv.includes('--mdns-query'))
829
+ return; // Skip querying if --mdns-query is not specified
830
+ queryUdp6();
831
+ setInterval(() => {
832
+ queryUdp6();
833
+ }, 10000).unref();
834
+ });
835
+ }
836
+ setTimeout(() => {
837
+ cleanupAndLogAndExit();
838
+ }, 600000); // 10 minutes timeout to exit if no activity
839
+ }
840
+ //# sourceMappingURL=mdns.js.map