1 /**
2  * Client for DNS blacklist services.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <ae@cy.md>
12  */
13 
14 
15 module ae.net.dns.dnsbl;
16 
17 import std.socket;
18 import std.string;
19 import ae.net.asockets;
20 
21 /// Resolve a hostname to an IPv4 dotted quad.
22 string getIP(string hostname)
23 {
24 	try
25 		return (new InternetAddress(hostname, 0)).toAddrString;
26 	catch (Exception o)
27 		return null;
28 }
29 
30 /// Look up an IP address against a specific DNS blacklist.
31 /// Returns: the numeric code (generally indicating a
32 /// blacklist-specific list reason).
33 int lookupAgainst(string ip, string db)
34 {
35 	string[] sections = split(ip, ".");
36 	assert(sections.length == 4);
37 	string addr = sections[3] ~ "." ~ sections[2] ~ "." ~ sections[1] ~ "." ~ sections[0] ~ "." ~ db;
38 	InternetHost ih = new InternetHost;
39 	if (ih.getHostByName(addr))
40 		return ih.addrList[0] & 0xFF;
41 	else
42 		return 0;
43 }
44 
45 /// Look up an IP address against DroneBL.
46 /// Returns: a string describing the reason this IP is listed,
47 /// or `null` if the IP is not listed.
48 string lookupDroneBL(string ip)
49 {
50 	switch (lookupAgainst(ip, "dnsbl.dronebl.org"))
51 	{
52 		case  0: return null;
53 		case  2: return "Sample";
54 		case  3: return "IRC Drone";
55 		case  5: return "Bottler";
56 		case  6: return "Unknown spambot or drone";
57 		case  7: return "DDOS Drone";
58 		case  8: return "SOCKS Proxy";
59 		case  9: return "HTTP Proxy";
60 		case 10: return "ProxyChain";
61 		case 13: return "Brute force attackers";
62 		case 14: return "Open Wingate Proxy";
63 		case 15: return "Compromised router / gateway";
64 		default: return "Unknown";
65 	}
66 }
67 
68 /// Look up an IP address against the EFnet RBL.
69 /// Returns: a string describing the reason this IP is listed,
70 /// or `null` if the IP is not listed.
71 string lookupEfnetRBL(string ip)
72 {
73 	switch (lookupAgainst(ip, "rbl.efnetrbl.org"))
74 	{
75 		case  0: return null;
76 		case  1: return "Open Proxy";
77 		case  2: return "spamtrap666";
78 		case  3: return "spamtrap50";
79 		case  4: return "TOR";
80 		case  5: return "Drones / Flooding";
81 		default: return "Unknown";
82 	}
83 }
84 
85 /// Look up an IP address against the sectoor.de Tor exit node DNSBL.
86 /// Returns: a string describing the reason this IP is listed,
87 /// or `null` if the IP is not listed.
88 string lookupSectoorTorDNSBL(string ip)
89 {
90 	switch (lookupAgainst(ip, "exitnodes.tor.dnsbl.sectoor.de"))
91 	{
92 		case  0: return null;
93 		case  1: return "Tor exit node";
94 		default: return "Unknown";
95 	}
96 }
97 
98 /// Look up an IP address against the dan.me's Tor exit node DNSBL.
99 /// Returns: a string describing the reason this IP is listed,
100 /// or `null` if the IP is not listed.
101 string lookupDanTorDNSBL(string ip)
102 {
103 	switch (lookupAgainst(ip, "torexit.dan.me.uk"))
104 	{
105 		case   0: return null;
106 		case 100: return "Tor exit node";
107 		default : return "Unknown";
108 	}
109 }
110 
111 /// Look up an IP address in all implemented DNS blacklists.
112 /// Returns: null, or an array with three elements:
113 /// 0. a string describing the reason this IP is listed
114 /// 1. the name of the DNS blacklist service
115 /// 2. an URL with more information about the listing.
116 string[] blacklistCheck(string hostname)
117 {
118 	string ip = getIP(hostname);
119 
120 	if (!ip)
121 		throw new Exception("Can't resolve hostname to IPv4 address: " ~ hostname);
122 
123 	string result;
124 
125 	result = lookupDroneBL(ip);
126 	if (result) return [result, "DroneBL"  , "http://dronebl.org/lookup?ip="~ip];
127 
128 	result = lookupEfnetRBL(ip);
129 	if (result) return [result, "EFnet RBL", "http://rbl.efnetrbl.org/?i="  ~ip];
130 
131 	result = lookupSectoorTorDNSBL(ip);
132 	if (result) return [result, "Sectoor Tor exit node", "http://www.sectoor.de/tor.php"];
133 
134 	result = lookupDanTorDNSBL(ip);
135 	if (result) return [result, "Dan Tor exit node", "https://www.dan.me.uk/dnsbl"];
136 
137 	return null;
138 }