main
c8b91bc · 4 months ago 7 commits
  1package dns
  2
  3import (
  4	"encoding/binary"
  5	"fmt"
  6)
  7
  8/**
  9Every DNS packet has this structure:
 10
 11+---------------------+
 12|       Header        |  12 bytes, always
 13+---------------------+
 14|      Question       |  variable
 15+---------------------+
 16|      Answer         |  variable (in responses)
 17+---------------------+
 18|     Authority       |  variable (we'll get to this)
 19+---------------------+
 20|     Additional      |  variable
 21+---------------------+
 22
 231byte - 8bits
 24Here its 12 bytes - 96 bits
 25
 26Which inturns split into 16bit per section so totally its 6 section as follows:
 27
 280  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15
 29+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 30|                      ID                       |
 31+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 32|QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
 33+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 34|                    QDCOUNT                    |
 35+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 36|                    ANCOUNT                    |
 37+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 38|                    NSCOUNT                    |
 39+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 40|                    ARCOUNT                    |
 41+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 42
 43ID - 16 Random ID, clients sets and server echoes
 44QR - QUERY/RESPONSE 0/1
 45Opcode - most case its 0 means 'QUERY'
 46AA - AUTHORATIVE ANSWER - determine whether its from the owning zone source or from recursive resolver
 47TC - Truncated - response is way big so try with TCP (0/1)
 48RD - Recurion Desired - client asking server to recurse and give full resolution (0/1)
 49RA - Recursion Available - whether server can recurse or not (0/1)
 50RCODE - Response Code - 0 → OK, 3 → NXDOMAIN (domain doesn't exist), 2 → Server failure, 5 → Refused
 51QDCOUNT,ANCOUNT,NSCOUNT,ARCOUNT - Number of Questions,Answers,Authority Records and Additional Records
 52
 53**/
 54
 55type Header struct {
 56	ID      uint16
 57	Flags   uint16
 58	QDCount uint16
 59	ANCount uint16
 60	NSCount uint16
 61	ARCount uint16
 62}
 63
 64const (
 65	FlagQR     = 1 << 15
 66	FlagAA     = 1 << 10
 67	FlagTC     = 1 << 9
 68	FlagRD     = 1 << 8
 69	FlagRA     = 1 << 7
 70	RCodeMask  = 0x000F // Bottom 4 bits
 71	OpcodeMask = 0x7800 // Bits 11-14
 72)
 73
 74func (h *Header) IsItResponse() bool {
 75	return h.Flags&FlagQR != 0
 76}
 77
 78func (h *Header) SetResponse() {
 79	h.Flags |= FlagQR
 80}
 81
 82func (h *Header) SetAA() {
 83	h.Flags |= FlagAA
 84}
 85
 86func (h *Header) SetRD() {
 87	h.Flags |= FlagRD
 88}
 89
 90func (h *Header) SetRA() {
 91	h.Flags |= FlagRA
 92}
 93
 94func (h *Header) SetRCode(code uint8) {
 95	h.Flags = (h.Flags &^ RCodeMask) | uint16(code)
 96}
 97
 98func (h *Header) GetRCode() uint8 {
 99	return uint8(h.Flags & RCodeMask)
100}
101
102// Pack serializes the header to 12 bytes (big-endian per RFC)
103func (h *Header) Pack() []byte {
104	buf := make([]byte, 12)
105	binary.BigEndian.PutUint16(buf[0:2], h.ID)
106	binary.BigEndian.PutUint16(buf[2:4], h.Flags)
107	binary.BigEndian.PutUint16(buf[4:6], h.QDCount)
108	binary.BigEndian.PutUint16(buf[6:8], h.ANCount)
109	binary.BigEndian.PutUint16(buf[8:10], h.NSCount)
110	binary.BigEndian.PutUint16(buf[10:12], h.ARCount)
111	return buf
112}
113
114// UnpackHeader parses a 12-byte DNS header
115func UnpackHeader(buf []byte) (*Header, error) {
116	if len(buf) < 12 {
117		return nil, fmt.Errorf("buffer too short for header: %d bytes", len(buf))
118	}
119	return &Header{
120		ID:      binary.BigEndian.Uint16(buf[0:2]),
121		Flags:   binary.BigEndian.Uint16(buf[2:4]),
122		QDCount: binary.BigEndian.Uint16(buf[4:6]),
123		ANCount: binary.BigEndian.Uint16(buf[6:8]),
124		NSCount: binary.BigEndian.Uint16(buf[8:10]),
125		ARCount: binary.BigEndian.Uint16(buf[10:12]),
126	}, nil
127}