Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Sign In

weaveworks / weave / #5287

29 Jan 2016 - 16:32 coverage decreased (-0.08%) to 75.358%
#5287

Pull #1879

circleci

94ccfc629c6862b5950de3512742bcae?size=18&default=identiconbboreham
Wait for plugin status via unix socket
Pull Request #1879: Improve visibility of startup problems

6682 of 8867 relevant lines covered (75.36%)

92936.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.94
/nameserver/dns.go
1
package nameserver
2

3
import (
4
        "bytes"
5
        "fmt"
6
        "math/rand"
7
        "net"
8
        "sort"
9
        "strings"
10
        "time"
11

12
        "github.com/miekg/dns"
13

14
        "github.com/weaveworks/weave/net/address"
15
)
16

17
const (
18
        topDomain        = "."
19
        reverseDNSdomain = "in-addr.arpa."
20
        etcResolvConf    = "/etc/resolv.conf"
21
        udpBuffSize      = uint16(4096)
22
        minUDPSize       = 512
23

24
        DefaultListenAddress = "0.0.0.0:53"
25
        DefaultTTL           = 1
26
        DefaultClientTimeout = 5 * time.Second
27
)
28

29
type DNSServer struct {
30
        ns      *Nameserver
31
        domain  string
32
        ttl     uint32
33
        address string
34

35
        servers   []*dns.Server
36
        upstream  *dns.ClientConfig
37
        tcpClient *dns.Client
38
        udpClient *dns.Client
39
}
40

41
func filter(ss []string, s string) []string {
53×
42
        for i := 0; i < len(ss); {
106×
43
                if ss[i] == s {
!
44
                        ss = append(ss[:i], ss[i+1:]...)
!
45
                        continue
!
46
                }
47
                i++
106×
48
        }
49
        return ss
53×
50
}
51

52
func NewDNSServer(ns *Nameserver, domain, address, effectiveAddress string, ttl uint32, clientTimeout time.Duration) (*DNSServer, error) {
53×
53
        s := &DNSServer{
53×
54
                ns:        ns,
53×
55
                domain:    dns.Fqdn(domain),
53×
56
                ttl:       ttl,
53×
57
                address:   address,
53×
58
                tcpClient: &dns.Client{Net: "tcp", ReadTimeout: clientTimeout},
53×
59
                udpClient: &dns.Client{Net: "udp", ReadTimeout: clientTimeout, UDPSize: udpBuffSize},
53×
60
        }
53×
61
        var err error
53×
62
        if s.upstream, err = dns.ClientConfigFromFile(etcResolvConf); err != nil {
!
63
                return nil, err
!
64
        }
!
65
        if s.upstream != nil {
53×
66
                s.upstream.Servers = filter(s.upstream.Servers, effectiveAddress)
53×
67
        }
53×
68

69
        err = s.listen(address)
53×
70
        return s, err
53×
71
}
72

73
func (d *DNSServer) String() string {
!
74
        var buf bytes.Buffer
!
75
        fmt.Fprintf(&buf, "WeaveDNS (%s)\n", d.ns.ourName)
!
76
        fmt.Fprintf(&buf, "  listening on %s, for domain %s\n", d.address, d.domain)
!
77
        fmt.Fprintf(&buf, "  response ttl %d\n", d.ttl)
!
78
        return buf.String()
!
79
}
!
80

81
func (d *DNSServer) listen(address string) error {
53×
82
        udpListener, err := net.ListenPacket("udp", address)
53×
83
        if err != nil {
!
84
                return err
!
85
        }
!
86
        udpServer := &dns.Server{PacketConn: udpListener, Handler: d.createMux(d.udpClient, minUDPSize)}
53×
87

53×
88
        tcpListener, err := net.Listen("tcp", address)
53×
89
        if err != nil {
!
90
                udpServer.Shutdown()
!
91
                return err
!
92
        }
!
93
        tcpServer := &dns.Server{Listener: tcpListener, Handler: d.createMux(d.tcpClient, -1)}
53×
94

53×
95
        d.servers = []*dns.Server{udpServer, tcpServer}
53×
96
        return nil
53×
97
}
98

