/* * DoHflare - High-Performance DNS over HTTPS Edge Proxy for Cloudflare Workers & Pages * Copyright (C) 2026 Racpast * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * ADDITIONAL NOTICE: Pursuant to Section 7 of the GNU AGPLv3, * additional terms apply to this work. Refer to the NOTICE file * in the repository for details regarding attribution requirements, * trademark disclaimers, and compliance policies. */ /* ========================================================================== 1. ISOLATE LEVEL CONSTANTS & RUNTIME DEFAULTS ========================================================================== */ const ISOLATE_CONSTANTS = { MEMORY_CACHE_LIMIT: 2000, MEMORY_CACHE_SIZE_BYTES: 8 * 1024 * 1024, }; const RUNTIME_DEFAULTS = { UPSTREAM_URLS: [ "https://dns.google/dns-query", "https://dns11.quad9.net/dns-query", ], DOH_CONTENT_TYPE: "application/dns-message", DOH_PATH: "/dns-query", ROOT_CONTENT: null, ROOT_CONTENT_TYPE: "text/html; charset=utf-8", ROOT_CACHE_TTL: 86400, MAX_CACHEABLE_BYTES: 64 * 1024, MAX_POST_BODY_BYTES: 8 * 1024, FETCH_TIMEOUT_MS: 2500, MAX_RETRIES: 2, DEFAULT_POSITIVE_TTL: 60, DEFAULT_NEGATIVE_TTL: 15, FALLBACK_ECS_IP: "119.29.29.0", CF_CACHE_WRITE_THRESHOLD: 500, GLOBAL_WRITE_COOLDOWN_MS: 5 * 60 * 1000, GLOBAL_WRITE_PER_MINUTE_LIMIT: 200, HOT_WINDOW_MS: 60 * 1000, HOT_HIT_THRESHOLD: 20, STALE_WINDOW_FACTOR: 0.5, EXTREME_STALE_FALLBACK_MS: 24 * 3600 * 1000, JITTER_PCT: 10, GLOBAL_CACHE_NAMESPACE: "https://dohflare.local/cache/", }; /* ========================================================================== 2. DNS PROTOCOL CONSTANTS & ERROR CODES ========================================================================== */ const DNS_CONSTANTS = { HEADER_LEN: 12, OFFSET_ID: 0, OFFSET_QDCOUNT: 4, OFFSET_ANCOUNT: 6, OFFSET_NSCOUNT: 8, OFFSET_ARCOUNT: 10, TYPE_OPT: 41, OPT_CODE_ECS: 8, RCODE_NXDOMAIN: 3, MAX_NAME_ITERATIONS: 130, OPT_HEADER_TEMPLATE: new Uint8Array([ 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]), }; const ERRORS = { OOB: "OOB_READ", LOOP: "MALFORMED_LOOP", TIMEOUT: "UPSTREAM_TIMEOUT", OVERSIZED: "PAYLOAD_TOO_LARGE", }; /* ==========================================================================    3. UTILITIES & HASH FUNCTIONS    ========================================================================== */ function generateFnv1a32Hex(dataView) { let hashValue = 0x811c9dc5; for (let index = 0; index < dataView.length; index++) { hashValue ^= dataView[index]; hashValue = Math.imul(hashValue, 0x01000193); } return (hashValue >>> 0).toString(16).padStart(8, "0"); } function convertBase64UrlToBuffer(base64UrlString) { let base64String = base64UrlString.replace(/-/g, "+").replace(/_/g, "/"); while (base64String.length % 4) base64String += "="; const decodedString = atob(base64String); const byteArray = new Uint8Array(decodedString.length); for (let index = 0; index < decodedString.length; index++) byteArray[index] = decodedString.charCodeAt(index); return byteArray.buffer; } function normalizeClientIp(rawIpAddress, fallbackIpAddress) { if (!rawIpAddress) return fallbackIpAddress; rawIpAddress = rawIpAddress.trim(); const colonIndex = rawIpAddress.indexOf(":"); if (colonIndex !== -1 && rawIpAddress.split(":").length > 2) return rawIpAddress; if (colonIndex !== -1) return rawIpAddress.split(":")[0]; return rawIpAddress; } async function executeFetchWithTimeout( requestUrl, fetchOptions, timeoutMilliseconds, ) { if (typeof AbortSignal !== "undefined" && AbortSignal.timeout) { return fetch(requestUrl, { ...fetchOptions, signal: AbortSignal.timeout(timeoutMilliseconds), }); } const abortController = new AbortController(); const timeoutId = setTimeout( () => abortController.abort(), timeoutMilliseconds, ); try { const fetchResponse = await fetch(requestUrl, { ...fetchOptions, signal: abortController.signal, }); return fetchResponse; } finally { clearTimeout(timeoutId); } } /* ========================================================================== 4. LRU CACHE & GLOBAL STATE ========================================================================== */ class LRUCacheNode { constructor(key, value) { this.key = key; this.value = value; this.prev = null; this.next = null; } } class MemoryLRUCache { constructor(maxItemCount, maxSizeBytes) { this.maxItemCount = maxItemCount; this.maxSizeBytes = maxSizeBytes; this.nodeMap = new Map(); this.headNode = null; this.tailNode = null; this.currentSizeBytes = 0; } peek(cacheKey) { const targetNode = this.nodeMap.get(cacheKey); return targetNode ? targetNode.value : null; } get(cacheKey, extremeStaleMilliseconds) { const targetNode = this.nodeMap.get(cacheKey); if (!targetNode) return null; if ( Date.now() > targetNode.value.expiryTimestamp + extremeStaleMilliseconds ) { this.delete(cacheKey); return null; } this._moveNodeToHead(targetNode); return targetNode.value; } set(cacheKey, cacheValue, extremeStaleMilliseconds) { const existingNode = this.nodeMap.get(cacheKey); if (existingNode) { const oldCacheValue = existingNode.value; cacheValue.hitTimestamps = oldCacheValue.hitTimestamps; cacheValue.currentHitIndex = oldCacheValue.currentHitIndex; cacheValue.totalHitCount = oldCacheValue.totalHitCount; cacheValue.lastGlobalWriteTimestamp = oldCacheValue.lastGlobalWriteTimestamp || cacheValue.lastGlobalWriteTimestamp; this.delete(cacheKey); } cacheValue.hardExpiryTimestamp = cacheValue.expiryTimestamp + extremeStaleMilliseconds; const newNode = new LRUCacheNode(cacheKey, cacheValue); this._addNodeToHead(newNode); this.nodeMap.set(cacheKey, newNode); this.currentSizeBytes += cacheValue.payloadSize || 0; this._evictStaleNodes(); } delete(cacheKey) { const targetNode = this.nodeMap.get(cacheKey); if (!targetNode) return; this._removeNode(targetNode); this.nodeMap.delete(cacheKey); this.currentSizeBytes = Math.max( 0, this.currentSizeBytes - (targetNode.value.payloadSize || 0), ); } _evictStaleNodes() { while ( (this.nodeMap.size > this.maxItemCount || this.currentSizeBytes > this.maxSizeBytes) && this.tailNode ) { this.delete(this.tailNode.key); } } _addNodeToHead(targetNode) { targetNode.next = this.headNode; targetNode.prev = null; if (this.headNode) this.headNode.prev = targetNode; this.headNode = targetNode; if (!this.tailNode) this.tailNode = targetNode; } _removeNode(targetNode) { if (targetNode.prev) targetNode.prev.next = targetNode.next; if (targetNode.next) targetNode.next.prev = targetNode.prev; if (this.headNode === targetNode) this.headNode = targetNode.next; if (this.tailNode === targetNode) this.tailNode = targetNode.prev; targetNode.prev = targetNode.next = null; } _moveNodeToHead(targetNode) { this._removeNode(targetNode); this._addNodeToHead(targetNode); } } const primaryMemoryCache = new MemoryLRUCache( ISOLATE_CONSTANTS.MEMORY_CACHE_LIMIT, ISOLATE_CONSTANTS.MEMORY_CACHE_SIZE_BYTES, ); const requestCoalescingMap = new Map(); const activeGlobalWriteLocks = new Set(); let rateLimitWindowStartTimestamp = 0; let rateLimitWriteCount = 0; /* ========================================================================== 5. DNS PACKET PARSER & MANIPULATOR ========================================================================== */ class DnsPacketProcessor { static skipDnsNameSafely(dataView, startOffset) { let currentOffset = startOffset; let iterationCount = 0; while (currentOffset < dataView.byteLength) { if (iterationCount++ > DNS_CONSTANTS.MAX_NAME_ITERATIONS) throw new Error(ERRORS.LOOP); const labelLength = dataView.getUint8(currentOffset); if (labelLength === 0) return currentOffset + 1; if ((labelLength & 0xc0) === 0xc0) { if (currentOffset + 2 > dataView.byteLength) throw new Error(ERRORS.OOB); return currentOffset + 2; } currentOffset += labelLength + 1; if (currentOffset > dataView.byteLength) throw new Error(ERRORS.OOB); } throw new Error(ERRORS.OOB); } static verifyEcsOptionPresence(packetBuffer) { try { const dataView = new DataView(packetBuffer); if (packetBuffer.byteLength < DNS_CONSTANTS.HEADER_LEN) return false; let currentOffset = DNS_CONSTANTS.HEADER_LEN; const questionCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_QDCOUNT); const answerCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_ANCOUNT); const authorityCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_NSCOUNT); const additionalCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_ARCOUNT); for (let index = 0; index < questionCount; index++) { currentOffset = this.skipDnsNameSafely(dataView, currentOffset); currentOffset += 4; } for (let index = 0; index < answerCount + authorityCount; index++) { currentOffset = this.skipDnsNameSafely(dataView, currentOffset); if (currentOffset + 10 > dataView.byteLength) return false; currentOffset += 8 + 2 + dataView.getUint16(currentOffset + 8); } for (let index = 0; index < additionalCount; index++) { currentOffset = this.skipDnsNameSafely(dataView, currentOffset); if (currentOffset + 10 > dataView.byteLength) return false; const recordType = dataView.getUint16(currentOffset); const recordDataLength = dataView.getUint16(currentOffset + 8); currentOffset += 10; if (recordType === DNS_CONSTANTS.TYPE_OPT) { let optionOffset = currentOffset; const optionEndOffset = currentOffset + recordDataLength; if (optionEndOffset > dataView.byteLength) return false; while (optionOffset + 4 <= optionEndOffset) { if (dataView.getUint16(optionOffset) === DNS_CONSTANTS.OPT_CODE_ECS) return true; optionOffset += 4 + dataView.getUint16(optionOffset + 2); } } currentOffset += recordDataLength; } return false; } catch { return false; } } static constructEcsOptionPayload(clientIpAddress, fallbackIpAddress) { const targetIp = clientIpAddress || fallbackIpAddress; const isIPv4Format = targetIp.indexOf(":") === -1; if (isIPv4Format) { const ipOctets = targetIp .split(".") .map((octet) => Math.max(0, Math.min(255, Number(octet) || 0))); const payloadBytes = [ipOctets[0], ipOctets[1], ipOctets[2]]; const totalOptionLength = 4 + payloadBytes.length; const optionBuffer = new Uint8Array(4 + totalOptionLength); optionBuffer.set( [ 0x00, DNS_CONSTANTS.OPT_CODE_ECS, (totalOptionLength >> 8) & 0xff, totalOptionLength & 0xff, 0x00, 0x01, 24, 0x00, ], 0, ); optionBuffer.set(payloadBytes, 8); return optionBuffer; } else { const ipSegments = targetIp.split("::"); const leftSegments = ipSegments[0] ? ipSegments[0].split(":").filter(Boolean) : []; const rightSegments = ipSegments.length > 1 && ipSegments[1] ? ipSegments[1].split(":").filter(Boolean) : []; const leftNumericSegments = leftSegments.map( (hexStr) => parseInt(hexStr, 16) || 0, ); const rightNumericSegments = rightSegments.map( (hexStr) => parseInt(hexStr, 16) || 0, ); const combinedSegments = [...leftNumericSegments]; const missingSegmentsCount = Math.max( 0, 8 - (leftNumericSegments.length + rightNumericSegments.length), ); for (let index = 0; index < missingSegmentsCount; index++) combinedSegments.push(0); combinedSegments.push(...rightNumericSegments); const ipByteArray = new Uint8Array(16); for (let index = 0; index < 8; index++) { ipByteArray[index * 2] = (combinedSegments[index] >> 8) & 0xff; ipByteArray[index * 2 + 1] = combinedSegments[index] & 0xff; } const prefixPayloadBytes = ipByteArray.slice(0, 7); // /56 prefix length const totalOptionLength = 4 + prefixPayloadBytes.length; const optionBuffer = new Uint8Array(4 + totalOptionLength); optionBuffer.set( [ 0x00, DNS_CONSTANTS.OPT_CODE_ECS, (totalOptionLength >> 8) & 0xff, totalOptionLength & 0xff, 0x00, 0x02, 56, 0x00, ], 0, ); optionBuffer.set(prefixPayloadBytes, 8); return optionBuffer; } } static injectEcsPayload( originalQueryBuffer, clientIpAddress, fallbackIpAddress, ) { try { if (this.verifyEcsOptionPresence(originalQueryBuffer)) return originalQueryBuffer; const dataView = new DataView(originalQueryBuffer); if (originalQueryBuffer.byteLength < DNS_CONSTANTS.HEADER_LEN) return originalQueryBuffer; const questionCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_QDCOUNT); const answerCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_ANCOUNT); const authorityCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_NSCOUNT); const additionalCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_ARCOUNT); let currentOffset = DNS_CONSTANTS.HEADER_LEN; for (let index = 0; index < questionCount; index++) { currentOffset = this.skipDnsNameSafely(dataView, currentOffset); currentOffset += 4; } for (let index = 0; index < answerCount + authorityCount; index++) { currentOffset = this.skipDnsNameSafely(dataView, currentOffset); if (currentOffset + 10 > dataView.byteLength) throw new Error(ERRORS.OOB); currentOffset += 8 + 2 + dataView.getUint16(currentOffset + 8); } let targetRecordOffset = -1; let targetDataLengthOffset = -1; let targetDataLength = 0; for (let index = 0; index < additionalCount; index++) { const recordStartOffset = currentOffset; currentOffset = this.skipDnsNameSafely(dataView, currentOffset); if (currentOffset + 10 > dataView.byteLength) throw new Error(ERRORS.OOB); const recordType = dataView.getUint16(currentOffset); if (recordType === DNS_CONSTANTS.TYPE_OPT) { targetRecordOffset = recordStartOffset; targetDataLengthOffset = currentOffset + 8; targetDataLength = dataView.getUint16(targetDataLengthOffset); break; } currentOffset += 8 + 2 + dataView.getUint16(currentOffset + 8); } const ecsOptionBuffer = this.constructEcsOptionPayload( clientIpAddress, fallbackIpAddress, ); if (targetRecordOffset !== -1) { const insertionPosition = targetDataLengthOffset + 2 + targetDataLength; if (insertionPosition > originalQueryBuffer.byteLength) throw new Error(ERRORS.OOB); const modifiedBufferLength = originalQueryBuffer.byteLength + ecsOptionBuffer.length; const modifiedBuffer = new Uint8Array(modifiedBufferLength); modifiedBuffer.set( new Uint8Array(originalQueryBuffer, 0, insertionPosition), 0, ); modifiedBuffer.set(ecsOptionBuffer, insertionPosition); modifiedBuffer.set( new Uint8Array(originalQueryBuffer, insertionPosition), insertionPosition + ecsOptionBuffer.length, ); new DataView(modifiedBuffer.buffer).setUint16( targetDataLengthOffset, targetDataLength + ecsOptionBuffer.length, ); return modifiedBuffer.buffer; } else { const insertionPosition = originalQueryBuffer.byteLength; const modifiedBufferLength = insertionPosition + DNS_CONSTANTS.OPT_HEADER_TEMPLATE.length + ecsOptionBuffer.length; const modifiedBuffer = new Uint8Array(modifiedBufferLength); modifiedBuffer.set(new Uint8Array(originalQueryBuffer), 0); modifiedBuffer.set( DNS_CONSTANTS.OPT_HEADER_TEMPLATE, insertionPosition, ); const modifiedDataView = new DataView(modifiedBuffer.buffer); modifiedDataView.setUint16( insertionPosition + DNS_CONSTANTS.OPT_HEADER_TEMPLATE.length - 2, ecsOptionBuffer.length, ); modifiedBuffer.set( ecsOptionBuffer, insertionPosition + DNS_CONSTANTS.OPT_HEADER_TEMPLATE.length, ); modifiedDataView.setUint16( DNS_CONSTANTS.OFFSET_ARCOUNT, (additionalCount + 1) & 0xffff, ); return modifiedBuffer.buffer; } } catch { return originalQueryBuffer; } } static stripEcsPayload(responseBuffer) { try { const dataView = new DataView(responseBuffer); if (responseBuffer.byteLength < DNS_CONSTANTS.HEADER_LEN) return responseBuffer; const questionCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_QDCOUNT); const answerCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_ANCOUNT); const authorityCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_NSCOUNT); const additionalCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_ARCOUNT); let currentOffset = DNS_CONSTANTS.HEADER_LEN; for (let index = 0; index < questionCount; index++) { currentOffset = this.skipDnsNameSafely(dataView, currentOffset); currentOffset += 4; } for (let index = 0; index < answerCount + authorityCount; index++) { currentOffset = this.skipDnsNameSafely(dataView, currentOffset); if (currentOffset + 10 > dataView.byteLength) throw new Error(ERRORS.OOB); currentOffset += 8 + 2 + dataView.getUint16(currentOffset + 8); } for (let index = 0; index < additionalCount; index++) { currentOffset = this.skipDnsNameSafely(dataView, currentOffset); if (currentOffset + 10 > dataView.byteLength) break; const recordType = dataView.getUint16(currentOffset); const dataLengthOffset = currentOffset + 8; const recordDataLength = dataView.getUint16(dataLengthOffset); if (recordType === DNS_CONSTANTS.TYPE_OPT) { let innerOptionOffset = dataLengthOffset + 2; const innerOptionEnd = innerOptionOffset + recordDataLength; if (innerOptionEnd > dataView.byteLength) throw new Error(ERRORS.OOB); let ecsDataOffset = -1; let ecsDataLength = 0; while (innerOptionOffset + 4 <= innerOptionEnd) { const optionCode = dataView.getUint16(innerOptionOffset); const optionLength = dataView.getUint16(innerOptionOffset + 2); if (optionCode === DNS_CONSTANTS.OPT_CODE_ECS) { ecsDataOffset = innerOptionOffset; ecsDataLength = 4 + optionLength; break; } innerOptionOffset += 4 + optionLength; } if (ecsDataOffset !== -1) { const strippedBufferLength = responseBuffer.byteLength - ecsDataLength; const strippedBuffer = new Uint8Array(strippedBufferLength); strippedBuffer.set( new Uint8Array(responseBuffer, 0, ecsDataOffset), 0, ); strippedBuffer.set( new Uint8Array(responseBuffer, ecsDataOffset + ecsDataLength), ecsDataOffset, ); new DataView(strippedBuffer.buffer).setUint16( dataLengthOffset, recordDataLength - ecsDataLength, ); return strippedBuffer.buffer; } } currentOffset += 10 + recordDataLength; } return responseBuffer; } catch { return responseBuffer; } } static extractTtlAndResponseCode(responseBuffer, defaultPositiveTtlValue) { let responseCode = 0; try { const dataView = new DataView(responseBuffer); if (responseBuffer.byteLength < DNS_CONSTANTS.HEADER_LEN) return { extractedTtl: defaultPositiveTtlValue, responseCode }; responseCode = dataView.getUint8(3) & 0x0f; let currentOffset = DNS_CONSTANTS.HEADER_LEN; const questionCount = dataView.getUint16(DNS_CONSTANTS.OFFSET_QDCOUNT); const totalResourceRecords = dataView.getUint16(DNS_CONSTANTS.OFFSET_ANCOUNT) + dataView.getUint16(DNS_CONSTANTS.OFFSET_NSCOUNT) + dataView.getUint16(DNS_CONSTANTS.OFFSET_ARCOUNT); for (let index = 0; index < questionCount; index++) { currentOffset = this.skipDnsNameSafely(dataView, currentOffset); if (currentOffset + 4 > dataView.byteLength) return { extractedTtl: defaultPositiveTtlValue, responseCode }; currentOffset += 4; } let minimumTtlValue = Infinity; for (let index = 0; index < totalResourceRecords; index++) { if (currentOffset >= dataView.byteLength) break; currentOffset = this.skipDnsNameSafely(dataView, currentOffset); if (currentOffset + 10 > dataView.byteLength) break; const recordType = dataView.getUint16(currentOffset); currentOffset += 4; const recordTtl = dataView.getUint32(currentOffset); currentOffset += 4; const recordDataLength = dataView.getUint16(currentOffset); currentOffset += 2; if (currentOffset + recordDataLength > dataView.byteLength) break; if (recordType !== DNS_CONSTANTS.TYPE_OPT) minimumTtlValue = Math.min(minimumTtlValue, recordTtl); currentOffset += recordDataLength; } return { extractedTtl: isFinite(minimumTtlValue) && minimumTtlValue > 0 ? minimumTtlValue : defaultPositiveTtlValue, responseCode, }; } catch { return { extractedTtl: defaultPositiveTtlValue, responseCode }; } } static generateStableCacheKey(processedQueryBuffer) { try { if (processedQueryBuffer.byteLength < DNS_CONSTANTS.HEADER_LEN) throw new Error("Invalid Buffer"); const normalizedDataView = new Uint8Array(processedQueryBuffer.slice(0)); normalizedDataView[0] = 0; normalizedDataView[1] = 0; return generateFnv1a32Hex(normalizedDataView); } catch { return generateFnv1a32Hex(new Uint8Array(processedQueryBuffer).slice(2)); } } static patchTransactionId(payloadByteArray, transactionIdByteArray) { const patchedBuffer = new Uint8Array(payloadByteArray.byteLength); patchedBuffer.set(payloadByteArray); patchedBuffer[0] = transactionIdByteArray[0]; patchedBuffer[1] = transactionIdByteArray[1]; return patchedBuffer.buffer; } } /* ========================================================================== 6. REQUEST HANDLER ========================================================================== */ class DoHRequestHandler { static initializeConfig(environmentVariables) { const parseArraySafely = (jsonString, fallbackArray) => { if (!jsonString) return fallbackArray; try { return JSON.parse(jsonString); } catch { return fallbackArray; } }; return { UPSTREAM_URLS: parseArraySafely( environmentVariables.UPSTREAM_URLS, RUNTIME_DEFAULTS.UPSTREAM_URLS, ), DOH_CONTENT_TYPE: environmentVariables.DOH_CONTENT_TYPE || RUNTIME_DEFAULTS.DOH_CONTENT_TYPE, DOH_PATH: environmentVariables.DOH_PATH || RUNTIME_DEFAULTS.DOH_PATH, ROOT_CONTENT: environmentVariables.ROOT_CONTENT || RUNTIME_DEFAULTS.ROOT_CONTENT, ROOT_CONTENT_TYPE: environmentVariables.ROOT_CONTENT_TYPE || RUNTIME_DEFAULTS.ROOT_CONTENT_TYPE, ROOT_CACHE_TTL: environmentVariables.ROOT_CACHE_TTL ? parseInt(environmentVariables.ROOT_CACHE_TTL, 10) : RUNTIME_DEFAULTS.ROOT_CACHE_TTL, MAX_CACHEABLE_BYTES: environmentVariables.MAX_CACHEABLE_BYTES ? parseInt(environmentVariables.MAX_CACHEABLE_BYTES, 10) : RUNTIME_DEFAULTS.MAX_CACHEABLE_BYTES, MAX_POST_BODY_BYTES: environmentVariables.MAX_POST_BODY_BYTES ? parseInt(environmentVariables.MAX_POST_BODY_BYTES, 10) : RUNTIME_DEFAULTS.MAX_POST_BODY_BYTES, FETCH_TIMEOUT_MS: environmentVariables.FETCH_TIMEOUT_MS ? parseInt(environmentVariables.FETCH_TIMEOUT_MS, 10) : RUNTIME_DEFAULTS.FETCH_TIMEOUT_MS, MAX_RETRIES: environmentVariables.MAX_RETRIES ? parseInt(environmentVariables.MAX_RETRIES, 10) : RUNTIME_DEFAULTS.MAX_RETRIES, DEFAULT_POSITIVE_TTL: environmentVariables.DEFAULT_POSITIVE_TTL ? parseInt(environmentVariables.DEFAULT_POSITIVE_TTL, 10) : RUNTIME_DEFAULTS.DEFAULT_POSITIVE_TTL, DEFAULT_NEGATIVE_TTL: environmentVariables.DEFAULT_NEGATIVE_TTL ? parseInt(environmentVariables.DEFAULT_NEGATIVE_TTL, 10) : RUNTIME_DEFAULTS.DEFAULT_NEGATIVE_TTL, FALLBACK_ECS_IP: environmentVariables.FALLBACK_ECS_IP || RUNTIME_DEFAULTS.FALLBACK_ECS_IP, CF_CACHE_WRITE_THRESHOLD: environmentVariables.CF_CACHE_WRITE_THRESHOLD ? parseInt(environmentVariables.CF_CACHE_WRITE_THRESHOLD, 10) : RUNTIME_DEFAULTS.CF_CACHE_WRITE_THRESHOLD, GLOBAL_WRITE_COOLDOWN_MS: environmentVariables.GLOBAL_WRITE_COOLDOWN_MS ? parseInt(environmentVariables.GLOBAL_WRITE_COOLDOWN_MS, 10) : RUNTIME_DEFAULTS.GLOBAL_WRITE_COOLDOWN_MS, GLOBAL_WRITE_PER_MINUTE_LIMIT: environmentVariables.GLOBAL_WRITE_PER_MINUTE_LIMIT ? parseInt(environmentVariables.GLOBAL_WRITE_PER_MINUTE_LIMIT, 10) : RUNTIME_DEFAULTS.GLOBAL_WRITE_PER_MINUTE_LIMIT, HOT_WINDOW_MS: environmentVariables.HOT_WINDOW_MS ? parseInt(environmentVariables.HOT_WINDOW_MS, 10) : RUNTIME_DEFAULTS.HOT_WINDOW_MS, HOT_HIT_THRESHOLD: environmentVariables.HOT_HIT_THRESHOLD ? parseInt(environmentVariables.HOT_HIT_THRESHOLD, 10) : RUNTIME_DEFAULTS.HOT_HIT_THRESHOLD, STALE_WINDOW_FACTOR: environmentVariables.STALE_WINDOW_FACTOR ? parseFloat(environmentVariables.STALE_WINDOW_FACTOR) : RUNTIME_DEFAULTS.STALE_WINDOW_FACTOR, EXTREME_STALE_FALLBACK_MS: environmentVariables.EXTREME_STALE_FALLBACK_MS ? parseInt(environmentVariables.EXTREME_STALE_FALLBACK_MS, 10) : RUNTIME_DEFAULTS.EXTREME_STALE_FALLBACK_MS, JITTER_PCT: environmentVariables.JITTER_PCT ? parseInt(environmentVariables.JITTER_PCT, 10) : RUNTIME_DEFAULTS.JITTER_PCT, GLOBAL_CACHE_NAMESPACE: environmentVariables.GLOBAL_CACHE_NAMESPACE || RUNTIME_DEFAULTS.GLOBAL_CACHE_NAMESPACE, }; } static checkWriteRateLimit(requestConfig) { const currentTimestamp = Date.now(); if (currentTimestamp - rateLimitWindowStartTimestamp > 60 * 1000) { rateLimitWindowStartTimestamp = currentTimestamp; rateLimitWriteCount = 0; } if (rateLimitWriteCount < requestConfig.GLOBAL_WRITE_PER_MINUTE_LIMIT) { rateLimitWriteCount++; return true; } return false; } static calculateDeterministicJitter(cacheKeyHex, jitterPercentage) { const hashSuffixValue = parseInt(cacheKeyHex.slice(-4), 16) % 10000; const signedJitter = (hashSuffixValue / 10000) * (2 * jitterPercentage) - jitterPercentage; return 1 + signedJitter / 100.0; } static createResponse( payloadByteArray, transactionId, hasClientEcs, cacheAgeSeconds, cacheStatusIndicator, cacheKeyHex, requestConfig, httpStatus = 200, supplementalHeaders = {}, ) { let finalResponseBuffer = DnsPacketProcessor.patchTransactionId( payloadByteArray, transactionId, ); if (!hasClientEcs) finalResponseBuffer = DnsPacketProcessor.stripEcsPayload(finalResponseBuffer); const responseHeaders = { "Content-Type": requestConfig.DOH_CONTENT_TYPE, "Access-Control-Allow-Origin": "*", "Cache-Control": `public, max-age=${cacheAgeSeconds}`, "X-DOHFLARE-Cache": cacheStatusIndicator, "X-DOHFLARE-CacheKey": cacheKeyHex, "X-DOHFLARE-TTL": String(cacheAgeSeconds), "Content-Length": String(finalResponseBuffer.byteLength), ...supplementalHeaders, }; return new Response(finalResponseBuffer, { status: httpStatus, headers: responseHeaders, }); } static generateLandingPage(request, reqConfig) { const currentYear = new Date().getFullYear(); const serviceHostname = new URL(request.url).hostname; const fullServiceEndpoint = `https://${serviceHostname}${reqConfig.DOH_PATH}`; return `DoHflare | Edge DNS Proxy

DoHflare

High-Performance DNS over HTTPS Edge Proxy for Cloudflare Workers & Pages

Multi-Tiered Caching

Combines L1 isolated in-memory caching with L2 distributed global storage. Strictly enforces TTL constraints to slash upstream resolution latency and maximize responsiveness.

Precision Traffic Steering

Full EDNS0 Client Subnet (ECS) support with privacy-preserving truncation. Directs CDNs to serve the optimal edge node IPs, dramatically improving resolution quality in complex network regions.

Resilient Cache Logic

Distributes cache keys via collision-resistant FNV-1a hashing. Paired with deterministic TTL jitter, it prevents cache stampedes and ensures rock-solid efficiency under high-concurrency loads.

Intelligent Traffic Shaping

Mitigates upstream overload and protocol abuse through request coalescing, payload validation, and global write-rate limiting, guaranteeing sustained service stability.

Resolver Endpoint URL
${fullServiceEndpoint}
`; } static async processIncomingRequest( incomingRequest, environmentVariables, executionContext, ) { const requestConfig = this.initializeConfig(environmentVariables); const parsedUrl = new URL(incomingRequest.url); if (incomingRequest.method === "OPTIONS") { return new Response(null, { status: 204, headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Accept", "Access-Control-Max-Age": "86400", }, }); } if (parsedUrl.pathname !== requestConfig.DOH_PATH) { if (parsedUrl.pathname === "/") { let responseContent; let responseType; if ( requestConfig.ROOT_CONTENT && requestConfig.ROOT_CONTENT.trim() !== "" ) { responseContent = requestConfig.ROOT_CONTENT; responseType = requestConfig.ROOT_CONTENT_TYPE; } else { responseContent = this.generateLandingPage( incomingRequest, requestConfig, ); responseType = "text/html; charset=utf-8"; } return new Response(responseContent, { status: 200, headers: { "Content-Type": responseType, "Cache-Control": `public, max-age=${requestConfig.ROOT_CACHE_TTL}`, "X-DOHFLARE-Mode": requestConfig.ROOT_CONTENT ? "Custom" : "Template", }, }); } return new Response("Not Found", { status: 404 }); } let rawQueryBuffer; if (incomingRequest.method === "GET") { const dnsQueryParam = parsedUrl.searchParams.get("dns"); if (!dnsQueryParam) return new Response("Bad Request: missing dns", { status: 400 }); try { rawQueryBuffer = convertBase64UrlToBuffer(dnsQueryParam); } catch { return new Response("Bad Request: invalid base64url", { status: 400 }); } } else if (incomingRequest.method === "POST") { if ( !(incomingRequest.headers.get("content-type") || "").includes( requestConfig.DOH_CONTENT_TYPE, ) ) return new Response("Unsupported Media Type", { status: 415 }); try { rawQueryBuffer = await incomingRequest.arrayBuffer(); if (rawQueryBuffer.byteLength > requestConfig.MAX_POST_BODY_BYTES) return new Response("Payload Too Large", { status: 413 }); } catch { return new Response("Bad Request", { status: 400 }); } } else { return new Response("Method Not Allowed", { status: 405 }); } if (rawQueryBuffer.byteLength < DNS_CONSTANTS.HEADER_LEN) return new Response("Malformed DNS Query", { status: 400 }); const transactionIdByteArray = new Uint8Array(rawQueryBuffer.slice(0, 2)); const resolvedClientIp = normalizeClientIp( incomingRequest.headers.get("cf-connecting-ip"), requestConfig.FALLBACK_ECS_IP, ); const hasClientEcs = DnsPacketProcessor.verifyEcsOptionPresence(rawQueryBuffer); const processedQueryBuffer = DnsPacketProcessor.injectEcsPayload( rawQueryBuffer, resolvedClientIp, requestConfig.FALLBACK_ECS_IP, ); const cacheKeyHex = DnsPacketProcessor.generateStableCacheKey(processedQueryBuffer); const probabilitySample = parseInt(cacheKeyHex.slice(-4), 16) % 10000; const currentTimestamp = Date.now(); const memoryCacheEntry = primaryMemoryCache.get( cacheKeyHex, requestConfig.EXTREME_STALE_FALLBACK_MS, ); if (memoryCacheEntry) { if (!memoryCacheEntry.hitTimestamps) { memoryCacheEntry.hitTimestamps = new Float64Array( requestConfig.HOT_HIT_THRESHOLD, ); memoryCacheEntry.currentHitIndex = 0; memoryCacheEntry.totalHitCount = 0; } memoryCacheEntry.hitTimestamps[memoryCacheEntry.currentHitIndex] = currentTimestamp; memoryCacheEntry.currentHitIndex = (memoryCacheEntry.currentHitIndex + 1) % requestConfig.HOT_HIT_THRESHOLD; if (memoryCacheEntry.totalHitCount < requestConfig.HOT_HIT_THRESHOLD) memoryCacheEntry.totalHitCount++; if (currentTimestamp < memoryCacheEntry.expiryTimestamp) { const cacheAgeSeconds = Math.max( 0, Math.floor( (memoryCacheEntry.expiryTimestamp - currentTimestamp) / 1000, ), ); return this.createResponse( memoryCacheEntry.payloadByteArray, transactionIdByteArray, hasClientEcs, cacheAgeSeconds, "HIT-L1", cacheKeyHex, requestConfig, ); } const staleWindowMilliseconds = Math.floor( (memoryCacheEntry.expiryTimestamp - (memoryCacheEntry.storedTimestamp || memoryCacheEntry.expiryTimestamp)) * requestConfig.STALE_WINDOW_FACTOR, ); if ( currentTimestamp < memoryCacheEntry.expiryTimestamp + staleWindowMilliseconds ) { if (!requestCoalescingMap.has(cacheKeyHex)) { executionContext.waitUntil( this.resolveUpstream( processedQueryBuffer, cacheKeyHex, probabilitySample, true, null, null, requestConfig, ).catch(() => {}), ); } return this.createResponse( memoryCacheEntry.payloadByteArray, transactionIdByteArray, hasClientEcs, 0, "HIT-L1-STALE", cacheKeyHex, requestConfig, ); } } const globalCacheRequestUrl = new Request( `${requestConfig.GLOBAL_CACHE_NAMESPACE}${cacheKeyHex}`, ); if (probabilitySample < requestConfig.CF_CACHE_WRITE_THRESHOLD) { try { const globalCacheResponse = await caches.default.match( globalCacheRequestUrl, ); if (globalCacheResponse && globalCacheResponse.ok) { const responseArrayBuffer = await globalCacheResponse.arrayBuffer(); if ( responseArrayBuffer.byteLength <= requestConfig.MAX_CACHEABLE_BYTES ) { const payloadByteArray = new Uint8Array(responseArrayBuffer); const ttlFromHeader = Number( globalCacheResponse.headers.get("X-DOHFLARE-Original-TTL") || requestConfig.DEFAULT_POSITIVE_TTL, ); primaryMemoryCache.set( cacheKeyHex, { payloadByteArray: payloadByteArray, expiryTimestamp: Date.now() + ttlFromHeader * 1000, payloadSize: payloadByteArray.byteLength, lastGlobalWriteTimestamp: Date.now(), storedTimestamp: Date.now(), }, requestConfig.EXTREME_STALE_FALLBACK_MS, ); return this.createResponse( payloadByteArray, transactionIdByteArray, hasClientEcs, ttlFromHeader, "HIT-L2-GLOBAL", cacheKeyHex, requestConfig, ); } } } catch (error) { /* Background degradation handling */ } } if (requestCoalescingMap.has(cacheKeyHex)) { try { const coalescedResult = await requestCoalescingMap.get(cacheKeyHex); return this.createResponse( coalescedResult.payloadByteArray, transactionIdByteArray, hasClientEcs, coalescedResult.jitteredTtl, "MISS-COALESCED", cacheKeyHex, requestConfig, coalescedResult.httpStatus, ); } catch (error) { /* Coalescing fallback triggers internal miss */ } } let promiseResolver = (value) => {}; let promiseRejecter = (reason) => {}; const upstreamFetchPromise = new Promise((resolve, reject) => { promiseResolver = resolve; promiseRejecter = reject; }); requestCoalescingMap.set(cacheKeyHex, upstreamFetchPromise); try { const resolutionResult = await this.resolveUpstream( processedQueryBuffer, cacheKeyHex, probabilitySample, false, executionContext, globalCacheRequestUrl, requestConfig, ); promiseResolver(resolutionResult); return this.createResponse( resolutionResult.payloadByteArray, transactionIdByteArray, hasClientEcs, resolutionResult.jitteredTtl, "MISS", cacheKeyHex, requestConfig, resolutionResult.httpStatus || 200, { "X-DOHFLARE-Write-Decision": resolutionResult.cacheWriteDecision, "X-DOHFLARE-RCODE": String(resolutionResult.responseCode || 0), }, ); } catch (resolutionError) { promiseRejecter(resolutionError); const staleCacheEntry = primaryMemoryCache.peek(cacheKeyHex); if (staleCacheEntry && staleCacheEntry.payloadByteArray) { return this.createResponse( staleCacheEntry.payloadByteArray, transactionIdByteArray, hasClientEcs, 0, "HIT-L1-STALE-FALLBACK", cacheKeyHex, requestConfig, 200, { "X-DOHFLARE-Degraded": "1" }, ); } return new Response("Bad Gateway", { status: 502, headers: { "X-DOHFLARE-Code": "UPSTREAM_ERR" }, }); } finally { requestCoalescingMap.delete(cacheKeyHex); } } static async resolveUpstream( processedQueryBuffer, cacheKeyHex, probabilitySample, isBackgroundExecution, executionContext, globalCacheRequestUrl, requestConfig, ) { let internalResolutionError = null; const maxAttempts = requestConfig.MAX_RETRIES + 1; const urlsCount = requestConfig.UPSTREAM_URLS.length; for (let attempt = 0; attempt < maxAttempts; attempt++) { const targetUrl = requestConfig.UPSTREAM_URLS[attempt % urlsCount]; try { return await this.fetchUpstream( targetUrl, processedQueryBuffer, cacheKeyHex, probabilitySample, isBackgroundExecution, executionContext, globalCacheRequestUrl, requestConfig, ); } catch (error) { internalResolutionError = error; } } throw internalResolutionError || new Error(ERRORS.TIMEOUT); } static async fetchUpstream( upstreamUrlEndpoint, processedQueryBuffer, cacheKeyHex, probabilitySample, isBackgroundExecution, executionContext, globalCacheRequestUrl, requestConfig, ) { const upstreamResponse = await executeFetchWithTimeout( upstreamUrlEndpoint, { method: "POST", headers: { "Content-Type": requestConfig.DOH_CONTENT_TYPE, Accept: requestConfig.DOH_CONTENT_TYPE, }, body: processedQueryBuffer, }, requestConfig.FETCH_TIMEOUT_MS, ); if (!upstreamResponse.ok) throw new Error(`Upstream HTTP ${upstreamResponse.status}`); const responseArrayBuffer = await upstreamResponse.arrayBuffer(); if (responseArrayBuffer.byteLength > requestConfig.MAX_CACHEABLE_BYTES) throw new Error(ERRORS.OVERSIZED); const payloadByteArray = new Uint8Array(responseArrayBuffer); const { extractedTtl, responseCode } = DnsPacketProcessor.extractTtlAndResponseCode( responseArrayBuffer, requestConfig.DEFAULT_POSITIVE_TTL, ); const effectiveTtlValue = responseCode === DNS_CONSTANTS.RCODE_NXDOMAIN ? requestConfig.DEFAULT_NEGATIVE_TTL : extractedTtl; const jitteredTtl = Math.max( 1, Math.floor( effectiveTtlValue * this.calculateDeterministicJitter( cacheKeyHex, requestConfig.JITTER_PCT, ), ), ); primaryMemoryCache.set( cacheKeyHex, { payloadByteArray: payloadByteArray, expiryTimestamp: Date.now() + jitteredTtl * 1000, payloadSize: payloadByteArray.byteLength, storedTimestamp: Date.now(), }, requestConfig.EXTREME_STALE_FALLBACK_MS, ); let cacheWriteDecision = "SKIP"; if (!isBackgroundExecution && executionContext && globalCacheRequestUrl) { const existingCacheEntry = primaryMemoryCache.peek(cacheKeyHex); let forceHotWrite = false; if ( existingCacheEntry && existingCacheEntry.totalHitCount === requestConfig.HOT_HIT_THRESHOLD ) { if ( Date.now() - existingCacheEntry.hitTimestamps[ existingCacheEntry.currentHitIndex ] <= requestConfig.HOT_WINDOW_MS ) forceHotWrite = true; } if ( probabilitySample < requestConfig.CF_CACHE_WRITE_THRESHOLD || forceHotWrite ) { const lastGlobalWriteTimestamp = existingCacheEntry ? existingCacheEntry.lastGlobalWriteTimestamp || 0 : 0; if ( Date.now() - lastGlobalWriteTimestamp > requestConfig.GLOBAL_WRITE_COOLDOWN_MS && this.checkWriteRateLimit(requestConfig) ) { if (!activeGlobalWriteLocks.has(cacheKeyHex)) { activeGlobalWriteLocks.add(cacheKeyHex); try { const globalMatchResult = await caches.default.match( globalCacheRequestUrl, ); if (!globalMatchResult) { const globalCacheHeaders = new Headers({ "Content-Type": requestConfig.DOH_CONTENT_TYPE, "Cache-Control": `public, max-age=${jitteredTtl}`, "X-DOHFLARE-Original-TTL": String(jitteredTtl), }); if (existingCacheEntry) existingCacheEntry.lastGlobalWriteTimestamp = Date.now(); executionContext.waitUntil( caches.default .put( globalCacheRequestUrl, new Response(payloadByteArray.slice(0), { status: 200, headers: globalCacheHeaders, }), ) .catch(() => {}), ); cacheWriteDecision = forceHotWrite ? "WRITE-FORCE" : "WRITE-SCHEDULED"; } else { cacheWriteDecision = "ALREADY-PRESENT"; if (existingCacheEntry) existingCacheEntry.lastGlobalWriteTimestamp = Date.now(); } } catch { cacheWriteDecision = "GLOBAL-CHECK-ERR"; } finally { activeGlobalWriteLocks.delete(cacheKeyHex); } } else { cacheWriteDecision = "WRITE-LOCKED"; } } else { cacheWriteDecision = "COOLDOWN-OR-RATE-LIMIT"; } } } return { payloadByteArray, httpStatus: upstreamResponse.status, jitteredTtl, cacheWriteDecision, responseCode, }; } } /* ========================================================================== 7. WORKER ENTRY POINT ========================================================================== */ export default { async fetch(incomingRequest, environmentVariables, executionContext) { try { return await DoHRequestHandler.processIncomingRequest( incomingRequest, environmentVariables, executionContext, ); } catch (criticalError) { return new Response("Internal Server Error", { status: 500, headers: { "X-DOHFLARE-Code": "INTERNAL_FATAL" }, }); } }, };