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}