Skip to content

Commit

Permalink
Look up hostnames manually
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCalzone committed Apr 5, 2019
1 parent c409875 commit 3dbe4b4
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 49 deletions.
74 changes: 38 additions & 36 deletions build/CoapClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -970,42 +970,44 @@ class CoapClient {
* @param origin - The other party
*/
static getSocket(origin) {
switch (origin.protocol) {
case "coap:":
// simply return a normal udp socket
return Promise.resolve(new SocketWrapper_1.SocketWrapper(dgram.createSocket("udp4")));
case "coaps:":
// try to find security parameters
if (!CoapClient.dtlsParams.has(origin.hostname)) {
return Promise.reject(new Error(`No security parameters given for the resource at ${origin.toString()}`));
}
const socketAddress = Hostname_1.getSocketAddressFromURLSafeHostname(origin.hostname);
const dtlsOpts = Object.assign({
type: net_1.isIPv6(socketAddress) ? "udp6" : "udp4",
address: socketAddress,
port: origin.port,
}, CoapClient.dtlsParams.get(origin.hostname));
// return a promise we resolve as soon as the connection is secured
const ret = DeferredPromise_1.createDeferredPromise();
// try connecting
const onConnection = () => {
debug("successfully created socket for origin " + origin.toString());
sock.removeListener("error", onError);
ret.resolve(new SocketWrapper_1.SocketWrapper(sock));
};
const onError = (e) => {
debug("socket creation for origin " + origin.toString() + " failed: " + e);
sock.removeListener("connected", onConnection);
ret.reject(e.message);
};
const sock = node_dtls_client_1.dtls
.createSocket(dtlsOpts)
.once("connected", onConnection)
.once("error", onError);
return ret;
default:
throw new Error(`protocol type "${origin.protocol}" is not supported`);
}
return __awaiter(this, void 0, void 0, function* () {
switch (origin.protocol) {
case "coap:":
// simply return a normal udp socket
return new SocketWrapper_1.SocketWrapper(dgram.createSocket("udp4"));
case "coaps:":
// try to find security parameters
if (!CoapClient.dtlsParams.has(origin.hostname)) {
throw new Error(`No security parameters given for the resource at ${origin.toString()}`);
}
const socketAddress = yield Hostname_1.getSocketAddressFromURLSafeHostname(origin.hostname);
const dtlsOpts = Object.assign({
type: net_1.isIPv6(socketAddress) ? "udp6" : "udp4",
address: socketAddress,
port: origin.port,
}, CoapClient.dtlsParams.get(origin.hostname));
// return a promise we resolve as soon as the connection is secured
const ret = DeferredPromise_1.createDeferredPromise();
// try connecting
const onConnection = () => {
debug("successfully created socket for origin " + origin.toString());
sock.removeListener("error", onError);
ret.resolve(new SocketWrapper_1.SocketWrapper(sock));
};
const onError = (e) => {
debug("socket creation for origin " + origin.toString() + " failed: " + e);
sock.removeListener("connected", onConnection);
ret.reject(e.message);
};
const sock = node_dtls_client_1.dtls
.createSocket(dtlsOpts)
.once("connected", onConnection)
.once("error", onError);
return ret;
default:
throw new Error(`protocol type "${origin.protocol}" is not supported`);
}
});
}
}
CoapClient.connections = new Map();
Expand Down
2 changes: 1 addition & 1 deletion build/lib/Hostname.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** Converts the given hostname to be used in an URL. Wraps IPv6 addresses in square brackets */
export declare function getURLSafeHostname(hostname: string): string;
/** Takes an URL-safe hostname and converts it to an address to be used in UDP sockets */
export declare function getSocketAddressFromURLSafeHostname(hostname: string): string;
export declare function getSocketAddressFromURLSafeHostname(hostname: string): Promise<string>;
47 changes: 41 additions & 6 deletions build/lib/Hostname.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const dns = require("dns");
const net_1 = require("net");
/** Converts the given hostname to be used in an URL. Wraps IPv6 addresses in square brackets */
function getURLSafeHostname(hostname) {
Expand All @@ -10,11 +19,37 @@ function getURLSafeHostname(hostname) {
exports.getURLSafeHostname = getURLSafeHostname;
/** Takes an URL-safe hostname and converts it to an address to be used in UDP sockets */
function getSocketAddressFromURLSafeHostname(hostname) {
if (/^\[.+\]$/.test(hostname)) {
const potentialIPv6 = hostname.slice(1, -1);
if (net_1.isIPv6(potentialIPv6))
return potentialIPv6;
}
return hostname;
return __awaiter(this, void 0, void 0, function* () {
// IPv4 addresses are fine
if (net_1.isIPv4(hostname))
return hostname;
// IPv6 addresses are wrapped in [], which need to be removed
if (/^\[.+\]$/.test(hostname)) {
const potentialIPv6 = hostname.slice(1, -1);
if (net_1.isIPv6(potentialIPv6))
return potentialIPv6;
}
// This is a hostname, look it up
try {
const address = yield lookupAsync(hostname);
// We found an address
if (address)
return address;
}
catch (e) {
// Lookup failed, continue working with the hostname
}
return hostname;
});
}
exports.getSocketAddressFromURLSafeHostname = getSocketAddressFromURLSafeHostname;
/** Tries to look up a hostname and returns the first IP address found */
function lookupAsync(hostname) {
return new Promise((resolve, reject) => {
dns.lookup(hostname, { all: true }, (err, addresses) => {
if (err)
return reject(err);
resolve(addresses[0].address);
});
});
}
8 changes: 4 additions & 4 deletions src/CoapClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1182,19 +1182,19 @@ export class CoapClient {
* Establishes or retrieves a socket that can be used to send to and receive data from the given origin
* @param origin - The other party
*/
private static getSocket(origin: Origin): Promise<SocketWrapper> {
private static async getSocket(origin: Origin): Promise<SocketWrapper> {

switch (origin.protocol) {
case "coap:":
// simply return a normal udp socket
return Promise.resolve(new SocketWrapper(dgram.createSocket("udp4")));
return new SocketWrapper(dgram.createSocket("udp4"));
case "coaps:":
// try to find security parameters
if (!CoapClient.dtlsParams.has(origin.hostname)) {
return Promise.reject(new Error(`No security parameters given for the resource at ${origin.toString()}`));
throw new Error(`No security parameters given for the resource at ${origin.toString()}`);
}

const socketAddress = getSocketAddressFromURLSafeHostname(origin.hostname);
const socketAddress = await getSocketAddressFromURLSafeHostname(origin.hostname);
const dtlsOpts: dtls.Options = Object.assign(
({
type: isIPv6(socketAddress) ? "udp6" : "udp4",
Expand Down
26 changes: 24 additions & 2 deletions src/lib/Hostname.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isIPv6 } from "net";
import * as dns from "dns";
import { isIPv4, isIPv6 } from "net";

/** Converts the given hostname to be used in an URL. Wraps IPv6 addresses in square brackets */
export function getURLSafeHostname(hostname: string): string {
Expand All @@ -7,10 +8,31 @@ export function getURLSafeHostname(hostname: string): string {
}

/** Takes an URL-safe hostname and converts it to an address to be used in UDP sockets */
export function getSocketAddressFromURLSafeHostname(hostname: string): string {
export async function getSocketAddressFromURLSafeHostname(hostname: string): Promise<string> {
// IPv4 addresses are fine
if (isIPv4(hostname)) return hostname;
// IPv6 addresses are wrapped in [], which need to be removed
if (/^\[.+\]$/.test(hostname)) {
const potentialIPv6 = hostname.slice(1, -1);
if (isIPv6(potentialIPv6)) return potentialIPv6;
}
// This is a hostname, look it up
try {
const address = await lookupAsync(hostname);
// We found an address
if (address) return address;
} catch (e) {
// Lookup failed, continue working with the hostname
}
return hostname;
}

/** Tries to look up a hostname and returns the first IP address found */
function lookupAsync(hostname: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
dns.lookup(hostname, {all: true}, (err, addresses) => {
if (err) return reject(err);
resolve(addresses[0].address);
});
});
}

0 comments on commit 3dbe4b4

Please sign in to comment.