Skip to content

Commit

Permalink
fix: fix domain validation for subdomains.
Browse files Browse the repository at this point in the history
  • Loading branch information
zicklag committed Nov 19, 2024
1 parent de89db1 commit 37c4528
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 65 deletions.
25 changes: 14 additions & 11 deletions src/lib/dns/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,24 @@ export async function resolveAuthoritative(hostname: string, type: 'CNAME'): Pro
export async function resolveAuthoritative(hostname: string, type = 'A'): Promise<ResolveResponse> {
const resolver = new dns.Resolver();
resolver.setServers(dns.getServers());
const ns: string[] = await new Promise((res, rej) => {
const ns: string[] = await new Promise(async (res, rej) => {
// Climb the subdomains up to find the nameserver responsible for it.
let h = hostname;
// Climb the subdomains looking for a NS response.
while (true) {
resolver.resolveNs(h, (err, addrs) => {
if (err) {
if (h.split('.').length <= 2) {
rej(err);
} else {
h = h.split('.').slice(1).join('.');
}
try {
const records = await new Promise((res, rej) =>
resolver.resolveNs(h, (err, addrs) => {
err ? rej(err) : res(addrs);
})
);
return res(records as string[]);
} catch (e) {
if (h.split('.').length <= 2) {
return rej(e);
} else {
return res(addrs);
h = h.split('.').slice(1).join('.');
}
});
}
}
});
const nsIps: string[][] = await Promise.all(
Expand Down
114 changes: 63 additions & 51 deletions src/lib/dns/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ export async function startDnsServer() {
if (res.finished) return next();

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

const question = req.packet.questions[0];
const { type, name } = question;

if (name.endsWith('.localhost')) {
if (type == 'A') {
res.answer([
{
name,
type,
data: '127.0.0.1',
ttl: DNS_TTL
}
]);
return next();
} else if (type == 'CNAME') {
res.answer([
{
name,
type,
data: pubenv.PUBLIC_DOMAIN.split(':')[0],
ttl: DNS_TTL
}
]);
return next();
}
}

const resolver = new dns.Resolver();
resolver.setServers(defaultDnsServers);

// Collect answers asynchronously
const results = (await Promise.all(
req.packet.questions.map(
(question) =>
new Promise((returnAnswers) => {
const { type, name } = question;
switch (type) {
case 'TXT':
case 'A':
resolver.resolve(name, type, (err, ans) => {
if (!err) {
returnAnswers(
(ans as AnyRecord[]).map((answer) => ({
name,
type,
data: answer,
ttl: DNS_TTL
}))
);
} else {
returnAnswers([]);
}
});
break;
case 'NS':
// Pretend to be the authoritative nameserver for everything so that resolving users
// by the authoritative namerserver always resolves locally during dev.
returnAnswers([
{
name,
type,
data: localServer,
ttl: DNS_TTL
}
]);
default:
returnAnswers(null);
switch (type) {
case 'TXT':
case 'A':
resolver.resolve(name, type, (err, ans) => {
if (!err) {
res.answer(
(ans as AnyRecord[]).map((answer) => ({
name,
type,
data: answer,
ttl: DNS_TTL
})) as unknown as SupportedAnswer[]
);
}
});
break;
case 'NS':
// Pretend to be the authoritative nameserver for every apex so that resolving users
// by the authoritative namerserver always resolves locally during dev.
if (name.split('.').length == 2) {
res.answer([
{
name,
type,
data: localServer,
ttl: DNS_TTL
}
})
)
)) as (SupportedAnswer[] | null)[];

// Return answers
const filtered = results
.filter((x) => !!x)
.map((x) => x as unknown as SupportedAnswer)
.flat();
if (filtered.length > 0) {
res.answer(filtered);
} else {
res.errors.nxDomain();
]);
} else {
res.answer([]);
// Don't go to next so that our normal default NS response doesn't get created.
// We're trying to emulate the way that authoritative nameservers won't respond
// to NS queries on subdomains that don't have custom nameservers.
return;
}
}

next();
Expand Down
4 changes: 1 addition & 3 deletions src/lib/usernames/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ to the weird server.`;
}
} else {
const cnames = await resolveAuthoritative(domainWithoutPort, 'CNAME');
if (!(cnames.length == 1 && cnames[0] == env.PUBLIC_DOMAIN)) {
if (!(cnames.length == 1 && cnames[0] == env.PUBLIC_DOMAIN.split(':')[0])) {
throw `Expected a single CNAME record pointing to ${env.PUBLIC_DOMAIN} \
but found ${cnames.length} records: ${cnames}`;
}
Expand Down Expand Up @@ -125,8 +125,6 @@ with value "${expectedValue}". Found other values: ${txtRecords.map((v) => `"${v
multi.hSet(usernameKey, 'rauthyId', rauthyId);
multi.hSet(rauthyIdKey, 'username', username);
multi.hSet(subspaceKey, 'username', username);
console.log(`Set ${rauthyIdKey} username to ${username}`);
console.log(`Set ${subspaceKey} username to ${username}`);

try {
await multi.exec();
Expand Down

0 comments on commit 37c4528

Please sign in to comment.