|
@@ -2,8 +2,9 @@ package zone
|
|
|
|
|
|
import (
|
|
|
"fmt"
|
|
|
- "net"
|
|
|
+ "sync"
|
|
|
|
|
|
+ "git.giaever.org/joachimmg/go-log.git/log"
|
|
|
"git.giaever.org/joachimmg/m-dns/config"
|
|
|
"git.giaever.org/joachimmg/m-dns/errors"
|
|
|
"git.giaever.org/joachimmg/m-dns/host"
|
|
@@ -11,75 +12,154 @@ import (
|
|
|
)
|
|
|
|
|
|
type Zone interface {
|
|
|
- Records(q dns.Question)
|
|
|
+ Records(q dns.Question) []dns.RR
|
|
|
}
|
|
|
|
|
|
-type ZoneRecord struct {
|
|
|
- Instance host.HostString
|
|
|
- TXT []string
|
|
|
+type Resource struct {
|
|
|
+ sync.Mutex
|
|
|
+ host.Host
|
|
|
+}
|
|
|
+
|
|
|
+func (r *Resource) qType(q dns.Question) string {
|
|
|
+ switch q.Qtype {
|
|
|
+ case dns.TypeANY:
|
|
|
+ return "TypeANY"
|
|
|
+ case dns.TypePTR:
|
|
|
+ return "TypePTR"
|
|
|
+ case dns.TypeSRV:
|
|
|
+ return "TypeSRV"
|
|
|
+ case dns.TypeA:
|
|
|
+ return "TypeA"
|
|
|
+ case dns.TypeAAAA:
|
|
|
+ return "TypeAAAA"
|
|
|
+ case dns.TypeTXT:
|
|
|
+ return "TypeTXT"
|
|
|
+ default:
|
|
|
+ return fmt.Sprintf("%d", q.Qtype)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func New(h host.Host) (*Resource, error) {
|
|
|
+
|
|
|
+ r := new(Resource)
|
|
|
+
|
|
|
+ if r == nil {
|
|
|
+ log.Traceln(errors.Zone, errors.OutOfMemory)
|
|
|
+ return nil, errors.OutOfMemory
|
|
|
+ }
|
|
|
|
|
|
- sAddr string
|
|
|
- iAddr string
|
|
|
- eAddr string
|
|
|
+ if h == nil {
|
|
|
+ log.Traceln(errors.Zone, errors.HostIsNil)
|
|
|
+ return nil, errors.HostIsNil
|
|
|
+ }
|
|
|
+
|
|
|
+ r.Host = h
|
|
|
|
|
|
- *host.Host
|
|
|
+ return r, nil
|
|
|
}
|
|
|
|
|
|
-func New(instance, service, domain, hostname string, port int, ips []net.IP, txt []string) (*ZoneRecord, error) {
|
|
|
- zr := new(ZoneRecord)
|
|
|
- zr.Host = new(host.Host)
|
|
|
+func (r *Resource) hdr(n string, rt uint16) dns.RR_Header {
|
|
|
+ return dns.RR_Header{
|
|
|
+ Name: n,
|
|
|
+ Rrtype: rt,
|
|
|
+ Class: dns.ClassINET,
|
|
|
+ Ttl: config.DefaultTTL,
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- if len(instance) == 0 {
|
|
|
- return nil, errors.ZoneInstanseIsEmpty
|
|
|
+func (r *Resource) ptrRecord(q dns.Question, p host.HostString) dns.RR {
|
|
|
+ r.Lock()
|
|
|
+ defer r.Unlock()
|
|
|
+ return &dns.PTR{
|
|
|
+ Hdr: r.hdr(q.Name, dns.TypePTR),
|
|
|
+ Ptr: p.String(),
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- zr.Instance = host.HostString(instance)
|
|
|
+func (r *Resource) srvRecord(q dns.Question) dns.RR {
|
|
|
+ r.Lock()
|
|
|
+ defer r.Unlock()
|
|
|
+ log.Traceln("srvRecord")
|
|
|
+ return &dns.SRV{
|
|
|
+ Hdr: r.hdr(q.Name, dns.TypeSRV),
|
|
|
+ Priority: 10,
|
|
|
+ Weight: 1,
|
|
|
+ Port: r.GetPort().Uint16(),
|
|
|
+ Target: r.GetHostnameAddr().String(),
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- if err := zr.SetService(service); err != nil {
|
|
|
- return nil, err
|
|
|
+func (r *Resource) txtRecord(q dns.Question) dns.RR {
|
|
|
+ r.Lock()
|
|
|
+ defer r.Unlock()
|
|
|
+ return &dns.TXT{
|
|
|
+ Hdr: r.hdr(q.Name, dns.TypeTXT),
|
|
|
+ Txt: r.GetTXTs(),
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- if err := zr.SetDomain(domain); err != nil {
|
|
|
- return nil, err
|
|
|
+func (r *Resource) records(rr ...dns.RR) []dns.RR {
|
|
|
+ rrn := make([]dns.RR, 0)
|
|
|
+ if rr == nil {
|
|
|
+ return rrn
|
|
|
}
|
|
|
+ return append(rrn, rr...)
|
|
|
+}
|
|
|
|
|
|
- if err := zr.SetHostname(hostname); err != nil {
|
|
|
- return nil, err
|
|
|
+func (r *Resource) aRecord(q dns.Question, ip host.HostIP) dns.RR {
|
|
|
+ r.Lock()
|
|
|
+ defer r.Unlock()
|
|
|
+ return &dns.A{
|
|
|
+ Hdr: r.hdr(r.GetHostnameAddr().String(), dns.TypeA),
|
|
|
+ A: ip.AsIP(),
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- if err := zr.SetPort(port); err != nil {
|
|
|
- return nil, err
|
|
|
+func (r *Resource) aaaaRecord(q dns.Question, ip host.HostIP) dns.RR {
|
|
|
+ r.Lock()
|
|
|
+ defer r.Unlock()
|
|
|
+ return &dns.AAAA{
|
|
|
+ Hdr: r.hdr(r.GetHostnameAddr().String(), dns.TypeAAAA),
|
|
|
+ AAAA: ip.AsIP(),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (r *Resource) axRecords(q dns.Question, atype uint16) []dns.RR {
|
|
|
+ a := r.records(nil)[:0]
|
|
|
+ for _, ip := range r.GetIPs() {
|
|
|
+ switch atype {
|
|
|
+ case dns.TypeA:
|
|
|
+ if _, ipType := ip.Type(); ipType == host.IPv4 {
|
|
|
+ a = append(a, r.aRecord(q, ip))
|
|
|
+ }
|
|
|
+ case dns.TypeAAAA:
|
|
|
+ if _, ipType := ip.Type(); ipType == host.IPv6 {
|
|
|
+ a = append(a, r.aaaaRecord(q, ip))
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- zr.TXT = txt
|
|
|
+ if len(a) == 0 {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
|
|
|
- zr.sAddr = fmt.Sprintf("%s.%s.",
|
|
|
- zr.GetService().TrimDot(),
|
|
|
- zr.GetDomain().TrimDot(),
|
|
|
- )
|
|
|
- zr.iAddr = fmt.Sprintf("%s.%s.%s.",
|
|
|
- zr.Instance.TrimDot,
|
|
|
- zr.GetService().TrimDot(),
|
|
|
- zr.GetDomain().TrimDot(),
|
|
|
- )
|
|
|
- zr.eAddr = fmt.Sprintf("_services._dns-ds._udp.%s.",
|
|
|
- zr.GetDomain().TrimDot(),
|
|
|
- )
|
|
|
- return zr, nil
|
|
|
+ return a
|
|
|
}
|
|
|
|
|
|
-func (zr *ZoneRecord) Records(q dns.Question) []dns.RR {
|
|
|
+// Records: Return DNS records based on the Question
|
|
|
+func (r *Resource) Records(q dns.Question) []dns.RR {
|
|
|
+ log.Traceln(errors.Zone, "Records", q.Name, r.qType(q))
|
|
|
switch q.Name {
|
|
|
- case zr.sAddr:
|
|
|
- return zr.sRecords(q)
|
|
|
- case zr.iAddr:
|
|
|
- return zr.iRecords(q)
|
|
|
- case zr.eAddr:
|
|
|
- return zr.sEnum(q)
|
|
|
- case zr.GetHostname().String():
|
|
|
+ case r.GetInstanceAddr().String(): // RFC 6763, 13.3 "instance"._<service>.<domain>
|
|
|
+ return r.iRecords(q)
|
|
|
+ case r.GetServiceAddr().String(): // RFC 6763, 13.1 <_service>.<domain>
|
|
|
+ return r.sRecords(q)
|
|
|
+ case r.GetDiscoveryAddr().String():
|
|
|
+ return r.dRecords(q)
|
|
|
+ case r.GetHostnameAddr().String():
|
|
|
switch q.Qtype {
|
|
|
- case dns.TypeA, dns.TypeAAAA:
|
|
|
- return zr.iRecords(q)
|
|
|
+ case dns.TypeANY, dns.TypeA, dns.TypeAAAA:
|
|
|
+ return r.iRecords(q)
|
|
|
}
|
|
|
fallthrough
|
|
|
default:
|
|
@@ -87,146 +167,67 @@ func (zr *ZoneRecord) Records(q dns.Question) []dns.RR {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (zr *ZoneRecord) sRecords(q dns.Question) []dns.RR {
|
|
|
+// iRecords: Instance records
|
|
|
+func (r *Resource) iRecords(q dns.Question) []dns.RR {
|
|
|
switch q.Qtype {
|
|
|
- case dns.TypeANY, dns.TypePTR:
|
|
|
- sr := []dns.RR{
|
|
|
- &dns.PTR{
|
|
|
- Hdr: dns.RR_Header{
|
|
|
- Name: q.Name,
|
|
|
- Rrtype: dns.TypePTR,
|
|
|
- Class: dns.ClassINET,
|
|
|
- Ttl: config.DefaultTTL,
|
|
|
- },
|
|
|
- Ptr: zr.iAddr,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- return append(sr, zr.Records(
|
|
|
- dns.Question{
|
|
|
- Name: zr.iAddr,
|
|
|
- Qtype: dns.TypeANY,
|
|
|
- },
|
|
|
- )...)
|
|
|
-
|
|
|
+ case dns.TypeANY:
|
|
|
+ return append(r.iRecords(dns.Question{
|
|
|
+ Name: r.GetInstanceAddr().String(),
|
|
|
+ Qtype: dns.TypeSRV,
|
|
|
+ }), r.iRecords(dns.Question{
|
|
|
+ Name: r.GetInstanceAddr().String(),
|
|
|
+ Qtype: dns.TypeTXT,
|
|
|
+ })...)
|
|
|
+ case dns.TypeA:
|
|
|
+ return r.axRecords(q, dns.TypeA)
|
|
|
+ case dns.TypeAAAA:
|
|
|
+ return r.axRecords(q, dns.TypeAAAA)
|
|
|
+ case dns.TypeSRV:
|
|
|
+ return append(
|
|
|
+ r.records(r.srvRecord(q)),
|
|
|
+ append(
|
|
|
+ r.iRecords(dns.Question{
|
|
|
+ Name: r.GetInstanceAddr().String(),
|
|
|
+ Qtype: dns.TypeA,
|
|
|
+ }),
|
|
|
+ r.iRecords(dns.Question{
|
|
|
+ Name: r.GetInstanceAddr().String(),
|
|
|
+ Qtype: dns.TypeAAAA,
|
|
|
+ })...,
|
|
|
+ )...,
|
|
|
+ )
|
|
|
+ case dns.TypeTXT:
|
|
|
+ return r.records(r.txtRecord(q))
|
|
|
default:
|
|
|
+ log.Traceln(errors.Zone, "None iRecord for", r.qType(q))
|
|
|
return nil
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (zr *ZoneRecord) iRecords(q dns.Question) []dns.RR {
|
|
|
+// sRecords: Service records
|
|
|
+func (r *Resource) sRecords(q dns.Question) []dns.RR {
|
|
|
switch q.Qtype {
|
|
|
- case dns.TypeANY:
|
|
|
- ir := zr.Records(
|
|
|
- dns.Question{
|
|
|
- Name: zr.iAddr,
|
|
|
- Qtype: dns.TypeSRV,
|
|
|
- },
|
|
|
+ case dns.TypeANY, dns.TypePTR:
|
|
|
+ return append(
|
|
|
+ r.records(r.ptrRecord(q, r.GetInstanceAddr())),
|
|
|
+ r.iRecords(dns.Question{
|
|
|
+ Name: r.GetInstanceAddr().String(),
|
|
|
+ Qtype: dns.TypeANY,
|
|
|
+ })...,
|
|
|
)
|
|
|
-
|
|
|
- return append(ir, zr.Records(
|
|
|
- dns.Question{
|
|
|
- Name: zr.iAddr,
|
|
|
- Qtype: dns.TypeTXT,
|
|
|
- },
|
|
|
- )...)
|
|
|
- case dns.TypeA:
|
|
|
- var ir []dns.RR
|
|
|
- for _, ip := range zr.GetIPs() {
|
|
|
- switch ip.Type() {
|
|
|
- case host.IPv4:
|
|
|
- ir = append(ir, &dns.A{
|
|
|
- Hdr: dns.RR_Header{
|
|
|
- Name: zr.GetHostname().String(),
|
|
|
- Rrtype: dns.TypeA,
|
|
|
- Class: dns.ClassINET,
|
|
|
- Ttl: config.DefaultTTL,
|
|
|
- },
|
|
|
- })
|
|
|
- case host.IPv6:
|
|
|
- continue
|
|
|
- }
|
|
|
- }
|
|
|
- return ir
|
|
|
- case dns.TypeAAAA:
|
|
|
- var ir []dns.RR
|
|
|
- for _, ip := range zr.GetIPs() {
|
|
|
- switch ip.Type() {
|
|
|
- case host.IPv4:
|
|
|
- continue
|
|
|
- case host.IPv6:
|
|
|
- ir = append(ir, &dns.AAAA{
|
|
|
- Hdr: dns.RR_Header{
|
|
|
- Name: zr.GetHostname().String(),
|
|
|
- Rrtype: dns.TypeAAAA,
|
|
|
- Class: dns.ClassINET,
|
|
|
- Ttl: config.DefaultTTL,
|
|
|
- },
|
|
|
- AAAA: ip.AsIP().To16(),
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
- return ir
|
|
|
- case dns.TypeSRV:
|
|
|
- ir := []dns.RR{
|
|
|
- &dns.SRV{
|
|
|
- Hdr: dns.RR_Header{
|
|
|
- Name: q.Name,
|
|
|
- Rrtype: dns.TypeSRV,
|
|
|
- Class: dns.ClassINET,
|
|
|
- Ttl: config.DefaultTTL,
|
|
|
- },
|
|
|
- Priority: 10,
|
|
|
- Weight: 1,
|
|
|
- Port: zr.GetPort().Uint16(),
|
|
|
- Target: zr.GetHostname().String(),
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- ir = append(ir, zr.Records(
|
|
|
- dns.Question{
|
|
|
- Name: zr.iAddr,
|
|
|
- Qtype: dns.TypeA,
|
|
|
- },
|
|
|
- )...)
|
|
|
-
|
|
|
- return append(ir, zr.Records(
|
|
|
- dns.Question{
|
|
|
- Name: zr.iAddr,
|
|
|
- Qtype: dns.TypeAAAA,
|
|
|
- },
|
|
|
- )...)
|
|
|
- case dns.TypeTXT:
|
|
|
- return []dns.RR{
|
|
|
- &dns.TXT{
|
|
|
- Hdr: dns.RR_Header{
|
|
|
- Name: q.Name,
|
|
|
- Rrtype: dns.TypeTXT,
|
|
|
- Class: dns.ClassINET,
|
|
|
- Ttl: config.DefaultTTL,
|
|
|
- },
|
|
|
- },
|
|
|
- }
|
|
|
default:
|
|
|
+ log.Traceln(errors.Zone, "None sRecord for", r.qType(q))
|
|
|
return nil
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (zr *ZoneRecord) sEnum(q dns.Question) []dns.RR {
|
|
|
+// dRecords: DNS-SD / Discovery aka enumerate
|
|
|
+func (r *Resource) dRecords(q dns.Question) []dns.RR {
|
|
|
switch q.Qtype {
|
|
|
case dns.TypeANY, dns.TypePTR:
|
|
|
- return []dns.RR{
|
|
|
- &dns.PTR{
|
|
|
- Hdr: dns.RR_Header{
|
|
|
- Name: q.Name,
|
|
|
- Rrtype: dns.TypePTR,
|
|
|
- Class: dns.ClassINET,
|
|
|
- Ttl: config.DefaultTTL,
|
|
|
- },
|
|
|
- Ptr: zr.sAddr,
|
|
|
- },
|
|
|
- }
|
|
|
+ return r.records(r.ptrRecord(q, r.GetServiceAddr()))
|
|
|
default:
|
|
|
+ log.Traceln(errors.Zone, "None dRecord for", r.qType(q))
|
|
|
return nil
|
|
|
}
|
|
|
}
|