1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
const dohUpstreams = [ 'https://dns.cloudflare.com/dns-query', 'https://security.cloudflare-dns.com/dns-query', 'https://dns.google/dns-query', 'https://[2606:4700:4700::1111]/dns-query', 'https://[2620:fe::fe]/dns-query', 'https://dns.adguard-dns.com/dns-query', 'https://unfiltered.adguard-dns.com/dns-query', 'https://doh.opendns.com/dns-query', 'https://doh.quad9.net/dns-query' ];
const customPath = "/dns-query"; const timeoutMs = 3000; const maxRetries = dohUpstreams.length;
export default { async fetch(request, env, ctx) { return handleRequest(request); }, };
async function fetchWithTimeout(url, options) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeoutMs); try { const response = await fetch(url, { ...options, signal: controller.signal }); return response; } finally { clearTimeout(timeoutId); } }
async function handleRequest(request) { const { method, headers, url } = request; const { searchParams, pathname } = new URL(url); const now = new Date().toISOString(); if (pathname !== customPath) { return new Response(`404 Not Found\n${now}`, { status: 404 }); } if (method === 'GET' && !searchParams.toString()) { return new Response( `DNS Worker - Priority-based Failover\nTime: ${now}\nUpstreams:\n${dohUpstreams.join('\n')}`, { headers: { 'Content-Type': 'text/plain' } } ); }
try { if (method === 'GET' && searchParams.has('dns')) { const dnsParam = searchParams.get('dns'); let lastError = null; for (let i = 0; i < maxRetries; i++) { try { const upstream = dohUpstreams[i]; return await fetchWithTimeout(`${upstream}?dns=${dnsParam}`, { method: 'GET', headers: { 'Accept': 'application/dns-message' } }); } catch (e) { lastError = e; console.warn(`[Failover] Upstream ${i} failed (${dohUpstreams[i]}): ${e.message}`); } } throw lastError || new Error("All upstreams failed"); } else if (method === 'GET' && (searchParams.has('name') || searchParams.has('type'))) { const params = new URLSearchParams(searchParams); let lastError = null; for (let i = 0; i < maxRetries; i++) { try { const baseUrl = dohUpstreams[i].includes('dns.google') ? 'https://dns.google/resolve' : dohUpstreams[i].replace('/dns-query', ''); return await fetchWithTimeout(`${baseUrl}?${params}`, { method: 'GET', headers: { 'Accept': 'application/dns-json' } }); } catch (e) { lastError = e; console.warn(`[Failover] JSON upstream ${i} failed: ${e.message}`); } } throw lastError || new Error("All JSON upstreams failed"); } else if (method === 'POST' && headers.get('content-type') === 'application/dns-message') { const body = await request.arrayBuffer(); let lastError = null; for (let i = 0; i < maxRetries; i++) { try { return await fetchWithTimeout(dohUpstreams[i], { method: 'POST', headers: { 'Accept': 'application/dns-message', 'Content-Type': 'application/dns-message', }, body: body }); } catch (e) { lastError = e; console.warn(`[Failover] POST upstream ${i} failed: ${e.message}`); } } throw lastError || new Error("All POST upstreams failed"); } return new Response( 'Invalid DNS request format\nRefer to RFC8484', { status: 400, headers: { 'Content-Type': 'text/plain' } } ); } catch (e) { return new Response(`DNS Resolution Failed\nError: ${e.message}`, { status: 503, headers: { 'Content-Type': 'text/plain', 'Cache-Control': 'no-store, max-age=0' } }); } }
|