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