From 3b88c50634a6c247f27bf93b427bddf0292f5034 Mon Sep 17 00:00:00 2001 From: kerry Date: Thu, 16 Mar 2023 16:45:46 +0800 Subject: [PATCH] dns header --- app/cmd/client.go | 1 + app/cmd/server.go | 1 + core/pktconns/dns/obfs.go | 131 ++++++++++++++++++++++++++++++++++++++ core/pktconns/funcs.go | 29 +++++++++ 4 files changed, 162 insertions(+) create mode 100644 core/pktconns/dns/obfs.go diff --git a/app/cmd/client.go b/app/cmd/client.go index 3f51e86a4f..04133d399c 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -36,6 +36,7 @@ var clientPacketConnFuncFactoryMap = map[string]pktconns.ClientPacketConnFuncFac "wechat": pktconns.NewClientWeChatConnFunc, "wechat-video": pktconns.NewClientWeChatConnFunc, "faketcp": pktconns.NewClientFakeTCPConnFunc, + "dns": pktconns.NewClientDnsConnFunc, } func client(config *clientConfig) { diff --git a/app/cmd/server.go b/app/cmd/server.go index c6909d3f90..8bc96eb873 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -30,6 +30,7 @@ var serverPacketConnFuncFactoryMap = map[string]pktconns.ServerPacketConnFuncFac "wechat": pktconns.NewServerWeChatConnFunc, "wechat-video": pktconns.NewServerWeChatConnFunc, "faketcp": pktconns.NewServerFakeTCPConnFunc, + "dns": pktconns.NewServerDnsConnFunc, } func server(config *serverConfig) { diff --git a/core/pktconns/dns/obfs.go b/core/pktconns/dns/obfs.go new file mode 100644 index 0000000000..a24ac6ef58 --- /dev/null +++ b/core/pktconns/dns/obfs.go @@ -0,0 +1,131 @@ +package dns + +import ( + "encoding/binary" + "math/rand" + "net" + "os" + "sync" + "syscall" + "time" + + "github.com/miekg/dns" +) + +const udpBufferSize = 4096 + +type DnsUDPPacketConn struct { + orig *net.UDPConn + + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex + header []byte +} + +func NewDnsUDPConn(orig *net.UDPConn, domain string) *DnsUDPPacketConn { + var header []byte + + header = binary.BigEndian.AppendUint16(header, 0x0000) // Transaction ID + header = binary.BigEndian.AppendUint16(header, 0x0100) // Flags: Standard query + header = binary.BigEndian.AppendUint16(header, 0x0001) // Questions + header = binary.BigEndian.AppendUint16(header, 0x0000) // Answer RRs + header = binary.BigEndian.AppendUint16(header, 0x0000) // Authority RRs + header = binary.BigEndian.AppendUint16(header, 0x0000) // Additional RRs + + buf := make([]byte, 0x100) + + off1, err := dns.PackDomainName(dns.Fqdn(domain), buf, 0, nil, false) + + if err != nil { + return nil + } + + header = append(header, buf[:off1]...) + + header = binary.BigEndian.AppendUint16(header, 0x0001) // Type: A + header = binary.BigEndian.AppendUint16(header, 0x0001) // Class: IN + + return &DnsUDPPacketConn{ + orig: orig, + readBuf: make([]byte, udpBufferSize), + writeBuf: make([]byte, udpBufferSize), + header: header, + } +} + +func (c *DnsUDPPacketConn) ReadFrom(p []byte) (int, net.Addr, error) { + for { + c.readMutex.Lock() + n, addr, err := c.orig.ReadFrom(c.readBuf) + if n <= len(c.header) { + c.readMutex.Unlock() + return 0, addr, err + } + + var newN = copy(p, c.readBuf[len(c.header):n]) + + c.readMutex.Unlock() + if newN > 0 { + // Valid packet + return newN, addr, err + } else if err != nil { + // Not valid and orig.ReadFrom had some error + return 0, addr, err + } + } +} + +func (c *DnsUDPPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + c.writeMutex.Lock() + + copy(c.writeBuf, c.header) + binary.BigEndian.PutUint16(c.writeBuf[0:], uint16(rand.Uint32())) + + var bn = copy(c.writeBuf[len(c.header):], p) + + _, err = c.orig.WriteTo(c.writeBuf[:len(c.header)+bn], addr) + c.writeMutex.Unlock() + if err != nil { + return 0, err + } else { + return len(p), nil + } +} + +func (c *DnsUDPPacketConn) Close() error { + return c.orig.Close() +} + +func (c *DnsUDPPacketConn) LocalAddr() net.Addr { + return c.orig.LocalAddr() +} + +func (c *DnsUDPPacketConn) SetDeadline(t time.Time) error { + return c.orig.SetDeadline(t) +} + +func (c *DnsUDPPacketConn) SetReadDeadline(t time.Time) error { + return c.orig.SetReadDeadline(t) +} + +func (c *DnsUDPPacketConn) SetWriteDeadline(t time.Time) error { + return c.orig.SetWriteDeadline(t) +} + +func (c *DnsUDPPacketConn) SetReadBuffer(bytes int) error { + return c.orig.SetReadBuffer(bytes) +} + +func (c *DnsUDPPacketConn) SetWriteBuffer(bytes int) error { + return c.orig.SetWriteBuffer(bytes) +} + +func (c *DnsUDPPacketConn) SyscallConn() (syscall.RawConn, error) { + return c.orig.SyscallConn() +} + +func (c *DnsUDPPacketConn) File() (f *os.File, err error) { + return c.orig.File() +} diff --git a/core/pktconns/funcs.go b/core/pktconns/funcs.go index b0675f1363..4f2650f276 100644 --- a/core/pktconns/funcs.go +++ b/core/pktconns/funcs.go @@ -5,6 +5,7 @@ import ( "strings" "time" + "github.com/apernet/hysteria/core/pktconns/dns" "github.com/apernet/hysteria/core/pktconns/faketcp" "github.com/apernet/hysteria/core/pktconns/obfs" "github.com/apernet/hysteria/core/pktconns/udp" @@ -109,6 +110,20 @@ func NewClientFakeTCPConnFunc(obfsPassword string, hopInterval time.Duration) Cl } } +func NewClientDnsConnFunc(domain string, hopInterval time.Duration) ClientPacketConnFunc { + return func(server string) (net.PacketConn, net.Addr, error) { + sAddr, err := net.ResolveUDPAddr("udp", server) + if err != nil { + return nil, nil, err + } + udpConn, err := net.ListenUDP("udp", nil) + if err != nil { + return nil, nil, err + } + return dns.NewDnsUDPConn(udpConn, domain), sAddr, nil + } +} + func NewServerUDPConnFunc(obfsPassword string) ServerPacketConnFunc { if obfsPassword == "" { return func(listen string) (net.PacketConn, error) { @@ -180,6 +195,20 @@ func NewServerFakeTCPConnFunc(obfsPassword string) ServerPacketConnFunc { } } +func NewServerDnsConnFunc(domain string) ServerPacketConnFunc { + return func(listen string) (net.PacketConn, error) { + laddrU, err := net.ResolveUDPAddr("udp", listen) + if err != nil { + return nil, err + } + udpConn, err := net.ListenUDP("udp", laddrU) + if err != nil { + return nil, err + } + return dns.NewDnsUDPConn(udpConn, domain), nil + } +} + func isMultiPortAddr(addr string) bool { _, portStr, err := net.SplitHostPort(addr) if err == nil && (strings.Contains(portStr, ",") || strings.Contains(portStr, "-")) {