Skip to content

Commit 4461939

Browse files
authored
Merge pull request #773 from tweltz/master
Fixes TXT query results
2 parents 2b78371 + ff217a4 commit 4461939

File tree

4 files changed

+147
-5
lines changed

4 files changed

+147
-5
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require (
1313
github.com/diskfs/go-diskfs v1.2.0
1414
github.com/docker/go-units v0.4.0
1515
github.com/elastic/go-libaudit/v2 v2.2.0
16+
github.com/foxcpp/go-mockdns v1.0.0
1617
github.com/google/go-cmp v0.5.7
1718
github.com/gorilla/mux v1.8.0
1819
github.com/hashicorp/go-multierror v1.1.1

go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,8 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF
384384
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
385385
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
386386
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
387+
github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI=
388+
github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4=
387389
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
388390
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
389391
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -680,6 +682,7 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f
680682
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
681683
github.com/mibk/dupl v1.0.0/go.mod h1:pCr4pNxxIbFGvtyCOi0c7LVjmV6duhKWV+ex5vh38ME=
682684
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
685+
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
683686
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
684687
github.com/miekg/dns v1.1.48 h1:Ucfr7IIVyMBz4lRE8qmGUuZ4Wt3/ZGu9hmcMT3Uu4tQ=
685688
github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
@@ -1016,6 +1019,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
10161019
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
10171020
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
10181021
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
1022+
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
10191023
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
10201024
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
10211025
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -1091,6 +1095,7 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL
10911095
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
10921096
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
10931097
golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
1098+
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
10941099
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
10951100
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
10961101
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -1188,6 +1193,8 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
11881193
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
11891194
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
11901195
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1196+
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1197+
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
11911198
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
11921199
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
11931200
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1311,6 +1318,7 @@ golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgw
13111318
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
13121319
golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
13131320
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
1321+
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
13141322
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
13151323
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
13161324
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=

pkg/hostagent/dns/dns.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,17 @@ func (h *Handler) handleQuery(w dns.ResponseWriter, req *dns.Msg) {
174174
case dns.TypeTXT:
175175
txt, err := net.LookupTXT(q.Name)
176176
if err == nil && len(txt) > 0 {
177-
a := &dns.TXT{
178-
Hdr: hdr,
179-
Txt: txt,
177+
for _, s := range txt {
178+
a := &dns.TXT{
179+
Hdr: hdr,
180+
}
181+
// Per RFC7208 3.3, when a TXT answer has multiple strings, the answer must be treated as
182+
// a single concatenated string. net.LookupTXT is pre-concatenating such answers, which
183+
// means we need to break it back up for this resolver to return a valid response.
184+
a.Txt = chunkify(s, 255)
185+
reply.Answer = append(reply.Answer, a)
186+
handled = true
180187
}
181-
reply.Answer = append(reply.Answer, a)
182-
handled = true
183188
}
184189
case dns.TypeNS:
185190
ns, err := net.LookupNS(q.Name)
@@ -291,3 +296,15 @@ func Start(udpLocalPort, tcpLocalPort int, IPv6 bool, hosts map[string]string) (
291296
}
292297
return server, nil
293298
}
299+
300+
func chunkify(buffer string, limit int) []string {
301+
var result []string
302+
for len(buffer) > 0 {
303+
if len(buffer) < limit {
304+
limit = len(buffer)
305+
}
306+
result = append(result, buffer[:limit])
307+
buffer = buffer[limit:]
308+
}
309+
return result
310+
}

pkg/hostagent/dns/dns_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package dns
2+
3+
import (
4+
"io"
5+
"log"
6+
"net"
7+
"regexp"
8+
"testing"
9+
10+
"github.com/foxcpp/go-mockdns"
11+
"github.com/miekg/dns"
12+
"gotest.tools/v3/assert"
13+
)
14+
15+
var (
16+
dnsResult *dns.Msg
17+
)
18+
19+
func TestTXTRecords(t *testing.T) {
20+
testDomains := make([]string, 3)
21+
testDomains[0] = "onerecord.com"
22+
testDomains[1] = "multistringrecord.com"
23+
testDomains[2] = "multiplerecords.com"
24+
25+
expectedResults := make([]string, 3)
26+
expectedResults[0] = `onerecord.com.\s+5\s+IN\s+TXT\s+"My txt record"`
27+
expectedResults[1] = `multistringrecord.com.\s+5\s+IN\s+TXT\s+"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345" "67890123456789012345678901234567890"`
28+
expectedResults[2] = `multiplerecords.com.\s+5\s+IN\s+TXT\s+"record 1"\nmultiplerecords.com.\s*5\s*IN\s*TXT\s*"record 2"`
29+
30+
srv, _ := mockdns.NewServerWithLogger(map[string]mockdns.Zone{
31+
"onerecord.com.": {
32+
TXT: []string{"My txt record"},
33+
},
34+
"multistringrecord.com.": {
35+
TXT: []string{"123456789012345678901234567890123456789012345678901234567890" +
36+
"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
37+
"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
38+
"12345678901234567890123456789012345678901234567890"},
39+
},
40+
"multiplerecords.com.": {
41+
TXT: []string{"record 1", "record 2"},
42+
},
43+
}, log.New(io.Discard, "mockdns server: ", log.LstdFlags), false)
44+
defer srv.Close()
45+
46+
srv.PatchNet(net.DefaultResolver)
47+
defer mockdns.UnpatchNet(net.DefaultResolver)
48+
49+
t.Run("test TXT records", func(t *testing.T) {
50+
w := new(TestResponseWriter)
51+
h, err := newHandler(true, map[string]string{
52+
"MY.Host": "host.lima.internal",
53+
})
54+
if err == nil {
55+
for i := 0; i < len(testDomains); i++ {
56+
req := new(dns.Msg)
57+
req.SetQuestion(dns.Fqdn(testDomains[i]), dns.TypeTXT)
58+
h.ServeDNS(w, req)
59+
regex_check := regexp.MustCompile(expectedResults[i]).MatchString(dnsResult.String())
60+
assert.Assert(t, regex_check)
61+
}
62+
}
63+
})
64+
}
65+
66+
type TestResponseWriter struct {
67+
}
68+
69+
// LocalAddr returns the net.Addr of the server
70+
func (r TestResponseWriter) LocalAddr() net.Addr {
71+
return new(TestAddr)
72+
}
73+
74+
// RemoteAddr returns the net.Addr of the client that sent the current request.
75+
func (r TestResponseWriter) RemoteAddr() net.Addr {
76+
return new(TestAddr)
77+
}
78+
79+
// WriteMsg writes a reply back to the client.
80+
func (r TestResponseWriter) WriteMsg(newMsg *dns.Msg) error {
81+
dnsResult = newMsg
82+
return nil
83+
}
84+
85+
// Write writes a raw buffer back to the client.
86+
func (r TestResponseWriter) Write([]byte) (int, error) {
87+
return 0, nil
88+
}
89+
90+
// Close closes the connection.
91+
func (r TestResponseWriter) Close() error {
92+
return nil
93+
}
94+
95+
// TsigStatus returns the status of the Tsig.
96+
func (r TestResponseWriter) TsigStatus() error {
97+
return nil
98+
}
99+
100+
// TsigTimersOnly sets the tsig timers only boolean.
101+
func (r TestResponseWriter) TsigTimersOnly(bool) {
102+
}
103+
104+
// Hijack lets the caller take over the connection.
105+
// After a call to Hijack(), the DNS package will not do anything with the connection.
106+
func (r TestResponseWriter) Hijack() {
107+
}
108+
109+
type TestAddr struct{}
110+
111+
func (r TestAddr) Network() string {
112+
return ""
113+
}
114+
func (r TestAddr) String() string {
115+
return ""
116+
}

0 commit comments

Comments
 (0)