text: /*
   *
   * 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 square brackets, brackets
  // are stripped 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 balancer 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()
  }
  