summaryrefslogtreecommitdiff
path: root/dns/client.go
diff options
context:
space:
mode:
Diffstat (limited to 'dns/client.go')
-rw-r--r--dns/client.go144
1 files changed, 144 insertions, 0 deletions
diff --git a/dns/client.go b/dns/client.go
new file mode 100644
index 0000000..f322b45
--- /dev/null
+++ b/dns/client.go
@@ -0,0 +1,144 @@
1package dns
2
3import (
4 "fmt"
5
6 "github.com/miekg/dns"
7
8 "code.crute.me/mcrute/go_ddns_manager/bind"
9)
10
11type DNSClient struct {
12 Server string
13}
14
15type DNSTransaction struct {
16 zone *bind.Zone
17 key *bind.Key
18 msg *dns.Msg
19}
20
21func (t *DNSTransaction) Upsert(rrs ...RR) *DNSTransaction {
22 t.RemoveAll(rrs...).Insert(rrs...)
23 return t
24}
25
26func (t *DNSTransaction) Insert(rrs ...RR) *DNSTransaction {
27 t.msg.Insert(toRRSet(t.zone, rrs...))
28 return t
29}
30
31func (t *DNSTransaction) Remove(rrs ...RR) *DNSTransaction {
32 t.msg.Remove(toRRSet(t.zone, rrs...))
33 return t
34}
35
36func (t *DNSTransaction) RemoveAll(rrs ...RR) *DNSTransaction {
37 t.msg.RemoveRRset(toRRSet(t.zone, rrs...))
38 return t
39}
40
41func (c *DNSClient) AXFR(zone *bind.Zone) (chan *dns.Envelope, error) {
42 k := zone.Keys()[0]
43 t := &dns.Transfer{TsigSecret: k.AsMap()} // Always uses tcp
44
45 m := &dns.Msg{}
46 m.SetAxfr(zone.Name)
47 k.Sign(m)
48
49 return t.In(m, fmt.Sprintf("%s:53", c.Server))
50}
51
52func (c *DNSClient) ReadRemoteZone(zone *bind.Zone) ([]RR, error) {
53 rrs := []RR{}
54 seenSoa := false
55
56 data, err := c.AXFR(zone)
57 if err != nil {
58 return nil, err
59 }
60
61 for rd := range data {
62 for _, r := range rd.RR {
63 switch dr := FromDNS(r).(type) {
64 case *SOA:
65 // Transfers have 2 SOA records, exclude the last one
66 if !seenSoa {
67 rrs = append(rrs, dr)
68 seenSoa = true
69 }
70 case RR:
71 rrs = append(rrs, dr)
72 default:
73 // This should only be possible if we somehow are
74 // missing generated DNS data types.
75 return nil, fmt.Errorf("Invalid return type")
76 }
77 }
78 }
79
80 return rrs, nil
81}
82
83func (c *DNSClient) StartUpdate(zone *bind.Zone) *DNSTransaction {
84 m := &dns.Msg{}
85 m.SetUpdate(zone.Name)
86
87 return &DNSTransaction{
88 zone: zone,
89 key: zone.Keys()[0],
90 msg: m,
91 }
92}
93
94func (c *DNSClient) QueryRecursive(zone *bind.Zone, fqdn string, rtype uint16) *DNSTransaction {
95 m := &dns.Msg{}
96 m.RecursionDesired = true
97 m.SetQuestion(fqdn, rtype)
98
99 return &DNSTransaction{
100 zone: zone,
101 key: zone.Keys()[0],
102 msg: m,
103 }
104}
105
106func (c *DNSClient) SendUpdate(t *DNSTransaction) error {
107 udp := &dns.Client{Net: "udp", TsigSecret: t.key.AsMap()}
108 tcp := &dns.Client{Net: "tcp", TsigSecret: t.key.AsMap()}
109
110 t.msg.SetEdns0(4096, false)
111 t.key.Sign(t.msg)
112
113 in, _, err := udp.Exchange(t.msg, c.Server)
114 if in != nil && in.Truncated {
115 // If the TCP request succeeds, the err will reset to nil
116 in, _, err = tcp.Exchange(t.msg, c.Server)
117 }
118
119 if err != nil {
120 return err
121 }
122
123 return nil
124}
125
126func (c *DNSClient) SendQuery(t *DNSTransaction) ([]dns.RR, error) {
127 udp := &dns.Client{Net: "udp", TsigSecret: t.key.AsMap()}
128 tcp := &dns.Client{Net: "tcp", TsigSecret: t.key.AsMap()}
129
130 t.msg.SetEdns0(4096, false)
131 t.key.Sign(t.msg)
132
133 in, _, err := udp.Exchange(t.msg, c.Server)
134 if in != nil && in.Truncated {
135 // If the TCP request succeeds, the err will reset to nil
136 in, _, err = tcp.Exchange(t.msg, c.Server)
137 }
138
139 if err != nil {
140 return nil, err
141 }
142
143 return in.Answer, nil
144}