server.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. package server
  2. import (
  3. "log"
  4. "github.com/abh/geodns/monitor"
  5. "github.com/abh/geodns/querylog"
  6. "github.com/abh/geodns/zones"
  7. "github.com/miekg/dns"
  8. "github.com/prometheus/client_golang/prometheus"
  9. )
  10. type serverMetrics struct {
  11. Queries *prometheus.CounterVec
  12. }
  13. // Server ...
  14. type Server struct {
  15. queryLogger querylog.QueryLogger
  16. mux *dns.ServeMux
  17. PublicDebugQueries bool
  18. info *monitor.ServerInfo
  19. metrics *serverMetrics
  20. }
  21. // NewServer ...
  22. func NewServer(si *monitor.ServerInfo) *Server {
  23. mux := dns.NewServeMux()
  24. queries := prometheus.NewCounterVec(
  25. prometheus.CounterOpts{
  26. Name: "dns_queries_total",
  27. Help: "Number of served queries",
  28. },
  29. []string{"zone", "qtype", "qname", "rcode"},
  30. )
  31. prometheus.MustRegister(queries)
  32. buildInfo := prometheus.NewGaugeVec(
  33. prometheus.GaugeOpts{
  34. Name: "geodns_build_info",
  35. Help: "GeoDNS build information (in labels)",
  36. },
  37. []string{"Version", "ID", "IP", "Group"},
  38. )
  39. prometheus.MustRegister(buildInfo)
  40. group := ""
  41. if len(si.Groups) > 0 {
  42. group = si.Groups[0]
  43. }
  44. buildInfo.WithLabelValues(si.Version, si.ID, si.IP, group).Set(1)
  45. startTime := prometheus.NewGauge(
  46. prometheus.GaugeOpts{
  47. Name: "geodns_start_time_seconds",
  48. Help: "Unix time process started",
  49. },
  50. )
  51. prometheus.MustRegister(startTime)
  52. nano := si.Started.UnixNano()
  53. startTime.Set(float64(nano) / 1e9)
  54. metrics := &serverMetrics{
  55. Queries: queries,
  56. }
  57. return &Server{mux: mux, info: si, metrics: metrics}
  58. }
  59. // SetQueryLogger configures the query logger. For now it only supports writing to
  60. // a file (and all zones get logged to the same file).
  61. func (srv *Server) SetQueryLogger(logger querylog.QueryLogger) {
  62. srv.queryLogger = logger
  63. }
  64. // Add adds the Zone to be handled under the specified name
  65. func (srv *Server) Add(name string, zone *zones.Zone) {
  66. srv.mux.HandleFunc(name, srv.setupServerFunc(zone))
  67. }
  68. // Remove removes the zone name from being handled by the server
  69. func (srv *Server) Remove(name string) {
  70. srv.mux.HandleRemove(name)
  71. }
  72. func (srv *Server) setupServerFunc(zone *zones.Zone) func(dns.ResponseWriter, *dns.Msg) {
  73. return func(w dns.ResponseWriter, r *dns.Msg) {
  74. srv.serve(w, r, zone)
  75. }
  76. }
  77. // ServeDNS calls ServeDNS in the dns package
  78. func (srv *Server) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
  79. srv.mux.ServeDNS(w, r)
  80. }
  81. // ListenAndServe starts the DNS server on the specified IP
  82. // (both tcp and udp) and returns. If something goes wrong
  83. // it will crash the process with an error message.
  84. func (srv *Server) ListenAndServe(ip string) {
  85. prots := []string{"udp", "tcp"}
  86. for _, prot := range prots {
  87. go func(p string) {
  88. server := &dns.Server{
  89. Addr: ip,
  90. Net: p,
  91. Handler: srv,
  92. }
  93. log.Printf("Opening on %s %s", ip, p)
  94. if err := server.ListenAndServe(); err != nil {
  95. log.Fatalf("geodns: failed to setup %s %s: %s", ip, p, err)
  96. }
  97. log.Fatalf("geodns: ListenAndServe unexpectedly returned")
  98. }(prot)
  99. }
  100. }