@atproto-labs/handle-resolver-node 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -7
- package/dist/atproto-handle-resolver-node.d.ts +22 -0
- package/dist/atproto-handle-resolver-node.d.ts.map +1 -0
- package/dist/atproto-handle-resolver-node.js +24 -0
- package/dist/atproto-handle-resolver-node.js.map +1 -0
- package/dist/index.d.ts +1 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -7
- package/dist/index.js.map +1 -1
- package/dist/node-resolve-txt-factory.d.ts +4 -0
- package/dist/node-resolve-txt-factory.d.ts.map +1 -0
- package/dist/node-resolve-txt-factory.js +83 -0
- package/dist/node-resolve-txt-factory.js.map +1 -0
- package/package.json +5 -6
- package/src/atproto-handle-resolver-node.ts +51 -0
- package/src/index.ts +1 -6
- package/src/node-resolve-txt-factory.ts +100 -0
- package/tsconfig.build.json +1 -1
- package/dist/dns-handle-resolver.d.ts +0 -15
- package/dist/dns-handle-resolver.d.ts.map +0 -1
- package/dist/dns-handle-resolver.js +0 -58
- package/dist/dns-handle-resolver.js.map +0 -1
- package/dist/node-handle-resolver.d.ts +0 -29
- package/dist/node-handle-resolver.d.ts.map +0 -1
- package/dist/node-handle-resolver.js +0 -49
- package/dist/node-handle-resolver.js.map +0 -1
- package/src/dns-handle-resolver.ts +0 -76
- package/src/node-handle-resolver.ts +0 -96
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
# @atproto-labs/handle-resolver-node
|
|
2
2
|
|
|
3
|
-
## 0.
|
|
3
|
+
## 0.1.1
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
- [`
|
|
7
|
+
- [#2633](https://github.com/bluesky-social/atproto/pull/2633) [`acc9093d2`](https://github.com/bluesky-social/atproto/commit/acc9093d2845eba02b68fb2f9db33e4f1b59bb10) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Use distinct type names to prevent conflicts
|
|
8
8
|
|
|
9
|
-
- Updated dependencies [[`
|
|
10
|
-
- @atproto-labs/handle-resolver@0.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
- Updated dependencies [[`acc9093d2`](https://github.com/bluesky-social/atproto/commit/acc9093d2845eba02b68fb2f9db33e4f1b59bb10)]:
|
|
10
|
+
- @atproto-labs/handle-resolver@0.1.1
|
|
11
|
+
|
|
12
|
+
## 0.1.0
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- [#2482](https://github.com/bluesky-social/atproto/pull/2482) [`a8d6c1123`](https://github.com/bluesky-social/atproto/commit/a8d6c112359f5c4c0cfbe2df63443ed275f2a646) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add OAuth provider capability & support for DPoP signed tokens
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [[`a8d6c1123`](https://github.com/bluesky-social/atproto/commit/a8d6c112359f5c4c0cfbe2df63443ed275f2a646)]:
|
|
21
|
+
- @atproto-labs/handle-resolver@0.1.0
|
|
22
|
+
- @atproto-labs/fetch-node@0.1.0
|
|
23
|
+
- @atproto/did@0.1.0
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Fetch } from '@atproto-labs/fetch-node';
|
|
2
|
+
import { AtprotoHandleResolver, HandleResolver } from '@atproto-labs/handle-resolver';
|
|
3
|
+
export type AtprotoHandleResolverNodeOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* List of backup nameservers to use in case the primary ones fail. Will
|
|
6
|
+
* default to no fallback nameservers.
|
|
7
|
+
*/
|
|
8
|
+
fallbackNameservers?: string[];
|
|
9
|
+
/**
|
|
10
|
+
* Fetch function to use for HTTP requests. Allows customizing the request
|
|
11
|
+
* behavior, e.g. adding headers, setting a timeout, mocking, etc. The
|
|
12
|
+
* provided fetch function will be wrapped with a safeFetchWrap function that
|
|
13
|
+
* adds SSRF protection.
|
|
14
|
+
*
|
|
15
|
+
* @default `globalThis.fetch`
|
|
16
|
+
*/
|
|
17
|
+
fetch?: Fetch;
|
|
18
|
+
};
|
|
19
|
+
export declare class AtprotoHandleResolverNode extends AtprotoHandleResolver implements HandleResolver {
|
|
20
|
+
constructor({ fetch, fallbackNameservers, }?: AtprotoHandleResolverNodeOptions);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=atproto-handle-resolver-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atproto-handle-resolver-node.d.ts","sourceRoot":"","sources":["../src/atproto-handle-resolver-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAiB,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EACL,qBAAqB,EACrB,cAAc,EACf,MAAM,+BAA+B,CAAA;AAOtC,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAA;IAE9B;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,CAAA;AAED,qBAAa,yBACX,SAAQ,qBACR,YAAW,cAAc;gBAEb,EACV,KAAwB,EACxB,mBAAmB,GACpB,GAAE,gCAAqC;CAczC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AtprotoHandleResolverNode = void 0;
|
|
4
|
+
const fetch_node_1 = require("@atproto-labs/fetch-node");
|
|
5
|
+
const handle_resolver_1 = require("@atproto-labs/handle-resolver");
|
|
6
|
+
const node_resolve_txt_factory_js_1 = require("./node-resolve-txt-factory.js");
|
|
7
|
+
class AtprotoHandleResolverNode extends handle_resolver_1.AtprotoHandleResolver {
|
|
8
|
+
constructor({ fetch = globalThis.fetch, fallbackNameservers, } = {}) {
|
|
9
|
+
super({
|
|
10
|
+
fetch: (0, fetch_node_1.safeFetchWrap)({
|
|
11
|
+
fetch,
|
|
12
|
+
timeout: 3000, // 3 seconds
|
|
13
|
+
ssrfProtection: true,
|
|
14
|
+
responseMaxSize: 10 * 1048, // DID are max 2048 characters, 10kb for safety
|
|
15
|
+
}),
|
|
16
|
+
resolveTxt: node_resolve_txt_factory_js_1.nodeResolveTxtDefault,
|
|
17
|
+
resolveTxtFallback: fallbackNameservers?.length
|
|
18
|
+
? (0, node_resolve_txt_factory_js_1.nodeResolveTxtFactory)(fallbackNameservers)
|
|
19
|
+
: undefined,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.AtprotoHandleResolverNode = AtprotoHandleResolverNode;
|
|
24
|
+
//# sourceMappingURL=atproto-handle-resolver-node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atproto-handle-resolver-node.js","sourceRoot":"","sources":["../src/atproto-handle-resolver-node.ts"],"names":[],"mappings":";;;AAAA,yDAA+D;AAC/D,mEAGsC;AAEtC,+EAGsC;AAoBtC,MAAa,yBACX,SAAQ,uCAAqB;IAG7B,YAAY,EACV,KAAK,GAAG,UAAU,CAAC,KAAK,EACxB,mBAAmB,MACiB,EAAE;QACtC,KAAK,CAAC;YACJ,KAAK,EAAE,IAAA,0BAAa,EAAC;gBACnB,KAAK;gBACL,OAAO,EAAE,IAAI,EAAE,YAAY;gBAC3B,cAAc,EAAE,IAAI;gBACpB,eAAe,EAAE,EAAE,GAAG,IAAI,EAAE,+CAA+C;aAC5E,CAAC;YACF,UAAU,EAAE,mDAAqB;YACjC,kBAAkB,EAAE,mBAAmB,EAAE,MAAM;gBAC7C,CAAC,CAAC,IAAA,mDAAqB,EAAC,mBAAmB,CAAC;gBAC5C,CAAC,CAAC,SAAS;SACd,CAAC,CAAA;IACJ,CAAC;CACF;AArBD,8DAqBC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -14,11 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
|
|
18
|
-
// Main export
|
|
19
|
-
__exportStar(require("./node-handle-resolver.js"), exports);
|
|
20
|
-
var node_handle_resolver_js_1 = require("./node-handle-resolver.js");
|
|
21
|
-
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return node_handle_resolver_js_1.NodeHandleResolver; } });
|
|
22
|
-
// Utilities
|
|
23
|
-
__exportStar(require("./dns-handle-resolver.js"), exports);
|
|
17
|
+
__exportStar(require("./atproto-handle-resolver-node.js"), exports);
|
|
24
18
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oEAAiD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-resolve-txt-factory.d.ts","sourceRoot":"","sources":["../src/node-resolve-txt-factory.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAE1D,eAAO,MAAM,qBAAqB,EAAE,UACiB,CAAA;AAErD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,UAAU,CA0CvE"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nodeResolveTxtFactory = exports.nodeResolveTxtDefault = void 0;
|
|
4
|
+
const promises_1 = require("node:dns/promises");
|
|
5
|
+
const node_net_1 = require("node:net");
|
|
6
|
+
const nodeResolveTxtDefault = (hostname) => (0, promises_1.resolveTxt)(hostname).then(groupChunks, handleError);
|
|
7
|
+
exports.nodeResolveTxtDefault = nodeResolveTxtDefault;
|
|
8
|
+
function nodeResolveTxtFactory(nameservers) {
|
|
9
|
+
// Optimization
|
|
10
|
+
if (!nameservers.length)
|
|
11
|
+
return async () => null;
|
|
12
|
+
// Build the resolver asynchronously (will be awaited on every use)
|
|
13
|
+
const resolverPromise = Promise.all(nameservers.map((nameserver) => {
|
|
14
|
+
const [domain, port = null] = nameserver.split(':', 2);
|
|
15
|
+
if (port !== null && !/^\d+$/.test(port)) {
|
|
16
|
+
throw new TypeError(`Invalid name server "${nameserver}"`);
|
|
17
|
+
}
|
|
18
|
+
return (0, node_net_1.isIP)(domain) === 4 || isBracedIPv6(domain)
|
|
19
|
+
? [nameserver] // No need to lookup
|
|
20
|
+
: (0, promises_1.lookup)(domain, { all: true }).then((r) => r.map((a) => appendPort(a.address, port)),
|
|
21
|
+
// Let's just ignore failed nameservers resolution
|
|
22
|
+
(_err) => []);
|
|
23
|
+
})).then((results) => {
|
|
24
|
+
const backupIps = results.flat(1);
|
|
25
|
+
// No resolver if no valid IP
|
|
26
|
+
if (!backupIps.length)
|
|
27
|
+
return null;
|
|
28
|
+
const resolver = new promises_1.Resolver();
|
|
29
|
+
resolver.setServers(backupIps);
|
|
30
|
+
return resolver;
|
|
31
|
+
});
|
|
32
|
+
// Avoid uncaught promise rejection
|
|
33
|
+
void resolverPromise.catch(() => {
|
|
34
|
+
// Should never happen though...
|
|
35
|
+
});
|
|
36
|
+
return async (hostname) => {
|
|
37
|
+
const resolver = await resolverPromise;
|
|
38
|
+
return resolver
|
|
39
|
+
? resolver.resolveTxt(hostname).then(groupChunks, handleError)
|
|
40
|
+
: null;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
exports.nodeResolveTxtFactory = nodeResolveTxtFactory;
|
|
44
|
+
function isBracedIPv6(address) {
|
|
45
|
+
return (address.startsWith('[') &&
|
|
46
|
+
address.endsWith(']') &&
|
|
47
|
+
(0, node_net_1.isIP)(address.slice(1, -1)) === 6);
|
|
48
|
+
}
|
|
49
|
+
function groupChunks(results) {
|
|
50
|
+
return results.map((chunks) => chunks.join(''));
|
|
51
|
+
}
|
|
52
|
+
function handleError(err) {
|
|
53
|
+
// Invalid argument type (e.g. hostname is a number)
|
|
54
|
+
if (err instanceof TypeError)
|
|
55
|
+
throw err;
|
|
56
|
+
// If the hostname does not resolve, return null
|
|
57
|
+
if (err instanceof Error) {
|
|
58
|
+
if (err['code'] === 'ENOTFOUND')
|
|
59
|
+
return null;
|
|
60
|
+
// Hostname is not a valid domain name
|
|
61
|
+
if (err['code'] === 'EBADNAME')
|
|
62
|
+
throw err;
|
|
63
|
+
// DNS server unreachable
|
|
64
|
+
// if (err['code'] === 'ETIMEOUT') throw err
|
|
65
|
+
}
|
|
66
|
+
// Historically, errors were not thrown here. A "null" value indicates to the
|
|
67
|
+
// AtprotoHandleResolver that it should try the fallback resolver.
|
|
68
|
+
// @TODO We might want to re-visit this to only apply when an unexpected error
|
|
69
|
+
// occurs (by throwing here). For now, let's keep the same behavior as before.
|
|
70
|
+
// throw err
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
function appendPort(address, port) {
|
|
74
|
+
switch ((0, node_net_1.isIP)(address)) {
|
|
75
|
+
case 4:
|
|
76
|
+
return port ? `${address}:${port}` : address;
|
|
77
|
+
case 6:
|
|
78
|
+
return port ? `[${address}]:${port}` : `[${address}]`;
|
|
79
|
+
default:
|
|
80
|
+
throw new TypeError(`Invalid IP address "${address}"`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=node-resolve-txt-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-resolve-txt-factory.js","sourceRoot":"","sources":["../src/node-resolve-txt-factory.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto-labs/handle-resolver-node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Node specific ATProto handle to DID resolver",
|
|
6
6
|
"keywords": [
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
16
16
|
"url": "https://github.com/bluesky-social/atproto",
|
|
17
|
-
"directory": "packages/handle-resolver-node"
|
|
17
|
+
"directory": "packages/internal/handle-resolver-node"
|
|
18
18
|
},
|
|
19
19
|
"type": "commonjs",
|
|
20
20
|
"main": "dist/index.js",
|
|
@@ -26,10 +26,9 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@atproto-labs/
|
|
30
|
-
"@atproto-labs/
|
|
31
|
-
"@atproto
|
|
32
|
-
"@atproto-labs/did": "0.0.1"
|
|
29
|
+
"@atproto-labs/fetch-node": "0.1.0",
|
|
30
|
+
"@atproto-labs/handle-resolver": "0.1.1",
|
|
31
|
+
"@atproto/did": "0.1.0"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
35
34
|
"typescript": "^5.3.3"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Fetch, safeFetchWrap } from '@atproto-labs/fetch-node'
|
|
2
|
+
import {
|
|
3
|
+
AtprotoHandleResolver,
|
|
4
|
+
HandleResolver,
|
|
5
|
+
} from '@atproto-labs/handle-resolver'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
nodeResolveTxtDefault,
|
|
9
|
+
nodeResolveTxtFactory,
|
|
10
|
+
} from './node-resolve-txt-factory.js'
|
|
11
|
+
|
|
12
|
+
export type AtprotoHandleResolverNodeOptions = {
|
|
13
|
+
/**
|
|
14
|
+
* List of backup nameservers to use in case the primary ones fail. Will
|
|
15
|
+
* default to no fallback nameservers.
|
|
16
|
+
*/
|
|
17
|
+
fallbackNameservers?: string[]
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Fetch function to use for HTTP requests. Allows customizing the request
|
|
21
|
+
* behavior, e.g. adding headers, setting a timeout, mocking, etc. The
|
|
22
|
+
* provided fetch function will be wrapped with a safeFetchWrap function that
|
|
23
|
+
* adds SSRF protection.
|
|
24
|
+
*
|
|
25
|
+
* @default `globalThis.fetch`
|
|
26
|
+
*/
|
|
27
|
+
fetch?: Fetch
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class AtprotoHandleResolverNode
|
|
31
|
+
extends AtprotoHandleResolver
|
|
32
|
+
implements HandleResolver
|
|
33
|
+
{
|
|
34
|
+
constructor({
|
|
35
|
+
fetch = globalThis.fetch,
|
|
36
|
+
fallbackNameservers,
|
|
37
|
+
}: AtprotoHandleResolverNodeOptions = {}) {
|
|
38
|
+
super({
|
|
39
|
+
fetch: safeFetchWrap({
|
|
40
|
+
fetch,
|
|
41
|
+
timeout: 3000, // 3 seconds
|
|
42
|
+
ssrfProtection: true,
|
|
43
|
+
responseMaxSize: 10 * 1048, // DID are max 2048 characters, 10kb for safety
|
|
44
|
+
}),
|
|
45
|
+
resolveTxt: nodeResolveTxtDefault,
|
|
46
|
+
resolveTxtFallback: fallbackNameservers?.length
|
|
47
|
+
? nodeResolveTxtFactory(fallbackNameservers)
|
|
48
|
+
: undefined,
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Resolver, lookup, resolveTxt } from 'node:dns/promises'
|
|
2
|
+
import { isIP } from 'node:net'
|
|
3
|
+
|
|
4
|
+
import { ResolveTxt } from '@atproto-labs/handle-resolver'
|
|
5
|
+
|
|
6
|
+
export const nodeResolveTxtDefault: ResolveTxt = (hostname) =>
|
|
7
|
+
resolveTxt(hostname).then(groupChunks, handleError)
|
|
8
|
+
|
|
9
|
+
export function nodeResolveTxtFactory(nameservers: string[]): ResolveTxt {
|
|
10
|
+
// Optimization
|
|
11
|
+
if (!nameservers.length) return async () => null
|
|
12
|
+
|
|
13
|
+
// Build the resolver asynchronously (will be awaited on every use)
|
|
14
|
+
const resolverPromise: Promise<Resolver | null> = Promise.all<string[]>(
|
|
15
|
+
nameservers.map((nameserver) => {
|
|
16
|
+
const [domain, port = null] = nameserver.split(':', 2)
|
|
17
|
+
|
|
18
|
+
if (port !== null && !/^\d+$/.test(port)) {
|
|
19
|
+
throw new TypeError(`Invalid name server "${nameserver}"`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return isIP(domain) === 4 || isBracedIPv6(domain)
|
|
23
|
+
? [nameserver] // No need to lookup
|
|
24
|
+
: lookup(domain, { all: true }).then(
|
|
25
|
+
(r) => r.map((a) => appendPort(a.address, port)),
|
|
26
|
+
// Let's just ignore failed nameservers resolution
|
|
27
|
+
(_err) => [],
|
|
28
|
+
)
|
|
29
|
+
}),
|
|
30
|
+
).then((results) => {
|
|
31
|
+
const backupIps = results.flat(1)
|
|
32
|
+
// No resolver if no valid IP
|
|
33
|
+
if (!backupIps.length) return null
|
|
34
|
+
|
|
35
|
+
const resolver = new Resolver()
|
|
36
|
+
resolver.setServers(backupIps)
|
|
37
|
+
return resolver
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// Avoid uncaught promise rejection
|
|
41
|
+
void resolverPromise.catch(() => {
|
|
42
|
+
// Should never happen though...
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
return async (hostname) => {
|
|
46
|
+
const resolver = await resolverPromise
|
|
47
|
+
return resolver
|
|
48
|
+
? resolver.resolveTxt(hostname).then(groupChunks, handleError)
|
|
49
|
+
: null
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isBracedIPv6(address: string): boolean {
|
|
54
|
+
return (
|
|
55
|
+
address.startsWith('[') &&
|
|
56
|
+
address.endsWith(']') &&
|
|
57
|
+
isIP(address.slice(1, -1)) === 6
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function groupChunks(results: string[][]): string[] {
|
|
62
|
+
return results.map((chunks) => chunks.join(''))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function handleError(err: unknown) {
|
|
66
|
+
// Invalid argument type (e.g. hostname is a number)
|
|
67
|
+
if (err instanceof TypeError) throw err
|
|
68
|
+
|
|
69
|
+
// If the hostname does not resolve, return null
|
|
70
|
+
if (err instanceof Error) {
|
|
71
|
+
if (err['code'] === 'ENOTFOUND') return null
|
|
72
|
+
|
|
73
|
+
// Hostname is not a valid domain name
|
|
74
|
+
if (err['code'] === 'EBADNAME') throw err
|
|
75
|
+
|
|
76
|
+
// DNS server unreachable
|
|
77
|
+
// if (err['code'] === 'ETIMEOUT') throw err
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Historically, errors were not thrown here. A "null" value indicates to the
|
|
81
|
+
// AtprotoHandleResolver that it should try the fallback resolver.
|
|
82
|
+
|
|
83
|
+
// @TODO We might want to re-visit this to only apply when an unexpected error
|
|
84
|
+
// occurs (by throwing here). For now, let's keep the same behavior as before.
|
|
85
|
+
|
|
86
|
+
// throw err
|
|
87
|
+
|
|
88
|
+
return null
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function appendPort(address: string, port: string | null): string {
|
|
92
|
+
switch (isIP(address)) {
|
|
93
|
+
case 4:
|
|
94
|
+
return port ? `${address}:${port}` : address
|
|
95
|
+
case 6:
|
|
96
|
+
return port ? `[${address}]:${port}` : `[${address}]`
|
|
97
|
+
default:
|
|
98
|
+
throw new TypeError(`Invalid IP address "${address}"`)
|
|
99
|
+
}
|
|
100
|
+
}
|
package/tsconfig.build.json
CHANGED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import { resolveTxt } from 'node:dns/promises';
|
|
3
|
-
import { HandleResolveOptions, HandleResolver, ResolvedHandle } from '@atproto-labs/handle-resolver';
|
|
4
|
-
export type DnsHandleResolverOptions = {
|
|
5
|
-
/**
|
|
6
|
-
* Nameservers to use in place of the system's default nameservers.
|
|
7
|
-
*/
|
|
8
|
-
nameservers?: string[];
|
|
9
|
-
};
|
|
10
|
-
export declare class DnsHandleResolver implements HandleResolver {
|
|
11
|
-
protected resolveTxt: typeof resolveTxt;
|
|
12
|
-
constructor(options?: DnsHandleResolverOptions);
|
|
13
|
-
resolve(handle: string, _options?: HandleResolveOptions): Promise<ResolvedHandle>;
|
|
14
|
-
}
|
|
15
|
-
//# sourceMappingURL=dns-handle-resolver.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dns-handle-resolver.d.ts","sourceRoot":"","sources":["../src/dns-handle-resolver.ts"],"names":[],"mappings":";AAAA,OAAO,EAAoB,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEhE,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,cAAc,EAEf,MAAM,+BAA+B,CAAA;AAKtC,MAAM,MAAM,wBAAwB,GAAG;IACrC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB,CAAA;AAED,qBAAa,iBAAkB,YAAW,cAAc;IACtD,SAAS,CAAC,UAAU,EAAE,OAAO,UAAU,CAAA;gBAE3B,OAAO,CAAC,EAAE,wBAAwB;IAMjC,OAAO,CAClB,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,cAAc,CAAC;CAO3B"}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DnsHandleResolver = void 0;
|
|
4
|
-
const promises_1 = require("node:dns/promises");
|
|
5
|
-
const handle_resolver_1 = require("@atproto-labs/handle-resolver");
|
|
6
|
-
const SUBDOMAIN = '_atproto';
|
|
7
|
-
const PREFIX = 'did=';
|
|
8
|
-
class DnsHandleResolver {
|
|
9
|
-
constructor(options) {
|
|
10
|
-
Object.defineProperty(this, "resolveTxt", {
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
writable: true,
|
|
14
|
-
value: void 0
|
|
15
|
-
});
|
|
16
|
-
this.resolveTxt = options?.nameservers
|
|
17
|
-
? buildResolveTxt(options.nameservers)
|
|
18
|
-
: promises_1.resolveTxt.bind(null);
|
|
19
|
-
}
|
|
20
|
-
async resolve(handle, _options) {
|
|
21
|
-
try {
|
|
22
|
-
return parseDnsResult(await this.resolveTxt(`${SUBDOMAIN}.${handle}`));
|
|
23
|
-
}
|
|
24
|
-
catch (err) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
exports.DnsHandleResolver = DnsHandleResolver;
|
|
30
|
-
function buildResolveTxt(nameservers) {
|
|
31
|
-
// Build the resolver asynchronously
|
|
32
|
-
const resolverPromise = Promise.allSettled(nameservers.map((h) => (0, promises_1.lookup)(h)))
|
|
33
|
-
.then((responses) => responses.flatMap((r) => r.status === 'fulfilled' ? [r.value.address] : []))
|
|
34
|
-
.then((backupIps) => {
|
|
35
|
-
if (!backupIps.length)
|
|
36
|
-
return null;
|
|
37
|
-
const resolver = new promises_1.Resolver();
|
|
38
|
-
resolver.setServers(backupIps);
|
|
39
|
-
return resolver;
|
|
40
|
-
});
|
|
41
|
-
resolverPromise.catch(() => {
|
|
42
|
-
// Should never happen
|
|
43
|
-
});
|
|
44
|
-
return async (hostname) => {
|
|
45
|
-
const resolver = await resolverPromise;
|
|
46
|
-
return resolver ? resolver.resolveTxt(hostname) : [];
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
function parseDnsResult(chunkedResults) {
|
|
50
|
-
const results = chunkedResults.map((chunks) => chunks.join(''));
|
|
51
|
-
const found = results.filter((i) => i.startsWith(PREFIX));
|
|
52
|
-
if (found.length !== 1) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
const did = found[0].slice(PREFIX.length);
|
|
56
|
-
return (0, handle_resolver_1.isResolvedHandle)(did) ? did : null;
|
|
57
|
-
}
|
|
58
|
-
//# sourceMappingURL=dns-handle-resolver.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dns-handle-resolver.js","sourceRoot":"","sources":["../src/dns-handle-resolver.ts"],"names":[],"mappings":";;;AAAA,gDAAgE;AAEhE,mEAKsC;AAEtC,MAAM,SAAS,GAAG,UAAU,CAAA;AAC5B,MAAM,MAAM,GAAG,MAAM,CAAA;AASrB,MAAa,iBAAiB;IAG5B,YAAY,OAAkC;QAFpC;;;;;WAA6B;QAGrC,IAAI,CAAC,UAAU,GAAG,OAAO,EAAE,WAAW;YACpC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,WAAW,CAAC;YACtC,CAAC,CAAC,qBAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;IAEM,KAAK,CAAC,OAAO,CAClB,MAAc,EACd,QAA+B;QAE/B,IAAI,CAAC;YACH,OAAO,cAAc,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,SAAS,IAAI,MAAM,EAAE,CAAC,CAAC,CAAA;QACxE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;CACF;AAnBD,8CAmBC;AAED,SAAS,eAAe,CAAC,WAAqB;IAC5C,oCAAoC;IACpC,MAAM,eAAe,GAA6B,OAAO,CAAC,UAAU,CAClE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,iBAAM,EAAC,CAAC,CAAC,CAAC,CAClC;SACE,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAClB,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CACtB,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAClD,CACF;SACA,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE;QAClB,IAAI,CAAC,SAAS,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QAClC,MAAM,QAAQ,GAAG,IAAI,mBAAQ,EAAE,CAAA;QAC/B,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAA;QAC9B,OAAO,QAAQ,CAAA;IACjB,CAAC,CAAC,CAAA;IAEJ,eAAe,CAAC,KAAK,CAAC,GAAG,EAAE;QACzB,sBAAsB;IACxB,CAAC,CAAC,CAAA;IAEF,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE;QACxB,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAA;QACtC,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACtD,CAAC,CAAA;AACH,CAAC;AAED,SAAS,cAAc,CAAC,cAA0B;IAChD,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;IAC/D,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAA;IACzD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACzC,OAAO,IAAA,kCAAgB,EAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;AAC3C,CAAC"}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Fetch } from '@atproto-labs/fetch';
|
|
2
|
-
import { SafeFetchWrapOptions } from '@atproto-labs/fetch-node';
|
|
3
|
-
import { CachedHandleResolver, CachedHandleResolverOptions, HandleResolver } from '@atproto-labs/handle-resolver';
|
|
4
|
-
import { DnsHandleResolverOptions } from './dns-handle-resolver.js';
|
|
5
|
-
export type NodeHandleResolverOptions = {
|
|
6
|
-
/**
|
|
7
|
-
* List of domain names that are forbidden to be resolved using the
|
|
8
|
-
* well-known/atproto-did method.
|
|
9
|
-
*/
|
|
10
|
-
wellKnownExclude?: SafeFetchWrapOptions['forbiddenDomainNames'];
|
|
11
|
-
/**
|
|
12
|
-
* List of backup nameservers to use for DNS resolution.
|
|
13
|
-
*/
|
|
14
|
-
fallbackNameservers?: DnsHandleResolverOptions['nameservers'];
|
|
15
|
-
cache?: CachedHandleResolverOptions['cache'];
|
|
16
|
-
/**
|
|
17
|
-
* Fetch function to use for HTTP requests. Allows customizing the request
|
|
18
|
-
* behavior, e.g. adding headers, setting a timeout, mocking, etc. The
|
|
19
|
-
* provided fetch function will be wrapped with a safeFetchWrap function that
|
|
20
|
-
* adds SSRF protection.
|
|
21
|
-
*
|
|
22
|
-
* @default `globalThis.fetch`
|
|
23
|
-
*/
|
|
24
|
-
fetch?: Fetch;
|
|
25
|
-
};
|
|
26
|
-
export declare class NodeHandleResolver extends CachedHandleResolver implements HandleResolver {
|
|
27
|
-
constructor({ cache, fetch, fallbackNameservers, wellKnownExclude, }?: NodeHandleResolverOptions);
|
|
28
|
-
}
|
|
29
|
-
//# sourceMappingURL=node-handle-resolver.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"node-handle-resolver.d.ts","sourceRoot":"","sources":["../src/node-handle-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAC3C,OAAO,EAAE,oBAAoB,EAAiB,MAAM,0BAA0B,CAAA;AAC9E,OAAO,EACL,oBAAoB,EACpB,2BAA2B,EAC3B,cAAc,EAEf,MAAM,+BAA+B,CAAA;AAEtC,OAAO,EAEL,wBAAwB,EACzB,MAAM,0BAA0B,CAAA;AAEjC,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,oBAAoB,CAAC,sBAAsB,CAAC,CAAA;IAE/D;;OAEG;IACH,mBAAmB,CAAC,EAAE,wBAAwB,CAAC,aAAa,CAAC,CAAA;IAE7D,KAAK,CAAC,EAAE,2BAA2B,CAAC,OAAO,CAAC,CAAA;IAE5C;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,CAAA;AAED,qBAAa,kBACX,SAAQ,oBACR,YAAW,cAAc;gBAEb,EACV,KAAK,EACL,KAAwB,EACxB,mBAAmB,EACnB,gBAAgB,GACjB,GAAE,yBAA8B;CA+ClC"}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NodeHandleResolver = void 0;
|
|
4
|
-
const fetch_node_1 = require("@atproto-labs/fetch-node");
|
|
5
|
-
const handle_resolver_1 = require("@atproto-labs/handle-resolver");
|
|
6
|
-
const dns_handle_resolver_js_1 = require("./dns-handle-resolver.js");
|
|
7
|
-
class NodeHandleResolver extends handle_resolver_1.CachedHandleResolver {
|
|
8
|
-
constructor({ cache, fetch = globalThis.fetch, fallbackNameservers, wellKnownExclude, } = {}) {
|
|
9
|
-
const safeFetch = (0, fetch_node_1.safeFetchWrap)({
|
|
10
|
-
fetch,
|
|
11
|
-
timeout: 3000, // 3 seconds
|
|
12
|
-
forbiddenDomainNames: wellKnownExclude,
|
|
13
|
-
ssrfProtection: true,
|
|
14
|
-
responseMaxSize: 10 * 1048, // DID are max 2048 characters, 10kb for safety
|
|
15
|
-
});
|
|
16
|
-
const httpResolver = new handle_resolver_1.WellKnownHandleResolver({
|
|
17
|
-
fetch: safeFetch,
|
|
18
|
-
});
|
|
19
|
-
const dnsResolver = new dns_handle_resolver_js_1.DnsHandleResolver();
|
|
20
|
-
const fallbackResolver = new dns_handle_resolver_js_1.DnsHandleResolver({
|
|
21
|
-
nameservers: fallbackNameservers,
|
|
22
|
-
});
|
|
23
|
-
super({
|
|
24
|
-
cache,
|
|
25
|
-
resolver: {
|
|
26
|
-
resolve: async (handle) => {
|
|
27
|
-
const abortController = new AbortController();
|
|
28
|
-
const dnsPromise = dnsResolver.resolve(handle);
|
|
29
|
-
const httpPromise = httpResolver.resolve(handle, {
|
|
30
|
-
signal: abortController.signal,
|
|
31
|
-
});
|
|
32
|
-
// Will be awaited later
|
|
33
|
-
httpPromise.catch(() => { });
|
|
34
|
-
const dnsRes = await dnsPromise;
|
|
35
|
-
if (dnsRes) {
|
|
36
|
-
abortController.abort();
|
|
37
|
-
return dnsRes;
|
|
38
|
-
}
|
|
39
|
-
const res = await httpPromise;
|
|
40
|
-
if (res)
|
|
41
|
-
return res;
|
|
42
|
-
return fallbackResolver.resolve(handle);
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
exports.NodeHandleResolver = NodeHandleResolver;
|
|
49
|
-
//# sourceMappingURL=node-handle-resolver.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"node-handle-resolver.js","sourceRoot":"","sources":["../src/node-handle-resolver.ts"],"names":[],"mappings":";;;AACA,yDAA8E;AAC9E,mEAKsC;AAEtC,qEAGiC;AA2BjC,MAAa,kBACX,SAAQ,sCAAoB;IAG5B,YAAY,EACV,KAAK,EACL,KAAK,GAAG,UAAU,CAAC,KAAK,EACxB,mBAAmB,EACnB,gBAAgB,MACa,EAAE;QAC/B,MAAM,SAAS,GAAG,IAAA,0BAAa,EAAC;YAC9B,KAAK;YACL,OAAO,EAAE,IAAI,EAAE,YAAY;YAC3B,oBAAoB,EAAE,gBAAgB;YACtC,cAAc,EAAE,IAAI;YACpB,eAAe,EAAE,EAAE,GAAG,IAAI,EAAE,+CAA+C;SAC5E,CAAC,CAAA;QAEF,MAAM,YAAY,GAAG,IAAI,yCAAuB,CAAC;YAC/C,KAAK,EAAE,SAAS;SACjB,CAAC,CAAA;QAEF,MAAM,WAAW,GAAG,IAAI,0CAAiB,EAAE,CAAA;QAE3C,MAAM,gBAAgB,GAAG,IAAI,0CAAiB,CAAC;YAC7C,WAAW,EAAE,mBAAmB;SACjC,CAAC,CAAA;QAEF,KAAK,CAAC;YACJ,KAAK;YACL,QAAQ,EAAE;gBACR,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;oBACxB,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAA;oBAE7C,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;oBAC9C,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE;wBAC/C,MAAM,EAAE,eAAe,CAAC,MAAM;qBAC/B,CAAC,CAAA;oBAEF,wBAAwB;oBACxB,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;oBAE3B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAA;oBAC/B,IAAI,MAAM,EAAE,CAAC;wBACX,eAAe,CAAC,KAAK,EAAE,CAAA;wBACvB,OAAO,MAAM,CAAA;oBACf,CAAC;oBAED,MAAM,GAAG,GAAG,MAAM,WAAW,CAAA;oBAC7B,IAAI,GAAG;wBAAE,OAAO,GAAG,CAAA;oBAEnB,OAAO,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;gBACzC,CAAC;aACF;SACF,CAAC,CAAA;IACJ,CAAC;CACF;AAxDD,gDAwDC"}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { Resolver, lookup, resolveTxt } from 'node:dns/promises'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
HandleResolveOptions,
|
|
5
|
-
HandleResolver,
|
|
6
|
-
ResolvedHandle,
|
|
7
|
-
isResolvedHandle,
|
|
8
|
-
} from '@atproto-labs/handle-resolver'
|
|
9
|
-
|
|
10
|
-
const SUBDOMAIN = '_atproto'
|
|
11
|
-
const PREFIX = 'did='
|
|
12
|
-
|
|
13
|
-
export type DnsHandleResolverOptions = {
|
|
14
|
-
/**
|
|
15
|
-
* Nameservers to use in place of the system's default nameservers.
|
|
16
|
-
*/
|
|
17
|
-
nameservers?: string[]
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export class DnsHandleResolver implements HandleResolver {
|
|
21
|
-
protected resolveTxt: typeof resolveTxt
|
|
22
|
-
|
|
23
|
-
constructor(options?: DnsHandleResolverOptions) {
|
|
24
|
-
this.resolveTxt = options?.nameservers
|
|
25
|
-
? buildResolveTxt(options.nameservers)
|
|
26
|
-
: resolveTxt.bind(null)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
public async resolve(
|
|
30
|
-
handle: string,
|
|
31
|
-
_options?: HandleResolveOptions,
|
|
32
|
-
): Promise<ResolvedHandle> {
|
|
33
|
-
try {
|
|
34
|
-
return parseDnsResult(await this.resolveTxt(`${SUBDOMAIN}.${handle}`))
|
|
35
|
-
} catch (err) {
|
|
36
|
-
return null
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function buildResolveTxt(nameservers: string[]): typeof resolveTxt {
|
|
42
|
-
// Build the resolver asynchronously
|
|
43
|
-
const resolverPromise: Promise<Resolver | null> = Promise.allSettled(
|
|
44
|
-
nameservers.map((h) => lookup(h)),
|
|
45
|
-
)
|
|
46
|
-
.then((responses) =>
|
|
47
|
-
responses.flatMap((r) =>
|
|
48
|
-
r.status === 'fulfilled' ? [r.value.address] : [],
|
|
49
|
-
),
|
|
50
|
-
)
|
|
51
|
-
.then((backupIps) => {
|
|
52
|
-
if (!backupIps.length) return null
|
|
53
|
-
const resolver = new Resolver()
|
|
54
|
-
resolver.setServers(backupIps)
|
|
55
|
-
return resolver
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
resolverPromise.catch(() => {
|
|
59
|
-
// Should never happen
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
return async (hostname) => {
|
|
63
|
-
const resolver = await resolverPromise
|
|
64
|
-
return resolver ? resolver.resolveTxt(hostname) : []
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function parseDnsResult(chunkedResults: string[][]): ResolvedHandle {
|
|
69
|
-
const results = chunkedResults.map((chunks) => chunks.join(''))
|
|
70
|
-
const found = results.filter((i) => i.startsWith(PREFIX))
|
|
71
|
-
if (found.length !== 1) {
|
|
72
|
-
return null
|
|
73
|
-
}
|
|
74
|
-
const did = found[0].slice(PREFIX.length)
|
|
75
|
-
return isResolvedHandle(did) ? did : null
|
|
76
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { Fetch } from '@atproto-labs/fetch'
|
|
2
|
-
import { SafeFetchWrapOptions, safeFetchWrap } from '@atproto-labs/fetch-node'
|
|
3
|
-
import {
|
|
4
|
-
CachedHandleResolver,
|
|
5
|
-
CachedHandleResolverOptions,
|
|
6
|
-
HandleResolver,
|
|
7
|
-
WellKnownHandleResolver,
|
|
8
|
-
} from '@atproto-labs/handle-resolver'
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
DnsHandleResolver,
|
|
12
|
-
DnsHandleResolverOptions,
|
|
13
|
-
} from './dns-handle-resolver.js'
|
|
14
|
-
|
|
15
|
-
export type NodeHandleResolverOptions = {
|
|
16
|
-
/**
|
|
17
|
-
* List of domain names that are forbidden to be resolved using the
|
|
18
|
-
* well-known/atproto-did method.
|
|
19
|
-
*/
|
|
20
|
-
wellKnownExclude?: SafeFetchWrapOptions['forbiddenDomainNames']
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* List of backup nameservers to use for DNS resolution.
|
|
24
|
-
*/
|
|
25
|
-
fallbackNameservers?: DnsHandleResolverOptions['nameservers']
|
|
26
|
-
|
|
27
|
-
cache?: CachedHandleResolverOptions['cache']
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Fetch function to use for HTTP requests. Allows customizing the request
|
|
31
|
-
* behavior, e.g. adding headers, setting a timeout, mocking, etc. The
|
|
32
|
-
* provided fetch function will be wrapped with a safeFetchWrap function that
|
|
33
|
-
* adds SSRF protection.
|
|
34
|
-
*
|
|
35
|
-
* @default `globalThis.fetch`
|
|
36
|
-
*/
|
|
37
|
-
fetch?: Fetch
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export class NodeHandleResolver
|
|
41
|
-
extends CachedHandleResolver
|
|
42
|
-
implements HandleResolver
|
|
43
|
-
{
|
|
44
|
-
constructor({
|
|
45
|
-
cache,
|
|
46
|
-
fetch = globalThis.fetch,
|
|
47
|
-
fallbackNameservers,
|
|
48
|
-
wellKnownExclude,
|
|
49
|
-
}: NodeHandleResolverOptions = {}) {
|
|
50
|
-
const safeFetch = safeFetchWrap({
|
|
51
|
-
fetch,
|
|
52
|
-
timeout: 3000, // 3 seconds
|
|
53
|
-
forbiddenDomainNames: wellKnownExclude,
|
|
54
|
-
ssrfProtection: true,
|
|
55
|
-
responseMaxSize: 10 * 1048, // DID are max 2048 characters, 10kb for safety
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
const httpResolver = new WellKnownHandleResolver({
|
|
59
|
-
fetch: safeFetch,
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
const dnsResolver = new DnsHandleResolver()
|
|
63
|
-
|
|
64
|
-
const fallbackResolver = new DnsHandleResolver({
|
|
65
|
-
nameservers: fallbackNameservers,
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
super({
|
|
69
|
-
cache,
|
|
70
|
-
resolver: {
|
|
71
|
-
resolve: async (handle) => {
|
|
72
|
-
const abortController = new AbortController()
|
|
73
|
-
|
|
74
|
-
const dnsPromise = dnsResolver.resolve(handle)
|
|
75
|
-
const httpPromise = httpResolver.resolve(handle, {
|
|
76
|
-
signal: abortController.signal,
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
// Will be awaited later
|
|
80
|
-
httpPromise.catch(() => {})
|
|
81
|
-
|
|
82
|
-
const dnsRes = await dnsPromise
|
|
83
|
-
if (dnsRes) {
|
|
84
|
-
abortController.abort()
|
|
85
|
-
return dnsRes
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const res = await httpPromise
|
|
89
|
-
if (res) return res
|
|
90
|
-
|
|
91
|
-
return fallbackResolver.resolve(handle)
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
})
|
|
95
|
-
}
|
|
96
|
-
}
|