forked from Eun/coredns-ipecho
/
ipecho.go
129 lines (116 loc) · 3.02 KB
/
ipecho.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
124
125
126
127
128
129
package ipecho
import (
"log"
"net"
"strings"
"github.com/coredns/coredns/plugin"
"github.com/miekg/dns"
"golang.org/x/net/context"
)
type ipecho struct {
Next plugin.Handler
Config *config
}
// ServeDNS implements the middleware.Handler interface.
func (p ipecho) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
if p.echoIP(w, r) {
return dns.RcodeSuccess, nil
}
return plugin.NextOrFailure(p.Name(), p.Next, ctx, w, r)
}
// Name implements the Handler interface.
func (ipecho) Name() string { return "IPEcho" }
func (p *ipecho) echoIP(w dns.ResponseWriter, r *dns.Msg) bool {
if len(r.Question) <= 0 {
return false
}
var rrs []dns.RR
for i := 0; i < len(r.Question); i++ {
question := r.Question[i]
if question.Qclass != dns.ClassINET {
continue
}
if question.Qtype == dns.TypeA || question.Qtype == dns.TypeAAAA {
ip := p.parseIP(&question)
if ip == nil {
if p.Config.Debug {
log.Printf("[ipecho] Parsed IP of '%s' is nil\n", question.Name)
}
continue
}
// not an ip4
if ip4 := ip.To4(); ip4 != nil {
if p.Config.Debug {
log.Printf("[ipecho] Parsed IP of '%s' is an IPv4 address\n", question.Name)
}
rrs = append(rrs, &dns.A{
Hdr: dns.RR_Header{
Name: question.Name,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: p.Config.TTL,
},
A: ip,
})
} else {
if p.Config.Debug {
log.Printf("[ipecho] Parsed IP of '%s' is an IPv6 address\n", question.Name)
}
rrs = append(rrs, &dns.AAAA{
Hdr: dns.RR_Header{
Name: question.Name,
Rrtype: dns.TypeAAAA,
Class: dns.ClassINET,
Ttl: p.Config.TTL,
},
AAAA: ip,
})
}
}
}
if len(rrs) > 0 {
if p.Config.Debug {
log.Printf("[ipecho] Answering with %d rr's\n", len(rrs))
}
m := new(dns.Msg)
m.SetReply(r)
m.Answer = rrs
w.WriteMsg(m)
return true
}
return false
}
func (p *ipecho) parseIP(question *dns.Question) net.IP {
if p.Config.Debug {
log.Printf("[ipecho] Query for '%s'", question.Name)
}
for _, domain := range p.Config.Domains {
if strings.HasSuffix(strings.ToLower(question.Name), domain) == true {
subdomain := question.Name[:len(question.Name)-len(domain)]
if len(subdomain) <= 0 {
if p.Config.Debug {
log.Printf("[ipecho] Query ('%s') has no subomain\n", question.Name)
}
return nil
}
subdomain = strings.Trim(subdomain, ".")
if len(subdomain) <= 0 {
if p.Config.Debug {
log.Printf("[ipecho] Parsed Subdomain of '%s' is empty\n", question.Name)
}
return nil
}
if splitdomain := strings.Split(subdomain, "-"); len(splitdomain) > 1 {
subdomain = splitdomain[len(splitdomain)-1]
}
if p.Config.Debug {
log.Printf("[ipecho] Parsed Subdomain of '%s' is '%s'\n", question.Name, subdomain)
}
return net.ParseIP(subdomain)
}
}
if p.Config.Debug {
log.Printf("[ipecho] Query ('%s') does not end with one of the domains (%s)\n", question.Name, strings.Join(p.Config.Domains, ", "))
}
return nil
}