1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
package http
import (
"context"
"net"
"net/http"
"strconv"
"time"
"github.com/mpolden/zdns/cache"
"github.com/mpolden/zdns/dns"
"github.com/mpolden/zdns/log"
)
// A Server defines paramaters for running an HTTP server. The HTTP server serves an API for inspecting cache contents
// and request log.
type Server struct {
cache *cache.Cache
logger *log.Logger
server *http.Server
}
type entry struct {
Time string `json:"time"`
TTL int64 `json:"ttl,omitempty"`
RemoteAddr net.IP `json:"remote_addr,omitempty"`
Hijacked *bool `json:"hijacked,omitempty"`
Qtype string `json:"type"`
Question string `json:"question"`
Answers []string `json:"answers,omitempty"`
Rcode string `json:"rcode,omitempty"`
}
type httpError struct {
err error
Status int `json:"status"`
Message string `json:"message"`
}
// NewServer creates a new HTTP server, serving logs from the given logger and listening on addr.
func NewServer(logger *log.Logger, cache *cache.Cache, addr string) *Server {
server := &http.Server{Addr: addr}
s := &Server{
cache: cache,
logger: logger,
server: server,
}
s.server.Handler = s.handler()
return s
}
func (s *Server) handler() http.Handler {
r := newRouter()
r.route("GET", "/cache/v1/", s.cacheHandler)
r.route("GET", "/log/v1/", s.logHandler)
r.route("DELETE", "/cache/v1/", s.cacheResetHandler)
return r.handler()
}
func listCountFrom(r *http.Request) int {
defaultCount := 100
param := r.URL.Query().Get("n")
n, err := strconv.Atoi(param)
if err != nil {
return defaultCount
}
return n
}
func (s *Server) cacheHandler(w http.ResponseWriter, r *http.Request) (interface{}, *httpError) {
cacheValues := s.cache.List(listCountFrom(r))
entries := make([]entry, 0, len(cacheValues))
for _, v := range cacheValues {
entries = append(entries, entry{
Time: v.CreatedAt.UTC().Format(time.RFC3339),
TTL: int64(v.TTL().Truncate(time.Second).Seconds()),
Qtype: dns.TypeToString[v.Qtype()],
Question: v.Question(),
Answers: v.Answers(),
Rcode: dns.RcodeToString[v.Rcode()],
})
}
return entries, nil
}
func (s *Server) cacheResetHandler(w http.ResponseWriter, r *http.Request) (interface{}, *httpError) {
s.cache.Reset()
return struct {
Message string `json:"message"`
}{
"Cleared cache",
}, nil
}
func (s *Server) logHandler(w http.ResponseWriter, r *http.Request) (interface{}, *httpError) {
logEntries, err := s.logger.Get(listCountFrom(r))
if err != nil {
return nil, &httpError{
err: err,
Status: http.StatusInternalServerError,
}
}
entries := make([]entry, 0, len(logEntries))
for _, le := range logEntries {
hijacked := le.Hijacked
entries = append(entries, entry{
Time: le.Time.UTC().Format(time.RFC3339),
RemoteAddr: le.RemoteAddr,
Hijacked: &hijacked,
Qtype: dns.TypeToString[le.Qtype],
Question: le.Question,
Answers: le.Answers,
})
}
return entries, nil
}
// Close shuts down the HTTP server.
func (s *Server) Close() error { return s.server.Shutdown(context.TODO()) }
// ListenAndServe starts the HTTP server listening on the configured address.
func (s *Server) ListenAndServe() error {
s.logger.Printf("http server listening on http://%s", s.server.Addr)
err := s.server.ListenAndServe()
if err == http.ErrServerClosed {
return nil // Do not treat server closing as an error
}
return err
}
|