UNPKG

sterfive-bonjour-service

Version:

A Bonjour/Zeroconf implementation in TypeScript

182 lines (158 loc) 4.52 kB
/** * Bonjour Service - Service Definition */ import { NetworkInterfaceInfo, networkInterfaces, hostname } from 'os' import { EventEmitter } from 'events' import * as mDNS from 'multicast-dns' import { toString as ServiceToString } from './service-types' import DnsTxt from './dns-txt' const TLD: string = '.local' export interface ServiceConfig extends Omit<mDNS.Options,"type"> { name: string type?: string port: number protocol?: 'tcp' | 'udp' host?: string fqdn?: string subtypes?: Array<string> txt?: Record<string, string> probe?: boolean } export interface ServiceRecord { name: string type: 'PTR' | 'SRV' | 'TXT' | 'A' | 'AAAA' ttl: number data: { [key: string]: any } | string | any } export interface ServiceReferer { address: string family: 'IPv4' | 'IPv6' port: number size: number } /** * Provide PTR record * @param service * @returns */ function RecordPTR(service: Service): ServiceRecord { return { name: `${service.type}${TLD}`, type: 'PTR', ttl: 28800, data: service.fqdn } } /** * Provide SRV record * @param service * @returns */ function RecordSRV(service: Service): ServiceRecord { return { name: service.fqdn, type: 'SRV', ttl: 120, data: { port: service.port, target: service.host } } } /** * Provide TXT record * @param service * @returns */ function RecordTXT(service: Service): ServiceRecord { const txtService = new DnsTxt() return { name: service.fqdn, type: 'TXT', ttl: 4500, data: txtService.encode(service.txt) } } /** * Provide A record * @param service * @param ip * @returns */ function RecordA(service: Service, ip: string): ServiceRecord { return { name: service.host, type: 'A', ttl: 120, data: ip } } /** * Provide AAAA record * @param service * @param ip * @returns */ function RecordAAAA(service: Service, ip: string): ServiceRecord { return { name: service.host, type: 'AAAA', ttl: 120, data: ip } } export class Service extends EventEmitter { public name: string public type: string public protocol: 'tcp' | 'udp' public port: number public host: string public fqdn: string public txt?: Record<string, string> public subtypes?: Array<string> public addresses?: Array<string> public referer?: ServiceReferer public probe: boolean = true public published: boolean = false public activated: boolean = false public destroyed: boolean = false public _broadCastTimeout: NodeJS.Timer | undefined public start?: any public stop?: any constructor(config: ServiceConfig) { super() if (!config.name) throw new Error('Required name not given') if (!config.type) throw new Error('Required type not given') if (!config.port) throw new Error('Required port not given') this.name = config.name this.protocol = config.protocol || 'tcp' this.type = ServiceToString({ name: config.type, protocol: this.protocol }) this.port = config.port this.host = config.host || hostname() this.fqdn = `${this.name}.${this.type}${TLD}` this.txt = config.txt this.subtypes = config.subtypes } public records(): ServiceRecord[] { const records: ServiceRecord[] = [RecordPTR(this), RecordSRV(this), RecordTXT(this)] // Create record per interface address const ifaces: Array<NetworkInterfaceInfo[]> = Object.values(networkInterfaces()) as unknown as NetworkInterfaceInfo[][] for (let iface of ifaces) { let addrs = iface for (let addr of addrs) { if (addr.internal || addr.mac === '00:00:00:00:00:00') continue switch (addr.family) { case 'IPv4': records.push(RecordA(this, addr.address)) break case 'IPv6': records.push(RecordAAAA(this, addr.address)) break } } } // Return all records return records } } export default Service