Skip to content
Merged
46 changes: 24 additions & 22 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { setupServer, SetupServerApi } from "msw/node";
import { http, HttpResponse } from "msw";
import { CustomDnsResolver, getDocumentStoreRecords, queryDns, parseDocumentStoreResults, getDnsDidRecords } from ".";
import {
aliDnsResolver,
cloudflareDnsResolver,
getDocumentStoreRecords,
getDnsDidRecords,
googleDnsResolver,
parseDocumentStoreResults,
queryDns,
} from ".";
import { DnsproveStatusCode } from "./common/error";

describe("getCertStoreRecords", () => {
Expand Down Expand Up @@ -208,22 +216,7 @@ describe("queryDns", () => {
Comment: "Response from 205.251.199.177.",
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can help update to use donotuse.trustvc.io

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


const testDnsResolvers: CustomDnsResolver[] = [
async (domain) => {
const data = await fetch(`https://dns.google/resolve?name=${domain}&type=TXT`, {
method: "GET",
});

return data.json();
},
async (domain) => {
const data = await fetch(`https://cloudflare-dns.com/dns-query?name=${domain}&type=TXT`, {
method: "GET",
headers: { accept: "application/dns-json", contentType: "application/json", connection: "keep-alive" },
});
return data.json();
},
];
const testDnsResolvers = [googleDnsResolver, cloudflareDnsResolver, aliDnsResolver];

afterEach(() => {
server.close();
Expand Down Expand Up @@ -270,14 +263,16 @@ describe("queryDns", () => {
http.get("https://cloudflare-dns.com/dns-query", (_) => {
return new HttpResponse(null, { status: 500 });
}),
http.get("https://dns.alidns.com/resolve", (_) => {
return new HttpResponse(null, { status: 500 });
}),
];
server = setupServer(...handlers);
server.listen();
try {
await queryDns("https://donotuse.openattestation.com", testDnsResolvers);
} catch (e: any) {
expect(e.code).toStrictEqual(DnsproveStatusCode.IDNS_QUERY_ERROR_GENERAL);
}

await expect(queryDns("https://donotuse.openattestation.com", testDnsResolvers)).rejects.toMatchObject({
code: DnsproveStatusCode.IDNS_QUERY_ERROR_GENERAL,
});
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can change to donotuse.trustvc.io

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

});

Expand Down Expand Up @@ -321,6 +316,13 @@ describe("getDocumentStoreRecords for Astron", () => {
addr: "0x18bc0127Ae33389cD96593a1a612774fD14c0737",
dnssec: false,
},
{
type: "openatts",
net: "ethereum",
netId: "1338",
addr: "0x94FD21A026E29E0686583b8be71Cb28a8ca1A8d4",
dnssec: false,
},
{
type: "openatts",
net: "ethereum",
Expand Down
31 changes: 11 additions & 20 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { OpenAttestationDNSTextRecord, OpenAttestationDNSTextRecordT } from "./r
import { OpenAttestationDnsDidRecord, OpenAttestationDnsDidRecordT } from "./records/dnsDid";
import { getLogger } from "./util/logger";
import { CodedError, DnsproveStatusCode } from "./common/error";
import { aliDnsResolver, cloudflareDnsResolver, googleDnsResolver } from "./util/dns-resolvers";

const { trace } = getLogger("index");

Expand All @@ -23,22 +24,7 @@ interface GenericObject {

export type CustomDnsResolver = (domain: string) => Promise<IDNSQueryResponse>;

export const defaultDnsResolvers: CustomDnsResolver[] = [
async (domain) => {
const data = await fetch(`https://dns.google/resolve?name=${domain}&type=TXT`, {
method: "GET",
});

return data.json();
},
async (domain) => {
const data = await fetch(`https://cloudflare-dns.com/dns-query?name=${domain}&type=TXT`, {
method: "GET",
headers: { accept: "application/dns-json", contentType: "application/json", connection: "keep-alive" },
});
return data.json();
},
];
export const defaultDnsResolvers: CustomDnsResolver[] = [googleDnsResolver, cloudflareDnsResolver, aliDnsResolver];

/**
* Returns true for strings that are openattestation records
Expand Down Expand Up @@ -115,8 +101,10 @@ export const parseOpenAttestationRecord = (record: string): GenericObject => {
trace(`Parsing record: ${record}`);
const keyValuePairs = record.trim().split(" "); // tokenize into key=value elements
const recordObject = {} as GenericObject;
// @ts-ignore: we already checked for this token
recordObject.type = keyValuePairs.shift();
const typeToken = keyValuePairs.shift();
if (typeToken !== undefined) {
recordObject.type = typeToken;
}
keyValuePairs.reduce<GenericObject>(addKeyValuePairToObject, recordObject);
return recordObject;
};
Expand Down Expand Up @@ -153,6 +141,7 @@ const parseOpenAttestationRecords = (recordSet: IDNSRecord[] = []): GenericObjec
/**
* Takes a DNS-TXT Record set and returns openattestation document store records if any
* @param recordSet Refer to tests for examples
* @param dnssec Resolver AD (authenticated data) flag; applied as each record's `dnssec` field
*/
export const parseDocumentStoreResults = (
recordSet: IDNSRecord[] = [],
Expand All @@ -177,6 +166,7 @@ export const parseDnsDidResults = (recordSet: IDNSRecord[] = [], dnssec: boolean
/**
* Queries a given domain and parses the results to retrieve openattestation document store records if any
* @param domain e.g: "example.openattestation.com"
* @param customDnsResolvers Optional resolver list; built-in HTTP DNS chain is used when omitted
* @example
* > getDocumentStoreRecords("example.openattestation.com")
* > [ { type: 'openatts',
Expand All @@ -191,7 +181,7 @@ export const getDocumentStoreRecords = async (
): Promise<OpenAttestationDNSTextRecord[]> => {
trace(`Received request to resolve ${domain}`);

const dnsResolvers = customDnsResolvers || defaultDnsResolvers;
const dnsResolvers = customDnsResolvers ?? defaultDnsResolvers;

const results = await queryDns(domain, dnsResolvers);
const answers = results.Answer || [];
Expand All @@ -207,7 +197,7 @@ export const getDnsDidRecords = async (
): Promise<OpenAttestationDnsDidRecord[]> => {
trace(`Received request to resolve ${domain}`);

const dnsResolvers = customDnsResolvers || defaultDnsResolvers;
const dnsResolvers = customDnsResolvers ?? defaultDnsResolvers;

const results = await queryDns(domain, dnsResolvers);
const answers = results.Answer || [];
Expand All @@ -218,3 +208,4 @@ export const getDnsDidRecords = async (
};

export { OpenAttestationDNSTextRecord, OpenAttestationDnsDidRecord };
export * from "./util/dns-resolvers";
8 changes: 8 additions & 0 deletions src/util/dns-resolvers/ali-dns-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { CustomDnsResolver, IDNSQueryResponse } from "../..";

export const aliDnsResolver: CustomDnsResolver = async (domain) => {
const res = await fetch(`https://dns.alidns.com/resolve?name=${domain}&type=16`, {
method: "GET",
});
return res.json() as Promise<IDNSQueryResponse>;
};
9 changes: 9 additions & 0 deletions src/util/dns-resolvers/cloudflare-dns-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { CustomDnsResolver, IDNSQueryResponse } from "../..";

export const cloudflareDnsResolver: CustomDnsResolver = async (domain) => {
const res = await fetch(`https://cloudflare-dns.com/dns-query?name=${domain}&type=TXT`, {
method: "GET",
headers: { accept: "application/dns-json", contentType: "application/json", connection: "keep-alive" },
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
});
return res.json() as Promise<IDNSQueryResponse>;
};
87 changes: 87 additions & 0 deletions src/util/dns-resolvers/dns-resolvers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { setupServer, SetupServerApi } from "msw/node";
import { http, HttpResponse } from "msw";
import { aliDnsResolver } from "./ali-dns-resolver";
import { cloudflareDnsResolver } from "./cloudflare-dns-resolver";
import { googleDnsResolver } from "./google-dns-resolver";

const emptyDnsJson = {
Status: 0,
TC: false,
RD: true,
RA: true,
AD: false,
CD: false,
Answer: [] as [],
};

describe("googleDnsResolver", () => {
let server: SetupServerApi;

afterEach(() => {
server.close();
});

test("requests Google DNS JSON with name and TXT type", async () => {
server = setupServer(
http.get("https://dns.google/resolve", ({ request }) => {
const url = new URL(request.url);
expect(url.searchParams.get("name")).toBe("my.domain.test");
expect(url.searchParams.get("type")).toBe("TXT");
return HttpResponse.json(emptyDnsJson);
})
);
server.listen();

const out = await googleDnsResolver("my.domain.test");
expect(out).toMatchObject({ Status: 0, Answer: [] });
});
});

describe("cloudflareDnsResolver", () => {
let server: SetupServerApi;

afterEach(() => {
server.close();
});

test("requests Cloudflare DNS JSON with name, TXT type, and expected headers", async () => {
server = setupServer(
http.get("https://cloudflare-dns.com/dns-query", ({ request }) => {
const url = new URL(request.url);
expect(url.searchParams.get("name")).toBe("cf.example.test");
expect(url.searchParams.get("type")).toBe("TXT");
expect(request.headers.get("accept")).toBe("application/dns-json");
expect(request.headers.get("connection")).toBe("keep-alive");
expect(request.headers.get("contenttype")).toBe("application/json");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
return HttpResponse.json(emptyDnsJson);
})
);
server.listen();

const out = await cloudflareDnsResolver("cf.example.test");
expect(out).toMatchObject({ Status: 0, Answer: [] });
});
});

describe("aliDnsResolver", () => {
let server: SetupServerApi;

afterEach(() => {
server.close();
});

test("requests Ali DNS JSON with name and type 16 (TXT)", async () => {
server = setupServer(
http.get("https://dns.alidns.com/resolve", ({ request }) => {
const url = new URL(request.url);
expect(url.searchParams.get("name")).toBe("ali.example.test");
expect(url.searchParams.get("type")).toBe("16");
return HttpResponse.json(emptyDnsJson);
})
);
server.listen();

const out = await aliDnsResolver("ali.example.test");
expect(out).toMatchObject({ Status: 0, Answer: [] });
});
});
8 changes: 8 additions & 0 deletions src/util/dns-resolvers/google-dns-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { CustomDnsResolver, IDNSQueryResponse } from "../..";

export const googleDnsResolver: CustomDnsResolver = async (domain) => {
const res = await fetch(`https://dns.google/resolve?name=${domain}&type=TXT`, {
method: "GET",
});
return res.json() as Promise<IDNSQueryResponse>;
};
3 changes: 3 additions & 0 deletions src/util/dns-resolvers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./google-dns-resolver";
export * from "./cloudflare-dns-resolver";
export * from "./ali-dns-resolver";