Skip to content

Commit

Permalink
[FIX] IPv6 handling of host port parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
aricart committed Jan 31, 2022
1 parent 0a3f40c commit 09d03a1
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 21 deletions.
75 changes: 55 additions & 20 deletions nats-base-client/servers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,64 @@ import { defaultPort, getUrlParseFn } from "./transport.ts";
import { shuffle } from "./util.ts";
import { isIP } from "./ipparser.ts";

function hostPort(
function isIPV4OrHostname(hp: string): boolean {
if (hp.indexOf(".") !== -1) {
return true;
}
if (hp.indexOf("[") !== -1 || hp.indexOf("::") !== -1) {
return false;
}
if (hp.split(":").length <= 2) {
return true;
}
return false;
}

function isIPV6(hp: string) {
return !isIPV4OrHostname(hp);
}

export function hostPort(
u: string,
): { listen: string; hostname: string; port: number } {
u = u.trim();
// remove any protocol that may have been provided
if (u.match(/^(.*:\/\/)(.*)/m)) {
u = u.replace(/^(.*:\/\/)(.*)/gm, "$2");
}

// in web environments, URL may not be a living standard
// that means that protocols other than HTTP/S are not
// parsable correctly.
const url = new URL(`http://${u}`);

let sport = "";
if (!url.port) {
if (u.endsWith(":80")) {
sport = "80";
} else if (u.endsWith(":443")) {
sport = "443";
} else {
url.port = `${DEFAULT_PORT}`;
}

// the third complication is that we may have been given
// an IPv6

// we only wrap cases where they gave us a plain ipv6
// and we are not already bracketed
if (isIPV6(u) && u.indexOf("[") === -1) {
u = `[${u}]`;
}
// if we have ipv6, we expect port after ']:' otherwise after ':'
const op = isIPV6(u) ? u.match(/(]:)(\d+)/) : u.match(/(:)(\d+)/);
const port = op && op.length === 3 && op[1] && op[2]
? parseInt(op[2])
: DEFAULT_PORT;

// the next complication is that new URL() may
// eat ports which match the protocol - so for example
// port 80 may be eliminated - so we flip the protocol
// so that it always yields a value
const protocol = port === 80 ? "https" : "http";
const url = new URL(`${protocol}://${u}`);
url.port = `${port}`;

let hostname = url.hostname;
// if we are bracketed, we need to rip it out
if (hostname.charAt(0) === "[") {
hostname = hostname.substring(1, hostname.length - 1);
}
const listen = url.host;
const hostname = url.hostname;
const port = sport !== "" ? parseInt(sport, 10) : parseInt(url.port, 10);

return { listen, hostname, port };
}
Expand Down Expand Up @@ -101,14 +133,17 @@ export class ServerImpl implements Server {
// don't add - to resolves or we get a circ reference
return [this];
} else {
// resolve the hostname to ips
const ips = await opts.fn(this.hostname);
for (const ip of ips) {

for (let ip of ips) {
// letting URL handle the details of representing IPV6 ip with a port, etc
const url = new URL(`http://${this.listen}`);
if (!url.port) {
url.port = `${DEFAULT_PORT}`;
}
url.hostname = ip;
// careful to make sure the protocol doesn't line with standard ports or they
// get swallowed
const proto = this.port === 80 ? "https" : "http";
// ipv6 won't be bracketed here, because it came from resolve
const url = new URL(`${proto}://${isIPV6(ip) ? "[" + ip + "]" : ip}`);
url.port = `${this.port}`;
const ss = new ServerImpl(url.host, false);
ss.tlsName = this.hostname;
buf.push(ss);
Expand Down
7 changes: 6 additions & 1 deletion tests/servers_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,15 @@ Deno.test("servers - save tls name", () => {
Deno.test("servers - port 80", () => {
function t(hp: string, port: number) {
let servers = new Servers([hp]);
assertEquals(servers.getCurrentServer().port, port);
const s = servers.getCurrentServer();
assertEquals(s.port, port);
}
t("localhost:80", 80);
t("localhost:443", 443);
t("localhost:201", 201);
t("localhost", 4222);
t("localhost/foo", 4222);
t("localhost:2342/foo", 2342);
t("[2001:db8:4006:812::200e]:8080", 8080);
t("::1", 4222);
});

0 comments on commit 09d03a1

Please sign in to comment.