From 4dbbde7d083d1c3d23b6ec2f1781911ca0a6b385 Mon Sep 17 00:00:00 2001 From: Amy Yan Date: Thu, 12 Oct 2023 19:18:29 +1100 Subject: [PATCH] feat: added ServiceDomain and FDQN to Hostname string utility functions --- src/MDNS.ts | 40 ++++++++++++++++++++++++---------------- src/types.ts | 9 ++++++++- src/utils.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 17 deletions(-) diff --git a/src/MDNS.ts b/src/MDNS.ts index 0b1ee5f..e1a2cdd 100644 --- a/src/MDNS.ts +++ b/src/MDNS.ts @@ -8,6 +8,7 @@ import type { MulticastSocketInfo, SocketHostRow, RemoteInfo, + FDQN, } from './types'; import type { CachableResourceRecord, @@ -57,10 +58,10 @@ class MDNS { protected localRecordCache: ResourceRecordCache; protected localRecordCacheDirty = true; - protected _localServices: Map = new Map(); + protected _localServices: Map = new Map(); protected networkRecordCache: ResourceRecordCache; - protected _networkServices: Map = new Map(); + protected _networkServices: Map = new Map(); protected sockets: Array = []; protected socketMap: WeakMap = new WeakMap(); protected socketHostTable: Table = new Table( @@ -140,7 +141,7 @@ class MDNS { * The Key is a FDQN. */ @ready(new errors.ErrorMDNSNotRunning()) - public get localServices(): ReadonlyMap { + public get localServices(): ReadonlyMap { return this._localServices; } @@ -149,7 +150,7 @@ class MDNS { * The Key is a FDQN. */ @ready(new errors.ErrorMDNSNotRunning()) - public get networkServices(): ReadonlyMap { + public get networkServices(): ReadonlyMap { return this._networkServices; } @@ -1003,22 +1004,22 @@ class MDNS { protected extractRelatedFdqns( resourceRecords: ResourceRecord | Array, - ): Array { + ): Array { if (!Array.isArray(resourceRecords)) { return this.extractRelatedFdqns([resourceRecords]); } - const relatedFdqns: Array = []; + const relatedFdqns: Array = []; for (const resourceRecord of resourceRecords) { if ( resourceRecord.type === RType.SRV || resourceRecord.type === RType.TXT ) { - relatedFdqns.push(resourceRecord.name as Hostname); + relatedFdqns.push(resourceRecord.name as FDQN); } else if ( resourceRecord.type === RType.PTR && resourceRecord.name !== '_services._dns-sd._udp.local' ) { - relatedFdqns.push(resourceRecord.data as Hostname); + relatedFdqns.push(resourceRecord.data as FDQN); } else if ( resourceRecord.type === RType.A || resourceRecord.type === RType.AAAA @@ -1029,7 +1030,7 @@ class MDNS { ); for (const relatedResourceRecord of relatedResourceRecords) { if (relatedResourceRecord.type === RType.SRV) { - relatedFdqns.push(relatedResourceRecord.name as Hostname); + relatedFdqns.push(relatedResourceRecord.name as FDQN); } } } @@ -1160,9 +1161,7 @@ class MDNS { hostname: this._hostname, hosts: [], }; - const serviceDomain = - `_${service.type}._${service.protocol}.local` as Hostname; - const fdqn = `${service.name}.${serviceDomain}` as Hostname; + const fdqn = utils.toFdqn(service); this._localServices.set(fdqn, service); this.localRecordCacheDirty = true; @@ -1200,8 +1199,11 @@ class MDNS { type: string; protocol: 'udp' | 'tcp'; }) { - const serviceDomain = `_${type}._${protocol}.local` as Hostname; - const fdqn = `${name}.${serviceDomain}` as Hostname; + const fdqn = utils.toFdqn({ + name, + type, + protocol, + }); const foundService = this._localServices.get(fdqn); if (foundService == null) return; @@ -1248,7 +1250,10 @@ class MDNS { minDelay?: number; maxDelay?: number; }) { - const serviceDomain = `_${type}._${protocol}.local` as Hostname; + const serviceDomain = utils.toServiceDomain({ + type, + protocol, + }); const questionRecord: QuestionRecord = { name: serviceDomain, type: QType.PTR, @@ -1329,7 +1334,10 @@ class MDNS { type: string; protocol: 'udp' | 'tcp'; }) { - const serviceDomain = `_${type}._${protocol}.local`; + const serviceDomain = utils.toServiceDomain({ + type, + protocol, + }); this.queries.get(serviceDomain)?.cancel(); } } diff --git a/src/types.ts b/src/types.ts index 6f2c18f..6c112f2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -32,7 +32,13 @@ type Host = Opaque<'Host', string>; /** * Hostnames are resolved to IP addresses */ -type Hostname = Opaque<'Hostname', string>; +type Hostname = Opaque<'Hostname', string> | FDQN; + +/** + * FDQNs are in the format `{service.name}._${service.type}._${service.protocol}.local`. + * FDQNs are also Hostnames. + */ +type FDQN = Opaque<'FDQN', string>; /** * Ports are numbers from 0 to 65535 @@ -122,6 +128,7 @@ export type { PromiseDeconstructed, Host, Hostname, + FDQN, Port, Address, Service, diff --git a/src/utils.ts b/src/utils.ts index 8039972..7f3cd25 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,6 @@ import type { Callback, + FDQN, Host, Hostname, NetworkInterfaces, @@ -239,6 +240,47 @@ function getRandomPacketId(): number { return Math.floor(Math.random() * 65535); } +function toServiceDomain({ + type, + protocol, +}: { + type: string; + protocol: 'udp' | 'tcp'; +}): Hostname { + return `_${type}._${protocol}.local` as Hostname; +} + +function toFdqn({ + name, + type, + protocol, + serviceDomain, +}: { + name: string; +} & ( + | { + type: string; + protocol: 'udp' | 'tcp'; + serviceDomain?: undefined; + } + | { + type?: undefined; + protocol?: undefined; + serviceDomain: Hostname; + } +)): FDQN { + let serviceDomain_: Hostname | undefined; + if (serviceDomain == null) { + serviceDomain_ = toServiceDomain({ + type, + protocol, + }); + } else { + serviceDomain_ = serviceDomain; + } + return `${name}.${serviceDomain_}` as FDQN; +} + export { isPort, isIPv4, @@ -254,4 +296,6 @@ export { toServiceResourceRecords, bindSocket, getRandomPacketId, + toServiceDomain, + toFdqn, };