This repository has been archived by the owner on Oct 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.go
118 lines (99 loc) · 2.51 KB
/
main.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
package main
import (
"fmt"
"log"
"net"
"os"
"os/signal"
"strconv"
"strings"
"time"
"github.com/miekg/dns"
)
type Info struct {
Timestamp time.Time
CustomValues []string
}
var (
saveChan = make(chan Info)
logPath = getEnvOrFallback("LOGPATH", "/output.log")
host = getEnvOrFallback("HOST", "0.0.0.0")
port = getEnvOrFallback("PORT", "53")
listenAddress = net.JoinHostPort(host, port)
domain = getEnvOrFallback("DOMAIN", "stats.mailu.io.")
matchingParts = dns.CountLabel(domain)
valueCount int
)
func fatalErr(e error) {
if e != nil {
log.Fatal(e)
}
}
func getEnvOrFallback(key, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return fallback
}
func init() {
n, err := strconv.Atoi(getEnvOrFallback("VALUECOUNT", "2"))
fatalErr(err)
valueCount = n
}
func main() {
// open log file and channel to write to it
go func() {
f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
fatalErr(err)
defer f.Close()
for v := range saveChan {
line := fmt.Sprintf("%s,%d\n", strings.Join(v.CustomValues, ","), v.Timestamp.Unix())
fmt.Print(line)
if _, err := f.WriteString(line); err != nil {
log.Printf("Could not write to file: %v\n", err)
}
}
}()
// start DNS-server
go func() {
fatalErr(dns.ListenAndServe(listenAddress, "udp", dns.HandlerFunc(handleQuery)))
}()
log.Printf("serving queries for subdomains of %s on %s", domain, listenAddress)
// block until interrupt
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
<-c
}
func handleQuery(w dns.ResponseWriter, r *dns.Msg) {
// respond to queries with SERVFAIL, then close the connection
// -> clients don't cache the result and won't block until they timeout
defer w.Close()
defer w.WriteMsg(&dns.Msg{
MsgHdr: dns.MsgHdr{
Response: true,
Opcode: dns.OpcodeQuery,
RecursionAvailable: true,
RecursionDesired: true,
Rcode: dns.RcodeServerFailure,
},
Question: r.Question,
})
for _, m := range r.Question {
// not a subdomain of domain
if dns.CompareDomainName(domain, m.Name) < matchingParts {
return
}
// more values than we asked for
if dns.CountLabel(m.Name) > valueCount+matchingParts {
return
}
// getting only the "value"-domainparts
domainParts := dns.SplitDomainName(m.Name)
values := domainParts[0 : len(domainParts)-matchingParts]
// push to channel for saving
saveChan <- Info{
Timestamp: time.Now(),
CustomValues: values,
}
}
}