99
func (d *DNSServer) ActivateAndServe() {
53×
100
        for _, server := range d.servers {
106×
101
                go func(server *dns.Server) {
106×
102
                        server.ActivateAndServe()
106×
103
                }(server)
106×
104
        }
105
}
106

107
func (d *DNSServer) Stop() error {
53×
108
        for _, server := range d.servers {
106×
109
                if err := server.Shutdown(); err != nil {
!
110
                        return err
!
111
                }
!
112
        }
113
        return nil
53×
114
}
115

116
type handler struct {
117
        *DNSServer
118
        maxResponseSize int
119
        client          *dns.Client
120
}
121

122
func (d *DNSServer) createMux(client *dns.Client, defaultMaxResponseSize int) *dns.ServeMux {
106×
123
        m := dns.NewServeMux()
106×
124
        h := &handler{
106×
125
                DNSServer:       d,
106×
126
                maxResponseSize: defaultMaxResponseSize,
106×
127
                client:          client,
106×
128
        }
106×
129
        m.HandleFunc(d.domain, h.handleLocal)
106×
130
        m.HandleFunc(reverseDNSdomain, h.handleReverse)
106×
131
        m.HandleFunc(topDomain, h.handleRecursive)
106×
132
        return m
106×
133
}
106×
134

135
func (h *handler) handleLocal(w dns.ResponseWriter, req *dns.Msg) {
279×
136
        h.ns.debugf("local request: %+v", *req)
279×
137
        if len(req.Question) != 1 || req.Question[0].Qtype != dns.TypeA {
173×
138
                h.nameError(w, req)
173×
139
                return
173×
140
        }
173×
141

142
        hostname := dns.Fqdn(req.Question[0].Name)
106×
UNCOV
143
        if strings.Count(hostname, ".") == 1 {
!
UNCOV
144
                hostname = hostname + h.domain
!
UNCOV
145
        }
!
146

147
        addrs := h.ns.Lookup(hostname)
106×
148
        if len(addrs) == 0 {
35×
149
                h.nameError(w, req)
35×
150
                return
35×
151
        }
35×
152

153
        header := dns.RR_Header{
71×
154
                Name:   req.Question[0].Name,
71×
155
                Rrtype: dns.TypeA,
71×
156
                Class:  dns.ClassINET,
71×
157
                Ttl:    h.ttl,
71×
158
        }
71×
159
        answers := make([]dns.RR, len(addrs))
71×
160
        for i, addr := range addrs {
698×
161
                ip := addr.IP4()
698×
162
                answers[i] = &dns.A{Hdr: header, A: ip}
698×
163
        }
698×
164
        shuffleAnswers(&answers)
71×
165

71×
166
        h.respond(w, h.makeResponse(req, answers))
71×
167
}
168

169
func (h *handler) handleReverse(w dns.ResponseWriter, req *dns.Msg) {
67×
170
        h.ns.debugf("reverse request: %+v", *req)
67×
171
        if len(req.Question) != 1 || req.Question[0].Qtype != dns.TypePTR {
!
172
                h.nameError(w, req)
!
173
                return
!
174
        }
!
175

176
        ipStr := strings.TrimSuffix(req.Question[0].Name, "."+reverseDNSdomain)
67×
177
        ip, err := address.ParseIP(ipStr)
67×
178
        if err != nil {
!
179
                h.nameError(w, req)
!
180
                return
!
181
        }
!
182

183
        hostname, err := h.ns.ReverseLookup(ip.Reverse())
67×
184
        if err != nil {
1×
185
                h.handleRecursive(w, req)
1×
186
                return
1×
187
        }
1×
188

189
        header := dns.RR_Header{
66×
190
                Name:   req.Question[0].Name,
66×
191
                Rrtype: dns.TypePTR,
66×
192
                Class:  dns.ClassINET,
66×
193
                Ttl:    h.ttl,
66×
194
        }
66×
195
        answers := []dns.RR{&dns.PTR{
66×
196
                Hdr: header,
66×
197
                Ptr: hostname,
66×
198
        }}
66×
199

66×
200
        h.respond(w, h.makeResponse(req, answers))
66×
201
}
202

