package vless
import (
"encoding/binary"
"io"
"net"
"sync"
"time"
)
const udpIdleTimeout = 5 * time.Minute
// relayTCP dials the target and relays data bidirectionally.
// The VLESS response header is written before relaying begins.
func relayTCP(clientConn net.Conn, target string) error {
remote, err := net.DialTimeout("tcp", target, 10*time.Second)
if err != nil {
return err
}
defer remote.Close()
if err := WriteResponse(clientConn); err != nil {
return err
}
var wg sync.WaitGroup
wg.Add(2)
// client → remote
go func() {
defer wg.Done()
io.Copy(remote, clientConn)
remote.(*net.TCPConn).CloseWrite()
}()
// remote → client
go func() {
defer wg.Done()
io.Copy(clientConn, remote)
}()
wg.Wait()
return nil
}
// relayUDP handles VLESS UDP-over-TCP relay.
// UDP datagrams are framed as [2B length BE][payload] on the TCP stream.
func relayUDP(clientConn net.Conn, target string) error {
remoteAddr, err := net.ResolveUDPAddr("udp", target)
if err != nil {
return err
}
udpConn, err := net.DialUDP("udp", nil, remoteAddr)
if err != nil {
return err
}
defer udpConn.Close()
if err := WriteResponse(clientConn); err != nil {
return err
}
var wg sync.WaitGroup
wg.Add(2)
// client TCP → remote UDP: read [2B len][payload] frames, send as UDP datagrams.
go func() {
defer wg.Done()
defer udpConn.Close()
var lenBuf [2]byte
for {
if _, err := io.ReadFull(clientConn, lenBuf[:]); err != nil {
return
}
payloadLen := binary.BigEndian.Uint16(lenBuf[:])
if payloadLen == 0 {
continue
}
buf := make([]byte, payloadLen)
if _, err := io.ReadFull(clientConn, buf); err != nil {
return
}
udpConn.SetWriteDeadline(time.Now().Add(10 * time.Second))
if _, err := udpConn.Write(buf); err != nil {
return
}
}
}()
// remote UDP → client TCP: read UDP datagrams, write as [2B len][payload] frames.
go func() {
defer wg.Done()
buf := make([]byte, 64*1024)
var lenBuf [2]byte
for {
udpConn.SetReadDeadline(time.Now().Add(udpIdleTimeout))
n, err := udpConn.Read(buf)
if err != nil {
return
}
binary.BigEndian.PutUint16(lenBuf[:], uint16(n))
if _, err := clientConn.Write(lenBuf[:]); err != nil {
return
}
if _, err := clientConn.Write(buf[:n]); err != nil {
return
}
}
}()
wg.Wait()
return nil
}