1package dns
2
3import "fmt"
4
5const (
6 maxTXTString = 255 // RFC 1035: each string max 255 bytes
7 maxUDPPayload = 512 // RFC 1035: max UDP DNS message
8)
9
10// PackTXT encodes a slice of strings into TXT RData wire format
11// Each string becomes a length-prefixed chunk
12// "hello" "world" → 0x05 h e l l o 0x05 w o r l d
13func PackTXT(strings []string) ([]byte, error) {
14 var rdata []byte
15
16 for _, s := range strings {
17 if len(s) > maxTXTString {
18 return nil, fmt.Errorf("TXT string exceeds 255 bytes: %d", len(s))
19 }
20 rdata = append(rdata, byte(len(s)))
21 rdata = append(rdata, []byte(s)...)
22 }
23
24 return rdata, nil
25}
26
27// ChunkText splits a long string into 255-byte chunks
28// ready to be passed into PackTXT
29func ChunkText(text string) []string {
30 var chunks []string
31
32 for len(text) > 0 {
33 if len(text) <= maxTXTString {
34 chunks = append(chunks, text)
35 break
36 }
37 // Try to split on a word boundary near the 255 limit
38 cutoff := maxTXTString
39 for cutoff > 200 && text[cutoff] != ' ' {
40 cutoff--
41 }
42 chunks = append(chunks, text[:cutoff])
43 text = text[cutoff+1:]
44 }
45
46 return chunks
47}
48
49// NewTXTRecord builds a complete RR with TXT type from a long string
50// It handles chunking automatically
51func NewTXTRecord(name string, ttl uint32, text string) (*RR, error) {
52 chunks := ChunkText(text)
53
54 rdata, err := PackTXT(chunks)
55 if err != nil {
56 return nil, fmt.Errorf("pack txt: %w", err)
57 }
58
59 return &RR{
60 Name: name,
61 Type: TypeTXT,
62 Class: ClassIN,
63 TTL: ttl,
64 RData: rdata,
65 }, nil
66}