Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions internal/dns/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ const (
FlagRD = 0x0100 // Bit 8: Recursion Desired
FlagRA = 0x0080 // Bit 7: Recursion Available

RcodeSuccess = 0 // No error
RcodeFormat = 1 // Format error - request malformed
RcodeServer = 2 // Server failure
RcodeNXDomain = 3 // Non-existent domain
RcodeSuccess = 0 // No error
RcodeFormat = 1 // Format error - request malformed
RcodeServer = 2 // Server failure
RcodeNXDomain = 3 // Non-existent domain
RcodeMask = 0x000F // Bottom 4 bits: Response code

TypeA uint16 = 1
TypeNS uint16 = 2
Expand All @@ -48,6 +49,7 @@ const (
TypeTXT uint16 = 16
TypeAAAA uint16 = 28
TypeOPT uint16 = 41
TypeTSIG uint16 = 250
ClassIN uint16 = 1

MaxMessageSize = 4096
Expand Down Expand Up @@ -337,7 +339,7 @@ func ValidateResponseWithRequest(reqHeader Header, reqQuestions []Question, resp
return ErrIDMismatch
}

if respHeader.Flags&0x8000 == 0 {
if respHeader.Flags&FlagQR == 0 {
return ErrNotResponse
}

Expand Down
2 changes: 1 addition & 1 deletion internal/dns/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func BuildResponse(req []byte, answers []Answer, rcode uint16) ([]byte, error) {
}

func responseFlags(reqFlags uint16, rcode uint16) uint16 {
return FlagQR | (reqFlags & FlagOpcode) | (reqFlags & FlagRD) | FlagRA | (rcode & 0x000F)
return FlagQR | (reqFlags & FlagOpcode) | (reqFlags & FlagRD) | FlagRA | (rcode & RcodeMask)
}

// MinimizeResponse strips authority/additional sections to reduce size.
Expand Down
58 changes: 1 addition & 57 deletions internal/dns/ttl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,6 @@ package dns

import "encoding/binary"

// RewriteAllRRsTTL rewrites the TTL field for all resource records
// (answer/authority/additional) to the provided value.
// This is used to serve cached packets with a TTL reflecting remaining lifetime.
//
// OPT (EDNS0) and TSIG are pseudo-RRs; their "TTL" fields are not TTLs and must not be rewritten.
func RewriteAllRRsTTL(msg []byte, ttl uint32) error {
h, err := ReadHeader(msg)
if err != nil {
return err
}
off := HeaderLen
for i := 0; i < int(h.QDCount); i++ {
next, err := SkipName(msg, off)
if err != nil {
return err
}
if len(msg) < next+4 {
return ErrShortBuffer
}
off = next + 4 // QTYPE(2) + QCLASS(2)
}

rewriteRR := func(count uint16) error {
for i := 0; i < int(count); i++ {
next, err := SkipName(msg, off)
if err != nil {
return err
}
if len(msg) < next+10 {
return ErrShortBuffer
}
rrType := binary.BigEndian.Uint16(msg[next : next+2])
if rrType != TypeOPT && rrType != 250 {
binary.BigEndian.PutUint32(msg[next+4:next+8], ttl)
}
rdlen := int(binary.BigEndian.Uint16(msg[next+8 : next+10]))
off = next + 10 + rdlen
if off > len(msg) {
return ErrShortBuffer
}
}
return nil
}

if err := rewriteRR(h.ANCount); err != nil {
return err
}
if err := rewriteRR(h.NSCount); err != nil {
return err
}
if err := rewriteRR(h.ARCount); err != nil {
return err
}
return nil
}

// RewriteTTLsAtOffsets rewrites TTL fields at provided offsets.
// Offsets must point at the start of the 4-byte TTL field.
func RewriteTTLsAtOffsets(msg []byte, ttl uint32, offsets []uint16) {
Expand Down Expand Up @@ -103,7 +47,7 @@ func CollectTTLOffsets(msg []byte) ([]uint16, error) {
return ErrShortBuffer
}
rrType := binary.BigEndian.Uint16(msg[next : next+2])
if rrType != TypeOPT && rrType != 250 {
if rrType != TypeOPT && rrType != TypeTSIG {
ttlOff := next + 4
if ttlOff+4 <= len(msg) {
offsets = append(offsets, uint16(ttlOff))
Expand Down
48 changes: 0 additions & 48 deletions internal/dns/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,6 @@ func NormalizeName(name string) string {
return asciiLowerIfNeeded(name)
}

// JoinLabels joins a slice of domain name labels with dots.
// Returns "." if the labels slice is empty.
func JoinLabels(labels []string) string {
if len(labels) == 0 {
return "."
}
return strings.Join(labels, ".")
}

// SplitLabels splits a domain name into its constituent labels.
// Returns an empty slice for empty names or root (".") names.
func SplitLabels(name string) []string {
if name == "" || name == "." {
return nil
}
name = strings.TrimSuffix(name, ".")

dotCount := strings.Count(name, ".")
labels := make([]string, 0, dotCount+1)

start := 0
for i := 0; i < len(name); i++ {
if name[i] == '.' {
if i > start {
labels = append(labels, name[start:i])
}
start = i + 1
}
}
if start < len(name) {
labels = append(labels, name[start:])
}
return labels
}

// IsSubdomain checks if child is a subdomain of parent.
// Both names should be normalized (lowercase, no trailing dot).
func IsSubdomain(child, parent string) bool {
Expand Down Expand Up @@ -161,16 +126,3 @@ func BuildQueryIntoWithEDNS(buf []byte, id uint16, name string, qtype, qclass ui

return off, nil
}

// BuildQuery constructs a DNS query message for the given name, type, and class.
// It uses a maximum-size buffer to avoid calculation errors, as DNS messages
// over UDP are limited to 512 bytes per RFC 1035.
// Returns the serialized query as a byte slice.
func BuildQuery(id uint16, name string, qtype, qclass uint16) ([]byte, error) {
buf := make([]byte, MaxMessageSize)
n, err := BuildQueryInto(buf, id, name, qtype, qclass)
if err != nil {
return nil, err
}
return buf[:n], nil
}
4 changes: 2 additions & 2 deletions internal/resolver/recursive.go
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ func (r *Recursive) resolveIterative(ctx context.Context, reqHeader dns.Header,
}

if debugEnabled {
rcode := respMsg.Header.Flags & 0x000F
rcode := respMsg.Header.Flags & dns.RcodeMask
kind := "empty"
if len(respMsg.Answers) > 0 {
kind = "answer"
Expand Down Expand Up @@ -506,7 +506,7 @@ func (r *Recursive) resolveIterative(ctx context.Context, reqHeader dns.Header,
return minimized, cleanup, nil
}

if (respMsg.Header.Flags & 0x000F) == dns.RcodeNXDomain {
if (respMsg.Header.Flags & dns.RcodeMask) == dns.RcodeNXDomain {
respMsg.Release()
segMin := r.tracers.iterMinimize.TraceNested(sampled)
minimized, _ := minimizeAndSetID(resp, clientID, true)
Expand Down
4 changes: 2 additions & 2 deletions internal/resolver/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func servfailFromRequest(req []byte) ([]byte, bool) {
resp := make([]byte, qEnd)
copy(resp, req[:qEnd])

hdr.Flags = dns.FlagQR | (hdr.Flags & dns.FlagOpcode) | (hdr.Flags & dns.FlagRD) | dns.FlagRA | (dns.RcodeServer & 0x000F)
hdr.Flags = dns.FlagQR | (hdr.Flags & dns.FlagOpcode) | (hdr.Flags & dns.FlagRD) | dns.FlagRA | (dns.RcodeServer & dns.RcodeMask)
hdr.QDCount = 1
hdr.ANCount = 0
hdr.NSCount = 0
Expand Down Expand Up @@ -134,7 +134,7 @@ func sleepOrDone(ctx context.Context, d time.Duration) bool {
// - NXDOMAIN and NOERROR/NODATA negative caching using SOA.MINIMUM (RFC2308-ish).
// It returns (ttl, true) when cacheable.
func cacheTTLForResponse(fullResp []byte, msg dns.Message, q dns.Question) (time.Duration, bool) {
rcode := msg.Header.Flags & 0x000F
rcode := msg.Header.Flags & dns.RcodeMask
q = q.Normalize()

soaTTL, hasSOA := negativeTTLFromSOA(fullResp, msg.Authorities)
Expand Down
24 changes: 5 additions & 19 deletions internal/server/udp.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func (s *Server) maybeLogServfail(resp []byte, addr net.Addr) {
if err != nil {
return
}
if (hdr.Flags & 0x000F) != dns.RcodeServer {
if (hdr.Flags & dns.RcodeMask) != dns.RcodeServer {
return
}
s.logger.Debug("sending SERVFAIL", "id", hdr.ID, "bytes", len(resp), "addr", addr.String())
Expand Down Expand Up @@ -146,25 +146,11 @@ func (s *Server) udpReadLoop(ctx context.Context, writer *udpWriter, cacheResolv
if !dns.IsValidRequest(b[:n]) {
if hdr, err := dns.ReadHeader(b[:n]); err == nil {
var formerr [dns.HeaderLen]byte
formerr[0] = byte(hdr.ID >> 8)
formerr[1] = byte(hdr.ID)
// 0x80 = QR=1 (response), 0x01 = RD; preserve OPCODE from request
opcode := byte((hdr.Flags & dns.FlagOpcode) >> 8)
if hdr.Flags&dns.FlagRD != 0 {
formerr[2] = 0x80 | 0x01 | opcode
} else {
formerr[2] = 0x80 | opcode
fmtHdr := dns.Header{
ID: hdr.ID,
Flags: dns.FlagQR | (hdr.Flags & dns.FlagOpcode) | (hdr.Flags & dns.FlagRD) | (dns.RcodeFormat & dns.RcodeMask),
}
// RCODE=1 (Format Error)
formerr[3] = 0x01
formerr[4] = 0x00
formerr[5] = 0x00
formerr[6] = 0x00
formerr[7] = 0x00
formerr[8] = 0x00
formerr[9] = 0x00
formerr[10] = 0x00
formerr[11] = 0x00
_ = dns.WriteHeader(formerr[:], fmtHdr)
if _, err := writer.conn.WriteTo(formerr[:], addr); err != nil {
s.WriteErrors.Add(1)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/server/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func servfailFromRequestInPlace(req []byte) ([]byte, bool) {
return nil, false
}

hdr.Flags = dns.FlagQR | (hdr.Flags & dns.FlagOpcode) | (hdr.Flags & dns.FlagRD) | dns.FlagRA | (dns.RcodeServer & 0x000F)
hdr.Flags = dns.FlagQR | (hdr.Flags & dns.FlagOpcode) | (hdr.Flags & dns.FlagRD) | dns.FlagRA | (dns.RcodeServer & dns.RcodeMask)
hdr.QDCount = 1
hdr.ANCount = 0
hdr.NSCount = 0
Expand Down