mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 16:33:04 +00:00
193 lines
4.4 KiB
Go
193 lines
4.4 KiB
Go
package rqlite
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/coredns/coredns/plugin"
|
|
"github.com/coredns/coredns/request"
|
|
"github.com/miekg/dns"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// RQLitePlugin implements the CoreDNS plugin interface
|
|
type RQLitePlugin struct {
|
|
Next plugin.Handler
|
|
logger *zap.Logger
|
|
backend *Backend
|
|
cache *Cache
|
|
zones []string
|
|
}
|
|
|
|
// Name returns the plugin name
|
|
func (p *RQLitePlugin) Name() string {
|
|
return "rqlite"
|
|
}
|
|
|
|
// ServeDNS implements the plugin.Handler interface
|
|
func (p *RQLitePlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
|
state := request.Request{W: w, Req: r}
|
|
|
|
// Only handle queries for our configured zones
|
|
if !p.isOurZone(state.Name()) {
|
|
return plugin.NextOrFailure(p.Name(), p.Next, ctx, w, r)
|
|
}
|
|
|
|
// Check cache first
|
|
if cachedMsg := p.cache.Get(state.Name(), state.QType()); cachedMsg != nil {
|
|
p.logger.Debug("Cache hit",
|
|
zap.String("qname", state.Name()),
|
|
zap.Uint16("qtype", state.QType()),
|
|
)
|
|
cachedMsg.SetReply(r)
|
|
w.WriteMsg(cachedMsg)
|
|
return dns.RcodeSuccess, nil
|
|
}
|
|
|
|
// Query RQLite backend
|
|
records, err := p.backend.Query(ctx, state.Name(), state.QType())
|
|
if err != nil {
|
|
p.logger.Error("Backend query failed",
|
|
zap.String("qname", state.Name()),
|
|
zap.Error(err),
|
|
)
|
|
return dns.RcodeServerFailure, err
|
|
}
|
|
|
|
// If no exact match, try wildcard
|
|
if len(records) == 0 {
|
|
wildcardName := p.getWildcardName(state.Name())
|
|
if wildcardName != "" {
|
|
records, err = p.backend.Query(ctx, wildcardName, state.QType())
|
|
if err != nil {
|
|
p.logger.Error("Wildcard query failed",
|
|
zap.String("wildcard", wildcardName),
|
|
zap.Error(err),
|
|
)
|
|
return dns.RcodeServerFailure, err
|
|
}
|
|
}
|
|
}
|
|
|
|
// No records found
|
|
if len(records) == 0 {
|
|
p.logger.Debug("No records found",
|
|
zap.String("qname", state.Name()),
|
|
zap.Uint16("qtype", state.QType()),
|
|
)
|
|
return p.handleNXDomain(ctx, w, r, &state)
|
|
}
|
|
|
|
// Build response
|
|
msg := new(dns.Msg)
|
|
msg.SetReply(r)
|
|
msg.Authoritative = true
|
|
|
|
for _, record := range records {
|
|
rr := p.buildRR(state.Name(), record)
|
|
if rr != nil {
|
|
msg.Answer = append(msg.Answer, rr)
|
|
}
|
|
}
|
|
|
|
// Cache the response
|
|
p.cache.Set(state.Name(), state.QType(), msg)
|
|
|
|
w.WriteMsg(msg)
|
|
return dns.RcodeSuccess, nil
|
|
}
|
|
|
|
// isOurZone checks if the query is for one of our configured zones
|
|
func (p *RQLitePlugin) isOurZone(qname string) bool {
|
|
for _, zone := range p.zones {
|
|
if plugin.Name(zone).Matches(qname) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// getWildcardName extracts the wildcard pattern for a given name
|
|
// e.g., myapp.node-7prvNa.orama.network -> *.node-7prvNa.orama.network
|
|
func (p *RQLitePlugin) getWildcardName(qname string) string {
|
|
labels := dns.SplitDomainName(qname)
|
|
if len(labels) < 3 {
|
|
return ""
|
|
}
|
|
|
|
// Replace first label with wildcard
|
|
labels[0] = "*"
|
|
return dns.Fqdn(dns.Fqdn(labels[0] + "." + labels[1] + "." + labels[2]))
|
|
}
|
|
|
|
// buildRR builds a DNS resource record from a DNSRecord
|
|
func (p *RQLitePlugin) buildRR(qname string, record *DNSRecord) dns.RR {
|
|
header := dns.RR_Header{
|
|
Name: qname,
|
|
Rrtype: record.Type,
|
|
Class: dns.ClassINET,
|
|
Ttl: uint32(record.TTL),
|
|
}
|
|
|
|
switch record.Type {
|
|
case dns.TypeA:
|
|
return &dns.A{
|
|
Hdr: header,
|
|
A: record.ParsedValue.(*dns.A).A,
|
|
}
|
|
case dns.TypeAAAA:
|
|
return &dns.AAAA{
|
|
Hdr: header,
|
|
AAAA: record.ParsedValue.(*dns.AAAA).AAAA,
|
|
}
|
|
case dns.TypeCNAME:
|
|
return &dns.CNAME{
|
|
Hdr: header,
|
|
Target: record.ParsedValue.(string),
|
|
}
|
|
case dns.TypeTXT:
|
|
return &dns.TXT{
|
|
Hdr: header,
|
|
Txt: record.ParsedValue.([]string),
|
|
}
|
|
default:
|
|
p.logger.Warn("Unsupported record type",
|
|
zap.Uint16("type", record.Type),
|
|
)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// handleNXDomain handles the case where no records are found
|
|
func (p *RQLitePlugin) handleNXDomain(ctx context.Context, w dns.ResponseWriter, r *dns.Msg, state *request.Request) (int, error) {
|
|
msg := new(dns.Msg)
|
|
msg.SetRcode(r, dns.RcodeNameError)
|
|
msg.Authoritative = true
|
|
|
|
// Add SOA record for negative caching
|
|
soa := &dns.SOA{
|
|
Hdr: dns.RR_Header{
|
|
Name: p.zones[0],
|
|
Rrtype: dns.TypeSOA,
|
|
Class: dns.ClassINET,
|
|
Ttl: 300,
|
|
},
|
|
Ns: "ns1." + p.zones[0],
|
|
Mbox: "admin." + p.zones[0],
|
|
Serial: uint32(time.Now().Unix()),
|
|
Refresh: 3600,
|
|
Retry: 600,
|
|
Expire: 86400,
|
|
Minttl: 300,
|
|
}
|
|
msg.Ns = append(msg.Ns, soa)
|
|
|
|
w.WriteMsg(msg)
|
|
return dns.RcodeNameError, nil
|
|
}
|
|
|
|
// Ready implements the ready.Readiness interface
|
|
func (p *RQLitePlugin) Ready() bool {
|
|
return p.backend.Healthy()
|
|
}
|