commit 094bfc6456c6b7d9a486231e4fa9af4c57bf31fe Author: 源文雨 <41315874+fumiama@users.noreply.github.com> Date: Fri Oct 3 16:50:56 2025 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36ccf87 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Dtore diff --git a/README.md b/README.md new file mode 100644 index 0000000..ec5c617 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# dnskip +A simple Golang DoT proxy using TRS. + +## Usage +```bash +go run -ldflags "-checklinkname=0" main.go +``` diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3f1eded --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module dnskip + +go 1.20 + +require ( + github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d + github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5 + github.com/fumiama/terasu v0.0.0-20251003064740-9974bdca12ab + github.com/sirupsen/logrus v1.9.3 +) + +require ( + github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..48ea8da --- /dev/null +++ b/go.sum @@ -0,0 +1,28 @@ +github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d h1:mUQ/c3wXKsUGa4Sg9DBy01APXKB68PmobhxOyaJI7lY= +github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= +github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 h1:S/ferNiehVjNaBMNNBxUjLtVmP/YWD6Yh79RfPv4ehU= +github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5 h1:j9o0XVvdAeLwrBYMnh0SerrMc9CgNU6AGszbsvFzoc0= +github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5/go.mod h1:FOjdw7KdCbK2eH3gRPhwFNCoXKpu9sN5vPH4El/8e0c= +github.com/fumiama/terasu v0.0.0-20251003064740-9974bdca12ab h1:PPc1fEgQzQJKl2n9DtiAP0e5DDoRpLSe7780m74K174= +github.com/fumiama/terasu v0.0.0-20251003064740-9974bdca12ab/go.mod h1:ltlQhbPE0423K0bNQxJdhtkYgFxXstUobc8k+9LFY88= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..aa8190a --- /dev/null +++ b/main.go @@ -0,0 +1,175 @@ +package main + +import ( + "context" + "encoding/binary" + "flag" + "io" + "math" + "net" + "net/netip" + "sync/atomic" + "time" + "unsafe" + + "github.com/FloatTech/ttl" + "github.com/fumiama/orbyte/pbuf" + "github.com/fumiama/terasu" + "github.com/fumiama/terasu/dns" + "github.com/sirupsen/logrus" +) + +var ( + freeconn = uintptr(0) + tlsconnCache = ttl.NewCacheOn( + 5*time.Minute, [4]func(uint8, net.Conn){ + nil, nil, func(_ uint8, c net.Conn) { + logrus.Warnln("Close idle/error tls conn to", c.RemoteAddr()) + _ = c.Close() + }, nil, + }) +) + +func main() { + iphost := flag.String("l", "127.0.0.1:5345", "listen DNS UDP port") + frag := flag.Uint("frag", 3, "TLS first fragemt size") + flag.Parse() + + if frag != nil { + terasu.DefaultFirstFragmentLen = uint8(*frag) + } + + addrport, err := netip.ParseAddrPort(*iphost) + if err != nil { + logrus.Fatal("ParseAddrPort err:", err) + } + + laddr := net.UDPAddrFromAddrPort(addrport) + +RECONN: + conn, err := net.ListenUDP("udp", laddr) + if err != nil { + logrus.Fatal("ListenUDP err:", err) + } + logrus.Infoln("Listen at", conn.LocalAddr()) + + // server loop + buf := make([]byte, 65536) + for { + n, addr, err := conn.ReadFromUDP(buf) + if err != nil { + logrus.Warnln("ReadFromUDP err:", err) + goto RECONN + } + logrus.Debugln("ReadFromUDP from", addr, "with", n, "bytes payload") + payload := pbuf.NewBytes(n) + payload.V(func(b []byte) { copy(b, buf[:n]) }) + cnt := lockfree() + if cnt == math.MaxUint8 { + logrus.Warnln("Drop request from", addr, "due to rate limit") + continue + } + go response(cnt, conn, addr, payload) + } +} + +func response(cnt uint8, conn *net.UDPConn, addr *net.UDPAddr, payload pbuf.Bytes) { + defer releasefree(cnt) + logrus.Debugln(addr, "Run on lock", cnt) + + loopcnt := 0 +REDAIL: + tlsconn, err := dialtls(cnt) + if err != nil { + logrus.Warnln(addr, "Dial DNS server err:", err) + return + } + logrus.Debugln(addr, "Dial to DNS server", tlsconn.RemoteAddr()) + + payload.V(func(b []byte) { + var plen [2]byte + binary.BigEndian.PutUint16(plen[:], uint16(len(b))) + _, err = io.Copy(tlsconn, &net.Buffers{plen[:], b}) + if err != nil { + return + } + logrus.Debugln(addr, "Write to DNS server", tlsconn.RemoteAddr()) + pbuf.BufferItemToBytes(pbuf.NewBuffer(nil).P(func(b *pbuf.Buffer) { + var plen [2]byte + _, err = io.ReadFull(tlsconn, plen[:]) + if err != nil { + return + } + payloadlen := int64(binary.BigEndian.Uint16(plen[:])) + logrus.Debugln(addr, "Read payload len", payloadlen, "from DNS server", tlsconn.RemoteAddr()) + _, err = io.CopyN(b, tlsconn, payloadlen) + if err != nil { + return + } + logrus.Debugln(addr, "Read", b.Len(), "bytes from DNS server", tlsconn.RemoteAddr()) + })).V(func(b []byte) { + if err != nil { + return + } + _, err = conn.WriteToUDP(b, addr) + if err != nil { + return + } + logrus.Debugln("Write response to", addr) + }) + }) + if err != nil { + logrus.Warnln(addr, "Proxy to DNS server err:", err) + tlsconnCache.Delete(cnt) + loopcnt++ + if loopcnt < 3 { + goto REDAIL + } + return + } +} + +// lockfree is spin update +func lockfree() uint8 { + old := atomic.LoadUintptr(&freeconn) + for i := uint8(0); i < uint8(unsafe.Sizeof(uintptr(0)))*8; i++ { + for old&(1<