Home | History | Annotate | Download | only in naming
      1 /*
      2  *
      3  * Copyright 2017 gRPC authors.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  *
     17  */
     18 
     19 package naming
     20 
     21 import (
     22 	"errors"
     23 	"fmt"
     24 	"net"
     25 	"strconv"
     26 	"time"
     27 
     28 	"golang.org/x/net/context"
     29 	"google.golang.org/grpc/grpclog"
     30 )
     31 
     32 const (
     33 	defaultPort = "443"
     34 	defaultFreq = time.Minute * 30
     35 )
     36 
     37 var (
     38 	errMissingAddr  = errors.New("missing address")
     39 	errWatcherClose = errors.New("watcher has been closed")
     40 )
     41 
     42 // NewDNSResolverWithFreq creates a DNS Resolver that can resolve DNS names, and
     43 // create watchers that poll the DNS server using the frequency set by freq.
     44 func NewDNSResolverWithFreq(freq time.Duration) (Resolver, error) {
     45 	return &dnsResolver{freq: freq}, nil
     46 }
     47 
     48 // NewDNSResolver creates a DNS Resolver that can resolve DNS names, and create
     49 // watchers that poll the DNS server using the default frequency defined by defaultFreq.
     50 func NewDNSResolver() (Resolver, error) {
     51 	return NewDNSResolverWithFreq(defaultFreq)
     52 }
     53 
     54 // dnsResolver handles name resolution for names following the DNS scheme
     55 type dnsResolver struct {
     56 	// frequency of polling the DNS server that the watchers created by this resolver will use.
     57 	freq time.Duration
     58 }
     59 
     60 // formatIP returns ok = false if addr is not a valid textual representation of an IP address.
     61 // If addr is an IPv4 address, return the addr and ok = true.
     62 // If addr is an IPv6 address, return the addr enclosed in square brackets and ok = true.
     63 func formatIP(addr string) (addrIP string, ok bool) {
     64 	ip := net.ParseIP(addr)
     65 	if ip == nil {
     66 		return "", false
     67 	}
     68 	if ip.To4() != nil {
     69 		return addr, true
     70 	}
     71 	return "[" + addr + "]", true
     72 }
     73 
     74 // parseTarget takes the user input target string, returns formatted host and port info.
     75 // If target doesn't specify a port, set the port to be the defaultPort.
     76 // If target is in IPv6 format and host-name is enclosed in sqarue brackets, brackets
     77 // are strippd when setting the host.
     78 // examples:
     79 // target: "www.google.com" returns host: "www.google.com", port: "443"
     80 // target: "ipv4-host:80" returns host: "ipv4-host", port: "80"
     81 // target: "[ipv6-host]" returns host: "ipv6-host", port: "443"
     82 // target: ":80" returns host: "localhost", port: "80"
     83 // target: ":" returns host: "localhost", port: "443"
     84 func parseTarget(target string) (host, port string, err error) {
     85 	if target == "" {
     86 		return "", "", errMissingAddr
     87 	}
     88 
     89 	if ip := net.ParseIP(target); ip != nil {
     90 		// target is an IPv4 or IPv6(without brackets) address
     91 		return target, defaultPort, nil
     92 	}
     93 	if host, port, err := net.SplitHostPort(target); err == nil {
     94 		// target has port, i.e ipv4-host:port, [ipv6-host]:port, host-name:port
     95 		if host == "" {
     96 			// Keep consistent with net.Dial(): If the host is empty, as in ":80", the local system is assumed.
     97 			host = "localhost"
     98 		}
     99 		if port == "" {
    100 			// If the port field is empty(target ends with colon), e.g. "[::1]:", defaultPort is used.
    101 			port = defaultPort
    102 		}
    103 		return host, port, nil
    104 	}
    105 	if host, port, err := net.SplitHostPort(target + ":" + defaultPort); err == nil {
    106 		// target doesn't have port
    107 		return host, port, nil
    108 	}
    109 	return "", "", fmt.Errorf("invalid target address %v", target)
    110 }
    111 
    112 // Resolve creates a watcher that watches the name resolution of the target.
    113 func (r *dnsResolver) Resolve(target string) (Watcher, error) {
    114 	host, port, err := parseTarget(target)
    115 	if err != nil {
    116 		return nil, err
    117 	}
    118 
    119 	if net.ParseIP(host) != nil {
    120 		ipWatcher := &ipWatcher{
    121 			updateChan: make(chan *Update, 1),
    122 		}
    123 		host, _ = formatIP(host)
    124 		ipWatcher.updateChan <- &Update{Op: Add, Addr: host + ":" + port}
    125 		return ipWatcher, nil
    126 	}
    127 
    128 	ctx, cancel := context.WithCancel(context.Background())
    129 	return &dnsWatcher{
    130 		r:      r,
    131 		host:   host,
    132 		port:   port,
    133 		ctx:    ctx,
    134 		cancel: cancel,
    135 		t:      time.NewTimer(0),
    136 	}, nil
    137 }
    138 
    139 // dnsWatcher watches for the name resolution update for a specific target
    140 type dnsWatcher struct {
    141 	r    *dnsResolver
    142 	host string
    143 	port string
    144 	// The latest resolved address set
    145 	curAddrs map[string]*Update
    146 	ctx      context.Context
    147 	cancel   context.CancelFunc
    148 	t        *time.Timer
    149 }
    150 
    151 // ipWatcher watches for the name resolution update for an IP address.
    152 type ipWatcher struct {
    153 	updateChan chan *Update
    154 }
    155 
    156 // Next returns the address resolution Update for the target. For IP address,
    157 // the resolution is itself, thus polling name server is unnecessary. Therefore,
    158 // Next() will return an Update the first time it is called, and will be blocked
    159 // for all following calls as no Update exists until watcher is closed.
    160 func (i *ipWatcher) Next() ([]*Update, error) {
    161 	u, ok := <-i.updateChan
    162 	if !ok {
    163 		return nil, errWatcherClose
    164 	}
    165 	return []*Update{u}, nil
    166 }
    167 
    168 // Close closes the ipWatcher.
    169 func (i *ipWatcher) Close() {
    170 	close(i.updateChan)
    171 }
    172 
    173 // AddressType indicates the address type returned by name resolution.
    174 type AddressType uint8
    175 
    176 const (
    177 	// Backend indicates the server is a backend server.
    178 	Backend AddressType = iota
    179 	// GRPCLB indicates the server is a grpclb load balancer.
    180 	GRPCLB
    181 )
    182 
    183 // AddrMetadataGRPCLB contains the information the name resolver for grpclb should provide. The
    184 // name resolver used by the grpclb balancer is required to provide this type of metadata in
    185 // its address updates.
    186 type AddrMetadataGRPCLB struct {
    187 	// AddrType is the type of server (grpc load balancer or backend).
    188 	AddrType AddressType
    189 	// ServerName is the name of the grpc load balancer. Used for authentication.
    190 	ServerName string
    191 }
    192 
    193 // compileUpdate compares the old resolved addresses and newly resolved addresses,
    194 // and generates an update list
    195 func (w *dnsWatcher) compileUpdate(newAddrs map[string]*Update) []*Update {
    196 	var res []*Update
    197 	for a, u := range w.curAddrs {
    198 		if _, ok := newAddrs[a]; !ok {
    199 			u.Op = Delete
    200 			res = append(res, u)
    201 		}
    202 	}
    203 	for a, u := range newAddrs {
    204 		if _, ok := w.curAddrs[a]; !ok {
    205 			res = append(res, u)
    206 		}
    207 	}
    208 	return res
    209 }
    210 
    211 func (w *dnsWatcher) lookupSRV() map[string]*Update {
    212 	newAddrs := make(map[string]*Update)
    213 	_, srvs, err := lookupSRV(w.ctx, "grpclb", "tcp", w.host)
    214 	if err != nil {
    215 		grpclog.Infof("grpc: failed dns SRV record lookup due to %v.\n", err)
    216 		return nil
    217 	}
    218 	for _, s := range srvs {
    219 		lbAddrs, err := lookupHost(w.ctx, s.Target)
    220 		if err != nil {
    221 			grpclog.Warningf("grpc: failed load banlacer address dns lookup due to %v.\n", err)
    222 			continue
    223 		}
    224 		for _, a := range lbAddrs {
    225 			a, ok := formatIP(a)
    226 			if !ok {
    227 				grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
    228 				continue
    229 			}
    230 			addr := a + ":" + strconv.Itoa(int(s.Port))
    231 			newAddrs[addr] = &Update{Addr: addr,
    232 				Metadata: AddrMetadataGRPCLB{AddrType: GRPCLB, ServerName: s.Target}}
    233 		}
    234 	}
    235 	return newAddrs
    236 }
    237 
    238 func (w *dnsWatcher) lookupHost() map[string]*Update {
    239 	newAddrs := make(map[string]*Update)
    240 	addrs, err := lookupHost(w.ctx, w.host)
    241 	if err != nil {
    242 		grpclog.Warningf("grpc: failed dns A record lookup due to %v.\n", err)
    243 		return nil
    244 	}
    245 	for _, a := range addrs {
    246 		a, ok := formatIP(a)
    247 		if !ok {
    248 			grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
    249 			continue
    250 		}
    251 		addr := a + ":" + w.port
    252 		newAddrs[addr] = &Update{Addr: addr}
    253 	}
    254 	return newAddrs
    255 }
    256 
    257 func (w *dnsWatcher) lookup() []*Update {
    258 	newAddrs := w.lookupSRV()
    259 	if newAddrs == nil {
    260 		// If failed to get any balancer address (either no corresponding SRV for the
    261 		// target, or caused by failure during resolution/parsing of the balancer target),
    262 		// return any A record info available.
    263 		newAddrs = w.lookupHost()
    264 	}
    265 	result := w.compileUpdate(newAddrs)
    266 	w.curAddrs = newAddrs
    267 	return result
    268 }
    269 
    270 // Next returns the resolved address update(delta) for the target. If there's no
    271 // change, it will sleep for 30 mins and try to resolve again after that.
    272 func (w *dnsWatcher) Next() ([]*Update, error) {
    273 	for {
    274 		select {
    275 		case <-w.ctx.Done():
    276 			return nil, errWatcherClose
    277 		case <-w.t.C:
    278 		}
    279 		result := w.lookup()
    280 		// Next lookup should happen after an interval defined by w.r.freq.
    281 		w.t.Reset(w.r.freq)
    282 		if len(result) > 0 {
    283 			return result, nil
    284 		}
    285 	}
    286 }
    287 
    288 func (w *dnsWatcher) Close() {
    289 	w.cancel()
    290 }
    291