@xmpp/resolve
Version:
XMPP connection methods resolution for JavaScript
155 lines (142 loc) • 3.47 kB
JavaScript
/* eslint-disable promise/no-nesting */
;
const dns = require("dns");
// eslint-disable-next-line unicorn/prefer-set-has
const IGNORE_CODES = ["ENOTFOUND", "ENODATA"];
function lookup(domain, options = {}) {
options.all = true;
return new Promise((resolve, reject) => {
dns.lookup(domain, options, (err, addresses) => {
if (err) {
return reject(err);
}
const result = [];
for (const { family, address } of addresses) {
const uri = `://${family === 4 ? address : "[" + address + "]"}:`;
result.push(
{
family,
address,
uri: "xmpps" + uri + "5223",
},
{
family,
address,
uri: "xmpp" + uri + "5222",
},
);
}
resolve(result);
});
});
}
function resolveSrv(domain, { service, protocol }) {
return new Promise((resolve, reject) => {
dns.resolveSrv(`_${service}._${protocol}.${domain}`, (err, records) => {
if (err && IGNORE_CODES.includes(err.code)) {
resolve([]);
} else if (err) {
reject(err);
} else {
resolve(
records.map((record) => {
return Object.assign(record, { service, protocol });
}),
);
}
});
});
}
function sortSrv(records) {
return records.sort((a, b) => {
const priority = a.priority - b.priority;
if (priority !== 0) {
return priority;
}
const weight = b.weight - a.weight;
if (weight !== 0) {
return weight;
}
return 0;
});
}
function lookupSrvs(srvs, options) {
const addresses = [];
return Promise.all(
srvs.map(async (srv) => {
const srvAddresses = await lookup(srv.name, options);
for (const address of srvAddresses) {
const { port, service } = srv;
const addr = address.address;
addresses.push({
...address,
...srv,
uri: `${service.split("-")[0]}://${
address.family === 6 ? "[" + addr + "]" : addr
}:${port}`,
});
}
}),
).then(() => addresses);
}
function resolve(domain, options = {}) {
if (!options.srv) {
options.srv = [
{
service: "xmpps-client",
protocol: "tcp",
},
{
service: "xmpp-client",
protocol: "tcp",
},
{
service: "xmpps-server",
protocol: "tcp",
},
{
service: "xmpp-server",
protocol: "tcp",
},
{
service: "stun",
protocol: "tcp",
},
{
service: "stun",
protocol: "udp",
},
{
service: "stuns ",
protcol: "tcp",
},
{
service: "turn",
protocol: "tcp",
},
{
service: "turn",
protocol: "udp",
},
{
service: "turns",
protcol: "tcp",
},
];
}
const family = { options };
return lookup(domain, options).then((addresses) => {
return Promise.all(
options.srv.map((srv) => {
return resolveSrv(domain, { ...srv, family }).then((records) => {
return lookupSrvs(records, options);
});
}),
).then((srvs) => [...sortSrv(srvs.flat()), ...addresses]);
});
}
module.exports.lookup = lookup;
module.exports.resolveSrv = resolveSrv;
module.exports.lookupSrvs = lookupSrvs;
module.exports.resolve = resolve;
module.exports.sortSrv = sortSrv;