@honeybbq/teamspeak 1.0.0-beta.22 → 1.0.0-beta.24

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.
@@ -1,16 +1,59 @@
1
- import type { ResolvedAddr } from "../types.js";
1
+ import type { AddrResolver, ResolvedAddr } from "../types.js";
2
2
  /**
3
3
  * The most a TSDNS server or the nickname service is read for. Either answers
4
4
  * with one short line; without a bound, one that kept sending would be held in
5
5
  * memory until the resolution budget ran out.
6
6
  */
7
7
  export declare const MAX_DISCOVERY_RESPONSE: number;
8
+ /** An SRV record, as `resolveSrv` gives it. */
9
+ export interface SrvRecord {
10
+ /** The target host; a trailing dot is allowed. */
11
+ readonly name: string;
12
+ /** The target's port. */
13
+ readonly port: number;
14
+ /** Lower is tried first. */
15
+ readonly priority: number;
16
+ /** Among records of one priority, the higher is tried first more often. */
17
+ readonly weight: number;
18
+ }
8
19
  /** How the default resolver resolves. */
9
20
  export interface ResolverOptions {
10
21
  /** Look dot-less names up at named.myteamspeak.com; off by default. */
11
22
  readonly nicknameLookup?: boolean | undefined;
23
+ /**
24
+ * The nickname lookup endpoint, an http: or https: URL; the default is
25
+ * `https://named.myteamspeak.com/lookup`. Used only with `nicknameLookup`:
26
+ * setting it does not turn the lookup on. For a test double or a mirror of
27
+ * the service. The name is sent as the `name` query parameter, and the first
28
+ * line of the answer is the address. A URL that is not http: or https: throws
29
+ * a TypeError.
30
+ */
31
+ readonly nicknameService?: string | URL | undefined;
32
+ /**
33
+ * Answers the SRV lookups, for DNS over HTTPS, another DNS server, or a
34
+ * test; the default is the system's `resolveSrv`. It answers every SRV
35
+ * lookup the resolver makes (`_ts3._udp.<host>` and `_tsdns._tcp.<domain>`),
36
+ * called with the full name and the resolution's signal. A rejection or an
37
+ * empty list means no record. One that ignores the signal cannot hold
38
+ * resolution up. The host names in the answers, and the TSDNS servers'
39
+ * names, are still looked up by the system.
40
+ */
41
+ readonly resolveSrv?: ((name: string, options: {
42
+ readonly signal: AbortSignal;
43
+ }) => Promise<readonly SrvRecord[]>) | undefined;
44
+ /**
45
+ * The `fetch` the nickname lookup uses, for a proxy or a test; the default
46
+ * is the global `fetch`. Used only with `nicknameLookup`.
47
+ */
48
+ readonly fetch?: typeof globalThis.fetch | undefined;
12
49
  }
