diff options
Diffstat (limited to 'internal/server/doh.go')
| -rw-r--r-- | internal/server/doh.go | 70 |
1 files changed, 58 insertions, 12 deletions
diff --git a/internal/server/doh.go b/internal/server/doh.go index 2f9dfc0..0feb094 100644 --- a/internal/server/doh.go +++ b/internal/server/doh.go @@ -2,13 +2,27 @@ package server import ( "encoding/base64" - "codeberg.org/miekg/dns" "io" "log/slog" "net/http" + + "codeberg.org/miekg/dns" + "linum/internal/cache" ) func (s *Server) dohHandler(w http.ResponseWriter, r *http.Request) { + clientIP := parseHTTPClientIP(r.RemoteAddr) + if !s.isAllowed(clientIP) { + slog.Warn("doh query denied by ACL", "client", clientIP) + http.Error(w, "refused", http.StatusForbidden) + return + } + if !s.rateLimit(clientIP.String()) { + slog.Warn("doh query rate limited", "client", clientIP) + http.Error(w, "too many requests", http.StatusTooManyRequests) + return + } + var raw []byte switch r.Method { @@ -53,23 +67,55 @@ func (s *Server) dohHandler(w http.ResponseWriter, r *http.Request) { return } + q := msg.Question[0] + qname := q.Header().Name + qtype := dns.RRToType(q) + qclass := q.Header().Class + + if s.blocklist != nil && s.blocklist.IsBlocked(qname) { + httpWriteMsg(w, s.blockedResponse(msg)) + slog.Debug("doh query served", + "qname", qname, + "qtype", dns.TypeToString[qtype], + "blocked", true, + ) + return + } + + if s.cache != nil { + key := cache.Key{Name: qname, Qtype: qtype, Class: qclass} + if packed, ok := s.cache.Get(key); ok { + packed[0] = byte(msg.ID >> 8) + packed[1] = byte(msg.ID) + w.Header().Set("Content-Type", "application/dns-message") + w.Header().Set("Cache-Control", "no-cache, max-age=0") + w.Write(packed) + slog.Debug("doh query served", + "qname", qname, + "qtype", dns.TypeToString[qtype], + "cached", true, + ) + return + } + } + resp, _ := s.buildResponse(msg) - if err := resp.Pack(); err != nil { + httpWriteMsg(w, resp) + slog.Debug("doh query served", + "qname", qname, + "qtype", dns.TypeToString[qtype], + "rcode", dns.RcodeToString[resp.Rcode], + ) +} + +func httpWriteMsg(w http.ResponseWriter, msg *dns.Msg) { + if err := msg.Pack(); err != nil { http.Error(w, "pack response", http.StatusInternalServerError) return } - w.Header().Set("Content-Type", "application/dns-message") w.Header().Set("Cache-Control", "no-cache, max-age=0") - if _, err := w.Write(resp.Data); err != nil { + if _, err := w.Write(msg.Data); err != nil { slog.Error("doh write failed", "err", err) - return } - - slog.Info("doh query served", - "qname", msg.Question[0].Header().Name, - "qtype", dns.TypeToString[dns.RRToType(msg.Question[0])], - "rcode", dns.RcodeToString[resp.Rcode], - "client", r.RemoteAddr, - ) } |
