|
@@ -1,6 +1,8 @@
|
|
|
package libnetwork
|
|
|
|
|
|
import (
|
|
|
+ "encoding/hex"
|
|
|
+ "errors"
|
|
|
"net"
|
|
|
"runtime"
|
|
|
"syscall"
|
|
@@ -10,6 +12,7 @@ import (
|
|
|
"github.com/docker/docker/libnetwork/testutils"
|
|
|
"github.com/miekg/dns"
|
|
|
"github.com/sirupsen/logrus"
|
|
|
+ "gotest.tools/v3/assert"
|
|
|
"gotest.tools/v3/skip"
|
|
|
)
|
|
|
|
|
@@ -23,7 +26,8 @@ func (a *tstaddr) String() string { return "127.0.0.1" }
|
|
|
|
|
|
// a simple writer that implements dns.ResponseWriter for unit testing purposes
|
|
|
type tstwriter struct {
|
|
|
- msg *dns.Msg
|
|
|
+ localAddr net.Addr
|
|
|
+ msg *dns.Msg
|
|
|
}
|
|
|
|
|
|
func (w *tstwriter) WriteMsg(m *dns.Msg) (err error) {
|
|
@@ -33,7 +37,12 @@ func (w *tstwriter) WriteMsg(m *dns.Msg) (err error) {
|
|
|
|
|
|
func (w *tstwriter) Write(m []byte) (int, error) { return 0, nil }
|
|
|
|
|
|
-func (w *tstwriter) LocalAddr() net.Addr { return new(tstaddr) }
|
|
|
+func (w *tstwriter) LocalAddr() net.Addr {
|
|
|
+ if w.localAddr != nil {
|
|
|
+ return w.localAddr
|
|
|
+ }
|
|
|
+ return new(tstaddr)
|
|
|
+}
|
|
|
|
|
|
func (w *tstwriter) RemoteAddr() net.Addr { return new(tstaddr) }
|
|
|
|
|
@@ -285,3 +294,117 @@ func TestDNSProxyServFail(t *testing.T) {
|
|
|
}
|
|
|
t.Logf("Expected number of DNS requests generated")
|
|
|
}
|
|
|
+
|
|
|
+// Packet 24 extracted from
|
|
|
+// https://gist.github.com/vojtad/3bac63b8c91b1ec50e8d8b36047317fa/raw/7d75eb3d3448381bf252ae55ea5123a132c46658/host.pcap
|
|
|
+// (https://github.com/moby/moby/issues/44575)
|
|
|
+// which is a non-compliant DNS reply > 512B (w/o EDNS(0)) to the query
|
|
|
+//
|
|
|
+// s3.amazonaws.com. IN A
|
|
|
+const oversizedDNSReplyMsg = "\xf5\x11\x81\x80\x00\x01\x00\x20\x00\x00\x00\x00\x02\x73\x33\x09" +
|
|
|
+ "\x61\x6d\x61\x7a\x6f\x6e\x61\x77\x73\x03\x63\x6f\x6d\x00\x00\x01" +
|
|
|
+ "\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
|
|
|
+ "\x11\x9e\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
|
|
|
+ "\x4c\x66\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
|
|
|
+ "\xda\x10\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
|
|
|
+ "\x01\x3e\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
|
|
|
+ "\x88\x68\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
|
|
|
+ "\x66\x9e\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
|
|
|
+ "\x5f\x28\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
|
|
|
+ "\x8e\x4e\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x36\xe7" +
|
|
|
+ "\x84\xf0\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x34\xd8" +
|
|
|
+ "\x92\x45\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
|
|
|
+ "\x8f\xa6\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x36\xe7" +
|
|
|
+ "\xc0\xd0\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
|
|
|
+ "\xfe\x28\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
|
|
|
+ "\xaa\x3d\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
|
|
|
+ "\x4e\x56\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
|
|
|
+ "\xea\xb0\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
|
|
|
+ "\x6d\xed\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
|
|
|
+ "\x28\x00\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x34\xd9" +
|
|
|
+ "\xe9\x78\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x34\xd9" +
|
|
|
+ "\x6e\x9e\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x34\xd9" +
|
|
|
+ "\x45\x86\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x34\xd8" +
|
|
|
+ "\x30\x38\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x36\xe7" +
|
|
|
+ "\xc6\xa8\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x03\x05" +
|
|
|
+ "\x01\x9d\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd9" +
|
|
|
+ "\xa8\xe8\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd9" +
|
|
|
+ "\x64\xa6\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd8" +
|
|
|
+ "\x3c\x48\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd8" +
|
|
|
+ "\x35\x20\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd9" +
|
|
|
+ "\x54\xf6\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd9" +
|
|
|
+ "\x5d\x36\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd9" +
|
|
|
+ "\x30\x36\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x36\xe7" +
|
|
|
+ "\x83\x90"
|
|
|
+
|
|
|
+// Regression test for https://github.com/moby/moby/issues/44575
|
|
|
+func TestOversizedDNSReply(t *testing.T) {
|
|
|
+ srv, err := net.ListenPacket("udp", "127.0.0.1:0")
|
|
|
+ assert.NilError(t, err)
|
|
|
+ defer srv.Close()
|
|
|
+ go func() {
|
|
|
+ buf := make([]byte, 65536)
|
|
|
+ for {
|
|
|
+ n, src, err := srv.ReadFrom(buf)
|
|
|
+ if errors.Is(err, net.ErrClosed) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ t.Logf("[<-%v]\n%s", src, hex.Dump(buf[:n]))
|
|
|
+ if n < 2 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ resp := []byte(oversizedDNSReplyMsg)
|
|
|
+ resp[0], resp[1] = buf[0], buf[1] // Copy query ID into response.
|
|
|
+ _, err = srv.WriteTo(resp, src)
|
|
|
+ if errors.Is(err, net.ErrClosed) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ t.Log(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
+ srvAddr := srv.LocalAddr().(*net.UDPAddr)
|
|
|
+ rsv := NewResolver("", true, noopDNSBackend{}).(*resolver)
|
|
|
+ rsv.SetExtServers([]extDNSEntry{
|
|
|
+ {IPStr: srvAddr.IP.String(), port: uint16(srvAddr.Port), HostLoopback: true},
|
|
|
+ })
|
|
|
+
|
|
|
+ // The resolver logs lots of valuable info at level debug. Redirect it
|
|
|
+ // to t.Log() so the log spew is emitted only if the test fails.
|
|
|
+ oldLevel, oldOut := logrus.StandardLogger().Level, logrus.StandardLogger().Out
|
|
|
+ defer func() {
|
|
|
+ logrus.StandardLogger().SetLevel(oldLevel)
|
|
|
+ logrus.StandardLogger().SetOutput(oldOut)
|
|
|
+ }()
|
|
|
+ logrus.StandardLogger().SetLevel(logrus.DebugLevel)
|
|
|
+ logrus.SetOutput(tlogWriter{t})
|
|
|
+
|
|
|
+ w := &tstwriter{localAddr: srv.LocalAddr()}
|
|
|
+ q := new(dns.Msg).SetQuestion("s3.amazonaws.com.", dns.TypeA)
|
|
|
+ rsv.ServeDNS(w, q)
|
|
|
+ resp := w.GetResponse()
|
|
|
+ checkNonNullResponse(t, resp)
|
|
|
+ t.Log("Response: ", resp.String())
|
|
|
+ checkDNSResponseCode(t, resp, dns.RcodeSuccess)
|
|
|
+ assert.Assert(t, len(resp.Answer) >= 1)
|
|
|
+ checkDNSRRType(t, resp.Answer[0].Header().Rrtype, dns.TypeA)
|
|
|
+}
|
|
|
+
|
|
|
+type tlogWriter struct{ t *testing.T }
|
|
|
+
|
|
|
+func (w tlogWriter) Write(p []byte) (n int, err error) {
|
|
|
+ w.t.Logf("%s", p)
|
|
|
+ return len(p), nil
|
|
|
+}
|
|
|
+
|
|
|
+type noopDNSBackend struct{ DNSBackend }
|
|
|
+
|
|
|
+func (noopDNSBackend) ResolveName(name string, iplen int) ([]net.IP, bool) { return nil, false }
|
|
|
+
|
|
|
+func (noopDNSBackend) ExecFunc(f func()) error { f(); return nil }
|
|
|
+
|
|
|
+func (noopDNSBackend) NdotsSet() bool { return false }
|
|
|
+
|
|
|
+func (noopDNSBackend) HandleQueryResp(name string, ip net.IP) {}
|