summaryrefslogtreecommitdiff
path: root/bind/config.go
diff options
context:
space:
mode:
Diffstat (limited to 'bind/config.go')
-rw-r--r--bind/config.go227
1 files changed, 227 insertions, 0 deletions
diff --git a/bind/config.go b/bind/config.go
new file mode 100644
index 0000000..584b1ee
--- /dev/null
+++ b/bind/config.go
@@ -0,0 +1,227 @@
1package bind
2
3import (
4 "fmt"
5 "io/ioutil"
6 "strings"
7 "time"
8
9 "github.com/miekg/dns"
10)
11
12type Signable interface {
13 SetTsig(string, string, uint16, int64) *dns.Msg
14}
15
16func NewBINDConfig() *BINDConfig {
17 return &BINDConfig{
18 zones: map[string]map[string]*Zone{},
19 keys: map[string]*Key{},
20 }
21}
22
23type BINDConfig struct {
24 zones map[string]map[string]*Zone
25 keys map[string]*Key
26}
27
28func (c *BINDConfig) Views() []string {
29 v := []string{}
30 for vn, _ := range c.zones {
31 v = append(v, vn)
32 }
33 return v
34}
35
36func (c *BINDConfig) Zone(view, name string) *Zone {
37 if !strings.HasSuffix(name, ".") {
38 name = fmt.Sprintf("%s.", name)
39 }
40
41 if _, ok := c.zones[view]; !ok {
42 return nil
43 }
44
45 z, ok := c.zones[view][name]
46 if !ok {
47 return nil
48 }
49
50 if z.InView != "" {
51 return c.zones[z.InView][name]
52 }
53
54 return z
55}
56
57func (c *BINDConfig) AddKey(k *Key) {
58 c.keys[k.Name] = k
59}
60
61func (c *BINDConfig) AddZone(z *Zone, view string) {
62 if c.zones[view] == nil {
63 c.zones[view] = map[string]*Zone{}
64 }
65 c.zones[view][z.Name] = z
66 z.config = c
67}
68
69func NewZone(name string) *Zone {
70 // Canonicalize name
71 if !strings.HasSuffix(name, ".") {
72 name = fmt.Sprintf("%s.", name)
73 }
74
75 return &Zone{
76 Name: name,
77 keys: []string{},
78 }
79}
80
81type Zone struct {
82 Name string
83 Type string
84 InView string
85 keys []string
86 config *BINDConfig
87}
88
89func (z *Zone) AddKey(key string) {
90 z.keys = append(z.keys, key)
91}
92
93func (z *Zone) Keys() []*Key {
94 k := []*Key{}
95 for _, kn := range z.keys {
96 k = append(k, z.config.keys[kn])
97 }
98 return k
99}
100
101type Key struct {
102 Name string
103 Algorithm string
104 Secret string
105}
106
107func (k *Key) CanonicalName() string {
108 if !strings.HasSuffix(k.Name, ".") {
109 return fmt.Sprintf("%s.", k.Name)
110 }
111 return k.Name
112}
113
114func (k *Key) CanonicalAlgorithm() string {
115 if !strings.HasSuffix(k.Algorithm, ".") {
116 return fmt.Sprintf("%s.", k.Algorithm)
117 }
118 return k.Name
119}
120
121func (k *Key) Sign(r Signable) {
122 r.SetTsig(k.CanonicalName(), k.CanonicalAlgorithm(), 300, time.Now().Unix())
123}
124
125func (k *Key) AsMap() map[string]string {
126 return map[string]string{k.CanonicalName(): k.Secret}
127}
128
129func NewStringStack() *StringStack {
130 return &StringStack{[]string{}}
131}
132
133type StringStack struct {
134 items []string
135}
136
137func (s *StringStack) isEmpty() bool {
138 return len(s.items) == 0
139}
140
141func (s *StringStack) Peek() string {
142 return s.items[len(s.items)-1]
143}
144
145func (s *StringStack) Pop() string {
146 if s.isEmpty() {
147 return ""
148 }
149 v := s.items[len(s.items)-1]
150 s.items = s.items[:len(s.items)-1]
151 return v
152}
153
154func (s *StringStack) Push(v string) {
155 s.items = append(s.items, v)
156}
157
158func cleanValue(s string) string {
159 return strings.Trim(strings.Trim(s, ";"), "\"")
160}
161
162func isContainerStart(line []string) bool {
163 return line[len(line)-1] == "{"
164}
165
166func ParseBINDConfig(filename string) (*BINDConfig, error) {
167 data, err := ioutil.ReadFile(filename)
168 if err != nil {
169 return nil, err
170 }
171
172 var view string
173 var zone *Zone
174 var key *Key
175
176 config := NewBINDConfig()
177 stack := NewStringStack()
178
179 for _, line := range strings.Split(string(data), "\n") {
180 line := strings.Split(strings.TrimSpace(line), " ")
181
182 switch line[0] {
183 case "};":
184 t := stack.Pop()
185 switch t {
186 case "view":
187 view = ""
188 case "key":
189 key = nil
190 case "zone":
191 zone = nil
192 }
193 case "view":
194 view = cleanValue(line[1])
195 stack.Push("view")
196 case "zone":
197 zone = NewZone(cleanValue(line[1]))
198 config.AddZone(zone, view)
199 stack.Push("zone")
200 case "in-view":
201 zone.Type = "reference"
202 zone.InView = cleanValue(line[1])
203 case "type":
204 zone.Type = cleanValue(line[1])
205 case "grant":
206 zone.AddKey(cleanValue(line[1]))
207 case "key":
208 if !isContainerStart(line) {
209 continue
210 }
211
212 key = &Key{Name: cleanValue(line[1])}
213 config.AddKey(key)
214 stack.Push("key")
215 case "algorithm":
216 key.Algorithm = cleanValue(line[1])
217 case "secret":
218 key.Secret = cleanValue(line[1])
219 default:
220 if isContainerStart(line) {
221 stack.Push(line[0])
222 }
223 }
224 }
225
226 return config, nil
227}