summaryrefslogtreecommitdiff
path: root/internal/server/doh.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/server/doh.go')
-rw-r--r--internal/server/doh.go70
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,
- )
}