UNPKG

@nictool/dns-resource-record

Version:
230 lines (195 loc) 5.92 kB
import RR from '../rr.js' import * as TINYDNS from '../lib/tinydns.js' const REF = { // RFC 1876 LATLON: 2 ** 31, // LAT equator, LON prime meridian ALTITUDE: 100000 * 100, // reference spheroid used by GPS, in cm } const CONV = { sec: 1000, min: 60 * 1000, deg: 60 * 60 * 1000, } export default class LOC extends RR { constructor(opts) { super(opts) } /****** Resource record specific setters *******/ setAddress(val) { if (!val) this.throwHelp('LOC: address is required') /* ... LOC ( d1 [m1 [s1]] {"N"|"S"} d2 [m2 [s2]] {"E"|"W"} alt["m"] [siz["m"] [hp["m"] [vp["m"]]]] ) */ this.parseLoc(val) this.set('address', val) } getDescription() { return 'Location' } getRdataFields(arg) { return ['address'] } getRFCs() { return [1876] } getTypeId() { return 29 } parseLoc(string) { // d1 [m1 [s1]] const dms = '(\\d+)\\s+(?:(\\d+)\\s+)?(?:([\\d.]+)\\s+)?' // alt["m"] [siz["m"] [hp["m"] [vp["m"]]]] const alt = '(-?[\\d.]+)m?(?:\\s+([\\d.]+)m?)?(?:\\s+([\\d.]+)m?)?(?:\\s+([\\d.]+)m?)?' // put them all together const locRe = new RegExp(`^${dms}(N|S)\\s+${dms}(E|W)\\s+${alt}`, 'i') const r = string.match(locRe) if (!r) this.throwHelp('LOC address: invalid format, see RFC 1876') const loc = { latitude: { degrees: r[1], minutes: r[2], seconds: r[3], hemisphere: r[4].toUpperCase(), }, longitude: { degrees: r[5], minutes: r[6], seconds: r[7], hemisphere: r[8].toUpperCase(), }, altitude: r[9] * 100, // m -> cm size: r[10] * 100, precision: { horizontal: r[11] * 100, vertical: r[12] * 100, }, } return loc } /****** IMPORTERS *******/ fromTinydns(opts) { // LOC via generic, :fqdn:n:rdata:ttl:timestamp:lo const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':') if (n != 29) this.throwHelp('LOC fromTinydns, invalid n') // divide by 100 is to convert cm to meters const l = { version: TINYDNS.octalToUInt8(rdata.substring(0, 4)), size: this.fromExponent(TINYDNS.octalToUInt8(rdata.substring(4, 8))), precision: { horizontal: this.fromExponent( TINYDNS.octalToUInt8(rdata.substring(8, 12)), ), vertical: this.fromExponent( TINYDNS.octalToUInt8(rdata.substring(12, 16)), ), }, latitude: this.arcSecToDMS( TINYDNS.octalToUInt32(rdata.substring(16, 32)), 'lat', ), longitude: this.arcSecToDMS( TINYDNS.octalToUInt32(rdata.substring(32, 48)), 'lon', ), altitude: TINYDNS.octalToUInt32(rdata.substring(48, 64)) - REF.ALTITUDE, } return new LOC({ type: 'LOC', owner: this.fullyQualify(fqdn), address: this.toHuman(l), ttl: parseInt(ttl, 10), timestamp: ts, location: loc !== '' && loc !== '\n' ? loc : '', }) } fromBind(opts) { const [owner, ttl, c, type] = opts.bindline.split(/\s+/) return new LOC({ owner, ttl: parseInt(ttl, 10), class: c, type: type, address: opts.bindline.split(/\s+/).slice(4).join(' ').trim(), }) } dmsToArcSec(obj) { let retval = obj.degrees * CONV.deg + (obj.minutes || 0) * CONV.min + (obj.seconds || 0) * CONV.sec switch (obj.hemisphere.toUpperCase()) { case 'W': case 'S': retval = -retval break } retval += REF.LATLON return retval } arcSecToDMS(rawmsec, latlon) { let msec = Math.abs(rawmsec - REF.LATLON) // console.log(`rawmsec: ${rawmsec}, abs msec: ${msec}`) const deg = Math.floor(msec / CONV.deg) msec -= deg * CONV.deg const min = Math.floor(msec / CONV.min) msec -= min * CONV.min const sec = Math.floor(msec / CONV.sec) msec -= sec * CONV.sec let hem switch (latlon) { case 'lat': hem = rawmsec >= REF.LATLON ? 'N' : 'S' break case 'lon': hem = rawmsec >= REF.LATLON ? 'E' : 'W' break default: this.throwHelp('unknown or missing hemisphere') } return `${deg} ${min} ${sec}${msec ? '.' + msec : ''} ${hem}` } fromExponent(prec) { const mantissa = ((prec >> 4) & 0x0f) % 10 const exponent = ((prec >> 0) & 0x0f) % 10 return mantissa * Math.pow(10, exponent) } toExponent(val) { /* RFC 1876, ... expressed as a pair of four-bit unsigned integers, each ranging from zero to nine, with the most significant four bits representing the base and the second number representing the power of ten by which to multiply the base. */ let exponent = 0 while (val >= 10) { val /= 10 ++exponent } return (parseInt(val) << 4) | (exponent & 0x0f) } toHuman(obj) { let r = `${obj.latitude} ${obj.longitude} ${obj.altitude / 100}m` if (obj.size) r += ` ${obj.size / 100}m` if (obj.precision.horizontal) r += ` ${obj.precision.horizontal / 100}m` if (obj.precision.vertical) r += ` ${obj.precision.vertical / 100}m` return r } /****** EXPORTERS *******/ toTinydns() { const loc = this.parseLoc(this.get('address')) // LOC format declares in meters, tinydns uses cm (hence * 100) let rdata = '' rdata += TINYDNS.UInt8toOctal(0) // version rdata += TINYDNS.UInt8toOctal(this.toExponent(loc.size)) rdata += TINYDNS.UInt8toOctal(this.toExponent(loc.precision.horizontal)) rdata += TINYDNS.UInt8toOctal(this.toExponent(loc.precision.vertical)) rdata += TINYDNS.UInt32toOctal(this.dmsToArcSec(loc.latitude)) rdata += TINYDNS.UInt32toOctal(this.dmsToArcSec(loc.longitude)) rdata += TINYDNS.UInt32toOctal(loc.altitude + REF.ALTITUDE) return this.getTinydnsGeneric(rdata) } }