zone.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package main
  2. import (
  3. "strings"
  4. "time"
  5. "github.com/abh/dns"
  6. "github.com/abh/go-metrics"
  7. )
  8. type ZoneOptions struct {
  9. Serial int
  10. Ttl int
  11. MaxHosts int
  12. Contact string
  13. Targeting TargetOptions
  14. }
  15. type ZoneLogging struct {
  16. StatHat bool
  17. StatHatAPI string
  18. InfluxDB bool
  19. }
  20. type Record struct {
  21. RR dns.RR
  22. Weight int
  23. }
  24. type Records []Record
  25. func (s Records) Len() int { return len(s) }
  26. func (s Records) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  27. type RecordsByWeight struct{ Records }
  28. func (s RecordsByWeight) Less(i, j int) bool { return s.Records[i].Weight > s.Records[j].Weight }
  29. type Label struct {
  30. Label string
  31. MaxHosts int
  32. Ttl int
  33. Records map[uint16]Records
  34. Weight map[uint16]int
  35. }
  36. type labels map[string]*Label
  37. type ZoneMetrics struct {
  38. Queries metrics.Meter
  39. EdnsQueries metrics.Meter
  40. LabelStats *zoneLabelStats
  41. ClientStats *zoneLabelStats
  42. }
  43. type Zone struct {
  44. Origin string
  45. Labels labels
  46. LabelCount int
  47. Options ZoneOptions
  48. Logging *ZoneLogging
  49. LastRead time.Time
  50. Metrics ZoneMetrics
  51. }
  52. type qTypes []uint16
  53. func NewZone(name string) *Zone {
  54. zone := new(Zone)
  55. zone.Labels = make(labels)
  56. zone.Origin = name
  57. zone.LabelCount = dns.CountLabel(zone.Origin)
  58. // defaults
  59. zone.Options.Ttl = 120
  60. zone.Options.MaxHosts = 2
  61. zone.Options.Contact = "support.bitnames.com"
  62. zone.Options.Targeting = TargetGlobal + TargetCountry + TargetContinent
  63. return zone
  64. }
  65. func (z *Zone) SetupMetrics(old *Zone) {
  66. if old != nil {
  67. z.Metrics = old.Metrics
  68. } else {
  69. z.Metrics.Queries = metrics.NewMeter()
  70. z.Metrics.EdnsQueries = metrics.NewMeter()
  71. metrics.Register(z.Origin+" queries", z.Metrics.Queries)
  72. metrics.Register(z.Origin+" EDNS queries", z.Metrics.EdnsQueries)
  73. z.Metrics.LabelStats = NewZoneLabelStats(10000)
  74. z.Metrics.ClientStats = NewZoneLabelStats(10000)
  75. }
  76. }
  77. func (z *Zone) Close() {
  78. metrics.Unregister(z.Origin + " queries")
  79. metrics.Unregister(z.Origin + " EDNS queries")
  80. z.Metrics.LabelStats.Close()
  81. z.Metrics.ClientStats.Close()
  82. }
  83. func (l *Label) firstRR(dnsType uint16) dns.RR {
  84. return l.Records[dnsType][0].RR
  85. }
  86. func (z *Zone) AddLabel(k string) *Label {
  87. k = strings.ToLower(k)
  88. z.Labels[k] = new(Label)
  89. label := z.Labels[k]
  90. label.Label = k
  91. label.Ttl = z.Options.Ttl
  92. label.MaxHosts = z.Options.MaxHosts
  93. label.Records = make(map[uint16]Records)
  94. label.Weight = make(map[uint16]int)
  95. return label
  96. }
  97. func (z *Zone) SoaRR() dns.RR {
  98. return z.Labels[""].firstRR(dns.TypeSOA)
  99. }
  100. // Find label "s" in country "cc" falling back to the appropriate
  101. // continent and the global label name as needed. Looks for the
  102. // first available qType at each targeting level. Return a Label
  103. // and the qtype that was "found"
  104. func (z *Zone) findLabels(s string, targets []string, qts qTypes) (*Label, uint16) {
  105. for _, target := range targets {
  106. var name string
  107. switch target {
  108. case "@":
  109. name = s
  110. default:
  111. if len(s) > 0 {
  112. name = s + "." + target
  113. } else {
  114. name = target
  115. }
  116. }
  117. if label, ok := z.Labels[name]; ok {
  118. for _, qtype := range qts {
  119. switch qtype {
  120. case dns.TypeANY:
  121. // short-circuit mostly to avoid subtle bugs later
  122. // to be correct we should run through all the selectors and
  123. // pick types not already picked
  124. return z.Labels[s], qtype
  125. case dns.TypeMF:
  126. if label.Records[dns.TypeMF] != nil {
  127. name = label.firstRR(dns.TypeMF).(*dns.MF).Mf
  128. // TODO: need to avoid loops here somehow
  129. return z.findLabels(name, targets, qts)
  130. }
  131. default:
  132. // return the label if it has the right record
  133. if label.Records[qtype] != nil && len(label.Records[qtype]) > 0 {
  134. return label, qtype
  135. }
  136. }
  137. }
  138. }
  139. }
  140. return z.Labels[s], 0
  141. }