Skip to content

Commit 37c4528

Browse files
committed
fix: fix domain validation for subdomains.
1 parent de89db1 commit 37c4528

File tree

3 files changed

+78
-65
lines changed

3 files changed

+78
-65
lines changed

src/lib/dns/resolve.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,24 @@ export async function resolveAuthoritative(hostname: string, type: 'CNAME'): Pro
2323
export async function resolveAuthoritative(hostname: string, type = 'A'): Promise<ResolveResponse> {
2424
const resolver = new dns.Resolver();
2525
resolver.setServers(dns.getServers());
26-
const ns: string[] = await new Promise((res, rej) => {
26+
const ns: string[] = await new Promise(async (res, rej) => {
27+
// Climb the subdomains up to find the nameserver responsible for it.
2728
let h = hostname;
28-
// Climb the subdomains looking for a NS response.
2929
while (true) {
30-
resolver.resolveNs(h, (err, addrs) => {
31-
if (err) {
32-
if (h.split('.').length <= 2) {
33-
rej(err);
34-
} else {
35-
h = h.split('.').slice(1).join('.');
36-
}
30+
try {
31+
const records = await new Promise((res, rej) =>
32+
resolver.resolveNs(h, (err, addrs) => {
33+
err ? rej(err) : res(addrs);
34+
})
35+
);
36+
return res(records as string[]);
37+
} catch (e) {
38+
if (h.split('.').length <= 2) {
39+
return rej(e);
3740
} else {
38-
return res(addrs);
41+
h = h.split('.').slice(1).join('.');
3942
}
40-
});
43+
}
4144
}
4245
});
4346
const nsIps: string[][] = await Promise.all(

src/lib/dns/server.ts

Lines changed: 63 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ export async function startDnsServer() {
190190
if (res.finished) return next();
191191

192192
const question = req.packet.questions[0];
193-
if (question.type == 'NS') {
193+
// Note: we have a different custom responder for NS records in dev
194+
if (question.type == 'NS' && !dev) {
194195
res.packet.flags = res.packet.flags;
195196
return res.answer(makeNsAnswers(question.name));
196197
}
@@ -382,60 +383,71 @@ export async function startDnsServer() {
382383
// If there's already an answer, continue
383384
if (res.finished) return next();
384385

386+
const question = req.packet.questions[0];
387+
const { type, name } = question;
388+
389+
if (name.endsWith('.localhost')) {
390+
if (type == 'A') {
391+
res.answer([
392+
{
393+
name,
394+
type,
395+
data: '127.0.0.1',
396+
ttl: DNS_TTL
397+
}
398+
]);
399+
return next();
400+
} else if (type == 'CNAME') {
401+
res.answer([
402+
{
403+
name,
404+
type,
405+
data: pubenv.PUBLIC_DOMAIN.split(':')[0],
406+
ttl: DNS_TTL
407+
}
408+
]);
409+
return next();
410+
}
411+
}
412+
385413
const resolver = new dns.Resolver();
386414
resolver.setServers(defaultDnsServers);
387415

388-
// Collect answers asynchronously
389-
const results = (await Promise.all(
390-
req.packet.questions.map(
391-
(question) =>
392-
new Promise((returnAnswers) => {
393-
const { type, name } = question;
394-
switch (type) {
395-
case 'TXT':
396-
case 'A':
397-
resolver.resolve(name, type, (err, ans) => {
398-
if (!err) {
399-
returnAnswers(
400-
(ans as AnyRecord[]).map((answer) => ({
401-
name,
402-
type,
403-
data: answer,
404-
ttl: DNS_TTL
405-
}))
406-
);
407-
} else {
408-
returnAnswers([]);
409-
}
410-
});
411-
break;
412-
case 'NS':
413-
// Pretend to be the authoritative nameserver for everything so that resolving users
414-
// by the authoritative namerserver always resolves locally during dev.
415-
returnAnswers([
416-
{
417-
name,
418-
type,
419-
data: localServer,
420-
ttl: DNS_TTL
421-
}
422-
]);
423-
default:
424-
returnAnswers(null);
416+
switch (type) {
417+
case 'TXT':
418+
case 'A':
419+
resolver.resolve(name, type, (err, ans) => {
420+
if (!err) {
421+
res.answer(
422+
(ans as AnyRecord[]).map((answer) => ({
423+
name,
424+
type,
425+
data: answer,
426+
ttl: DNS_TTL
427+
})) as unknown as SupportedAnswer[]
428+
);
429+
}
430+
});
431+
break;
432+
case 'NS':
433+
// Pretend to be the authoritative nameserver for every apex so that resolving users
434+
// by the authoritative namerserver always resolves locally during dev.
435+
if (name.split('.').length == 2) {
436+
res.answer([
437+
{
438+
name,
439+
type,
440+
data: localServer,
441+
ttl: DNS_TTL
425442
}
426-
})
427-
)
428-
)) as (SupportedAnswer[] | null)[];
429-
430-
// Return answers
431-
const filtered = results
432-
.filter((x) => !!x)
433-
.map((x) => x as unknown as SupportedAnswer)
434-
.flat();
435-
if (filtered.length > 0) {
436-
res.answer(filtered);
437-
} else {
438-
res.errors.nxDomain();
443+
]);
444+
} else {
445+
res.answer([]);
446+
// Don't go to next so that our normal default NS response doesn't get created.
447+
// We're trying to emulate the way that authoritative nameservers won't respond
448+
// to NS queries on subdomains that don't have custom nameservers.
449+
return;
450+
}
439451
}
440452

441453
next();

src/lib/usernames/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ to the weird server.`;
7171
}
7272
} else {
7373
const cnames = await resolveAuthoritative(domainWithoutPort, 'CNAME');
74-
if (!(cnames.length == 1 && cnames[0] == env.PUBLIC_DOMAIN)) {
74+
if (!(cnames.length == 1 && cnames[0] == env.PUBLIC_DOMAIN.split(':')[0])) {
7575
throw `Expected a single CNAME record pointing to ${env.PUBLIC_DOMAIN} \
7676
but found ${cnames.length} records: ${cnames}`;
7777
}
@@ -125,8 +125,6 @@ with value "${expectedValue}". Found other values: ${txtRecords.map((v) => `"${v
125125
multi.hSet(usernameKey, 'rauthyId', rauthyId);
126126
multi.hSet(rauthyIdKey, 'username', username);
127127
multi.hSet(subspaceKey, 'username', username);
128-
console.log(`Set ${rauthyIdKey} username to ${username}`);
129-
console.log(`Set ${subspaceKey} username to ${username}`);
130128

131129
try {
132130
await multi.exec();

0 commit comments

Comments
 (0)