mirror of
https://github.com/coredns/coredns.git
synced 2025-11-01 18:53:43 -04:00
fix(grpc): enforce DNS message size limits (#7490)
Add DNS wire size validation for requests/replies. Limit gRPC recv/send via default call options, accounting necessary framing/protobuf overhead. An error is returned for oversized messages. Add test. Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
This commit is contained in:
@@ -3,6 +3,8 @@ package grpc
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -16,6 +18,20 @@ import (
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const (
|
||||
// maxDNSMessageBytes is the maximum size of a DNS message on the wire.
|
||||
maxDNSMessageBytes = dns.MaxMsgSize
|
||||
|
||||
// maxProtobufPayloadBytes accounts for protobuf overhead.
|
||||
// Field tag=1 (1 byte) + length varint for 65535 (3 bytes) = 4 bytes total
|
||||
maxProtobufPayloadBytes = maxDNSMessageBytes + 4
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrDNSMessageTooLarge is returned when a DNS message exceeds the maximum allowed size.
|
||||
ErrDNSMessageTooLarge = errors.New("dns message exceeds size limit")
|
||||
)
|
||||
|
||||
// Proxy defines an upstream host.
|
||||
type Proxy struct {
|
||||
addr string
|
||||
@@ -37,6 +53,15 @@ func newProxy(addr string, tlsConfig *tls.Config) (*Proxy, error) {
|
||||
p.dialOpts = append(p.dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
}
|
||||
|
||||
// Cap send/recv sizes to avoid oversized messages.
|
||||
// Note: gRPC size limits apply to the serialized protobuf message size.
|
||||
p.dialOpts = append(p.dialOpts,
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallRecvMsgSize(maxProtobufPayloadBytes),
|
||||
grpc.MaxCallSendMsgSize(maxProtobufPayloadBytes),
|
||||
),
|
||||
)
|
||||
|
||||
conn, err := grpc.NewClient(p.addr, p.dialOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -55,6 +80,10 @@ func (p *Proxy) query(ctx context.Context, req *dns.Msg) (*dns.Msg, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := validateDNSSize(msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply, err := p.client.Query(ctx, &pb.DnsPacket{Msg: msg})
|
||||
if err != nil {
|
||||
// if not found message, return empty message with NXDomain code
|
||||
@@ -64,8 +93,14 @@ func (p *Proxy) query(ctx context.Context, req *dns.Msg) (*dns.Msg, error) {
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
wire := reply.GetMsg()
|
||||
|
||||
if err := validateDNSSize(wire); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := new(dns.Msg)
|
||||
if err := ret.Unpack(reply.GetMsg()); err != nil {
|
||||
if err := ret.Unpack(wire); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -80,3 +115,11 @@ func (p *Proxy) query(ctx context.Context, req *dns.Msg) (*dns.Msg, error) {
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func validateDNSSize(data []byte) error {
|
||||
l := len(data)
|
||||
if l > maxDNSMessageBytes {
|
||||
return fmt.Errorf("%w: %d bytes (limit %d)", ErrDNSMessageTooLarge, l, maxDNSMessageBytes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user