config.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. package main
  2. import (
  3. "encoding/json"
  4. "github.com/miekg/dns"
  5. "io/ioutil"
  6. "log"
  7. "net"
  8. "os"
  9. "path"
  10. "runtime"
  11. "runtime/debug"
  12. "sort"
  13. "strconv"
  14. "strings"
  15. "time"
  16. )
  17. var configLastRead = map[string]time.Time{}
  18. func configReader(dirName string, Zones Zones) {
  19. for {
  20. configReadDir(dirName, Zones)
  21. time.Sleep(5 * time.Second)
  22. }
  23. }
  24. func configReadDir(dirName string, Zones Zones) {
  25. dir, err := ioutil.ReadDir(dirName)
  26. if err != nil {
  27. panic(err)
  28. }
  29. seenFiles := map[string]bool{}
  30. for _, file := range dir {
  31. fileName := file.Name()
  32. if !strings.HasSuffix(strings.ToLower(fileName), ".json") {
  33. continue
  34. }
  35. seenFiles[fileName] = true
  36. if lastRead, ok := configLastRead[fileName]; !ok || file.ModTime().After(lastRead) {
  37. log.Println("Updated file, going to read", fileName)
  38. configLastRead[fileName] = file.ModTime()
  39. zoneName := fileName[0:strings.LastIndex(fileName, ".")]
  40. //log.Println("FILE:", i, file, zoneName)
  41. runtime.GC()
  42. config, err := readZoneFile(zoneName, path.Join(dirName, fileName))
  43. if config == nil || err != nil {
  44. log.Println("error reading file: ", err)
  45. }
  46. if config != nil && err == nil {
  47. Zones[zoneName] = config
  48. dns.HandleFunc(zoneName, setupServerFunc(config))
  49. runtime.GC()
  50. }
  51. }
  52. // TODO(ask) Disable zones not seen in two subsequent runs
  53. }
  54. }
  55. func setupPgeodnsZone(Zones Zones) {
  56. zoneName := "pgeodns"
  57. Zone := new(Zone)
  58. Zone.Labels = make(labels)
  59. Zone.Origin = zoneName
  60. Zone.LenLabels = dns.LenLabels(Zone.Origin)
  61. label := new(Label)
  62. label.Records = make(map[uint16]Records)
  63. label.Weight = make(map[uint16]int)
  64. Zone.Labels[""] = label
  65. setupSOA(Zone)
  66. Zones[zoneName] = Zone
  67. dns.HandleFunc(zoneName, setupServerFunc(Zone))
  68. }
  69. func readZoneFile(zoneName, fileName string) (*Zone, error) {
  70. defer func() {
  71. if err := recover(); err != nil {
  72. log.Printf("reading %s failed: %s", zoneName, err)
  73. debug.PrintStack()
  74. }
  75. }()
  76. fh, err := os.Open(fileName)
  77. if err != nil {
  78. log.Println("Could not read ", fileName, ": ", err)
  79. panic(err)
  80. }
  81. Zone := new(Zone)
  82. Zone.Labels = make(labels)
  83. Zone.Origin = zoneName
  84. Zone.LenLabels = dns.LenLabels(Zone.Origin)
  85. Zone.Options.Ttl = 120
  86. Zone.Options.MaxHosts = 2
  87. Zone.Options.Contact = "support.bitnames.com"
  88. if err == nil {
  89. var objmap map[string]interface{}
  90. decoder := json.NewDecoder(fh)
  91. err := decoder.Decode(&objmap)
  92. if err != nil {
  93. panic(err)
  94. }
  95. //log.Println(objmap)
  96. var data map[string]interface{}
  97. for k, v := range objmap {
  98. //log.Printf("k: %s v: %#v, T: %T\n", k, v, v)
  99. switch k {
  100. case "ttl", "serial", "max_hosts", "contact":
  101. switch option := k; option {
  102. case "ttl":
  103. Zone.Options.Ttl = valueToInt(v)
  104. case "serial":
  105. Zone.Options.Serial = valueToInt(v)
  106. case "contact":
  107. Zone.Options.Contact = v.(string)
  108. case "max_hosts":
  109. Zone.Options.MaxHosts = valueToInt(v)
  110. }
  111. continue
  112. case "data":
  113. data = v.(map[string]interface{})
  114. }
  115. }
  116. setupZoneData(data, Zone)
  117. }
  118. //log.Printf("ZO T: %T %s\n", Zones["0.us"], Zones["0.us"])
  119. //log.Println("IP", string(Zone.Regions["0.us"].IPv4[0].ip))
  120. return Zone, nil
  121. }
  122. func setupZoneData(data map[string]interface{}, Zone *Zone) {
  123. recordTypes := map[string]uint16{
  124. "a": dns.TypeA,
  125. "aaaa": dns.TypeAAAA,
  126. "ns": dns.TypeNS,
  127. "cname": dns.TypeCNAME,
  128. "mx": dns.TypeMX,
  129. "alias": dns.TypeMF,
  130. }
  131. for dk, dv_inter := range data {
  132. dv := dv_inter.(map[string]interface{})
  133. //log.Printf("K %s V %s TYPE-V %T\n", dk, dv, dv)
  134. dk = strings.ToLower(dk)
  135. Zone.Labels[dk] = new(Label)
  136. label := Zone.Labels[dk]
  137. label.Label = dk
  138. label.Ttl = Zone.Options.Ttl
  139. label.MaxHosts = Zone.Options.MaxHosts
  140. if ttl, ok := dv["ttl"]; ok {
  141. label.Ttl = valueToInt(ttl)
  142. }
  143. if maxHosts, ok := dv["max_hosts"]; ok {
  144. label.MaxHosts = valueToInt(maxHosts)
  145. }
  146. for rType, dnsType := range recordTypes {
  147. rdata := dv[rType]
  148. if rdata == nil {
  149. //log.Printf("No %s records for label %s\n", rType, dk)
  150. continue
  151. }
  152. //log.Printf("rdata %s TYPE-R %T\n", rdata, rdata)
  153. records := make(map[string][]interface{})
  154. switch rdata.(type) {
  155. case map[string]interface{}:
  156. // Handle NS map syntax, map[ns2.example.net:<nil> ns1.example.net:<nil>]
  157. tmp := make([]interface{}, 0)
  158. for rdata_k, rdata_v := range rdata.(map[string]interface{}) {
  159. if rdata_v == nil {
  160. rdata_v = ""
  161. }
  162. tmp = append(tmp, []string{rdata_k, rdata_v.(string)})
  163. }
  164. records[rType] = tmp
  165. case string:
  166. // CNAME and alias
  167. tmp := make([]interface{}, 1)
  168. tmp[0] = rdata.(string)
  169. records[rType] = tmp
  170. default:
  171. records[rType] = rdata.([]interface{})
  172. }
  173. //log.Printf("RECORDS %s TYPE-REC %T\n", Records, Records)
  174. if label.Records == nil {
  175. label.Records = make(map[uint16]Records)
  176. label.Weight = make(map[uint16]int)
  177. }
  178. label.Records[dnsType] = make(Records, len(records[rType]))
  179. for i := 0; i < len(records[rType]); i++ {
  180. //log.Printf("RT %T %#v\n", records[rType][i], records[rType][i])
  181. record := new(Record)
  182. var h dns.RR_Header
  183. // log.Println("TTL OPTIONS", Zone.Options.Ttl)
  184. h.Ttl = uint32(label.Ttl)
  185. h.Class = dns.ClassINET
  186. h.Rrtype = dnsType
  187. h.Name = label.Label + "." + Zone.Origin + "."
  188. switch dnsType {
  189. case dns.TypeA, dns.TypeAAAA:
  190. rec := records[rType][i].([]interface{})
  191. ip := rec[0].(string)
  192. var err error
  193. switch rec[1].(type) {
  194. case string:
  195. record.Weight, err = strconv.Atoi(rec[1].(string))
  196. if err != nil {
  197. panic("Error converting weight to integer")
  198. }
  199. case float64:
  200. record.Weight = int(rec[1].(float64))
  201. }
  202. switch dnsType {
  203. case dns.TypeA:
  204. if x := net.ParseIP(ip); x != nil {
  205. record.RR = &dns.A{Hdr: h, A: x}
  206. break
  207. }
  208. panic("Bad A record")
  209. case dns.TypeAAAA:
  210. if x := net.ParseIP(ip); x != nil {
  211. record.RR = &dns.AAAA{Hdr: h, AAAA: x}
  212. break
  213. }
  214. panic("Bad AAAA record")
  215. }
  216. case dns.TypeMX:
  217. rec := records[rType][i].(map[string]interface{})
  218. pref := uint16(0)
  219. mx := rec["mx"].(string)
  220. if !strings.HasSuffix(mx, ".") {
  221. mx = mx + "."
  222. }
  223. if rec["weight"] != nil {
  224. record.Weight = valueToInt(rec["weight"])
  225. }
  226. if rec["preference"] != nil {
  227. pref = uint16(valueToInt(rec["preference"]))
  228. }
  229. record.RR = &dns.MX{
  230. Hdr: h,
  231. Mx: mx,
  232. Preference: pref}
  233. case dns.TypeCNAME:
  234. rec := records[rType][i]
  235. target := rec.(string)
  236. if !dns.IsFqdn(target) {
  237. target = target + "." + Zone.Origin
  238. }
  239. record.RR = &dns.CNAME{Hdr: h, Target: dns.Fqdn(target)}
  240. case dns.TypeMF:
  241. rec := records[rType][i]
  242. // MF records (how we store aliases) are not FQDNs
  243. record.RR = &dns.MF{Hdr: h, Mf: rec.(string)}
  244. case dns.TypeNS:
  245. rec := records[rType][i]
  246. if h.Ttl < 86400 {
  247. h.Ttl = 86400
  248. }
  249. var ns string
  250. switch rec.(type) {
  251. case string:
  252. ns = rec.(string)
  253. case []string:
  254. recl := rec.([]string)
  255. ns = recl[0]
  256. if len(recl[1]) > 0 {
  257. log.Println("NS records with names syntax not supported")
  258. }
  259. default:
  260. log.Printf("Data: %T %#v\n", rec, rec)
  261. panic("Unrecognized NS format/syntax")
  262. }
  263. rr := &dns.NS{Hdr: h, Ns: dns.Fqdn(ns)}
  264. record.RR = rr
  265. default:
  266. log.Println("type:", rType)
  267. panic("Don't know how to handle this type")
  268. }
  269. if record.RR == nil {
  270. panic("record.RR is nil")
  271. }
  272. label.Weight[dnsType] += record.Weight
  273. label.Records[dnsType][i] = *record
  274. }
  275. if label.Weight[dnsType] > 0 {
  276. sort.Sort(RecordsByWeight{label.Records[dnsType]})
  277. }
  278. }
  279. }
  280. setupSOA(Zone)
  281. //log.Println(Zones[k])
  282. }
  283. func setupSOA(Zone *Zone) {
  284. label := Zone.Labels[""]
  285. primaryNs := "ns"
  286. if record, ok := label.Records[dns.TypeNS]; ok {
  287. primaryNs = record[0].RR.(*dns.NS).Ns
  288. }
  289. s := Zone.Origin + ". 3600 IN SOA " +
  290. primaryNs + " " + Zone.Options.Contact + " " +
  291. strconv.Itoa(Zone.Options.Serial) +
  292. " 5400 5400 2419200 " +
  293. strconv.Itoa(Zone.Options.Ttl)
  294. log.Println("SOA: ", s)
  295. rr, err := dns.NewRR(s)
  296. if err != nil {
  297. log.Println("SOA Error", err)
  298. panic("Could not setup SOA")
  299. }
  300. record := Record{RR: rr}
  301. label.Records[dns.TypeSOA] = make([]Record, 1)
  302. label.Records[dns.TypeSOA][0] = record
  303. }
  304. func valueToInt(v interface{}) (rv int) {
  305. switch v.(type) {
  306. case string:
  307. i, err := strconv.Atoi(v.(string))
  308. if err != nil {
  309. panic("Error converting weight to integer")
  310. }
  311. rv = i
  312. case float64:
  313. rv = int(v.(float64))
  314. default:
  315. log.Println("Can't convert", v, "to integer")
  316. panic("Can't convert value")
  317. }
  318. return rv
  319. }