main
c8b91bc · 4 months ago 7 commits
 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}