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",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/workers-types": "^4.20230518.0",
|
"@cloudflare/workers-types": "^4.20230518.0",
|
||||||
"wrangler": "^3.0.1"
|
"wrangler": "^3.1.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"dns-packet": "^5.6.0",
|
||||||
"ip-cidr": "^3.1.0",
|
"ip-cidr": "^3.1.0",
|
||||||
"ip-range-check": "^0.2.0",
|
"ip-range-check": "^0.2.0",
|
||||||
"ipaddr.js": "^2.0.1"
|
"ipaddr.js": "^2.0.1"
|
||||||
|
@ -81,13 +81,14 @@ async function vlessOverWSHandler(request) {
|
|||||||
let remoteSocketWapper = {
|
let remoteSocketWapper = {
|
||||||
value: null,
|
value: null,
|
||||||
};
|
};
|
||||||
|
let udpStreamWrite = null;
|
||||||
let isDns = false;
|
let isDns = false;
|
||||||
|
|
||||||
// ws --> remote
|
// ws --> remote
|
||||||
readableWebSocketStream.pipeTo(new WritableStream({
|
readableWebSocketStream.pipeTo(new WritableStream({
|
||||||
async write(chunk, controller) {
|
async write(chunk, controller) {
|
||||||
if (isDns) {
|
if (isDns) {
|
||||||
return await handleDNSQuery(chunk, webSocket, null, log);
|
return udpStreamWrite(chunk);
|
||||||
}
|
}
|
||||||
if (remoteSocketWapper.value) {
|
if (remoteSocketWapper.value) {
|
||||||
const writer = remoteSocketWapper.value.writable.getWriter()
|
const writer = remoteSocketWapper.value.writable.getWriter()
|
||||||
@ -128,8 +129,12 @@ async function vlessOverWSHandler(request) {
|
|||||||
const vlessResponseHeader = new Uint8Array([vlessVersion[0], 0]);
|
const vlessResponseHeader = new Uint8Array([vlessVersion[0], 0]);
|
||||||
const rawClientData = chunk.slice(rawDataIndex);
|
const rawClientData = chunk.slice(rawDataIndex);
|
||||||
|
|
||||||
|
// TODO: support udp here when cf runtime has udp support
|
||||||
if (isDns) {
|
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);
|
handleTCPOutBound(remoteSocketWapper, addressRemote, portRemote, rawClientData, webSocket, vlessResponseHeader, log);
|
||||||
},
|
},
|
||||||
@ -515,54 +520,77 @@ function stringify(arr, offset = 0) {
|
|||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {ArrayBuffer} udpChunk
|
|
||||||
* @param {import("@cloudflare/workers-types").WebSocket} webSocket
|
* @param {import("@cloudflare/workers-types").WebSocket} webSocket
|
||||||
* @param {ArrayBuffer} vlessResponseHeader
|
* @param {ArrayBuffer} vlessResponseHeader
|
||||||
* @param {(string)=> void} log
|
* @param {(string)=> void} log
|
||||||
*/
|
*/
|
||||||
async function handleDNSQuery(udpChunk, webSocket, vlessResponseHeader, log) {
|
async function handleUDPOutBound(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,
|
|
||||||
});
|
|
||||||
|
|
||||||
log(`connected to ${dnsServer}:${dnsPort}`);
|
let isVlessHeaderSent = false;
|
||||||
const writer = tcpSocket.writable.getWriter();
|
const transformStream = new TransformStream({
|
||||||
await writer.write(udpChunk);
|
start(controller) {
|
||||||
writer.releaseLock();
|
|
||||||
await tcpSocket.readable.pipeTo(new WritableStream({
|
},
|
||||||
async write(chunk) {
|
transform(chunk, controller) {
|
||||||
if (webSocket.readyState === WS_READY_STATE_OPEN) {
|
// udp message 2 byte is the the length of udp data
|
||||||
if (vlessHeader) {
|
// TODO: this should have bug, beacsue maybe udp chunk can be in two websocket message
|
||||||
webSocket.send(await new Blob([vlessHeader, chunk]).arrayBuffer());
|
for (let index = 0; index < chunk.byteLength;) {
|
||||||
vlessHeader = null;
|
const lengthBuffer = chunk.slice(index, index + 2);
|
||||||
} else {
|
const udpPakcetLength = new DataView(lengthBuffer).getUint16(0);
|
||||||
webSocket.send(chunk);
|
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`);
|
})).catch((error) => {
|
||||||
},
|
log('dns udp has error' + error)
|
||||||
abort(reason) {
|
});
|
||||||
console.error(`dns server(${dnsServer}) tcp is abort`, reason);
|
|
||||||
},
|
const writer = transformStream.writable.getWriter();
|
||||||
}));
|
|
||||||
} catch (error) {
|
return {
|
||||||
console.error(
|
/**
|
||||||
`handleDNSQuery have exception, error: ${error.message}`
|
*
|
||||||
);
|
* @param {Uint8Array} chunk
|
||||||
}
|
*/
|
||||||
|
write(chunk) {
|
||||||
|
writer.write(chunk);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,4 +8,4 @@ main = "src/worker-vless.js"
|
|||||||
compatibility_date = "2023-05-26"
|
compatibility_date = "2023-05-26"
|
||||||
|
|
||||||
[vars]
|
[vars]
|
||||||
UUID = "example_dev_token"
|
# UUID = "example_dev_token"
|
Loading…
Reference in New Issue
Block a user