zone.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. package zone
  2. import (
  3. "fmt"
  4. "sync"
  5. "git.giaever.org/joachimmg/go-log.git/log"
  6. "git.giaever.org/joachimmg/m-dns/config"
  7. "git.giaever.org/joachimmg/m-dns/errors"
  8. "git.giaever.org/joachimmg/m-dns/host"
  9. "github.com/miekg/dns"
  10. )
  11. type Zone interface {
  12. Records(q dns.Question) []dns.RR
  13. }
  14. type Resource struct {
  15. sync.Mutex
  16. host.Host
  17. }
  18. func (r *Resource) qType(q dns.Question) string {
  19. switch q.Qtype {
  20. case dns.TypeANY:
  21. return "TypeANY"
  22. case dns.TypePTR:
  23. return "TypePTR"
  24. case dns.TypeSRV:
  25. return "TypeSRV"
  26. case dns.TypeA:
  27. return "TypeA"
  28. case dns.TypeAAAA:
  29. return "TypeAAAA"
  30. case dns.TypeTXT:
  31. return "TypeTXT"
  32. default:
  33. return fmt.Sprintf("%d", q.Qtype)
  34. }
  35. }
  36. func New(h host.Host) (*Resource, error) {
  37. r := new(Resource)
  38. if r == nil {
  39. log.Traceln(errors.Zone, errors.OutOfMemory)
  40. return nil, errors.OutOfMemory
  41. }
  42. if h == nil {
  43. log.Traceln(errors.Zone, errors.HostIsNil)
  44. return nil, errors.HostIsNil
  45. }
  46. r.Host = h
  47. return r, nil
  48. }
  49. func (r *Resource) hdr(n string, rt uint16) dns.RR_Header {
  50. return dns.RR_Header{
  51. Name: n,
  52. Rrtype: rt,
  53. Class: dns.ClassINET,
  54. Ttl: config.DefaultTTL,
  55. }
  56. }
  57. func (r *Resource) ptrRecord(q dns.Question, p host.HostString) dns.RR {
  58. r.Lock()
  59. defer r.Unlock()
  60. return &dns.PTR{
  61. Hdr: r.hdr(q.Name, dns.TypePTR),
  62. Ptr: p.String(),
  63. }
  64. }
  65. func (r *Resource) srvRecord(q dns.Question) dns.RR {
  66. r.Lock()
  67. defer r.Unlock()
  68. log.Traceln("srvRecord")
  69. return &dns.SRV{
  70. Hdr: r.hdr(q.Name, dns.TypeSRV),
  71. Priority: 10,
  72. Weight: 1,
  73. Port: r.GetPort().Uint16(),
  74. Target: r.GetHostnameAddr().String(),
  75. }
  76. }
  77. func (r *Resource) txtRecord(q dns.Question) dns.RR {
  78. r.Lock()
  79. defer r.Unlock()
  80. return &dns.TXT{
  81. Hdr: r.hdr(q.Name, dns.TypeTXT),
  82. Txt: r.GetTXTs(),
  83. }
  84. }
  85. func (r *Resource) records(rr ...dns.RR) []dns.RR {
  86. rrn := make([]dns.RR, 0)
  87. if rr == nil {
  88. return rrn
  89. }
  90. return append(rrn, rr...)
  91. }
  92. func (r *Resource) aRecord(q dns.Question, ip host.HostIP) dns.RR {
  93. r.Lock()
  94. defer r.Unlock()
  95. return &dns.A{
  96. Hdr: r.hdr(r.GetHostnameAddr().String(), dns.TypeA),
  97. A: ip.AsIP(),
  98. }
  99. }
  100. func (r *Resource) aaaaRecord(q dns.Question, ip host.HostIP) dns.RR {
  101. r.Lock()
  102. defer r.Unlock()
  103. return &dns.AAAA{
  104. Hdr: r.hdr(r.GetHostnameAddr().String(), dns.TypeAAAA),
  105. AAAA: ip.AsIP(),
  106. }
  107. }
  108. func (r *Resource) axRecords(q dns.Question, atype uint16) []dns.RR {
  109. a := r.records(nil)[:0]
  110. for _, ip := range r.GetIPs() {
  111. switch atype {
  112. case dns.TypeA:
  113. if _, ipType := ip.Type(); ipType == host.IPv4 {
  114. a = append(a, r.aRecord(q, ip))
  115. }
  116. case dns.TypeAAAA:
  117. if _, ipType := ip.Type(); ipType == host.IPv6 {
  118. a = append(a, r.aaaaRecord(q, ip))
  119. }
  120. }
  121. }
  122. if len(a) == 0 {
  123. return nil
  124. }
  125. return a
  126. }
  127. // Records: Return DNS records based on the Question
  128. func (r *Resource) Records(q dns.Question) []dns.RR {
  129. log.Traceln(errors.Zone, "Records", q.Name, r.qType(q))
  130. switch q.Name {
  131. case r.GetInstanceAddr().String(): // RFC 6763, 13.3 "instance"._<service>.<domain>
  132. return r.iRecords(q)
  133. case r.GetServiceAddr().String(): // RFC 6763, 13.1 <_service>.<domain>
  134. return r.sRecords(q)
  135. case r.GetDiscoveryAddr().String():
  136. return r.dRecords(q)
  137. case r.GetHostnameAddr().String():
  138. switch q.Qtype {
  139. case dns.TypeANY, dns.TypeA, dns.TypeAAAA:
  140. return r.iRecords(q)
  141. }
  142. fallthrough
  143. default:
  144. return nil
  145. }
  146. }
  147. // iRecords: Instance records
  148. func (r *Resource) iRecords(q dns.Question) []dns.RR {
  149. switch q.Qtype {
  150. case dns.TypeANY:
  151. return append(r.iRecords(dns.Question{
  152. Name: r.GetInstanceAddr().String(),
  153. Qtype: dns.TypeSRV,
  154. }), r.iRecords(dns.Question{
  155. Name: r.GetInstanceAddr().String(),
  156. Qtype: dns.TypeTXT,
  157. })...)
  158. case dns.TypeA:
  159. return r.axRecords(q, dns.TypeA)
  160. case dns.TypeAAAA:
  161. return r.axRecords(q, dns.TypeAAAA)
  162. case dns.TypeSRV:
  163. return append(
  164. r.records(r.srvRecord(q)),
  165. append(
  166. r.iRecords(dns.Question{
  167. Name: r.GetInstanceAddr().String(),
  168. Qtype: dns.TypeA,
  169. }),
  170. r.iRecords(dns.Question{
  171. Name: r.GetInstanceAddr().String(),
  172. Qtype: dns.TypeAAAA,
  173. })...,
  174. )...,
  175. )
  176. case dns.TypeTXT:
  177. return r.records(r.txtRecord(q))
  178. default:
  179. log.Traceln(errors.Zone, "None iRecord for", r.qType(q))
  180. return nil
  181. }
  182. }
  183. // sRecords: Service records
  184. func (r *Resource) sRecords(q dns.Question) []dns.RR {
  185. switch q.Qtype {
  186. case dns.TypeANY, dns.TypePTR:
  187. return append(
  188. r.records(r.ptrRecord(q, r.GetInstanceAddr())),
  189. r.iRecords(dns.Question{
  190. Name: r.GetInstanceAddr().String(),
  191. Qtype: dns.TypeANY,
  192. })...,
  193. )
  194. default:
  195. log.Traceln(errors.Zone, "None sRecord for", r.qType(q))
  196. return nil
  197. }
  198. }
  199. // dRecords: DNS-SD / Discovery aka enumerate
  200. func (r *Resource) dRecords(q dns.Question) []dns.RR {
  201. switch q.Qtype {
  202. case dns.TypeANY, dns.TypePTR:
  203. return r.records(r.ptrRecord(q, r.GetServiceAddr()))
  204. default:
  205. log.Traceln(errors.Zone, "None dRecord for", r.qType(q))
  206. return nil
  207. }
  208. }