zones.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. package main
  2. import (
  3. "camlistore.org/pkg/errorutil"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/abh/dns"
  7. "io/ioutil"
  8. "log"
  9. "net"
  10. "os"
  11. "path"
  12. "runtime/debug"
  13. "sort"
  14. "strconv"
  15. "strings"
  16. "time"
  17. )
  18. // Zones maps domain names to zone data
  19. type Zones map[string]*Zone
  20. func zonesReader(dirName string, zones Zones) {
  21. for {
  22. zonesReadDir(dirName, zones)
  23. time.Sleep(5 * time.Second)
  24. }
  25. }
  26. func addHandler(zones Zones, name string, config *Zone) {
  27. oldZone := zones[name]
  28. config.SetupMetrics(oldZone)
  29. zones[name] = config
  30. dns.HandleFunc(name, setupServerFunc(config))
  31. }
  32. func zonesReadDir(dirName string, zones Zones) error {
  33. dir, err := ioutil.ReadDir(dirName)
  34. if err != nil {
  35. log.Println("Could not read", dirName, ":", err)
  36. return err
  37. }
  38. seenZones := map[string]bool{}
  39. var parseErr error
  40. for _, file := range dir {
  41. fileName := file.Name()
  42. if !strings.HasSuffix(strings.ToLower(fileName), ".json") {
  43. continue
  44. }
  45. zoneName := zoneNameFromFile(fileName)
  46. seenZones[zoneName] = true
  47. if zone, ok := zones[zoneName]; !ok || file.ModTime().After(zone.LastRead) {
  48. if ok {
  49. log.Printf("Reloading %s\n", fileName)
  50. } else {
  51. logPrintf("Reading new file %s\n", fileName)
  52. }
  53. //log.Println("FILE:", i, file, zoneName)
  54. config, err := readZoneFile(zoneName, path.Join(dirName, fileName))
  55. if config == nil || err != nil {
  56. log.Println("Caught an error", err)
  57. if config == nil {
  58. config = new(Zone)
  59. }
  60. config.LastRead = file.ModTime()
  61. zones[zoneName] = config
  62. parseErr = err
  63. continue
  64. }
  65. config.LastRead = file.ModTime()
  66. addHandler(zones, zoneName, config)
  67. }
  68. }
  69. for zoneName, zone := range zones {
  70. if zoneName == "pgeodns" {
  71. continue
  72. }
  73. if ok, _ := seenZones[zoneName]; ok {
  74. continue
  75. }
  76. log.Println("Removing zone", zone.Origin)
  77. zone.Close()
  78. dns.HandleRemove(zoneName)
  79. delete(zones, zoneName)
  80. }
  81. return parseErr
  82. }
  83. func setupPgeodnsZone(zones Zones) {
  84. zoneName := "pgeodns"
  85. Zone := NewZone(zoneName)
  86. label := new(Label)
  87. label.Records = make(map[uint16]Records)
  88. label.Weight = make(map[uint16]int)
  89. Zone.Labels[""] = label
  90. setupSOA(Zone)
  91. addHandler(zones, zoneName, Zone)
  92. }
  93. func readZoneFile(zoneName, fileName string) (zone *Zone, zerr error) {
  94. defer func() {
  95. if r := recover(); r != nil {
  96. log.Printf("reading %s failed: %s", zoneName, r)
  97. debug.PrintStack()
  98. zerr = fmt.Errorf("reading %s failed: %s", zoneName, r)
  99. }
  100. }()
  101. fh, err := os.Open(fileName)
  102. if err != nil {
  103. log.Printf("Could not read '%s': %s", fileName, err)
  104. panic(err)
  105. }
  106. zone = NewZone(zoneName)
  107. fileInfo, err := fh.Stat()
  108. if err != nil {
  109. log.Printf("Could not stat '%s': %s", fileName, err)
  110. } else {
  111. zone.Options.Serial = int(fileInfo.ModTime().Unix())
  112. }
  113. var objmap map[string]interface{}
  114. decoder := json.NewDecoder(fh)
  115. if err = decoder.Decode(&objmap); err != nil {
  116. extra := ""
  117. if serr, ok := err.(*json.SyntaxError); ok {
  118. if _, serr := fh.Seek(0, os.SEEK_SET); serr != nil {
  119. log.Fatalf("seek error: %v", serr)
  120. }
  121. line, col, highlight := errorutil.HighlightBytePosition(fh, serr.Offset)
  122. extra = fmt.Sprintf(":\nError at line %d, column %d (file offset %d):\n%s",
  123. line, col, serr.Offset, highlight)
  124. }
  125. return nil, fmt.Errorf("error parsing JSON object in config file %s%s\n%v",
  126. fh.Name(), extra, err)
  127. }
  128. if err != nil {
  129. panic(err)
  130. }
  131. //log.Println(objmap)
  132. var data map[string]interface{}
  133. for k, v := range objmap {
  134. //log.Printf("k: %s v: %#v, T: %T\n", k, v, v)
  135. switch k {
  136. case "ttl", "serial", "max_hosts", "contact":
  137. switch option := k; option {
  138. case "ttl":
  139. zone.Options.Ttl = valueToInt(v)
  140. case "serial":
  141. zone.Options.Serial = valueToInt(v)
  142. case "contact":
  143. zone.Options.Contact = v.(string)
  144. case "max_hosts":
  145. zone.Options.MaxHosts = valueToInt(v)
  146. case "targeting":
  147. zone.Options.Targeting, err = parseTargets(v.(string))
  148. if err != nil {
  149. log.Printf("Could not parse targeting '%s': %s", v, err)
  150. return nil, err
  151. }
  152. }
  153. case "logging":
  154. {
  155. logging := new(ZoneLogging)
  156. for logger, v := range v.(map[string]interface{}) {
  157. switch logger {
  158. case "stathat":
  159. logging.StatHat = valueToBool(v)
  160. case "stathat_api":
  161. logging.StatHatAPI = valueToString(v)
  162. logging.StatHat = true
  163. default:
  164. log.Println("Unknown logger option", logger)
  165. }
  166. }
  167. zone.Logging = logging
  168. // log.Printf("logging options: %#v", logging)
  169. }
  170. continue
  171. case "data":
  172. data = v.(map[string]interface{})
  173. }
  174. }
  175. setupZoneData(data, zone)
  176. //log.Printf("ZO T: %T %s\n", Zones["0.us"], Zones["0.us"])
  177. //log.Println("IP", string(Zone.Regions["0.us"].IPv4[0].ip))
  178. switch {
  179. case zone.Options.Targeting >= TargetRegionGroup:
  180. geoIP.setupGeoIPCity()
  181. case zone.Options.Targeting >= TargetContinent:
  182. geoIP.setupGeoIPCountry()
  183. }
  184. return zone, nil
  185. }
  186. func setupZoneData(data map[string]interface{}, Zone *Zone) {
  187. recordTypes := map[string]uint16{
  188. "a": dns.TypeA,
  189. "aaaa": dns.TypeAAAA,
  190. "alias": dns.TypeMF,
  191. "cname": dns.TypeCNAME,
  192. "mx": dns.TypeMX,
  193. "ns": dns.TypeNS,
  194. "txt": dns.TypeTXT,
  195. }
  196. for dk, dv_inter := range data {
  197. dv := dv_inter.(map[string]interface{})
  198. //log.Printf("K %s V %s TYPE-V %T\n", dk, dv, dv)
  199. label := Zone.AddLabel(dk)
  200. for rType, rdata := range dv {
  201. switch rType {
  202. case "max_hosts":
  203. label.MaxHosts = valueToInt(rdata)
  204. continue
  205. case "ttl":
  206. label.Ttl = valueToInt(rdata)
  207. continue
  208. }
  209. dnsType, ok := recordTypes[rType]
  210. if !ok {
  211. log.Printf("Unsupported record type '%s'\n", rType)
  212. continue
  213. }
  214. if rdata == nil {
  215. //log.Printf("No %s records for label %s\n", rType, dk)
  216. continue
  217. }
  218. //log.Printf("rdata %s TYPE-R %T\n", rdata, rdata)
  219. records := make(map[string][]interface{})
  220. switch rdata.(type) {
  221. case map[string]interface{}:
  222. // Handle NS map syntax, map[ns2.example.net:<nil> ns1.example.net:<nil>]
  223. tmp := make([]interface{}, 0)
  224. for rdataK, rdataV := range rdata.(map[string]interface{}) {
  225. if rdataV == nil {
  226. rdataV = ""
  227. }
  228. tmp = append(tmp, []string{rdataK, rdataV.(string)})
  229. }
  230. records[rType] = tmp
  231. case string:
  232. // CNAME and alias
  233. tmp := make([]interface{}, 1)
  234. tmp[0] = rdata.(string)
  235. records[rType] = tmp
  236. default:
  237. records[rType] = rdata.([]interface{})
  238. }
  239. //log.Printf("RECORDS %s TYPE-REC %T\n", Records, Records)
  240. label.Records[dnsType] = make(Records, len(records[rType]))
  241. for i := 0; i < len(records[rType]); i++ {
  242. //log.Printf("RT %T %#v\n", records[rType][i], records[rType][i])
  243. record := new(Record)
  244. var h dns.RR_Header
  245. // log.Println("TTL OPTIONS", Zone.Options.Ttl)
  246. h.Ttl = uint32(label.Ttl)
  247. h.Class = dns.ClassINET
  248. h.Rrtype = dnsType
  249. h.Name = label.Label + "." + Zone.Origin + "."
  250. switch dnsType {
  251. case dns.TypeA, dns.TypeAAAA:
  252. rec := records[rType][i].([]interface{})
  253. ip := rec[0].(string)
  254. var err error
  255. if len(rec) > 1 {
  256. switch rec[1].(type) {
  257. case string:
  258. record.Weight, err = strconv.Atoi(rec[1].(string))
  259. if err != nil {
  260. panic("Error converting weight to integer")
  261. }
  262. case float64:
  263. record.Weight = int(rec[1].(float64))
  264. }
  265. }
  266. switch dnsType {
  267. case dns.TypeA:
  268. if x := net.ParseIP(ip); x != nil {
  269. record.RR = &dns.A{Hdr: h, A: x}
  270. break
  271. }
  272. panic(fmt.Errorf("Bad A record %s for %s", ip, dk))
  273. case dns.TypeAAAA:
  274. if x := net.ParseIP(ip); x != nil {
  275. record.RR = &dns.AAAA{Hdr: h, AAAA: x}
  276. break
  277. }
  278. panic(fmt.Errorf("Bad AAAA record %s for %s", ip, dk))
  279. }
  280. case dns.TypeMX:
  281. rec := records[rType][i].(map[string]interface{})
  282. pref := uint16(0)
  283. mx := rec["mx"].(string)
  284. if !strings.HasSuffix(mx, ".") {
  285. mx = mx + "."
  286. }
  287. if rec["weight"] != nil {
  288. record.Weight = valueToInt(rec["weight"])
  289. }
  290. if rec["preference"] != nil {
  291. pref = uint16(valueToInt(rec["preference"]))
  292. }
  293. record.RR = &dns.MX{
  294. Hdr: h,
  295. Mx: mx,
  296. Preference: pref}
  297. case dns.TypeCNAME:
  298. rec := records[rType][i]
  299. target := rec.(string)
  300. if !dns.IsFqdn(target) {
  301. target = target + "." + Zone.Origin
  302. }
  303. record.RR = &dns.CNAME{Hdr: h, Target: dns.Fqdn(target)}
  304. case dns.TypeMF:
  305. rec := records[rType][i]
  306. // MF records (how we store aliases) are not FQDNs
  307. record.RR = &dns.MF{Hdr: h, Mf: rec.(string)}
  308. case dns.TypeNS:
  309. rec := records[rType][i]
  310. if h.Ttl < 86400 {
  311. h.Ttl = 86400
  312. }
  313. var ns string
  314. switch rec.(type) {
  315. case string:
  316. ns = rec.(string)
  317. case []string:
  318. recl := rec.([]string)
  319. ns = recl[0]
  320. if len(recl[1]) > 0 {
  321. log.Println("NS records with names syntax not supported")
  322. }
  323. default:
  324. log.Printf("Data: %T %#v\n", rec, rec)
  325. panic("Unrecognized NS format/syntax")
  326. }
  327. rr := &dns.NS{Hdr: h, Ns: dns.Fqdn(ns)}
  328. record.RR = rr
  329. case dns.TypeTXT:
  330. rec := records[rType][i]
  331. var txt string
  332. switch rec.(type) {
  333. case string:
  334. txt = rec.(string)
  335. case map[string]interface{}:
  336. recmap := rec.(map[string]interface{})
  337. if weight, ok := recmap["weight"]; ok {
  338. record.Weight = valueToInt(weight)
  339. }
  340. if t, ok := recmap["txt"]; ok {
  341. txt = t.(string)
  342. }
  343. }
  344. if len(txt) > 0 {
  345. rr := &dns.TXT{Hdr: h, Txt: []string{txt}}
  346. record.RR = rr
  347. } else {
  348. log.Printf("Zero length txt record for '%s' in '%s'\n", label.Label, Zone.Origin)
  349. continue
  350. }
  351. default:
  352. log.Println("type:", rType)
  353. panic("Don't know how to handle this type")
  354. }
  355. if record.RR == nil {
  356. panic("record.RR is nil")
  357. }
  358. label.Weight[dnsType] += record.Weight
  359. label.Records[dnsType][i] = *record
  360. }
  361. if label.Weight[dnsType] > 0 {
  362. sort.Sort(RecordsByWeight{label.Records[dnsType]})
  363. }
  364. }
  365. }
  366. setupSOA(Zone)
  367. //log.Println(Zones[k])
  368. }
  369. func setupSOA(Zone *Zone) {
  370. label := Zone.Labels[""]
  371. primaryNs := "ns"
  372. // log.Println("LABEL", label)
  373. if label == nil {
  374. log.Println(Zone.Origin, "doesn't have any 'root' records,",
  375. "you should probably add some NS records")
  376. label = Zone.AddLabel("")
  377. }
  378. if record, ok := label.Records[dns.TypeNS]; ok {
  379. primaryNs = record[0].RR.(*dns.NS).Ns
  380. }
  381. ttl := Zone.Options.Ttl * 10
  382. if ttl > 3600 {
  383. ttl = 3600
  384. }
  385. if ttl == 0 {
  386. ttl = 600
  387. }
  388. s := Zone.Origin + ". " + strconv.Itoa(ttl) + " IN SOA " +
  389. primaryNs + " " + Zone.Options.Contact + " " +
  390. strconv.Itoa(Zone.Options.Serial) +
  391. // refresh, retry, expire, minimum are all
  392. // meaningless with this implementation
  393. " 5400 5400 1209600 3600"
  394. // log.Println("SOA: ", s)
  395. rr, err := dns.NewRR(s)
  396. if err != nil {
  397. log.Println("SOA Error", err)
  398. panic("Could not setup SOA")
  399. }
  400. record := Record{RR: rr}
  401. label.Records[dns.TypeSOA] = make([]Record, 1)
  402. label.Records[dns.TypeSOA][0] = record
  403. }
  404. func valueToBool(v interface{}) (rv bool) {
  405. switch v.(type) {
  406. case bool:
  407. rv = v.(bool)
  408. case string:
  409. str := v.(string)
  410. switch str {
  411. case "true":
  412. rv = true
  413. case "1":
  414. rv = true
  415. }
  416. case float64:
  417. if v.(float64) > 0 {
  418. rv = true
  419. }
  420. default:
  421. log.Println("Can't convert", v, "to bool")
  422. panic("Can't convert value")
  423. }
  424. return rv
  425. }
  426. func valueToString(v interface{}) (rv string) {
  427. switch v.(type) {
  428. case string:
  429. rv = v.(string)
  430. case float64:
  431. rv = strconv.FormatFloat(v.(float64), 'f', -1, 64)
  432. default:
  433. log.Println("Can't convert", v, "to string")
  434. panic("Can't convert value")
  435. }
  436. return rv
  437. }
  438. func valueToInt(v interface{}) (rv int) {
  439. switch v.(type) {
  440. case string:
  441. i, err := strconv.Atoi(v.(string))
  442. if err != nil {
  443. panic("Error converting weight to integer")
  444. }
  445. rv = i
  446. case float64:
  447. rv = int(v.(float64))
  448. default:
  449. log.Println("Can't convert", v, "to integer")
  450. panic("Can't convert value")
  451. }
  452. return rv
  453. }
  454. func zoneNameFromFile(fileName string) string {
  455. return fileName[0:strings.LastIndex(fileName, ".")]
  456. }