Skip to content

Commit

Permalink
H-3792: Switch to dns.resolve in HaRPC (#5894)
Browse files Browse the repository at this point in the history
  • Loading branch information
indietyp authored and CiaranMn committed Jan 13, 2025
1 parent 28a755b commit 2ab3a31
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 46 deletions.
136 changes: 136 additions & 0 deletions libs/@local/harpc/client/typescript/src/net/internal/dns.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import dns from "node:dns/promises";
import { isIPv4, isIPv6 } from "node:net";

import { Array, Cause, Data, Duration, Effect, Function } from "effect";
import type { NonEmptyReadonlyArray } from "effect/Array";

/** @internal */
export class DnsError extends Data.TaggedError("DnsError")<{
cause: unknown;
}> {
get message() {
return "Underlying DNS resolver experienced an error";
}
}

/** @internal */
export type RecordType = "A" | "AAAA";

/** @internal */
export interface Ipv4AddressRecord {
type: "A";

address: string;
timeToLive: Duration.Duration;
}

/** @internal */
export interface Ipv6AddressRecord {
type: "AAAA";

address: string;
timeToLive: Duration.Duration;
}

/** @internal */
export type DnsRecord = Ipv4AddressRecord | Ipv6AddressRecord;

const resolveA = (hostname: string) =>
Effect.tryPromise({
try: () => dns.resolve4(hostname, { ttl: true }),
catch: (cause) => new DnsError({ cause }),
}).pipe(
Effect.map(
Array.map(
({ address, ttl }): Ipv4AddressRecord => ({
type: "A",

address,
timeToLive: Duration.seconds(ttl),
}),
),
),
);

const resolveAAAA = (hostname: string) =>
Effect.tryPromise({
try: () => dns.resolve6(hostname, { ttl: true }),
catch: (cause) => new DnsError({ cause }),
}).pipe(
Effect.map(
Array.map(
({ address, ttl }): Ipv6AddressRecord => ({
type: "AAAA",

address,
timeToLive: Duration.seconds(ttl),
}),
),
),
);

/** @internal */
export const resolve = (
hostname: string,
query: {
records: NonEmptyReadonlyArray<RecordType>;
},
) =>
Effect.gen(function* () {
const resolvers: Effect.Effect<DnsRecord[], DnsError>[] = [];

if (query.records.includes("A")) {
resolvers.push(resolveA(hostname));

if (isIPv4(hostname)) {
return [
{
type: "A",

address: hostname,
timeToLive: Duration.infinity,
} as DnsRecord,
];
}
}

if (query.records.includes("AAAA")) {
resolvers.push(resolveAAAA(hostname));

if (isIPv6(hostname)) {
return [
{
type: "AAAA",

address: hostname,
timeToLive: Duration.infinity,
} as DnsRecord,
];
}
}

if (resolvers.length === 0) {
return yield* new DnsError({
cause: new Error("No record types to resolve"),
});
}

const [excluded, satisfying] = yield* Effect.partition(
resolvers,
Function.identity,
{
concurrency: "unbounded",
},
);

if (satisfying.length === 0) {
// means that excluded is non empty

return yield* Effect.failCause(
// reduce without default is save here, because we guarantee non empty satisfying array
excluded.map(Cause.fail).reduce(Cause.parallel),
);
}

return Array.flatten(satisfying);
});
65 changes: 19 additions & 46 deletions libs/@local/harpc/client/typescript/src/net/internal/transport.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import type { LookupAddress } from "node:dns";
import dns from "node:dns/promises";
import { isIP, isIPv4, isIPv6 } from "node:net";

import { noise } from "@chainsafe/libp2p-noise";
import { yamux } from "@chainsafe/libp2p-yamux";
import type { Identify } from "@libp2p/identify";
Expand All @@ -14,7 +10,7 @@ import { type DNS, dns as defaultDns } from "@multiformats/dns";
import {
multiaddr as makeMultiaddr,
protocols as getProtocol,
resolvers,
resolvers as multiaddrResolvers,
} from "@multiformats/multiaddr";
import {
Array,
Expand All @@ -33,12 +29,14 @@ import {
Stream,
Struct,
} from "effect";
import type { NonEmptyArray } from "effect/Array";
import type { Libp2p } from "libp2p";
import { createLibp2p } from "libp2p";

import * as NetworkLogger from "../NetworkLogger.js";
import type { DNSConfig, Multiaddr, TransportConfig } from "../Transport.js";
import { InitializationError } from "../Transport.js";
import * as Dns from "./dns.js";
import * as HashableMultiaddr from "./multiaddr.js";

/** @internal */
Expand Down Expand Up @@ -68,15 +66,6 @@ export class TransportError extends Data.TaggedError("TransportError")<{
}
}

/** @internal */
export class DnsError extends Data.TaggedError("DnsError")<{
cause: unknown;
}> {
get message() {
return "Underlying DNS resolver experienced an error";
}
}

const DNS_PROTOCOL = getProtocol("dns");
const DNS4_PROTOCOL = getProtocol("dns4");
const DNS6_PROTOCOL = getProtocol("dns6");
Expand All @@ -100,46 +89,30 @@ const resolveDnsMultiaddrSegment = (code: number, value?: string) =>
}

