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
19package naming
20
21import (
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
32const (
33	defaultPort = "443"
34	defaultFreq = time.Minute * 30
35)
36
37var (
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.
44func 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.
50func NewDNSResolver() (Resolver, error) {
51	return NewDNSResolverWithFreq(defaultFreq)
52}
53
54// dnsResolver handles name resolution for names following the DNS scheme
55type 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.
63func 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"
84func 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.
113func (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
140type 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.
152type 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.
160func (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.
169func (i *ipWatcher) Close() {
170	close(i.updateChan)
171}
172
173// AddressType indicates the address type returned by name resolution.
174type AddressType uint8
175
176const (
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.
186type 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
195func (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
211func (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
238func (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
257func (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.
272func (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
288func (w *dnsWatcher) Close() {
289	w.cancel()
290}
291