config.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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. if err == nil {
  88. var objmap map[string]interface{}
  89. decoder := json.NewDecoder(fh)
  90. err := decoder.Decode(&objmap)
  91. if err != nil {
  92. panic(err)
  93. }
  94. //log.Println(objmap)
  95. var data map[string]interface{}
  96. for k, v := range objmap {
  97. //log.Printf("k: %s v: %#v, T: %T\n", k, v, v)
  98. switch k {
  99. case "ttl", "serial", "max_hosts":
  100. switch option := k; option {
  101. case "ttl":
  102. Zone.Options.Ttl = valueToInt(v)
  103. case "serial":
  104. Zone.Options.Serial = valueToInt(v)
  105. case "max_hosts":
  106. Zone.Options.MaxHosts = valueToInt(v)
  107. }
  108. continue
  109. case "data":
  110. data = v.(map[string]interface{})
  111. }
  112. }
  113. setupZoneData(data, Zone)
  114. }
  115. //log.Printf("ZO T: %T %s\n", Zones["0.us"], Zones["0.us"])
  116. //log.Println("IP", string(Zone.Regions["0.us"].IPv4[0].ip))
  117. return Zone, nil
  118. }
  119. func setupZoneData(data map[string]interface{}, Zone *Zone) {
  120. recordTypes := map[string]uint16{
  121. "a": dns.TypeA,
  122. "aaaa": dns.TypeAAAA,
  123. "ns": dns.TypeNS,
  124. "cname": dns.TypeCNAME,
  125. "mx": dns.TypeMX,
  126. "alias": dns.TypeMF,
  127. }
  128. for dk, dv_inter := range data {
  129. dv := dv_inter.(map[string]interface{})
  130. //log.Printf("K %s V %s TYPE-V %T\n", dk, dv, dv)
  131. dk = strings.ToLower(dk)
  132. Zone.Labels[dk] = new(Label)
  133. label := Zone.Labels[dk]
  134. label.Label = dk
  135. label.Ttl = Zone.Options.Ttl
  136. label.MaxHosts = Zone.Options.MaxHosts
  137. if ttl, ok := dv["ttl"]; ok {
  138. label.Ttl = valueToInt(ttl)
  139. }
  140. if maxHosts, ok := dv["max_hosts"]; ok {
  141. label.MaxHosts = valueToInt(maxHosts)
  142. }
  143. for rType, dnsType := range recordTypes {
  144. rdata := dv[rType]
  145. if rdata == nil {
  146. //log.Printf("No %s records for label %s\n", rType, dk)
  147. continue
  148. }
  149. //log.Printf("rdata %s TYPE-R %T\n", rdata, rdata)
  150. records := make(map[string][]interface{})
  151. switch rdata.(type) {
  152. case map[string]interface{}:
  153. // Handle NS map syntax, map[ns2.example.net:<nil> ns1.example.net:<nil>]
  154. tmp := make([]interface{}, 0)
  155. for rdata_k, rdata_v := range rdata.(map[string]interface{}) {
  156. if rdata_v == nil {
  157. rdata_v = ""
  158. }
  159. tmp = append(tmp, []string{rdata_k, rdata_v.(string)})
  160. }
  161. records[rType] = tmp
  162. case string:
  163. // CNAME and alias
  164. tmp := make([]interface{}, 1)
  165. tmp[0] = rdata.(string)
  166. records[rType] = tmp
  167. default:
  168. records[rType] = rdata.([]interface{})
  169. }
  170. //log.Printf("RECORDS %s TYPE-REC %T\n", Records, Records)
  171. if label.Records == nil {
  172. label.Records = make(map[uint16]Records)
  173. label.Weight = make(map[uint16]int)
  174. }
  175. label.Records[dnsType] = make(Records, len(records[rType]))
  176. for i := 0; i < len(records[rType]); i++ {
  177. //log.Printf("RT %T %#v\n", records[rType][i], records[rType][i])
  178. record := new(Record)
  179. var h dns.RR_Header
  180. // log.Println("TTL OPTIONS", Zone.Options.Ttl)
  181. h.Ttl = uint32(label.Ttl)
  182. h.Class = dns.ClassINET
  183. h.Rrtype = dnsType
  184. h.Name = label.Label + "." + Zone.Origin + "."
  185. switch dnsType {
  186. case dns.TypeA, dns.TypeAAAA:
  187. rec := records[rType][i].([]interface{})
  188. ip := rec[0].(string)
  189. var err error
  190. switch rec[1].(type) {
  191. case string:
  192. record.Weight, err = strconv.Atoi(rec[1].(string))
  193. if err != nil {
  194. panic("Error converting weight to integer")
  195. }
  196. case float64:
  197. record.Weight = int(rec[1].(float64))
  198. }
  199. switch dnsType {
  200. case dns.TypeA:
  201. if x := net.ParseIP(ip); x != nil {
  202. record.RR = &dns.RR_A{Hdr: h, A: x}
  203. break
  204. }
  205. panic("Bad A record")
  206. case dns.TypeAAAA:
  207. if x := net.ParseIP(ip); x != nil {
  208. record.RR = &dns.RR_AAAA{Hdr: h, AAAA: x}
  209. break
  210. }
  211. panic("Bad AAAA record")
  212. }
  213. case dns.TypeMX:
  214. rec := records[rType][i].(map[string]interface{})
  215. pref := uint16(0)
  216. mx := rec["mx"].(string)
  217. if !strings.HasSuffix(mx, ".") {
  218. mx = mx + "."
  219. }
  220. if rec["weight"] != nil {
  221. record.Weight = valueToInt(rec["weight"])
  222. }
  223. if rec["preference"] != nil {
  224. pref = uint16(valueToInt(rec["preference"]))
  225. }
  226. record.RR = &dns.RR_MX{
  227. Hdr: h,
  228. Mx: mx,
  229. Pref: pref}
  230. case dns.TypeCNAME:
  231. rec := records[rType][i]
  232. record.RR = &dns.RR_CNAME{Hdr: h, Target: dns.Fqdn(rec.(string))}
  233. case dns.TypeMF:
  234. rec := records[rType][i]
  235. // MF records (how we store aliases) are not FQDNs
  236. record.RR = &dns.RR_MF{Hdr: h, Mf: rec.(string)}
  237. case dns.TypeNS:
  238. rec := records[rType][i]
  239. if h.Ttl < 86400 {
  240. h.Ttl = 86400
  241. }
  242. var ns string
  243. switch rec.(type) {
  244. case string:
  245. ns = rec.(string)
  246. case []string:
  247. recl := rec.([]string)
  248. ns = recl[0]
  249. if len(recl[1]) > 0 {
  250. log.Println("NS records with names syntax not supported")
  251. }
  252. default:
  253. log.Printf("Data: %T %#v\n", rec, rec)
  254. panic("Unrecognized NS format/syntax")
  255. }
  256. rr := &dns.RR_NS{Hdr: h, Ns: dns.Fqdn(ns)}
  257. record.RR = rr
  258. default:
  259. log.Println("type:", rType)
  260. panic("Don't know how to handle this type")
  261. }
  262. if record.RR == nil {
  263. panic("record.RR is nil")
  264. }
  265. label.Weight[dnsType] += record.Weight
  266. label.Records[dnsType][i] = *record
  267. }
  268. if label.Weight[dnsType] > 0 {
  269. sort.Sort(RecordsByWeight{label.Records[dnsType]})
  270. }
  271. }
  272. }
  273. setupSOA(Zone)
  274. //log.Println(Zones[k])
  275. }
  276. func setupSOA(Zone *Zone) {
  277. label := Zone.Labels[""]
  278. primaryNs := "ns"
  279. if record, ok := label.Records[dns.TypeNS]; ok {
  280. primaryNs = record[0].RR.(*dns.RR_NS).Ns
  281. }
  282. s := Zone.Origin + ". 3600 IN SOA " +
  283. primaryNs + " support.bitnames.com. " +
  284. strconv.Itoa(Zone.Options.Serial) +
  285. " 5400 5400 2419200 " +
  286. strconv.Itoa(Zone.Options.Ttl)
  287. log.Println("SOA: ", s)
  288. rr, err := dns.NewRR(s)
  289. if err != nil {
  290. log.Println("SOA Error", err)
  291. panic("Could not setup SOA")
  292. }
  293. record := Record{RR: rr}
  294. label.Records[dns.TypeSOA] = make([]Record, 1)
  295. label.Records[dns.TypeSOA][0] = record
  296. }
  297. func valueToInt(v interface{}) (rv int) {
  298. switch v.(type) {
  299. case string:
  300. i, err := strconv.Atoi(v.(string))
  301. if err != nil {
  302. panic("Error converting weight to integer")
  303. }
  304. rv = i
  305. case float64:
  306. rv = int(v.(float64))
  307. default:
  308. log.Println("Can't convert", v, "to integer")
  309. panic("Can't convert value")
  310. }
  311. return rv
  312. }