dns.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. package agent
  2. import (
  3. "fmt"
  4. "net"
  5. "strings"
  6. "sync/atomic"
  7. "time"
  8. "github.com/miekg/dns"
  9. "go-common/app/service/main/bns/conf"
  10. "go-common/app/service/main/bns/lib/resolvconf"
  11. "go-common/app/service/main/bns/lib/shuffle"
  12. "go-common/library/log"
  13. "go-common/library/stat/prom"
  14. )
  15. const (
  16. maxRecurseRecords = 5
  17. )
  18. var grpcPrefixs = []string{"_grpclb._tcp.", "_grpc_config."}
  19. var dnsProm = prom.New().WithTimer("go_bns_server", []string{"time"})
  20. func wrapProm(handler func(dns.ResponseWriter, *dns.Msg)) func(dns.ResponseWriter, *dns.Msg) {
  21. return func(w dns.ResponseWriter, m *dns.Msg) {
  22. start := time.Now()
  23. handler(w, m)
  24. dt := int64(time.Since(start) / time.Millisecond)
  25. dnsProm.Timing("bns:dns_query", dt)
  26. }
  27. }
  28. // DNSServer is used to wrap an Agent and expose various
  29. // service discovery endpoints using a DNS interface.
  30. type DNSServer struct {
  31. *dns.Server
  32. cfg *conf.DNSServer
  33. agent *Agent
  34. domain string
  35. recursors []string
  36. // disableCompression is the config.DisableCompression flag that can
  37. // be safely changed at runtime. It always contains a bool and is
  38. // initialized with the value from config.DisableCompression.
  39. disableCompression atomic.Value
  40. udpClient *dns.Client
  41. tcpClient *dns.Client
  42. }
  43. // NewDNSServer new dns server
  44. func NewDNSServer(a *Agent, cfg *conf.DNSServer) (*DNSServer, error) {
  45. var recursors []string
  46. var confRecursors = cfg.Config.Recursors
  47. if len(confRecursors) == 0 {
  48. resolv, err := resolvconf.ParseResolvConf()
  49. if err != nil {
  50. log.Warn("read resolv.conf error: %s", err)
  51. } else {
  52. confRecursors = resolv
  53. }
  54. }
  55. for _, r := range confRecursors {
  56. ra, err := recursorAddr(r)
  57. if err != nil {
  58. return nil, fmt.Errorf("Invalid recursor address: %v", err)
  59. }
  60. recursors = append(recursors, ra)
  61. }
  62. log.Info("recursors %v", recursors)
  63. // Make sure domain is FQDN, make it case insensitive for ServeMux
  64. domain := dns.Fqdn(strings.ToLower(cfg.Config.Domain))
  65. srv := &DNSServer{
  66. agent: a,
  67. domain: domain,
  68. recursors: recursors,
  69. cfg: cfg,
  70. udpClient: &dns.Client{Net: "udp", Timeout: time.Duration(cfg.Config.RecursorTimeout)},
  71. tcpClient: &dns.Client{Net: "tcp", Timeout: time.Duration(cfg.Config.RecursorTimeout)},
  72. }
  73. srv.disableCompression.Store(cfg.Config.DisableCompression)
  74. return srv, nil
  75. }
  76. // ListenAndServe listen and serve dns
  77. func (s *DNSServer) ListenAndServe(network, addr string, notif func()) error {
  78. mux := dns.NewServeMux()
  79. mux.HandleFunc("arpa.", wrapProm(s.handlePtr))
  80. mux.HandleFunc(".", wrapProm(s.handleRecurse))
  81. mux.HandleFunc(s.domain, wrapProm(s.handleQuery))
  82. s.Server = &dns.Server{
  83. Addr: addr,
  84. Net: network,
  85. Handler: mux,
  86. NotifyStartedFunc: notif,
  87. }
  88. if network == "udp" {
  89. s.UDPSize = 65535
  90. }
  91. return s.Server.ListenAndServe()
  92. }
  93. // recursorAddr is used to add a port to the recursor if omitted.
  94. func recursorAddr(recursor string) (string, error) {
  95. // Add the port if none
  96. START:
  97. _, _, err := net.SplitHostPort(recursor)
  98. if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
  99. recursor = fmt.Sprintf("%s:%d", recursor, 53)
  100. goto START
  101. }
  102. if err != nil {
  103. return "", err
  104. }
  105. // Get the address
  106. addr, err := net.ResolveTCPAddr("tcp", recursor)
  107. if err != nil {
  108. return "", err
  109. }
  110. // Return string
  111. return addr.String(), nil
  112. }
  113. // handlePtr is used to handle "reverse" DNS queries
  114. func (s *DNSServer) handlePtr(resp dns.ResponseWriter, req *dns.Msg) {
  115. q := req.Question[0]
  116. defer func(s time.Time) {
  117. log.V(5).Info("dns: request for %v (%v) from client %s (%s)",
  118. q, time.Since(s), resp.RemoteAddr().String(),
  119. resp.RemoteAddr().Network())
  120. }(time.Now())
  121. // Setup the message response
  122. m := new(dns.Msg)
  123. m.SetReply(req)
  124. m.Compress = !s.disableCompression.Load().(bool)
  125. m.Authoritative = true
  126. m.RecursionAvailable = (len(s.recursors) > 0)
  127. // Only add the SOA if requested
  128. if req.Question[0].Qtype == dns.TypeSOA {
  129. s.addSOA(m)
  130. }
  131. // Get the QName without the domain suffix
  132. qName := strings.ToLower(dns.Fqdn(req.Question[0].Name))
  133. // FIXME: should return multiple nameservers?
  134. log.V(5).Info("dns: we said handled ptr with %v", qName)
  135. // nothing found locally, recurse
  136. if len(m.Answer) == 0 {
  137. s.handleRecurse(resp, req)
  138. return
  139. }
  140. // Enable EDNS if enabled
  141. if edns := req.IsEdns0(); edns != nil {
  142. m.SetEdns0(edns.UDPSize(), false)
  143. }
  144. // Write out the complete response
  145. if err := resp.WriteMsg(m); err != nil {
  146. log.Warn("dns: failed to respond: %v", err)
  147. }
  148. }
  149. // handleQuery is used to handle DNS queries in the configured domain
  150. func (s *DNSServer) handleQuery(resp dns.ResponseWriter, req *dns.Msg) {
  151. q := req.Question[0]
  152. defer func(s time.Time) {
  153. log.V(5).Info("dns: request for %v (%v) from client %s (%s)",
  154. q, time.Since(s), resp.RemoteAddr().String(),
  155. resp.RemoteAddr().Network())
  156. }(time.Now())
  157. // Switch to TCP if the client is
  158. network := "udp"
  159. if _, ok := resp.RemoteAddr().(*net.TCPAddr); ok {
  160. network = "tcp"
  161. }
  162. // Setup the message response
  163. m := new(dns.Msg)
  164. m.SetReply(req)
  165. m.Compress = !s.disableCompression.Load().(bool)
  166. m.Authoritative = true
  167. m.RecursionAvailable = (len(s.recursors) > 0)
  168. switch req.Question[0].Qtype {
  169. case dns.TypeSOA:
  170. ns, glue := s.nameservers(req.IsEdns0() != nil)
  171. m.Answer = append(m.Answer, s.soa())
  172. m.Ns = append(m.Ns, ns...)
  173. m.Extra = append(m.Extra, glue...)
  174. m.SetRcode(req, dns.RcodeSuccess)
  175. case dns.TypeNS:
  176. ns, glue := s.nameservers(req.IsEdns0() != nil)
  177. m.Answer = ns
  178. m.Extra = glue
  179. m.SetRcode(req, dns.RcodeSuccess)
  180. case dns.TypeAXFR:
  181. m.SetRcode(req, dns.RcodeNotImplemented)
  182. default:
  183. s.dispatch(network, req, m)
  184. }
  185. // Handle EDNS
  186. if edns := req.IsEdns0(); edns != nil {
  187. m.SetEdns0(edns.UDPSize(), false)
  188. }
  189. // Write out the complete response
  190. if err := resp.WriteMsg(m); err != nil {
  191. log.Warn("dns: failed to respond: %v", err)
  192. }
  193. }
  194. func (s *DNSServer) soa() *dns.SOA {
  195. return &dns.SOA{
  196. Hdr: dns.RR_Header{
  197. Name: s.domain,
  198. Rrtype: dns.TypeSOA,
  199. Class: dns.ClassINET,
  200. Ttl: 0,
  201. },
  202. Ns: "ns." + s.domain,
  203. Serial: uint32(time.Now().Unix()),
  204. // todo(fs): make these configurable
  205. Mbox: "hostmaster." + s.domain,
  206. Refresh: 3600,
  207. Retry: 600,
  208. Expire: 86400,
  209. Minttl: 30,
  210. }
  211. }
  212. // addSOA is used to add an SOA record to a message for the given domain
  213. func (s *DNSServer) addSOA(msg *dns.Msg) {
  214. msg.Ns = append(msg.Ns, s.soa())
  215. }
  216. // formatNodeRecord takes an Easyns Agent node and returns an A, AAAA, or CNAME record
  217. func (s *DNSServer) formatNodeRecord(addr, qName string, qType uint16, ttl time.Duration, edns bool) (records []dns.RR) {
  218. // Parse the IP
  219. ip := net.ParseIP(addr)
  220. var ipv4 net.IP
  221. if ip != nil {
  222. ipv4 = ip.To4()
  223. }
  224. switch {
  225. case ipv4 != nil && (qType == dns.TypeANY || qType == dns.TypeA):
  226. return []dns.RR{&dns.A{
  227. Hdr: dns.RR_Header{
  228. Name: qName,
  229. Rrtype: dns.TypeA,
  230. Class: dns.ClassINET,
  231. Ttl: uint32(ttl / time.Second),
  232. },
  233. A: ip,
  234. }}
  235. case ip != nil && ipv4 == nil && (qType == dns.TypeANY || qType == dns.TypeAAAA):
  236. return []dns.RR{&dns.AAAA{
  237. Hdr: dns.RR_Header{
  238. Name: qName,
  239. Rrtype: dns.TypeAAAA,
  240. Class: dns.ClassINET,
  241. Ttl: uint32(ttl / time.Second),
  242. },
  243. AAAA: ip,
  244. }}
  245. case ip == nil && (qType == dns.TypeANY || qType == dns.TypeCNAME ||
  246. qType == dns.TypeA || qType == dns.TypeAAAA):
  247. // Get the CNAME
  248. cnRec := &dns.CNAME{
  249. Hdr: dns.RR_Header{
  250. Name: qName,
  251. Rrtype: dns.TypeCNAME,
  252. Class: dns.ClassINET,
  253. Ttl: uint32(ttl / time.Second),
  254. },
  255. Target: dns.Fqdn(addr),
  256. }
  257. records = append(records, cnRec)
  258. // Recurse
  259. more := s.resolveCNAME(cnRec.Target)
  260. extra := 0
  261. MORE_REC:
  262. for _, rr := range more {
  263. switch rr.Header().Rrtype {
  264. case dns.TypeCNAME, dns.TypeA, dns.TypeAAAA:
  265. records = append(records, rr)
  266. extra++
  267. if extra == maxRecurseRecords && !edns {
  268. break MORE_REC
  269. }
  270. }
  271. }
  272. }
  273. return records
  274. }
  275. // nameservers returns the names and ip addresses of up to three random servers
  276. // in the current cluster which serve as authoritative name servers for zone.
  277. func (s *DNSServer) nameservers(edns bool) (ns []dns.RR, extra []dns.RR) {
  278. // TODO: get list of bns dns nameservers
  279. // Then, construct them into NS RR.
  280. // We just hardcode here right now...
  281. name := "bns"
  282. addr := s.agent.cfg.DNS.Addr
  283. fqdn := name + "." + s.domain
  284. fqdn = dns.Fqdn(strings.ToLower(fqdn))
  285. // NS record
  286. nsrr := &dns.NS{
  287. Hdr: dns.RR_Header{
  288. Name: s.domain,
  289. Rrtype: dns.TypeNS,
  290. Class: dns.ClassINET,
  291. Ttl: uint32(time.Duration(s.agent.cfg.DNS.Config.TTL) / time.Second),
  292. },
  293. Ns: fqdn,
  294. }
  295. ns = append(ns, nsrr)
  296. // A or AAAA glue record
  297. glue := s.formatNodeRecord(addr, fqdn, dns.TypeANY, time.Duration(s.agent.cfg.DNS.Config.TTL), edns)
  298. extra = append(extra, glue...)
  299. return
  300. }
  301. func trimDomainSuffix(name string, domain string) (reversed string) {
  302. reversed = strings.TrimSuffix(name, "."+domain)
  303. return strings.Trim(reversed, ".")
  304. }
  305. // Answers answers
  306. type Answers []dns.RR
  307. // Len the number of answer
  308. func (as Answers) Len() int {
  309. return len(as)
  310. }
  311. // Swap order
  312. func (as Answers) Swap(i, j int) {
  313. as[i], as[j] = as[j], as[i]
  314. }
  315. // dispatch is used to parse a request and invoke the correct handler
  316. func (s *DNSServer) dispatch(network string, req, resp *dns.Msg) {
  317. var answers Answers
  318. // Get the QName
  319. qName := strings.ToLower(dns.Fqdn(req.Question[0].Name))
  320. name := trimDomainSuffix(qName, s.agent.cfg.DNS.Config.Domain)
  321. for _, prefix := range grpcPrefixs {
  322. name = strings.TrimPrefix(name, prefix)
  323. }
  324. inss, err := s.agent.Query(name)
  325. if err != nil {
  326. log.Error("dns: query %s failed to resolve from bns server, err: %s", name, err)
  327. goto INVALID
  328. }
  329. if len(inss) == 0 {
  330. log.Error("dns: QName %s has no upstreams found!", qName)
  331. goto INVALID
  332. }
  333. for _, ins := range inss {
  334. answers = append(answers, &dns.A{
  335. Hdr: dns.RR_Header{
  336. Name: qName,
  337. Rrtype: dns.TypeA,
  338. Class: dns.ClassINET,
  339. Ttl: uint32(time.Duration(s.agent.cfg.DNS.Config.TTL) / time.Second),
  340. },
  341. A: ins.IPAddr,
  342. })
  343. log.V(5).Info("dns: QName resolved ipAddress: %s - %s", qName, ins.IPAddr)
  344. }
  345. shuffle.Shuffle(answers)
  346. resp.Answer = []dns.RR(answers)
  347. return
  348. INVALID:
  349. s.addSOA(resp)
  350. resp.SetRcode(req, dns.RcodeNameError)
  351. }
  352. // handleRecurse is used to handle recursive DNS queries
  353. func (s *DNSServer) handleRecurse(resp dns.ResponseWriter, req *dns.Msg) {
  354. q := req.Question[0]
  355. network := "udp"
  356. client := s.udpClient
  357. defer func(s time.Time) {
  358. log.V(5).Info("dns: request for %v (%s) (%v) from client %s (%s)",
  359. q, network, time.Since(s), resp.RemoteAddr().String(),
  360. resp.RemoteAddr().Network())
  361. }(time.Now())
  362. // Switch to TCP if the client is
  363. if _, ok := resp.RemoteAddr().(*net.TCPAddr); ok {
  364. network = "tcp"
  365. client = s.tcpClient
  366. }
  367. for _, recursor := range s.recursors {
  368. r, rtt, err := client.Exchange(req, recursor)
  369. if err == nil || err == dns.ErrTruncated {
  370. // Compress the response; we don't know if the incoming
  371. // response was compressed or not, so by not compressing
  372. // we might generate an invalid packet on the way out.
  373. r.Compress = !s.disableCompression.Load().(bool)
  374. // Forward the response
  375. log.V(5).Info("dns: recurse RTT for %v (%v)", q, rtt)
  376. if err = resp.WriteMsg(r); err != nil {
  377. log.Warn("dns: failed to respond: %v", err)
  378. }
  379. return
  380. }
  381. log.Error("dns: recurse failed: %v", err)
  382. }
  383. // If all resolvers fail, return a SERVFAIL message
  384. log.Error("dns: all resolvers failed for %v from client %s (%s)",
  385. q, resp.RemoteAddr().String(), resp.RemoteAddr().Network())
  386. m := &dns.Msg{}
  387. m.SetReply(req)
  388. m.Compress = !s.disableCompression.Load().(bool)
  389. m.RecursionAvailable = true
  390. m.SetRcode(req, dns.RcodeServerFailure)
  391. if edns := req.IsEdns0(); edns != nil {
  392. m.SetEdns0(edns.UDPSize(), false)
  393. }
  394. resp.WriteMsg(m)
  395. }
  396. // resolveCNAME is used to recursively resolve CNAME records
  397. func (s *DNSServer) resolveCNAME(name string) []dns.RR {
  398. // If the CNAME record points to a Easyns Name address, resolve it internally
  399. // Convert query to lowercase because DNS is case insensitive; d.domain is
  400. // already converted
  401. if strings.HasSuffix(strings.ToLower(name), "."+s.domain) {
  402. req := &dns.Msg{}
  403. resp := &dns.Msg{}
  404. req.SetQuestion(name, dns.TypeANY)
  405. s.dispatch("udp", req, resp)
  406. return resp.Answer
  407. }
  408. // Do nothing if we don't have a recursor
  409. if len(s.recursors) == 0 {
  410. return nil
  411. }
  412. // Ask for any A records
  413. m := new(dns.Msg)
  414. m.SetQuestion(name, dns.TypeA)
  415. // Make a DNS lookup request
  416. c := &dns.Client{Net: "udp", Timeout: time.Duration(s.agent.cfg.DNS.Config.RecursorTimeout)}
  417. var r *dns.Msg
  418. var rtt time.Duration
  419. var err error
  420. for _, recursor := range s.recursors {
  421. r, rtt, err = c.Exchange(m, recursor)
  422. if err == nil {
  423. log.V(5).Info("dns: cname recurse RTT for %v (%v)", name, rtt)
  424. return r.Answer
  425. }
  426. log.Error("dns: cname recurse failed for %v: %v", name, err)
  427. }
  428. log.Error("dns: all resolvers failed for %v", name)
  429. return nil
  430. }