1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
//go:build !windows
package libnetwork
import (
"net"
"testing"
"github.com/docker/docker/internal/testutils/netnsutils"
"github.com/miekg/dns"
)
// test only works on linux
func TestDNSIPQuery(t *testing.T) {
defer netnsutils.SetupTestOSContext(t)()
c, err := New(OptionBoltdbWithRandomDBFile(t))
if err != nil {
t.Fatal(err)
}
defer c.Stop()
n, err := c.NewNetwork("bridge", "dtnet1", "", nil)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := n.Delete(); err != nil {
t.Fatal(err)
}
}()
ep, err := n.CreateEndpoint("testep")
if err != nil {
t.Fatal(err)
}
sb, err := c.NewSandbox("c1")
if err != nil {
t.Fatal(err)
}
defer func() {
if err := sb.Delete(); err != nil {
t.Fatal(err)
}
}()
// we need the endpoint only to populate ep_list for the sandbox as part of resolve_name
// it is not set as a target for name resolution and does not serve any other purpose
err = ep.Join(sb)
if err != nil {
t.Fatal(err)
}
// add service records which are used to resolve names. These are the real targets for the DNS querries
n.addSvcRecords("ep1", "name1", "svc1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
w := new(tstwriter)
// the unit tests right now will focus on non-proxyed DNS requests
r := NewResolver(resolverIPSandbox, false, sb)
// test name1's IP is resolved correctly with the default A type query
// Also make sure DNS lookups are case insensitive
names := []string{"name1.", "NaMe1."}
for _, name := range names {
q := new(dns.Msg)
q.SetQuestion(name, dns.TypeA)
r.serveDNS(w, q)
resp := w.GetResponse()
checkNonNullResponse(t, resp)
t.Log("Response: ", resp.String())
checkDNSResponseCode(t, resp, dns.RcodeSuccess)
checkDNSAnswersCount(t, resp, 1)
checkDNSRRType(t, resp.Answer[0].Header().Rrtype, dns.TypeA)
if answer, ok := resp.Answer[0].(*dns.A); ok {
if !answer.A.Equal(net.ParseIP("192.168.0.1")) {
t.Fatalf("IP response in Answer %v does not match 192.168.0.1", answer.A)
}
} else {
t.Fatal("Answer of type A not found")
}
w.ClearResponse()
}
// test MX query with name1 results in Success response with 0 answer records
q := new(dns.Msg)
q.SetQuestion("name1.", dns.TypeMX)
r.serveDNS(w, q)
resp := w.GetResponse()
checkNonNullResponse(t, resp)
t.Log("Response: ", resp.String())
checkDNSResponseCode(t, resp, dns.RcodeSuccess)
checkDNSAnswersCount(t, resp, 0)
w.ClearResponse()
// test MX query with non existent name results in ServFail response with 0 answer records
// since this is a unit test env, we disable proxying DNS above which results in ServFail rather than NXDOMAIN
q = new(dns.Msg)
q.SetQuestion("nonexistent.", dns.TypeMX)
r.serveDNS(w, q)
resp = w.GetResponse()
checkNonNullResponse(t, resp)
t.Log("Response: ", resp.String())
checkDNSResponseCode(t, resp, dns.RcodeServerFailure)
w.ClearResponse()
}
// test only works on linux
func TestDNSProxyServFail(t *testing.T) {
osctx := netnsutils.SetupTestOSContextEx(t)
defer osctx.Cleanup(t)
c, err := New(OptionBoltdbWithRandomDBFile(t))
if err != nil {
t.Fatal(err)
}
defer c.Stop()
n, err := c.NewNetwork("bridge", "dtnet2", "", nil)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := n.Delete(); err != nil {
t.Fatal(err)
}
}()
sb, err := c.NewSandbox("c1")
if err != nil {
t.Fatal(err)
}
defer func() {
if err := sb.Delete(); err != nil {
t.Fatal(err)
}
}()
var nRequests int
// initialize a local DNS server and configure it to fail the first query
dns.HandleFunc(".", newDNSHandlerServFailOnce(&nRequests))
// use TCP for predictable results. Connection tests (to figure out DNS server initialization) don't work with UDP
server := &dns.Server{Addr: "127.0.0.1:53", Net: "tcp"}
srvErrCh := make(chan error, 1)
osctx.Go(t, func() {
srvErrCh <- server.ListenAndServe()
})
defer func() {
server.Shutdown() //nolint:errcheck
if err := <-srvErrCh; err != nil {
t.Error(err)
}
}()
waitForLocalDNSServer(t)
t.Log("DNS Server can be reached")
w := new(tstwriter)
r := NewResolver(resolverIPSandbox, true, sb)
q := new(dns.Msg)
q.SetQuestion("name1.", dns.TypeA)
var localDNSEntries []extDNSEntry
extTestDNSEntry := extDNSEntry{IPStr: "127.0.0.1", HostLoopback: true}
// configure two external DNS entries and point both to local DNS server thread
localDNSEntries = append(localDNSEntries, extTestDNSEntry)
localDNSEntries = append(localDNSEntries, extTestDNSEntry)
// this should generate two requests: the first will fail leading to a retry
r.SetExtServers(localDNSEntries)
r.serveDNS(w, q)
if nRequests != 2 {
t.Fatalf("Expected 2 DNS querries. Found: %d", nRequests)
}
t.Logf("Expected number of DNS requests generated")
}
|