zone.go 3.6 KB

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