]> git.stg.codes - stg.git/blob - projects/traffcounter/rules.cpp
Factored out users' parser.
[stg.git] / projects / traffcounter / rules.cpp
1 /*
2  *    This program is free software; you can redistribute it and/or modify
3  *    it under the terms of the GNU General Public License as published by
4  *    the Free Software Foundation; either version 2 of the License, or
5  *    (at your option) any later version.
6  *
7  *    This program is distributed in the hope that it will be useful,
8  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *    GNU General Public License for more details.
11  *
12  *    You should have received a copy of the GNU General Public License
13  *    along with this program; if not, write to the Free Software
14  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  */
16
17 /*
18  *    Author : Maxim Mamontov <faust@stargazer.dp.ua>
19  */
20
21 /*
22  $Revision: 1.1.1.1 $
23  $Date: 2009/02/24 08:13:03 $
24  $Author: faust $
25  */
26
27 #include <fstream>
28 #include <sstream>
29 #include <cstdlib>
30 #include <limits>
31 #include <cerrno>
32 #include <locale>
33
34 #include <arpa/inet.h>
35 #include <netdb.h>
36
37 #include "rules.h"
38 #include "utils.h"
39
40 using namespace std;
41
42 STG::RULES_PARSER::RULES_PARSER()
43     : rules(),
44       error(false),
45       errorStream(""),
46       protocols()
47 {
48 error = InitProtocols();
49 }
50
51 STG::RULES_PARSER::RULES_PARSER(const string & fileName)
52     : rules(),
53       error(false),
54       errorStream(""),
55       protocols()
56 {
57 error = InitProtocols();
58 SetFile(fileName);
59 }
60
61 void STG::RULES_PARSER::SetFile(const string & fileName)
62 {
63 errorStream.str("");
64
65 ifstream rulesFile(fileName.c_str());
66
67 int lineNumber = 0;
68
69 if (!rulesFile)
70     {
71     error = true;
72     errorStream << "RULES_PARSER::SetFile - Error opening file '" << fileName << "'\n";
73     return;
74     }
75
76 string line;
77
78 rules.erase(rules.begin(), rules.end());
79
80 while (getline(rulesFile, line))
81     {
82     lineNumber++;
83     if (ParseLine(line))
84         {
85         error = true;
86         errorStream << "RULES_PARSER::SetFile - Error parsing line at '" << fileName << ":" << lineNumber << "'\n";
87         return;
88         }
89     }
90
91 STG::RULE rule;
92
93 // Adding lastest rule: ALL 0.0.0.0/0 NULL
94 rule.dir = -1; //NULL
95 rule.ip = 0;  //0.0.0.0
96 rule.mask = 0;
97 rule.port1 = 0;
98 rule.port2 = 65535;
99 rule.proto = -1;
100
101 rules.push_back(rule);
102
103 errorStream.str("");
104
105 return;
106 }
107
108 bool STG::RULES_PARSER::ParseLine(string line)
109 {
110 size_t pos;
111
112 pos = line.find('#');
113 if (pos != string::npos)
114     {
115     line = line.substr(0, pos);
116     }
117
118 if (line.empty())
119     {
120     return false;
121     }
122
123 size_t lpos = line.find_first_not_of("\t ", 0, 2);
124
125 if (lpos == string::npos)
126     {
127     return false;
128     }
129
130 size_t rpos = line.find_first_of("\t ", lpos, 2);
131
132 if (rpos == string::npos)
133     {
134     return false;
135     }
136
137 string proto(line.begin() + lpos, line.begin() + rpos);
138
139 lpos = line.find_first_not_of("\t ", rpos, 2);
140
141 if (lpos == string::npos)
142     {
143     return false;
144     }
145
146 rpos = line.find_first_of("\t ", lpos, 2);
147
148 if (rpos == string::npos)
149     {
150     return false;
151     }
152
153 string address(line.begin() + lpos, line.begin() + rpos);
154
155 lpos = line.find_first_not_of("\t ", rpos, 2);
156
157 if (lpos == string::npos)
158     {
159     return false;
160     }
161 string direction(line.begin() + lpos, line.end());
162
163 if (proto.empty() ||
164     address.empty() ||
165     direction.empty())
166     {
167     return false;
168     }
169
170 map<string, int>::const_iterator it(protocols.find(proto));
171
172 if (it == protocols.end())
173     {
174     errorStream << "RULES_PARSER::ParseLine - Invalid protocol\n";
175     return true;
176     }
177
178 STG::RULE rule;
179
180 rule.proto = it->second;
181
182 if (direction.length() < 4)
183     {
184     errorStream << "RULES_PARSER::ParseLine - Invalid direction\n";
185     return true;
186     }
187
188 if (direction == "NULL")
189     {
190     rule.dir = -1;
191     }
192 else
193     {
194     string prefix(direction.begin(), direction.begin() + 3);
195     direction = direction.substr(3, direction.length() - 3);
196     if (prefix != "DIR")
197         {
198         errorStream << "RULES_PARSER::ParseLine - Invalid direction prefix\n";
199         return true;
200         }
201     char * endptr;
202     /* 
203      * 'cause strtol don't change errno on success
204      * according to: http://www.opengroup.org/onlinepubs/000095399/functions/strtol.html
205      */
206     errno = 0;
207     rule.dir = strtol(direction.c_str(), &endptr, 10);
208
209     // Code from strtol(3) release 3.10
210     if ((errno == ERANGE && (rule.dir == numeric_limits<int>::max() ||
211                              rule.dir == numeric_limits<int>::min()))
212         || (errno != 0 && rule.dir == 0))
213         {
214             errorStream << "RULES_PARSER::ParseLine - Direction out of range\n";
215             return true;
216         }
217     if (endptr == direction.c_str())
218         {
219             errorStream << "RULES_PARSER::ParseLine - Invalid direction\n";
220             return true;
221         }
222     }
223
224 if (ParseAddress(address, &rule))
225     {
226     errorStream << "RULES_PARSER::ParseLine - Invalid address\n";
227     return true;
228     }
229
230 rules.push_back(rule);
231
232 return false;
233 }
234
235 bool STG::RULES_PARSER::ParseAddress(const string & address, RULE * rule) const
236 {
237 // Format: <address>[/<mask>[:<port1>[-<port2>]]]
238 size_t pos = address.find('/');
239 string ip;
240 string mask;
241 string ports;
242
243 if (pos != string::npos)
244     {
245     ip = address.substr(0, pos);
246     mask = address.substr(pos + 1, address.length() - pos - 1);
247     pos = mask.find(':');
248     if (pos != string::npos)
249         {
250         ports = mask.substr(pos + 1, mask.length() - pos - 1);
251         mask = mask.substr(0, pos);
252         }
253     else
254         {
255         ports = "0-65535";
256         }
257     }
258 else
259     {
260     mask = "32";
261     pos = address.find(':');
262     if (pos != string::npos)
263         {
264         ip = address.substr(0, pos);
265         ports = address.substr(pos + 1, address.length() - pos - 1);
266         }
267     else
268         {
269         ip = address;
270         ports = "0-65536";
271         }
272     }
273
274 struct in_addr ipaddr;
275
276 if (!inet_aton(ip.c_str(), &ipaddr))
277     {
278     errorStream << "RULES_PARSER::ParseAddress - Invalid IP\n";
279     return true;
280     }
281
282 rule->ip = ntohl(ipaddr.s_addr);
283
284 if (ParseMask(mask, rule))
285     {
286     errorStream << "RULES_PARSER::ParseAddress - Error parsing mask\n";
287     return true;
288     }
289
290 pos = ports.find('-');
291 string port1;
292 string port2;
293
294 if (pos != string::npos)
295     {
296     port1 = ports.substr(0, pos);
297     port2 = ports.substr(pos + 1, ports.length() - pos - 1);
298     }
299 else
300     {
301     port1 = port2 = ports;
302     }
303
304 if (ParsePorts(port1, port2, rule))
305     {
306     errorStream << "RULES_PARSER::ParseAddress - Error pasing ports\n";
307     return true;
308     }
309
310 return false;
311 }
312
313 bool STG::RULES_PARSER::ParseMask(const string & mask, RULE * rule) const
314 {
315 char * endptr;
316
317 errno = 0;
318 /* 
319  * 'cause strtol don't change errno on success
320  * according to: http://www.opengroup.org/onlinepubs/000095399/functions/strtol.html
321  */
322 rule->mask = strtol(mask.c_str(), &endptr, 10);
323
324 if ((errno == ERANGE && (rule->mask == numeric_limits<uint32_t>::max() ||
325                          rule->mask == numeric_limits<uint32_t>::min()))
326     || (errno != 0 && rule->mask == 0))
327     {
328     errorStream << "RULES_PARSER::ParseMask - Mask is out of range\n";
329     return true;
330     }
331
332 if (endptr == NULL)
333     {
334     errorStream << "RULES_PARSER::ParseMask - NULL endptr\n";
335     return true;
336     }
337
338 if (*endptr != '\0')
339     {
340     errorStream << "RULES_PARSER::ParseMask - Invalid mask\n";
341     return true;
342     }
343
344 if (rule->mask > 32)
345     {
346     errorStream << "RULES_PARSER::ParseMask - Mask is greater than 32\n";
347     return true;
348     }
349
350 rule->mask = 0xffFFffFF >> (32 - rule->mask);
351
352 return false;
353 }
354
355 bool STG::RULES_PARSER::ParsePorts(const string & port1,
356                               const string & port2,
357                               RULE * rule) const
358 {
359 char * endptr;
360
361 errno = 0;
362 /* 
363  * 'cause strtol don't change errno on success
364  * according to: http://www.opengroup.org/onlinepubs/000095399/functions/strtol.html
365  */
366 rule->port1 = strtol(port1.c_str(), &endptr, 10);
367
368 if ((errno == ERANGE && (rule->port1 == numeric_limits<uint16_t>::max() ||
369                          rule->port1 == numeric_limits<uint16_t>::min()))
370     || (errno != 0 && rule->port1 == 0))
371     {
372     errorStream << "RULES_PARSER::ParsePorts - Min port is out of range\n";
373     return true;
374     }
375
376 if (endptr == NULL)
377     {
378     errorStream << "RULES_PARSER::ParsePorts - NULL endptr on min port\n";
379     return true;
380     }
381
382 if (*endptr != '\0')
383     {
384     errorStream << "RULES_PARSER::ParsePorts - Invalid min port\n";
385     return true;
386     }
387
388 errno = 0;
389 /* 
390  * 'cause strtol don't change errno on success
391  * according to: http://www.opengroup.org/onlinepubs/000095399/functions/strtol.html
392  */
393 rule->port2 = strtol(port2.c_str(), &endptr, 10);
394
395 if ((errno == ERANGE && (rule->port2 == numeric_limits<uint16_t>::max() ||
396                          rule->port2 == numeric_limits<uint16_t>::min()))
397     || (errno != 0 && rule->port2 == 0))
398     {
399         errorStream << "RULES_PARSER::ParseAddress - Max port is out of range\n";
400         return true;
401     }
402
403 if (endptr == NULL)
404     {
405     errorStream << "RULES_PARSER::ParsePorts - NULL endptr on max port\n";
406     return true;
407     }
408
409 if (*endptr != '\0')
410     {
411     errorStream << "RULES_PARSER::ParsePorts - Invalid max port\n";
412     return true;
413     }
414
415 return false;
416 }
417
418 bool STG::RULES_PARSER::InitProtocols()
419 {
420 struct protoent * pe;
421
422 locale loc("");
423
424 protocols.erase(protocols.begin(), protocols.end());
425
426 setprotoent(true); // Open link to /etc/protocols
427
428 while ((pe = getprotoent()) != NULL)
429     {
430     string proto(pe->p_name);
431     protocols.insert(make_pair(STG::ToUpper(pe->p_name, loc), pe->p_proto));
432     }
433
434 endprotoent();
435
436 protocols["ALL"] = -1;
437 protocols["TCP_UDP"] = -2;
438
439 errorStream.str("");
440
441 return protocols.empty();
442 }