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