diff --git a/.changelog/14811.txt b/.changelog/14811.txt new file mode 100644 index 0000000000..bc1f5547ed --- /dev/null +++ b/.changelog/14811.txt @@ -0,0 +1,3 @@ +```release-note:feature +DNS-proxy support via gRPC request. +``` diff --git a/GNUmakefile b/GNUmakefile index f9dd160811..2ea50833f2 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -14,6 +14,8 @@ PROTOC_GEN_GO_GRPC_VERSION="v1.2.0" MOG_VERSION='v0.3.0' PROTOC_GO_INJECT_TAG_VERSION='v1.3.0' +MOCKED_PB_DIRS= pbdns + GOTAGS ?= GOPATH=$(shell go env GOPATH) GOARCH?=$(shell go env GOARCH) @@ -401,9 +403,20 @@ else endif .PHONY: proto -proto: proto-tools +proto: proto-tools proto-gen proto-mocks + +.PHONY: proto-gen +proto-gen: proto-tools @$(SHELL) $(CURDIR)/build-support/scripts/protobuf.sh +.PHONY: proto-mocks +proto-mocks: + for dir in $(MOCKED_PB_DIRS) ; do \ + cd proto-public && \ + rm -f $$dir/mock*.go && \ + mockery --dir $$dir --inpackage --all --recursive --log-level trace ; \ + done + .PHONY: proto-format proto-format: proto-tools @buf format -w diff --git a/agent/agent.go b/agent/agent.go index e5101ab886..ff41544ef5 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -42,6 +42,7 @@ import ( "github.com/hashicorp/consul/agent/consul/servercert" "github.com/hashicorp/consul/agent/dns" external "github.com/hashicorp/consul/agent/grpc-external" + grpcDNS "github.com/hashicorp/consul/agent/grpc-external/services/dns" "github.com/hashicorp/consul/agent/hcp/scada" libscada "github.com/hashicorp/consul/agent/hcp/scada" "github.com/hashicorp/consul/agent/local" @@ -920,6 +921,15 @@ func (a *Agent) listenAndServeDNS() error { } }(addr) } + s, _ := NewDNSServer(a) + + grpcDNS.NewServer(grpcDNS.Config{ + Logger: a.logger.Named("grpc-api.dns"), + DNSServeMux: s.mux, + LocalAddr: grpcDNS.LocalAddr{IP: net.IPv4(127, 0, 0, 1), Port: a.config.GRPCPort}, + }).Register(a.externalGRPCServer) + + a.dnsServers = append(a.dnsServers, s) // wait for servers to be up timeout := time.After(time.Second) diff --git a/agent/dns.go b/agent/dns.go index b627f1f4a8..d458928e80 100644 --- a/agent/dns.go +++ b/agent/dns.go @@ -5,16 +5,16 @@ import ( "encoding/hex" "errors" "fmt" + "math" "net" "regexp" "strings" "sync/atomic" "time" + "github.com/armon/go-metrics" "github.com/armon/go-metrics/prometheus" - - metrics "github.com/armon/go-metrics" - radix "github.com/armon/go-radix" + "github.com/armon/go-radix" "github.com/coredns/coredns/plugin/pkg/dnsutil" "github.com/hashicorp/go-hclog" "github.com/miekg/dns" @@ -61,6 +61,13 @@ const ( staleCounterThreshold = 5 * time.Second defaultMaxUDPSize = 512 + + // If a consumer sets a buffer size greater than this amount we will default it down + // to this amount to ensure that consul does respond. Previously if consumer had a larger buffer + // size than 65535 - 60 bytes (maximim 60 bytes for IP header. UDP header will be offset in the + // trimUDP call) consul would fail to respond and the consumer timesout + // the request. + maxUDPDatagramSize = math.MaxUint16 - 68 ) type dnsSOAConfig struct { @@ -139,13 +146,13 @@ func NewDNSServer(a *Agent) (*DNSServer, error) { // Make sure domains are FQDN, make them case insensitive for ServeMux domain := dns.Fqdn(strings.ToLower(a.config.DNSDomain)) altDomain := dns.Fqdn(strings.ToLower(a.config.DNSAltDomain)) - srv := &DNSServer{ agent: a, domain: domain, altDomain: altDomain, logger: a.logger.Named(logging.DNS), defaultEnterpriseMeta: *a.AgentEnterpriseMeta(), + mux: dns.NewServeMux(), } cfg, err := GetDNSConfig(a.config) if err != nil { @@ -153,6 +160,19 @@ func NewDNSServer(a *Agent) (*DNSServer, error) { } srv.config.Store(cfg) + srv.mux.HandleFunc("arpa.", srv.handlePtr) + srv.mux.HandleFunc(srv.domain, srv.handleQuery) + // this is not an empty string check because NewDNSServer will have + // converted the configured alt domain into an FQDN which will ensure that + // the value ends with a ".". Therefore "." is the empty string equivalent + // for originally having no alternate domain set. If there is a reason + // why consul should be configured to handle the root zone I have yet + // to think of it. + if srv.altDomain != "." { + srv.mux.HandleFunc(srv.altDomain, srv.handleQuery) + } + srv.toggleRecursorHandlerFromConfig(cfg) + return srv, nil } @@ -227,22 +247,6 @@ func (cfg *dnsConfig) GetTTLForService(service string) (time.Duration, bool) { } func (d *DNSServer) ListenAndServe(network, addr string, notif func()) error { - cfg := d.config.Load().(*dnsConfig) - - d.mux = dns.NewServeMux() - d.mux.HandleFunc("arpa.", d.handlePtr) - d.mux.HandleFunc(d.domain, d.handleQuery) - // this is not an empty string check because NewDNSServer will have - // converted the configured alt domain into an FQDN which will ensure that - // the value ends with a ".". Therefore "." is the empty string equivalent - // for originally having no alternate domain set. If there is a reason - // why consul should be configured to handle the root zone I have yet - // to think of it. - if d.altDomain != "." { - d.mux.HandleFunc(d.altDomain, d.handleQuery) - } - d.toggleRecursorHandlerFromConfig(cfg) - d.Server = &dns.Server{ Addr: addr, Net: network, @@ -1258,6 +1262,11 @@ func trimUDPResponse(req, resp *dns.Msg, udpAnswerLimit int) (trimmed bool) { maxSize = int(size) } } + // Overriding maxSize as the maxSize cannot be larger than the + // maxUDPDatagram size. Reliability guarantees disappear > than this amount. + if maxSize > maxUDPDatagramSize { + maxSize = maxUDPDatagramSize + } // We avoid some function calls and allocations by only handling the // extra data when necessary. @@ -1286,8 +1295,9 @@ func trimUDPResponse(req, resp *dns.Msg, udpAnswerLimit int) (trimmed bool) { // will allow our responses to be compliant even if some downstream server // uncompresses them. // Even when size is too big for one single record, try to send it anyway - // (useful for 512 bytes messages) - for len(resp.Answer) > 1 && resp.Len() > maxSize-7 { + // (useful for 512 bytes messages). 8 is removed from maxSize to ensure that we account + // for the udp header (8 bytes). + for len(resp.Answer) > 1 && resp.Len() > maxSize-8 { // first try to remove the NS section may be it will truncate enough if len(resp.Ns) != 0 { resp.Ns = []dns.RR{} diff --git a/agent/dns_test.go b/agent/dns_test.go index 9f876eaebb..2f2499a2ef 100644 --- a/agent/dns_test.go +++ b/agent/dns_test.go @@ -3,6 +3,7 @@ package agent import ( "errors" "fmt" + "math" "math/rand" "net" "reflect" @@ -7563,6 +7564,55 @@ func TestDNS_trimUDPResponse_TrimSizeEDNS(t *testing.T) { } } +func TestDNS_trimUDPResponse_TrimSizeMaxSize(t *testing.T) { + t.Parallel() + cfg := loadRuntimeConfig(t, `node_name = "test" data_dir = "a" bind_addr = "127.0.0.1" node_name = "dummy"`) + + resp := &dns.Msg{} + + for i := 0; i < 600; i++ { + target := fmt.Sprintf("ip-10-0-1-%d.node.dc1.consul.", 150+i) + srv := &dns.SRV{ + Hdr: dns.RR_Header{ + Name: "redis-cache-redis.service.consul.", + Rrtype: dns.TypeSRV, + Class: dns.ClassINET, + }, + Target: target, + } + a := &dns.A{ + Hdr: dns.RR_Header{ + Name: target, + Rrtype: dns.TypeA, + Class: dns.ClassINET, + }, + A: net.ParseIP(fmt.Sprintf("10.0.1.%d", 150+i)), + } + + resp.Answer = append(resp.Answer, srv) + resp.Extra = append(resp.Extra, a) + } + + reqEDNS, respEDNS := &dns.Msg{}, &dns.Msg{} + reqEDNS.SetEdns0(math.MaxUint16, true) + respEDNS.Answer = append(respEDNS.Answer, resp.Answer...) + respEDNS.Extra = append(respEDNS.Extra, resp.Extra...) + require.Greater(t, respEDNS.Len(), math.MaxUint16) + t.Logf("length is: %v", respEDNS.Len()) + + if trimmed := trimUDPResponse(reqEDNS, respEDNS, cfg.DNSUDPAnswerLimit); !trimmed { + t.Errorf("expected edns to be trimmed: %#v", resp) + } + require.Greater(t, math.MaxUint16, respEDNS.Len()) + + t.Logf("length is: %v", respEDNS.Len()) + + if len(respEDNS.Answer) == 0 || len(respEDNS.Answer) != len(respEDNS.Extra) { + t.Errorf("bad edns answer length: %#v", resp) + } + +} + func TestDNS_syncExtra(t *testing.T) { t.Parallel() resp := &dns.Msg{ diff --git a/agent/grpc-external/services/dns/server.go b/agent/grpc-external/services/dns/server.go new file mode 100644 index 0000000000..50b12f5d91 --- /dev/null +++ b/agent/grpc-external/services/dns/server.go @@ -0,0 +1,138 @@ +package dns + +import ( + "context" + "fmt" + "net" + + "github.com/hashicorp/go-hclog" + "github.com/miekg/dns" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/peer" + "google.golang.org/grpc/status" + + "github.com/hashicorp/consul/proto-public/pbdns" +) + +type LocalAddr struct { + IP net.IP + Port int +} + +type Config struct { + Logger hclog.Logger + DNSServeMux *dns.ServeMux + LocalAddr LocalAddr +} + +type Server struct { + Config +} + +func NewServer(cfg Config) *Server { + return &Server{cfg} +} + +func (s *Server) Register(grpcServer *grpc.Server) { + pbdns.RegisterDNSServiceServer(grpcServer, s) +} + +// BufferResponseWriter writes a DNS response to a byte buffer. +type BufferResponseWriter struct { + responseBuffer []byte + LocalAddress net.Addr + RemoteAddress net.Addr + Logger hclog.Logger +} + +// LocalAddr returns the net.Addr of the server +func (b *BufferResponseWriter) LocalAddr() net.Addr { + return b.LocalAddress +} + +// RemoteAddr returns the net.Addr of the client that sent the current request. +func (b *BufferResponseWriter) RemoteAddr() net.Addr { + return b.RemoteAddress +} + +// WriteMsg writes a reply back to the client. +func (b *BufferResponseWriter) WriteMsg(m *dns.Msg) error { + // Pack message to bytes first. + msgBytes, err := m.Pack() + if err != nil { + b.Logger.Error("error packing message", "err", err) + return err + } + b.responseBuffer = msgBytes + return nil +} + +// Write writes a raw buffer back to the client. +func (b *BufferResponseWriter) Write(m []byte) (int, error) { + b.Logger.Debug("Write was called") + return copy(b.responseBuffer, m), nil +} + +// Close closes the connection. +func (b *BufferResponseWriter) Close() error { + // There's nothing for us to do here as we don't handle the connection. + return nil +} + +// TsigStatus returns the status of the Tsig. +func (b *BufferResponseWriter) TsigStatus() error { + // TSIG doesn't apply to this response writer. + return nil +} + +// TsigTimersOnly sets the tsig timers only boolean. +func (b *BufferResponseWriter) TsigTimersOnly(bool) {} + +// Hijack lets the caller take over the connection. +// After a call to Hijack(), the DNS package will not do anything with the connection. { +func (b *BufferResponseWriter) Hijack() {} + +// Query is a gRPC endpoint that will serve dns requests. It will be consumed primarily by the +// consul dataplane to proxy dns requests to consul. +func (s *Server) Query(ctx context.Context, req *pbdns.QueryRequest) (*pbdns.QueryResponse, error) { + pr, ok := peer.FromContext(ctx) + if !ok { + return nil, fmt.Errorf("error retrieving peer information from context") + } + + var local net.Addr + var remote net.Addr + // We do this so that we switch to udp/tcp when handling the request since it will be proxied + // through consul through gRPC and we need to 'fake' the protocol so that the message is trimmed + // according to wether it is UDP or TCP. + switch req.GetProtocol() { + case pbdns.Protocol_PROTOCOL_TCP: + remote = pr.Addr + local = &net.TCPAddr{IP: s.LocalAddr.IP, Port: s.LocalAddr.Port} + case pbdns.Protocol_PROTOCOL_UDP: + remoteAddr := pr.Addr.(*net.TCPAddr) + remote = &net.UDPAddr{IP: remoteAddr.IP, Port: remoteAddr.Port} + local = &net.UDPAddr{IP: s.LocalAddr.IP, Port: s.LocalAddr.Port} + default: + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("error protocol type not set: %v", req.GetProtocol())) + } + + respWriter := &BufferResponseWriter{ + LocalAddress: local, + RemoteAddress: remote, + Logger: s.Logger, + } + + msg := &dns.Msg{} + err := msg.Unpack(req.Msg) + if err != nil { + s.Logger.Error("error unpacking message", "err", err) + return nil, status.Error(codes.Internal, fmt.Sprintf("failure decoding dns request: %s", err.Error())) + } + s.DNSServeMux.ServeDNS(respWriter, msg) + + queryResponse := &pbdns.QueryResponse{Msg: respWriter.responseBuffer} + + return queryResponse, nil +} diff --git a/agent/grpc-external/services/dns/server_test.go b/agent/grpc-external/services/dns/server_test.go new file mode 100644 index 0000000000..b477fed498 --- /dev/null +++ b/agent/grpc-external/services/dns/server_test.go @@ -0,0 +1,127 @@ +package dns + +import ( + "context" + "errors" + "net" + "testing" + + "github.com/hashicorp/go-hclog" + "github.com/miekg/dns" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + + "github.com/hashicorp/consul/agent/grpc-external/testutils" + "github.com/hashicorp/consul/proto-public/pbdns" +) + +var txtRR = []string{"Hello world"} + +func helloServer(w dns.ResponseWriter, req *dns.Msg) { + m := new(dns.Msg) + m.SetReply(req) + + m.Extra = make([]dns.RR, 1) + m.Extra[0] = &dns.TXT{ + Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}, + Txt: txtRR, + } + w.WriteMsg(m) +} + +func testClient(t *testing.T, server *Server) pbdns.DNSServiceClient { + t.Helper() + + addr := testutils.RunTestServer(t, server) + + conn, err := grpc.DialContext(context.Background(), addr.String(), grpc.WithInsecure()) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, conn.Close()) + }) + + return pbdns.NewDNSServiceClient(conn) +} + +type DNSTestSuite struct { + suite.Suite +} + +func TestDNS_suite(t *testing.T) { + suite.Run(t, new(DNSTestSuite)) +} + +func (s *DNSTestSuite) TestProxy_Success() { + mux := dns.NewServeMux() + mux.Handle(".", dns.HandlerFunc(helloServer)) + server := NewServer(Config{ + Logger: hclog.Default(), + DNSServeMux: mux, + LocalAddr: LocalAddr{ + net.IPv4(127, 0, 0, 1), + 0, + }, + }) + + client := testClient(s.T(), server) + + testCases := map[string]struct { + question string + clientQuery func(qR *pbdns.QueryRequest) + expectedErr error + }{ + + "happy path udp": { + question: "abc.com.", + clientQuery: func(qR *pbdns.QueryRequest) { + qR.Protocol = pbdns.Protocol_PROTOCOL_UDP + }, + }, + "happy path tcp": { + question: "abc.com.", + clientQuery: func(qR *pbdns.QueryRequest) { + qR.Protocol = pbdns.Protocol_PROTOCOL_TCP + }, + }, + "No protocol set": { + question: "abc.com.", + clientQuery: func(qR *pbdns.QueryRequest) {}, + expectedErr: errors.New("error protocol type not set: PROTOCOL_UNSET_UNSPECIFIED"), + }, + "Invalid question": { + question: "notvalid", + clientQuery: func(qR *pbdns.QueryRequest) { + qR.Protocol = pbdns.Protocol_PROTOCOL_UDP + }, + expectedErr: errors.New("failure decoding dns request"), + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + req := dns.Msg{} + req.SetQuestion(tc.question, dns.TypeA) + + bytes, _ := req.Pack() + + clientReq := &pbdns.QueryRequest{Msg: bytes} + tc.clientQuery(clientReq) + clientResp, err := client.Query(context.Background(), clientReq) + if tc.expectedErr != nil { + s.Require().Error(err, "no errror calling gRPC endpoint") + s.Require().ErrorContains(err, tc.expectedErr.Error()) + } else { + s.Require().NoError(err, "error calling gRPC endpoint") + + resp := clientResp.GetMsg() + var dnsResp dns.Msg + + err = dnsResp.Unpack(resp) + s.Require().NoError(err, "error unpacking dns response") + rr := dnsResp.Extra[0].(*dns.TXT) + s.Require().EqualValues(rr.Txt, txtRR) + } + }) + } +} diff --git a/build-support/scripts/devtools.sh b/build-support/scripts/devtools.sh index 7e15215028..18b07d8b9e 100755 --- a/build-support/scripts/devtools.sh +++ b/build-support/scripts/devtools.sh @@ -60,7 +60,9 @@ function proto_tools_install { local buf_version local mog_version local protoc_go_inject_tag_version + local mockery_version + mockery_version="$(make --no-print-directory print-MOCKERY_VERSION)" protoc_gen_go_version="$(grep github.com/golang/protobuf go.mod | awk '{print $2}')" protoc_gen_go_grpc_version="$(make --no-print-directory print-PROTOC_GEN_GO_GRPC_VERSION)" mog_version="$(make --no-print-directory print-MOG_VERSION)" @@ -71,6 +73,12 @@ function proto_tools_install { # echo "mog: ${mog_version}" # echo "tag: ${protoc_go_inject_tag_version}" + install_versioned_tool \ + 'mockery' \ + 'github.com/vektra/mockery/v2' \ + "${mockery_version}" \ + 'github.com/vektra/mockery/v2' + install_versioned_tool \ 'buf' \ 'github.com/bufbuild/buf' \ @@ -128,15 +136,6 @@ function lint_install { } function tools_install { - local mockery_version - - mockery_version="$(make --no-print-directory print-MOCKERY_VERSION)" - - install_versioned_tool \ - 'mockery' \ - 'github.com/vektra/mockery/v2' \ - "${mockery_version}" \ - 'github.com/vektra/mockery/v2' lint_install proto_tools_install diff --git a/proto-public/pbdns/dns.pb.binary.go b/proto-public/pbdns/dns.pb.binary.go new file mode 100644 index 0000000000..486c4ff887 --- /dev/null +++ b/proto-public/pbdns/dns.pb.binary.go @@ -0,0 +1,28 @@ +// Code generated by protoc-gen-go-binary. DO NOT EDIT. +// source: proto-public/pbdns/dns.proto + +package pbdns + +import ( + "github.com/golang/protobuf/proto" +) + +// MarshalBinary implements encoding.BinaryMarshaler +func (msg *QueryRequest) MarshalBinary() ([]byte, error) { + return proto.Marshal(msg) +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler +func (msg *QueryRequest) UnmarshalBinary(b []byte) error { + return proto.Unmarshal(b, msg) +} + +// MarshalBinary implements encoding.BinaryMarshaler +func (msg *QueryResponse) MarshalBinary() ([]byte, error) { + return proto.Marshal(msg) +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler +func (msg *QueryResponse) UnmarshalBinary(b []byte) error { + return proto.Unmarshal(b, msg) +} diff --git a/proto-public/pbdns/dns.pb.go b/proto-public/pbdns/dns.pb.go new file mode 100644 index 0000000000..cf136c2bd3 --- /dev/null +++ b/proto-public/pbdns/dns.pb.go @@ -0,0 +1,298 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0-rc.1 +// protoc (unknown) +// source: proto-public/pbdns/dns.proto + +package pbdns + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Protocol int32 + +const ( + Protocol_PROTOCOL_UNSET_UNSPECIFIED Protocol = 0 + Protocol_PROTOCOL_TCP Protocol = 1 + Protocol_PROTOCOL_UDP Protocol = 2 +) + +// Enum value maps for Protocol. +var ( + Protocol_name = map[int32]string{ + 0: "PROTOCOL_UNSET_UNSPECIFIED", + 1: "PROTOCOL_TCP", + 2: "PROTOCOL_UDP", + } + Protocol_value = map[string]int32{ + "PROTOCOL_UNSET_UNSPECIFIED": 0, + "PROTOCOL_TCP": 1, + "PROTOCOL_UDP": 2, + } +) + +func (x Protocol) Enum() *Protocol { + p := new(Protocol) + *p = x + return p +} + +func (x Protocol) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Protocol) Descriptor() protoreflect.EnumDescriptor { + return file_proto_public_pbdns_dns_proto_enumTypes[0].Descriptor() +} + +func (Protocol) Type() protoreflect.EnumType { + return &file_proto_public_pbdns_dns_proto_enumTypes[0] +} + +func (x Protocol) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Protocol.Descriptor instead. +func (Protocol) EnumDescriptor() ([]byte, []int) { + return file_proto_public_pbdns_dns_proto_rawDescGZIP(), []int{0} +} + +type QueryRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // msg is the DNS request message. + Msg []byte `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` + // protocol is the protocol of the request + Protocol Protocol `protobuf:"varint,2,opt,name=protocol,proto3,enum=hashicorp.consul.dns.Protocol" json:"protocol,omitempty"` +} + +func (x *QueryRequest) Reset() { + *x = QueryRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_public_pbdns_dns_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryRequest) ProtoMessage() {} + +func (x *QueryRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_public_pbdns_dns_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryRequest.ProtoReflect.Descriptor instead. +func (*QueryRequest) Descriptor() ([]byte, []int) { + return file_proto_public_pbdns_dns_proto_rawDescGZIP(), []int{0} +} + +func (x *QueryRequest) GetMsg() []byte { + if x != nil { + return x.Msg + } + return nil +} + +func (x *QueryRequest) GetProtocol() Protocol { + if x != nil { + return x.Protocol + } + return Protocol_PROTOCOL_UNSET_UNSPECIFIED +} + +type QueryResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // msg is the DNS reply message. + Msg []byte `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` +} + +func (x *QueryResponse) Reset() { + *x = QueryResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_public_pbdns_dns_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryResponse) ProtoMessage() {} + +func (x *QueryResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_public_pbdns_dns_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryResponse.ProtoReflect.Descriptor instead. +func (*QueryResponse) Descriptor() ([]byte, []int) { + return file_proto_public_pbdns_dns_proto_rawDescGZIP(), []int{1} +} + +func (x *QueryResponse) GetMsg() []byte { + if x != nil { + return x.Msg + } + return nil +} + +var File_proto_public_pbdns_dns_proto protoreflect.FileDescriptor + +var file_proto_public_pbdns_dns_proto_rawDesc = []byte{ + 0x0a, 0x1c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, + 0x62, 0x64, 0x6e, 0x73, 0x2f, 0x64, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x2e, 0x64, 0x6e, 0x73, 0x22, 0x5c, 0x0a, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x3a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x64, 0x6e, 0x73, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x22, 0x21, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x03, 0x6d, 0x73, 0x67, 0x2a, 0x4e, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x12, 0x1e, 0x0a, 0x1a, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, 0x55, 0x4e, + 0x53, 0x45, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, 0x54, 0x43, + 0x50, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, + 0x55, 0x44, 0x50, 0x10, 0x02, 0x32, 0x60, 0x0a, 0x0a, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x12, 0x52, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, + 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x23, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, + 0x73, 0x75, 0x6c, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xc6, 0x01, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x2e, 0x64, 0x6e, 0x73, 0x42, 0x08, 0x44, 0x6e, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x64, 0x6e, 0x73, + 0xa2, 0x02, 0x03, 0x48, 0x43, 0x44, 0xaa, 0x02, 0x14, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x44, 0x6e, 0x73, 0xca, 0x02, 0x14, + 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x5c, 0x44, 0x6e, 0x73, 0xe2, 0x02, 0x20, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x44, 0x6e, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x16, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x44, 0x6e, 0x73, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_public_pbdns_dns_proto_rawDescOnce sync.Once + file_proto_public_pbdns_dns_proto_rawDescData = file_proto_public_pbdns_dns_proto_rawDesc +) + +func file_proto_public_pbdns_dns_proto_rawDescGZIP() []byte { + file_proto_public_pbdns_dns_proto_rawDescOnce.Do(func() { + file_proto_public_pbdns_dns_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_public_pbdns_dns_proto_rawDescData) + }) + return file_proto_public_pbdns_dns_proto_rawDescData +} + +var file_proto_public_pbdns_dns_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_proto_public_pbdns_dns_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_proto_public_pbdns_dns_proto_goTypes = []interface{}{ + (Protocol)(0), // 0: hashicorp.consul.dns.Protocol + (*QueryRequest)(nil), // 1: hashicorp.consul.dns.QueryRequest + (*QueryResponse)(nil), // 2: hashicorp.consul.dns.QueryResponse +} +var file_proto_public_pbdns_dns_proto_depIdxs = []int32{ + 0, // 0: hashicorp.consul.dns.QueryRequest.protocol:type_name -> hashicorp.consul.dns.Protocol + 1, // 1: hashicorp.consul.dns.DNSService.Query:input_type -> hashicorp.consul.dns.QueryRequest + 2, // 2: hashicorp.consul.dns.DNSService.Query:output_type -> hashicorp.consul.dns.QueryResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_proto_public_pbdns_dns_proto_init() } +func file_proto_public_pbdns_dns_proto_init() { + if File_proto_public_pbdns_dns_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_public_pbdns_dns_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_public_pbdns_dns_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_public_pbdns_dns_proto_rawDesc, + NumEnums: 1, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_public_pbdns_dns_proto_goTypes, + DependencyIndexes: file_proto_public_pbdns_dns_proto_depIdxs, + EnumInfos: file_proto_public_pbdns_dns_proto_enumTypes, + MessageInfos: file_proto_public_pbdns_dns_proto_msgTypes, + }.Build() + File_proto_public_pbdns_dns_proto = out.File + file_proto_public_pbdns_dns_proto_rawDesc = nil + file_proto_public_pbdns_dns_proto_goTypes = nil + file_proto_public_pbdns_dns_proto_depIdxs = nil +} diff --git a/proto-public/pbdns/dns.proto b/proto-public/pbdns/dns.proto new file mode 100644 index 0000000000..bcf3e8d06f --- /dev/null +++ b/proto-public/pbdns/dns.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +package hashicorp.consul.dns; + +option go_package = "github.com/hashicorp/consul/proto-public/pbdns"; + +service DNSService { + // Query sends a DNS request over to Consul server and returns a DNS reply message. + rpc Query(QueryRequest) returns (QueryResponse) {} +} + +enum Protocol { + PROTOCOL_UNSET_UNSPECIFIED = 0; + PROTOCOL_TCP = 1; + PROTOCOL_UDP = 2; +} + +message QueryRequest { + // msg is the DNS request message. + bytes msg = 1; + // protocol is the protocol of the request + Protocol protocol = 2; +} + +message QueryResponse { + // msg is the DNS reply message. + bytes msg = 1; +} diff --git a/proto-public/pbdns/dns_grpc.pb.go b/proto-public/pbdns/dns_grpc.pb.go new file mode 100644 index 0000000000..85ecc8d48d --- /dev/null +++ b/proto-public/pbdns/dns_grpc.pb.go @@ -0,0 +1,105 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc (unknown) +// source: proto-public/pbdns/dns.proto + +package pbdns + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// DNSServiceClient is the client API for DNSService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type DNSServiceClient interface { + // Query sends a DNS request over to Consul server and returns a DNS reply message. + Query(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) +} + +type dNSServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewDNSServiceClient(cc grpc.ClientConnInterface) DNSServiceClient { + return &dNSServiceClient{cc} +} + +func (c *dNSServiceClient) Query(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) { + out := new(QueryResponse) + err := c.cc.Invoke(ctx, "/hashicorp.consul.dns.DNSService/Query", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// DNSServiceServer is the server API for DNSService service. +// All implementations should embed UnimplementedDNSServiceServer +// for forward compatibility +type DNSServiceServer interface { + // Query sends a DNS request over to Consul server and returns a DNS reply message. + Query(context.Context, *QueryRequest) (*QueryResponse, error) +} + +// UnimplementedDNSServiceServer should be embedded to have forward compatible implementations. +type UnimplementedDNSServiceServer struct { +} + +func (UnimplementedDNSServiceServer) Query(context.Context, *QueryRequest) (*QueryResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Query not implemented") +} + +// UnsafeDNSServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to DNSServiceServer will +// result in compilation errors. +type UnsafeDNSServiceServer interface { + mustEmbedUnimplementedDNSServiceServer() +} + +func RegisterDNSServiceServer(s grpc.ServiceRegistrar, srv DNSServiceServer) { + s.RegisterService(&DNSService_ServiceDesc, srv) +} + +func _DNSService_Query_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DNSServiceServer).Query(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/hashicorp.consul.dns.DNSService/Query", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DNSServiceServer).Query(ctx, req.(*QueryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// DNSService_ServiceDesc is the grpc.ServiceDesc for DNSService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var DNSService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "hashicorp.consul.dns.DNSService", + HandlerType: (*DNSServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Query", + Handler: _DNSService_Query_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto-public/pbdns/dns.proto", +} diff --git a/proto-public/pbdns/mock_DNSServiceClient.go b/proto-public/pbdns/mock_DNSServiceClient.go new file mode 100644 index 0000000000..a11f1e963e --- /dev/null +++ b/proto-public/pbdns/mock_DNSServiceClient.go @@ -0,0 +1,58 @@ +// Code generated by mockery v2.12.2. DO NOT EDIT. + +package pbdns + +import ( + context "context" + + grpc "google.golang.org/grpc" + + mock "github.com/stretchr/testify/mock" + + testing "testing" +) + +// MockDNSServiceClient is an autogenerated mock type for the DNSServiceClient type +type MockDNSServiceClient struct { + mock.Mock +} + +// Query provides a mock function with given fields: ctx, in, opts +func (_m *MockDNSServiceClient) Query(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *QueryResponse + if rf, ok := ret.Get(0).(func(context.Context, *QueryRequest, ...grpc.CallOption) *QueryResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*QueryResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *QueryRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewMockDNSServiceClient creates a new instance of MockDNSServiceClient. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockDNSServiceClient(t testing.TB) *MockDNSServiceClient { + mock := &MockDNSServiceClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/proto-public/pbdns/mock_DNSServiceServer.go b/proto-public/pbdns/mock_DNSServiceServer.go new file mode 100644 index 0000000000..97b98dddbd --- /dev/null +++ b/proto-public/pbdns/mock_DNSServiceServer.go @@ -0,0 +1,48 @@ +// Code generated by mockery v2.12.2. DO NOT EDIT. + +package pbdns + +import ( + context "context" + testing "testing" + + mock "github.com/stretchr/testify/mock" +) + +// MockDNSServiceServer is an autogenerated mock type for the DNSServiceServer type +type MockDNSServiceServer struct { + mock.Mock +} + +// Query provides a mock function with given fields: _a0, _a1 +func (_m *MockDNSServiceServer) Query(_a0 context.Context, _a1 *QueryRequest) (*QueryResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *QueryResponse + if rf, ok := ret.Get(0).(func(context.Context, *QueryRequest) *QueryResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*QueryResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *QueryRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewMockDNSServiceServer creates a new instance of MockDNSServiceServer. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockDNSServiceServer(t testing.TB) *MockDNSServiceServer { + mock := &MockDNSServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/proto-public/pbdns/mock_UnsafeDNSServiceServer.go b/proto-public/pbdns/mock_UnsafeDNSServiceServer.go new file mode 100644 index 0000000000..a56e55bcb6 --- /dev/null +++ b/proto-public/pbdns/mock_UnsafeDNSServiceServer.go @@ -0,0 +1,29 @@ +// Code generated by mockery v2.12.2. DO NOT EDIT. + +package pbdns + +import ( + testing "testing" + + mock "github.com/stretchr/testify/mock" +) + +// MockUnsafeDNSServiceServer is an autogenerated mock type for the UnsafeDNSServiceServer type +type MockUnsafeDNSServiceServer struct { + mock.Mock +} + +// mustEmbedUnimplementedDNSServiceServer provides a mock function with given fields: +func (_m *MockUnsafeDNSServiceServer) mustEmbedUnimplementedDNSServiceServer() { + _m.Called() +} + +// NewMockUnsafeDNSServiceServer creates a new instance of MockUnsafeDNSServiceServer. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockUnsafeDNSServiceServer(t testing.TB) *MockUnsafeDNSServiceServer { + mock := &MockUnsafeDNSServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +}