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