@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
JavaScript
;
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