dnsapi.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. //////////////////////////////////////////////////////////////////////////
  2. // DN42 Registry API Server
  3. //////////////////////////////////////////////////////////////////////////
  4. package main
  5. //////////////////////////////////////////////////////////////////////////
  6. import (
  7. "fmt"
  8. "github.com/gorilla/mux"
  9. log "github.com/sirupsen/logrus"
  10. // "math/big"
  11. "net/http"
  12. "strings"
  13. "time"
  14. )
  15. //////////////////////////////////////////////////////////////////////////
  16. // register the api
  17. func init() {
  18. EventBus.Listen("APIEndpoint", InitDNSAPI)
  19. EventBus.Listen("RegistryUpdate", DNSUpdate)
  20. }
  21. //////////////////////////////////////////////////////////////////////////
  22. // data model
  23. // very simple DNS record data
  24. type DNSRecord struct {
  25. Name string
  26. Type string
  27. Content string
  28. Comment string `json:",omitempty"`
  29. }
  30. type DNSZone struct {
  31. Records []*DNSRecord
  32. Commit string
  33. Generated time.Time
  34. }
  35. var DNSRootZone *DNSZone
  36. //////////////////////////////////////////////////////////////////////////
  37. // fixed set of authoritative zones
  38. var DNSRootAuthZones = map[string]string{
  39. "dn42": "domain/dn42",
  40. "recursive-servers.dn42": "domain/recursive-servers.dn42",
  41. "delegation-servers.dn42": "domain/delegation-servers.dn42",
  42. "d.f.ip6.arpa": "inet6num/fd00::_8",
  43. "20.172.in-addr.arpa": "inetnum/172.20.0.0_16",
  44. "21.172.in-addr.arpa": "inetnum/172.21.0.0_16",
  45. "22.172.in-addr.arpa": "inetnum/172.22.0.0_16",
  46. "23.172.in-addr.arpa": "inetnum/172.23.0.0_16",
  47. "31.172.in-addr.arpa": "inetnum/172.31.0.0_16",
  48. "10.in-addr.arpa": "inetnum/10.0.0.0_8",
  49. }
  50. //////////////////////////////////////////////////////////////////////////
  51. // called from main to initialise the API routing
  52. func InitDNSAPI(params ...interface{}) {
  53. router := params[0].(*mux.Router)
  54. s := router.
  55. Methods("GET").
  56. PathPrefix("/dns").
  57. Subrouter()
  58. s.HandleFunc("/root-zone", dnsRZoneHandler)
  59. log.Info("DNS API installed")
  60. }
  61. //////////////////////////////////////////////////////////////////////////
  62. // api handlers
  63. // return records that should be included in a DN42 root zone
  64. func dnsRZoneHandler(w http.ResponseWriter, r *http.Request) {
  65. var format []string
  66. query := r.URL.Query()
  67. format = query["format"]
  68. if format == nil || len(format) != 1 {
  69. format = []string{"json"}
  70. }
  71. switch format[0] {
  72. case "bind":
  73. DNSRootZone.WriteBindFormat(w)
  74. case "json":
  75. ResponseJSON(w, DNSRootZone)
  76. default:
  77. ResponseJSON(w, DNSRootZone)
  78. }
  79. }
  80. //////////////////////////////////////////////////////////////////////////
  81. // called whenever the registry is updated
  82. func DNSUpdate(params ...interface{}) {
  83. registry := params[0].(*Registry)
  84. // path := params[1].(string)
  85. zone := &DNSZone{
  86. Generated: time.Now(),
  87. Commit: registry.Commit,
  88. }
  89. // add zones that are authoritative within DN42
  90. for name, object := range DNSRootAuthZones {
  91. zone.AddRecords(registry, name, object, "DN42 Authoritative Zone")
  92. }
  93. // search all domain objects and add stub records for each TLD
  94. rtype := registry.Types["domain"]
  95. for name, object := range rtype.Objects {
  96. // domain is a TLD if it doesn't contain a '.'
  97. if strings.IndexRune(name, '.') == -1 {
  98. // don't include zones which are authoritative within DN42
  99. if DNSRootAuthZones[name] == "" {
  100. zone.AddRecords(registry, name, object.Ref, "Forward Zone")
  101. }
  102. }
  103. }
  104. DNSRootZone = zone
  105. }
  106. //////////////////////////////////////////////////////////////////////////
  107. // utility function to add a DNS record to a zone
  108. func (zone *DNSZone) AddRecord(name string, t string,
  109. content string, comment string) {
  110. record := &DNSRecord{
  111. Name: name,
  112. Type: t,
  113. Content: content,
  114. Comment: comment,
  115. }
  116. zone.Records = append(zone.Records, record)
  117. }
  118. //////////////////////////////////////////////////////////////////////////
  119. // add nserver and ds-rdata records from a registry object
  120. func (zone *DNSZone) AddRecords(registry *Registry, name string,
  121. path string, comment string) {
  122. // use the registry metadata key index to find the appropriate values
  123. object := registry.GetObject(path)
  124. if object == nil {
  125. log.WithFields(log.Fields{
  126. "zone": name,
  127. "path": path,
  128. }).Error("DNS: unable to find object in registry")
  129. return
  130. }
  131. nserver := object.GetKey("nserver")
  132. for _, ns := range nserver {
  133. // check if stub record needs to be added
  134. fields := strings.Split(ns.RawValue, " ")
  135. if len(fields) == 2 {
  136. // add a record for the NS, together with a stub A or AAAA record
  137. var stubtype string
  138. if strings.IndexRune(fields[1], ':') == -1 {
  139. // no : so IPv4
  140. stubtype = "A"
  141. } else {
  142. // has : so IPv6
  143. stubtype = "AAAA"
  144. }
  145. zone.AddRecord(name, "NS", fields[0]+".", comment)
  146. zone.AddRecord(fields[0], stubtype, fields[1], comment)
  147. } else {
  148. // no, just add an NS record as it was presented
  149. zone.AddRecord(name, "NS", ns.RawValue+".", comment)
  150. }
  151. }
  152. dsrdata := object.GetKey("ds-rdata")
  153. for _, ds := range dsrdata {
  154. zone.AddRecord(name, "DS", ds.RawValue, comment)
  155. }
  156. }
  157. //////////////////////////////////////////////////////////////////////////
  158. // Functions for outputting zone records in different formats
  159. func (r *DNSRecord) ToBindString() string {
  160. var comment string
  161. if r.Comment == "" {
  162. comment = ""
  163. } else {
  164. comment = "\t; " + r.Comment
  165. }
  166. return fmt.Sprintf("%s\tIN\t%s\t%s%s",
  167. r.Name, r.Type, r.Content, comment,
  168. )
  169. }
  170. func (zone *DNSZone) WriteBindFormat(w http.ResponseWriter) {
  171. w.Header().Set("Content-Type", "text/plain")
  172. w.Header().Set("Access-Control-Allow-Origin", "*")
  173. // provide a header
  174. fmt.Fprintf(w, ";; DN42 Root Zone Records\n"+
  175. ";; Commit Reference: %s\n;; Generated: %s\n",
  176. zone.Commit, zone.Generated)
  177. // then simply output each record in turn
  178. for _, record := range zone.Records {
  179. fmt.Fprintln(w, record.ToBindString())
  180. }
  181. }
  182. //////////////////////////////////////////////////////////////////////////
  183. // end of code