mirror of
https://github.com/coredns/coredns.git
synced 2025-11-20 19:02:16 -05:00
fix: prevent QUIC reload panic by lazily initializing the listener (#7680)
* fix: prevent QUIC reload panic by lazily initializing the listener ServePacket on reload receives the reused PacketConn before the new ServerQUIC has recreated its quic.Listener, so quicListener is nil and the process panics. Lazily initialise quicListener from the provided PacketConn when it’s nil and then proceed with ServeQUIC. fixes: #7679 Signed-off-by: Nico Berlee <nico.berlee@on2it.net> * test: add regression test for QUIC reload panic Signed-off-by: Nico Berlee <nico.berlee@on2it.net> --------- Signed-off-by: Nico Berlee <nico.berlee@on2it.net>
This commit is contained in:
@@ -23,6 +23,12 @@ var quicCorefile = `quic://.:0 {
|
||||
whoami
|
||||
}`
|
||||
|
||||
var quicReloadCorefile = `quic://.:0 {
|
||||
tls ../plugin/tls/test_cert.pem ../plugin/tls/test_key.pem ../plugin/tls/test_ca.pem
|
||||
whoami
|
||||
reload 2s
|
||||
}`
|
||||
|
||||
// Corefile with custom stream limits
|
||||
var quicLimitCorefile = `quic://.:0 {
|
||||
tls ../plugin/tls/test_cert.pem ../plugin/tls/test_key.pem ../plugin/tls/test_ca.pem
|
||||
@@ -89,6 +95,29 @@ func TestQUIC(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestQUICReloadDoesNotPanic(t *testing.T) {
|
||||
inst, udp, _, err := CoreDNSServerAndPorts(quicCorefile)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get CoreDNS serving instance: %s", err)
|
||||
}
|
||||
t.Cleanup(func() { inst.Stop() })
|
||||
|
||||
assertQUICQuerySucceeds(t, udp)
|
||||
|
||||
restart, err := inst.Restart(NewInput(quicReloadCorefile))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to restart CoreDNS: %s", err)
|
||||
}
|
||||
t.Cleanup(func() { restart.Stop() })
|
||||
|
||||
udpReload, _ := CoreDNSServerPorts(restart, 0)
|
||||
if udpReload == "" {
|
||||
t.Fatal("Failed to determine QUIC listener address after reload")
|
||||
}
|
||||
|
||||
assertQUICQuerySucceeds(t, udpReload)
|
||||
}
|
||||
|
||||
func TestQUICProtocolError(t *testing.T) {
|
||||
q, udp, _, err := CoreDNSServerAndPorts(quicCorefile)
|
||||
if err != nil {
|
||||
@@ -352,3 +381,50 @@ func createInvalidDOQMsg() []byte {
|
||||
msg, _ := m.Pack()
|
||||
return msg
|
||||
}
|
||||
|
||||
func assertQUICQuerySucceeds(t *testing.T, address string) {
|
||||
t.Helper()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
conn, err := quic.DialAddr(ctx, convertAddress(address), generateTLSConfig(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error but got: %s", err)
|
||||
}
|
||||
defer func() { _ = conn.CloseWithError(0, "") }()
|
||||
|
||||
stream, err := conn.OpenStreamSync(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error but got: %s", err)
|
||||
}
|
||||
defer func() { _ = stream.Close() }()
|
||||
|
||||
msg := createTestMsg()
|
||||
if _, err = stream.Write(msg); err != nil {
|
||||
t.Fatalf("Expected no error but got: %s", err)
|
||||
}
|
||||
|
||||
sizeBuf := make([]byte, 2)
|
||||
if _, err = io.ReadFull(stream, sizeBuf); err != nil {
|
||||
t.Fatalf("Expected no error but got: %s", err)
|
||||
}
|
||||
|
||||
size := binary.BigEndian.Uint16(sizeBuf)
|
||||
buf := make([]byte, size)
|
||||
if _, err = io.ReadFull(stream, buf); err != nil {
|
||||
t.Fatalf("Expected no error but got: %s", err)
|
||||
}
|
||||
|
||||
resp := new(dns.Msg)
|
||||
if err = resp.Unpack(buf); err != nil {
|
||||
t.Fatalf("Expected no error but got: %s", err)
|
||||
}
|
||||
|
||||
if resp.Rcode != dns.RcodeSuccess {
|
||||
t.Fatalf("Expected success but got %d", resp.Rcode)
|
||||
}
|
||||
|
||||
if len(resp.Extra) != 2 {
|
||||
t.Fatalf("Expected 2 RRs in additional section, but got %d", len(resp.Extra))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user