Files
routedns/ecs-modifier.go
Ali e51f51e1bc move from logrus to slog (#422)
* Migrate from logrus to slog

* fully removing logrus

* should be working now

* Update pipeline.go

Co-authored-by: Frank Olbricht <frank.olbricht@gmail.com>

* Update response-blocklist-name.go

Co-authored-by: Frank Olbricht <frank.olbricht@gmail.com>

* added null logger

* Update pipeline.go

---------

Co-authored-by: Frank Olbricht <frank.olbricht@gmail.com>
2025-01-13 08:43:30 +01:00

190 lines
4.4 KiB
Go

package rdns
import (
"errors"
"net"
"github.com/miekg/dns"
)
// ECSModifier manipulates EDNS0 Client Subnet in queries.
type ECSModifier struct {
id string
resolver Resolver
modifier ECSModifierFunc
}
var _ Resolver = &ECSModifier{}
// ECSModifierFunc takes a DNS query and modifies its EDN0 Client Subdomain record
type ECSModifierFunc func(id string, q *dns.Msg, ci ClientInfo)
// NewECSModifier initializes an ECS modifier.
func NewECSModifier(id string, resolver Resolver, f ECSModifierFunc) (*ECSModifier, error) {
c := &ECSModifier{id: id, resolver: resolver, modifier: f}
return c, nil
}
// Resolve modifies the OPT EDNS0 record and passes it to the next resolver.
func (r *ECSModifier) Resolve(q *dns.Msg, ci ClientInfo) (*dns.Msg, error) {
if len(q.Question) < 1 {
return nil, errors.New("no question in query")
}
// Modify the query
if r.modifier != nil {
r.modifier(r.id, q, ci)
}
// Pass it on upstream
return r.resolver.Resolve(q, ci)
}
func (r *ECSModifier) String() string {
return r.id
}
func ECSModifierDelete(id string, q *dns.Msg, ci ClientInfo) {
edns0 := q.IsEdns0()
if edns0 == nil {
return
}
// Filter out any ECS options
var hasECS bool
newOpt := make([]dns.EDNS0, 0, len(edns0.Option))
for _, opt := range edns0.Option {
if _, ok := opt.(*dns.EDNS0_SUBNET); ok {
hasECS = true
continue
}
newOpt = append(newOpt, opt)
}
edns0.Option = newOpt
// Only log if id is set to a non-empty string. Avoids double-logging
// if called by other modifiers
if hasECS && id != "" {
logger(id, q, ci).Debug("removing ecs option")
}
}
func ECSModifierAdd(addr net.IP, prefix4, prefix6 uint8) ECSModifierFunc {
return func(id string, q *dns.Msg, ci ClientInfo) {
// Drop any existing ECS options
ECSModifierDelete("", q, ci)
// If no address is configured, use that of the client
sourceIP := addr
if sourceIP == nil {
sourceIP = ci.SourceIP
}
var (
family uint16
mask uint8
)
if ip4 := sourceIP.To4(); len(ip4) == net.IPv4len {
family = 1 // ip4
sourceIP = ip4
mask = prefix4
sourceIP = sourceIP.Mask(net.CIDRMask(int(prefix4), 32))
} else {
family = 2 // ip6
mask = prefix6
sourceIP = sourceIP.Mask(net.CIDRMask(int(prefix6), 128))
}
// Add a new record if there's no EDNS0 at all
edns0 := q.IsEdns0()
if edns0 == nil {
q.SetEdns0(4096, false)
edns0 = q.IsEdns0()
}
// Append the ECS option
ecs := new(dns.EDNS0_SUBNET)
ecs.Code = dns.EDNS0SUBNET
ecs.Family = family // 1 for IPv4 source address, 2 for IPv6
ecs.SourceNetmask = mask // 32 for IPV4, 128 for IPv6
ecs.SourceScope = 0
ecs.Address = sourceIP
edns0.Option = append(edns0.Option, ecs)
log := logger(id, q, ci)
log.Debug("adding ecs option",
"ecs", sourceIP.String(),
"mask", mask)
}
}
func ECSModifierAddIfMissing(addr net.IP, prefix4, prefix6 uint8) ECSModifierFunc {
addFunc := ECSModifierAdd(addr, prefix4, prefix6)
return func(id string, q *dns.Msg, ci ClientInfo) {
// See if we have an ECS option already
edns0 := q.IsEdns0()
if edns0 != nil {
// Find the ECS option
for _, opt := range edns0.Option {
ecs, ok := opt.(*dns.EDNS0_SUBNET)
if ok {
log := logger(id, q, ci)
log.Debug("ecs option already present",
"ecs", ecs.Address.String(),
"mask", ecs.SourceNetmask)
return // There's an ECS option already, don't touch it
}
}
}
// No ECS option found, add it
addFunc(id, q, ci)
}
}
func ECSModifierPrivacy(prefix4, prefix6 uint8) ECSModifierFunc {
return func(id string, q *dns.Msg, ci ClientInfo) {
edns0 := q.IsEdns0()
if edns0 == nil {
return
}
// Find the ECS option
var (
hasECS bool
beforeAddr net.IP
afterAddr net.IP
)
for _, opt := range edns0.Option {
ecs, ok := opt.(*dns.EDNS0_SUBNET)
if !ok {
continue
}
switch ecs.Family {
case 1: // ip4
beforeAddr = ecs.Address.To4()
afterAddr = beforeAddr.Mask(net.CIDRMask(int(prefix4), 32))
ecs.Address = afterAddr
ecs.SourceNetmask = prefix4
case 2: // ip6
beforeAddr = ecs.Address
afterAddr = beforeAddr.Mask(net.CIDRMask(int(prefix6), 128))
ecs.Address = afterAddr
ecs.SourceNetmask = prefix6
}
hasECS = true
}
if hasECS {
log := logger(id, q, ci)
log.Debug("modifying ecs privacy",
"before-addr", beforeAddr.String(),
"after-addr", afterAddr.String(),
"ip4prefix", prefix4,
"ip6prefix", prefix6)
}
}
}