@@ -81,13 +81,14 @@ async function vlessOverWSHandler(request) {
81
81
let remoteSocketWapper = {
82
82
value : null ,
83
83
} ;
84
+ let udpStreamWrite = null ;
84
85
let isDns = false ;
85
86
86
87
// ws --> remote
87
88
readableWebSocketStream . pipeTo ( new WritableStream ( {
88
89
async write ( chunk , controller ) {
89
90
if ( isDns ) {
90
- return await handleDNSQuery ( chunk , webSocket , null , log ) ;
91
+ return udpStreamWrite ( chunk ) ;
91
92
}
92
93
if ( remoteSocketWapper . value ) {
93
94
const writer = remoteSocketWapper . value . writable . getWriter ( )
@@ -128,8 +129,12 @@ async function vlessOverWSHandler(request) {
128
129
const vlessResponseHeader = new Uint8Array ( [ vlessVersion [ 0 ] , 0 ] ) ;
129
130
const rawClientData = chunk . slice ( rawDataIndex ) ;
130
131
132
+ // TODO: support udp here when cf runtime has udp support
131
133
if ( isDns ) {
132
- return handleDNSQuery ( rawClientData , webSocket , vlessResponseHeader , log ) ;
134
+ const { write } = await handleUDPOutBound ( webSocket , vlessResponseHeader , log ) ;
135
+ udpStreamWrite = write ;
136
+ udpStreamWrite ( rawClientData ) ;
137
+ return ;
133
138
}
134
139
handleTCPOutBound ( remoteSocketWapper , addressRemote , portRemote , rawClientData , webSocket , vlessResponseHeader , log ) ;
135
140
} ,
@@ -515,54 +520,77 @@ function stringify(arr, offset = 0) {
515
520
return uuid ;
516
521
}
517
522
523
+
518
524
/**
519
525
*
520
- * @param {ArrayBuffer } udpChunk
521
526
* @param {import("@cloudflare/workers-types").WebSocket } webSocket
522
527
* @param {ArrayBuffer } vlessResponseHeader
523
528
* @param {(string)=> void } log
524
529
*/
525
- async function handleDNSQuery ( udpChunk , webSocket , vlessResponseHeader , log ) {
526
- // no matter which DNS server client send, we alwasy use hard code one.
527
- // beacsue someof DNS server is not support DNS over TCP
528
- try {
529
- const dnsServer = '8.8.4.4' ; // change to 1.1.1.1 after cf fix connect own ip bug
530
- const dnsPort = 53 ;
531
- /** @type {ArrayBuffer | null } */
532
- let vlessHeader = vlessResponseHeader ;
533
- /** @type {import("@cloudflare/workers-types").Socket } */
534
- const tcpSocket = connect ( {
535
- hostname : dnsServer ,
536
- port : dnsPort ,
537
- } ) ;
530
+ async function handleUDPOutBound ( webSocket , vlessResponseHeader , log ) {
538
531
539
- log ( `connected to ${ dnsServer } :${ dnsPort } ` ) ;
540
- const writer = tcpSocket . writable . getWriter ( ) ;
541
- await writer . write ( udpChunk ) ;
542
- writer . releaseLock ( ) ;
543
- await tcpSocket . readable . pipeTo ( new WritableStream ( {
544
- async write ( chunk ) {
545
- if ( webSocket . readyState === WS_READY_STATE_OPEN ) {
546
- if ( vlessHeader ) {
547
- webSocket . send ( await new Blob ( [ vlessHeader , chunk ] ) . arrayBuffer ( ) ) ;
548
- vlessHeader = null ;
549
- } else {
550
- webSocket . send ( chunk ) ;
551
- }
532
+ let isVlessHeaderSent = false ;
533
+ const transformStream = new TransformStream ( {
534
+ start ( controller ) {
535
+
536
+ } ,
537
+ transform ( chunk , controller ) {
538
+ // udp message 2 byte is the the length of udp data
539
+ // TODO: this should have bug, beacsue maybe udp chunk can be in two websocket message
540
+ for ( let index = 0 ; index < chunk . byteLength ; ) {
541
+ const lengthBuffer = chunk . slice ( index , index + 2 ) ;
542
+ const udpPakcetLength = new DataView ( lengthBuffer ) . getUint16 ( 0 ) ;
543
+ const udpData = new Uint8Array (
544
+ chunk . slice ( index + 2 , index + 2 + udpPakcetLength )
545
+ ) ;
546
+ index = index + 2 + udpPakcetLength ;
547
+ controller . enqueue ( udpData ) ;
548
+ }
549
+ } ,
550
+ flush ( controller ) {
551
+ }
552
+ } ) ;
553
+
554
+ // only handle dns udp for now
555
+ transformStream . readable . pipeTo ( new WritableStream ( {
556
+ async write ( chunk ) {
557
+ const resp = await fetch ( 'https://1.1.1.1/dns-query' ,
558
+ {
559
+ method : 'POST' ,
560
+ headers : {
561
+ 'content-type' : 'application/dns-message' ,
562
+ } ,
563
+ body : chunk ,
564
+ } )
565
+ const dnsQueryResult = await resp . arrayBuffer ( ) ;
566
+ const udpSize = dnsQueryResult . byteLength ;
567
+ // console.log([...new Uint8Array(dnsQueryResult)].map((x) => x.toString(16)));
568
+ const udpSizeBuffer = new Uint8Array ( [ ( udpSize >> 8 ) & 0xff , udpSize & 0xff ] ) ;
569
+ if ( webSocket . readyState === WS_READY_STATE_OPEN ) {
570
+ log ( `doh success and dns message length is ${ udpSize } ` ) ;
571
+ if ( isVlessHeaderSent ) {
572
+ webSocket . send ( await new Blob ( [ udpSizeBuffer , dnsQueryResult ] ) . arrayBuffer ( ) ) ;
573
+ } else {
574
+ webSocket . send ( await new Blob ( [ vlessResponseHeader , udpSizeBuffer , dnsQueryResult ] ) . arrayBuffer ( ) ) ;
575
+ isVlessHeaderSent = true ;
552
576
}
553
- } ,
554
- close ( ) {
555
- log ( `dns server(${ dnsServer } ) tcp is close` ) ;
556
- } ,
557
- abort ( reason ) {
558
- console . error ( `dns server(${ dnsServer } ) tcp is abort` , reason ) ;
559
- } ,
560
- } ) ) ;
561
- } catch ( error ) {
562
- console . error (
563
- `handleDNSQuery have exception, error: ${ error . message } `
564
- ) ;
565
- }
577
+ }
578
+ }
579
+ } ) ) . catch ( ( error ) => {
580
+ log ( 'dns udp has error' + error )
581
+ } ) ;
582
+
583
+ const writer = transformStream . writable . getWriter ( ) ;
584
+
585
+ return {
586
+ /**
587
+ *
588
+ * @param {Uint8Array } chunk
589
+ */
590
+ write ( chunk ) {
591
+ writer . write ( chunk ) ;
592
+ }
593
+ } ;
566
594
}
567
595
568
596
/**
0 commit comments