203
func (h *handler) handleRecursive(w dns.ResponseWriter, req *dns.Msg) {
12×
204
        h.ns.debugf("recursive request: %+v", *req)
12×
205

12×
206
        // Resolve unqualified names locally
12×
207
        if len(req.Question) == 1 && req.Question[0].Qtype == dns.TypeA {
2×
208
                hostname := dns.Fqdn(req.Question[0].Name)
2×
UNCOV
209
                if strings.Count(hostname, ".") == 1 {
!
UNCOV
210
                        h.handleLocal(w, req)
!
UNCOV
211
                        return
!
UNCOV
212
                }
!
213
        }
214

215
        for _, server := range h.upstream.Servers {
12×
216
                reqCopy := req.Copy()
12×
217
                reqCopy.Id = dns.Id()
12×
218
                response, _, err := h.client.Exchange(reqCopy, fmt.Sprintf("%s:%s", server, h.upstream.Port))
12×
219
                if (err != nil && err != dns.ErrTruncated) || response == nil {
!
220
                        h.ns.debugf("error trying %s: %v", server, err)
!
221
                        continue
!
222
                }
223
                response.Id = req.Id
12×
224
                if h.responseTooBig(req, response) {
1×
225
                        response.Compress = true
1×
226
                }
1×
227
                h.respond(w, response)
12×
228
                return
12×
229
        }
230

231
        h.respond(w, h.makeErrorResponse(req, dns.RcodeServerFailure))
!
232
}
233

234
func (h *handler) makeResponse(req *dns.Msg, answers []dns.RR) *dns.Msg {
10,137×
235
        response := &dns.Msg{}
10,137×
236
        response.SetReply(req)
10,137×
237
        response.RecursionAvailable = true
10,137×
238
        response.Authoritative = true
10,137×
239
        response.Answer = answers
10,137×
240
        if !h.responseTooBig(req, response) {
220×
241
                return response
220×
242
        }
220×
243

244
        // search for smallest i that is too big
245
        maxSize := h.getMaxResponseSize(req)
9,917×
246
        i := sort.Search(len(answers), func(i int) bool {
69,926×
247
                // return true if too big
69,926×
248
                response.Answer = answers[:i+1]
69,926×
249
                return response.Len() > maxSize
69,926×
250
        })
69,926×
251

252
        response.Answer = answers[:i]
9,917×
253
        if i < len(answers) {
9,917×
254
                response.Truncated = true
9,917×
255
        }
9,917×
256
        return response
9,917×
257
}
258

259
func (h *handler) makeErrorResponse(req *dns.Msg, code int) *dns.Msg {
208×
260
        response := &dns.Msg{}
208×
261
        response.SetReply(req)
208×
262
        response.RecursionAvailable = true
208×
263
        response.Rcode = code
208×
264
        return response
208×
265
}
208×
266

267
func (h *handler) responseTooBig(req, response *dns.Msg) bool {
10,149×
268
        return len(response.Answer) > 1 && h.maxResponseSize > 0 && response.Len() > h.getMaxResponseSize(req)
10,149×
269
}
10,149×
270

271
func (h *handler) respond(w dns.ResponseWriter, response *dns.Msg) {
357×
272
        h.ns.debugf("response: %+v", response)
357×
273
        if err := w.WriteMsg(response); err != nil {
!
274
                h.ns.infof("error responding: %v", err)
!
275
        }
!
276
}
277

278
func (h *handler) nameError(w dns.ResponseWriter, req *dns.Msg) {
208×
279
        h.respond(w, h.makeErrorResponse(req, dns.RcodeNameError))
208×
280
}
208×
281

282
func (h *handler) getMaxResponseSize(req *dns.Msg) int {
19,943×
283
        if opt := req.IsEdns0(); opt != nil {
6×
284
                return int(opt.UDPSize())
6×
285
        }
6×
286
        return h.maxResponseSize
19,937×
287
}
288

289
func shuffleAnswers(answers *[]dns.RR) {
71×
290
        if len(*answers) <= 1 {
42×
291
                return
42×
292
        }
42×
293

294
        for i := range *answers {
656×
295
                j := rand.Intn(i + 1)
656×
296
                (*answers)[i], (*answers)[j] = (*answers)[j], (*answers)[i]
656×
297
        }
656×
298
}
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
BLOG · TWITTER · Legal & Privacy · Supported CI Services · What's a CI service? · Automated Testing

© 2022 Coveralls, Inc