mirror of
https://github.com/AdguardTeam/AdGuardDNS.git
synced 2025-02-20 11:23:36 +08:00
247 lines
5.7 KiB
Go
247 lines
5.7 KiB
Go
package dnsmsg
|
|
|
|
import (
|
|
"net"
|
|
|
|
"github.com/AdguardTeam/golibs/syncutil"
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
// httpsCloner is a pool that can clone common parts of DNS messages of type
|
|
// HTTPS with fewer allocations.
|
|
type httpsCloner struct {
|
|
// Top-level structures.
|
|
|
|
rr *syncutil.Pool[dns.HTTPS]
|
|
|
|
// Values.
|
|
|
|
alpn *syncutil.Pool[dns.SVCBAlpn]
|
|
dohpath *syncutil.Pool[dns.SVCBDoHPath]
|
|
echconfig *syncutil.Pool[dns.SVCBECHConfig]
|
|
ipv4hint *syncutil.Pool[dns.SVCBIPv4Hint]
|
|
ipv6hint *syncutil.Pool[dns.SVCBIPv6Hint]
|
|
local *syncutil.Pool[dns.SVCBLocal]
|
|
mandatory *syncutil.Pool[dns.SVCBMandatory]
|
|
port *syncutil.Pool[dns.SVCBPort]
|
|
|
|
// Miscellaneous.
|
|
|
|
ip *syncutil.Pool[[16]byte]
|
|
}
|
|
|
|
// newHTTPSCloner returns a new properly initialized *httpsCloner.
|
|
func newHTTPSCloner() (c *httpsCloner) {
|
|
return &httpsCloner{
|
|
rr: syncutil.NewPool(func() (v *dns.HTTPS) {
|
|
return &dns.HTTPS{}
|
|
}),
|
|
|
|
alpn: syncutil.NewPool(func() (v *dns.SVCBAlpn) {
|
|
return &dns.SVCBAlpn{}
|
|
}),
|
|
dohpath: syncutil.NewPool(func() (v *dns.SVCBDoHPath) {
|
|
return &dns.SVCBDoHPath{}
|
|
}),
|
|
echconfig: syncutil.NewPool(func() (v *dns.SVCBECHConfig) {
|
|
return &dns.SVCBECHConfig{}
|
|
}),
|
|
ipv4hint: syncutil.NewPool(func() (v *dns.SVCBIPv4Hint) {
|
|
return &dns.SVCBIPv4Hint{}
|
|
}),
|
|
ipv6hint: syncutil.NewPool(func() (v *dns.SVCBIPv6Hint) {
|
|
return &dns.SVCBIPv6Hint{}
|
|
}),
|
|
local: syncutil.NewPool(func() (v *dns.SVCBLocal) {
|
|
return &dns.SVCBLocal{}
|
|
}),
|
|
mandatory: syncutil.NewPool(func() (v *dns.SVCBMandatory) {
|
|
return &dns.SVCBMandatory{}
|
|
}),
|
|
port: syncutil.NewPool(func() (v *dns.SVCBPort) {
|
|
return &dns.SVCBPort{}
|
|
}),
|
|
|
|
ip: syncutil.NewPool(func() (v *[16]byte) {
|
|
// Use the IPv6 length to increase the effectiveness of the pool.
|
|
return &[16]byte{}
|
|
}),
|
|
}
|
|
}
|
|
|
|
// clone returns a deep clone of rr. full is true if rr was cloned entirely
|
|
// without the use of [dns.Copy].
|
|
func (c *httpsCloner) clone(rr *dns.HTTPS) (clone *dns.HTTPS, full bool) {
|
|
if rr == nil {
|
|
return nil, true
|
|
}
|
|
|
|
clone = c.rr.Get()
|
|
|
|
clone.Hdr = rr.Hdr
|
|
clone.Priority = rr.Priority
|
|
clone.Target = rr.Target
|
|
|
|
if rr.Value == nil {
|
|
clone.Value = nil
|
|
|
|
return clone, true
|
|
}
|
|
|
|
clone.Value = clone.Value[:0]
|
|
for _, orig := range rr.Value {
|
|
valClone := c.cloneKV(orig)
|
|
if valClone == nil {
|
|
// This branch is only reached if there is a new SVCB key-value type
|
|
// in miekg/dns. Give up and just use their copy function.
|
|
return dns.Copy(rr).(*dns.HTTPS), false
|
|
}
|
|
|
|
clone.Value = append(clone.Value, valClone)
|
|
}
|
|
|
|
return clone, true
|
|
}
|
|
|
|
// cloneKV returns a deep clone of orig. clone is nil if orig wasn't
|
|
// recognized.
|
|
func (c *httpsCloner) cloneKV(orig dns.SVCBKeyValue) (clone dns.SVCBKeyValue) {
|
|
switch orig := orig.(type) {
|
|
case *dns.SVCBAlpn:
|
|
v := c.alpn.Get()
|
|
v.Alpn = appendIfNotNil(v.Alpn[:0], orig.Alpn)
|
|
|
|
clone = v
|
|
case *dns.SVCBDoHPath:
|
|
v := c.dohpath.Get()
|
|
*v = *orig
|
|
|
|
clone = v
|
|
case *dns.SVCBECHConfig:
|
|
v := c.echconfig.Get()
|
|
v.ECH = appendIfNotNil(v.ECH[:0], orig.ECH)
|
|
|
|
clone = v
|
|
case *dns.SVCBLocal:
|
|
v := c.local.Get()
|
|
v.KeyCode = orig.KeyCode
|
|
v.Data = appendIfNotNil(v.Data[:0], orig.Data)
|
|
|
|
clone = v
|
|
case *dns.SVCBMandatory:
|
|
v := c.mandatory.Get()
|
|
v.Code = appendIfNotNil(v.Code[:0], orig.Code)
|
|
|
|
clone = v
|
|
case *dns.SVCBPort:
|
|
v := c.port.Get()
|
|
*v = *orig
|
|
|
|
clone = v
|
|
case
|
|
*dns.SVCBNoDefaultAlpn,
|
|
*dns.SVCBOhttp:
|
|
// Just use the original value since these [dns.SVCBKeyValue] types are
|
|
// pointers to empty structures, so we're only interested in the actual
|
|
// type.
|
|
clone = orig
|
|
default:
|
|
clone = c.cloneIfHint(orig)
|
|
}
|
|
|
|
// This is only nil if there is a new SVCB key-value type in miekg/dns.
|
|
return clone
|
|
}
|
|
|
|
// cloneIfHint returns a deep clone of orig if it's either an [dns.SVCBIPv4Hint]
|
|
// or [dns.SVCBIPv6Hint]. Otherwise, it returns nil.
|
|
func (c *httpsCloner) cloneIfHint(orig dns.SVCBKeyValue) (clone dns.SVCBKeyValue) {
|
|
switch orig := orig.(type) {
|
|
case *dns.SVCBIPv4Hint:
|
|
v := c.ipv4hint.Get()
|
|
v.Hint = c.appendIPs(v.Hint[:0], orig.Hint)
|
|
|
|
return v
|
|
case *dns.SVCBIPv6Hint:
|
|
v := c.ipv6hint.Get()
|
|
v.Hint = c.appendIPs(v.Hint[:0], orig.Hint)
|
|
|
|
return v
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// appendIPs appends the clones of IP addresses from orig to hints and returns
|
|
// the resulting slice. clone is allocated as a single continuous slice.
|
|
func (c *httpsCloner) appendIPs(hints, orig []net.IP) (clone []net.IP) {
|
|
if len(orig) == 0 {
|
|
if orig == nil {
|
|
return nil
|
|
}
|
|
|
|
return []net.IP{}
|
|
}
|
|
|
|
for _, origIP := range orig {
|
|
ipArr := c.ip.Get()
|
|
ip := append(ipArr[:0], origIP...)
|
|
hints = append(hints, ip)
|
|
}
|
|
|
|
return hints
|
|
}
|
|
|
|
// put returns structures from rr into c's pools.
|
|
func (c *httpsCloner) put(rr *dns.HTTPS) {
|
|
if rr == nil {
|
|
return
|
|
}
|
|
|
|
for _, kv := range rr.Value {
|
|
c.putKV(kv)
|
|
}
|
|
|
|
c.rr.Put(rr)
|
|
}
|
|
|
|
// putKV returns structures from kv into c's pools.
|
|
func (c *httpsCloner) putKV(kv dns.SVCBKeyValue) {
|
|
switch kv := kv.(type) {
|
|
case *dns.SVCBAlpn:
|
|
c.alpn.Put(kv)
|
|
case *dns.SVCBDoHPath:
|
|
c.dohpath.Put(kv)
|
|
case *dns.SVCBECHConfig:
|
|
c.echconfig.Put(kv)
|
|
case *dns.SVCBIPv4Hint:
|
|
c.putIPs(kv.Hint)
|
|
c.ipv4hint.Put(kv)
|
|
case *dns.SVCBIPv6Hint:
|
|
c.putIPs(kv.Hint)
|
|
c.ipv6hint.Put(kv)
|
|
case *dns.SVCBLocal:
|
|
c.local.Put(kv)
|
|
case *dns.SVCBMandatory:
|
|
c.mandatory.Put(kv)
|
|
case *dns.SVCBPort:
|
|
c.port.Put(kv)
|
|
case
|
|
*dns.SVCBNoDefaultAlpn,
|
|
*dns.SVCBOhttp:
|
|
// Don't use pool for empty structures, see comment in [cloneKV].
|
|
default:
|
|
// This branch is only reached if there is a new SVCB key-value type
|
|
// in miekg/dns. Noting to do.
|
|
}
|
|
}
|
|
|
|
// putIPs returns the underlying arrays of ips into c if possible.
|
|
func (c *httpsCloner) putIPs(ips []net.IP) {
|
|
for _, ip := range ips {
|
|
if cap(ip) >= 16 {
|
|
c.ip.Put((*[16]byte)(ip[:16]))
|
|
}
|
|
}
|
|
}
|