From ca4a5f7afb6590fd1db2023824ac13541b97428f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 24 Jun 2024 10:07:56 +0800 Subject: [PATCH 1/8] Update dependencies --- go.mod | 14 +++++++------- go.sum | 40 +++++++++++++++++++++++----------------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 5305ff0..8c245b1 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/miekg/dns v1.1.59 - github.com/sagernet/quic-go v0.43.0-beta.3 + github.com/sagernet/quic-go v0.45.1-beta.2 github.com/sagernet/sing v0.4.1 github.com/stretchr/testify v1.9.0 ) @@ -17,13 +17,13 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.24.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20190121172915-509febef88a4 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.20.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 89a7061..7c6910e 100644 --- a/go.sum +++ b/go.sum @@ -5,11 +5,12 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= +github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= +github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= @@ -19,31 +20,36 @@ github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/sagernet/quic-go v0.43.0-beta.3 h1:qclJbbpgZe76EH62Bdu3LfDSC2zmuxj7zXCpdQBbe7c= -github.com/sagernet/quic-go v0.43.0-beta.3/go.mod h1:3EtxR1Yaa1FZu6jFPiBHpOAdhOxL4A3EPxmiVgjJvVM= +github.com/sagernet/quic-go v0.45.1-beta.2 h1:zkEeCbhdFFkrxKcuIRBtXNKci/1t2J/39QSG/sPvlmc= +github.com/sagernet/quic-go v0.45.1-beta.2/go.mod h1:+N3FqM9DAzOWfe64uxXuBejVJwX7DeW7BslzLO6N/xI= github.com/sagernet/sing v0.4.1 h1:zVlpE+7k7AFoC2pv6ReqLf0PIHjihL/jsBl5k05PQFk= github.com/sagernet/sing v0.4.1/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.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= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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= From ca1bf475bc757305fc05422b4de715c0f728e6c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 24 Jun 2024 12:12:36 +0800 Subject: [PATCH 2/8] Update dependencies --- go.mod | 10 +++++----- go.sum | 6 ------ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 8c245b1..86d2ba5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/sagernet/sing-dns go 1.18 require ( - github.com/miekg/dns v1.1.59 + github.com/miekg/dns v1.1.61 github.com/sagernet/quic-go v0.45.1-beta.2 github.com/sagernet/sing v0.4.1 github.com/stretchr/testify v1.9.0 @@ -17,13 +17,13 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/exp v0.0.0-20190121172915-509febef88a4 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect golang.org/x/mod v0.18.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/net v0.26.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.22.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 7c6910e..6ea4c66 100644 --- a/go.sum +++ b/go.sum @@ -8,7 +8,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= @@ -28,16 +27,12 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= @@ -46,7 +41,6 @@ golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= From f1482f926dc4137b903f5b36b49c9cbd3efc958e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 2 Jun 2024 21:07:04 +0800 Subject: [PATCH 3/8] Improve TCP/TLS transports --- client.go | 18 +++++++++++- transport_base.go | 75 ++++++++++++++++++++++++++++------------------- transport_tcp.go | 2 +- transport_tls.go | 2 +- transport_udp.go | 2 +- 5 files changed, 65 insertions(+), 34 deletions(-) diff --git a/client.go b/client.go index f9139f6..cd60cf8 100644 --- a/client.go +++ b/client.go @@ -17,7 +17,10 @@ import ( "github.com/miekg/dns" ) -const DefaultTTL = 600 +const ( + DefaultTTL = 600 + DefaultTimeout = 5 * time.Second +) var ( ErrNoRawSupport = E.New("no raw query support by current transport") @@ -27,6 +30,7 @@ var ( ) type Client struct { + timeout time.Duration disableCache bool disableExpire bool independentCache bool @@ -49,6 +53,7 @@ type transportCacheKey struct { } type ClientOptions struct { + Timeout time.Duration DisableCache bool DisableExpire bool IndependentCache bool @@ -58,12 +63,16 @@ type ClientOptions struct { func NewClient(options ClientOptions) *Client { client := &Client{ + timeout: options.Timeout, disableCache: options.DisableCache, disableExpire: options.DisableExpire, independentCache: options.IndependentCache, initRDRCFunc: options.RDRC, logger: options.Logger, } + if client.timeout == 0 { + client.timeout = DefaultTimeout + } if !client.disableCache { if !client.independentCache { client.cache = cache.New[dns.Question, *dns.Msg]() @@ -149,7 +158,14 @@ func (c *Client) ExchangeWithResponseCheck(ctx context.Context, transport Transp return nil, ErrResponseRejectedCached } } + var cancel context.CancelFunc + if c.timeout > 0 { + ctx, cancel = context.WithTimeout(ctx, c.timeout) + } response, err := transport.Exchange(ctx, message) + if cancel != nil { + cancel() + } if err != nil { return nil, err } diff --git a/transport_base.go b/transport_base.go index 8fd91af..c3db8c4 100644 --- a/transport_base.go +++ b/transport_base.go @@ -29,12 +29,13 @@ type myTransportAdapter struct { dialer N.Dialer serverAddr M.Socksaddr clientAddr netip.Prefix + reuse bool handler myTransportHandler access sync.Mutex conn *dnsConnection } -func newAdapter(options TransportOptions, serverAddr M.Socksaddr) myTransportAdapter { +func newAdapter(options TransportOptions, serverAddr M.Socksaddr, reuse bool) myTransportAdapter { ctx, cancel := context.WithCancel(options.Context) return myTransportAdapter{ name: options.Name, @@ -43,6 +44,7 @@ func newAdapter(options TransportOptions, serverAddr M.Socksaddr) myTransportAda dialer: options.Dialer, serverAddr: serverAddr, clientAddr: options.ClientSubnet, + reuse: reuse, } } @@ -55,18 +57,21 @@ func (t *myTransportAdapter) Start() error { } func (t *myTransportAdapter) open(ctx context.Context) (*dnsConnection, error) { - connection := t.conn - if connection != nil { - if !common.Done(connection.ctx) { - return connection, nil + var connection *dnsConnection + if t.reuse { + connection = t.conn + if connection != nil { + if !common.Done(connection.ctx) { + return connection, nil + } } - } - t.access.Lock() - defer t.access.Unlock() - connection = t.conn - if connection != nil { - if !common.Done(connection.ctx) { - return connection, nil + t.access.Lock() + defer t.access.Unlock() + connection = t.conn + if connection != nil { + if !common.Done(connection.ctx) { + return connection, nil + } } } conn, err := t.handler.DialContext(ctx) @@ -81,7 +86,9 @@ func (t *myTransportAdapter) open(ctx context.Context) (*dnsConnection, error) { callbacks: make(map[uint16]*dnsCallback), } go t.recvLoop(connection) - t.conn = connection + if t.reuse { + t.conn = connection + } return connection, nil } @@ -110,7 +117,6 @@ func (t *myTransportAdapter) recvLoop(conn *dnsConnection) { } }) group.Cleanup(func() { - conn.cancel() conn.Close() }) group.Run(conn.ctx) @@ -118,16 +124,7 @@ func (t *myTransportAdapter) recvLoop(conn *dnsConnection) { func (t *myTransportAdapter) Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error) { messageId := message.Id - var ( - conn *dnsConnection - err error - ) - for attempts := 0; attempts < 2; attempts++ { - conn, err = t.open(t.ctx) - if err == nil { - break - } - } + conn, err := t.open(t.ctx) if err != nil { return nil, err } @@ -135,6 +132,9 @@ func (t *myTransportAdapter) Exchange(ctx context.Context, message *dns.Msg) (*d if err != nil { return nil, err } + if !t.reuse { + conn.Close() + } response.Id = messageId return response, nil } @@ -151,11 +151,22 @@ func (t *myTransportAdapter) exchange(ctx context.Context, conn *dnsConnection, conn.callbacks[exMessage.Id] = callback conn.access.Unlock() defer t.cleanup(conn, exMessage.Id, callback) - conn.writeAccess.Lock() - err := t.handler.WriteMessage(conn, &exMessage) - conn.writeAccess.Unlock() + var err error + done := make(chan struct{}) + go func() { + conn.writeAccess.Lock() + err = t.handler.WriteMessage(conn, &exMessage) + conn.writeAccess.Unlock() + close(done) + }() + select { + case <-done: + case <-ctx.Done(): + conn.Close() + return nil, ctx.Err() + } if err != nil { - conn.cancel() + conn.Close() return nil, err } select { @@ -165,7 +176,7 @@ func (t *myTransportAdapter) exchange(ctx context.Context, conn *dnsConnection, case <-conn.ctx.Done(): return nil, E.Errors(conn.err, conn.ctx.Err()) case <-ctx.Done(): - conn.cancel() + conn.Close() return nil, ctx.Err() } } @@ -186,7 +197,6 @@ func (t *myTransportAdapter) cleanup(conn *dnsConnection, messageId uint16, call func (t *myTransportAdapter) Reset() { conn := t.conn if conn != nil { - conn.cancel() conn.Close() } } @@ -215,6 +225,11 @@ type dnsConnection struct { callbacks map[uint16]*dnsCallback } +func (c *dnsConnection) Close() error { + c.cancel() + return c.Conn.Close() +} + type dnsCallback struct { access sync.Mutex message *dns.Msg diff --git a/transport_tcp.go b/transport_tcp.go index 690c8bd..c74ee50 100644 --- a/transport_tcp.go +++ b/transport_tcp.go @@ -44,7 +44,7 @@ func NewTCPTransport(options TransportOptions) (*TCPTransport, error) { func newTCPTransport(options TransportOptions, serverAddr M.Socksaddr) *TCPTransport { transport := &TCPTransport{ - newAdapter(options, serverAddr), + newAdapter(options, serverAddr, false), } transport.handler = transport return transport diff --git a/transport_tls.go b/transport_tls.go index 183ba64..6a4fe55 100644 --- a/transport_tls.go +++ b/transport_tls.go @@ -41,7 +41,7 @@ func NewTLSTransport(options TransportOptions) (*TLSTransport, error) { serverAddr.Port = 853 } transport := &TLSTransport{ - newAdapter(options, serverAddr), + newAdapter(options, serverAddr, true), } transport.handler = transport return transport, nil diff --git a/transport_udp.go b/transport_udp.go index 5d32989..2d341f7 100644 --- a/transport_udp.go +++ b/transport_udp.go @@ -43,7 +43,7 @@ func NewUDPTransport(options TransportOptions) (*UDPTransport, error) { serverAddr.Port = 53 } transport := &UDPTransport{ - newAdapter(options, serverAddr), + newAdapter(options, serverAddr, true), newTCPTransport(options, serverAddr), options.Logger, 512, From 5b9f704ca929979a3ce6e0344c54e528606e24d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 7 Jun 2024 15:58:02 +0800 Subject: [PATCH 4/8] Drop support for go1.18 and go1.19 --- .github/workflows/debug.yml | 2 ++ go.mod | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/debug.yml b/.github/workflows/debug.yml index d1814d1..7ae30fa 100644 --- a/.github/workflows/debug.yml +++ b/.github/workflows/debug.yml @@ -3,6 +3,7 @@ name: Debug build on: push: branches: + - main - dev paths-ignore: - '**.md' @@ -10,6 +11,7 @@ on: - '!.github/workflows/debug.yml' pull_request: branches: + - main - dev jobs: diff --git a/go.mod b/go.mod index 86d2ba5..3f9b02a 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/sagernet/sing-dns -go 1.18 +go 1.20 require ( github.com/miekg/dns v1.1.61 From f74a906728e4ceaf1d386585ec6142e14c5c620b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 8 Jun 2024 17:42:43 +0800 Subject: [PATCH 5/8] Rewrite base transports --- transport_base.go | 237 ---------------------------------------------- transport_tcp.go | 68 +++++++++---- transport_tls.go | 129 ++++++++++++++++--------- transport_udp.go | 207 ++++++++++++++++++++++++++++++++++------ 4 files changed, 315 insertions(+), 326 deletions(-) delete mode 100644 transport_base.go diff --git a/transport_base.go b/transport_base.go deleted file mode 100644 index c3db8c4..0000000 --- a/transport_base.go +++ /dev/null @@ -1,237 +0,0 @@ -package dns - -import ( - "context" - "net" - "net/netip" - "os" - "sync" - - "github.com/sagernet/sing/common" - E "github.com/sagernet/sing/common/exceptions" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" - "github.com/sagernet/sing/common/task" - - "github.com/miekg/dns" -) - -type myTransportHandler interface { - DialContext(ctx context.Context) (net.Conn, error) - ReadMessage(conn net.Conn) (*dns.Msg, error) - WriteMessage(conn net.Conn, message *dns.Msg) error -} - -type myTransportAdapter struct { - name string - ctx context.Context - cancel context.CancelFunc - dialer N.Dialer - serverAddr M.Socksaddr - clientAddr netip.Prefix - reuse bool - handler myTransportHandler - access sync.Mutex - conn *dnsConnection -} - -func newAdapter(options TransportOptions, serverAddr M.Socksaddr, reuse bool) myTransportAdapter { - ctx, cancel := context.WithCancel(options.Context) - return myTransportAdapter{ - name: options.Name, - ctx: ctx, - cancel: cancel, - dialer: options.Dialer, - serverAddr: serverAddr, - clientAddr: options.ClientSubnet, - reuse: reuse, - } -} - -func (t *myTransportAdapter) Name() string { - return t.name -} - -func (t *myTransportAdapter) Start() error { - return nil -} - -func (t *myTransportAdapter) open(ctx context.Context) (*dnsConnection, error) { - var connection *dnsConnection - if t.reuse { - connection = t.conn - if connection != nil { - if !common.Done(connection.ctx) { - return connection, nil - } - } - t.access.Lock() - defer t.access.Unlock() - connection = t.conn - if connection != nil { - if !common.Done(connection.ctx) { - return connection, nil - } - } - } - conn, err := t.handler.DialContext(ctx) - if err != nil { - return nil, err - } - connCtx, cancel := context.WithCancel(t.ctx) - connection = &dnsConnection{ - Conn: conn, - ctx: connCtx, - cancel: cancel, - callbacks: make(map[uint16]*dnsCallback), - } - go t.recvLoop(connection) - if t.reuse { - t.conn = connection - } - return connection, nil -} - -func (t *myTransportAdapter) recvLoop(conn *dnsConnection) { - var group task.Group - group.Append0(func(ctx context.Context) error { - for { - message, err := t.handler.ReadMessage(conn) - if err != nil { - return err - } - conn.access.RLock() - callback, loaded := conn.callbacks[message.Id] - conn.access.RUnlock() - if !loaded { - continue - } - callback.access.Lock() - select { - case <-callback.done: - default: - callback.message = message - close(callback.done) - } - callback.access.Unlock() - } - }) - group.Cleanup(func() { - conn.Close() - }) - group.Run(conn.ctx) -} - -func (t *myTransportAdapter) Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error) { - messageId := message.Id - conn, err := t.open(t.ctx) - if err != nil { - return nil, err - } - response, err := t.exchange(ctx, conn, message) - if err != nil { - return nil, err - } - if !t.reuse { - conn.Close() - } - response.Id = messageId - return response, nil -} - -func (t *myTransportAdapter) exchange(ctx context.Context, conn *dnsConnection, message *dns.Msg) (*dns.Msg, error) { - messageId := message.Id - callback := &dnsCallback{ - done: make(chan struct{}), - } - exMessage := *message - conn.access.Lock() - conn.queryId++ - exMessage.Id = conn.queryId - conn.callbacks[exMessage.Id] = callback - conn.access.Unlock() - defer t.cleanup(conn, exMessage.Id, callback) - var err error - done := make(chan struct{}) - go func() { - conn.writeAccess.Lock() - err = t.handler.WriteMessage(conn, &exMessage) - conn.writeAccess.Unlock() - close(done) - }() - select { - case <-done: - case <-ctx.Done(): - conn.Close() - return nil, ctx.Err() - } - if err != nil { - conn.Close() - return nil, err - } - select { - case <-callback.done: - callback.message.Id = messageId - return callback.message, nil - case <-conn.ctx.Done(): - return nil, E.Errors(conn.err, conn.ctx.Err()) - case <-ctx.Done(): - conn.Close() - return nil, ctx.Err() - } -} - -func (t *myTransportAdapter) cleanup(conn *dnsConnection, messageId uint16, callback *dnsCallback) { - conn.access.Lock() - delete(conn.callbacks, messageId) - conn.access.Unlock() - callback.access.Lock() - select { - case <-callback.done: - default: - close(callback.done) - } - callback.access.Unlock() -} - -func (t *myTransportAdapter) Reset() { - conn := t.conn - if conn != nil { - conn.Close() - } -} - -func (t *myTransportAdapter) Close() error { - t.Reset() - return nil -} - -func (t *myTransportAdapter) Raw() bool { - return true -} - -func (t *myTransportAdapter) Lookup(ctx context.Context, domain string, strategy DomainStrategy) ([]netip.Addr, error) { - return nil, os.ErrInvalid -} - -type dnsConnection struct { - net.Conn - ctx context.Context - cancel context.CancelFunc - access sync.RWMutex - writeAccess sync.Mutex - err error - queryId uint16 - callbacks map[uint16]*dnsCallback -} - -func (c *dnsConnection) Close() error { - c.cancel() - return c.Conn.Close() -} - -type dnsCallback struct { - access sync.Mutex - message *dns.Msg - done chan struct{} -} diff --git a/transport_tcp.go b/transport_tcp.go index c74ee50..9e9a6c7 100644 --- a/transport_tcp.go +++ b/transport_tcp.go @@ -3,8 +3,10 @@ package dns import ( "context" "encoding/binary" - "net" + "io" + "net/netip" "net/url" + "os" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" @@ -24,7 +26,9 @@ func init() { } type TCPTransport struct { - myTransportAdapter + name string + dialer N.Dialer + serverAddr M.Socksaddr } func NewTCPTransport(options TransportOptions) (*TCPTransport, error) { @@ -43,29 +47,61 @@ func NewTCPTransport(options TransportOptions) (*TCPTransport, error) { } func newTCPTransport(options TransportOptions, serverAddr M.Socksaddr) *TCPTransport { - transport := &TCPTransport{ - newAdapter(options, serverAddr, false), + return &TCPTransport{ + name: options.Name, + dialer: options.Dialer, + serverAddr: serverAddr, } - transport.handler = transport - return transport } -func (t *TCPTransport) DialContext(ctx context.Context) (net.Conn, error) { - return t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr) +func (t *TCPTransport) Name() string { + return t.name } -func (t *TCPTransport) ReadMessage(conn net.Conn) (*dns.Msg, error) { - var length uint16 - err := binary.Read(conn, binary.BigEndian, &length) +func (t *TCPTransport) Start() error { + return nil +} + +func (t *TCPTransport) Reset() { +} + +func (t *TCPTransport) Close() error { + return nil +} + +func (t *TCPTransport) Raw() bool { + return true +} + +func (t *TCPTransport) Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error) { + conn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr) + if err != nil { + return nil, err + } + defer conn.Close() + err = writeMessage(conn, message) + if err != nil { + return nil, err + } + return readMessage(conn) +} + +func (t *TCPTransport) Lookup(ctx context.Context, domain string, strategy DomainStrategy) ([]netip.Addr, error) { + return nil, os.ErrInvalid +} + +func readMessage(reader io.Reader) (*dns.Msg, error) { + var responseLen uint16 + err := binary.Read(reader, binary.BigEndian, &responseLen) if err != nil { return nil, err } - if length < 10 { + if responseLen < 10 { return nil, dns.ErrShortRead } - buffer := buf.NewSize(int(length)) + buffer := buf.NewSize(int(responseLen)) defer buffer.Release() - _, err = buffer.ReadFullFrom(conn, int(length)) + _, err = buffer.ReadFullFrom(reader, int(responseLen)) if err != nil { return nil, err } @@ -74,7 +110,7 @@ func (t *TCPTransport) ReadMessage(conn net.Conn) (*dns.Msg, error) { return &message, err } -func (t *TCPTransport) WriteMessage(conn net.Conn, message *dns.Msg) error { +func writeMessage(writer io.Writer, message *dns.Msg) error { requestLen := message.Len() buffer := buf.NewSize(3 + requestLen) defer buffer.Release() @@ -86,5 +122,5 @@ func (t *TCPTransport) WriteMessage(conn net.Conn, message *dns.Msg) error { return err } buffer.Truncate(2 + len(rawMessage)) - return common.Error(conn.Write(buffer.Bytes())) + return common.Error(writer.Write(buffer.Bytes())) } diff --git a/transport_tls.go b/transport_tls.go index 6a4fe55..dcd014f 100644 --- a/transport_tls.go +++ b/transport_tls.go @@ -3,15 +3,16 @@ package dns import ( "context" "crypto/tls" - "encoding/binary" - "net" + "net/netip" "net/url" + "os" + "sync" - "github.com/sagernet/sing/common" - "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/x/list" "github.com/miekg/dns" ) @@ -25,7 +26,17 @@ func init() { } type TLSTransport struct { - myTransportAdapter + name string + dialer N.Dialer + logger logger.ContextLogger + serverAddr M.Socksaddr + access sync.Mutex + connections list.List[*tlsDNSConn] +} + +type tlsDNSConn struct { + *tls.Conn + queryId uint16 } func NewTLSTransport(options TransportOptions) (*TLSTransport, error) { @@ -40,57 +51,83 @@ func NewTLSTransport(options TransportOptions) (*TLSTransport, error) { if serverAddr.Port == 0 { serverAddr.Port = 853 } - transport := &TLSTransport{ - newAdapter(options, serverAddr, true), - } - transport.handler = transport - return transport, nil + return newTLSTransport(options, serverAddr), nil } -func (t *TLSTransport) DialContext(ctx context.Context) (net.Conn, error) { - conn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr) - if err != nil { - return nil, err +func newTLSTransport(options TransportOptions, serverAddr M.Socksaddr) *TLSTransport { + return &TLSTransport{ + name: options.Name, + dialer: options.Dialer, + logger: options.Logger, + serverAddr: serverAddr, } - tlsConn := tls.Client(conn, &tls.Config{ - ServerName: t.serverAddr.AddrString(), - }) - err = tlsConn.HandshakeContext(ctx) - if err != nil { - conn.Close() - return nil, err +} + +func (t *TLSTransport) Name() string { + return t.name +} + +func (t *TLSTransport) Start() error { + return nil +} + +func (t *TLSTransport) Reset() { + t.access.Lock() + defer t.access.Unlock() + for connection := t.connections.Front(); connection != nil; connection = connection.Next() { + connection.Value.Close() } - return tlsConn, nil + t.connections.Init() +} + +func (t *TLSTransport) Close() error { + t.Reset() + return nil } -func (t *TLSTransport) ReadMessage(conn net.Conn) (*dns.Msg, error) { - var length uint16 - err := binary.Read(conn, binary.BigEndian, &length) +func (t *TLSTransport) Raw() bool { + return true +} + +func (t *TLSTransport) Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error) { + t.access.Lock() + conn := t.connections.PopFront() + t.access.Unlock() + if conn == nil { + tcpConn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr) + if err != nil { + return nil, err + } + tlsConn := tls.Client(tcpConn, &tls.Config{ + ServerName: t.serverAddr.AddrString(), + }) + err = tlsConn.HandshakeContext(ctx) + if err != nil { + tcpConn.Close() + return nil, err + } + conn = &tlsDNSConn{Conn: tlsConn} + } + messageId := message.Id + conn.queryId++ + message.Id = conn.queryId + err := writeMessage(conn, message) if err != nil { - return nil, err + conn.Close() + return nil, E.Cause(err, "write request") } - buffer := buf.NewSize(int(length)) - defer buffer.Release() - _, err = buffer.ReadFullFrom(conn, int(length)) + response, err := readMessage(conn) if err != nil { - return nil, err + conn.Close() + return nil, E.Cause(err, "read response") } - var message dns.Msg - err = message.Unpack(buffer.Bytes()) - return &message, err + response.Id = messageId + t.access.Lock() + t.connections.PushBack(conn) + t.access.Unlock() + return response, nil } -func (t *TLSTransport) WriteMessage(conn net.Conn, message *dns.Msg) error { - requestLen := message.Len() - buffer := buf.NewSize(3 + requestLen) - defer buffer.Release() - common.Must(binary.Write(buffer, binary.BigEndian, uint16(requestLen))) - exMessage := *message - exMessage.Compress = true - rawMessage, err := exMessage.PackBuffer(buffer.FreeBytes()) - if err != nil { - return err - } - buffer.Truncate(2 + len(rawMessage)) - return common.Error(conn.Write(buffer.Bytes())) +func (t *TLSTransport) Lookup(ctx context.Context, domain string, strategy DomainStrategy) ([]netip.Addr, error) { + return nil, os.ErrInvalid } diff --git a/transport_udp.go b/transport_udp.go index 2d341f7..3903de1 100644 --- a/transport_udp.go +++ b/transport_udp.go @@ -3,13 +3,18 @@ package dns import ( "context" "net" + "net/netip" "net/url" + "os" + "sync" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/task" "github.com/miekg/dns" ) @@ -23,10 +28,18 @@ func init() { } type UDPTransport struct { - myTransportAdapter - tcpTransport *TCPTransport + name string + optCtx context.Context + ctx context.Context + cancel context.CancelFunc + dialer N.Dialer logger logger.ContextLogger + serverAddr M.Socksaddr + clientAddr netip.Prefix udpSize int + tcpTransport *TCPTransport + access sync.Mutex + conn *dnsConnection } func NewUDPTransport(options TransportOptions) (*UDPTransport, error) { @@ -42,18 +55,49 @@ func NewUDPTransport(options TransportOptions) (*UDPTransport, error) { if serverAddr.Port == 0 { serverAddr.Port = 53 } - transport := &UDPTransport{ - newAdapter(options, serverAddr, true), - newTCPTransport(options, serverAddr), - options.Logger, - 512, - } - transport.handler = transport - return transport, nil + ctx, cancel := context.WithCancel(options.Context) + return &UDPTransport{ + name: options.Name, + optCtx: options.Context, + ctx: ctx, + cancel: cancel, + dialer: options.Dialer, + logger: options.Logger, + serverAddr: serverAddr, + clientAddr: options.ClientSubnet, + udpSize: 512, + tcpTransport: newTCPTransport(options, serverAddr), + }, nil +} + +func (t *UDPTransport) Name() string { + return t.name +} + +func (t *UDPTransport) Start() error { + return nil +} + +func (t *UDPTransport) Reset() { + t.cancel() + t.ctx, t.cancel = context.WithCancel(t.optCtx) +} + +func (t *UDPTransport) Close() error { + t.cancel() + return nil +} + +func (t *UDPTransport) Raw() bool { + return true +} + +func (t *UDPTransport) Lookup(ctx context.Context, domain string, strategy DomainStrategy) ([]netip.Addr, error) { + return nil, os.ErrInvalid } func (t *UDPTransport) Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error) { - response, err := t.myTransportAdapter.Exchange(ctx, message) + response, err := t.exchange(ctx, message) if err != nil { return nil, err } @@ -64,23 +108,11 @@ func (t *UDPTransport) Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg return response, nil } -func (t *UDPTransport) DialContext(ctx context.Context) (net.Conn, error) { - return t.dialer.DialContext(ctx, "udp", t.serverAddr) -} - -func (t *UDPTransport) ReadMessage(conn net.Conn) (*dns.Msg, error) { - buffer := buf.NewSize(t.udpSize) - defer buffer.Release() - _, err := buffer.ReadOnceFrom(conn) +func (t *UDPTransport) exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error) { + conn, err := t.open(ctx) if err != nil { return nil, err } - var message dns.Msg - err = message.Unpack(buffer.Bytes()) - return &message, err -} - -func (t *UDPTransport) WriteMessage(conn net.Conn, message *dns.Msg) error { if edns0Opt := message.IsEdns0(); edns0Opt != nil { if udpSize := int(edns0Opt.UDPSize()); udpSize > t.udpSize { t.udpSize = udpSize @@ -90,9 +122,130 @@ func (t *UDPTransport) WriteMessage(conn net.Conn, message *dns.Msg) error { defer buffer.Release() exMessage := *message exMessage.Compress = true + messageId := message.Id + callback := &dnsCallback{ + done: make(chan struct{}), + } + conn.access.Lock() + conn.queryId++ + exMessage.Id = conn.queryId + conn.callbacks[exMessage.Id] = callback + conn.access.Unlock() + defer func() { + conn.access.Lock() + delete(conn.callbacks, messageId) + conn.access.Unlock() + callback.access.Lock() + select { + case <-callback.done: + default: + close(callback.done) + } + callback.access.Unlock() + }() rawMessage, err := exMessage.PackBuffer(buffer.FreeBytes()) if err != nil { - return err + return nil, err + } + _, err = conn.Write(rawMessage) + if err != nil { + conn.Close() + return nil, err + } + select { + case <-callback.done: + callback.message.Id = messageId + return callback.message, nil + case <-conn.ctx.Done(): + return nil, E.Errors(conn.err, conn.ctx.Err()) + case <-ctx.Done(): + conn.Close() + return nil, ctx.Err() + } +} + +func (t *UDPTransport) open(ctx context.Context) (*dnsConnection, error) { + connection := t.conn + if connection != nil && !common.Done(connection.ctx) { + return connection, nil + } + t.access.Lock() + defer t.access.Unlock() + connection = t.conn + if connection != nil && !common.Done(connection.ctx) { + return connection, nil + } + conn, err := t.dialer.DialContext(ctx, "udp", t.serverAddr) + if err != nil { + return nil, err + } + connCtx, cancel := context.WithCancel(t.ctx) + connection = &dnsConnection{ + Conn: conn, + ctx: connCtx, + cancel: cancel, + callbacks: make(map[uint16]*dnsCallback), } - return common.Error(conn.Write(rawMessage)) + t.conn = connection + go t.recvLoop(connection) + return connection, nil +} + +func (t *UDPTransport) recvLoop(conn *dnsConnection) { + var group task.Group + group.Append0(func(ctx context.Context) error { + for { + buffer := buf.NewSize(t.udpSize) + _, err := buffer.ReadOnceFrom(conn) + if err != nil { + buffer.Release() + return err + } + var message dns.Msg + err = message.Unpack(buffer.Bytes()) + buffer.Release() + if err != nil { + return err + } + conn.access.RLock() + callback, loaded := conn.callbacks[message.Id] + conn.access.RUnlock() + if !loaded { + continue + } + callback.access.Lock() + select { + case <-callback.done: + default: + callback.message = &message + close(callback.done) + } + callback.access.Unlock() + } + }) + group.Cleanup(func() { + conn.Close() + }) + group.Run(conn.ctx) +} + +type dnsConnection struct { + net.Conn + ctx context.Context + cancel context.CancelFunc + access sync.RWMutex + err error + queryId uint16 + callbacks map[uint16]*dnsCallback +} + +func (c *dnsConnection) Close() error { + c.cancel() + return c.Conn.Close() +} + +type dnsCallback struct { + access sync.Mutex + message *dns.Msg + done chan struct{} } From c704fd084746ddda0368c6a8eb41c5d81c3a7ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 8 Jun 2024 22:18:45 +0800 Subject: [PATCH 6/8] Fix timeout not apply to lookup --- client.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/client.go b/client.go index cd60cf8..1475687 100644 --- a/client.go +++ b/client.go @@ -19,7 +19,7 @@ import ( const ( DefaultTTL = 600 - DefaultTimeout = 5 * time.Second + DefaultTimeout = 10 * time.Second ) var ( @@ -158,14 +158,9 @@ func (c *Client) ExchangeWithResponseCheck(ctx context.Context, transport Transp return nil, ErrResponseRejectedCached } } - var cancel context.CancelFunc - if c.timeout > 0 { - ctx, cancel = context.WithTimeout(ctx, c.timeout) - } + ctx, cancel := context.WithTimeout(ctx, c.timeout) response, err := transport.Exchange(ctx, message) - if cancel != nil { - cancel() - } + cancel() if err != nil { return nil, err } @@ -306,8 +301,10 @@ func (c *Client) LookupWithResponseCheck(ctx context.Context, transport Transpor return nil, ErrResponseRejectedCached } } + ctx, cancel := context.WithTimeout(ctx, c.timeout) var rCode int response, err := transport.Lookup(ctx, domain, strategy) + cancel() if err != nil { return nil, wrapError(err) } From e88a7cbce9ca4c1822464dc34bb68c1195b59cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 9 Jun 2024 07:15:47 +0800 Subject: [PATCH 7/8] Improve TLS transport --- transport_tls.go | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/transport_tls.go b/transport_tls.go index dcd014f..93eeead 100644 --- a/transport_tls.go +++ b/transport_tls.go @@ -93,21 +93,28 @@ func (t *TLSTransport) Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg t.access.Lock() conn := t.connections.PopFront() t.access.Unlock() - if conn == nil { - tcpConn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr) - if err != nil { - return nil, err + if conn != nil { + response, err := t.exchange(message, conn) + if err == nil { + return response, nil } - tlsConn := tls.Client(tcpConn, &tls.Config{ - ServerName: t.serverAddr.AddrString(), - }) - err = tlsConn.HandshakeContext(ctx) - if err != nil { - tcpConn.Close() - return nil, err - } - conn = &tlsDNSConn{Conn: tlsConn} } + tcpConn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr) + if err != nil { + return nil, err + } + tlsConn := tls.Client(tcpConn, &tls.Config{ + ServerName: t.serverAddr.AddrString(), + }) + err = tlsConn.HandshakeContext(ctx) + if err != nil { + tcpConn.Close() + return nil, err + } + return t.exchange(message, &tlsDNSConn{Conn: tlsConn}) +} + +func (t *TLSTransport) exchange(message *dns.Msg, conn *tlsDNSConn) (*dns.Msg, error) { messageId := message.Id conn.queryId++ message.Id = conn.queryId From be4b06b5f40d4d4662d3708c57caf690245fbeb4 Mon Sep 17 00:00:00 2001 From: TargetLocked <32962687+TargetLocked@users.noreply.github.com> Date: Sun, 14 Jul 2024 18:34:40 +0800 Subject: [PATCH 8/8] fix: missing context in lookupToExchange --- client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client.go b/client.go index 1475687..7e6597e 100644 --- a/client.go +++ b/client.go @@ -231,7 +231,7 @@ func (c *Client) LookupWithResponseCheck(ctx context.Context, transport Transpor var response4 []netip.Addr var response6 []netip.Addr var group task.Group - group.Append("exchange4", func(ctx context.Context) error { + group.Append("exchange4", func(_ context.Context) error { response, err := c.lookupToExchange(ctx, transport, dnsName, dns.TypeA, strategy, responseChecker) if err != nil { return err @@ -239,7 +239,7 @@ func (c *Client) LookupWithResponseCheck(ctx context.Context, transport Transpor response4 = response return nil }) - group.Append("exchange6", func(ctx context.Context) error { + group.Append("exchange6", func(_ context.Context) error { response, err := c.lookupToExchange(ctx, transport, dnsName, dns.TypeAAAA, strategy, responseChecker) if err != nil { return err