|
@@ -0,0 +1,142 @@
|
|
1
|
+package irc
|
|
2
|
+
|
|
3
|
+import (
|
|
4
|
+ "fmt"
|
|
5
|
+ "net"
|
|
6
|
+ "sort"
|
|
7
|
+ "strings"
|
|
8
|
+
|
|
9
|
+ "github.com/oragono/oragono/irc/sno"
|
|
10
|
+)
|
|
11
|
+
|
|
12
|
+func ReverseAddress(ip net.IP) string {
|
|
13
|
+ // This is a IPv4 address
|
|
14
|
+ if ip.To4() != nil {
|
|
15
|
+ address := strings.Split(ip.String(), ".")
|
|
16
|
+
|
|
17
|
+ for i, j := 0, len(address)-1; i < j; i, j = i+1, j-1 {
|
|
18
|
+ address[i], address[j] = address[j], address[i]
|
|
19
|
+ }
|
|
20
|
+
|
|
21
|
+ return strings.Join(address, ".")
|
|
22
|
+ }
|
|
23
|
+
|
|
24
|
+ // fallback to returning the String of IP if it is not an IPv4 address
|
|
25
|
+ return ip.String()
|
|
26
|
+}
|
|
27
|
+
|
|
28
|
+func LastIpOctet(addr string) string {
|
|
29
|
+ address := strings.Split(addr, ".")
|
|
30
|
+
|
|
31
|
+ return address[len(address)-1]
|
|
32
|
+}
|
|
33
|
+
|
|
34
|
+func (server *Server) LookupBlacklistEntry(list *DnsblListEntry, client *Client) []string {
|
|
35
|
+ res, err := net.LookupHost(fmt.Sprintf("%s.%s", ReverseAddress(client.IP()), list.Host))
|
|
36
|
+
|
|
37
|
+ var entries []string
|
|
38
|
+ if err != nil {
|
|
39
|
+ server.logger.Info("dnsbl-lookup", fmt.Sprintf("DNSBL loopup failed: %s", err))
|
|
40
|
+ return entries
|
|
41
|
+ }
|
|
42
|
+
|
|
43
|
+ if len(res) > 0 {
|
|
44
|
+ for _, addr := range res {
|
|
45
|
+ entries = append(entries, LastIpOctet(addr))
|
|
46
|
+ }
|
|
47
|
+ }
|
|
48
|
+
|
|
49
|
+ return entries
|
|
50
|
+}
|
|
51
|
+
|
|
52
|
+func sendDnsblMessage(client *Client, message string) {
|
|
53
|
+ /*fmt.Printf(client.server.DnsblConfig().Channel)
|
|
54
|
+ if channel := client.server.DnsblConfig().Channel; channel != "" {
|
|
55
|
+ fmt.Printf(channel)
|
|
56
|
+ client.Send(nil, client.server.name, "PRIVMSG", channel, message)
|
|
57
|
+ }
|
|
58
|
+ */
|
|
59
|
+ client.server.snomasks.Send(sno.Dnsbl, message)
|
|
60
|
+}
|
|
61
|
+
|
|
62
|
+// ProcessBlacklist does
|
|
63
|
+func (server *Server) ProcessBlacklist(client *Client) {
|
|
64
|
+
|
|
65
|
+ if !server.DnsblConfig().Enabled || len(server.DnsblConfig().Lists) == 0 {
|
|
66
|
+ // do nothing if dnsbl is disabled, empty lists is treated as if dnsbl was disabled
|
|
67
|
+ return
|
|
68
|
+ }
|
|
69
|
+
|
|
70
|
+ type DnsblTypeResponse struct {
|
|
71
|
+ Host string
|
|
72
|
+ Action string
|
|
73
|
+ Reason string
|
|
74
|
+ }
|
|
75
|
+ var items = []DnsblTypeResponse{}
|
|
76
|
+ for _, list := range server.DnsblConfig().Lists {
|
|
77
|
+ response := DnsblTypeResponse{
|
|
78
|
+ Host: list.Host,
|
|
79
|
+ Action: list.Action,
|
|
80
|
+ Reason: list.Reason,
|
|
81
|
+ }
|
|
82
|
+ // update action/reason if matched with new ...
|
|
83
|
+ for _, entry := range server.LookupBlacklistEntry(&list, client) {
|
|
84
|
+ if reply, exists := list.Reply[entry]; exists {
|
|
85
|
+ response.Action, response.Reason = reply.Action, reply.Reason
|
|
86
|
+ }
|
|
87
|
+ items = append(items, response)
|
|
88
|
+ }
|
|
89
|
+ }
|
|
90
|
+
|
|
91
|
+ // Sort responses so that require-sasl blocks come first. Otherwise A>B (allow>block, allow>notify, block>notify)
|
|
92
|
+ // so that responses come in this order:
|
|
93
|
+ // - require-sasl
|
|
94
|
+ // - allow
|
|
95
|
+ // - block
|
|
96
|
+ // - notify
|
|
97
|
+ sort.Slice(items, func(i, j int) bool {
|
|
98
|
+ if items[i].Action == "require-sasl" {
|
|
99
|
+ return true
|
|
100
|
+ }
|
|
101
|
+ return items[i].Action > items[j].Action
|
|
102
|
+ })
|
|
103
|
+
|
|
104
|
+ if len(items) > 0 {
|
|
105
|
+ item := items[0]
|
|
106
|
+ switch item.Action {
|
|
107
|
+ case "require-sasl":
|
|
108
|
+ sendDnsblMessage(client, fmt.Sprintf("Connecting client %s matched %s, requiring SASL to proceed", client.IP(), item.Host))
|
|
109
|
+ client.SetRequireSasl(true, item.Reason)
|
|
110
|
+
|
|
111
|
+ case "block":
|
|
112
|
+ sendDnsblMessage(client, fmt.Sprintf("Connecting client %s matched %s - killing", client.IP(), item.Host))
|
|
113
|
+ client.Quit(strings.Replace(item.Reason, "{ip}", client.IPString(), -1))
|
|
114
|
+
|
|
115
|
+ case "notify":
|
|
116
|
+ sendDnsblMessage(client, fmt.Sprintf("Connecting client %s matched %s", client.IP(), item.Host))
|
|
117
|
+
|
|
118
|
+ case "allow":
|
|
119
|
+ sendDnsblMessage(client, fmt.Sprintf("Allowing host %s [%s]", client.IP(), item.Host))
|
|
120
|
+ }
|
|
121
|
+ }
|
|
122
|
+
|
|
123
|
+ return
|
|
124
|
+}
|
|
125
|
+
|
|
126
|
+func connectionRequiresSasl(client *Client) bool {
|
|
127
|
+ sasl, reason := client.RequireSasl()
|
|
128
|
+
|
|
129
|
+ if !sasl {
|
|
130
|
+ return false
|
|
131
|
+ }
|
|
132
|
+
|
|
133
|
+ if client.Account() == "" {
|
|
134
|
+ sendDnsblMessage(client, fmt.Sprintf("Connecting client %s and did not authenticate through SASL - blocking connection", client.IP()))
|
|
135
|
+ client.Quit(strings.Replace(reason, "{ip}", client.IPString(), -1))
|
|
136
|
+ return true
|
|
137
|
+ }
|
|
138
|
+
|
|
139
|
+ sendDnsblMessage(client, fmt.Sprintf("Connecting client %s authenticated through SASL - allowing", client.IP()))
|
|
140
|
+
|
|
141
|
+ return false
|
|
142
|
+}
|