summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/server/server.go104
1 files changed, 104 insertions, 0 deletions
diff --git a/internal/server/server.go b/internal/server/server.go
new file mode 100644
index 0000000..01bda09
--- /dev/null
+++ b/internal/server/server.go
@@ -0,0 +1,104 @@
+package server
+
+import (
+ "context"
+ "log/slog"
+ "time"
+
+ "github.com/miekg/dns"
+)
+
+type Server struct {
+ addr string
+ logger *slog.Logger
+ inner *dns.Server
+}
+
+func New(addr string, logger *slog.Logger) (*Server, error) {
+ mux := dns.NewServeMux()
+ mux.HandleFunc(".", handleQuery)
+
+ inner := &dns.Server{
+ Addr: addr,
+ Net: "udp",
+ Handler: mux,
+ UDPSize: 4096,
+ ReadTimeout: 5 * time.Second,
+ WriteTimeout: 5 * time.Second,
+ }
+ return &Server{
+ addr: addr,
+ logger: logger,
+ inner: inner,
+ }, nil
+}
+
+func (s *Server) Run(ctx context.Context) error {
+ errCh := make(chan error, 1)
+ go func() {
+ s.logger.Info("udp listener active", "addr", s.addr)
+ errCh <- s.inner.ListenAndServe()
+ }()
+
+ select {
+ case <-ctx.Done():
+ shutdownCtx, cancel := context.WithTimeout(context.Background(), 5 *time.Second)
+ defer cancel()
+ if err := s.inner.ShutdownContext(shutdownCtx); err != nil {
+ s.logger.Error("graceful shutdown failed", "err", err)
+ return err
+ }
+ return ctx.Err()
+ case err := <-errCh:
+ return err
+ }
+}
+
+func (s *Server) Close() error {
+ return s.inner.Shutdown()
+}
+
+func handleQuery(w dns.ResponseWriter, req *dns.Msg) {
+ if len(req.Question) == 0 {
+ m := new(dns.Msg)
+ m.SetRcode(req, dns.RcodeFormatError)
+ _ = w.WriteMsg(m)
+ return
+ }
+
+ q := req.Question[0]
+ resp := new(dns.Msg)
+ resp.SetReply(req)
+ resp.Authoritative = false
+
+ if q.Name == "example.com." && q.Qtype == dns.TypeA {
+ resp.Answer = []dns.RR{
+ &dns.A{
+ Hdr: dns.RR_Header{
+ Name: q.Name,
+ Rrtype: dns.TypeA,
+ Class: dns.ClassINET,
+ Ttl: 60,
+ },
+ A: []byte{127,0,0,1},
+ },
+ }
+ } else {
+ resp.Rcode = dns.RcodeNameError
+ }
+
+ if err := w.WriteMsg(resp); err != nil {
+ slog.Error("write response failed",
+ "err", err,
+ "qname", q.Name,
+ "qtype", dns.TypeToString[q.Qtype],
+ )
+ return
+}
+slog.Info("query served",
+"qname", q.Name,
+"qtype", dns.TypeToString[q.Qtype],
+"rcode", dns.RcodeToString[resp.Rcode],
+"client", w.RemoteAddr().String(),
+)
+}