geodns.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. package main
  2. /*
  3. Copyright 2012-2015 Ask Bjørn Hansen
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. import (
  15. "context"
  16. "flag"
  17. "fmt"
  18. "log"
  19. "net"
  20. "os"
  21. "os/signal"
  22. "path/filepath"
  23. "runtime"
  24. "runtime/pprof"
  25. "strings"
  26. "syscall"
  27. "time"
  28. "github.com/pborman/uuid"
  29. "golang.org/x/sync/errgroup"
  30. "go.ntppool.org/common/version"
  31. "github.com/abh/geodns/v3/appconfig"
  32. "github.com/abh/geodns/v3/applog"
  33. "github.com/abh/geodns/v3/health"
  34. "github.com/abh/geodns/v3/monitor"
  35. "github.com/abh/geodns/v3/querylog"
  36. "github.com/abh/geodns/v3/server"
  37. "github.com/abh/geodns/v3/targeting"
  38. "github.com/abh/geodns/v3/targeting/geoip2"
  39. "github.com/abh/geodns/v3/zones"
  40. )
  41. var (
  42. serverInfo *monitor.ServerInfo
  43. )
  44. var (
  45. flagconfig = flag.String("config", "./dns/", "directory of zone files")
  46. flagconfigfile = flag.String("configfile", "geodns.conf", "filename of config file (in 'config' directory)")
  47. flagcheckconfig = flag.Bool("checkconfig", false, "check configuration and exit")
  48. flagidentifier = flag.String("identifier", "", "identifier (hostname, pop name or similar)")
  49. flaginter = flag.String("interface", "*", "set the listener address")
  50. flagport = flag.String("port", "53", "default port number")
  51. flaghttp = flag.String("http", ":8053", "http listen address (:8053)")
  52. flaglog = flag.Bool("log", false, "be more verbose")
  53. flagcpus = flag.Int("cpus", 0, "Set the maximum number of CPUs to use")
  54. flagLogFile = flag.String("logfile", "", "log to file")
  55. flagShowVersion = flag.Bool("version", false, "Show GeoDNS version")
  56. cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
  57. memprofile = flag.String("memprofile", "", "write memory profile to this file")
  58. )
  59. func init() {
  60. log.SetPrefix("geodns ")
  61. log.SetFlags(log.Lmicroseconds | log.Lshortfile)
  62. serverInfo = &monitor.ServerInfo{}
  63. serverInfo.Version = version.Version()
  64. serverInfo.UUID = uuid.New()
  65. serverInfo.Started = time.Now()
  66. }
  67. func main() {
  68. flag.Parse()
  69. if *memprofile != "" {
  70. runtime.MemProfileRate = 1024
  71. }
  72. if *flagShowVersion {
  73. fmt.Printf("geodns %s\n", version.Version())
  74. os.Exit(0)
  75. }
  76. if *flaglog {
  77. applog.Enabled = true
  78. }
  79. if len(*flagLogFile) > 0 {
  80. applog.FileOpen(*flagLogFile)
  81. }
  82. if len(*flagidentifier) > 0 {
  83. ids := strings.Split(*flagidentifier, ",")
  84. serverInfo.ID = ids[0]
  85. if len(ids) > 1 {
  86. serverInfo.Groups = ids[1:]
  87. }
  88. }
  89. var configFileName string
  90. if filepath.IsAbs(*flagconfigfile) {
  91. configFileName = *flagconfigfile
  92. } else {
  93. configFileName = filepath.Clean(filepath.Join(*flagconfig, *flagconfigfile))
  94. }
  95. if *flagcheckconfig {
  96. err := appconfig.ConfigReader(configFileName)
  97. if err != nil {
  98. log.Println("Errors reading config", err)
  99. os.Exit(2)
  100. }
  101. dirName := *flagconfig
  102. _, err = zones.NewMuxManager(dirName, &zones.NilReg{})
  103. if err != nil {
  104. log.Println("Errors reading zones", err)
  105. os.Exit(2)
  106. }
  107. // todo: setup health stuff when configured
  108. return
  109. }
  110. if *flagcpus > 0 {
  111. runtime.GOMAXPROCS(*flagcpus)
  112. }
  113. log.Printf("Starting geodns %s\n", version.Version())
  114. ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill, syscall.SIGTERM)
  115. g, ctx := errgroup.WithContext(ctx)
  116. g.Go(func() error {
  117. <-ctx.Done()
  118. log.Printf("server shutting down")
  119. go func() {
  120. time.Sleep(time.Second * 5)
  121. log.Fatal("shutdown appears stalled; force exit")
  122. os.Exit(99)
  123. }()
  124. return nil
  125. })
  126. if *cpuprofile != "" {
  127. prof, err := os.Create(*cpuprofile)
  128. if err != nil {
  129. panic(err.Error())
  130. }
  131. pprof.StartCPUProfile(prof)
  132. defer func() {
  133. log.Println("closing file")
  134. prof.Close()
  135. }()
  136. defer func() {
  137. log.Println("stopping profile")
  138. pprof.StopCPUProfile()
  139. }()
  140. }
  141. // load geodns.conf config
  142. err := appconfig.ConfigReader(configFileName)
  143. if err != nil {
  144. log.Printf("error reading config file %s: %s", configFileName, err)
  145. os.Exit(2)
  146. }
  147. if len(appconfig.Config.Health.Directory) > 0 {
  148. go health.DirectoryReader(appconfig.Config.Health.Directory)
  149. }
  150. // load (and re-load) zone data
  151. g.Go(func() error {
  152. err := appconfig.ConfigWatcher(ctx, configFileName)
  153. if err != nil {
  154. log.Printf("config watcher error: %s", err)
  155. return err
  156. }
  157. return nil
  158. })
  159. if *flaginter == "*" {
  160. addrs, _ := net.InterfaceAddrs()
  161. ips := make([]string, 0)
  162. for _, addr := range addrs {
  163. ip, _, err := net.ParseCIDR(addr.String())
  164. if err != nil {
  165. continue
  166. }
  167. if !(ip.IsLoopback() || ip.IsGlobalUnicast()) {
  168. continue
  169. }
  170. ips = append(ips, ip.String())
  171. }
  172. *flaginter = strings.Join(ips, ",")
  173. }
  174. inter := getInterfaces()
  175. if len(appconfig.Config.GeoIPDirectory()) > 0 {
  176. geoProvider, err := geoip2.New(appconfig.Config.GeoIPDirectory())
  177. if err != nil {
  178. log.Printf("Configuring geo provider: %s", err)
  179. }
  180. if geoProvider != nil {
  181. targeting.Setup(geoProvider)
  182. }
  183. }
  184. srv := server.NewServer(appconfig.Config, serverInfo)
  185. if qlc := appconfig.Config.AvroLog; len(qlc.Path) > 0 {
  186. maxsize := qlc.MaxSize
  187. if maxsize < 50000 {
  188. maxsize = 1000000
  189. }
  190. maxtime, err := time.ParseDuration(qlc.MaxTime)
  191. if err != nil {
  192. log.Printf("could not parse avrolog maxtime setting %q: %s", qlc.MaxTime, err)
  193. }
  194. if maxtime < 1*time.Second {
  195. maxtime = 1 * time.Second
  196. }
  197. ql, err := querylog.NewAvroLogger(qlc.Path, maxsize, maxtime)
  198. if err != nil {
  199. log.Fatalf("Could not start avro query logger: %s", err)
  200. }
  201. srv.SetQueryLogger(ql)
  202. } else if qlc := appconfig.Config.QueryLog; len(qlc.Path) > 0 {
  203. ql, err := querylog.NewFileLogger(qlc.Path, qlc.MaxSize, qlc.Keep)
  204. if err != nil {
  205. log.Fatalf("Could not start file query logger: %s", err)
  206. }
  207. srv.SetQueryLogger(ql)
  208. }
  209. muxm, err := zones.NewMuxManager(*flagconfig, srv)
  210. if err != nil {
  211. log.Printf("error loading zones: %s", err)
  212. }
  213. g.Go(func() error {
  214. muxm.Run(ctx)
  215. return nil
  216. })
  217. for _, host := range inter {
  218. host := host
  219. g.Go(func() error {
  220. return srv.ListenAndServe(ctx, host)
  221. })
  222. }
  223. g.Go(func() error {
  224. <-ctx.Done()
  225. log.Printf("shutting down DNS servers")
  226. err = srv.Shutdown()
  227. if err != nil {
  228. return err
  229. }
  230. return nil
  231. })
  232. if len(*flaghttp) > 0 {
  233. g.Go(func() error {
  234. hs := NewHTTPServer(muxm, serverInfo)
  235. err := hs.Run(ctx, *flaghttp)
  236. return err
  237. })
  238. }
  239. err = g.Wait()
  240. if err != nil {
  241. log.Printf("server error: %s", err)
  242. }
  243. if *memprofile != "" {
  244. f, err := os.Create(*memprofile)
  245. if err != nil {
  246. log.Fatal(err)
  247. }
  248. pprof.WriteHeapProfile(f)
  249. f.Close()
  250. }
  251. applog.FileClose()
  252. }