mirror of
https://github.com/zizifn/edgetunnel
synced 2024-11-22 01:10:22 +00:00
add doh for udp dns
This commit is contained in:
parent
a6f0064a4c
commit
2b9927a1b1
@ -11,9 +11,10 @@
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20230518.0",
|
||||
"wrangler": "^3.0.1"
|
||||
"wrangler": "^3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"dns-packet": "^5.6.0",
|
||||
"ip-cidr": "^3.1.0",
|
||||
"ip-range-check": "^0.2.0",
|
||||
"ipaddr.js": "^2.0.1"
|
||||
|
@ -81,13 +81,14 @@ async function vlessOverWSHandler(request) {
|
||||
let remoteSocketWapper = {
|
||||
value: null,
|
||||
};
|
||||
let udpStreamWrite = null;
|
||||
let isDns = false;
|
||||
|
||||
// ws --> remote
|
||||
readableWebSocketStream.pipeTo(new WritableStream({
|
||||
async write(chunk, controller) {
|
||||
if (isDns) {
|
||||
return await handleDNSQuery(chunk, webSocket, null, log);
|
||||
return udpStreamWrite(chunk);
|
||||
}
|
||||
if (remoteSocketWapper.value) {
|
||||
const writer = remoteSocketWapper.value.writable.getWriter()
|
||||
@ -128,8 +129,12 @@ async function vlessOverWSHandler(request) {
|
||||
const vlessResponseHeader = new Uint8Array([vlessVersion[0], 0]);
|
||||
const rawClientData = chunk.slice(rawDataIndex);
|
||||
|
||||
// TODO: support udp here when cf runtime has udp support
|
||||
if (isDns) {
|
||||
return handleDNSQuery(rawClientData, webSocket, vlessResponseHeader, log);
|
||||
const { write } = await handleUDPOutBound(webSocket, vlessResponseHeader, log);
|
||||
udpStreamWrite = write;
|
||||
udpStreamWrite(rawClientData);
|
||||
return;
|
||||
}
|
||||
handleTCPOutBound(remoteSocketWapper, addressRemote, portRemote, rawClientData, webSocket, vlessResponseHeader, log);
|
||||
},
|
||||
@ -515,54 +520,77 @@ function stringify(arr, offset = 0) {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ArrayBuffer} udpChunk
|
||||
* @param {import("@cloudflare/workers-types").WebSocket} webSocket
|
||||
* @param {ArrayBuffer} vlessResponseHeader
|
||||
* @param {(string)=> void} log
|
||||
*/
|
||||
async function handleDNSQuery(udpChunk, webSocket, vlessResponseHeader, log) {
|
||||
// no matter which DNS server client send, we alwasy use hard code one.
|
||||
// beacsue someof DNS server is not support DNS over TCP
|
||||
try {
|
||||
const dnsServer = '8.8.4.4'; // change to 1.1.1.1 after cf fix connect own ip bug
|
||||
const dnsPort = 53;
|
||||
/** @type {ArrayBuffer | null} */
|
||||
let vlessHeader = vlessResponseHeader;
|
||||
/** @type {import("@cloudflare/workers-types").Socket} */
|
||||
const tcpSocket = connect({
|
||||
hostname: dnsServer,
|
||||
port: dnsPort,
|
||||
});
|
||||
async function handleUDPOutBound(webSocket, vlessResponseHeader, log) {
|
||||
|
||||
log(`connected to ${dnsServer}:${dnsPort}`);
|
||||
const writer = tcpSocket.writable.getWriter();
|
||||
await writer.write(udpChunk);
|
||||
writer.releaseLock();
|
||||
await tcpSocket.readable.pipeTo(new WritableStream({
|
||||
async write(chunk) {
|
||||
if (webSocket.readyState === WS_READY_STATE_OPEN) {
|
||||
if (vlessHeader) {
|
||||
webSocket.send(await new Blob([vlessHeader, chunk]).arrayBuffer());
|
||||
vlessHeader = null;
|
||||
} else {
|
||||
webSocket.send(chunk);
|
||||
}
|
||||
let isVlessHeaderSent = false;
|
||||
const transformStream = new TransformStream({
|
||||
start(controller) {
|
||||
|
||||
},
|
||||
transform(chunk, controller) {
|
||||
// udp message 2 byte is the the length of udp data
|
||||
// TODO: this should have bug, beacsue maybe udp chunk can be in two websocket message
|
||||
for (let index = 0; index < chunk.byteLength;) {
|
||||
const lengthBuffer = chunk.slice(index, index + 2);
|
||||
const udpPakcetLength = new DataView(lengthBuffer).getUint16(0);
|
||||
const udpData = new Uint8Array(
|
||||
chunk.slice(index + 2, index + 2 + udpPakcetLength)
|
||||
);
|
||||
index = index + 2 + udpPakcetLength;
|
||||
controller.enqueue(udpData);
|
||||
}
|
||||
},
|
||||
flush(controller) {
|
||||
}
|
||||
});
|
||||
|
||||
// only handle dns udp for now
|
||||
transformStream.readable.pipeTo(new WritableStream({
|
||||
async write(chunk) {
|
||||
const resp = await fetch('https://1.1.1.1/dns-query',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/dns-message',
|
||||
},
|
||||
body: chunk,
|
||||
})
|
||||
const dnsQueryResult = await resp.arrayBuffer();
|
||||
const udpSize = dnsQueryResult.byteLength;
|
||||
// console.log([...new Uint8Array(dnsQueryResult)].map((x) => x.toString(16)));
|
||||
const udpSizeBuffer = new Uint8Array([(udpSize >> 8) & 0xff, udpSize & 0xff]);
|
||||
if (webSocket.readyState === WS_READY_STATE_OPEN) {
|
||||
log(`doh success and dns message length is ${udpSize}`);
|
||||
if (isVlessHeaderSent) {
|
||||
webSocket.send(await new Blob([udpSizeBuffer, dnsQueryResult]).arrayBuffer());
|
||||
} else {
|
||||
webSocket.send(await new Blob([vlessResponseHeader, udpSizeBuffer, dnsQueryResult]).arrayBuffer());
|
||||
isVlessHeaderSent = true;
|
||||
}
|
||||
},
|
||||
close() {
|
||||
log(`dns server(${dnsServer}) tcp is close`);
|
||||
},
|
||||
abort(reason) {
|
||||
console.error(`dns server(${dnsServer}) tcp is abort`, reason);
|
||||
},
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`handleDNSQuery have exception, error: ${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
})).catch((error) => {
|
||||
log('dns udp has error' + error)
|
||||
});
|
||||
|
||||
const writer = transformStream.writable.getWriter();
|
||||
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {Uint8Array} chunk
|
||||
*/
|
||||
write(chunk) {
|
||||
writer.write(chunk);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,4 +8,4 @@ main = "src/worker-vless.js"
|
||||
compatibility_date = "2023-05-26"
|
||||
|
||||
[vars]
|
||||
UUID = "example_dev_token"
|
||||
# UUID = "example_dev_token"
|
Loading…
Reference in New Issue
Block a user