123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- /*
- *
- * Copyright 2017 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
- package naming
- import (
- "context"
- "errors"
- "fmt"
- "net"
- "strconv"
- "time"
- "google.golang.org/grpc/grpclog"
- )
- const (
- defaultPort = "443"
- defaultFreq = time.Minute * 30
- )
- var (
- errMissingAddr = errors.New("missing address")
- errWatcherClose = errors.New("watcher has been closed")
- lookupHost = net.DefaultResolver.LookupHost
- lookupSRV = net.DefaultResolver.LookupSRV
- )
- // NewDNSResolverWithFreq creates a DNS Resolver that can resolve DNS names, and
- // create watchers that poll the DNS server using the frequency set by freq.
- func NewDNSResolverWithFreq(freq time.Duration) (Resolver, error) {
- return &dnsResolver{freq: freq}, nil
- }
- // NewDNSResolver creates a DNS Resolver that can resolve DNS names, and create
- // watchers that poll the DNS server using the default frequency defined by defaultFreq.
- func NewDNSResolver() (Resolver, error) {
- return NewDNSResolverWithFreq(defaultFreq)
- }
- // dnsResolver handles name resolution for names following the DNS scheme
- type dnsResolver struct {
- // frequency of polling the DNS server that the watchers created by this resolver will use.
- freq time.Duration
- }
- // formatIP returns ok = false if addr is not a valid textual representation of an IP address.
- // If addr is an IPv4 address, return the addr and ok = true.
- // If addr is an IPv6 address, return the addr enclosed in square brackets and ok = true.
- func formatIP(addr string) (addrIP string, ok bool) {
- ip := net.ParseIP(addr)
- if ip == nil {
- return "", false
- }
- if ip.To4() != nil {
- return addr, true
- }
- return "[" + addr + "]", true
- }
- // parseTarget takes the user input target string, returns formatted host and port info.
- // If target doesn't specify a port, set the port to be the defaultPort.
- // If target is in IPv6 format and host-name is enclosed in sqarue brackets, brackets
- // are strippd when setting the host.
- // examples:
- // target: "www.google.com" returns host: "www.google.com", port: "443"
- // target: "ipv4-host:80" returns host: "ipv4-host", port: "80"
- // target: "[ipv6-host]" returns host: "ipv6-host", port: "443"
- // target: ":80" returns host: "localhost", port: "80"
- // target: ":" returns host: "localhost", port: "443"
- func parseTarget(target string) (host, port string, err error) {
- if target == "" {
- return "", "", errMissingAddr
- }
- if ip := net.ParseIP(target); ip != nil {
- // target is an IPv4 or IPv6(without brackets) address
- return target, defaultPort, nil
- }
- if host, port, err := net.SplitHostPort(target); err == nil {
- // target has port, i.e ipv4-host:port, [ipv6-host]:port, host-name:port
- if host == "" {
- // Keep consistent with net.Dial(): If the host is empty, as in ":80", the local system is assumed.
- host = "localhost"
- }
- if port == "" {
- // If the port field is empty(target ends with colon), e.g. "[::1]:", defaultPort is used.
- port = defaultPort
- }
- return host, port, nil
- }
- if host, port, err := net.SplitHostPort(target + ":" + defaultPort); err == nil {
- // target doesn't have port
- return host, port, nil
- }
- return "", "", fmt.Errorf("invalid target address %v", target)
- }
- // Resolve creates a watcher that watches the name resolution of the target.
- func (r *dnsResolver) Resolve(target string) (Watcher, error) {
- host, port, err := parseTarget(target)
- if err != nil {
- return nil, err
- }
- if net.ParseIP(host) != nil {
- ipWatcher := &ipWatcher{
- updateChan: make(chan *Update, 1),
- }
- host, _ = formatIP(host)
- ipWatcher.updateChan <- &Update{Op: Add, Addr: host + ":" + port}
- return ipWatcher, nil
- }
- ctx, cancel := context.WithCancel(context.Background())
- return &dnsWatcher{
- r: r,
- host: host,
- port: port,
- ctx: ctx,
- cancel: cancel,
- t: time.NewTimer(0),
- }, nil
- }
- // dnsWatcher watches for the name resolution update for a specific target
- type dnsWatcher struct {
- r *dnsResolver
- host string
- port string
- // The latest resolved address set
- curAddrs map[string]*Update
- ctx context.Context
- cancel context.CancelFunc
- t *time.Timer
- }
- // ipWatcher watches for the name resolution update for an IP address.
- type ipWatcher struct {
- updateChan chan *Update
- }
- // Next returns the address resolution Update for the target. For IP address,
- // the resolution is itself, thus polling name server is unnecessary. Therefore,
- // Next() will return an Update the first time it is called, and will be blocked
- // for all following calls as no Update exists until watcher is closed.
- func (i *ipWatcher) Next() ([]*Update, error) {
- u, ok := <-i.updateChan
- if !ok {
- return nil, errWatcherClose
- }
- return []*Update{u}, nil
- }
- // Close closes the ipWatcher.
- func (i *ipWatcher) Close() {
- close(i.updateChan)
- }
- // AddressType indicates the address type returned by name resolution.
- type AddressType uint8
- const (
- // Backend indicates the server is a backend server.
- Backend AddressType = iota
- // GRPCLB indicates the server is a grpclb load balancer.
- GRPCLB
- )
- // AddrMetadataGRPCLB contains the information the name resolver for grpclb should provide. The
- // name resolver used by the grpclb balancer is required to provide this type of metadata in
- // its address updates.
- type AddrMetadataGRPCLB struct {
- // AddrType is the type of server (grpc load balancer or backend).
- AddrType AddressType
- // ServerName is the name of the grpc load balancer. Used for authentication.
- ServerName string
- }
- // compileUpdate compares the old resolved addresses and newly resolved addresses,
- // and generates an update list
- func (w *dnsWatcher) compileUpdate(newAddrs map[string]*Update) []*Update {
- var res []*Update
- for a, u := range w.curAddrs {
- if _, ok := newAddrs[a]; !ok {
- u.Op = Delete
- res = append(res, u)
- }
- }
- for a, u := range newAddrs {
- if _, ok := w.curAddrs[a]; !ok {
- res = append(res, u)
- }
- }
- return res
- }
- func (w *dnsWatcher) lookupSRV() map[string]*Update {
- newAddrs := make(map[string]*Update)
- _, srvs, err := lookupSRV(w.ctx, "grpclb", "tcp", w.host)
- if err != nil {
- grpclog.Infof("grpc: failed dns SRV record lookup due to %v.\n", err)
- return nil
- }
- for _, s := range srvs {
- lbAddrs, err := lookupHost(w.ctx, s.Target)
- if err != nil {
- grpclog.Warningf("grpc: failed load banlacer address dns lookup due to %v.\n", err)
- continue
- }
- for _, a := range lbAddrs {
- a, ok := formatIP(a)
- if !ok {
- grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
- continue
- }
- addr := a + ":" + strconv.Itoa(int(s.Port))
- newAddrs[addr] = &Update{Addr: addr,
- Metadata: AddrMetadataGRPCLB{AddrType: GRPCLB, ServerName: s.Target}}
- }
- }
- return newAddrs
- }
- func (w *dnsWatcher) lookupHost() map[string]*Update {
- newAddrs := make(map[string]*Update)
- addrs, err := lookupHost(w.ctx, w.host)
- if err != nil {
- grpclog.Warningf("grpc: failed dns A record lookup due to %v.\n", err)
- return nil
- }
- for _, a := range addrs {
- a, ok := formatIP(a)
- if !ok {
- grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
- continue
- }
- addr := a + ":" + w.port
- newAddrs[addr] = &Update{Addr: addr}
- }
- return newAddrs
- }
- func (w *dnsWatcher) lookup() []*Update {
- newAddrs := w.lookupSRV()
- if newAddrs == nil {
- // If failed to get any balancer address (either no corresponding SRV for the
- // target, or caused by failure during resolution/parsing of the balancer target),
- // return any A record info available.
- newAddrs = w.lookupHost()
- }
- result := w.compileUpdate(newAddrs)
- w.curAddrs = newAddrs
- return result
- }
- // Next returns the resolved address update(delta) for the target. If there's no
- // change, it will sleep for 30 mins and try to resolve again after that.
- func (w *dnsWatcher) Next() ([]*Update, error) {
- for {
- select {
- case <-w.ctx.Done():
- return nil, errWatcherClose
- case <-w.t.C:
- }
- result := w.lookup()
- // Next lookup should happen after an interval defined by w.r.freq.
- w.t.Reset(w.r.freq)
- if len(result) > 0 {
- return result, nil
- }
- }
- }
- func (w *dnsWatcher) Close() {
- w.cancel()
- }
|