dns_resolver.go 8.3 KB

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