8 files changed,
320 insertions(+),
0 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-03-21 12:27:53 +0200
Change ID:
lmopynqxtwnlswmsrnsxqnkmqnkomtqs
Parent:
85452e1
A
dns-server/header.go
··· 1 +package main 2 + 3 +import ( 4 + "bytes" 5 + "encoding/binary" 6 + "fmt" 7 +) 8 + 9 +type ResultCode uint 10 + 11 +const ( 12 + NOERROR ResultCode = iota 13 + FORMERR 14 + SERVFAIL 15 + NXDOMAIN 16 + NOTIMP 17 + REFUSED 18 +) 19 + 20 +type Header struct { 21 + ID uint16 22 + 23 + RecursionDesired bool 24 + TruncatedMessage bool 25 + AuthoritativeAnswer bool 26 + OPCode uint8 27 + Response bool 28 + Rescode ResultCode 29 + CheckingDisabled bool 30 + AuthedData bool 31 + Z bool 32 + RecursionAvailable bool 33 + 34 + Questions uint16 35 + Answers uint16 36 + AuthoritativeEntries uint16 37 + ResourceEntries uint16 38 +} 39 + 40 +func ReadHeader(r *bytes.Reader) (Header, error) { 41 + var h Header 42 + if err := binary.Read(r, binary.BigEndian, &h.ID); err != nil { 43 + return h, fmt.Errorf("reading ID: %w", err) 44 + } 45 + 46 + var flags uint16 47 + if err := binary.Read(r, binary.BigEndian, &flags); err != nil { 48 + return h, fmt.Errorf("reading flags: %w", err) 49 + } 50 + h.unpackFlags(flags) 51 + 52 + if err := binary.Read(r, binary.BigEndian, &h.Questions); err != nil { 53 + return h, fmt.Errorf("reading questions: %w", err) 54 + } 55 + if err := binary.Read(r, binary.BigEndian, &h.Answers); err != nil { 56 + return h, fmt.Errorf("reading answers: %w", err) 57 + } 58 + if err := binary.Read(r, binary.BigEndian, &h.AuthoritativeEntries); err != nil { 59 + return h, fmt.Errorf("reading auth entries: %w", err) 60 + } 61 + if err := binary.Read(r, binary.BigEndian, &h.ResourceEntries); err != nil { 62 + return h, fmt.Errorf("reading resource entries: %w", err) 63 + } 64 + 65 + return h, nil 66 +} 67 + 68 +func (h *Header) unpackFlags(flags uint16) { 69 + h.RecursionDesired = flags&(1<<8) != 0 70 + h.TruncatedMessage = flags&(1<<9) != 0 71 + h.AuthoritativeAnswer = flags&(1<<10) != 0 72 + h.OPCode = uint8((flags >> 11) & 0xF) 73 + h.Response = flags&(1<<15) != 0 74 + h.Rescode = ResultCode(flags & 0xF) 75 + h.CheckingDisabled = flags&(1<<4) != 0 76 + h.AuthedData = flags&(1<<5) != 0 77 + h.Z = flags&(1<<6) != 0 78 + h.RecursionAvailable = flags&(1<<7) != 0 79 +} 80 + 81 +func (h Header) packFlags() uint16 { 82 + var flags uint16 83 + if h.RecursionDesired { 84 + flags |= 1 << 8 85 + } 86 + if h.TruncatedMessage { 87 + flags |= 1 << 9 88 + } 89 + if h.AuthoritativeAnswer { 90 + flags |= 1 << 10 91 + } 92 + flags |= uint16(h.OPCode) << 11 93 + if h.Response { 94 + flags |= 1 << 15 95 + } 96 + flags |= uint16(h.Rescode) 97 + if h.CheckingDisabled { 98 + flags |= 1 << 4 99 + } 100 + if h.AuthedData { 101 + flags |= 1 << 5 102 + } 103 + if h.Z { 104 + flags |= 1 << 6 105 + } 106 + if h.RecursionAvailable { 107 + flags |= 1 << 7 108 + } 109 + return flags 110 +}
A
dns-server/main.go
··· 1 +package main 2 + 3 +import ( 4 + _ "embed" 5 + "fmt" 6 +) 7 + 8 +//go:embed response_packet.txt 9 +var respPack []byte 10 + 11 +func main() { 12 + p, err := ParsePacket(respPack) 13 + if err != nil { 14 + panic(err) 15 + } 16 + 17 + fmt.Printf("%+v\n", p.Header) 18 + for _, q := range p.Questions { 19 + fmt.Printf("%+v\n", q) 20 + } 21 + for _, r := range p.Answers { 22 + fmt.Printf("%+v\n", r) 23 + } 24 + for _, r := range p.Authorities { 25 + fmt.Printf("%+v\n", r) 26 + } 27 + for _, r := range p.Resources { 28 + fmt.Printf("%+v\n", r) 29 + } 30 +}
A
dns-server/packet.go
··· 1 +package main 2 + 3 +import "bytes" 4 + 5 +type Packet struct { 6 + Header Header 7 + Questions []Question 8 + Answers []Record 9 + Authorities []Record 10 + Resources []Record 11 +} 12 + 13 +func ParsePacket(packet []byte) (Packet, error) { 14 + r := bytes.NewReader(packet) 15 + 16 + var err error 17 + var p Packet 18 + 19 + p.Header, err = ReadHeader(r) 20 + if err != nil { 21 + return Packet{}, err 22 + } 23 + 24 + // TODO: don't do int(Questions) ???? 25 + for i := 0; i < int(p.Header.Questions); i++ { 26 + q, err := ReadQuestion(r, packet) 27 + if err != nil { 28 + return Packet{}, err 29 + } 30 + p.Questions = append(p.Questions, q) 31 + } 32 + 33 + for i := 0; i < int(p.Header.Answers); i++ { 34 + a, err := ReadRecord(r, packet) 35 + if err != nil { 36 + return Packet{}, err 37 + } 38 + p.Answers = append(p.Answers, a) 39 + } 40 + 41 + for i := 0; i < int(p.Header.AuthoritativeEntries); i++ { 42 + ae, err := ReadRecord(r, packet) 43 + if err != nil { 44 + return Packet{}, err 45 + } 46 + p.Authorities = append(p.Authorities, ae) 47 + } 48 + 49 + for i := 0; i < int(p.Header.ResourceEntries); i++ { 50 + re, err := ReadRecord(r, packet) 51 + if err != nil { 52 + return Packet{}, err 53 + } 54 + p.Resources = append(p.Resources, re) 55 + } 56 + 57 + return p, nil 58 +}
A
dns-server/question.go
··· 1 +package main 2 + 3 +import ( 4 + "bytes" 5 + "encoding/binary" 6 +) 7 + 8 +type Question struct { 9 + Name string 10 + Type uint16 11 + Class uint16 12 +} 13 + 14 +func ReadQuestion(r *bytes.Reader, packet []byte) (Question, error) { 15 + name, err := readName(r, packet) 16 + if err != nil { 17 + return Question{}, err 18 + } 19 + 20 + var qtype, qclass uint16 21 + _ = binary.Read(r, binary.BigEndian, &qtype) 22 + _ = binary.Read(r, binary.BigEndian, &qclass) 23 + 24 + return Question{ 25 + Name: name, 26 + Type: qtype, 27 + Class: qclass, 28 + }, nil 29 +}
A
dns-server/readme
··· 1 +dns-server 2 +---------- 3 + 4 +Mainly me going through: <github.com/EmilHernvall/dnsguide>
A
dns-server/record.go
··· 1 +package main 2 + 3 +import ( 4 + "bytes" 5 + "encoding/binary" 6 + "fmt" 7 + "strings" 8 +) 9 + 10 +type Record struct { 11 + Name string 12 + Type uint16 13 + Class uint16 14 + TTL uint32 15 + Data string 16 +} 17 + 18 +func ReadRecord(r *bytes.Reader, packet []byte) (Record, error) { 19 + name, err := readName(r, packet) 20 + if err != nil { 21 + return Record{}, err 22 + } 23 + 24 + var rtype, class, rdlen uint16 25 + var ttl uint32 26 + _ = binary.Read(r, binary.BigEndian, &rtype) 27 + _ = binary.Read(r, binary.BigEndian, &class) 28 + _ = binary.Read(r, binary.BigEndian, &ttl) 29 + _ = binary.Read(r, binary.BigEndian, &rdlen) 30 + 31 + var data string 32 + switch rtype { 33 + case 1: // A 34 + var ip [4]byte 35 + _, _ = r.Read(ip[:]) 36 + data = fmt.Sprintf("%d.%d.%d.%d", 37 + ip[0], ip[1], ip[2], ip[3]) 38 + 39 + default: 40 + buf := make([]byte, rdlen) 41 + _, _ = r.Read(buf) 42 + data = fmt.Sprintf("%x", buf) 43 + } 44 + 45 + return Record{ 46 + Name: name, 47 + Type: rtype, 48 + Class: class, 49 + TTL: ttl, 50 + Data: data, 51 + }, nil 52 +} 53 + 54 +func readName(r *bytes.Reader, packet []byte) (string, error) { 55 + var labels []string 56 + for { 57 + length, err := r.ReadByte() 58 + if err != nil { 59 + return "", err 60 + } 61 + if length == 0 { 62 + break 63 + } 64 + // pointer: top two bits set (0xC0) 65 + if length&0xC0 == 0xC0 { 66 + low, err := r.ReadByte() 67 + if err != nil { 68 + return "", err 69 + } 70 + offset := int(uint16(length&0x3F)<<8 | uint16(low)) 71 + sub := bytes.NewReader(packet[offset:]) 72 + name, err := readName(sub, packet) 73 + if err != nil { 74 + return "", err 75 + } 76 + labels = append(labels, name) 77 + break // pointer always ends the name 78 + } 79 + buf := make([]byte, length) 80 + if _, err := r.Read(buf); err != nil { 81 + return "", err 82 + } 83 + labels = append(labels, string(buf)) 84 + } 85 + return strings.Join(labels, "."), nil 86 +}