3 * Copyright 2017 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
28 "golang.org/x/net/context"
29 "google.golang.org/grpc/grpclog"
34 defaultFreq = time.Minute * 30
38 errMissingAddr = errors.New("missing address")
39 errWatcherClose = errors.New("watcher has been closed")
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
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)
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.
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)
71 return "[" + addr + "]", true
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.
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) {
86 return "", "", errMissingAddr
89 if ip := net.ParseIP(target); ip != nil {
90 // target is an IPv4 or IPv6(without brackets) address
91 return target, defaultPort, nil
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
96 // Keep consistent with net.Dial(): If the host is empty, as in ":80", the local system is assumed.
100 // If the port field is empty(target ends with colon), e.g. "[::1]:", defaultPort is used.
103 return host, port, nil
105 if host, port, err := net.SplitHostPort(target + ":" + defaultPort); err == nil {
106 // target doesn't have port
107 return host, port, nil
109 return "", "", fmt.Errorf("invalid target address %v", target)
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)
119 if net.ParseIP(host) != nil {
120 ipWatcher := &ipWatcher{
121 updateChan: make(chan *Update, 1),
123 host, _ = formatIP(host)
124 ipWatcher.updateChan <- &Update{Op: Add, Addr: host + ":" + port}
125 return ipWatcher, nil
128 ctx, cancel := context.WithCancel(context.Background())
139 // dnsWatcher watches for the name resolution update for a specific target
140 type dnsWatcher struct {
144 // The latest resolved address list
147 cancel context.CancelFunc
151 // ipWatcher watches for the name resolution update for an IP address.
152 type ipWatcher struct {
153 updateChan chan *Update
156 // Next returns the adrress resolution Update for the target. For IP address,
157 // the resolution is itself, thus polling name server is unncessary. 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 exisits until watcher is closed.
160 func (i *ipWatcher) Next() ([]*Update, error) {
161 u, ok := <-i.updateChan
163 return nil, errWatcherClose
165 return []*Update{u}, nil
168 // Close closes the ipWatcher.
169 func (i *ipWatcher) Close() {
173 // AddressType indicates the address type returned by name resolution.
174 type AddressType uint8
177 // Backend indicates the server is a backend server.
178 Backend AddressType = iota
179 // GRPCLB indicates the server is a grpclb load balancer.
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).
189 // ServerName is the name of the grpc load balancer. Used for authentication.
193 // compileUpdate compares the old resolved addresses and newly resolved addresses,
194 // and generates an update list
195 func (w *dnsWatcher) compileUpdate(newAddrs []*Update) []*Update {
196 update := make(map[Update]bool)
197 for _, u := range newAddrs {
200 for _, u := range w.curAddrs {
201 if _, ok := update[*u]; ok {
205 update[Update{Addr: u.Addr, Op: Delete, Metadata: u.Metadata}] = true
207 res := make([]*Update, 0, len(update))
208 for k := range update {
210 res = append(res, &tmp)
215 func (w *dnsWatcher) lookupSRV() []*Update {
216 var newAddrs []*Update
217 _, srvs, err := lookupSRV(w.ctx, "grpclb", "tcp", w.host)
219 grpclog.Infof("grpc: failed dns SRV record lookup due to %v.\n", err)
222 for _, s := range srvs {
223 lbAddrs, err := lookupHost(w.ctx, s.Target)
225 grpclog.Warningf("grpc: failed load banlacer address dns lookup due to %v.\n", err)
228 for _, a := range lbAddrs {
231 grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
234 newAddrs = append(newAddrs, &Update{Addr: a + ":" + strconv.Itoa(int(s.Port)),
235 Metadata: AddrMetadataGRPCLB{AddrType: GRPCLB, ServerName: s.Target}})
241 func (w *dnsWatcher) lookupHost() []*Update {
242 var newAddrs []*Update
243 addrs, err := lookupHost(w.ctx, w.host)
245 grpclog.Warningf("grpc: failed dns A record lookup due to %v.\n", err)
248 for _, a := range addrs {
251 grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
254 newAddrs = append(newAddrs, &Update{Addr: a + ":" + w.port})
259 func (w *dnsWatcher) lookup() []*Update {
260 newAddrs := w.lookupSRV()
262 // If failed to get any balancer address (either no corresponding SRV for the
263 // target, or caused by failure during resolution/parsing of the balancer target),
264 // return any A record info available.
265 newAddrs = w.lookupHost()
267 result := w.compileUpdate(newAddrs)
268 w.curAddrs = newAddrs
272 // Next returns the resolved address update(delta) for the target. If there's no
273 // change, it will sleep for 30 mins and try to resolve again after that.
274 func (w *dnsWatcher) Next() ([]*Update, error) {
278 return nil, errWatcherClose
282 // Next lookup should happen after an interval defined by w.r.freq.
290 func (w *dnsWatcher) Close() {