UNPKG

@homebridge/ciao

Version:

ciao is a RFC 6763 compliant dns-sd library, advertising on multicast dns (RFC 6762) implemented in plain Typescript/JavaScript

225 lines 9.17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseFQDN = parseFQDN; exports.stringify = stringify; exports.formatHostname = formatHostname; exports.removeTLD = removeTLD; exports.formatMappedIPv4Address = formatMappedIPv4Address; exports.enlargeIPv6 = enlargeIPv6; exports.shortenIPv6 = shortenIPv6; exports.formatReverseAddressPTRName = formatReverseAddressPTRName; exports.ipAddressFromReversAddressName = ipAddressFromReversAddressName; exports.getNetAddress = getNetAddress; const tslib_1 = require("tslib"); const assert_1 = tslib_1.__importDefault(require("assert")); const net_1 = tslib_1.__importDefault(require("net")); const v4mapped_1 = require("./v4mapped"); function isProtocol(part) { return part === "_" + "tcp" /* Protocol.TCP */ || part === "_" + "udp" /* Protocol.UDP */; } function isSub(part) { return part === "_sub"; } function removePrefixedUnderscore(part) { return part.startsWith("_") ? part.slice(1) : part; } function isSubTypePTRParts(parts) { return "subtype" in parts; } function parseFQDN(fqdn) { const parts = fqdn.split("."); (0, assert_1.default)(parts.length >= 3, "Received illegal fqdn: " + fqdn); let i = parts.length - 1; let domain = ""; while (!isProtocol(parts[i])) { domain = removePrefixedUnderscore(parts[i]) + (domain ? "." + domain : ""); i--; } (0, assert_1.default)(i >= 1, "Failed to parse illegal fqdn: " + fqdn); const protocol = removePrefixedUnderscore(parts[i--]); const type = removePrefixedUnderscore(parts[i--]); if (i < 0) { return { domain: domain, protocol: protocol, type: type, }; } else if (isSub(parts[i])) { i--; // skip "_sub"; (0, assert_1.default)(i === 0, "Received illegal formatted sub type fqdn: " + fqdn); const subtype = removePrefixedUnderscore(parts[i]); return { domain: domain, protocol: protocol, type: type, subtype: subtype, }; } else { // the name can contain dots as of RFC 6763 4.1.1. const name = removePrefixedUnderscore(parts.slice(0, i + 1).join(".")); return { domain: domain, protocol: protocol, type: type, name: name, }; } } function stringify(parts) { (0, assert_1.default)(parts.type, "type cannot be undefined"); (0, assert_1.default)(parts.type.length <= 15, "type must not be longer than 15 characters"); let prefix; if (isSubTypePTRParts(parts)) { prefix = `_${parts.subtype}._sub.`; } else { prefix = parts.name ? `${parts.name}.` : ""; } return `${prefix}_${parts.type}._${parts.protocol || "tcp" /* Protocol.TCP */}.${parts.domain || "local"}.`; } function formatHostname(hostname, domain = "local") { (0, assert_1.default)(!hostname.endsWith("."), "hostname must not end with the root label!"); const tld = "." + domain; return (!hostname.endsWith(tld) ? hostname + tld : hostname) + "."; } function removeTLD(hostname) { if (hostname.endsWith(".")) { // check for the DNS root label hostname = hostname.substring(0, hostname.length - 1); } const lastDot = hostname.lastIndexOf("."); return hostname.slice(0, lastDot); } function formatMappedIPv4Address(address) { (0, assert_1.default)(net_1.default.isIPv4(address), "Illegal argument. Must be an IPv4 address!"); // Convert IPv4 address to its hexadecimal representation const hexParts = address.split(".").map(part => parseInt(part).toString(16).padStart(2, "0")); const ipv6Part = `::ffff:${hexParts.join("")}`; // Convert the hexadecimal representation to the standard IPv6 format return ipv6Part.replace(/(.{4})(.{4})$/, "$1:$2"); } function enlargeIPv6(address) { (0, assert_1.default)(net_1.default.isIPv6(address), "Illegal argument. Must be ipv6 address!"); const parts = address.split("::"); // Initialize head and tail arrays const head = parts[0] ? parts[0].split(":") : []; const tail = parts[1] ? parts[1].split(":") : []; // Calculate the number of groups to fill in with "0000" when we expand const fill = new Array(8 - head.length - tail.length).fill("0000"); // Combine it all and normalize each hextet to be 4 characters long return [...head, ...fill, ...tail].map(hextet => hextet.padStart(4, "0")).join(":"); } function shortenIPv6(address) { if (typeof address === "string") { address = address.split(":"); } for (let i = 0; i < address.length; i++) { const part = address[i]; let j = 0; for (; j < Math.min(3, part.length - 1); j++) { // search for the first index which is non-zero, but leaving at least one zero if (part.charAt(j) !== "0") { break; } } address[i] = part.slice(j); } let longestBlockOfZerosIndex = -1; let longestBlockOfZerosLength = 0; for (let i = 0; i < address.length; i++) { // this is not very optimized, but it works if (address[i] !== "0") { continue; } let zerosCount = 1; let j = i + 1; for (; j < address.length; j++) { if (address[j] === "0") { zerosCount++; } else { break; } } if (zerosCount > longestBlockOfZerosLength) { longestBlockOfZerosIndex = i; longestBlockOfZerosLength = zerosCount; } i = j; // skip all the zeros we already checked + the one after that, we know that's not a zero } if (longestBlockOfZerosIndex !== -1) { const startOrEnd = longestBlockOfZerosIndex === 0 || (longestBlockOfZerosIndex + longestBlockOfZerosLength === 8); address[longestBlockOfZerosIndex] = startOrEnd ? ":" : ""; if (longestBlockOfZerosLength > 1) { address.splice(longestBlockOfZerosIndex + 1, longestBlockOfZerosLength - 1); } } const result = address.join(":"); if (result === ":") { // special case for the unspecified address return "::"; } return result; } function formatReverseAddressPTRName(address) { if (net_1.default.isIPv4(address)) { const split = address.split(".").reverse(); return split.join(".") + ".in-addr.arpa"; } if (!net_1.default.isIPv6(address)) { throw new Error("Supplied illegal ip address format: " + address); } if ((0, v4mapped_1.isIPv4Mapped)(address)) { return (0, v4mapped_1.getIPFromV4Mapped)(address).split(".").reverse().join(".") + ".in-addr.arpa"; } address = enlargeIPv6(address).toUpperCase(); const nibbleSplit = address.replace(/:/g, "").split("").reverse(); (0, assert_1.default)(nibbleSplit.length === 32, "Encountered invalid ipv6 address length! " + nibbleSplit.length); return nibbleSplit.join(".") + ".ip6.arpa"; } function ipAddressFromReversAddressName(name) { name = name.toLowerCase(); if (name.endsWith(".in-addr.arpa")) { const split = name.replace(".in-addr.arpa", "").split(".").reverse(); return split.join("."); } else if (name.endsWith(".ip6.arpa")) { const split = name.replace(".ip6.arpa", "").split(".").reverse(); (0, assert_1.default)(split.length === 32, "Encountered illegal length for .ip6.arpa split!"); const parts = []; for (let i = 0; i < split.length; i += 4) { parts.push(split.slice(i, i + 4).join("")); } return shortenIPv6(parts.join(":")); } else { throw new Error("Supplied unknown reverse address name format: " + name); } } function getNetAddress(address, netmask) { (0, assert_1.default)(net_1.default.isIP(address) === net_1.default.isIP(netmask), "IP address version must match. Netmask cannot have a version different from the address!"); if (net_1.default.isIPv4(address)) { const addressParts = address.split("."); const netmaskParts = netmask.split("."); const netAddressParts = new Array(4); for (let i = 0; i < addressParts.length; i++) { const addressNum = parseInt(addressParts[i]); const netmaskNum = parseInt(netmaskParts[i]); netAddressParts[i] = (addressNum & netmaskNum).toString(); } return netAddressParts.join("."); } else if (net_1.default.isIPv6(address)) { const addressParts = enlargeIPv6(address).split(":"); const netmaskParts = enlargeIPv6(netmask).split(":"); const netAddressParts = new Array(8); for (let i = 0; i < addressParts.length; i++) { const addressNum = parseInt(addressParts[i], 16); const netmaskNum = parseInt(netmaskParts[i], 16); netAddressParts[i] = (addressNum & netmaskNum).toString(16); } return shortenIPv6(enlargeIPv6(netAddressParts.join(":"))); } else { throw new Error("Illegal argument. Address is not an ip address!"); } } //# sourceMappingURL=domain-formatter.js.map