1 // For all support, instructions and copyright go to:
2 // http://e2guardian.org/
3 // Released under the GPL v2, with the OpenSSL exception described in the README file.
4
5 // INCLUDES
6
7 #ifdef HAVE_CONFIG_H
8 #include "dgconfig.h"
9 #endif
10 #include "OptionContainer.hpp"
11 #include "FOptionContainer.hpp"
12
13 #include <iostream>
14 #include <fstream>
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <arpa/inet.h>
18 #include <algorithm>
19 #include <memory>
20 #include <list>
21 #include <vector>
22 #include <syslog.h>
23
24 // GLOBALS
25
26 extern OptionContainer o;
27 extern bool is_daemonised;
28 extern thread_local std::string thread_id;
29
30 // INPLEMENTATION
31
32 // clear out the list
reset()33 void IPList::reset()
34 {
35 iplist.clear();
36 iprangelist.clear();
37 ipsubnetlist.clear();
38 hostlist.clear();
39 }
40
41 // search for IP in list of individual IPs, ranges, subnets and - if reverse lookups are enabled - hostnames.
inList(const std::string & ipstr,std::string * & host) const42 bool IPList::inList(const std::string &ipstr, std::string *&host) const
43 {
44 struct in_addr addr;
45 inet_aton(ipstr.c_str(), &addr);
46 uint32_t ip = ntohl(addr.s_addr);
47 // start with individual IPs
48 if (std::binary_search(iplist.begin(), iplist.end(), ip)) {
49 // only return a hostname if that's what we matched against
50 delete host;
51 host = NULL;
52 return true;
53 }
54
55 // ranges
56 for (std::list<ipl_rangestruct>::const_iterator i = iprangelist.begin(); i != iprangelist.end(); ++i) {
57 if ((ip >= i->startaddr) && (ip <= i->endaddr)) {
58 delete host;
59 host = NULL;
60 return true;
61 }
62 }
63
64 // subnets
65 for (std::list<ipl_subnetstruct>::const_iterator i = ipsubnetlist.begin(); i != ipsubnetlist.end(); ++i) {
66 if (i->maskedaddr == (ip & i->mask)) {
67 delete host;
68 host = NULL;
69 return true;
70 }
71 }
72
73 // hostnames
74 // TODO - take in a suggested hostname, look up only if not supplied, and return suggestion if found
75 if (o.reverse_client_ip_lookups) {
76 std::unique_ptr<std::deque<String> > hostnames;
77 if (host == NULL)
78 hostnames.reset(ipToHostname(ipstr.c_str()));
79 else {
80 hostnames.reset(new std::deque<String>);
81 hostnames->push_back(*host);
82 }
83 for (std::deque<String>::iterator i = hostnames->begin(); i != hostnames->end(); ++i) {
84 if (std::binary_search(hostlist.begin(), hostlist.end(), *i)) {
85 delete host;
86 host = new std::string(i->toCharArray());
87 return true;
88 }
89 }
90 // Even if we don't match anything, return a hostname
91 // if desired for logging and we don't already have one.
92 if (o.log_client_hostnames && (host == NULL) && (hostnames->size() > 0))
93 host = new std::string(hostnames->front().toCharArray());
94 }
95
96 return false;
97 }
98
ifsreadIPMelangeList(std::ifstream * input,bool checkendstring,const char * endstring)99 bool IPList::ifsreadIPMelangeList(std::ifstream *input, bool checkendstring, const char *endstring)
100 {
101 // compile regexps for determining whether a list entry is an IP, a subnet (IP + mask), or a range
102 RegExp matchIP, matchSubnet, matchRange, matchCIDR;
103 #ifdef HAVE_PCRE
104 matchIP.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$");
105 matchSubnet.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$");
106 matchSubnet.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$");
107 matchCIDR.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,2}$");
108 matchRange.comp("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}-\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$");
109 #else
110 matchIP.comp("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$");
111 matchSubnet.comp("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$");
112 matchCIDR.comp("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2}$");
113 matchRange.comp("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}-[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$");
114 #endif
115 RegResult Rre;
116
117 // read in the file
118 String line;
119 char buffer[2048];
120 while (input) {
121 if (!input->getline(buffer, sizeof(buffer))) {
122 break;
123 }
124 line = buffer;
125 if (checkendstring && line.startsWith(endstring)) {
126 break;
127 }
128
129 // ignore comments
130 if (buffer[0] == '#')
131 continue;
132 // ignore blank lines
133 if (strlen(buffer) < 7)
134 continue;
135 #ifdef DGDEBUG
136 std::cerr << thread_id << "line: " << line << std::endl;
137 #endif
138 // store the IP address (numerically, not as a string) and filter group in either the IP list, subnet list or range list
139 if (matchIP.match(line.toCharArray(),Rre)) {
140 struct in_addr address;
141 if (inet_aton(line.toCharArray(), &address)) {
142 uint32_t addr = ntohl(address.s_addr);
143 iplist.push_back(addr);
144 }
145 } else if (matchSubnet.match(line.toCharArray(),Rre)) {
146 struct in_addr address;
147 struct in_addr addressmask;
148 String subnet(line.before("/"));
149 String mask(line.after("/"));
150 if (inet_aton(subnet.toCharArray(), &address) && inet_aton(mask.toCharArray(), &addressmask)) {
151 ipl_subnetstruct s;
152 uint32_t addr = ntohl(address.s_addr);
153 s.mask = ntohl(addressmask.s_addr);
154 // pre-mask the address for quick comparison
155 s.maskedaddr = addr & s.mask;
156 ipsubnetlist.push_back(s);
157 }
158 } else if (matchCIDR.match(line.toCharArray(),Rre)) {
159 struct in_addr address;
160 struct in_addr addressmask;
161 String subnet(line.before("/"));
162 String cidr(line.after("/"));
163 int m = cidr.toInteger();
164 int host_part = 32 - m;
165 if (host_part > -1) {
166 String mask = (0xFFFFFFFF << host_part);
167 if (inet_aton(subnet.toCharArray(), &address) && inet_aton(mask.toCharArray(), &addressmask)) {
168 ipl_subnetstruct s;
169 uint32_t addr = ntohl(address.s_addr);
170 s.mask = ntohl(addressmask.s_addr);
171 // pre-mask the address for quick comparison
172 s.maskedaddr = addr & s.mask;
173 ipsubnetlist.push_back(s);
174 }
175 }
176 } else if (matchRange.match(line.toCharArray(),Rre)) {
177 struct in_addr addressstart;
178 struct in_addr addressend;
179 String start(line.before("-"));
180 String end(line.after("-"));
181 if (inet_aton(start.toCharArray(), &addressstart) && inet_aton(end.toCharArray(), &addressend)) {
182 ipl_rangestruct r;
183 r.startaddr = ntohl(addressstart.s_addr);
184 r.endaddr = ntohl(addressend.s_addr);
185 iprangelist.push_back(r);
186 }
187 }
188 // hmmm. the line didn't match any of our regular expressions.
189 // assume it's a hostname.
190 else {
191 line.toLower();
192 hostlist.push_back(line);
193 }
194 }
195 #ifdef DGDEBUG
196 std::cerr << thread_id << "starting sort" << std::endl;
197 #endif
198 std::sort(iplist.begin(), iplist.end());
199 std::sort(hostlist.begin(), hostlist.end());
200 #ifdef DGDEBUG
201 std::cerr << thread_id << "sort complete" << std::endl;
202 std::cerr << thread_id << "ip list dump:" << std::endl;
203 std::vector<uint32_t>::iterator i = iplist.begin();
204 while (i != iplist.end()) {
205 std::cerr << thread_id << "IP: " << *i << std::endl;
206 ++i;
207 }
208 std::cerr << thread_id << "subnet list dump:" << std::endl;
209 std::list<ipl_subnetstruct>::iterator j = ipsubnetlist.begin();
210 while (j != ipsubnetlist.end()) {
211 std::cerr << thread_id << "Masked IP: " << j->maskedaddr << " Mask: " << j->mask << std::endl;
212 ++j;
213 }
214 std::cerr << thread_id << "range list dump:" << std::endl;
215 std::list<ipl_rangestruct>::iterator k = iprangelist.begin();
216 while (k != iprangelist.end()) {
217 std::cerr << thread_id << "Start IP: " << k->startaddr << " End IP: " << k->endaddr << std::endl;
218 ++k;
219 }
220 std::cerr << thread_id << "host list dump:" << std::endl;
221 std::vector<String>::iterator l = hostlist.begin();
222 while (l != hostlist.end()) {
223 std::cerr << thread_id << "Hostname: " << *l << std::endl;
224 ++l;
225 }
226 #endif
227 return true;
228 }
229
230 // read in a list linking IPs, subnets & IP ranges to filter groups
readIPMelangeList(const char * filename)231 bool IPList::readIPMelangeList(const char *filename)
232 {
233 // load in the list file
234 std::ifstream input(filename);
235 if (!input) {
236 if (!is_daemonised) {
237 std::cerr << thread_id << "Error reading file (does it exist?): " << filename << std::endl;
238 }
239 syslog(LOG_ERR, "%s%s%s", thread_id.c_str(), "Error reading file (does it exist?): ", filename);
240 return false;
241 }
242 #ifdef DGDEBUG
243 std::cerr << thread_id << "reading: " << filename << std::endl;
244 #endif
245 if (ifsreadIPMelangeList(&input, false, NULL)) {
246 input.close();
247 return true;
248 }
249 input.close();
250 return false;
251 }
252