-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgodnsbl.go
179 lines (155 loc) · 5.23 KB
/
godnsbl.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// Package godnsbl implements dnsbl lookups
package godnsbl
import (
"context"
"fmt"
"log"
"net"
"strconv"
"strings"
)
// LookupIP gets the first reply from our lookup lists
func (l *LookupService) LookupIP(stringIP string) DnsblReturn {
l.TotalChecked++
reversedIP := reverseIP(stringIP)
returnChan := make(chan DnsblReturn)
counter := dnsblCounter{ClearCount: 0}
lookupCtx, lookupCancel := context.WithTimeout(context.Background(), l.timeout)
defer lookupCancel()
for key := range l.DnsblListing {
go l.dnsblLookup(lookupCtx, returnChan, stringIP, reversedIP, key)
}
// Loop until one of the following happen:
// 1. We find a match in any of the dronebl services we're using
// 2. We time out after the specified timeout
// 3. We successfully pass all lookups
for {
select {
case ok := <-returnChan: // Increment counter, and if it's a BLOCK type then return immediately
counter.Lock()
counter.ClearCount++
counter.Unlock()
if ok.Type == "BLOCK" {
return ok
}
case <-lookupCtx.Done(): // Lookup timed out
return DnsblReturn{
IP: stringIP,
Type: "TIMEOUT",
Dnsbl: "N/A",
}
default: // Check the counter and if it equals the number of Dnsbls we have, return all clear
counter.RLock()
endcount := counter.ClearCount
counter.RUnlock()
if endcount == len(l.DnsblListing) {
return DnsblReturn{
IP: stringIP,
Type: "CLEAR",
Dnsbl: "ALL",
}
}
}
}
}
// LookupIPGetAll gets replies from all Dnsbls, not just the first to reply with a block
func (l *LookupService) LookupIPGetAll(stringIP string) []DnsblReturn {
l.TotalChecked++
reversedIP := reverseIP(stringIP)
returnChan := make(chan DnsblReturn)
var replyList []DnsblReturn
lookupCtx, lookupCancel := context.WithTimeout(context.Background(), l.timeout)
defer lookupCancel()
for key := range l.DnsblListing {
go l.dnsblLookup(lookupCtx, returnChan, stringIP, reversedIP, key)
}
// Wait until we get all the replies or we time out
for {
select {
case <-lookupCtx.Done(): // We timed out, return what we have
return replyList
case reply := <-returnChan:
replyList = append(replyList, reply)
if len(replyList) == len(l.DnsblListing) {
return replyList
}
}
}
}
func (l *LookupService) dnsblLookup(lookupCtx context.Context, returnChan chan<- DnsblReturn, stringIP, reversedIP string, key int) {
returnDnsbl := DnsblReturn{
IP: stringIP,
Type: "CLEAR",
Dnsbl: l.DnsblListing[key].Name,
}
lookup := fmt.Sprintf("%s%s", reversedIP, l.DnsblListing[key].Address)
lookupReply, err := net.LookupHost(lookup)
if err != nil && !err.(*net.DNSError).IsNotFound {
log.Fatal("Couldn't lookup the host:", err)
}
if len(lookupReply) == 0 || replyMatch(lookupReply[0], l.DnsblListing[key].AllowList) {
select {
case <-lookupCtx.Done():
return
case returnChan <- returnDnsbl:
return
}
}
// Replace %IPADDR with the IP we're checking
returnMessage := strings.Replace(l.DnsblListing[key].BlockMessage, "%IPADDR", stringIP, -1)
// Lookup if we have a known index for the given reply, if we do tack it on the end of the message.
replyIndex := strings.LastIndex(lookupReply[0], ".") + 1
if l.DnsblListing[key].Reply[lookupReply[0][replyIndex:]] != "" {
returnMessage = fmt.Sprintf("%s (%s)", returnMessage, l.DnsblListing[key].Reply[lookupReply[0][replyIndex:]])
}
returnDnsbl.Type = "BLOCK"
returnDnsbl.Message = returnMessage
select {
case <-lookupCtx.Done():
return
case returnChan <- returnDnsbl:
return
}
}
// replyMatch checks to see if the string we got has a reply that's in our list of matches
// that we want to ban
func replyMatch(check string, list []int) bool {
checkstrip := strings.LastIndex(check, ".") + 1 // Strip all but the last part
checkint, err := strconv.Atoi(check[checkstrip:]) // Convert it to an int
if err != nil {
log.Fatal("Couldn't make an int: ", err)
}
// Loop for our ban list
for _, value := range list {
if value == checkint {
return true // Ban it!
}
}
return false // It's approved, let it pass.
}
// reverseIP takes the IP we're given and reverse it
func reverseIP(IP string) string {
var stringSplitIP []string
// Do IPvX-specific splitting
if net.ParseIP(IP).To4() != nil {
stringSplitIP = strings.Split(IP, ".") // Split into 4 groups
} else {
stringSplitIP = strings.Split(IP, ":") // Split into however many groups
/* Due to IPv6 lookups being different than IPv4 we have an extra check here
We have to expand the :: and do 0-padding if there are less than 4 digits */
for key := range stringSplitIP {
if len(stringSplitIP[key]) == 0 { // Found the ::
stringSplitIP[key] = strings.Repeat("0000", 8-strings.Count(IP, ":"))
} else if len(stringSplitIP[key]) < 4 { // 0-padding needed
stringSplitIP[key] = strings.Repeat("0", 4-len(stringSplitIP[key])) + stringSplitIP[key]
}
}
// We have to join what we have and split it again to get all the letters split individually
stringSplitIP = strings.Split(strings.Join(stringSplitIP, ""), "")
}
// Reverse the IP, join by . and return it
for x, y := 0, len(stringSplitIP)-1; x < y; x, y = x+1, y-1 {
stringSplitIP[x], stringSplitIP[y] = stringSplitIP[y], stringSplitIP[x] // Reverse the groups
}
return strings.Join(stringSplitIP, ".") // Return the IP.
}