Skip to content

Commit 65fea35

Browse files
committed
feat: doh client support ecs and ecs-override
1 parent f305e44 commit 65fea35

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

dns/doh.go

+29
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"io"
1010
"net"
1111
"net/http"
12+
"net/netip"
1213
"net/url"
1314
"runtime"
1415
"strconv"
@@ -67,6 +68,8 @@ type dnsOverHTTPS struct {
6768
dialer *dnsDialer
6869
addr string
6970
skipCertVerify bool
71+
ecsPrefix netip.Prefix
72+
ecsOverride bool
7073
}
7174

7275
// type check
@@ -99,6 +102,28 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin
99102
doh.skipCertVerify = true
100103
}
101104

105+
if ecs := params["ecs"]; ecs != "" {
106+
prefix, err := netip.ParsePrefix(ecs)
107+
if err != nil {
108+
addr, err := netip.ParseAddr(ecs)
109+
if err != nil {
110+
log.Warnln("DOH config with invalid ecs: %s", ecs)
111+
} else {
112+
doh.ecsPrefix = netip.PrefixFrom(addr, addr.BitLen())
113+
}
114+
} else {
115+
doh.ecsPrefix = prefix
116+
}
117+
}
118+
119+
if doh.ecsPrefix.IsValid() {
120+
log.Debugln("DOH [%s] config with ecs: %s", doh.addr, doh.ecsPrefix)
121+
}
122+
123+
if params["ecs-override"] == "true" {
124+
doh.ecsOverride = true
125+
}
126+
102127
runtime.SetFinalizer(doh, (*dnsOverHTTPS).Close)
103128

104129
return doh
@@ -126,6 +151,10 @@ func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.
126151
}
127152
}()
128153

154+
if doh.ecsPrefix.IsValid() {
155+
setEdns0Subnet(m, doh.ecsPrefix, doh.ecsOverride)
156+
}
157+
129158
// Check if there was already an active client before sending the request.
130159
// We'll only attempt to re-connect if there was one.
131160
client, isCached, err := doh.getClient(ctx)

dns/edns0_subnet.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package dns
2+
3+
import (
4+
"net/netip"
5+
6+
"github.com/miekg/dns"
7+
)
8+
9+
func setEdns0Subnet(message *dns.Msg, clientSubnet netip.Prefix, override bool) bool {
10+
var (
11+
optRecord *dns.OPT
12+
subnetOption *dns.EDNS0_SUBNET
13+
)
14+
findExists:
15+
for _, record := range message.Extra {
16+
var isOPTRecord bool
17+
if optRecord, isOPTRecord = record.(*dns.OPT); isOPTRecord {
18+
for _, option := range optRecord.Option {
19+
var isEDNS0Subnet bool
20+
if subnetOption, isEDNS0Subnet = option.(*dns.EDNS0_SUBNET); isEDNS0Subnet {
21+
if !override {
22+
return false
23+
}
24+
break findExists
25+
}
26+
}
27+
}
28+
}
29+
if optRecord == nil {
30+
optRecord = &dns.OPT{
31+
Hdr: dns.RR_Header{
32+
Name: ".",
33+
Rrtype: dns.TypeOPT,
34+
},
35+
}
36+
message.Extra = append(message.Extra, optRecord)
37+
}
38+
if subnetOption == nil {
39+
subnetOption = new(dns.EDNS0_SUBNET)
40+
optRecord.Option = append(optRecord.Option, subnetOption)
41+
}
42+
subnetOption.Code = dns.EDNS0SUBNET
43+
if clientSubnet.Addr().Is4() {
44+
subnetOption.Family = 1
45+
} else {
46+
subnetOption.Family = 2
47+
}
48+
subnetOption.SourceNetmask = uint8(clientSubnet.Bits())
49+
subnetOption.Address = clientSubnet.Addr().AsSlice()
50+
return true
51+
}

0 commit comments

Comments
 (0)