diff options
Diffstat (limited to 'dns/client.go')
-rw-r--r-- | dns/client.go | 144 |
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 @@ | |||
1 | package dns | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/miekg/dns" | ||
7 | |||
8 | "code.crute.me/mcrute/go_ddns_manager/bind" | ||
9 | ) | ||
10 | |||
11 | type DNSClient struct { | ||
12 | Server string | ||
13 | } | ||
14 | |||
15 | type DNSTransaction struct { | ||
16 | zone *bind.Zone | ||
17 | key *bind.Key | ||
18 | msg *dns.Msg | ||
19 | } | ||
20 | |||
21 | func (t *DNSTransaction) Upsert(rrs ...RR) *DNSTransaction { | ||
22 | t.RemoveAll(rrs...).Insert(rrs...) | ||
23 | return t | ||
24 | } | ||
25 | |||
26 | func (t *DNSTransaction) Insert(rrs ...RR) *DNSTransaction { | ||
27 | t.msg.Insert(toRRSet(t.zone, rrs...)) | ||
28 | return t | ||
29 | } | ||
30 | |||
31 | func (t *DNSTransaction) Remove(rrs ...RR) *DNSTransaction { | ||
32 | t.msg.Remove(toRRSet(t.zone, rrs...)) | ||
33 | return t | ||
34 | } | ||
35 | |||
36 | func (t *DNSTransaction) RemoveAll(rrs ...RR) *DNSTransaction { | ||
37 | t.msg.RemoveRRset(toRRSet(t.zone, rrs...)) | ||
38 | return t | ||
39 | } | ||
40 | |||
41 | func (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 | |||
52 | func (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 | |||
83 | func (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 | |||
94 | func (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 | |||
106 | func (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 | |||
126 | func (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 | } | ||