updated the dns resolver in js part
This commit is contained in:
parent
d8c008ad56
commit
7e87ec772e
@ -1,245 +1,38 @@
|
||||
/// <reference path="../imports/imports_shared.d.ts" />
|
||||
|
||||
|
||||
window["require_setup"](module);
|
||||
|
||||
import * as dns_handler from "dns";
|
||||
import * as dns_handler from "teaclient_dns";
|
||||
|
||||
namespace _dns {
|
||||
type Lookup<R> = (hostname: string, callback: (err: NodeJS.ErrnoException, result: R) => void) => void;
|
||||
type AsyncLookup<R> = (hostname: string, timeout: number) => Promise<R>;
|
||||
function make_async<R>(fun: Lookup<R>) : AsyncLookup<R> {
|
||||
return (hostname, timeout) => {
|
||||
return new Promise<R>((resolve, reject) => {
|
||||
const timeout_id = setTimeout(() => {
|
||||
reject("timeout");
|
||||
}, timeout);
|
||||
fun(hostname, (err, result) => {
|
||||
clearTimeout(timeout_id);
|
||||
if(err) {
|
||||
if(err.errno as any == "ENOTFOUND" || err.errno as any == "ENODATA") {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
reject(err);
|
||||
} else
|
||||
resolve(result);
|
||||
})
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const async_resolve_srv = make_async(dns_handler.resolveSrv);
|
||||
const async_resolve_cname = make_async(dns_handler.resolveCname);
|
||||
const async_resolve_a = make_async(dns_handler.resolve4);
|
||||
const async_resolve_aaaa = make_async(dns_handler.resolve6);
|
||||
const async_resolve_any = make_async(dns_handler.resolveAny);
|
||||
|
||||
export interface AddressTarget {
|
||||
target_ip: string;
|
||||
target_port?: number;
|
||||
}
|
||||
export interface ResolveOptions extends dns.ResolveOptions {
|
||||
log?: (message: string, ...args: any[]) => void;
|
||||
override_port?: number;
|
||||
}
|
||||
|
||||
export function supported() { return true; }
|
||||
export async function resolve_address(address: string, _options?: ResolveOptions) : Promise<AddressTarget> {
|
||||
if(address === "localhost") {
|
||||
return {
|
||||
target_ip: "localhost"
|
||||
};
|
||||
}
|
||||
|
||||
const options: ResolveOptions = {};
|
||||
Object.assign(options, dns.default_options);
|
||||
Object.assign(options, _options || {});
|
||||
|
||||
if(options.max_depth <= 0)
|
||||
throw "max depth exceeded";
|
||||
|
||||
if(typeof(options.log) !== "function")
|
||||
options.log = (message, ...args) => console.debug("[DNS] " + message, ...args);
|
||||
|
||||
const mod_options: ResolveOptions = {};
|
||||
Object.assign(mod_options, options);
|
||||
mod_options.max_depth = mod_options.max_depth - 1;
|
||||
mod_options.allow_srv = false;
|
||||
mod_options.log = (message, ...args) => options.log(" " + message, ...args);
|
||||
|
||||
options.log("Resolving %s", address);
|
||||
|
||||
let response: AddressTarget;
|
||||
if(typeof(options.allow_aaaa) !== "boolean" || options.allow_aaaa) {
|
||||
const aaaa_response: string[] | undefined | null = await async_resolve_aaaa(address, options.timeout).catch(error => {
|
||||
options.log("AAAA record resolved unsuccessfully (%o)", error);
|
||||
return Promise.resolve(undefined);
|
||||
}) as any;
|
||||
|
||||
|
||||
if(typeof(aaaa_response) !== "undefined") {
|
||||
if(!aaaa_response || aaaa_response.length == 0)
|
||||
options.log("No AAAA records found");
|
||||
else {
|
||||
options.log("Resolved AAAA records: %o. Returning: %s", aaaa_response, aaaa_response[0]);
|
||||
response = {
|
||||
target_ip: aaaa_response[0],
|
||||
target_port: options.override_port
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!response && (typeof(options.allow_a) !== "boolean" || options.allow_a)) {
|
||||
const a_response: string[] | undefined | null = await async_resolve_a(address, options.timeout).catch(error => {
|
||||
options.log("A record resolved unsuccessfully (%o)", error);
|
||||
return Promise.resolve(undefined);
|
||||
}) as any;
|
||||
|
||||
if(typeof(a_response) !== "undefined") {
|
||||
if(!a_response || a_response.length == 0)
|
||||
options.log("No A records found");
|
||||
else {
|
||||
options.log("Resolved A records: %o. Returning: %s", a_response, a_response[0]);
|
||||
response = {
|
||||
target_ip: a_response[0],
|
||||
target_port: options.override_port
|
||||
};
|
||||
}
|
||||
export async function resolve_address(address: ServerAddress, _options?: dns.ResolveOptions) : Promise<dns.AddressTarget> {
|
||||
/* backwards compatibility */
|
||||
if(typeof(address) === "string") {
|
||||
address = {
|
||||
host: address,
|
||||
port: 9987
|
||||
}
|
||||
}
|
||||
|
||||
if(!response && (typeof(options.allow_any) !== "boolean" || options.allow_any)) {
|
||||
const any_response: dns_handler.AnyRecord[] = await async_resolve_any(address, options.timeout).catch(error => {
|
||||
options.log("ANY record resolved unsuccessfully (%o)", error);
|
||||
return Promise.resolve(undefined);
|
||||
return new Promise<dns.AddressTarget>((resolve, reject) => {
|
||||
dns_handler.resolve_cr(address.host, address.port, result => {
|
||||
if(typeof(result) === "string")
|
||||
reject(result);
|
||||
else
|
||||
resolve({
|
||||
target_ip: result.host,
|
||||
target_port: result.port
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
if(typeof(any_response) !== "undefined") {
|
||||
if(!any_response || any_response.length == 0)
|
||||
options.log("No ANY records found");
|
||||
else {
|
||||
options.log("Resolved ANY records: %o.", any_response);
|
||||
for(const record of any_response) {
|
||||
if(record.type === "A") {
|
||||
const a_record = record as dns_handler.AnyARecord;
|
||||
options.log("Returning A record from ANY query: %s", a_record.address);
|
||||
return {
|
||||
target_ip: a_record.address,
|
||||
target_port: options.override_port
|
||||
};
|
||||
} else if(record.type === "AAAA") {
|
||||
const aaaa_record = record as dns_handler.AnyAaaaRecord;
|
||||
options.log("Returning AAAA record from ANY query: %s", aaaa_record.address);
|
||||
return {
|
||||
target_ip: aaaa_record.address,
|
||||
target_port: options.override_port
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(typeof(options.allow_srv) !== "boolean" || options.allow_srv) {
|
||||
const response: dns_handler.SrvRecord[] = await async_resolve_srv("_ts3._udp." + address, options.timeout).catch(error => {
|
||||
options.log("SRV resolve unsuccessfully (%o)", error);
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
|
||||
if(typeof(response) !== "undefined") {
|
||||
if(!response || response.length == 0)
|
||||
options.log("No SRV records found");
|
||||
else {
|
||||
const sorted = response.sort((a, b) => b.weight - a.weight);
|
||||
const original_port = mod_options.override_port;
|
||||
options.log("Resolved SRV records: %o", sorted);
|
||||
for(const entry of sorted) {
|
||||
options.log("Resolving SRV record: %o", entry);
|
||||
mod_options.override_port = entry.port || mod_options.override_port;
|
||||
const resp = await resolve_address(entry.name, mod_options).catch(error => {
|
||||
options.log("SRV entry resolved unsuccessfully (%o)", error);
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
if(resp) {
|
||||
options.log("SRV entry resolved to %o. Found result", resp);
|
||||
return resp;
|
||||
} else {
|
||||
options.log("No response for SRV record");
|
||||
}
|
||||
}
|
||||
mod_options.override_port = original_port;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* resolve CNAME in the last step, else may the A records will be empty! */
|
||||
if(typeof(options.allow_cname) !== "boolean" || options.allow_cname) {
|
||||
const cname_response: string[] = await async_resolve_cname(address, options.timeout).catch(error => {
|
||||
options.log("CName resolved unsuccessfully (%o)", error);
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
|
||||
if(typeof(cname_response) !== "undefined") {
|
||||
if(!cname_response || cname_response.length == 0)
|
||||
options.log("No CNAME records found");
|
||||
else {
|
||||
options.log("Resolved %d CNAME records", cname_response.length);
|
||||
for(const entry of cname_response) {
|
||||
options.log("Resolving CNAME record: %o", entry);
|
||||
const resp = await resolve_address(entry, mod_options).catch(error => {
|
||||
options.log("Failed to resolve resolved CName (%o)", error);
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
if(resp) {
|
||||
options.log("CName entry resolved to %o. Found result", resp);
|
||||
return resp;
|
||||
} else {
|
||||
options.log("No response for CName record");
|
||||
}
|
||||
}
|
||||
response = undefined; /* overridden by a CNAME */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
const lookup_result = await new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
reject("timeout");
|
||||
}, options.timeout);
|
||||
dns_handler.lookup(address, {
|
||||
hints: dns_handler.ADDRCONFIG | dns_handler.V4MAPPED,
|
||||
all: true,
|
||||
family: 0
|
||||
}, (error, result, family) => {
|
||||
clearTimeout(timeout);
|
||||
console.log(result);
|
||||
if(error) {
|
||||
if(error.errno as any == "ENOTFOUND" || error.errno as any == "ENODATA") {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
reject(error);
|
||||
} else
|
||||
resolve(result);
|
||||
});
|
||||
}).catch(error => {
|
||||
options.log("General lookup failed: %o", error);
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
console.log(lookup_result);
|
||||
*/
|
||||
if(response)
|
||||
return response;
|
||||
options.log("No records found, no result.");
|
||||
return undefined;
|
||||
})
|
||||
}
|
||||
|
||||
dns_handler.setServers(["8.8.8.8", "8.8.8.4", "1.1.1.1"]);
|
||||
}
|
||||
|
||||
Object.assign(window["dns"] || (window["dns"] = {} as any), _dns);
|
||||
Object.assign(window["dns"] || (window["dns"] = {} as any), _dns);
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "Native DNS initialized",
|
||||
function: async () => {
|
||||
dns_handler.initialize();
|
||||
},
|
||||
priority: 10
|
||||
});
|
Loading…
Reference in New Issue
Block a user