Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

query cf, google, opendns and use first successful queryof NS instead, warn on multiple txt records

authored by

nekomimi.pet and committed by
Tangled
11755b25 b381d2b1

+127 -343
+4 -1
apps/main-app/public/editor/hooks/useDomainData.ts
··· 60 60 } 61 61 } 62 62 63 - const verifyDomain = async (id: string) => { 63 + const verifyDomain = async (id: string): Promise<{ warning?: string }> => { 64 64 setVerificationStatus({ ...verificationStatus, [id]: 'verifying' }) 65 65 66 66 try { ··· 74 74 if (data.success && data.verified) { 75 75 setVerificationStatus({ ...verificationStatus, [id]: 'success' }) 76 76 await fetchDomains() 77 + return { warning: data.warning } 77 78 } else { 78 79 setVerificationStatus({ ...verificationStatus, [id]: 'error' }) 79 80 if (data.error) { 80 81 alert(`Verification failed: ${data.error}`) 81 82 } 83 + return {} 82 84 } 83 85 } catch (err) { 84 86 console.error('Verify domain error:', err) 85 87 setVerificationStatus({ ...verificationStatus, [id]: 'error' }) 86 88 alert(`Verification failed: ${err instanceof Error ? err.message : 'Unknown error'}`) 89 + return {} 87 90 } 88 91 } 89 92
+21 -3
apps/main-app/public/editor/tabs/DomainsTab.tsx
··· 31 31 verificationStatus: { [id: string]: 'idle' | 'verifying' | 'success' | 'error' } 32 32 userInfo: UserInfo | null 33 33 onAddCustomDomain: (domain: string) => Promise<{ success: boolean; id?: string }> 34 - onVerifyDomain: (id: string) => Promise<void> 34 + onVerifyDomain: (id: string) => Promise<{ warning?: string }> 35 35 onDeleteCustomDomain: (id: string) => Promise<boolean> 36 36 onDeleteWispDomain: (domain: string) => Promise<boolean> 37 37 onClaimWispDomain: (handle: string) => Promise<{ success: boolean; error?: string }> ··· 62 62 available: boolean | null 63 63 checking: boolean 64 64 }>({ available: null, checking: false }) 65 + 66 + // Verification warning state 67 + const [verificationWarning, setVerificationWarning] = useState<{ id: string; message: string } | null>(null) 65 68 66 69 // Custom domain modal state 67 70 const [addDomainModalOpen, setAddDomainModalOpen] = useState(false) ··· 152 155 const cd = domain as CustomDomain 153 156 if (!cd.verified && verificationStatus[cd.id] !== 'verifying') { 154 157 e.preventDefault() 155 - onVerifyDomain(cd.id) 158 + onVerifyDomain(cd.id).then((result) => { 159 + if (result.warning) { 160 + setVerificationWarning({ id: cd.id, message: result.warning }) 161 + } 162 + }) 156 163 } 157 164 } 158 165 break ··· 470 477 {domain.rkey && domain.rkey !== 'self' && ( 471 478 <p className="text-xs text-muted-foreground mt-1 ml-5">→ {domain.rkey}</p> 472 479 )} 480 + {verificationWarning?.id === domain.id && ( 481 + <div className="flex items-start gap-1.5 mt-2 ml-5 text-xs text-yellow-600 dark:text-yellow-400"> 482 + <AlertCircle className="w-3 h-3 shrink-0 mt-0.5" /> 483 + <span>{verificationWarning.message}</span> 484 + </div> 485 + )} 473 486 </div> 474 487 <div className="flex items-center gap-1 flex-shrink-0 ml-2"> 475 488 <Button ··· 485 498 variant="outline" 486 499 size="sm" 487 500 className="h-7 text-xs px-2" 488 - onClick={() => onVerifyDomain(domain.id)} 501 + onClick={async () => { 502 + const result = await onVerifyDomain(domain.id) 503 + if (result.warning) { 504 + setVerificationWarning({ id: domain.id, message: result.warning }) 505 + } 506 + }} 489 507 disabled={isVerifying} 490 508 > 491 509 {isVerifying ? <Loader2 className="w-3 h-3 animate-spin" /> : 'Verify'}
+101 -247
apps/main-app/src/lib/dns-verify.ts
··· 1 - import * as dgram from 'dgram' 1 + import * as dgram from 'node:dgram' 2 2 import * as dnsPacket from 'dns-packet' 3 - import { readFileSync } from 'fs' 4 - import { join } from 'path' 5 3 6 - /** 7 - * Parse the named.root hints file to extract IPv4 addresses. 8 - * Source: https://www.internic.net/domain/named.root 9 - * Format: lines like "A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4" 10 - */ 11 - function loadRootServers(): string[] { 12 - const text = readFileSync(join(import.meta.dir, 'named.root'), 'utf-8') 13 - const ips: string[] = [] 14 - for (const line of text.split('\n')) { 15 - const trimmed = line.trim() 16 - if (trimmed.startsWith(';') || trimmed === '') continue 17 - const match = trimmed.match(/^\S+\s+\d+\s+A\s+(\d+\.\d+\.\d+\.\d+)$/i) 18 - if (match) { 19 - ips.push(match[1]) 20 - } 21 - } 22 - if (ips.length === 0) { 23 - throw new Error('Failed to parse any root servers from named.root') 24 - } 25 - return ips 26 - } 4 + // Cloudflare, Google, OpenDNS — queried in parallel for NS discovery 5 + const PUBLIC_RESOLVERS = ['1.1.1.1', '8.8.8.8', '208.67.222.222'] 27 6 28 - const ROOT_SERVERS = loadRootServers() 7 + const QUERY_TIMEOUT_MS = 3000 29 8 30 - const QUERY_TIMEOUT_MS = 5000 31 - const MAX_RECURSION_DEPTH = 10 32 - 33 - /** 34 - * Pick a random element from an array 35 - */ 36 - function pickRandom<T>(arr: T[]): T { 37 - return arr[Math.floor(Math.random() * arr.length)] 38 - } 9 + // RD (recursion desired) flag bit 10 + const RD_FLAG = 0x0100 39 11 40 12 /** 41 13 * Send a raw DNS query to a specific server and parse the response. 42 - * Handles both authoritative answers and referral responses. 14 + * recursive=true sets RD=1 (for public resolvers); false sets RD=0 (for authoritative NS). 43 15 */ 44 16 function queryDNS( 45 17 name: string, 46 18 type: dnsPacket.RecordType, 47 19 server: string, 48 - port = 53 20 + port = 53, 21 + recursive = false, 49 22 ): Promise<dnsPacket.Packet> { 50 23 return new Promise((resolve, reject) => { 51 24 const socket = dgram.createSocket('udp4') ··· 57 30 const query = dnsPacket.encode({ 58 31 type: 'query', 59 32 id: Math.floor(Math.random() * 65535), 60 - flags: 0, // No recursion desired — we handle it ourselves 61 - questions: [{ type, name, class: 'IN' }] 33 + flags: recursive ? RD_FLAG : 0, 34 + questions: [{ type, name, class: 'IN' }], 62 35 }) 63 36 64 37 socket.on('message', (msg) => { ··· 83 56 84 57 /** 85 58 * Extract IPv4 glue records from the additional section of a DNS response. 86 - * These are A records for nameservers mentioned in the authority section. 87 59 */ 88 60 function extractGlueRecords(response: dnsPacket.Packet): Map<string, string[]> { 89 61 const glue = new Map<string, string[]>() ··· 98 70 return glue 99 71 } 100 72 101 - /** 102 - * Extract NS hostnames from the authority section of a referral response. 103 - */ 104 - function extractNSFromAuthority(response: dnsPacket.Packet): string[] { 105 - const nsNames: string[] = [] 106 - for (const record of response.authorities ?? []) { 107 - if (record.type === 'NS' && 'data' in record && typeof record.data === 'string') { 108 - nsNames.push(record.data.toLowerCase().replace(/\.$/, '')) 109 - } 110 - } 111 - return nsNames 112 - } 113 - 114 - /** 115 - * Extract NS hostnames from the answer section. 116 - */ 117 73 function extractNSFromAnswer(response: dnsPacket.Packet): string[] { 118 - const nsNames: string[] = [] 119 - for (const record of response.answers ?? []) { 120 - if (record.type === 'NS' && 'data' in record && typeof record.data === 'string') { 121 - nsNames.push(record.data.toLowerCase().replace(/\.$/, '')) 122 - } 123 - } 124 - return nsNames 74 + return (response.answers ?? []) 75 + .filter((r) => r.type === 'NS' && 'data' in r && typeof r.data === 'string') 76 + .map((r) => (r as any).data.toLowerCase().replace(/\.$/, '')) 125 77 } 126 78 127 79 /** 128 - * Resolve a nameserver hostname to an IP address. 129 - * Uses the system resolver as a fallback for resolving NS hostnames to IPs 130 - * when glue records are not available. 80 + * Discover authoritative nameserver IPs for a given name. 81 + * 82 + * Walks up the label hierarchy (e.g. _wisp.example.com → example.com) querying 83 + * all three public resolvers in parallel (RD=1) until NS records appear in the 84 + * answer section. NS records only exist at zone apexes, so subdomains and 85 + * underscore labels are skipped automatically. 131 86 */ 132 - async function resolveNStoIP(nsName: string): Promise<string | null> { 133 - try { 134 - // Do a recursive resolve from root for the NS hostname itself 135 - const response = await recursiveResolve(nsName, 'A', ROOT_SERVERS, 0) 136 - for (const record of response.answers ?? []) { 137 - if (record.type === 'A' && 'data' in record && typeof record.data === 'string') { 138 - return record.data 139 - } 140 - } 141 - } catch { 142 - // Fallback: use system resolver 87 + async function getAuthoritativeServers(name: string): Promise<string[]> { 88 + const labels = name.split('.') 89 + 90 + for (let i = 0; i <= labels.length - 2; i++) { 91 + const candidate = labels.slice(i).join('.') 92 + 93 + let response: dnsPacket.Packet 143 94 try { 144 - const { Resolver } = await import('dns') 145 - const resolver = new Resolver() 146 - const ips = await new Promise<string[]>((resolve, reject) => { 147 - resolver.resolve4(nsName, (err, addresses) => { 148 - if (err) reject(err) 149 - else resolve(addresses) 150 - }) 151 - }) 152 - if (ips.length > 0) return ips[0] 95 + response = await Promise.any(PUBLIC_RESOLVERS.map((r) => queryDNS(candidate, 'NS', r, 53, true))) 153 96 } catch { 154 - // Both methods failed 97 + continue 155 98 } 156 - } 157 - return null 158 - } 159 99 160 - /** 161 - * Get usable IP addresses for nameservers from a referral response. 162 - * First tries glue records, then resolves NS hostnames. 163 - */ 164 - async function getServerIPsFromReferral(response: dnsPacket.Packet): Promise<string[]> { 165 - const nsNames = extractNSFromAuthority(response) 166 - if (nsNames.length === 0) return [] 100 + const nsNames = extractNSFromAnswer(response) 101 + if (nsNames.length === 0) continue 167 102 168 - const glue = extractGlueRecords(response) 169 - const ips: string[] = [] 103 + const glue = extractGlueRecords(response) 104 + const ips: string[] = [] 170 105 171 - // First, collect all IPs from glue records 172 - for (const ns of nsNames) { 173 - const glueIps = glue.get(ns) 174 - if (glueIps) { 175 - ips.push(...glueIps) 176 - } 177 - } 178 - 179 - // If we have glue IPs, use them 180 - if (ips.length > 0) return ips 181 - 182 - // Otherwise, resolve NS hostnames (this is rare but happens with out-of-bailiwick NS) 183 - for (const ns of nsNames) { 184 - const ip = await resolveNStoIP(ns) 185 - if (ip) { 186 - ips.push(ip) 187 - // One is enough to continue 188 - if (ips.length >= 2) break 106 + for (const ns of nsNames) { 107 + const glueIps = glue.get(ns) 108 + if (glueIps) ips.push(...glueIps) 189 109 } 190 - } 191 - 192 - return ips 193 - } 194 - 195 - /** 196 - * Recursively resolve a DNS query starting from the given servers. 197 - * Follows referrals (NS delegations) down the DNS tree until we get 198 - * an authoritative answer or hit max depth. 199 - */ 200 - async function recursiveResolve( 201 - name: string, 202 - type: dnsPacket.RecordType, 203 - servers: string[], 204 - depth: number 205 - ): Promise<dnsPacket.Packet> { 206 - if (depth >= MAX_RECURSION_DEPTH) { 207 - throw new Error(`Max recursion depth reached resolving ${type} ${name}`) 208 - } 209 - 210 - const server = pickRandom(servers) 211 - const response = await queryDNS(name, type, server) 212 110 213 - // Check if we got an authoritative answer 214 - const hasAnswers = (response.answers?.length ?? 0) > 0 215 - if (hasAnswers) { 216 - return response 217 - } 218 - 219 - // Check for NXDOMAIN or NODATA (authoritative negative response) 220 - const rcode = response.rcode 221 - if (rcode === 'NXDOMAIN' || rcode === 'NOTFOUND') { 222 - return response // No such domain 223 - } 111 + if (ips.length > 0) return ips 224 112 225 - // Check if this is a referral (has authority NS records) 226 - const nsNames = extractNSFromAuthority(response) 227 - if (nsNames.length === 0) { 228 - // No answers and no referrals — return what we have 229 - return response 230 - } 113 + // No glue — resolve NS hostnames via public resolvers in parallel 114 + await Promise.allSettled( 115 + nsNames.slice(0, 3).map(async (ns) => { 116 + try { 117 + const aResp = await Promise.any(PUBLIC_RESOLVERS.map((r) => queryDNS(ns, 'A', r, 53, true))) 118 + for (const record of aResp.answers ?? []) { 119 + if (record.type === 'A' && 'data' in record && typeof record.data === 'string') { 120 + ips.push(record.data) 121 + } 122 + } 123 + } catch {} 124 + }), 125 + ) 231 126 232 - // Follow the referral 233 - const nextServers = await getServerIPsFromReferral(response) 234 - if (nextServers.length === 0) { 235 - throw new Error(`Could not resolve any NS IPs for referral while resolving ${type} ${name}`) 127 + if (ips.length > 0) return ips 236 128 } 237 129 238 - return recursiveResolve(name, type, nextServers, depth + 1) 130 + throw new Error(`No NS records found for ${name}`) 239 131 } 240 132 241 133 /** 242 - * Resolve a DNS query from root nameservers to authoritative answer. 134 + * Query a DNS record directly from the domain's authoritative nameservers. 135 + * NS discovery uses public resolvers (cached, fast); the actual record query 136 + * goes direct to the NS with RD=0 for an authoritative answer. 243 137 */ 244 138 async function authoritativeResolve(name: string, type: dnsPacket.RecordType): Promise<dnsPacket.Packet> { 245 - console.log(`[DNS Recursive] Resolving ${type} ${name} from root`) 246 - return recursiveResolve(name, type, ROOT_SERVERS, 0) 139 + console.log(`[DNS] Resolving ${type} ${name} via authoritative NS`) 140 + const servers = await getAuthoritativeServers(name) 141 + 142 + let lastError: Error | null = null 143 + for (const server of servers.toSorted(() => Math.random() - 0.5)) { 144 + try { 145 + return await queryDNS(name, type, server) 146 + } catch (err) { 147 + lastError = err as Error 148 + } 149 + } 150 + throw lastError ?? new Error(`All nameservers failed for ${type} ${name}`) 247 151 } 248 152 249 - /** 250 - * Query TXT records from authoritative nameservers, resolved from root. 251 - */ 252 153 async function authoritativeResolveTxt(domain: string): Promise<string[][]> { 253 154 const response = await authoritativeResolve(domain, 'TXT') 254 - 255 155 const records: string[][] = [] 256 156 for (const answer of response.answers ?? []) { 257 157 if (answer.type === 'TXT' && 'data' in answer) { 258 158 const data = answer.data as Buffer | Buffer[] | string | string[] 259 159 if (Array.isArray(data)) { 260 - records.push(data.map(d => Buffer.isBuffer(d) ? d.toString('utf-8') : String(d))) 160 + records.push(data.map((d) => (Buffer.isBuffer(d) ? d.toString('utf-8') : String(d)))) 261 161 } else if (Buffer.isBuffer(data)) { 262 162 records.push([data.toString('utf-8')]) 263 163 } else { ··· 265 165 } 266 166 } 267 167 } 268 - 269 168 return records 270 169 } 271 170 272 - /** 273 - * Query CNAME records from authoritative nameservers, resolved from root. 274 - */ 275 171 async function authoritativeResolveCname(domain: string): Promise<string[]> { 276 172 const response = await authoritativeResolve(domain, 'CNAME') 277 - 278 - const records: string[] = [] 279 - for (const answer of response.answers ?? []) { 280 - if (answer.type === 'CNAME' && 'data' in answer && typeof answer.data === 'string') { 281 - records.push(answer.data.toLowerCase().replace(/\.$/, '')) 282 - } 283 - } 284 - 285 - return records 173 + return (response.answers ?? []) 174 + .filter((r) => r.type === 'CNAME' && 'data' in r && typeof r.data === 'string') 175 + .map((r) => (r as any).data.toLowerCase().replace(/\.$/, '')) 286 176 } 287 177 288 178 /** ··· 293 183 verified: boolean 294 184 /** Error message if verification failed */ 295 185 error?: string 186 + /** Warning message (e.g. duplicate records detected) */ 187 + warning?: string 296 188 /** DNS records found during verification */ 297 189 found?: { 298 190 /** TXT records found (used for domain verification) */ ··· 303 195 } 304 196 305 197 /** 306 - * Verify domain ownership via TXT record at _wisp.{domain} 307 - * Expected format: did:plc:xxx or did:web:xxx 308 - * 309 - * Resolves from root nameservers to get authoritative answers. 198 + * Verify domain ownership via TXT record at _wisp.{domain}. 199 + * Expected value: the user's DID (did:plc:xxx or did:web:xxx). 310 200 */ 311 201 export const verifyDomainOwnership = async (domain: string, expectedDid: string): Promise<VerificationResult> => { 312 202 try { 313 203 const txtDomain = `_wisp.${domain}` 314 - 315 - console.log(`[DNS Verify] Checking TXT record for ${txtDomain} (recursive from root)`) 316 - console.log(`[DNS Verify] Expected DID: ${expectedDid}`) 204 + console.log(`[DNS Verify] Checking TXT ${txtDomain}, expected: ${expectedDid}`) 317 205 318 206 const records = await authoritativeResolveTxt(txtDomain) 319 - 320 - const foundTxtValues = records.map((record) => record.join('')) 207 + const foundTxtValues = records.map((r) => r.join('')) 321 208 console.log(`[DNS Verify] Found TXT records:`, foundTxtValues) 322 209 323 - for (const record of records) { 324 - const txtValue = record.join('') 325 - if (txtValue === expectedDid) { 326 - console.log(`[DNS Verify] ✓ TXT record matches!`) 327 - return { verified: true, found: { txt: foundTxtValues } } 328 - } 210 + if (foundTxtValues.find((v) => v === expectedDid)) { 211 + console.log(`[DNS Verify] ✓ TXT record matches`) 212 + const extras = foundTxtValues.filter((v) => v !== expectedDid) 213 + const warning = 214 + extras.length > 0 215 + ? `Multiple TXT records found at ${txtDomain}. Remove the extra record(s) to avoid issues: ${extras.join(', ')}` 216 + : undefined 217 + if (warning) console.log(`[DNS Verify] ⚠️ ${warning}`) 218 + return { verified: true, warning, found: { txt: foundTxtValues } } 329 219 } 330 220 331 221 console.log(`[DNS Verify] ✗ TXT record does not match`) ··· 336 226 } 337 227 } catch (err: any) { 338 228 console.log(`[DNS Verify] ✗ TXT lookup error:`, err.message) 339 - if (err.code === 'ENOTFOUND' || err.code === 'ENODATA') { 340 - return { 341 - verified: false, 342 - error: `No TXT record found at _wisp.${domain}`, 343 - found: { txt: [] }, 344 - } 345 - } 346 229 return { 347 230 verified: false, 348 231 error: `DNS lookup failed: ${err.message}`, ··· 352 235 } 353 236 354 237 /** 355 - * Verify CNAME record points to the expected hash target 356 - * For custom domains, we expect: domain CNAME -> {hash}.dns.wisp.place 357 - * 358 - * Resolves from root nameservers to get authoritative answers. 238 + * Verify CNAME record points to {hash}.dns.wisp.place. 359 239 */ 360 240 export const verifyCNAME = async (domain: string, expectedHash: string): Promise<VerificationResult> => { 361 241 try { 362 - console.log(`[DNS Verify] Checking CNAME record for ${domain} (recursive from root)`) 363 242 const expectedTarget = `${expectedHash}.dns.wisp.place` 364 - console.log(`[DNS Verify] Expected CNAME: ${expectedTarget}`) 243 + console.log(`[DNS Verify] Checking CNAME ${domain}, expected: ${expectedTarget}`) 365 244 366 - const cname = await authoritativeResolveCname(domain) 245 + const cnames = await authoritativeResolveCname(domain) 246 + const foundCname = cnames[0] ?? null 247 + console.log(`[DNS Verify] Found CNAME:`, foundCname ?? 'none') 367 248 368 - const foundCname = cname.length > 0 ? cname[0]?.toLowerCase().replace(/\.$/, '') : null 369 - console.log(`[DNS Verify] Found CNAME:`, foundCname || 'none') 370 - 371 - if (cname.length === 0 || !foundCname) { 372 - console.log(`[DNS Verify] ✗ No CNAME record found`) 373 - return { 374 - verified: false, 375 - error: `No CNAME record found for ${domain}`, 376 - found: { cname: '' }, 377 - } 249 + if (!foundCname) { 250 + return { verified: false, error: `No CNAME record found for ${domain}`, found: { cname: '' } } 378 251 } 379 252 380 - const actualTarget = foundCname 381 - 382 - if (actualTarget === expectedTarget.toLowerCase()) { 383 - console.log(`[DNS Verify] ✓ CNAME record matches!`) 384 - return { verified: true, found: { cname: actualTarget } } 253 + if (foundCname === expectedTarget.toLowerCase()) { 254 + console.log(`[DNS Verify] ✓ CNAME record matches`) 255 + return { verified: true, found: { cname: foundCname } } 385 256 } 386 257 387 258 console.log(`[DNS Verify] ✗ CNAME record does not match`) 388 259 return { 389 260 verified: false, 390 - error: `CNAME for ${domain} points to ${actualTarget}, expected ${expectedTarget}`, 391 - found: { cname: actualTarget }, 261 + error: `CNAME for ${domain} points to ${foundCname}, expected ${expectedTarget}`, 262 + found: { cname: foundCname }, 392 263 } 393 264 } catch (err: any) { 394 265 console.log(`[DNS Verify] ✗ CNAME lookup error:`, err.message) 395 - if (err.code === 'ENOTFOUND' || err.code === 'ENODATA') { 396 - return { 397 - verified: false, 398 - error: `No CNAME record found for ${domain}`, 399 - found: { cname: '' }, 400 - } 401 - } 402 266 return { 403 267 verified: false, 404 268 error: `DNS lookup failed: ${err.message}`, ··· 408 272 } 409 273 410 274 /** 411 - * Verify custom domain using TXT record as authoritative proof 412 - * CNAME check is optional/advisory - TXT record is sufficient for verification 413 - * 414 - * This approach works with CNAME flattening (e.g., Cloudflare) where the CNAME 415 - * is resolved to A/AAAA records and won't be visible in DNS queries. 416 - * 417 - * All queries are resolved recursively from root nameservers for authoritative answers. 275 + * Verify a custom domain by checking both TXT ownership proof and CNAME routing. 276 + * TXT is authoritative — CNAME is advisory (may be flattened by providers like Cloudflare). 418 277 */ 419 278 export const verifyCustomDomain = async ( 420 279 domain: string, 421 280 expectedDid: string, 422 281 expectedHash: string, 423 282 ): Promise<VerificationResult> => { 424 - // TXT record is authoritative - it proves ownership 425 283 const txtResult = await verifyDomainOwnership(domain, expectedDid) 426 284 if (!txtResult.verified) { 427 285 return txtResult 428 286 } 429 287 430 - // CNAME check is advisory only - we still check it for logging/debugging 431 - // but don't fail verification if it's missing (could be flattened) 432 288 const cnameResult = await verifyCNAME(domain, expectedHash) 433 - 434 - // Log CNAME status for debugging, but don't fail on it 435 289 if (!cnameResult.verified) { 436 290 console.log(`[DNS Verify] ⚠️ CNAME verification failed (may be flattened):`, cnameResult.error) 437 291 } 438 292 439 - // TXT verification is sufficient 440 293 return { 441 294 verified: true, 295 + warning: txtResult.warning, 442 296 found: { 443 297 txt: txtResult.found?.txt, 444 298 cname: cnameResult.found?.cname,
-92
apps/main-app/src/lib/named.root
··· 1 - ; This file holds the information on root name servers needed to 2 - ; initialize cache of Internet domain name servers 3 - ; (e.g. reference this file in the "cache . <file>" 4 - ; configuration file of BIND domain name servers). 5 - ; 6 - ; This file is made available by InterNIC 7 - ; under anonymous FTP as 8 - ; file /domain/named.cache 9 - ; on server FTP.INTERNIC.NET 10 - ; -OR- RS.INTERNIC.NET 11 - ; 12 - ; last update: March 05, 2026 13 - ; related version of root zone: 2026030501 14 - ; 15 - ; FORMERLY NS.INTERNIC.NET 16 - ; 17 - . 3600000 NS A.ROOT-SERVERS.NET. 18 - A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4 19 - A.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:ba3e::2:30 20 - ; 21 - ; FORMERLY NS1.ISI.EDU 22 - ; 23 - . 3600000 NS B.ROOT-SERVERS.NET. 24 - B.ROOT-SERVERS.NET. 3600000 A 170.247.170.2 25 - B.ROOT-SERVERS.NET. 3600000 AAAA 2801:1b8:10::b 26 - ; 27 - ; FORMERLY C.PSI.NET 28 - ; 29 - . 3600000 NS C.ROOT-SERVERS.NET. 30 - C.ROOT-SERVERS.NET. 3600000 A 192.33.4.12 31 - C.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2::c 32 - ; 33 - ; FORMERLY TERP.UMD.EDU 34 - ; 35 - . 3600000 NS D.ROOT-SERVERS.NET. 36 - D.ROOT-SERVERS.NET. 3600000 A 199.7.91.13 37 - D.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2d::d 38 - ; 39 - ; FORMERLY NS.NASA.GOV 40 - ; 41 - . 3600000 NS E.ROOT-SERVERS.NET. 42 - E.ROOT-SERVERS.NET. 3600000 A 192.203.230.10 43 - E.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:a8::e 44 - ; 45 - ; FORMERLY NS.ISC.ORG 46 - ; 47 - . 3600000 NS F.ROOT-SERVERS.NET. 48 - F.ROOT-SERVERS.NET. 3600000 A 192.5.5.241 49 - F.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2f::f 50 - ; 51 - ; FORMERLY NS.NIC.DDN.MIL 52 - ; 53 - . 3600000 NS G.ROOT-SERVERS.NET. 54 - G.ROOT-SERVERS.NET. 3600000 A 192.112.36.4 55 - G.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:12::d0d 56 - ; 57 - ; FORMERLY AOS.ARL.ARMY.MIL 58 - ; 59 - . 3600000 NS H.ROOT-SERVERS.NET. 60 - H.ROOT-SERVERS.NET. 3600000 A 198.97.190.53 61 - H.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:1::53 62 - ; 63 - ; FORMERLY NIC.NORDU.NET 64 - ; 65 - . 3600000 NS I.ROOT-SERVERS.NET. 66 - I.ROOT-SERVERS.NET. 3600000 A 192.36.148.17 67 - I.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fe::53 68 - ; 69 - ; OPERATED BY VERISIGN, INC. 70 - ; 71 - . 3600000 NS J.ROOT-SERVERS.NET. 72 - J.ROOT-SERVERS.NET. 3600000 A 192.58.128.30 73 - J.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:c27::2:30 74 - ; 75 - ; OPERATED BY RIPE NCC 76 - ; 77 - . 3600000 NS K.ROOT-SERVERS.NET. 78 - K.ROOT-SERVERS.NET. 3600000 A 193.0.14.129 79 - K.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fd::1 80 - ; 81 - ; OPERATED BY ICANN 82 - ; 83 - . 3600000 NS L.ROOT-SERVERS.NET. 84 - L.ROOT-SERVERS.NET. 3600000 A 199.7.83.42 85 - L.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:9f::42 86 - ; 87 - ; OPERATED BY WIDE 88 - ; 89 - . 3600000 NS M.ROOT-SERVERS.NET. 90 - M.ROOT-SERVERS.NET. 3600000 A 202.12.27.33 91 - M.ROOT-SERVERS.NET. 3600000 AAAA 2001:dc3::35 92 - ; End of file
+1
apps/main-app/src/routes/domain.ts
··· 269 269 success: true, 270 270 verified: result.verified, 271 271 error: result.error, 272 + warning: result.warning, 272 273 found: result.found, 273 274 } 274 275 } catch (err) {