diff options
-rw-r--r-- | packages/bun-types/dns.d.ts | 368 | ||||
-rw-r--r-- | packages/bun-types/dns/promises.d.ts | 86 | ||||
-rw-r--r-- | src/bun.js/api/bun/dns_resolver.zig | 759 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 27 | ||||
-rw-r--r-- | src/bun.js/node-dns.exports.js | 371 | ||||
-rw-r--r-- | src/bun.js/webcore/encoding.zig | 14 | ||||
-rw-r--r-- | src/deps/c_ares.zig | 489 | ||||
m--------- | src/deps/uws | 0 | ||||
-rw-r--r-- | test/bun.js/node-dns.test.js | 144 |
9 files changed, 1736 insertions, 522 deletions
diff --git a/packages/bun-types/dns.d.ts b/packages/bun-types/dns.d.ts index a51afe145..f5c24c7f2 100644 --- a/packages/bun-types/dns.d.ts +++ b/packages/bun-types/dns.d.ts @@ -318,127 +318,127 @@ declare module "dns" { hostname: string, callback: (err: ErrnoException | null, addresses: string[]) => void, ): void; - // export function resolve( - // hostname: string, - // rrtype: "A", - // callback: (err: ErrnoException | null, addresses: string[]) => void, - // ): void; - // export function resolve( - // hostname: string, - // rrtype: "AAAA", - // callback: (err: ErrnoException | null, addresses: string[]) => void, - // ): void; - // export function resolve( - // hostname: string, - // rrtype: "ANY", - // callback: ( - // err: ErrnoException | null, - // addresses: AnyRecord[], - // ) => void, - // ): void; - // export function resolve( - // hostname: string, - // rrtype: "CNAME", - // callback: (err: ErrnoException | null, addresses: string[]) => void, - // ): void; - // export function resolve( - // hostname: string, - // rrtype: "MX", - // callback: ( - // err: ErrnoException | null, - // addresses: MxRecord[], - // ) => void, - // ): void; - // export function resolve( - // hostname: string, - // rrtype: "NAPTR", - // callback: ( - // err: ErrnoException | null, - // addresses: NaptrRecord[], - // ) => void, - // ): void; - // export function resolve( - // hostname: string, - // rrtype: "NS", - // callback: (err: ErrnoException | null, addresses: string[]) => void, - // ): void; - // export function resolve( - // hostname: string, - // rrtype: "PTR", - // callback: (err: ErrnoException | null, addresses: string[]) => void, - // ): void; - // export function resolve( - // hostname: string, - // rrtype: "SOA", - // callback: (err: ErrnoException | null, addresses: SoaRecord) => void, - // ): void; - // export function resolve( - // hostname: string, - // rrtype: "SRV", - // callback: ( - // err: ErrnoException | null, - // addresses: SrvRecord[], - // ) => void, - // ): void; - // export function resolve( - // hostname: string, - // rrtype: "TXT", - // callback: ( - // err: ErrnoException | null, - // addresses: string[][], - // ) => void, - // ): void; - // export function resolve( - // hostname: string, - // rrtype: string, - // callback: ( - // err: ErrnoException | null, - // addresses: - // | string[] - // | MxRecord[] - // | NaptrRecord[] - // | SoaRecord - // | SrvRecord[] - // | string[][] - // | AnyRecord[], - // ) => void, - // ): void; - // export namespace resolve { - // function __promisify__( - // hostname: string, - // rrtype?: "A" | "AAAA" | "CNAME" | "NS" | "PTR", - // ): Promise<string[]>; - // function __promisify__( - // hostname: string, - // rrtype: "ANY", - // ): Promise<AnyRecord[]>; - // function __promisify__(hostname: string, rrtype: "MX"): Promise<MxRecord[]>; - // function __promisify__( - // hostname: string, - // rrtype: "NAPTR", - // ): Promise<NaptrRecord[]>; - // function __promisify__(hostname: string, rrtype: "SOA"): Promise<SoaRecord>; - // function __promisify__( - // hostname: string, - // rrtype: "SRV", - // ): Promise<SrvRecord[]>; - // function __promisify__( - // hostname: string, - // rrtype: "TXT", - // ): Promise<string[][]>; - // function __promisify__( - // hostname: string, - // rrtype: string, - // ): Promise< - // | string[] - // | MxRecord[] - // | NaptrRecord[] - // | SoaRecord - // | SrvRecord[] - // | string[][] - // | AnyRecord[] - // >; - // } + export function resolve( + hostname: string, + rrtype: "A", + callback: (err: ErrnoException | null, addresses: string[]) => void, + ): void; + export function resolve( + hostname: string, + rrtype: "AAAA", + callback: (err: ErrnoException | null, addresses: string[]) => void, + ): void; + export function resolve( + hostname: string, + rrtype: "ANY", + callback: ( + err: ErrnoException | null, + addresses: AnyRecord[], + ) => void, + ): void; + export function resolve( + hostname: string, + rrtype: "CNAME", + callback: (err: ErrnoException | null, addresses: string[]) => void, + ): void; + export function resolve( + hostname: string, + rrtype: "MX", + callback: ( + err: ErrnoException | null, + addresses: MxRecord[], + ) => void, + ): void; + export function resolve( + hostname: string, + rrtype: "NAPTR", + callback: ( + err: ErrnoException | null, + addresses: NaptrRecord[], + ) => void, + ): void; + export function resolve( + hostname: string, + rrtype: "NS", + callback: (err: ErrnoException | null, addresses: string[]) => void, + ): void; + export function resolve( + hostname: string, + rrtype: "PTR", + callback: (err: ErrnoException | null, addresses: string[]) => void, + ): void; + export function resolve( + hostname: string, + rrtype: "SOA", + callback: (err: ErrnoException | null, addresses: SoaRecord) => void, + ): void; + export function resolve( + hostname: string, + rrtype: "SRV", + callback: ( + err: ErrnoException | null, + addresses: SrvRecord[], + ) => void, + ): void; + export function resolve( + hostname: string, + rrtype: "TXT", + callback: ( + err: ErrnoException | null, + addresses: string[][], + ) => void, + ): void; + export function resolve( + hostname: string, + rrtype: string, + callback: ( + err: ErrnoException | null, + addresses: + | string[] + | MxRecord[] + | NaptrRecord[] + | SoaRecord + | SrvRecord[] + | string[][] + | AnyRecord[], + ) => void, + ): void; + export namespace resolve { + function __promisify__( + hostname: string, + rrtype?: "A" | "AAAA" | "CNAME" | "NS" | "PTR", + ): Promise<string[]>; + function __promisify__( + hostname: string, + rrtype: "ANY", + ): Promise<AnyRecord[]>; + function __promisify__(hostname: string, rrtype: "MX"): Promise<MxRecord[]>; + function __promisify__( + hostname: string, + rrtype: "NAPTR", + ): Promise<NaptrRecord[]>; + function __promisify__(hostname: string, rrtype: "SOA"): Promise<SoaRecord>; + function __promisify__( + hostname: string, + rrtype: "SRV", + ): Promise<SrvRecord[]>; + function __promisify__( + hostname: string, + rrtype: "TXT", + ): Promise<string[][]>; + function __promisify__( + hostname: string, + rrtype: string, + ): Promise< + | string[] + | MxRecord[] + | NaptrRecord[] + | SoaRecord + | SrvRecord[] + | string[][] + | AnyRecord[] + >; + } /** * Uses the DNS protocol to resolve a IPv4 addresses (`A` records) for the`hostname`. The `addresses` argument passed to the `callback` function * will contain an array of IPv4 addresses (e.g.`['74.125.79.104', '74.125.79.105', '74.125.79.106']`). @@ -512,38 +512,38 @@ declare module "dns" { * will contain an array of canonical name records available for the `hostname`(e.g. `['bar.example.com']`). * @since v0.3.2 */ - // export function resolveCname( - // hostname: string, - // callback: (err: ErrnoException | null, addresses: string[]) => void, - // ): void; - // export namespace resolveCname { - // function __promisify__(hostname: string): Promise<string[]>; - // } + export function resolveCname( + hostname: string, + callback: (err: ErrnoException | null, addresses: string[]) => void, + ): void; + export namespace resolveCname { + function __promisify__(hostname: string): Promise<string[]>; + } /** * Uses the DNS protocol to resolve `CAA` records for the `hostname`. The`addresses` argument passed to the `callback` function * will contain an array of certification authority authorization records * available for the `hostname` (e.g. `[{critical: 0, iodef: 'mailto:pki@example.com'}, {critical: 128, issue: 'pki.example.com'}]`). * @since v15.0.0, v14.17.0 */ - // export function resolveCaa( - // hostname: string, - // callback: (err: ErrnoException | null, records: CaaRecord[]) => void, - // ): void; - // export namespace resolveCaa { - // function __promisify__(hostname: string): Promise<CaaRecord[]>; - // } + export function resolveCaa( + hostname: string, + callback: (err: ErrnoException | null, records: CaaRecord[]) => void, + ): void; + export namespace resolveCaa { + function __promisify__(hostname: string): Promise<CaaRecord[]>; + } /** * Uses the DNS protocol to resolve mail exchange records (`MX` records) for the`hostname`. The `addresses` argument passed to the `callback` function will * contain an array of objects containing both a `priority` and `exchange`property (e.g. `[{priority: 10, exchange: 'mx.example.com'}, ...]`). * @since v0.1.27 */ - // export function resolveMx( - // hostname: string, - // callback: (err: ErrnoException | null, addresses: MxRecord[]) => void, - // ): void; - // export namespace resolveMx { - // function __promisify__(hostname: string): Promise<MxRecord[]>; - // } + export function resolveMx( + hostname: string, + callback: (err: ErrnoException | null, addresses: MxRecord[]) => void, + ): void; + export namespace resolveMx { + function __promisify__(hostname: string): Promise<MxRecord[]>; + } /** * Uses the DNS protocol to resolve regular expression based records (`NAPTR`records) for the `hostname`. The `addresses` argument passed to the `callback`function will contain an array of * objects with the following properties: @@ -567,37 +567,37 @@ declare module "dns" { * ``` * @since v0.9.12 */ - // export function resolveNaptr( - // hostname: string, - // callback: (err: ErrnoException | null, addresses: NaptrRecord[]) => void, - // ): void; - // export namespace resolveNaptr { - // function __promisify__(hostname: string): Promise<NaptrRecord[]>; - // } + export function resolveNaptr( + hostname: string, + callback: (err: ErrnoException | null, addresses: NaptrRecord[]) => void, + ): void; + export namespace resolveNaptr { + function __promisify__(hostname: string): Promise<NaptrRecord[]>; + } /** * Uses the DNS protocol to resolve name server records (`NS` records) for the`hostname`. The `addresses` argument passed to the `callback` function will * contain an array of name server records available for `hostname`(e.g. `['ns1.example.com', 'ns2.example.com']`). * @since v0.1.90 */ - // export function resolveNs( - // hostname: string, - // callback: (err: ErrnoException | null, addresses: string[]) => void, - // ): void; - // export namespace resolveNs { - // function __promisify__(hostname: string): Promise<string[]>; - // } + export function resolveNs( + hostname: string, + callback: (err: ErrnoException | null, addresses: string[]) => void, + ): void; + export namespace resolveNs { + function __promisify__(hostname: string): Promise<string[]>; + } /** * Uses the DNS protocol to resolve pointer records (`PTR` records) for the`hostname`. The `addresses` argument passed to the `callback` function will * be an array of strings containing the reply records. * @since v6.0.0 */ - // export function resolvePtr( - // hostname: string, - // callback: (err: ErrnoException | null, addresses: string[]) => void, - // ): void; - // export namespace resolvePtr { - // function __promisify__(hostname: string): Promise<string[]>; - // } + export function resolvePtr( + hostname: string, + callback: (err: ErrnoException | null, addresses: string[]) => void, + ): void; + export namespace resolvePtr { + function __promisify__(hostname: string): Promise<string[]>; + } /** * Uses the DNS protocol to resolve a start of authority record (`SOA` record) for * the `hostname`. The `address` argument passed to the `callback` function will @@ -624,13 +624,13 @@ declare module "dns" { * ``` * @since v0.11.10 */ - // export function resolveSoa( - // hostname: string, - // callback: (err: ErrnoException | null, address: SoaRecord) => void, - // ): void; - // export namespace resolveSoa { - // function __promisify__(hostname: string): Promise<SoaRecord>; - // } + export function resolveSoa( + hostname: string, + callback: (err: ErrnoException | null, address: SoaRecord) => void, + ): void; + export namespace resolveSoa { + function __promisify__(hostname: string): Promise<SoaRecord>; + } /** * Uses the DNS protocol to resolve service records (`SRV` records) for the`hostname`. The `addresses` argument passed to the `callback` function will * be an array of objects with the following properties: @@ -664,13 +664,13 @@ declare module "dns" { * treated separately. * @since v0.1.27 */ - // export function resolveTxt( - // hostname: string, - // callback: (err: ErrnoException | null, addresses: string[][]) => void, - // ): void; - // export namespace resolveTxt { - // function __promisify__(hostname: string): Promise<string[][]>; - // } + export function resolveTxt( + hostname: string, + callback: (err: ErrnoException | null, addresses: string[][]) => void, + ): void; + export namespace resolveTxt { + function __promisify__(hostname: string): Promise<string[][]>; + } /** * Uses the DNS protocol to resolve all records (also known as `ANY` or `*` query). * The `ret` argument passed to the `callback` function will be an array containing @@ -861,14 +861,14 @@ declare module "dns" { resolve4: typeof resolve4; resolve6: typeof resolve6; // resolveAny: typeof resolveAny; - // resolveCname: typeof resolveCname; - // resolveMx: typeof resolveMx; - // resolveNaptr: typeof resolveNaptr; - // resolveNs: typeof resolveNs; - // resolvePtr: typeof resolvePtr; - // resolveSoa: typeof resolveSoa; + resolveCname: typeof resolveCname; + resolveMx: typeof resolveMx; + resolveNaptr: typeof resolveNaptr; + resolveNs: typeof resolveNs; + resolvePtr: typeof resolvePtr; + resolveSoa: typeof resolveSoa; resolveSrv: typeof resolveSrv; - // resolveTxt: typeof resolveTxt; + resolveTxt: typeof resolveTxt; // reverse: typeof reverse; /** * The resolver instance will send its requests from the specified IP address. diff --git a/packages/bun-types/dns/promises.d.ts b/packages/bun-types/dns/promises.d.ts index 2b84f2c0f..7f5faad75 100644 --- a/packages/bun-types/dns/promises.d.ts +++ b/packages/bun-types/dns/promises.d.ts @@ -10,11 +10,11 @@ declare module "dns/promises" { LookupOneOptions, LookupAllOptions, LookupOptions, - // AnyRecord, - // CaaRecord, - // MxRecord, - // NaptrRecord, - // SoaRecord, + AnyRecord, + CaaRecord, + MxRecord, + NaptrRecord, + SoaRecord, SrvRecord, ResolveWithTtlOptions, RecordWithTtl, @@ -134,30 +134,30 @@ declare module "dns/promises" { * @param [rrtype='A'] Resource record type. */ function resolve(hostname: string): Promise<string[]>; - // function resolve(hostname: string, rrtype: "A"): Promise<string[]>; - // function resolve(hostname: string, rrtype: "AAAA"): Promise<string[]>; - // function resolve(hostname: string, rrtype: "ANY"): Promise<AnyRecord[]>; - // function resolve(hostname: string, rrtype: "CAA"): Promise<CaaRecord[]>; - // function resolve(hostname: string, rrtype: "CNAME"): Promise<string[]>; - // function resolve(hostname: string, rrtype: "MX"): Promise<MxRecord[]>; - // function resolve(hostname: string, rrtype: "NAPTR"): Promise<NaptrRecord[]>; - // function resolve(hostname: string, rrtype: "NS"): Promise<string[]>; - // function resolve(hostname: string, rrtype: "PTR"): Promise<string[]>; - // function resolve(hostname: string, rrtype: "SOA"): Promise<SoaRecord>; + function resolve(hostname: string, rrtype: "A"): Promise<string[]>; + function resolve(hostname: string, rrtype: "AAAA"): Promise<string[]>; + function resolve(hostname: string, rrtype: "ANY"): Promise<AnyRecord[]>; + function resolve(hostname: string, rrtype: "CAA"): Promise<CaaRecord[]>; + function resolve(hostname: string, rrtype: "CNAME"): Promise<string[]>; + function resolve(hostname: string, rrtype: "MX"): Promise<MxRecord[]>; + function resolve(hostname: string, rrtype: "NAPTR"): Promise<NaptrRecord[]>; + function resolve(hostname: string, rrtype: "NS"): Promise<string[]>; + function resolve(hostname: string, rrtype: "PTR"): Promise<string[]>; + function resolve(hostname: string, rrtype: "SOA"): Promise<SoaRecord>; function resolveSrv(hostname: string): Promise<SrvRecord[]>; - // function resolve(hostname: string, rrtype: "TXT"): Promise<string[][]>; - // function resolve( - // hostname: string, - // rrtype: string, - // ): Promise< - // | string[] - // | MxRecord[] - // | NaptrRecord[] - // | SoaRecord - // | SrvRecord[] - // | string[][] - // | AnyRecord[] - // >; + function resolve(hostname: string, rrtype: "TXT"): Promise<string[][]>; + function resolve( + hostname: string, + rrtype: string, + ): Promise< + | string[] + | MxRecord[] + | NaptrRecord[] + | SoaRecord + | SrvRecord[] + | string[][] + | AnyRecord[] + >; /** * Uses the DNS protocol to resolve IPv4 addresses (`A` records) for the`hostname`. On success, the `Promise` is resolved with an array of IPv4 * addresses (e.g. `['74.125.79.104', '74.125.79.105', '74.125.79.106']`). @@ -223,20 +223,20 @@ declare module "dns/promises" { * certification authority authorization records available for the `hostname`(e.g. `[{critical: 0, iodef: 'mailto:pki@example.com'},{critical: 128, issue: 'pki.example.com'}]`). * @since v15.0.0, v14.17.0 */ - // function resolveCaa(hostname: string): Promise<CaaRecord[]>; + function resolveCaa(hostname: string): Promise<CaaRecord[]>; /** * Uses the DNS protocol to resolve `CNAME` records for the `hostname`. On success, * the `Promise` is resolved with an array of canonical name records available for * the `hostname` (e.g. `['bar.example.com']`). * @since v10.6.0 */ - // function resolveCname(hostname: string): Promise<string[]>; + function resolveCname(hostname: string): Promise<string[]>; /** * Uses the DNS protocol to resolve mail exchange records (`MX` records) for the`hostname`. On success, the `Promise` is resolved with an array of objects * containing both a `priority` and `exchange` property (e.g.`[{priority: 10, exchange: 'mx.example.com'}, ...]`). * @since v10.6.0 */ - // function resolveMx(hostname: string): Promise<MxRecord[]>; + function resolveMx(hostname: string): Promise<MxRecord[]>; /** * Uses the DNS protocol to resolve regular expression based records (`NAPTR`records) for the `hostname`. On success, the `Promise` is resolved with an array * of objects with the following properties: @@ -260,19 +260,19 @@ declare module "dns/promises" { * ``` * @since v10.6.0 */ - // function resolveNaptr(hostname: string): Promise<NaptrRecord[]>; + function resolveNaptr(hostname: string): Promise<NaptrRecord[]>; /** * Uses the DNS protocol to resolve name server records (`NS` records) for the`hostname`. On success, the `Promise` is resolved with an array of name server * records available for `hostname` (e.g.`['ns1.example.com', 'ns2.example.com']`). * @since v10.6.0 */ - // function resolveNs(hostname: string): Promise<string[]>; + function resolveNs(hostname: string): Promise<string[]>; /** * Uses the DNS protocol to resolve pointer records (`PTR` records) for the`hostname`. On success, the `Promise` is resolved with an array of strings * containing the reply records. * @since v10.6.0 */ - // function resolvePtr(hostname: string): Promise<string[]>; + function resolvePtr(hostname: string): Promise<string[]>; /** * Uses the DNS protocol to resolve a start of authority record (`SOA` record) for * the `hostname`. On success, the `Promise` is resolved with an object with the @@ -299,7 +299,7 @@ declare module "dns/promises" { * ``` * @since v10.6.0 */ - // function resolveSoa(hostname: string): Promise<SoaRecord>; + function resolveSoa(hostname: string): Promise<SoaRecord>; /** * Uses the DNS protocol to resolve service records (`SRV` records) for the`hostname`. On success, the `Promise` is resolved with an array of objects with * the following properties: @@ -327,7 +327,7 @@ declare module "dns/promises" { * treated separately. * @since v10.6.0 */ - // function resolveTxt(hostname: string): Promise<string[][]>; + function resolveTxt(hostname: string): Promise<string[][]>; /** * Performs a reverse DNS query that resolves an IPv4 or IPv6 address to an * array of host names. @@ -384,14 +384,14 @@ declare module "dns/promises" { resolve4: typeof resolve4; resolve6: typeof resolve6; // resolveAny: typeof resolveAny; - // resolveCname: typeof resolveCname; - // resolveMx: typeof resolveMx; - // resolveNaptr: typeof resolveNaptr; - // resolveNs: typeof resolveNs; - // resolvePtr: typeof resolvePtr; - // resolveSoa: typeof resolveSoa; + resolveCname: typeof resolveCname; + resolveMx: typeof resolveMx; + resolveNaptr: typeof resolveNaptr; + resolveNs: typeof resolveNs; + resolvePtr: typeof resolvePtr; + resolveSoa: typeof resolveSoa; resolveSrv: typeof resolveSrv; - // resolveTxt: typeof resolveTxt; + resolveTxt: typeof resolveTxt; // reverse: typeof reverse; // setLocalAddress(ipv4?: string, ipv6?: string): void; // setServers: typeof setServers; diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig index 92577d556..08202a09e 100644 --- a/src/bun.js/api/bun/dns_resolver.zig +++ b/src/bun.js/api/bun/dns_resolver.zig @@ -614,104 +614,104 @@ pub const GetAddrInfo = struct { }; }; -pub const ResolveSrvInfoRequest = struct { - const log = Output.scoped(.ResolveSrvInfoRequest, false); - - resolver_for_caching: ?*DNSResolver = null, - hash: u64 = 0, - cache: ResolveSrvInfoRequest.CacheConfig = ResolveSrvInfoRequest.CacheConfig{}, - head: SrvLookup, - tail: *SrvLookup = undefined, - task: bun.ThreadPool.Task = undefined, - - pub fn init( - cache: DNSResolver.SrvCacheHit, - resolver: ?*DNSResolver, - name: []const u8, - globalThis: *JSC.JSGlobalObject, - comptime cache_field: []const u8, - ) !*ResolveSrvInfoRequest { - var request = try globalThis.allocator().create(ResolveSrvInfoRequest); - var hasher = std.hash.Wyhash.init(0); - hasher.update(name); - const hash = hasher.final(); - var poll_ref = JSC.PollRef.init(); - poll_ref.ref(globalThis.bunVM()); - request.* = .{ - .resolver_for_caching = resolver, - .hash = hash, - .head = .{ - .poll_ref = poll_ref, - .globalThis = globalThis, - .promise = JSC.JSPromise.Strong.init(globalThis), - .allocated = false, - }, - }; - request.tail = &request.head; - if (cache == .new) { - request.resolver_for_caching = resolver; - request.cache = ResolveSrvInfoRequest.CacheConfig{ - .pending_cache = true, - .entry_cache = false, - .pos_in_pending = @truncate(u5, @field(resolver.?, cache_field).indexOf(cache.new).?), - .name_len = @truncate(u9, name.len), - }; - cache.new.lookup = request; - } - return request; - } - - pub const Task = bun.JSC.WorkTask(ResolveSrvInfoRequest, false); - - pub const CacheConfig = packed struct(u16) { - pending_cache: bool = false, - entry_cache: bool = false, - pos_in_pending: u5 = 0, - name_len: u9 = 0, - }; - - pub const PendingCacheKey = struct { - hash: u64, - len: u16, - lookup: *ResolveSrvInfoRequest = undefined, - - pub fn append(this: *PendingCacheKey, srv_lookup: *SrvLookup) void { - var tail = this.lookup.tail; - tail.next = srv_lookup; - this.lookup.tail = srv_lookup; - } - - pub fn init(name: []const u8) PendingCacheKey { +pub fn ResolveInfoRequest(comptime cares_type: type, comptime type_name: []const u8) type { + return struct { + const request_type = @This(); + + const log = Output.scoped(@This(), false); + + resolver_for_caching: ?*DNSResolver = null, + hash: u64 = 0, + cache: @This().CacheConfig = @This().CacheConfig{}, + head: CAresLookup(cares_type, type_name), + tail: *CAresLookup(cares_type, type_name) = undefined, + + pub fn init( + cache: DNSResolver.LookupCacheHit(@This()), + resolver: ?*DNSResolver, + name_str: *const JSC.ZigString.Slice, + globalThis: *JSC.JSGlobalObject, + comptime cache_field: []const u8, + ) !*@This() { + const name = name_str.slice(); + var request = try globalThis.allocator().create(@This()); var hasher = std.hash.Wyhash.init(0); hasher.update(name); const hash = hasher.final(); - return PendingCacheKey{ + var poll_ref = JSC.PollRef.init(); + poll_ref.ref(globalThis.bunVM()); + request.* = .{ + .resolver_for_caching = resolver, .hash = hash, - .len = @truncate(u16, name.len), - .lookup = undefined, + .head = .{ .poll_ref = poll_ref, .globalThis = globalThis, .promise = JSC.JSPromise.Strong.init(globalThis), .allocated = false, .name = name_str }, }; + request.tail = &request.head; + if (cache == .new) { + request.resolver_for_caching = resolver; + request.cache = @This().CacheConfig{ + .pending_cache = true, + .entry_cache = false, + .pos_in_pending = @truncate(u5, @field(resolver.?, cache_field).indexOf(cache.new).?), + .name_len = @truncate(u9, name.len), + }; + cache.new.lookup = request; + } + return request; } - }; - pub fn onCaresComplete(this: *ResolveSrvInfoRequest, err_: ?c_ares.Error, timeout: i32, result: ?*c_ares.struct_ares_srv_reply) void { - if (this.resolver_for_caching) |resolver| { - if (this.cache.pending_cache) { - resolver.drainPendingSrvCares( - this.cache.pos_in_pending, - err_, - timeout, - result, - ); - return; + pub const CacheConfig = packed struct(u16) { + pending_cache: bool = false, + entry_cache: bool = false, + pos_in_pending: u5 = 0, + name_len: u9 = 0, + }; + + pub const PendingCacheKey = struct { + hash: u64, + len: u16, + lookup: *request_type = undefined, + + pub fn append(this: *PendingCacheKey, cares_lookup: *CAresLookup(cares_type, type_name)) void { + var tail = this.lookup.tail; + tail.next = cares_lookup; + this.lookup.tail = cares_lookup; } - } - var head = this.head; - bun.default_allocator.destroy(this); + pub fn init(name: []const u8) PendingCacheKey { + var hasher = std.hash.Wyhash.init(0); + hasher.update(name); + const hash = hasher.final(); + return PendingCacheKey{ + .hash = hash, + .len = @truncate(u16, name.len), + .lookup = undefined, + }; + } + }; - head.processResolveSrv(err_, timeout, result); - } -}; + pub fn onCaresComplete(this: *@This(), err_: ?c_ares.Error, timeout: i32, result: ?*cares_type) void { + if (this.resolver_for_caching) |resolver| { + if (this.cache.pending_cache) { + resolver.drainPendingCares( + this.cache.pos_in_pending, + err_, + timeout, + @This(), + cares_type, + type_name, + result, + ); + return; + } + } + + var head = this.head; + bun.default_allocator.destroy(this); + + head.processResolve(err_, timeout, result); + } + }; +} pub const GetAddrInfoRequest = struct { const log = Output.scoped(.GetAddrInfoRequest, false); @@ -934,79 +934,78 @@ pub const GetAddrInfoRequest = struct { } }; -pub const SrvLookup = struct { - const log = Output.scoped(.SrvLookup, true); - - globalThis: *JSC.JSGlobalObject = undefined, - promise: JSC.JSPromise.Strong, - poll_ref: JSC.PollRef, - allocated: bool = false, - next: ?*SrvLookup = null, +pub fn CAresLookup(comptime cares_type: type, comptime type_name: []const u8) type { + return struct { + const log = Output.scoped(@This(), true); + + globalThis: *JSC.JSGlobalObject = undefined, + promise: JSC.JSPromise.Strong, + poll_ref: JSC.PollRef, + allocated: bool = false, + next: ?*@This() = null, + name: *const JSC.ZigString.Slice, + + pub fn init(globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator, name: *const JSC.ZigString.Slice) !*@This() { + var this = try allocator.create(@This()); + var poll_ref = JSC.PollRef.init(); + poll_ref.ref(globalThis.bunVM()); + this.* = .{ .globalThis = globalThis, .promise = JSC.JSPromise.Strong.init(globalThis), .poll_ref = poll_ref, .allocated = true, .name = name }; + return this; + } - pub fn init(globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) !*SrvLookup { - var this = try allocator.create(SrvLookup); - var poll_ref = JSC.PollRef.init(); - poll_ref.ref(globalThis.bunVM()); - this.* = .{ - .globalThis = globalThis, - .promise = JSC.JSPromise.Strong.init(globalThis), - .poll_ref = poll_ref, - .allocated = true, - }; - return this; - } + pub fn processResolve(this: *@This(), err_: ?c_ares.Error, _: i32, result: ?*cares_type) void { + if (err_) |err| { + var promise = this.promise; + var globalThis = this.globalThis; + const error_value = globalThis.createErrorInstance("{s} lookup failed: {s}", .{ type_name, err.label() }); + error_value.put( + globalThis, + JSC.ZigString.static("code"), + JSC.ZigString.init(err.code()).toValueGC(globalThis), + ); - pub fn processResolveSrv(this: *SrvLookup, err_: ?c_ares.Error, _: i32, result: ?*c_ares.struct_ares_srv_reply) void { - if (err_) |err| { - var promise = this.promise; - var globalThis = this.globalThis; - const error_value = globalThis.createErrorInstance("SRV lookup failed: {s}", .{err.label()}); - error_value.put( - globalThis, - JSC.ZigString.static("code"), - JSC.ZigString.init(err.code()).toValueGC(globalThis), - ); + promise.reject(globalThis, error_value); + this.deinit(); + return; + } + if (result == null) { + var promise = this.promise; + var globalThis = this.globalThis; + const error_value = globalThis.createErrorInstance("{s} lookup failed: {s}", .{ type_name, "No results" }); + error_value.put( + globalThis, + JSC.ZigString.static("code"), + JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis), + ); - promise.reject(globalThis, error_value); - this.deinit(); + promise.reject(globalThis, error_value); + this.deinit(); + return; + } + var node = result.?; + const array = node.toJSReponse(this.globalThis.allocator(), this.globalThis, type_name); + this.onComplete(array); return; } - if (result == null or result.?.next == null) { + pub fn onComplete(this: *@This(), result: JSC.JSValue) void { var promise = this.promise; var globalThis = this.globalThis; - const error_value = globalThis.createErrorInstance("SRV lookup failed: {s}", .{"No results"}); - error_value.put( - globalThis, - JSC.ZigString.static("code"), - JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis), - ); - - promise.reject(globalThis, error_value); + this.promise = .{}; + promise.resolve(globalThis, result); this.deinit(); - return; } - var node = result.?; - const array = node.toJSArray(this.globalThis.allocator(), this.globalThis); - this.onComplete(array); - return; - } - pub fn onComplete(this: *SrvLookup, result: JSC.JSValue) void { - var promise = this.promise; - var globalThis = this.globalThis; - this.promise = .{}; - promise.resolve(globalThis, result); - this.deinit(); - } + pub fn deinit(this: *@This()) void { + this.poll_ref.unrefOnNextTick(this.globalThis.bunVM()); + this.name.deinit(); - pub fn deinit(this: *SrvLookup) void { - this.poll_ref.unrefOnNextTick(this.globalThis.bunVM()); + if (this.allocated) + this.globalThis.allocator().destroy(this); + } + }; +} - if (this.allocated) - this.globalThis.allocator().destroy(this); - } -}; pub const DNSLookup = struct { const log = Output.scoped(.DNSLookup, true); @@ -1141,15 +1140,30 @@ pub const DNSResolver = struct { polls: std.AutoArrayHashMap(i32, ?*JSC.FilePoll) = undefined, pending_host_cache_cares: PendingCache = PendingCache.init(), - pending_srv_cache_cares: SrvPendingCache = SrvPendingCache.init(), pending_host_cache_native: PendingCache = PendingCache.init(), - // entry_host_cache: std.BoundedArray(128) + pending_srv_cache_cares: SrvPendingCache = SrvPendingCache.init(), + pending_soa_cache_cares: SoaPendingCache = SoaPendingCache.init(), + pending_txt_cache_cares: TxtPendingCache = TxtPendingCache.init(), + pending_naptr_cache_cares: NaptrPendingCache = NaptrPendingCache.init(), + pending_mx_cache_cares: MxPendingCache = MxPendingCache.init(), + pending_caa_cache_cares: CaaPendingCache = CaaPendingCache.init(), + pending_ns_cache_cares: NSPendingCache = NSPendingCache.init(), + pending_ptr_cache_cares: PtrPendingCache = PtrPendingCache.init(), + pending_cname_cache_cares: CnamePendingCache = CnamePendingCache.init(), const PendingCache = bun.HiveArray(GetAddrInfoRequest.PendingCacheKey, 32); - const SrvPendingCache = bun.HiveArray(ResolveSrvInfoRequest.PendingCacheKey, 32); - - fn getKey(this: *DNSResolver, index: u8, comptime cache_name: []const u8) GetAddrInfoRequest.PendingCacheKey { - var cache: *PendingCache = &@field(this, cache_name); + const SrvPendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_ares_srv_reply, "srv").PendingCacheKey, 32); + const SoaPendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_ares_soa_reply, "soa").PendingCacheKey, 32); + const TxtPendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_ares_txt_reply, "txt").PendingCacheKey, 32); + const NaptrPendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_ares_naptr_reply, "naptr").PendingCacheKey, 32); + const MxPendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_ares_mx_reply, "mx").PendingCacheKey, 32); + const CaaPendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_ares_caa_reply, "caa").PendingCacheKey, 32); + const NSPendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_hostent, "ns").PendingCacheKey, 32); + const PtrPendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_hostent, "ptr").PendingCacheKey, 32); + const CnamePendingCache = bun.HiveArray(ResolveInfoRequest(c_ares.struct_hostent, "cname").PendingCacheKey, 32); + + fn getKey(this: *DNSResolver, index: u8, comptime cache_name: []const u8, comptime request_type: type) request_type.PendingCacheKey { + var cache = &@field(this, cache_name); std.debug.assert(!cache.available.isSet(index)); const entry = cache.buffer[index]; cache.buffer[index] = undefined; @@ -1160,37 +1174,27 @@ pub const DNSResolver = struct { return entry; } - fn getSrvKey(this: *DNSResolver, index: u8, comptime cache_name: []const u8) ResolveSrvInfoRequest.PendingCacheKey { - var cache: *SrvPendingCache = &@field(this, cache_name); - std.debug.assert(!cache.available.isSet(index)); - const entry = cache.buffer[index]; - cache.buffer[index] = undefined; - var available = cache.available; - available.set(index); - cache.available = available; - - return entry; - } + pub fn drainPendingCares(this: *DNSResolver, index: u8, err: ?c_ares.Error, timeout: i32, comptime request_type: type, comptime cares_type: type, comptime lookup_name: []const u8, result: ?*cares_type) void { + const cache_name = comptime std.fmt.comptimePrint("pending_{s}_cache_cares", .{lookup_name}); - pub fn drainPendingSrvCares(this: *DNSResolver, index: u8, err: ?c_ares.Error, timeout: i32, result: ?*c_ares.struct_ares_srv_reply) void { - const key = this.getSrvKey(index, "pending_srv_cache_cares"); + const key = this.getKey(index, cache_name, request_type); var addr = result orelse { - var pending: ?*SrvLookup = key.lookup.head.next; - key.lookup.head.processResolveSrv(err, timeout, null); + var pending: ?*CAresLookup(cares_type, lookup_name) = key.lookup.head.next; + key.lookup.head.processResolve(err, timeout, null); bun.default_allocator.destroy(key.lookup); while (pending) |value| { pending = value.next; - value.processResolveSrv(err, timeout, null); + value.processResolve(err, timeout, null); } return; }; - var pending: ?*SrvLookup = key.lookup.head.next; + var pending: ?*CAresLookup(cares_type, lookup_name) = key.lookup.head.next; var prev_global = key.lookup.head.globalThis; - var array = addr.toJSArray(this.vm.allocator, prev_global); + var array = addr.toJSReponse(this.vm.allocator, prev_global, lookup_name); defer addr.deinit(); array.ensureStillAlive(); key.lookup.head.onComplete(array); @@ -1201,7 +1205,7 @@ pub const DNSResolver = struct { while (pending) |value| { var new_global = value.globalThis; if (prev_global != new_global) { - array = addr.toJSArray(this.vm.allocator, new_global); + array = addr.toJSReponse(this.vm.allocator, new_global, lookup_name); prev_global = new_global; } pending = value.next; @@ -1215,7 +1219,7 @@ pub const DNSResolver = struct { } pub fn drainPendingHostCares(this: *DNSResolver, index: u8, err: ?c_ares.Error, timeout: i32, result: ?*c_ares.AddrInfo) void { - const key = this.getKey(index, "pending_host_cache_cares"); + const key = this.getKey(index, "pending_host_cache_cares", GetAddrInfoRequest); var addr = result orelse { var pending: ?*DNSLookup = key.lookup.head.next; @@ -1257,7 +1261,7 @@ pub const DNSResolver = struct { } pub fn drainPendingHostNative(this: *DNSResolver, index: u8, globalObject: *JSC.JSGlobalObject, err: i32, result: GetAddrInfo.Result.Any) void { - const key = this.getKey(index, "pending_host_cache_native"); + const key = this.getKey(index, "pending_host_cache_native", GetAddrInfoRequest); var array = result.toJS(globalObject) orelse { var pending: ?*DNSLookup = key.lookup.head.next; @@ -1305,18 +1309,22 @@ pub const DNSResolver = struct { new: *GetAddrInfoRequest.PendingCacheKey, disabled: void, }; - pub const SrvCacheHit = union(enum) { - inflight: *ResolveSrvInfoRequest.PendingCacheKey, - new: *ResolveSrvInfoRequest.PendingCacheKey, - disabled: void, - }; - pub fn getOrPutIntoSrvPendingCache( + pub fn LookupCacheHit(comptime request_type: type) type { + return union(enum) { + inflight: *request_type.PendingCacheKey, + new: *request_type.PendingCacheKey, + disabled: void, + }; + } + + pub fn getOrPutIntoResolvePendingCache( this: *DNSResolver, - key: ResolveSrvInfoRequest.PendingCacheKey, - comptime field: std.meta.FieldEnum(DNSResolver), - ) SrvCacheHit { - var cache: *SrvPendingCache = &@field(this, @tagName(field)); + comptime request_type: type, + key: request_type.PendingCacheKey, + comptime field: []const u8, + ) LookupCacheHit(request_type) { + var cache = &@field(this, field); var inflight_iter = cache.available.iterator( .{ .kind = .unset, @@ -1324,7 +1332,7 @@ pub const DNSResolver = struct { ); while (inflight_iter.next()) |index| { - var entry: *ResolveSrvInfoRequest.PendingCacheKey = &cache.buffer[index]; + var entry: *request_type.PendingCacheKey = &cache.buffer[index]; if (entry.hash == key.hash and entry.len == key.len) { return .{ .inflight = entry }; } @@ -1504,7 +1512,6 @@ pub const DNSResolver = struct { return .zero; }; }; - _ = record_type; const name_value = arguments.ptr[0]; @@ -1522,9 +1529,44 @@ pub const DNSResolver = struct { return .zero; } - // const name = name_str.toSliceZ(globalThis).cloneZ(bun.default_allocator) catch unreachable; - // TODO: - return JSC.JSValue.jsUndefined(); + const name = name_str.toSlice(globalThis, bun.default_allocator); + + var vm = globalThis.bunVM(); + var resolver = vm.rareData().globalDNSResolver(vm); + //TODO: ANY CASE + switch (record_type) { + RecordType.A => { + defer name.deinit(); + const options = GetAddrInfo.Options{ .family = GetAddrInfo.Family.inet }; + return resolver.doLookup(name.slice(), 0, options, globalThis); + }, + RecordType.AAAA => { + defer name.deinit(); + const options = GetAddrInfo.Options{ .family = GetAddrInfo.Family.inet6 }; + return resolver.doLookup(name.slice(), 0, options, globalThis); + }, + RecordType.CNAME => { + return resolver.doResolveCAres(c_ares.struct_hostent, "cname", &name, globalThis); + }, + RecordType.MX => { + return resolver.doResolveCAres(c_ares.struct_ares_mx_reply, "mx", &name, globalThis); + }, + RecordType.NS => { + return resolver.doResolveCAres(c_ares.struct_hostent, "ns", &name, globalThis); + }, + RecordType.PTR => { + return resolver.doResolveCAres(c_ares.struct_hostent, "ptr", &name, globalThis); + }, + RecordType.SOA => { + return resolver.doResolveCAres(c_ares.struct_ares_soa_reply, "soa", &name, globalThis); + }, + RecordType.SRV => { + return resolver.doResolveCAres(c_ares.struct_ares_srv_reply, "srv", &name, globalThis); + }, + RecordType.TXT => { + return resolver.doResolveCAres(c_ares.struct_ares_txt_reply, "txt", &name, globalThis); + }, + } } // pub fn reverse(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { // const arguments = callframe.arguments(3); @@ -1622,14 +1664,264 @@ pub const DNSResolver = struct { } const name = name_str.toSlice(globalThis, bun.default_allocator); - defer name.deinit(); + var vm = globalThis.bunVM(); var resolver = vm.rareData().globalDNSResolver(vm); - return resolver.doResolveSrv(name.slice(), globalThis); + return resolver.doResolveCAres(c_ares.struct_ares_srv_reply, "srv", &name, globalThis); } - pub fn doResolveSrv(this: *DNSResolver, name: []const u8, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + pub fn resolveSoa(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(2); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("resolveSoa", 2, arguments.len); + return .zero; + } + + const name_value = arguments.ptr[0]; + + if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) { + globalThis.throwInvalidArgumentType("resolveSoa", "hostname", "string"); + return .zero; + } + + const name_str = name_value.toStringOrNull(globalThis) orelse { + return .zero; + }; + + if (name_str.length() == 0) { + globalThis.throwInvalidArgumentType("resolveSoa", "hostname", "non-empty string"); + return .zero; + } + + const name = name_str.toSlice(globalThis, bun.default_allocator); + + var vm = globalThis.bunVM(); + var resolver = vm.rareData().globalDNSResolver(vm); + + return resolver.doResolveCAres(c_ares.struct_ares_soa_reply, "soa", &name, globalThis); + } + + pub fn resolveCaa(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(2); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("resolveCaa", 2, arguments.len); + return .zero; + } + + const name_value = arguments.ptr[0]; + + if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) { + globalThis.throwInvalidArgumentType("resolveCaa", "hostname", "string"); + return .zero; + } + + const name_str = name_value.toStringOrNull(globalThis) orelse { + return .zero; + }; + + if (name_str.length() == 0) { + globalThis.throwInvalidArgumentType("resolveCaa", "hostname", "non-empty string"); + return .zero; + } + + const name = name_str.toSlice(globalThis, bun.default_allocator); + + var vm = globalThis.bunVM(); + var resolver = vm.rareData().globalDNSResolver(vm); + + return resolver.doResolveCAres(c_ares.struct_ares_caa_reply, "caa", &name, globalThis); + } + + pub fn resolveNs(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(2); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("resolveNs", 2, arguments.len); + return .zero; + } + + const name_value = arguments.ptr[0]; + + if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) { + globalThis.throwInvalidArgumentType("resolveNs", "hostname", "string"); + return .zero; + } + + const name_str = name_value.toStringOrNull(globalThis) orelse { + return .zero; + }; + + if (name_str.length() == 0) { + globalThis.throwInvalidArgumentType("resolveNs", "hostname", "non-empty string"); + return .zero; + } + + const name = name_str.toSlice(globalThis, bun.default_allocator); + + var vm = globalThis.bunVM(); + var resolver = vm.rareData().globalDNSResolver(vm); + + return resolver.doResolveCAres(c_ares.struct_hostent, "ns", &name, globalThis); + } + + pub fn resolvePtr(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(2); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("resolvePtr", 2, arguments.len); + return .zero; + } + + const name_value = arguments.ptr[0]; + + if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) { + globalThis.throwInvalidArgumentType("resolvePtr", "hostname", "string"); + return .zero; + } + + const name_str = name_value.toStringOrNull(globalThis) orelse { + return .zero; + }; + + if (name_str.length() == 0) { + globalThis.throwInvalidArgumentType("resolvePtr", "hostname", "non-empty string"); + return .zero; + } + + const name = name_str.toSlice(globalThis, bun.default_allocator); + + var vm = globalThis.bunVM(); + var resolver = vm.rareData().globalDNSResolver(vm); + + return resolver.doResolveCAres(c_ares.struct_hostent, "ptr", &name, globalThis); + } + + pub fn resolveCname(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(2); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("resolveCname", 2, arguments.len); + return .zero; + } + + const name_value = arguments.ptr[0]; + + if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) { + globalThis.throwInvalidArgumentType("resolveCname", "hostname", "string"); + return .zero; + } + + const name_str = name_value.toStringOrNull(globalThis) orelse { + return .zero; + }; + + if (name_str.length() == 0) { + globalThis.throwInvalidArgumentType("resolveCname", "hostname", "non-empty string"); + return .zero; + } + + const name = name_str.toSlice(globalThis, bun.default_allocator); + + var vm = globalThis.bunVM(); + var resolver = vm.rareData().globalDNSResolver(vm); + + return resolver.doResolveCAres(c_ares.struct_hostent, "cname", &name, globalThis); + } + + pub fn resolveMx(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(2); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("resolveMx", 2, arguments.len); + return .zero; + } + + const name_value = arguments.ptr[0]; + + if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) { + globalThis.throwInvalidArgumentType("resolveMx", "hostname", "string"); + return .zero; + } + + const name_str = name_value.toStringOrNull(globalThis) orelse { + return .zero; + }; + + if (name_str.length() == 0) { + globalThis.throwInvalidArgumentType("resolveMx", "hostname", "non-empty string"); + return .zero; + } + + const name = name_str.toSlice(globalThis, bun.default_allocator); + + var vm = globalThis.bunVM(); + var resolver = vm.rareData().globalDNSResolver(vm); + + return resolver.doResolveCAres(c_ares.struct_ares_mx_reply, "mx", &name, globalThis); + } + + pub fn resolveNaptr(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(2); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("resolveNaptr", 2, arguments.len); + return .zero; + } + + const name_value = arguments.ptr[0]; + + if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) { + globalThis.throwInvalidArgumentType("resolveNaptr", "hostname", "string"); + return .zero; + } + + const name_str = name_value.toStringOrNull(globalThis) orelse { + return .zero; + }; + + if (name_str.length() == 0) { + globalThis.throwInvalidArgumentType("resolveNaptr", "hostname", "non-empty string"); + return .zero; + } + + const name = name_str.toSlice(globalThis, bun.default_allocator); + + var vm = globalThis.bunVM(); + var resolver = vm.rareData().globalDNSResolver(vm); + + return resolver.doResolveCAres(c_ares.struct_ares_naptr_reply, "naptr", &name, globalThis); + } + + pub fn resolveTxt(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(2); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("resolveTxt", 2, arguments.len); + return .zero; + } + + const name_value = arguments.ptr[0]; + + if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) { + globalThis.throwInvalidArgumentType("resolveTxt", "hostname", "string"); + return .zero; + } + + const name_str = name_value.toStringOrNull(globalThis) orelse { + return .zero; + }; + + if (name_str.length() == 0) { + globalThis.throwInvalidArgumentType("resolveTxt", "hostname", "non-empty string"); + return .zero; + } + + const name = name_str.toSlice(globalThis, bun.default_allocator); + + var vm = globalThis.bunVM(); + var resolver = vm.rareData().globalDNSResolver(vm); + + return resolver.doResolveCAres(c_ares.struct_ares_txt_reply, "txt", &name, globalThis); + } + + pub fn doResolveCAres(this: *DNSResolver, comptime cares_type: type, comptime type_name: []const u8, name_str: *const JSC.ZigString.Slice, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + var name = name_str.slice(); + var channel: *c_ares.Channel = switch (this.getChannel()) { .result => |res| res, .err => |err| { @@ -1644,29 +1936,34 @@ pub const DNSResolver = struct { }, }; - const key = ResolveSrvInfoRequest.PendingCacheKey.init(name); + const cache_name = comptime std.fmt.comptimePrint("pending_{s}_cache_cares", .{type_name}); + + const key = ResolveInfoRequest(cares_type, type_name).PendingCacheKey.init(name); - var cache = this.getOrPutIntoSrvPendingCache(key, .pending_srv_cache_cares); + var cache = this.getOrPutIntoResolvePendingCache(ResolveInfoRequest(cares_type, type_name), key, cache_name); if (cache == .inflight) { - var srv_lookup = SrvLookup.init(globalThis, globalThis.allocator()) catch unreachable; - cache.inflight.append(srv_lookup); - return srv_lookup.promise.value(); + // CAresLookup will have the name ownership + var cares_lookup = CAresLookup(cares_type, type_name).init(globalThis, globalThis.allocator(), name_str) catch unreachable; + cache.inflight.append(cares_lookup); + return cares_lookup.promise.value(); } - var request = ResolveSrvInfoRequest.init( + var request = ResolveInfoRequest(cares_type, type_name).init( cache, this, - name, + name_str, // CAresLookup will have the ownership globalThis, - "pending_srv_cache_cares", + cache_name, ) catch unreachable; const promise = request.tail.promise.value(); - channel.resolveSrv( + channel.resolve( name, - ResolveSrvInfoRequest, + type_name, + ResolveInfoRequest(cares_type, type_name), request, - ResolveSrvInfoRequest.onCaresComplete, + cares_type, + ResolveInfoRequest(cares_type, type_name).onCaresComplete, ); return promise; @@ -1722,17 +2019,71 @@ pub const DNSResolver = struct { comptime { @export( + resolve, + .{ + .name = "Bun__DNSResolver__resolve", + }, + ); + @export( lookup, .{ .name = "Bun__DNSResolver__lookup", }, ); @export( + resolveTxt, + .{ + .name = "Bun__DNSResolver__resolveTxt", + }, + ); + @export( + resolveSoa, + .{ + .name = "Bun__DNSResolver__resolveSoa", + }, + ); + @export( + resolveMx, + .{ + .name = "Bun__DNSResolver__resolveMx", + }, + ); + @export( + resolveNaptr, + .{ + .name = "Bun__DNSResolver__resolveNaptr", + }, + ); + @export( resolveSrv, .{ .name = "Bun__DNSResolver__resolveSrv", }, ); + @export( + resolveCaa, + .{ + .name = "Bun__DNSResolver__resolveCaa", + }, + ); + @export( + resolveNs, + .{ + .name = "Bun__DNSResolver__resolveNs", + }, + ); + @export( + resolvePtr, + .{ + .name = "Bun__DNSResolver__resolvePtr", + }, + ); + @export( + resolveCname, + .{ + .name = "Bun__DNSResolver__resolveCname", + }, + ); } // pub fn lookupService(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { // const arguments = callframe.arguments(3); diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 58497cad4..fb429340f 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -2172,7 +2172,16 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotask, (JSGlobalObject * globalObj } extern "C" EncodedJSValue Bun__DNSResolver__lookup(JSGlobalObject*, JSC::CallFrame*); +extern "C" EncodedJSValue Bun__DNSResolver__resolve(JSGlobalObject*, JSC::CallFrame*); extern "C" EncodedJSValue Bun__DNSResolver__resolveSrv(JSGlobalObject*, JSC::CallFrame*); +extern "C" EncodedJSValue Bun__DNSResolver__resolveTxt(JSGlobalObject*, JSC::CallFrame*); +extern "C" EncodedJSValue Bun__DNSResolver__resolveSoa(JSGlobalObject*, JSC::CallFrame*); +extern "C" EncodedJSValue Bun__DNSResolver__resolveNaptr(JSGlobalObject*, JSC::CallFrame*); +extern "C" EncodedJSValue Bun__DNSResolver__resolveMx(JSGlobalObject*, JSC::CallFrame*); +extern "C" EncodedJSValue Bun__DNSResolver__resolveCaa(JSGlobalObject*, JSC::CallFrame*); +extern "C" EncodedJSValue Bun__DNSResolver__resolveNs(JSGlobalObject*, JSC::CallFrame*); +extern "C" EncodedJSValue Bun__DNSResolver__resolvePtr(JSGlobalObject*, JSC::CallFrame*); +extern "C" EncodedJSValue Bun__DNSResolver__resolveCname(JSGlobalObject*, JSC::CallFrame*); JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotaskVariadic, (JSGlobalObject * globalObject, CallFrame* callframe)) { @@ -3325,8 +3334,26 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm JSC::JSObject* dnsObject = JSC::constructEmptyObject(this); dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "lookup"_s), 2, Bun__DNSResolver__lookup, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); + dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolve"_s), 2, Bun__DNSResolver__resolve, ImplementationVisibility::Public, NoIntrinsic, + JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolveSrv"_s), 2, Bun__DNSResolver__resolveSrv, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); + dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolveTxt"_s), 2, Bun__DNSResolver__resolveTxt, ImplementationVisibility::Public, NoIntrinsic, + JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); + dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolveSoa"_s), 2, Bun__DNSResolver__resolveSoa, ImplementationVisibility::Public, NoIntrinsic, + JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); + dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolveNaptr"_s), 2, Bun__DNSResolver__resolveNaptr, ImplementationVisibility::Public, NoIntrinsic, + JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); + dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolveMx"_s), 2, Bun__DNSResolver__resolveMx, ImplementationVisibility::Public, NoIntrinsic, + JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); + dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolveCaa"_s), 2, Bun__DNSResolver__resolveCaa, ImplementationVisibility::Public, NoIntrinsic, + JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); + dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolveNs"_s), 2, Bun__DNSResolver__resolveNs, ImplementationVisibility::Public, NoIntrinsic, + JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); + dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolvePtr"_s), 2, Bun__DNSResolver__resolvePtr, ImplementationVisibility::Public, NoIntrinsic, + JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); + dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "resolveCname"_s), 2, Bun__DNSResolver__resolveCname, ImplementationVisibility::Public, NoIntrinsic, + JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); object->putDirect(vm, PropertyName(identifier), dnsObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0); } diff --git a/src/bun.js/node-dns.exports.js b/src/bun.js/node-dns.exports.js index 864edfdbb..0bcc915cd 100644 --- a/src/bun.js/node-dns.exports.js +++ b/src/bun.js/node-dns.exports.js @@ -40,6 +40,128 @@ function resolveSrv(hostname, callback) { ); } + +function resolveTxt(hostname, callback) { + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveTxt(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); +} + +function resolveSoa(hostname, callback) { + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveSoa(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); +} + +function resolveNaptr(hostname, callback) { + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveNaptr(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); +} + +function resolveMx(hostname, callback) { + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveMx(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); + +} + +function resolveCaa(hostname, callback) { + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveCaa(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); +} + +function resolveNs(hostname, callback) { + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveNs(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); +} + +function resolvePtr(hostname, callback) { + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolvePtr(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); +} + +function resolveCname(hostname, callback) { + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveCname(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); +} + function lookupService(address, port, callback) { if (typeof callback != "function") { throw new TypeError("callback must be a function"); @@ -49,9 +171,9 @@ function lookupService(address, port, callback) { } var InternalResolver = class Resolver { - constructor(options) {} + constructor(options) { } - cancel() {} + cancel() { } getServers() { return []; @@ -66,13 +188,22 @@ var InternalResolver = class Resolver { throw new TypeError("callback must be a function"); } - dns.lookup(hostname).then( - (addresses) => { - callback( - null, - hostname, - addresses.map(({ address }) => address), - ); + dns.resolve(hostname).then( + (results) => { + switch (rrtype?.toLowerCase()) { + case 'a': + case 'aaaa': + callback( + null, + hostname, + results.map(({ address }) => address), + ); + break; + default: + callback(null, results); + break; + } + }, (error) => { callback(error); @@ -129,27 +260,79 @@ var InternalResolver = class Resolver { } resolveCname(hostname, callback) { - callback(null, []); + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveCname(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); } resolveMx(hostname, callback) { - callback(null, []); + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveMx(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); + } resolveNaptr(hostname, callback) { - callback(null, []); + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveNaptr(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); } resolveNs(hostname, callback) { - callback(null, []); + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveNs(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); } resolvePtr(hostname, callback) { - callback(null, []); - } + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } - resolveSoa(hostname, callback) { - callback(null, []); + dns.resolvePtr(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); } resolveSrv(hostname, callback) { @@ -168,20 +351,88 @@ var InternalResolver = class Resolver { } resolveCaa(hostname, callback) { - callback(null, []); + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveCaa(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); } resolveTxt(hostname, callback) { - callback(null, []); + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveTxt(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); + } + resolveSoa(hostname, callback) { + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolveSoa(hostname, callback).then( + (results) => { + callback(null, results); + }, + (error) => { + callback(error); + }, + ); } reverse(ip, callback) { callback(null, []); } - setServers(servers) {} + setServers(servers) { } }; +function resolve(hostname, rrtype, callback) { + if (typeof rrtype == "function") { + callback = rrtype; + } + + if (typeof callback != "function") { + throw new TypeError("callback must be a function"); + } + + dns.resolve(hostname).then( + (results) => { + switch (rrtype?.toLowerCase()) { + case 'a': + case 'aaaa': + callback( + null, + hostname, + results.map(({ address }) => address), + ); + break; + default: + callback(null, results); + break; + } + + }, + (error) => { + callback(error); + }, + ); +} + function Resolver(options) { return new InternalResolver(options); } @@ -205,8 +456,8 @@ export var { resolveTxt, } = InternalResolver.prototype; -function setDefaultResultOrder() {} -function setServers() {} +function setDefaultResultOrder() { } +function setServers() { } const promisifyLookup = (res) => { res.sort((a, b) => a.family - b.family); @@ -214,6 +465,21 @@ const promisifyLookup = (res) => { return { address, family }; }; +const promisifyResolve = (rrtype) => { + switch (rrtype?.toLowerCase()) { + case 'a': + case 'aaaa': + return (res) => { + + res.sort((a, b) => a.family - b.family); + const [{ address, family }] = res; + return { address, family }; + }; + default: + return (res) => res; + } +} + // promisified versions export const promises = { lookup(domain, options) { @@ -225,7 +491,7 @@ export const promises = { }, resolve(hostname, rrtype) { - return dns.lookup(hostname); + return dns.resolve(hostname, rrtype).then(promisifyResolve); }, resolve4(hostname, options) { @@ -239,18 +505,43 @@ export const promises = { resolveSrv(hostname) { return dns.resolveSrv(hostname); }, + resolveTxt(hostname) { + return dns.resolveTxt(hostname); + }, + resolveSoa(hostname) { + return dns.resolveSoa(hostname); + }, + resolveNaptr(hostname) { + return dns.resolveNaptr(hostname); + }, + + resolveMx(hostname) { + return dns.resolveMx(hostname); + }, + resolveCaa(hostname) { + return dns.resolveCaa(hostname); + }, + resolveNs(hostname) { + return dns.resolveNs(hostname); + }, + resolvePtr(hostname) { + return dns.resolvePtr(hostname); + }, + resolveCname(hostname) { + return dns.resolveCname(hostname); + }, Resolver: class Resolver { - constructor(options) {} + constructor(options) { } - cancel() {} + cancel() { } getServers() { return []; } resolve(hostname, rrtype) { - return dns.lookup(hostname); + return dns.resolve(hostname, rrtype).then(promisifyResolve); } resolve4(hostname, options) { @@ -266,27 +557,27 @@ export const promises = { } resolveCname(hostname) { - return Promise.resolve([]); + return dns.resolveCname(hostname); } resolveMx(hostname) { - return Promise.resolve([]); + return dns.resolveMx(hostname); } resolveNaptr(hostname) { - return Promise.resolve([]); + return dns.resolveNaptr(hostname); } resolveNs(hostname) { - return Promise.resolve([]); + return dns.resolveNs(hostname); } resolvePtr(hostname) { - return Promise.resolve([]); + return dns.resolvePtr(hostname); } resolveSoa(hostname) { - return Promise.resolve([]); + return dns.resolveSoa(hostname); } resolveSrv(hostname) { @@ -294,30 +585,22 @@ export const promises = { } resolveCaa(hostname) { - return Promise.resolve([]); + return dns.resolveCaa(hostname); } resolveTxt(hostname) { - return Promise.resolve([]); + return dns.resolveTxt(hostname); } reverse(ip) { return Promise.resolve([]); } - setServers(servers) {} + setServers(servers) { } }, }; for (const key of [ "resolveAny", - "resolveCname", - "resolveCaa", - "resolveMx", - "resolveNaptr", - "resolveNs", - "resolvePtr", - "resolveSoa", - "resolveTxt", "reverse", ]) { promises[key] = () => Promise.resolve(undefined); @@ -369,12 +652,12 @@ const exports = { resolveCname, resolveCaa, resolveMx, - resolveNaptr, resolveNs, resolvePtr, resolveSoa, resolveSrv, resolveTxt, + resolveNaptr, promises, [Symbol.for("CommonJS")]: 0, }; diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig index 4f30aadae..4dc5d0bd0 100644 --- a/src/bun.js/webcore/encoding.zig +++ b/src/bun.js/webcore/encoding.zig @@ -860,14 +860,12 @@ pub const Encoder = struct { switch (comptime encoding) { JSC.Node.Encoding.buffer => { - const written = @min(len, to_len); @memcpy(to, input, written); return @intCast(i64, written); }, .latin1, .ascii => { - const written = @min(len, to_len); @memcpy(to, input, written); @@ -899,7 +897,6 @@ pub const Encoder = struct { var written = strings.copyLatin1IntoUTF16([]align(1) u16, output, []const u8, buf).written; return written * 2; - } }, @@ -908,7 +905,6 @@ pub const Encoder = struct { }, JSC.Node.Encoding.base64url => { - var slice = strings.trim(input[0..len], "\r\n\t " ++ [_]u8{std.ascii.control_code.vt}); if (slice.len == 0) return 0; @@ -948,7 +944,7 @@ pub const Encoder = struct { }, JSC.Node.Encoding.hex => { - return len / 2; + return len / 2; }, JSC.Node.Encoding.base64, JSC.Node.Encoding.base64url => { @@ -972,12 +968,12 @@ pub const Encoder = struct { }, // string is already encoded, just need to copy the data JSC.Node.Encoding.ucs2, JSC.Node.Encoding.utf16le => { - var bytes_input_len = len * 2; - var written = @min(bytes_input_len, to_len); + const bytes_input_len = len * 2; + const written = @min(bytes_input_len, to_len); if (written < 2) return 0; - var fixed_len = (written/2) * 2; - var input_u8 = @ptrCast([*] const u8, input); + const fixed_len = (written / 2) * 2; + const input_u8 = @ptrCast([*]const u8, input); strings.copyU16IntoU8(to[0..written], []const u8, input_u8[0..fixed_len]); return @intCast(i64, written); }, diff --git a/src/deps/c_ares.zig b/src/deps/c_ares.zig index bb7bda5db..c3e452772 100644 --- a/src/deps/c_ares.zig +++ b/src/deps/c_ares.zig @@ -1,6 +1,7 @@ const c = @import("std").c; const std = @import("std"); const bun = @import("bun"); +const strings = bun.strings; const iovec = @import("std").os.iovec; const struct_in_addr = std.os.sockaddr.in; const struct_sockaddr = std.os.sockaddr; @@ -16,13 +17,13 @@ pub const NSClass = enum(c_int) { /// Cookie. ns_c_invalid = 0, /// Internet. - ns_c_in = 1, + ns_c_in = 1, /// unallocated/unsupported. - ns_c_2 = 2, + ns_c_2 = 2, /// MIT Chaos-net. - ns_c_chaos = 3, + ns_c_chaos = 3, /// MIT Hesiod. - ns_c_hs = 4, + ns_c_hs = 4, /// Query class values which do not appear in resource records /// for prereq. sections in update requests ns_c_none = 254, @@ -172,7 +173,102 @@ pub const Options = extern struct { resolvconf_path: ?[*:0]u8 = null, hosts_path: ?[*:0]u8 = null, }; -pub const struct_hostent = opaque {}; +pub const struct_hostent = extern struct { + h_name: [*c]u8, + h_aliases: [*c][*c]u8, + h_addrtype: c_int, + h_length: c_int, + h_addr_list: [*c][*c]u8, + + const JSC = bun.JSC; + + pub fn toJSReponse(this: *struct_hostent, _: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime lookup_name: []const u8) JSC.JSValue { + + // A cname lookup always returns a single record but we follow the common API here. + if (comptime strings.eqlComptime(lookup_name, "cname")) { + if(this.h_name != null){ + const array = JSC.JSValue.createEmptyArray(globalThis, 1); + const h_name_len = bun.len(this.h_name); + const h_name_slice = this.h_name[0..h_name_len]; + array.putIndex(globalThis, 0, JSC.ZigString.fromUTF8(h_name_slice).toValueGC(globalThis)); + return array; + } + return JSC.JSValue.createEmptyArray(globalThis, 0); + } else { + if (this.h_aliases == null){ + return JSC.JSValue.createEmptyArray(globalThis, 0); + } + + var count: u32 = 0; + while (this.h_aliases[count] != null) { + count += 1; + } + + const array = JSC.JSValue.createEmptyArray(globalThis, count); + count = 0; + + while (this.h_aliases[count]) |alias| { + const alias_len = bun.len(alias); + const alias_slice = alias[0..alias_len]; + array.putIndex(globalThis, count, JSC.ZigString.fromUTF8(alias_slice).toValueGC(globalThis)); + count += 1; + } + + return array; + } + } + + pub fn Callback(comptime Type: type) type { + return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_hostent) void; + } + + pub fn callbackWrapper( + comptime lookup_name: []const u8, + comptime Type: type, + comptime function: Callback(Type), + ) ares_callback { + return &struct { + pub fn handle(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void { + var this = bun.cast(*Type, ctx.?); + if (status != ARES_SUCCESS) { + function(this, Error.get(status), timeouts, null); + return; + } + + var start: [*c]struct_hostent = undefined; + if (comptime strings.eqlComptime(lookup_name, "ns")) { + var result = ares_parse_ns_reply(buffer, buffer_length, &start); + if (result != ARES_SUCCESS) { + function(this, Error.get(result), timeouts, null); + return; + } + function(this, null, timeouts, start); + } else if (comptime strings.eqlComptime(lookup_name, "ptr")) { + var result = ares_parse_ptr_reply(buffer, buffer_length, null, 0, std.os.AF.INET, &start); + if (result != ARES_SUCCESS) { + function(this, Error.get(result), timeouts, null); + return; + } + function(this, null, timeouts, start); + } else if (comptime strings.eqlComptime(lookup_name, "cname")) { + var addrttls: [256]struct_ares_addrttl = undefined; + var naddrttls: i32 = 256; + + var result = ares_parse_a_reply(buffer, buffer_length, &start, &addrttls, &naddrttls); + if (result != ARES_SUCCESS) { + function(this, Error.get(result), timeouts, null); + return; + } + function(this, null, timeouts, start); + } + } + }.handle; + } + + pub fn deinit(this: *struct_hostent) void { + ares_free_hostent(this); + } +}; pub const struct_timeval = opaque {}; pub const struct_Channeldata = opaque {}; pub const AddrInfo_cname = extern struct { @@ -413,7 +509,7 @@ pub const Channel = opaque { ares_getaddrinfo(this, host_ptr, port_ptr, hints_, AddrInfo.callbackWrapper(Type, callback), ctx); } - pub fn resolveSrv(this: *Channel, name: []const u8, comptime Type: type, ctx: *Type, comptime callback: struct_ares_srv_reply.Callback(Type)) void { + pub fn resolve(this: *Channel, name: []const u8, comptime lookup_name: []const u8, comptime Type: type, ctx: *Type, comptime cares_type: type, comptime callback: cares_type.Callback(Type)) void { var name_buf: [1024]u8 = undefined; const name_ptr: ?[*:0]const u8 = brk: { if (name.len == 0 or name.len >= 1023) { @@ -425,7 +521,8 @@ pub const Channel = opaque { break :brk name_buf[0..len :0]; }; - ares_query(this, name_ptr, NSClass.ns_c_in, NSType.ns_t_srv, struct_ares_srv_reply.callbackWrapper(Type, callback), ctx); + const field_name = comptime std.fmt.comptimePrint("ns_t_{s}", .{lookup_name}); + ares_query(this, name_ptr, NSClass.ns_c_in, @field(NSType, field_name), cares_type.callbackWrapper(lookup_name, Type, callback), ctx); } pub inline fn process(this: *Channel, fd: i32, readable: bool, writable: bool) void { @@ -519,12 +616,85 @@ pub const struct_ares_addr6ttl = extern struct { ttl: c_int, }; pub const struct_ares_caa_reply = extern struct { - next: [*c]struct_ares_caa_reply, + next: ?*struct_ares_caa_reply, critical: c_int, property: [*c]u8, plength: usize, value: [*c]u8, length: usize, + + const JSC = bun.JSC; + + pub fn toJSReponse(this: *struct_ares_caa_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue { + var stack = std.heap.stackFallback(2048, parent_allocator); + var arena = std.heap.ArenaAllocator.init(stack.get()); + defer arena.deinit(); + + var allocator = arena.allocator(); + var count: usize = 0; + var caa: ?*struct_ares_caa_reply = this; + while (caa != null) : (caa = caa.?.next) { + count += 1; + } + + const array = JSC.JSValue.createEmptyArray(globalThis, count); + + caa = this; + var i: u32 = 0; + while (caa != null) { + var node = caa.?; + array.putIndex(globalThis, i, node.toJS(globalThis, allocator)); + caa = node.next; + i += 1; + } + + return array; + } + + pub fn toJS(this: *struct_ares_caa_reply, globalThis: *JSC.JSGlobalObject, _: std.mem.Allocator) JSC.JSValue { + var obj = JSC.JSValue.createEmptyObject(globalThis, 2); + + obj.put(globalThis, JSC.ZigString.static("critical"), JSC.JSValue.jsNumber(this.critical)); + + const property = this.property[0..this.plength]; + const value = this.value[0..this.length]; + const property_str = JSC.ZigString.fromUTF8(property); + obj.put(globalThis, &property_str, JSC.ZigString.fromUTF8(value).toValueGC(globalThis)); + + return obj; + } + + pub fn Callback(comptime Type: type) type { + return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_caa_reply) void; + } + + pub fn callbackWrapper( + comptime _: []const u8, + comptime Type: type, + comptime function: Callback(Type), + ) ares_callback { + return &struct { + pub fn handle(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void { + var this = bun.cast(*Type, ctx.?); + if (status != ARES_SUCCESS) { + function(this, Error.get(status), timeouts, null); + return; + } + + var start: [*c]struct_ares_caa_reply = undefined; + var result = ares_parse_caa_reply(buffer, buffer_length, &start); + if (result != ARES_SUCCESS) { + function(this, Error.get(result), timeouts, null); + return; + } + function(this, null, timeouts, start); + } + }.handle; + } + + pub fn deinit(this: *struct_ares_caa_reply) void { + ares_free_data(this); + } }; pub const struct_ares_srv_reply = extern struct { next: ?*struct_ares_srv_reply, @@ -534,7 +704,7 @@ pub const struct_ares_srv_reply = extern struct { port: c_ushort, const JSC = bun.JSC; - pub fn toJSArray(this: *struct_ares_srv_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + pub fn toJSReponse(this: *struct_ares_srv_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue { var stack = std.heap.stackFallback(2048, parent_allocator); var arena = std.heap.ArenaAllocator.init(stack.get()); defer arena.deinit(); @@ -545,7 +715,7 @@ pub const struct_ares_srv_reply = extern struct { while (srv != null) : (srv = srv.?.next) { count += 1; } - + const array = JSC.JSValue.createEmptyArray(globalThis, count); srv = this; @@ -561,7 +731,7 @@ pub const struct_ares_srv_reply = extern struct { } pub fn toJS(this: *struct_ares_srv_reply, globalThis: *JSC.JSGlobalObject, _: std.mem.Allocator) JSC.JSValue { - var obj = JSC.JSValue.createEmptyObject(globalThis, 4); + const obj = JSC.JSValue.createEmptyObject(globalThis, 4); // { // priority: 10, // weight: 5, @@ -573,9 +743,8 @@ pub const struct_ares_srv_reply = extern struct { obj.put(globalThis, JSC.ZigString.static("weight"), JSC.JSValue.jsNumber(this.weight)); obj.put(globalThis, JSC.ZigString.static("port"), JSC.JSValue.jsNumber(this.port)); - const len = bun.len(this.host); - var host = this.host[0..len]; + const host = this.host[0..len]; obj.put(globalThis, JSC.ZigString.static("name"), JSC.ZigString.fromUTF8(host).toValueGC(globalThis)); return obj; @@ -586,6 +755,7 @@ pub const struct_ares_srv_reply = extern struct { } pub fn callbackWrapper( + comptime _: []const u8, comptime Type: type, comptime function: Callback(Type), ) ares_callback { @@ -613,14 +783,152 @@ pub const struct_ares_srv_reply = extern struct { } }; pub const struct_ares_mx_reply = extern struct { - next: [*c]struct_ares_mx_reply, + next: ?*struct_ares_mx_reply, host: [*c]u8, priority: c_ushort, + + const JSC = bun.JSC; + + pub fn toJSReponse(this: *struct_ares_mx_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue { + var stack = std.heap.stackFallback(2048, parent_allocator); + var arena = std.heap.ArenaAllocator.init(stack.get()); + defer arena.deinit(); + + var allocator = arena.allocator(); + var count: usize = 0; + var mx: ?*struct_ares_mx_reply = this; + while (mx != null) : (mx = mx.?.next) { + count += 1; + } + + const array = JSC.JSValue.createEmptyArray(globalThis, count); + + mx = this; + var i: u32 = 0; + while (mx != null) { + var node = mx.?; + array.putIndex(globalThis, i, node.toJS(globalThis, allocator)); + mx = node.next; + i += 1; + } + + return array; + } + + pub fn toJS(this: *struct_ares_mx_reply, globalThis: *JSC.JSGlobalObject, _: std.mem.Allocator) JSC.JSValue { + const obj = JSC.JSValue.createEmptyObject(globalThis, 2); + obj.put(globalThis, JSC.ZigString.static("priority"), JSC.JSValue.jsNumber(this.priority)); + + const host_len = bun.len(this.host); + const host = this.host[0..host_len]; + obj.put(globalThis, JSC.ZigString.static("exchange"), JSC.ZigString.fromUTF8(host).toValueGC(globalThis)); + + return obj; + } + + pub fn Callback(comptime Type: type) type { + return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_mx_reply) void; + } + + pub fn callbackWrapper( + comptime _: []const u8, + comptime Type: type, + comptime function: Callback(Type), + ) ares_callback { + return &struct { + pub fn handle(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void { + var this = bun.cast(*Type, ctx.?); + if (status != ARES_SUCCESS) { + function(this, Error.get(status), timeouts, null); + return; + } + + var start: [*c]struct_ares_mx_reply = undefined; + var result = ares_parse_mx_reply(buffer, buffer_length, &start); + if (result != ARES_SUCCESS) { + function(this, Error.get(result), timeouts, null); + return; + } + function(this, null, timeouts, start); + } + }.handle; + } + + pub fn deinit(this: *struct_ares_mx_reply) void { + ares_free_data(this); + } }; pub const struct_ares_txt_reply = extern struct { - next: [*c]struct_ares_txt_reply, + next: ?*struct_ares_txt_reply, txt: [*c]u8, length: usize, + + const JSC = bun.JSC; + + pub fn toJSReponse(this: *struct_ares_txt_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue { + var stack = std.heap.stackFallback(2048, parent_allocator); + var arena = std.heap.ArenaAllocator.init(stack.get()); + defer arena.deinit(); + + var allocator = arena.allocator(); + var count: usize = 0; + var txt: ?*struct_ares_txt_reply = this; + while (txt != null) : (txt = txt.?.next) { + count += 1; + } + + const array = JSC.JSValue.createEmptyArray(globalThis, count); + + txt = this; + var i: u32 = 0; + while (txt != null) { + var node = txt.?; + array.putIndex(globalThis, i, node.toJS(globalThis, allocator)); + txt = node.next; + i += 1; + } + + return array; + } + + pub fn toJS(this: *struct_ares_txt_reply, globalThis: *JSC.JSGlobalObject, _: std.mem.Allocator) JSC.JSValue { + const array = JSC.JSValue.createEmptyArray(globalThis, 1); + const value = this.txt[0..this.length]; + array.putIndex(globalThis, 0, JSC.ZigString.fromUTF8(value).toValueGC(globalThis)); + return array; + } + + pub fn Callback(comptime Type: type) type { + return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_txt_reply) void; + } + + pub fn callbackWrapper( + comptime _: []const u8, + comptime Type: type, + comptime function: Callback(Type), + ) ares_callback { + return &struct { + pub fn handleTxt(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void { + var this = bun.cast(*Type, ctx.?); + if (status != ARES_SUCCESS) { + function(this, Error.get(status), timeouts, null); + return; + } + + var srv_start: [*c]struct_ares_txt_reply = undefined; + var result = ares_parse_txt_reply(buffer, buffer_length, &srv_start); + if (result != ARES_SUCCESS) { + function(this, Error.get(result), timeouts, null); + return; + } + function(this, null, timeouts, srv_start); + } + }.handleTxt; + } + + pub fn deinit(this: *struct_ares_txt_reply) void { + ares_free_data(this); + } }; pub const struct_ares_txt_ext = extern struct { next: [*c]struct_ares_txt_ext, @@ -629,13 +937,98 @@ pub const struct_ares_txt_ext = extern struct { record_start: u8, }; pub const struct_ares_naptr_reply = extern struct { - next: [*c]struct_ares_naptr_reply, + next: ?*struct_ares_naptr_reply, flags: [*c]u8, service: [*c]u8, regexp: [*c]u8, replacement: [*c]u8, order: c_ushort, preference: c_ushort, + + const JSC = bun.JSC; + + pub fn toJSReponse(this: *struct_ares_naptr_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue { + var stack = std.heap.stackFallback(2048, parent_allocator); + var arena = std.heap.ArenaAllocator.init(stack.get()); + defer arena.deinit(); + + var allocator = arena.allocator(); + var count: usize = 0; + var naptr: ?*struct_ares_naptr_reply = this; + while (naptr != null) : (naptr = naptr.?.next) { + count += 1; + } + + const array = JSC.JSValue.createEmptyArray(globalThis, count); + + naptr = this; + var i: u32 = 0; + while (naptr != null) { + var node = naptr.?; + array.putIndex(globalThis, i, node.toJS(globalThis, allocator)); + naptr = node.next; + i += 1; + } + + return array; + } + + pub fn toJS(this: *struct_ares_naptr_reply, globalThis: *JSC.JSGlobalObject, _: std.mem.Allocator) JSC.JSValue { + const obj = JSC.JSValue.createEmptyObject(globalThis, 6); + + obj.put(globalThis, JSC.ZigString.static("preference"), JSC.JSValue.jsNumber(this.preference)); + obj.put(globalThis, JSC.ZigString.static("order"), JSC.JSValue.jsNumber(this.order)); + + const flags_len = bun.len(this.flags); + const flags = this.flags[0..flags_len]; + obj.put(globalThis, JSC.ZigString.static("flags"), JSC.ZigString.fromUTF8(flags).toValueGC(globalThis)); + + const service_len = bun.len(this.service); + const service = this.service[0..service_len]; + obj.put(globalThis, JSC.ZigString.static("service"), JSC.ZigString.fromUTF8(service).toValueGC(globalThis)); + + const regexp_len = bun.len(this.regexp); + const regexp = this.regexp[0..regexp_len]; + obj.put(globalThis, JSC.ZigString.static("regexp"), JSC.ZigString.fromUTF8(regexp).toValueGC(globalThis)); + + const replacement_len = bun.len(this.replacement); + const replacement = this.replacement[0..replacement_len]; + obj.put(globalThis, JSC.ZigString.static("replacement"), JSC.ZigString.fromUTF8(replacement).toValueGC(globalThis)); + + return obj; + } + + pub fn Callback(comptime Type: type) type { + return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_naptr_reply) void; + } + + pub fn callbackWrapper( + comptime _: []const u8, + comptime Type: type, + comptime function: Callback(Type), + ) ares_callback { + return &struct { + pub fn handleNaptr(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void { + var this = bun.cast(*Type, ctx.?); + if (status != ARES_SUCCESS) { + function(this, Error.get(status), timeouts, null); + return; + } + + var naptr_start: [*c]struct_ares_naptr_reply = undefined; + var result = ares_parse_naptr_reply(buffer, buffer_length, &naptr_start); + if (result != ARES_SUCCESS) { + function(this, Error.get(result), timeouts, null); + return; + } + function(this, null, timeouts, naptr_start); + } + }.handleNaptr; + } + + pub fn deinit(this: *struct_ares_naptr_reply) void { + ares_free_data(this); + } }; pub const struct_ares_soa_reply = extern struct { nsname: [*c]u8, @@ -645,6 +1038,70 @@ pub const struct_ares_soa_reply = extern struct { retry: c_uint, expire: c_uint, minttl: c_uint, + + const JSC = bun.JSC; + + pub fn toJSReponse(this: *struct_ares_soa_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue { + var stack = std.heap.stackFallback(2048, parent_allocator); + var arena = std.heap.ArenaAllocator.init(stack.get()); + defer arena.deinit(); + + var allocator = arena.allocator(); + + return this.toJS(globalThis, allocator); + } + + pub fn toJS(this: *struct_ares_soa_reply, globalThis: *JSC.JSGlobalObject, _: std.mem.Allocator) JSC.JSValue { + const obj = JSC.JSValue.createEmptyObject(globalThis, 7); + + obj.put(globalThis, JSC.ZigString.static("serial"), JSC.JSValue.jsNumber(this.serial)); + obj.put(globalThis, JSC.ZigString.static("refresh"), JSC.JSValue.jsNumber(this.refresh)); + obj.put(globalThis, JSC.ZigString.static("retry"), JSC.JSValue.jsNumber(this.retry)); + obj.put(globalThis, JSC.ZigString.static("expire"), JSC.JSValue.jsNumber(this.expire)); + obj.put(globalThis, JSC.ZigString.static("minttl"), JSC.JSValue.jsNumber(this.minttl)); + + const nsname_len = bun.len(this.nsname); + const nsname = this.nsname[0..nsname_len]; + obj.put(globalThis, JSC.ZigString.static("nsname"), JSC.ZigString.fromUTF8(nsname).toValueGC(globalThis)); + + const hostmaster_len = bun.len(this.hostmaster); + const hostmaster = this.hostmaster[0..hostmaster_len]; + obj.put(globalThis, JSC.ZigString.static("hostmaster"), JSC.ZigString.fromUTF8(hostmaster).toValueGC(globalThis)); + + return obj; + } + + pub fn Callback(comptime Type: type) type { + return fn (*Type, status: ?Error, timeouts: i32, results: ?*struct_ares_soa_reply) void; + } + + pub fn callbackWrapper( + comptime _: []const u8, + comptime Type: type, + comptime function: Callback(Type), + ) ares_callback { + return &struct { + pub fn handleSoa(ctx: ?*anyopaque, status: c_int, timeouts: c_int, buffer: [*c]u8, buffer_length: c_int) callconv(.C) void { + var this = bun.cast(*Type, ctx.?); + if (status != ARES_SUCCESS) { + function(this, Error.get(status), timeouts, null); + return; + } + + var soa_start: [*c]struct_ares_soa_reply = undefined; + var result = ares_parse_soa_reply(buffer, buffer_length, &soa_start); + if (result != ARES_SUCCESS) { + function(this, Error.get(result), timeouts, null); + return; + } + function(this, null, timeouts, soa_start); + } + }.handleSoa; + } + + pub fn deinit(this: *struct_ares_soa_reply) void { + ares_free_data(this); + } }; pub const struct_ares_uri_reply = extern struct { next: [*c]struct_ares_uri_reply, diff --git a/src/deps/uws b/src/deps/uws -Subproject a076c28a37ae2ffbcb4e2cec023056b13ba0518 +Subproject 665680ca11e09649b96e665a5eb97edc65cbb65 diff --git a/test/bun.js/node-dns.test.js b/test/bun.js/node-dns.test.js index 287645bd9..b3ee09d48 100644 --- a/test/bun.js/node-dns.test.js +++ b/test/bun.js/node-dns.test.js @@ -9,30 +9,18 @@ test("it exists", () => { expect(dns.resolve).toBeDefined(); expect(dns.resolve4).toBeDefined(); expect(dns.resolve6).toBeDefined(); + expect(dns.resolveSrv).toBeDefined(); + expect(dns.resolveTxt).toBeDefined(); + expect(dns.resolveSoa).toBeDefined(); + expect(dns.resolveNaptr).toBeDefined(); + expect(dns.resolveMx).toBeDefined(); + expect(dns.resolveCaa).toBeDefined(); + expect(dns.resolveNs).toBeDefined(); + expect(dns.resolvePtr).toBeDefined(); + expect(dns.resolveCname).toBeDefined(); }); -test("dns.lookup (localhost)", (done) => { - dns.lookup("localhost", (err, address, family) => { - expect(err).toBeNull(); - if (family === 6) { - expect(address).toBe("::1"); - } else { - expect(address).toBe("127.0.0.1"); - } - - done(err); - }); -}); - -test("dns.lookup (example.com)", (done) => { - dns.lookup("example.com", (err, address, family) => { - expect(err).toBeNull(); - expect(typeof address).toBe("string"); - done(err); - }); -}); - -//TODO: use a bun.sh SRV for testing +// //TODO: use a bun.sh SRV for testing test("dns.resolveSrv (_test._tcp.test.socketify.dev)", (done) => { dns.resolveSrv("_test._tcp.test.socketify.dev", (err, results) => { expect(err).toBeNull(); @@ -51,4 +39,116 @@ test("dns.resolveSrv (_test._tcp.invalid.localhost)", (done) => { expect(results).toBeUndefined(true); done(); }); +}); + +test("dns.resolveTxt (txt.socketify.dev)", (done) => { + dns.resolveTxt("txt.socketify.dev", (err, results) => { + expect(err).toBeNull(); + expect(results instanceof Array).toBe(true); + expect(results[0][0]).toBe("bun_test;test"); + done(err); + }); +}); + + +test("dns.resolveSoa (bun.sh)", (done) => { + dns.resolveSoa("bun.sh", (err, result) => { + expect(err).toBeNull(); + + expect(result.serial).toBe(2295878541); + expect(result.refresh).toBe(10000); + expect(result.retry).toBe(2400); + expect(result.expire).toBe(604800); + expect(result.minttl).toBe(3600); + expect(result.nsname).toBe("hans.ns.cloudflare.com"); + expect(result.hostmaster).toBe("dns.cloudflare.com"); + + done(err); + }); +}); + +test("dns.resolveNaptr (naptr.socketify.dev)", (done) => { + dns.resolveNaptr("naptr.socketify.dev", (err, results) => { + expect(err).toBeNull(); + expect(results instanceof Array).toBe(true); + expect(results[0].flags).toBe('S'); + expect(results[0].service).toBe('test'); + expect(results[0].regexp).toBe(''); + expect(results[0].replacement).toBe(''); + expect(results[0].order).toBe(1); + expect(results[0].preference).toBe(12); + done(err); + }); +}); + +test("dns.resolveCaa (caa.socketify.dev)", (done) => { + dns.resolveCaa("caa.socketify.dev", (err, results) => { + expect(err).toBeNull(); + expect(results instanceof Array).toBe(true); + expect(results[0].critical).toBe(0); + expect(results[0].issue).toBe('bun.sh'); + done(err); + }); +}); + + +test("dns.resolveMx (bun.sh)", (done) => { + dns.resolveMx("bun.sh", (err, results) => { + expect(err).toBeNull(); + expect(results instanceof Array).toBe(true); + expect(results[0].priority).toBe(10); + expect(results[0].exchange).toBe('eforward1.registrar-servers.com'); + done(err); + }); +}); + + + +test("dns.resolveNs (bun.sh) ", (done) => { + dns.resolveNs("bun.sh", (err, results) => { + expect(err).toBeNull(); + expect(results instanceof Array).toBe(true); + expect(results[0]).toBe('hans.ns.cloudflare.com'); + done(err); + }); +}); + +test("dns.resolvePtr (ptr.socketify.dev)", (done) => { + dns.resolvePtr("ptr.socketify.dev", (err, results) => { + expect(err).toBeNull(); + expect(results instanceof Array).toBe(true); + expect(results[0]).toBe('bun.sh'); + done(err); + }); +}); + +test("dns.resolveCname (cname.socketify.dev)", (done) => { + dns.resolveCname("cname.socketify.dev", (err, results) => { + expect(err).toBeNull(); + expect(results instanceof Array).toBe(true); + expect(results[0]).toBe('bun.sh'); + done(err); + }); +}); + + +test("dns.lookup (example.com)", (done) => { + dns.lookup("example.com", (err, address, family) => { + expect(err).toBeNull(); + expect(typeof address).toBe("string"); + done(err); + }); +}); + +test("dns.lookup (localhost)", (done) => { + dns.lookup("localhost", (err, address, family) => { + expect(err).toBeNull(); + if (family === 6) { + expect(address).toBe("::1"); + } else { + expect(address).toBe("127.0.0.1"); + } + + done(err); + }); });
\ No newline at end of file |