13
- export declare class Resolver {
50
+ /**
51
+ * The resolver a Client uses unless it is given another. It resolves as the
52
+ * TeamSpeak application does, so on its own it shows where an address leads:
53
+ * `await new Resolver().resolve("ts.example.com")`. Answers are kept for ten
54
+ * minutes.
55
+ */
56
+ export declare class Resolver implements AddrResolver {
14
57
  #private;
15
58
  constructor(options?: ResolverOptions);
16
59
  /**
@@ -23,4 +66,22 @@ export declare class Resolver {
23
66
  readonly signal?: AbortSignal | undefined;
24
67
  }): Promise<ResolvedAddr[]>;
25
68
  }
69
+ /**
70
+ * Ask a TSDNS server where `host` is. The answer is the server's targets in
71
+ * its order and as it sent them: an entry may have no port, which TeamSpeak
72
+ * takes as 9987, or the literal port `$PORT`, the port the user gave (9987 by
73
+ * default). {@link Resolver} applies both. A name the server has no record for
74
+ * gives an empty list.
75
+ *
76
+ * The answer ends at its first line break or when the server closes the
77
+ * connection, whichever comes first. It rejects when the connection fails,
78
+ * when no answer completes within three seconds or grows past 8 KiB, and with
79
+ * the signal's reason when it aborts.
80
+ *
81
+ * @param server - The TSDNS server, `host` or `host:port`; the port defaults to 41144.
82
+ * @param host - The name to look up; it is sent without a port.
83
+ */
84
+ export declare function lookupTSDNS(server: string, host: string, options?: {
85
+ readonly signal?: AbortSignal | undefined;
86
+ }): Promise<string[]>;
26
87
  //# sourceMappingURL=resolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/client/discovery/resolver.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAkBhD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAiB,CAAC;AAEvD,yCAAyC;AACzC,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC/C;AAED,qBAAa,QAAQ;;IAInB,YAAY,OAAO,GAAE,eAAoB,EAExC;IAED;;;;;OAKG;IACG,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;KAAO,GAC1D,OAAO,CAAC,YAAY,EAAE,CAAC,CAYzB;CAkHF"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/client/discovery/resolver.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAqB9D;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAiB,CAAC;AAEvD,+CAA+C;AAC/C,MAAM,WAAW,SAAS;IACxB,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yBAAyB;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4BAA4B;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,yCAAyC;AACzC,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9C;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC;IACpD;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,CAAC,EAChB,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC,CAAC,GAC5F,SAAS,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,GAAG,SAAS,CAAC;CACtD;AAED;;;;;GAKG;AACH,qBAAa,QAAS,YAAW,YAAY;;IAO3C,YAAY,OAAO,GAAE,eAAoB,EAWxC;IAED;;;;;OAKG;IACG,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;KAAO,GAC1D,OAAO,CAAC,YAAY,EAAE,CAAC,CAYzB;CAkIF;AAiJD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CAAO,GAC1D,OAAO,CAAC,MAAM,EAAE,CAAC,CAuDnB"}
@@ -3,8 +3,11 @@ import { createConnection } from "node:net";
3
3
  import { ResolveSource } from "../types.js";
4
4
  import { isIpAddress, joinHostPort, splitHostPort } from "../address.js";
5
5
  import { abortable } from "../abort.js";
6
+ import { TeamspeakError } from "@honeybbq/teamspeak";
6
7
  import { InvalidAddressError } from "../errors.js";
7
8
  const TS_DNS_DEFAULT_PORT = 41144;
9
+ /** What a TSDNS entry has for a port to say: the one the user gave. */
10
+ const PORT_PLACEHOLDER = ":$PORT";
8
11
  const NICKNAME_LOOKUP_URL = "https://named.myteamspeak.com/lookup";
9
12
  const CACHE_TTL_MS = 10 * 60 * 1000;
10
13
  /**
@@ -22,11 +25,29 @@ const TSDNS_TIMEOUT_MS = 3_000;
22
25
  * memory until the resolution budget ran out.
23
26
  */
24
27
  export const MAX_DISCOVERY_RESPONSE = 8 * 1024;
28
+ /**
29
+ * The resolver a Client uses unless it is given another. It resolves as the
30
+ * TeamSpeak application does, so on its own it shows where an address leads:
31
+ * `await new Resolver().resolve("ts.example.com")`. Answers are kept for ten
32
+ * minutes.
33
+ */
25
34
  export class Resolver {
26
35
  #cache = new Map();
27
36
  #nicknameLookup;
37
+ #nicknameService;
38
+ #resolveSrv;
39
+ #fetch;
28
40
  constructor(options = {}) {
41
+ if (options.resolveSrv !== undefined && typeof options.resolveSrv !== "function") {
42
+ throw new TypeError("resolveSrv is a function");
43
+ }
44
+ if (options.fetch !== undefined && typeof options.fetch !== "function") {
45
+ throw new TypeError("fetch is a function");
46
+ }
47
+ this.#resolveSrv = options.resolveSrv ?? ((name) => resolveSrv(name));
48
+ this.#fetch = options.fetch;
29
49
  this.#nicknameLookup = options.nicknameLookup ?? false;
50
+ this.#nicknameService = parseNicknameService(options.nicknameService ?? NICKNAME_LOOKUP_URL);
30
51
  }
31
52
  /**
32
53
  * Resolve as the TeamSpeak application does: a server nickname (only with
@@ -62,7 +83,7 @@ export class Resolver {
62
83
  // service can answer: asking it sends the name to a third party, so it is
63
84
  // opt-in. Otherwise the name is resolved like any host.
64
85
  if (nicknames && this.#nicknameLookup && !host.includes(".") && host !== "localhost") {
65
- const nickAddr = await resolveNickname(host, signal);
86
+ const nickAddr = await resolveNickname(host, this.#nicknameService, signal, this.#fetch);
66
87
  signal.throwIfAborted();
67
88
  if (nickAddr)
68
89
  return this.#resolve(nickAddr, signal, false);
@@ -73,14 +94,14 @@ export class Resolver {
73
94
  return this.#setCache(inputAddr, srvResults);
74
95
  const domainList = getDomainList(host);
75
96
  // TSDNS via SRV _tsdns._tcp.<domain>
76
- const tsdnsSrv = await this.#resolveTSDNSSRV(domainList, host, signal);
77
- if (tsdnsSrv) {
78
- return this.#setCache(inputAddr, [{ addr: tsdnsSrv, source: ResolveSource.TSDNSSRV }]);
97
+ const tsdnsSrv = await this.#resolveTSDNSSRV(domainList, host, port, signal);
98
+ if (tsdnsSrv.length > 0) {
99
+ return this.#setCache(inputAddr, tsdnsSrv.map((addr) => ({ addr, source: ResolveSource.TSDNSSRV })));
79
100
  }
80
101
  // TSDNS direct :41144
81
- const tsdnsDirect = await this.#resolveTSDNSDirect(domainList, host, signal);
82
- if (tsdnsDirect) {
83
- return this.#setCache(inputAddr, [{ addr: tsdnsDirect, source: ResolveSource.TSDNSDirect }]);
102
+ const tsdnsDirect = await this.#resolveTSDNSDirect(domainList, host, port, signal);
103
+ if (tsdnsDirect.length > 0) {
104
+ return this.#setCache(inputAddr, tsdnsDirect.map((addr) => ({ addr, source: ResolveSource.TSDNSDirect })));
84
105
  }
85
106
  signal.throwIfAborted();
86
107
  // Plain DNS fallback
@@ -104,9 +125,13 @@ export class Resolver {
104
125
  this.#cache.set(key, withExpiry);
105
126
  return withExpiry;
106
127
  }
128
+ /** One SRV lookup, however it is answered, ended by the signal. */
129
+ async #srv(name, signal) {
130
+ return abortable(Promise.resolve().then(() => this.#resolveSrv(name, { signal })), signal);
131
+ }
107
132
  async #resolveSRV(host, signal) {
108
133
  try {
109
- const srvs = await abortable(resolveSrv(`_ts3._udp.${host}`), signal);
134
+ const srvs = await this.#srv(`_ts3._udp.${host}`, signal);
110
135
  if (!srvs || srvs.length === 0)
111
136
  return null;
112
137
  return orderSrvRecords(srvs).map((srv) => ({
@@ -118,16 +143,16 @@ export class Resolver {
118
143
  return null;
119
144
  }
120
145
  }
121
- async #resolveTSDNSSRV(domains, queryHost, signal) {
146
+ async #resolveTSDNSSRV(domains, queryHost, port, signal) {
122
147
  for (const domain of domains) {
123
148
  try {
124
- const srvs = await abortable(resolveSrv(`_tsdns._tcp.${domain}`), signal);
149
+ const srvs = await this.#srv(`_tsdns._tcp.${domain}`, signal);
125
150
  if (!srvs || srvs.length === 0)
126
151
  continue;
127
152
  for (const srv of orderSrvRecords(srvs)) {
128
153
  const tsdnsAddr = joinHostPort(srv.name.replace(/\.$/, ""), String(srv.port));
129
- const result = await queryTSDNS(tsdnsAddr, queryHost, signal);
130
- if (result)
154
+ const result = await askTSDNS(tsdnsAddr, queryHost, port, signal);
155
+ if (result.length > 0)
131
156
  return result;
132
157
  }
133
158
  }
@@ -135,15 +160,44 @@ export class Resolver {
135
160
  continue;
136
161
  }
137
162
  }
138
- return null;
163
+ return [];
139
164
  }
140
- async #resolveTSDNSDirect(domains, queryHost, signal) {
165
+ async #resolveTSDNSDirect(domains, queryHost, port, signal) {
141
166
  // Asked at once rather than in turn, so the probes cost one timeout in
142
167
  // all; the most specific domain's answer wins.
143
- const answers = await Promise.all(domains.map((domain) => queryTSDNS(joinHostPort(domain, String(TS_DNS_DEFAULT_PORT)), queryHost, signal)));
144
- return answers.find((answer) => answer !== null) ?? null;
168
+ const answers = await Promise.all(domains.map((domain) => askTSDNS(joinHostPort(domain, String(TS_DNS_DEFAULT_PORT)), queryHost, port, signal)));
169
+ return answers.find((answer) => answer.length > 0) ?? [];
145
170
  }
146
171
  }
172
+ /**
173
+ * Ask one TSDNS server; a server that fails to answer is one with no answer,
174
+ * so it never fails the resolution.
175
+ */
176
+ async function askTSDNS(server, host, port, signal) {
177
+ try {
178
+ return normalizeTSDNSEntries(await lookupTSDNS(server, host, { signal }), port);
179
+ }
180
+ catch {
181
+ return [];
182
+ }
183
+ }
184
+ /**
185
+ * Make each entry of a TSDNS answer a `host:port`, as TeamSpeak reads it: an
186
+ * entry without a port means 9987, and the port `$PORT` means `port`, the one
187
+ * the address being resolved had.
188
+ *
189
+ * @internal
190
+ */
191
+ export function normalizeTSDNSEntries(entries, port) {
192
+ return entries.map((entry) => {
193
+ if (entry.endsWith(PORT_PLACEHOLDER)) {
194
+ const host = splitHostPort(entry.slice(0, -PORT_PLACEHOLDER.length)).host;
195
+ return joinHostPort(host, port);
196
+ }
197
+ const target = splitHostPort(entry, "9987");
198
+ return joinHostPort(target.host, target.port);
199
+ });
200
+ }
147
201
  /**
148
202
  * Put SRV records in the order RFC 2782 has a client try them: by priority,
149
203
  * lowest first, and within a priority by weighted random selection, so a
@@ -188,13 +242,26 @@ export function orderSrvRecords(records, random = Math.random) {
188
242
  }
189
243
  return ordered;
190
244
  }
245
+ function parseNicknameService(service) {
246
+ let url;
247
+ try {
248
+ url = new URL(service);
249
+ }
250
+ catch {
251
+ throw new TypeError(`nicknameService is not a URL: ${String(service)}`);
252
+ }
253
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
254
+ throw new TypeError(`nicknameService is an http: or https: URL, not ${url.protocol}`);
255
+ }
256
+ return url;
257
+ }
191
258
  /** @internal Ask the nickname service for `nickname`'s address. */
192
- export async function resolveNickname(nickname, signal) {
259
+ export async function resolveNickname(nickname, service, signal, fetchImpl) {
193
260
  try {
194
- const url = new URL(NICKNAME_LOOKUP_URL);
261
+ const url = new URL(service);
195
262
  url.searchParams.set("name", nickname);
196
263
  const init = signal ? { signal } : {};
197
- const resp = await fetch(url.toString(), init);
264
+ const resp = await (fetchImpl ?? globalThis.fetch)(url.toString(), init);
198
265
  if (!resp.ok)
199
266
  return null;
200
267
  const text = await readLimited(resp, MAX_DISCOVERY_RESPONSE);
@@ -227,46 +294,74 @@ async function readLimited(resp, limit) {
227
294
  }
228
295
  return Buffer.concat(chunks).toString("utf8");
229
296
  }
230
- /** @internal Ask the TSDNS server at `tsdnsAddr` for `queryHost`'s address. */
231
- export function queryTSDNS(tsdnsAddr, queryHost, signal) {
232
- const [host, portStr] = splitHostPortParts(tsdnsAddr);
233
- const port = parseInt(portStr, 10);
234
- return new Promise((resolve) => {
297
+ /**
298
+ * Ask a TSDNS server where `host` is. The answer is the server's targets in
299
+ * its order and as it sent them: an entry may have no port, which TeamSpeak
300
+ * takes as 9987, or the literal port `$PORT`, the port the user gave (9987 by
301
+ * default). {@link Resolver} applies both. A name the server has no record for
302
+ * gives an empty list.
303
+ *
304
+ * The answer ends at its first line break or when the server closes the
305
+ * connection, whichever comes first. It rejects when the connection fails,
306
+ * when no answer completes within three seconds or grows past 8 KiB, and with
307
+ * the signal's reason when it aborts.
308
+ *
309
+ * @param server - The TSDNS server, `host` or `host:port`; the port defaults to 41144.
310
+ * @param host - The name to look up; it is sent without a port.
311
+ */
312
+ export function lookupTSDNS(server, host, options = {}) {
313
+ const { signal } = options;
314
+ const target = splitHostPort(server, String(TS_DNS_DEFAULT_PORT));
315
+ const port = parseInt(target.port, 10);
316
+ return new Promise((resolve, reject) => {
235
317
  if (signal?.aborted)
236
- return resolve(null);
237
- const finish = (answer) => {
318
+ return reject(signal.reason);
319
+ const chunks = [];
320
+ let size = 0;
321
+ let done = false;
322
+ const finish = (error, answer) => {
323
+ if (done)
324
+ return;
325
+ done = true;
238
326
  clearTimeout(timeout);
239
327
  signal?.removeEventListener("abort", onAbort);
240
328
  socket.destroy();
241
- resolve(answer);
329
+ if (error !== undefined)
330
+ reject(error);
331
+ else
332
+ resolve(answer ?? []);
242
333
  };
243
- const onAbort = () => finish(null);
244
- const timeout = setTimeout(() => finish(null), TSDNS_TIMEOUT_MS);
245
- const socket = createConnection({ host, port, timeout: 2_000 }, () => {
246
- socket.write(`${queryHost}\n`);
334
+ const answerOf = (bytes) => {
335
+ const line = bytes.toString("utf8").trim();
336
+ return !line || line === "404" || line === "errors" ? [] : line.split(/\s+/);
337
+ };
338
+ const timedOut = () => finish(new TeamspeakError(`the TSDNS server ${server} did not answer in time`));
339
+ const onAbort = () => finish(signal?.reason);
340
+ const timeout = setTimeout(timedOut, TSDNS_TIMEOUT_MS);
341
+ const socket = createConnection({ host: target.host, port, timeout: 2_000 }, () => {
342
+ socket.write(`${host}\n`);
247
343
  });
248
- let buf = "";
249
344
  socket.on("data", (chunk) => {
250
- buf += chunk.toString("latin1");
251
- const idx = buf.indexOf("\n");
345
+ chunks.push(chunk);
346
+ size += chunk.length;
347
+ const all = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
348
+ chunks.length = 0;
349
+ chunks.push(all);
350
+ const idx = all.indexOf(0x0a);
252
351
  if (idx >= 0 && idx < MAX_DISCOVERY_RESPONSE) {
253
- const line = Buffer.from(buf.slice(0, idx), "latin1").toString("utf8").trim();
254
- finish(!line || line === "404" || line === "errors" ? null : line);
352
+ finish(undefined, answerOf(all.subarray(0, idx)));
255
353
  }
256
- else if (buf.length >= MAX_DISCOVERY_RESPONSE) {
354
+ else if (size >= MAX_DISCOVERY_RESPONSE) {
257
355
  // No answer is that long.
258
- finish(null);
356
+ finish(new TeamspeakError(`the TSDNS server ${server} sent ${MAX_DISCOVERY_RESPONSE} bytes without ending its answer`));
259
357
  }
260
358
  });
261
- socket.on("error", () => finish(null));
262
- socket.on("timeout", () => finish(null));
359
+ socket.on("end", () => finish(undefined, answerOf(Buffer.concat(chunks))));
360
+ socket.on("error", (error) => finish(error));
361
+ socket.on("timeout", timedOut);
263
362
  signal?.addEventListener("abort", onAbort, { once: true });
264
363
  });
265
364
  }
266
- function splitHostPortParts(addr) {
267
- const { host, port } = splitHostPort(addr);
268
- return [host, port];
269
- }
270
365
  function getDomainList(host) {
271
366
  const parts = host.split(".");
272
367
  const list = [];
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../../src/client/discovery/resolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../../src/client/discovery/resolver.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,8 @@
9
9
  */
10
10
  export { Client } from "./client.js";
11
11
  export { EventName, GroupWhisperType, GroupWhisperScope, ResolveSource } from "./types.js";
12
+ export { Resolver, lookupTSDNS } from "./discovery/resolver.js";
13
+ export type { ResolverOptions, SrvRecord } from "./discovery/resolver.js";
12
14
  export { ViewStatus } from "./view.js";
13
15
  export type { ServerView, ViewChangedEvent, ViewReadyEvent } from "./view.js";
14
16
  export type { EventMap, EventMiddleware, ServerEvent, ServerEventName, VoiceOptions, WhisperTargets, GroupWhisperTarget, AddrResolver, ClientOptions, ResolvedAddr, DatagramSocket, SocketFactory, FileTransferSocketFactory, } from "./types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3F,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE9E,YAAY,EACV,QAAQ,EACR,eAAe,EACf,WAAW,EACX,eAAe,EACf,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,cAAc,EACd,aAAa,EACb,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAGrB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAG/B,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,yBAAyB,EACzB,wBAAwB,EACxB,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGpD,OAAO,EACL,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3F,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE9E,YAAY,EACV,QAAQ,EACR,eAAe,EACf,WAAW,EACX,eAAe,EACf,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,cAAc,EACd,aAAa,EACb,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAGrB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAG/B,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,yBAAyB,EACzB,wBAAwB,EACxB,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGpD,OAAO,EACL,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
@@ -10,6 +10,7 @@
10
10
  // The client
11
11
  export { Client } from "./client.js";
12
12
  export { EventName, GroupWhisperType, GroupWhisperScope, ResolveSource } from "./types.js";
13
+ export { Resolver, lookupTSDNS } from "./discovery/resolver.js";
13
14
  export { ViewStatus } from "./view.js";
14
15
  export { FileTransferError, HandshakeError, PuzzleTooHardError, InvalidAddressError, InvalidIdentityError, RemovedError, ConnectionTimeoutError, ConnectionStalledError, } from "./errors.js";
15
16
  // Commands only the voice protocol has.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,aAAa;AACb,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3F,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAmBvC,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAErB,wCAAwC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAE/B,qBAAqB;AACrB,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAWvB,+BAA+B;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAkB1C,+CAA+C;AAC/C,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAG9D,qDAAqD;AACrD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGnE,sBAAsB;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,WAAW;AACX,OAAO,EACL,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,aAAa;AACb,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3F,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAmBvC,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAErB,wCAAwC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAE/B,qBAAqB;AACrB,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAWvB,+BAA+B;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAkB1C,+CAA+C;AAC/C,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAG9D,qDAAqD;AACrD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGnE,sBAAsB;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,WAAW;AACX,OAAO,EACL,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@honeybbq/teamspeak",
3
- "version": "1.0.0-beta.22",
3
+ "version": "1.0.0-beta.24",
4
4
  "description": "TeamSpeak SDK for TypeScript: an independent voice client for Node.js, and the typed commands, ids and errors every connection shares",
5
5
  "keywords": [
6
6
  "client",
@@ -1,12 +1,15 @@
1
1
  import { resolveSrv } from "node:dns/promises";
2
2
  import { createConnection } from "node:net";
3
3
  import { ResolveSource } from "../types.js";
4
- import type { ResolvedAddr } from "../types.js";
4
+ import type { AddrResolver, ResolvedAddr } from "../types.js";
5
5
  import { isIpAddress, joinHostPort, splitHostPort } from "../address.js";
6
6
  import { abortable } from "../abort.js";
7
+ import { TeamspeakError } from "@honeybbq/teamspeak";
7
8
  import { InvalidAddressError } from "../errors.js";
8
9
 
9
10
  const TS_DNS_DEFAULT_PORT = 41144;
11
+ /** What a TSDNS entry has for a port to say: the one the user gave. */
12
+ const PORT_PLACEHOLDER = ":$PORT";
10
13
  const NICKNAME_LOOKUP_URL = "https://named.myteamspeak.com/lookup";
11
14
  const CACHE_TTL_MS = 10 * 60 * 1000;
12
15
  /**
@@ -26,18 +29,74 @@ const TSDNS_TIMEOUT_MS = 3_000;
26
29
  */
27
30
  export const MAX_DISCOVERY_RESPONSE: number = 8 * 1024;
28
31
 
32
+ /** An SRV record, as `resolveSrv` gives it. */
33
+ export interface SrvRecord {
34
+ /** The target host; a trailing dot is allowed. */
35
+ readonly name: string;
36
+ /** The target's port. */
37
+ readonly port: number;
38
+ /** Lower is tried first. */
39
+ readonly priority: number;
40
+ /** Among records of one priority, the higher is tried first more often. */
41
+ readonly weight: number;
42
+ }
43
+
29
44
  /** How the default resolver resolves. */
30
45
  export interface ResolverOptions {
31
46
  /** Look dot-less names up at named.myteamspeak.com; off by default. */
32
47
  readonly nicknameLookup?: boolean | undefined;
48
+ /**
49
+ * The nickname lookup endpoint, an http: or https: URL; the default is
50
+ * `https://named.myteamspeak.com/lookup`. Used only with `nicknameLookup`:
51
+ * setting it does not turn the lookup on. For a test double or a mirror of
52
+ * the service. The name is sent as the `name` query parameter, and the first
53
+ * line of the answer is the address. A URL that is not http: or https: throws
54
+ * a TypeError.
55
+ */
56
+ readonly nicknameService?: string | URL | undefined;
57
+ /**
58
+ * Answers the SRV lookups, for DNS over HTTPS, another DNS server, or a
59
+ * test; the default is the system's `resolveSrv`. It answers every SRV
60
+ * lookup the resolver makes (`_ts3._udp.<host>` and `_tsdns._tcp.<domain>`),
61
+ * called with the full name and the resolution's signal. A rejection or an
62
+ * empty list means no record. One that ignores the signal cannot hold
63
+ * resolution up. The host names in the answers, and the TSDNS servers'
64
+ * names, are still looked up by the system.
65
+ */
66
+ readonly resolveSrv?:
67
+ | ((name: string, options: { readonly signal: AbortSignal }) => Promise<readonly SrvRecord[]>)
68
+ | undefined;
69
+ /**
70
+ * The `fetch` the nickname lookup uses, for a proxy or a test; the default
71
+ * is the global `fetch`. Used only with `nicknameLookup`.
72
+ */
73
+ readonly fetch?: typeof globalThis.fetch | undefined;
33
74
  }
34
75
 
35
- export class Resolver {
76
+ /**
77
+ * The resolver a Client uses unless it is given another. It resolves as the
78
+ * TeamSpeak application does, so on its own it shows where an address leads:
79
+ * `await new Resolver().resolve("ts.example.com")`. Answers are kept for ten
80
+ * minutes.
81
+ */
82
+ export class Resolver implements AddrResolver {
36
83
  readonly #cache = new Map<string, ResolvedAddr[]>();
37
84
  readonly #nicknameLookup: boolean;
85
+ readonly #nicknameService: URL;
86
+ readonly #resolveSrv: NonNullable<ResolverOptions["resolveSrv"]>;
87
+ readonly #fetch: ResolverOptions["fetch"];
38
88
 
39
89
  constructor(options: ResolverOptions = {}) {
90
+ if (options.resolveSrv !== undefined && typeof options.resolveSrv !== "function") {
91
+ throw new TypeError("resolveSrv is a function");
92
+ }
93
+ if (options.fetch !== undefined && typeof options.fetch !== "function") {
94
+ throw new TypeError("fetch is a function");
95
+ }
96
+ this.#resolveSrv = options.resolveSrv ?? ((name) => resolveSrv(name));
97
+ this.#fetch = options.fetch;
40
98
  this.#nicknameLookup = options.nicknameLookup ?? false;
99
+ this.#nicknameService = parseNicknameService(options.nicknameService ?? NICKNAME_LOOKUP_URL);
41
100
  }
42
101
 
43
102
  /**
@@ -81,7 +140,7 @@ export class Resolver {
81
140
  // service can answer: asking it sends the name to a third party, so it is
82
141
  // opt-in. Otherwise the name is resolved like any host.
83
142
  if (nicknames && this.#nicknameLookup && !host.includes(".") && host !== "localhost") {
84
- const nickAddr = await resolveNickname(host, signal);
143
+ const nickAddr = await resolveNickname(host, this.#nicknameService, signal, this.#fetch);
85
144
  signal.throwIfAborted();
86
145
  if (nickAddr) return this.#resolve(nickAddr, signal, false);
87
146
  }
@@ -93,15 +152,21 @@ export class Resolver {
93
152
  const domainList = getDomainList(host);
94
153
 
95
154
  // TSDNS via SRV _tsdns._tcp.<domain>
96
- const tsdnsSrv = await this.#resolveTSDNSSRV(domainList, host, signal);
97
- if (tsdnsSrv) {
98
- return this.#setCache(inputAddr, [{ addr: tsdnsSrv, source: ResolveSource.TSDNSSRV }]);
155
+ const tsdnsSrv = await this.#resolveTSDNSSRV(domainList, host, port, signal);
156
+ if (tsdnsSrv.length > 0) {
157
+ return this.#setCache(
158
+ inputAddr,
159
+ tsdnsSrv.map((addr) => ({ addr, source: ResolveSource.TSDNSSRV })),
160
+ );
99
161
  }
100
162
 
101
163
  // TSDNS direct :41144
102
- const tsdnsDirect = await this.#resolveTSDNSDirect(domainList, host, signal);
103
- if (tsdnsDirect) {
104
- return this.#setCache(inputAddr, [{ addr: tsdnsDirect, source: ResolveSource.TSDNSDirect }]);
164
+ const tsdnsDirect = await this.#resolveTSDNSDirect(domainList, host, port, signal);
165
+ if (tsdnsDirect.length > 0) {
166
+ return this.#setCache(
167
+ inputAddr,
168
+ tsdnsDirect.map((addr) => ({ addr, source: ResolveSource.TSDNSDirect })),
169
+ );
105
170
  }
106
171
 
107
172
  signal.throwIfAborted();
@@ -127,9 +192,17 @@ export class Resolver {
127
192
  return withExpiry;
128
193
  }
129
194
 
130
- async #resolveSRV(host: string, signal?: AbortSignal): Promise<ResolvedAddr[] | null> {
195
+ /** One SRV lookup, however it is answered, ended by the signal. */
196
+ async #srv(name: string, signal: AbortSignal): Promise<readonly SrvRecord[]> {
197
+ return abortable(
198
+ Promise.resolve().then(() => this.#resolveSrv(name, { signal })),
199
+ signal,
200
+ );
201
+ }
202
+
203
+ async #resolveSRV(host: string, signal: AbortSignal): Promise<ResolvedAddr[] | null> {
131
204
  try {
132
- const srvs = await abortable(resolveSrv(`_ts3._udp.${host}`), signal);
205
+ const srvs = await this.#srv(`_ts3._udp.${host}`, signal);
133
206
  if (!srvs || srvs.length === 0) return null;
134
207
  return orderSrvRecords(srvs).map((srv) => ({
135
208
  addr: joinHostPort(srv.name.replace(/\.$/, ""), String(srv.port)),
@@ -143,44 +216,81 @@ export class Resolver {
143
216
  async #resolveTSDNSSRV(
144
217
  domains: string[],
145
218
  queryHost: string,
146
- signal?: AbortSignal,
147
- ): Promise<string | null> {
219
+ port: string,
220
+ signal: AbortSignal,
221
+ ): Promise<string[]> {
148
222
  for (const domain of domains) {
149
223
  try {
150
- const srvs = await abortable(resolveSrv(`_tsdns._tcp.${domain}`), signal);
224
+ const srvs = await this.#srv(`_tsdns._tcp.${domain}`, signal);
151
225
  if (!srvs || srvs.length === 0) continue;
152
226
  for (const srv of orderSrvRecords(srvs)) {
153
227
  const tsdnsAddr = joinHostPort(srv.name.replace(/\.$/, ""), String(srv.port));
154
- const result = await queryTSDNS(tsdnsAddr, queryHost, signal);
155
- if (result) return result;
228
+ const result = await askTSDNS(tsdnsAddr, queryHost, port, signal);
229
+ if (result.length > 0) return result;
156
230
  }
157
231
  } catch {
158
232
  continue;
159
233
  }
160
234
  }
161
- return null;
235
+ return [];
162
236
  }
163
237
 
164
238
  async #resolveTSDNSDirect(
165
239
  domains: string[],
166
240
  queryHost: string,
241
+ port: string,
167
242
  signal?: AbortSignal,
168
- ): Promise<string | null> {
243
+ ): Promise<string[]> {
169
244
  // Asked at once rather than in turn, so the probes cost one timeout in
170
245
  // all; the most specific domain's answer wins.
171
246
  const answers = await Promise.all(
172
247
  domains.map((domain) =>
173
- queryTSDNS(joinHostPort(domain, String(TS_DNS_DEFAULT_PORT)), queryHost, signal),
248
+ askTSDNS(joinHostPort(domain, String(TS_DNS_DEFAULT_PORT)), queryHost, port, signal),
174
249
  ),
175
250
  );
176
- return answers.find((answer) => answer !== null) ?? null;
251
+ return answers.find((answer) => answer.length > 0) ?? [];
177
252
  }
178
253
  }
179
254
 
255
+ /**
256
+ * Ask one TSDNS server; a server that fails to answer is one with no answer,
257
+ * so it never fails the resolution.
258
+ */
259
+ async function askTSDNS(
260
+ server: string,
261
+ host: string,
262
+ port: string,
263
+ signal?: AbortSignal,
264
+ ): Promise<string[]> {
265
+ try {
266
+ return normalizeTSDNSEntries(await lookupTSDNS(server, host, { signal }), port);
267
+ } catch {
268
+ return [];
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Make each entry of a TSDNS answer a `host:port`, as TeamSpeak reads it: an
274
+ * entry without a port means 9987, and the port `$PORT` means `port`, the one
275
+ * the address being resolved had.
276
+ *
277
+ * @internal
278
+ */
279
+ export function normalizeTSDNSEntries(entries: readonly string[], port: string): string[] {
280
+ return entries.map((entry) => {
281
+ if (entry.endsWith(PORT_PLACEHOLDER)) {
282
+ const host = splitHostPort(entry.slice(0, -PORT_PLACEHOLDER.length)).host;
283
+ return joinHostPort(host, port);
284
+ }
285
+ const target = splitHostPort(entry, "9987");
286
+ return joinHostPort(target.host, target.port);
287
+ });
288
+ }
289
+
180
290
  // ---- Helpers ----------------------------------------------------------------
181
291
 
182
292
  /** The part of an SRV record that decides the order they are tried in. */
183
- interface SrvRecord {
293
+ interface SrvOrder {
184
294
  readonly priority: number;
185
295
  readonly weight: number;
186
296
  }
@@ -195,7 +305,7 @@ interface SrvRecord {
195
305
  * @param random Returns a number in [0, 1); injected to make the order testable.
196
306
  * @internal
197
307
  */
198
- export function orderSrvRecords<T extends SrvRecord>(
308
+ export function orderSrvRecords<T extends SrvOrder>(
199
309
  records: readonly T[],
200
310
  random: () => number = Math.random,
201
311
  ): T[] {
@@ -231,16 +341,31 @@ export function orderSrvRecords<T extends SrvRecord>(
231
341
  return ordered;
232
342
  }
233
343
 
344
+ function parseNicknameService(service: string | URL): URL {
345
+ let url: URL;
346
+ try {
347
+ url = new URL(service);
348
+ } catch {
349
+ throw new TypeError(`nicknameService is not a URL: ${String(service)}`);
350
+ }
351
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
352
+ throw new TypeError(`nicknameService is an http: or https: URL, not ${url.protocol}`);
353
+ }
354
+ return url;
355
+ }
356
+
234
357
  /** @internal Ask the nickname service for `nickname`'s address. */
235
358
  export async function resolveNickname(
236
359
  nickname: string,
360
+ service: URL,
237
361
  signal?: AbortSignal,
362
+ fetchImpl?: typeof globalThis.fetch,
238
363
  ): Promise<string | null> {
239
364
  try {
240
- const url = new URL(NICKNAME_LOOKUP_URL);
365
+ const url = new URL(service);
241
366
  url.searchParams.set("name", nickname);
242
367
  const init: RequestInit = signal ? { signal } : {};
243
- const resp = await fetch(url.toString(), init);
368
+ const resp = await (fetchImpl ?? globalThis.fetch)(url.toString(), init);
244
369
  if (!resp.ok) return null;
245
370
  const text = await readLimited(resp, MAX_DISCOVERY_RESPONSE);
246
371
  const line = text.split("\n")[0]?.trim();
@@ -270,54 +395,82 @@ async function readLimited(resp: Response, limit: number): Promise<string> {
270
395
  return Buffer.concat(chunks).toString("utf8");
271
396
  }
272
397
 
273
- /** @internal Ask the TSDNS server at `tsdnsAddr` for `queryHost`'s address. */
274
- export function queryTSDNS(
275
- tsdnsAddr: string,
276
- queryHost: string,
277
- signal?: AbortSignal,
278
- ): Promise<string | null> {
279
- const [host, portStr] = splitHostPortParts(tsdnsAddr);
280
- const port = parseInt(portStr, 10);
281
-
282
- return new Promise((resolve) => {
283
- if (signal?.aborted) return resolve(null);
284
- const finish = (answer: string | null): void => {
398
+ /**
399
+ * Ask a TSDNS server where `host` is. The answer is the server's targets in
400
+ * its order and as it sent them: an entry may have no port, which TeamSpeak
401
+ * takes as 9987, or the literal port `$PORT`, the port the user gave (9987 by
402
+ * default). {@link Resolver} applies both. A name the server has no record for
403
+ * gives an empty list.
404
+ *
405
+ * The answer ends at its first line break or when the server closes the
406
+ * connection, whichever comes first. It rejects when the connection fails,
407
+ * when no answer completes within three seconds or grows past 8 KiB, and with
408
+ * the signal's reason when it aborts.
409
+ *
410
+ * @param server - The TSDNS server, `host` or `host:port`; the port defaults to 41144.
411
+ * @param host - The name to look up; it is sent without a port.
412
+ */
413
+ export function lookupTSDNS(
414
+ server: string,
415
+ host: string,
416
+ options: { readonly signal?: AbortSignal | undefined } = {},
417
+ ): Promise<string[]> {
418
+ const { signal } = options;
419
+ const target = splitHostPort(server, String(TS_DNS_DEFAULT_PORT));
420
+ const port = parseInt(target.port, 10);
421
+
422
+ return new Promise((resolve, reject) => {
423
+ if (signal?.aborted) return reject(signal.reason);
424
+ const chunks: Buffer[] = [];
425
+ let size = 0;
426
+ let done = false;
427
+ const finish = (error: unknown, answer?: string[]): void => {
428
+ if (done) return;
429
+ done = true;
285
430
  clearTimeout(timeout);
286
431
  signal?.removeEventListener("abort", onAbort);
287
432
  socket.destroy();
288
- resolve(answer);
433
+ if (error !== undefined) reject(error);
434
+ else resolve(answer ?? []);
435
+ };
436
+ const answerOf = (bytes: Buffer): string[] => {
437
+ const line = bytes.toString("utf8").trim();
438
+ return !line || line === "404" || line === "errors" ? [] : line.split(/\s+/);
289
439
  };
290
- const onAbort = (): void => finish(null);
291
- const timeout = setTimeout(() => finish(null), TSDNS_TIMEOUT_MS);
440
+ const timedOut = (): void =>
441
+ finish(new TeamspeakError(`the TSDNS server ${server} did not answer in time`));
442
+ const onAbort = (): void => finish(signal?.reason);
443
+ const timeout = setTimeout(timedOut, TSDNS_TIMEOUT_MS);
292
444
 
293
- const socket = createConnection({ host, port, timeout: 2_000 }, () => {
294
- socket.write(`${queryHost}\n`);
445
+ const socket = createConnection({ host: target.host, port, timeout: 2_000 }, () => {
446
+ socket.write(`${host}\n`);
295
447
  });
296
448
 
297
- let buf = "";
298
449
  socket.on("data", (chunk: Buffer) => {
299
- buf += chunk.toString("latin1");
300
- const idx = buf.indexOf("\n");
450
+ chunks.push(chunk);
451
+ size += chunk.length;
452
+ const all = chunks.length === 1 ? chunks[0]! : Buffer.concat(chunks);
453
+ chunks.length = 0;
454
+ chunks.push(all);
455
+ const idx = all.indexOf(0x0a);
301
456
  if (idx >= 0 && idx < MAX_DISCOVERY_RESPONSE) {
302
- const line = Buffer.from(buf.slice(0, idx), "latin1").toString("utf8").trim();
303
- finish(!line || line === "404" || line === "errors" ? null : line);
304
- } else if (buf.length >= MAX_DISCOVERY_RESPONSE) {
457
+ finish(undefined, answerOf(all.subarray(0, idx)));
458
+ } else if (size >= MAX_DISCOVERY_RESPONSE) {
305
459
  // No answer is that long.
306
- finish(null);
460
+ finish(
461
+ new TeamspeakError(
462
+ `the TSDNS server ${server} sent ${MAX_DISCOVERY_RESPONSE} bytes without ending its answer`,
463
+ ),
464
+ );
307
465
  }
308
466
  });
309
-
310
- socket.on("error", () => finish(null));
311
- socket.on("timeout", () => finish(null));
467
+ socket.on("end", () => finish(undefined, answerOf(Buffer.concat(chunks))));
468
+ socket.on("error", (error) => finish(error));
469
+ socket.on("timeout", timedOut);
312
470
  signal?.addEventListener("abort", onAbort, { once: true });
313
471
  });
314
472
  }
315
473
 
316
- function splitHostPortParts(addr: string): [string, string] {
317
- const { host, port } = splitHostPort(addr);
318
- return [host, port];
319
- }
320
-
321
474
  function getDomainList(host: string): string[] {
322
475
  const parts = host.split(".");
323
476
  const list: string[] = [];
@@ -12,6 +12,9 @@
12
12
  export { Client } from "./client.js";
13
13
  export { EventName, GroupWhisperType, GroupWhisperScope, ResolveSource } from "./types.js";
14
14
 
15
+ export { Resolver, lookupTSDNS } from "./discovery/resolver.js";
16
+ export type { ResolverOptions, SrvRecord } from "./discovery/resolver.js";
17
+
15
18
  export { ViewStatus } from "./view.js";
16
19
  export type { ServerView, ViewChangedEvent, ViewReadyEvent } from "./view.js";
17
20