Skip to content

Commit

Permalink
feat: serve extra DNS records from redis.
Browse files Browse the repository at this point in the history
  • Loading branch information
zicklag committed Oct 30, 2024
1 parent 6f10dfe commit a6766ee
Showing 1 changed file with 53 additions and 3 deletions.
56 changes: 53 additions & 3 deletions src/lib/dns/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,17 @@ import * as network from 'dinodns/common/network';
import type { SupportedAnswer } from 'dinodns/types/dns';
import { DefaultStore } from 'dinodns/plugins/storage';
import { dev } from '$app/environment';
import { z } from 'zod';

const REDIS_USER_PREFIX = 'weird:users:';
const REDIS_DNS_RECORD_PREFIX = 'weird:dns:records:';

const redisDnsRecordSchema = z.array(
z.object({
ttl: z.optional(z.number().int().min(0)),
data: z.string()
})
);

/** Helper function to escape a string so we can put it literally into a regex without some
* of it's characters being interpreted as regex special characters. */
Expand Down Expand Up @@ -59,9 +68,47 @@ export async function startDnsServer() {

// Now we can add an A record that will direct web traffic to the app
staticRecords.set(appDomain, 'A', selfIps);

s.use(staticRecords.handler);

// Resolve records stored in Redis
s.use(async (req, res, next) => {
const results = (await Promise.all(
req.packet.questions.map(
(question) =>
new Promise(async (returnAnswers) => {
const { type, name } = question;
const redisKey = REDIS_DNS_RECORD_PREFIX + type + ':' + name;
let record;
try {
record = await redis.get(redisKey);
if (!record) return returnAnswers(null);
const parsed = redisDnsRecordSchema.parse(JSON.parse(record));
returnAnswers(
parsed.map((record) => ({
name,
type,
data: record.data,
ttl: record.ttl
}))
);
} catch (e) {
console.warn('Error parsing DNS record from redis:', redisKey, record, e);
returnAnswers(null);
}
})
)
)) 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);

next();
});

// Resolve records for registered users
s.use(async (req, res, next) => {
if (res.packet.answers.length > 0) return next();
Expand Down Expand Up @@ -89,8 +136,11 @@ export async function startDnsServer() {
case 'A':
const aUsername = name.match(WEIRD_HOST_A_RECORD_REGEX)?.[1];
if (!aUsername) return returnAnswers(null);
const exists = await redis.exists(REDIS_USER_PREFIX + aUsername);
if (!exists) return returnAnswers(null);

// TODO: eventually we only want to return records for users that exist
// const exists = await redis.exists(REDIS_USER_PREFIX + aUsername);
// if (!exists) return returnAnswers(null);

returnAnswers(
selfIps.map((ip) => ({
name,
Expand Down

0 comments on commit a6766ee

Please sign in to comment.