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