zone.go 3.7 KB

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