const hostname = value;
const types: Dns.RecordType[] = [];

let family: 0 | 4 | 6;
if (code === DNS4_PROTOCOL.code) {
family = 4;

if (isIPv4(hostname)) {
return [[IPV4_PROTOCOL.code, hostname] as const];
}
} else if (code === DNS6_PROTOCOL.code) {
family = 6;

if (isIPv6(hostname)) {
return [[IPV6_PROTOCOL.code, hostname] as const];
}
} else {
family = 0;
if (code === DNS_PROTOCOL.code || code === DNS4_PROTOCOL.code) {
types.push("A");
}

const hostnameFamily = isIP(hostname);
if (hostnameFamily === 4) {
return [[IPV4_PROTOCOL.code, hostname] as const];
} else if (hostnameFamily === 6) {
return [[IPV6_PROTOCOL.code, hostname] as const];
}
if (code === DNS_PROTOCOL.code || code === DNS6_PROTOCOL.code) {
types.push("AAAA");
}

const responses = yield* Effect.tryPromise({
try: () => dns.lookup(hostname, { all: true, family }),
catch: (cause) => new DnsError({ cause }),
const records = yield* Dns.resolve(hostname, {
records: types as NonEmptyArray<Dns.RecordType>,
}).pipe(Effect.mapError((cause) => new TransportError({ cause })));

return pipe(
responses,
records,
Array.filterMap(
Match.type<LookupAddress>().pipe(
Match.type<Dns.DnsRecord>().pipe(
Match.when(
{ family: 4 },
{ type: "A" },
({ address }) => [IPV4_PROTOCOL.code, address] as const,
),
Match.when(
{ family: 6 },
{ type: "AAAA" },
({ address }) => [IPV6_PROTOCOL.code, address] as const,
),
Match.option,
Expand Down Expand Up @@ -189,7 +162,7 @@ const resolveDnsMultiaddr = (multiaddr: Multiaddr) => {
Effect.logDebug("resolved DNS multiaddr").pipe(
Effect.annotateLogs({
multiaddr: multiaddr.toString(),
resolved: resolved.map((address) => address.toString()),
resolved,
}),
),
),
Expand All @@ -204,7 +177,7 @@ const resolveDnsMultiaddr = (multiaddr: Multiaddr) => {
const resolveDnsaddrMultiaddr = (multiaddr: Multiaddr, options: DNSConfig) =>
Effect.gen(function* () {
// check multiaddr resolvers
const resolvable = Iterable.some(resolvers.keys(), (key) =>
const resolvable = Iterable.some(multiaddrResolvers.keys(), (key) =>
multiaddr.protoNames().includes(key),
);

Expand Down Expand Up @@ -268,7 +241,7 @@ const lookupPeer = (transport: Transport, address: Multiaddr) =>
Array.filter(
flow(
Struct.get("addresses"),
Array.unionWith(resolved, (a, b) => a.equals(b)),
Array.intersectionWith<Multiaddr>((a, b) => a.equals(b))(resolved),
Array.isNonEmptyArray,
),
),
Expand All @@ -278,7 +251,7 @@ const lookupPeer = (transport: Transport, address: Multiaddr) =>
Effect.annotateLogs({
known: addressesByPeer,
match: matchingPeers,
resolved: resolved.map((resolvedAddress) => resolvedAddress.toString()),
resolved,
}),
);

Expand Down

0 comments on commit 2ab3a31

Please sign in to comment.