2023-11-12 12:14:43 +01:00
|
|
|
import dns from 'node:dns'
|
2023-11-19 20:02:55 +01:00
|
|
|
import { isIP } from 'node:net'
|
2023-11-12 12:14:43 +01:00
|
|
|
import punycode from 'punycode/punycode.js'
|
|
|
|
|
|
|
|
export default class DnsResolver {
|
|
|
|
/**
|
|
|
|
* @param {Logger} logger
|
|
|
|
*/
|
|
|
|
constructor (logger) {
|
|
|
|
this.logger = logger
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-11-19 20:02:55 +01:00
|
|
|
* Resolve a host name to its IP, if the given host name is already
|
|
|
|
* an IP address no request is made.
|
|
|
|
*
|
|
|
|
* If a srvRecordPrefix is provided a SRV request will be made and the
|
|
|
|
* port returned will be included in the output.
|
2023-11-12 12:14:43 +01:00
|
|
|
* @param {string} host
|
|
|
|
* @param {number} ipFamily
|
|
|
|
* @param {string=} srvRecordPrefix
|
|
|
|
* @returns {Promise<{address:string, port:number=}>}
|
|
|
|
*/
|
|
|
|
async resolve (host, ipFamily, srvRecordPrefix) {
|
|
|
|
this.logger.debug('DNS Lookup: ' + host)
|
|
|
|
|
2023-11-19 20:02:55 +01:00
|
|
|
// Check if host is IPv4 or IPv6
|
|
|
|
if (isIP(host) === 4 || isIP(host) === 6) {
|
2023-11-12 12:14:43 +01:00
|
|
|
this.logger.debug('Raw IP Address: ' + host)
|
|
|
|
return { address: host }
|
|
|
|
}
|
|
|
|
|
|
|
|
const asciiForm = punycode.toASCII(host)
|
|
|
|
if (asciiForm !== host) {
|
|
|
|
this.logger.debug('Encoded punycode: ' + host + ' -> ' + asciiForm)
|
|
|
|
host = asciiForm
|
|
|
|
}
|
|
|
|
|
|
|
|
if (srvRecordPrefix) {
|
|
|
|
this.logger.debug('SRV Resolve: ' + srvRecordPrefix + '.' + host)
|
|
|
|
let records
|
|
|
|
try {
|
|
|
|
records = await dns.promises.resolve(srvRecordPrefix + '.' + host, 'SRV')
|
|
|
|
if (records.length >= 1) {
|
|
|
|
this.logger.debug('Found SRV Records: ', records)
|
|
|
|
const record = records[0]
|
|
|
|
const srvPort = record.port
|
|
|
|
const srvHost = record.name
|
|
|
|
if (srvHost === host) {
|
|
|
|
throw new Error('Loop in DNS SRV records')
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
port: srvPort,
|
|
|
|
...await this.resolve(srvHost, ipFamily, srvRecordPrefix)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.logger.debug('No SRV Record')
|
|
|
|
} catch (e) {
|
|
|
|
this.logger.debug(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.logger.debug('Standard Resolve: ' + host)
|
|
|
|
const dnsResult = await dns.promises.lookup(host, ipFamily)
|
|
|
|
// For some reason, this sometimes returns a string address rather than an object.
|
|
|
|
// I haven't been able to reproduce, but it's been reported on the issue tracker.
|
|
|
|
let address
|
|
|
|
if (typeof dnsResult === 'string') {
|
|
|
|
address = dnsResult
|
|
|
|
} else {
|
|
|
|
address = dnsResult.address
|
|
|
|
}
|
|
|
|
this.logger.debug('Found address: ' + address)
|
|
|
|
return { address }
|
|
|
|
}
|
|
|
|
}
|