Using a provider for the dns resolver
parent
126b836cbd
commit
38ea11725f
|
@ -0,0 +1,3 @@
|
|||
window.__native_client_init_shared(__webpack_require__);
|
||||
|
||||
import "tc-shared/ui/react-elements/external-modal/PopoutEntrypoint";
|
|
@ -1,6 +0,0 @@
|
|||
import {AddressTarget, ResolveOptions} from "tc-shared/dns";
|
||||
import {ServerAddress} from "tc-shared/tree/Server";
|
||||
|
||||
export function supported();
|
||||
export function resolve_address(address: ServerAddress, options?: ResolveOptions) : Promise<AddressTarget>;
|
||||
export function resolve_address_ipv4(address: string) : Promise<string>;
|
|
@ -15,7 +15,6 @@ import {defaultRecorder, RecorderProfile} from "./voice/RecorderProfile";
|
|||
import {Regex} from "./ui/modal/ModalConnect";
|
||||
import {formatMessage} from "./ui/frames/chat";
|
||||
import {spawnAvatarUpload} from "./ui/modal/ModalAvatar";
|
||||
import * as dns from "tc-backend/dns";
|
||||
import {EventHandler, Registry} from "./events";
|
||||
import {FileManager} from "./file/FileManager";
|
||||
import {FileTransferState, TransferProvider} from "./file/Transfer";
|
||||
|
@ -42,6 +41,7 @@ import {PlaylistManager} from "tc-shared/music/PlaylistManager";
|
|||
import {connectionHistory} from "tc-shared/connectionlog/History";
|
||||
import {ConnectParameters} from "tc-shared/ui/modal/connect/Controller";
|
||||
import {assertMainApplication} from "tc-shared/ui/utils";
|
||||
import {getDNSProvider} from "tc-shared/dns";
|
||||
|
||||
assertMainApplication();
|
||||
|
||||
|
@ -318,20 +318,25 @@ export class ConnectionHandler {
|
|||
|
||||
if(resolvedAddress.host.match(Regex.IP_V4) || resolvedAddress.host.match(Regex.IP_V6)) {
|
||||
/* We don't have to resolve the target host */
|
||||
} else if(dns.supported()) {
|
||||
} else {
|
||||
this.log.log("connection.hostname.resolve", {});
|
||||
try {
|
||||
const resolved = await dns.resolve_address(parsedAddress, { timeout: 5000 });
|
||||
const resolved = await getDNSProvider().resolveAddress({ hostname: parsedAddress.host, port: parsedAddress.port }, { timeout: 5000 });
|
||||
if(this.connectAttemptId !== localConnectionAttemptId) {
|
||||
/* Our attempt has been aborted */
|
||||
return;
|
||||
}
|
||||
|
||||
if(resolved?.target_ip) {
|
||||
resolvedAddress.host = resolved.target_ip;
|
||||
resolvedAddress.port = typeof resolved.target_port === "number" ? resolved.target_port : resolvedAddress.port;
|
||||
} else {
|
||||
throw tr("address resolve result id empty");
|
||||
if(resolved.status === "empty-result") {
|
||||
throw tr("address resolve result empty");
|
||||
} else if(resolved.status === "error") {
|
||||
throw resolved.message;
|
||||
}
|
||||
|
||||
resolvedAddress.host = resolved.resolvedAddress.hostname;
|
||||
resolvedAddress.port = resolved.resolvedAddress.port;
|
||||
if(typeof resolvedAddress.port !== "number") {
|
||||
resolvedAddress.port = parsedAddress.port;
|
||||
}
|
||||
|
||||
this.log.log("connection.hostname.resolved", {
|
||||
|
@ -349,8 +354,6 @@ export class ConnectionHandler {
|
|||
this.handleDisconnect(DisconnectReason.DNS_FAILED, error);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.handleDisconnect(DisconnectReason.DNS_FAILED, tr("Unable to resolve hostname"));
|
||||
}
|
||||
|
||||
if(this.autoReconnectAttempt) {
|
||||
|
@ -495,7 +498,7 @@ export class ConnectionHandler {
|
|||
return this.serverConnection && this.serverConnection.connected();
|
||||
}
|
||||
|
||||
private generate_ssl_certificate_accept() : JQuery {
|
||||
private generate_ssl_certificate_accept() : HTMLAnchorElement {
|
||||
const properties = {
|
||||
connect_default: true,
|
||||
connect_profile: this.serverConnection.handshake_handler().parameters.profile.id,
|
||||
|
@ -504,26 +507,29 @@ export class ConnectionHandler {
|
|||
|
||||
const build_url = (base: string, search: string, props: any) => {
|
||||
const parameters: string[] = [];
|
||||
for(const key of Object.keys(props))
|
||||
for(const key of Object.keys(props)) {
|
||||
parameters.push(key + "=" + encodeURIComponent(props[key]));
|
||||
}
|
||||
|
||||
let callback = base + search; /* don't use document.URL because it may contains a #! */
|
||||
if(!search)
|
||||
if(!search) {
|
||||
callback += "?" + parameters.join("&");
|
||||
else
|
||||
} else {
|
||||
callback += "&" + parameters.join("&");
|
||||
}
|
||||
|
||||
return "https://" + this.serverConnection.remote_address().host + ":" + this.serverConnection.remote_address().port + "/?forward_url=" + encodeURIComponent(callback);
|
||||
};
|
||||
|
||||
/* generate the tag */
|
||||
const tag = $.spawn("a").text(tr("here"));
|
||||
const tag = document.createElement("a");
|
||||
tag.text = tr("here");
|
||||
|
||||
let pathname = document.location.pathname;
|
||||
if(pathname.endsWith(".php"))
|
||||
pathname = pathname.substring(0, pathname.lastIndexOf("/"));
|
||||
|
||||
tag.attr('href', build_url(document.location.origin + pathname, document.location.search, properties));
|
||||
tag.href = build_url(document.location.origin + pathname, document.location.search, properties);
|
||||
return tag;
|
||||
}
|
||||
|
||||
|
@ -561,7 +567,7 @@ export class ConnectionHandler {
|
|||
logError(LogCategory.CLIENT, tr("Could not connect to remote host!"), data);
|
||||
}
|
||||
|
||||
if(__build.target === "client" || !dns.resolve_address_ipv4) {
|
||||
if(__build.target === "client") {
|
||||
createErrorModal(
|
||||
tr("Could not connect"),
|
||||
tr("Could not connect to remote host (Connection refused)")
|
||||
|
@ -569,9 +575,19 @@ export class ConnectionHandler {
|
|||
} else {
|
||||
const generateAddressPart = () => Math.floor(Math.random() * 256);
|
||||
const addressParts = [generateAddressPart(), generateAddressPart(), generateAddressPart(), generateAddressPart()];
|
||||
dns.resolve_address_ipv4(addressParts.join("-") + ".con-gate.work").then(async result => {
|
||||
if(result !== addressParts.join("."))
|
||||
getDNSProvider().resolveAddressIPv4({
|
||||
hostname: addressParts.join("-") + ".con-gate.work",
|
||||
port: 9987
|
||||
}, { timeout: 5000 }).then(async result => {
|
||||
if(result.status === "empty-result") {
|
||||
throw tr("empty result");
|
||||
} else if(result.status === "error") {
|
||||
throw result.message;
|
||||
}
|
||||
|
||||
if(result.resolvedAddress.hostname !== addressParts.join(".")) {
|
||||
throw "miss matching address";
|
||||
}
|
||||
|
||||
createErrorModal(
|
||||
tr("Could not connect"),
|
||||
|
|
|
@ -5,18 +5,60 @@ export interface AddressTarget {
|
|||
|
||||
export interface ResolveOptions {
|
||||
timeout?: number;
|
||||
allow_cache?: boolean;
|
||||
max_depth?: number;
|
||||
allowCache?: boolean;
|
||||
maxDepth?: number;
|
||||
|
||||
allow_srv?: boolean;
|
||||
allow_cname?: boolean;
|
||||
allow_any?: boolean;
|
||||
allow_a?: boolean;
|
||||
allow_aaaa?: boolean;
|
||||
allowSrv?: boolean;
|
||||
allowCName?: boolean;
|
||||
allowAny?: boolean;
|
||||
allowA?: boolean;
|
||||
allowAAAA?: boolean;
|
||||
}
|
||||
|
||||
export const default_options: ResolveOptions = {
|
||||
timeout: 5000,
|
||||
allow_cache: true,
|
||||
max_depth: 5
|
||||
allowCache: true,
|
||||
maxDepth: 5
|
||||
};
|
||||
|
||||
export interface DNSResolveOptions {
|
||||
timeout?: number;
|
||||
allowCache?: boolean;
|
||||
maxDepth?: number;
|
||||
|
||||
allowSrv?: boolean;
|
||||
allowCName?: boolean;
|
||||
allowAny?: boolean;
|
||||
allowA?: boolean;
|
||||
allowAAAA?: boolean;
|
||||
}
|
||||
|
||||
export interface DNSAddress {
|
||||
hostname: string,
|
||||
port: number
|
||||
}
|
||||
|
||||
export type DNSResolveResult = {
|
||||
status: "success",
|
||||
originalAddress: DNSAddress,
|
||||
resolvedAddress: DNSAddress
|
||||
} | {
|
||||
status: "error",
|
||||
message: string
|
||||
} | {
|
||||
status: "empty-result"
|
||||
};
|
||||
|
||||
export interface DNSProvider {
|
||||
resolveAddress(address: DNSAddress, options: DNSResolveOptions) : Promise<DNSResolveResult>;
|
||||
resolveAddressIPv4(address: DNSAddress, options: DNSResolveOptions) : Promise<DNSResolveResult>;
|
||||
}
|
||||
|
||||
let provider: DNSProvider;
|
||||
export function getDNSProvider() : DNSProvider {
|
||||
return provider;
|
||||
}
|
||||
|
||||
export function setDNSProvider(newProvider: DNSProvider) {
|
||||
provider = newProvider;
|
||||
}
|
|
@ -1,21 +1,16 @@
|
|||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import * as ipc from "../../../ipc/BrowserIPC";
|
||||
import * as i18n from "../../../i18n/localize";
|
||||
import {Stage} from "tc-loader";
|
||||
import {AbstractModal} from "../../../ui/react-elements/ModalDefinitions";
|
||||
import {AppParameters} from "../../../settings";
|
||||
import {getPopoutController} from "./PopoutController";
|
||||
import {setupJSRender} from "../../../ui/jsrender";
|
||||
|
||||
import "../../../file/RemoteAvatars";
|
||||
import "../../../file/RemoteIcons";
|
||||
import {findRegisteredModal} from "tc-shared/ui/react-elements/modal/Registry";
|
||||
import {ModalRenderer} from "tc-shared/ui/react-elements/external-modal/ModalRenderer";
|
||||
import {constructAbstractModalClass} from "tc-shared/ui/react-elements/modal/Definitions";
|
||||
|
||||
if("__native_client_init_shared" in window) {
|
||||
(window as any).__native_client_init_shared(__webpack_require__);
|
||||
}
|
||||
import "../../../file/RemoteAvatars";
|
||||
import "../../../file/RemoteIcons";
|
||||
|
||||
let modalRenderer: ModalRenderer;
|
||||
let modalInstance: AbstractModal;
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import {AddressTarget, ResolveOptions} from "tc-shared/dns";
|
||||
import {ServerAddress} from "tc-shared/tree/Server";
|
||||
import {resolveAddressIpV4, resolveTeaSpeakServerAddress} from "./dns/resolver";
|
||||
|
||||
export function supported() { return true; }
|
||||
|
||||
export async function resolve_address(address: ServerAddress, options?: ResolveOptions) : Promise<AddressTarget> {
|
||||
return await resolveTeaSpeakServerAddress(address, options);
|
||||
}
|
||||
|
||||
export async function resolve_address_ipv4(address: string) : Promise<string> {
|
||||
return await resolveAddressIpV4(address);
|
||||
}
|
|
@ -1,15 +1,11 @@
|
|||
import * as log from "tc-shared/log";
|
||||
import {LogCategory, logTrace, logWarn} from "tc-shared/log";
|
||||
import {LogCategory, logError, logTrace, logWarn} from "tc-shared/log";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {ServerAddress} from "tc-shared/tree/Server";
|
||||
import {AddressTarget, default_options, ResolveOptions} from "tc-shared/dns";
|
||||
import {executeDnsRequest, RRType} from "tc-backend/web/dns/api";
|
||||
|
||||
type Address = { host: string, port: number };
|
||||
import {default_options, DNSAddress, DNSResolveResult, ResolveOptions} from "tc-shared/dns";
|
||||
import {executeDnsRequest, RRType} from "./api";
|
||||
|
||||
interface DNSResolveMethod {
|
||||
name() : string;
|
||||
resolve(address: Address) : Promise<Address | undefined>;
|
||||
resolve(address: DNSAddress) : Promise<DNSAddress | undefined>;
|
||||
}
|
||||
|
||||
class LocalhostResolver implements DNSResolveMethod {
|
||||
|
@ -17,10 +13,10 @@ class LocalhostResolver implements DNSResolveMethod {
|
|||
return "localhost";
|
||||
}
|
||||
|
||||
async resolve(address: Address): Promise<Address | undefined> {
|
||||
if(address.host === "localhost") {
|
||||
async resolve(address: DNSAddress): Promise<DNSAddress | undefined> {
|
||||
if(address.hostname === "localhost") {
|
||||
return {
|
||||
host: "127.0.0.1",
|
||||
hostname: "127.0.0.1",
|
||||
port: address.port
|
||||
}
|
||||
}
|
||||
|
@ -42,14 +38,14 @@ class IPResolveMethod implements DNSResolveMethod {
|
|||
return "ip v" + (this.v6 ? "6" : "4") + " resolver";
|
||||
}
|
||||
|
||||
async resolve(address: Address): Promise<Address | undefined> {
|
||||
const answer = await executeDnsRequest(address.host, this.v6 ? RRType.AAAA : RRType.A);
|
||||
async resolve(address: DNSAddress): Promise<DNSAddress | undefined> {
|
||||
const answer = await executeDnsRequest(address.hostname, this.v6 ? RRType.AAAA : RRType.A);
|
||||
if(!answer.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
host: answer[0].data,
|
||||
hostname: answer[0].data,
|
||||
port: address.port
|
||||
}
|
||||
}
|
||||
|
@ -73,8 +69,8 @@ class SRVResolveMethod implements DNSResolveMethod {
|
|||
return "srv resolve [" + this.application + "]";
|
||||
}
|
||||
|
||||
async resolve(address: Address): Promise<Address | undefined> {
|
||||
const answer = await executeDnsRequest((this.application ? this.application + "." : "") + address.host, RRType.SRV);
|
||||
async resolve(address: DNSAddress): Promise<DNSAddress | undefined> {
|
||||
const answer = await executeDnsRequest((this.application ? this.application + "." : "") + address.hostname, RRType.SRV);
|
||||
|
||||
const records: {[key: number]: ParsedSVRRecord[]} = {};
|
||||
for(const record of answer) {
|
||||
|
@ -146,7 +142,7 @@ class SRVResolveMethod implements DNSResolveMethod {
|
|||
}
|
||||
|
||||
return {
|
||||
host: record.target,
|
||||
hostname: record.target,
|
||||
port: record.port == 0 ? address.port : record.port
|
||||
};
|
||||
}
|
||||
|
@ -167,7 +163,7 @@ class SRV_IPResolveMethod implements DNSResolveMethod {
|
|||
return "srv ip resolver [" + this.srvResolver.name() + "; " + this.ipv4Resolver.name() + "; " + this.ipv6Resolver.name() + "]";
|
||||
}
|
||||
|
||||
async resolve(address: Address): Promise<Address | undefined> {
|
||||
async resolve(address: DNSAddress): Promise<DNSAddress | undefined> {
|
||||
const srvAddress = await this.srvResolver.resolve(address);
|
||||
if(!srvAddress) {
|
||||
return undefined;
|
||||
|
@ -192,21 +188,21 @@ class DomainRootResolveMethod implements DNSResolveMethod {
|
|||
return "domain-root [" + this.resolver.name() + "]";
|
||||
}
|
||||
|
||||
async resolve(address: Address): Promise<Address | undefined> {
|
||||
const parts = address.host.split(".");
|
||||
async resolve(address: DNSAddress): Promise<DNSAddress | undefined> {
|
||||
const parts = address.hostname.split(".");
|
||||
if(parts.length < 3) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return await this.resolver.resolve({
|
||||
host: parts.slice(-2).join("."),
|
||||
hostname: parts.slice(1).join("."),
|
||||
port: address.port
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class TeaSpeakDNSResolve {
|
||||
readonly address: Address;
|
||||
readonly address: DNSAddress;
|
||||
private resolvers: {[key: string]:{ resolver: DNSResolveMethod, after: string[] }} = {};
|
||||
private resolving = false;
|
||||
private timeout;
|
||||
|
@ -217,7 +213,7 @@ class TeaSpeakDNSResolve {
|
|||
private finished_resolvers: string[];
|
||||
private resolving_resolvers: string[];
|
||||
|
||||
constructor(addr: Address) {
|
||||
constructor(addr: DNSAddress) {
|
||||
this.address = addr;
|
||||
}
|
||||
|
||||
|
@ -229,7 +225,7 @@ class TeaSpeakDNSResolve {
|
|||
this.resolvers[resolver.name()] = { resolver: resolver, after: after.map(e => typeof e === "string" ? e : e.name()) };
|
||||
}
|
||||
|
||||
resolve(timeout: number) : Promise<Address> {
|
||||
resolve(timeout: number) : Promise<DNSAddress> {
|
||||
if(this.resolving) {
|
||||
throw tr("already resolving");
|
||||
}
|
||||
|
@ -246,9 +242,9 @@ class TeaSpeakDNSResolve {
|
|||
this.timeout = setTimeout(() => {
|
||||
this.callback_fail(tr("timeout"));
|
||||
}, timeout);
|
||||
logTrace(LogCategory.DNS, tr("Start resolving %s:%d"), this.address.host, this.address.port);
|
||||
logTrace(LogCategory.DNS, tr("Start resolving %s:%d"), this.address.hostname, this.address.port);
|
||||
|
||||
return new Promise<Address>((resolve, reject) => {
|
||||
return new Promise<DNSAddress>((resolve, reject) => {
|
||||
this.callback_success = data => {
|
||||
cleanup();
|
||||
resolve(data);
|
||||
|
@ -290,8 +286,8 @@ class TeaSpeakDNSResolve {
|
|||
}
|
||||
|
||||
logTrace(LogCategory.DNS, tr(" Successfully resolved address %s:%d to %s:%d via resolver %s"),
|
||||
this.address.host, this.address.port,
|
||||
result.host, result.port,
|
||||
this.address.hostname, this.address.port,
|
||||
result.hostname, result.port,
|
||||
resolver_name);
|
||||
this.callback_success(result);
|
||||
}).catch(error => {
|
||||
|
@ -324,7 +320,8 @@ const resolverSrvTS3 = new SRV_IPResolveMethod(new SRVResolveMethod("_ts3._udp")
|
|||
const resolverDrSrvTS = new DomainRootResolveMethod(resolverSrvTS);
|
||||
const resolverDrSrvTS3 = new DomainRootResolveMethod(resolverSrvTS3);
|
||||
|
||||
export async function resolveTeaSpeakServerAddress(address: ServerAddress, _options?: ResolveOptions) : Promise<AddressTarget> {
|
||||
export async function resolveTeaSpeakServerAddress(address: DNSAddress, _options?: ResolveOptions) : Promise<DNSResolveResult> {
|
||||
try {
|
||||
const options = Object.assign({}, default_options);
|
||||
Object.assign(options, _options);
|
||||
|
||||
|
@ -343,10 +340,28 @@ export async function resolveTeaSpeakServerAddress(address: ServerAddress, _opti
|
|||
resolver.registerResolver(kResolverIpV6, kResolverIpV4);
|
||||
|
||||
const response = await resolver.resolve(options.timeout || 5000);
|
||||
if(!response) {
|
||||
return {
|
||||
target_ip: response.host,
|
||||
target_port: response.port
|
||||
status: "empty-result"
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: "success",
|
||||
originalAddress: address,
|
||||
resolvedAddress: response
|
||||
};
|
||||
} catch (error) {
|
||||
if(typeof error !== "string") {
|
||||
logError(LogCategory.DNS, tr("Failed to resolve %o: %o"), address, error);
|
||||
error = tr("lookup the console");
|
||||
}
|
||||
|
||||
return {
|
||||
status: "error",
|
||||
message: error
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function resolveAddressIpV4(address: string) : Promise<string> {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import {DNSAddress, DNSProvider, DNSResolveOptions, DNSResolveResult, setDNSProvider} from "tc-shared/dns";
|
||||
import {resolveAddressIpV4, resolveTeaSpeakServerAddress} from "tc-backend/web/dns/resolver";
|
||||
import {LogCategory, logError} from "tc-shared/log";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
|
||||
setDNSProvider(new class implements DNSProvider {
|
||||
resolveAddress(address: DNSAddress, options: DNSResolveOptions): Promise<DNSResolveResult> {
|
||||
return resolveTeaSpeakServerAddress(address, options);
|
||||
}
|
||||
|
||||
async resolveAddressIPv4(address: DNSAddress, options: DNSResolveOptions): Promise<DNSResolveResult> {
|
||||
try {
|
||||
const result = await resolveAddressIpV4(address.hostname);
|
||||
if(!result) {
|
||||
return { status: "empty-result" };
|
||||
}
|
||||
return {
|
||||
status: "success",
|
||||
originalAddress: address,
|
||||
resolvedAddress: {
|
||||
hostname: result,
|
||||
port: address.port
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
if(typeof error !== "string") {
|
||||
logError(LogCategory.DNS, tr("Failed to resolve %o: %o"), address, error);
|
||||
error = tr("lookup the console");
|
||||
}
|
||||
|
||||
return {
|
||||
status: "error",
|
||||
message: error
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
|
@ -9,6 +9,7 @@ import "./hooks/ExternalModal";
|
|||
import "./hooks/AudioRecorder";
|
||||
import "./hooks/MenuBar";
|
||||
import "./hooks/Video";
|
||||
import "./hooks/Dns";
|
||||
|
||||
import "./UnloadHandler";
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@ import * as config_base from "./webpack.config";
|
|||
|
||||
export = env => config_base.config(env, "client").then(config => {
|
||||
Object.assign(config.entry, {
|
||||
"client-app": ["./client/app/index.ts"]
|
||||
"client-app": ["./client/app/index.ts"],
|
||||
"modal-external": ["./client/app/EntryPointPopoutModal.ts"]
|
||||
});
|
||||
|
||||
Object.assign(config.resolve.alias, {
|
||||
|
|
Loading…
Reference in New Issue