-
Notifications
You must be signed in to change notification settings - Fork 0
/
dns.go
123 lines (116 loc) · 3.16 KB
/
dns.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package main
import (
"fmt"
"net"
"strings"
"github.com/miekg/dns"
log "github.com/sirupsen/logrus"
)
func (e *localDNSProviderSolver) handleDNSRequest(w dns.ResponseWriter, req *dns.Msg) {
msg := new(dns.Msg)
msg.SetReply(req)
switch req.Opcode {
case dns.OpcodeQuery:
for _, q := range msg.Question {
if err := e.addDNSAnswer(q, msg, req); err != nil {
msg.SetRcode(req, dns.RcodeServerFailure)
break
}
}
}
w.WriteMsg(msg)
}
func (e *localDNSProviderSolver) PublicIPIsCNAME() bool {
// if e.PublicIP is not an IP address, it must be a CNAME
pubIP := e.PublicIP
if pubIP == "" {
pubIP = e.Nameserver
}
if pubIP == "" {
return false
}
// if the IP is not an IP address, it must be a CNAME
return net.ParseIP(pubIP) == nil
}
func (e *localDNSProviderSolver) addDNSAnswer(q dns.Question, msg *dns.Msg, req *dns.Msg) error {
q.Name = strings.ToLower(q.Name)
l := log.WithFields(log.Fields{
"question": q,
"qname": q.Name,
"type": dns.TypeToString[q.Qtype],
"msg": msg,
"req": req,
})
l.Debug("handling DNS request")
switch q.Qtype {
// TXT records are the only important record for ACME dns-01 challenges
case dns.TypeTXT:
l.Debug("handling TXT request")
record, err := e.store.Get(q.Name)
if err != nil {
msg.SetRcode(req, dns.RcodeNameError)
return nil
}
rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN TXT %s", q.Name, record))
if err != nil {
return err
}
msg.Answer = append(msg.Answer, rr)
return nil
case dns.TypeCNAME:
l.Debug("handling CNAME request")
// always return bad domain name error for any CNAME query except
// for the domain name we are authoritative for
if q.Name != e.DomainName {
msg.SetRcode(req, dns.RcodeNameError)
return nil
}
// if the public IP is a CNAME, return that
if e.PublicIPIsCNAME() {
rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN CNAME %s", q.Name, e.PublicIP))
if err != nil {
return err
}
msg.Answer = append(msg.Answer, rr)
return nil
}
return nil
// Always return loopback for any A query
case dns.TypeA:
l.Debug("handling A request")
v := "127.0.0.1"
if q.Name == e.DomainName && !e.PublicIPIsCNAME() {
v = e.PublicIP
}
rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN A %s", q.Name, v))
if err != nil {
return err
}
msg.Answer = append(msg.Answer, rr)
l.WithField("answer", rr).Debug("added answer")
return nil
// NS and SOA are for authoritative lookups, return the values configured
case dns.TypeNS:
l.Debug("handling NS request")
rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN NS %s", q.Name, e.Nameserver))
if err != nil {
l.WithError(err).Error("failed to create NS record")
return err
}
msg.Answer = append(msg.Answer, rr)
l.WithField("answer", rr).Debug("added answer")
return nil
case dns.TypeSOA:
l.Debug("handling SOA request")
rr, err := dns.NewRR(fmt.Sprintf("%s 5 IN SOA %s %s 0 0 0 0 0", e.Nameserver, e.Nameserver, e.RName))
if err != nil {
l.WithError(err).Error("failed to create SOA record")
return err
}
msg.Answer = append(msg.Answer, rr)
l.WithField("answer", rr).Debug("added answer")
return nil
default:
return fmt.Errorf("unimplemented record type %v", q.Qtype)
}
}