In preparation for more tools with differing config data needs I pulled in my "Mini INI" crackers and some additional string utilities. The Config class has been refactored to use them and the individual group parsers refactored onto appropriate MiniINIgroup based classes.master
@@ -1,17 +1,22 @@ | |||
iptraffic: iptraffic.cpp strutil.o data.o config.o cli.o | |||
g++ -o $@ $@.cpp strutil.o data.o config.o cli.o | |||
O=-s | |||
config.o: config.cpp config.h strutil.o data.o | |||
g++ -c -o $@ config.cpp | |||
iptraffic: iptraffic.cpp strutil.o data.o config.o cli.o miniini.o | |||
g++ $O -o $@ $@.cpp strutil.o data.o config.o cli.o miniini.o | |||
cli.o: cli.cpp cli.h | |||
g++ $O -c -o $@ cli.cpp | |||
config.o: config.cpp config.h strutil.o data.o miniini.o | |||
g++ $O -c -o $@ config.cpp | |||
data.o: data.cpp data.h strutil.o | |||
g++ -c -o $@ data.cpp | |||
g++ $O -c -o $@ data.cpp | |||
cli.o: cli.cpp cli.h | |||
g++ -c -o $@ cli.cpp | |||
miniini.o: miniini.cpp miniini.h strutil.o | |||
g++ $O -c -o $@ miniini.cpp | |||
strutil.o: strutil.cpp strutil.h | |||
g++ -c -o $@ strutil.cpp | |||
g++ $O -c -o $@ strutil.cpp | |||
@@ -6,58 +6,57 @@ | |||
////////////////////////////////////////////////////////////////////// | |||
#include <fstream> | |||
#include <iostream> | |||
#include <stdexcept> | |||
#include "config.h" | |||
#include "strutil.h" | |||
void Config::load(const std::string &fname) { | |||
std::string l; | |||
std::ifstream f(fname.c_str()); | |||
TSV tsv; | |||
Conn conn; | |||
int ln=0; | |||
////////////////////////////////////////////////////////////////////// | |||
// INIusList | |||
////////////////////////////////////////////////////////////////////// | |||
void INIusList::add(const std::string &in) { | |||
std::string s=trim(in); | |||
if(s!="" && s[0]!='#') MiniINIlines::add(s); | |||
} | |||
while(std::getline(f, l)) { | |||
ln++; | |||
l = strip(l); | |||
if(l=="" || l[0]=='#') continue; | |||
if(l.size()>2 && l[0]=='[' && l.end()[-1]==']') { | |||
heading: | |||
if(l=="[us]") { | |||
////////////////////////////////////////////////////////////////////// | |||
// INIconnList | |||
////////////////////////////////////////////////////////////////////// | |||
void INIconnList::add(const std::string &in) { | |||
int i; | |||
if(in=="" || in[0]=='#') return; // remarks | |||
// TODO: we don't want to keep create+destroy-ing these? | |||
TSV tsv(in); | |||
if(tsv.count!=7) throw | |||
// TODO: really need a line number! | |||
std::runtime_error("INIconnList::add: Incorrect column count in config file line"); | |||
if(tsv.count>6) { | |||
i = vals.size(); | |||
vals.resize(i+1); | |||
tsv >> vals[i]; | |||
} | |||
} | |||
/// Read in "us" list /// | |||
while(std::getline(f, l)) { | |||
ln++; | |||
l=strip(l); | |||
if(l=="" || l[0]=='#') continue; | |||
if(l.size()>2 && l[0]=='[' && l.end()[-1]==']') goto heading; | |||
us.push_back(l); | |||
} | |||
// NOP right now, since I don't intend to be writing the config file. | |||
std::ostream &INIconnList::save(std::ostream &out) const { | |||
throw std::runtime_error("INIconnList::save: not implented."); | |||
} | |||
} else if(l=="[ignores]") { | |||
/// Read in ignore list /// | |||
while(std::getline(f, l)) { | |||
ln++; | |||
if(l=="" || l[0]=='#') continue; | |||
if(l.size()>2 && l[0]=='[' && l.end()[-1]==']') goto heading; | |||
tsv = l; | |||
if(tsv.count!=7) { | |||
std::cerr << "Incorrrect column count in config file line " << ln << std::endl; | |||
continue; | |||
} | |||
if(tsv.count>6) { | |||
tsv >> conn; | |||
ignores.push_back(conn); | |||
} | |||
} | |||
} | |||
////////////////////////////////////////////////////////////////////// | |||
// Config | |||
////////////////////////////////////////////////////////////////////// | |||
} | |||
} | |||
Config::Config() { | |||
groups["us" ] = &us; | |||
groups["ignores"] = &ignores; | |||
} |
@@ -12,17 +12,49 @@ | |||
#ifndef __JFP_IPTRAFFIC_CONF_H__ | |||
#define __JFP_IPTRAFFIC_CONF_H__ | |||
#include <string> | |||
#include <vector> | |||
#include <ostream> | |||
#include "data.h" | |||
#include "miniini.h" | |||
#include "strutil.h" | |||
struct Config { | |||
StringList us; | |||
ConnList ignores; | |||
////////////////////////////////////////////////////////////////////// | |||
// INI group parser for "us" records | |||
// | |||
// This is mostly "raw lines" but we need to throw out remarks and WS. | |||
////////////////////////////////////////////////////////////////////// | |||
struct INIusList: public MiniINIlines { | |||
void add(const std::string &in); | |||
}; | |||
////////////////////////////////////////////////////////////////////// | |||
// INI group parser for "ignore" records | |||
////////////////////////////////////////////////////////////////////// | |||
void load(const std::string &fname); | |||
struct INIconnList: public MiniINIgroup { | |||
ConnList vals; | |||
// Read records | |||
void add(const std::string &in); | |||
// Write records | |||
std::ostream &save(std::ostream &out) const; | |||
}; | |||
////////////////////////////////////////////////////////////////////// | |||
// INI based Configuration container for IPtraffic | |||
////////////////////////////////////////////////////////////////////// | |||
struct Config: public MiniINI { | |||
INIusList us; | |||
INIconnList ignores; | |||
Config(); | |||
}; | |||
#endif |
@@ -61,7 +61,7 @@ struct IPtraffic: public cBaseApp { | |||
IPtraffic(): out(&cout), log(0) | |||
{ // I'd rather this initialization be static... | |||
analyze.us = &(config.us); | |||
analyze.us = &(config.us.vals); | |||
} | |||
@@ -98,7 +98,7 @@ struct IPtraffic: public cBaseApp { | |||
switch(*sw) { | |||
case 'c': | |||
config.load(val); | |||
if(!config.us.size()) throw CLIerror( | |||
if(!config.us.vals.size()) throw CLIerror( | |||
"The configuration files MUST contain an [us] section with " | |||
"appropriate values" | |||
); | |||
@@ -130,7 +130,7 @@ struct IPtraffic: public cBaseApp { | |||
/// parse log file /// | |||
if(!config.us.size()) throw CLIerror( | |||
if(!config.us.vals.size()) throw CLIerror( | |||
"A configuration file must be specified before input files." | |||
); | |||
line_no=0; | |||
@@ -141,7 +141,7 @@ struct IPtraffic: public cBaseApp { | |||
/// process connections /// | |||
if(analyze.line(l)) { | |||
if(config.ignores.find(analyze.conn)<0) | |||
if(config.ignores.vals.find(analyze.conn)<0) | |||
*out << analyze.ln[0] << " " << analyze.ln[1] << " " << analyze.ln[2] | |||
<< " " << analyze.conn << "\n"; | |||
else | |||
@@ -0,0 +1,129 @@ | |||
////////////////////////////////////////////////////////////////////// | |||
// Mini INI File Crackers | |||
// Written by Jonathan A. Foster <jon@jfpossibilities.com> | |||
// Started June 1st, 2021 | |||
// Copyright JF Possibilities, Inc. All rights reserved. | |||
// Used with permission in the "Poor Man's IDS" project | |||
////////////////////////////////////////////////////////////////////// | |||
#include <stdlib.h> | |||
#include <fstream> | |||
#include <stdexcept> | |||
#include "miniini.h" | |||
////////////////////////////////////////////////////////////////////// | |||
// MiniINIvars | |||
////////////////////////////////////////////////////////////////////// | |||
std::string MiniINIvars::get(const std::string &name, const std::string &def) { | |||
NameVal::iterator v = vals.find(name); | |||
if(v!=vals.end()) return v->second; | |||
vals[name]=def; | |||
return def; | |||
} | |||
std::string MiniINIvars::get(const std::string &name) const { | |||
NameVal::const_iterator v = vals.find(name); | |||
return v!=vals.end() ? v->second : ""; | |||
} | |||
// TODO: come up with better range checking to allow full range of 32bit ints. | |||
int MiniINIvars::geti(const std::string &name, int def, int min, int max) const { | |||
int r; | |||
NameVal::const_iterator v = vals.find(name); | |||
if(v!=vals.end()) { | |||
const std::string &s = v->second; | |||
if(s!="" && s.size()<=9+(s[0]=='+' || s[0]=='-')) { | |||
r = atoi(s.c_str()); | |||
if(r>=min && r<=max) return r; | |||
} | |||
} | |||
return def; | |||
} | |||
void MiniINIvars::add(const std::string &in) { | |||
std::string s = trim(in); | |||
std::string::size_type i; | |||
if(s=="" or s[0]=='#' or s[0]==';') return; // NOP | |||
i = s.find('='); | |||
if(i==s.npos || i==0) throw std::runtime_error( | |||
"MiniINIvars.add: incorrectly formatted name / value pair"); | |||
// TODO: catch dupe vars? | |||
vals[trim(s.substr(0, i-1))] = trim(s.substr(i+1)); | |||
} | |||
std::ostream &MiniINIvars::save(std::ostream &out) const { | |||
NameVal::const_iterator var; | |||
for(var = vals.begin(); var!=vals.end(); var++) | |||
out << var->first << " = " << var->second << '\n'; | |||
return out; | |||
} | |||
////////////////////////////////////////////////////////////////////// | |||
// MiniINIlines | |||
////////////////////////////////////////////////////////////////////// | |||
void MiniINIlines::add(const std::string &in) { | |||
vals.push_back(in); | |||
} | |||
std::ostream &MiniINIlines::save(std::ostream &out) const { | |||
StringList::const_iterator val; | |||
for(val = vals.begin(); val!=vals.end(); val++) out << *val << '\n'; | |||
return out; | |||
} | |||
////////////////////////////////////////////////////////////////////// | |||
// MiniINI | |||
////////////////////////////////////////////////////////////////////// | |||
void MiniINI::load(const std::string &fname) { | |||
std::string l; | |||
std::string gname; | |||
MiniINIgroup *group = 0; | |||
std::ifstream f(fname.c_str()); | |||
INIgroupList::iterator gmap; | |||
int ln=0; | |||
while(std::getline(f, l)) { | |||
ln++; | |||
gname = trim(l); | |||
if(gname.size()>2 && gname[0]=='[' && gname.end()[-1]==']') { | |||
gname = gname.substr(1, gname.size()-2); | |||
group = (gmap = groups.find(gname))==groups.end() ? 0 : gmap->second; | |||
continue; | |||
} | |||
try { | |||
if(group) group->add(l); | |||
} catch(const std::exception &e) { | |||
throw std::runtime_error("Error parsing "+fname+" line "+str(ln)+ | |||
" "+e.what()); | |||
} | |||
} | |||
} | |||
void MiniINI::save(const std::string &fname) { | |||
std::ofstream f(fname.c_str()); | |||
INIgroupList::iterator group; | |||
for(group = groups.begin(); group!=groups.end(); group++) { | |||
f << '[' << group->first << "]\n"; | |||
f << *group->second << '\n'; | |||
} | |||
} |
@@ -0,0 +1,136 @@ | |||
////////////////////////////////////////////////////////////////////// | |||
// Mini INI File Crackers | |||
// Written by Jonathan A. Foster <jon@jfpossibilities.com> | |||
// Started June 1st, 2021 | |||
// Copyright JF Possibilities, Inc. All rights reserved. | |||
// Used with permission in the "Poor Man's IDS" project | |||
// | |||
// This set of classes is to prvide easy access to reading config data | |||
// from INI files. I'm going to take some liberty with the origianl | |||
// M$ format and allow groups to parse specialized text formats, not | |||
// just name/value pairs. | |||
////////////////////////////////////////////////////////////////////// | |||
#ifndef __JFP_MINIINI_H__ | |||
#define __JFP_MINIINI_H__ | |||
#include <string> | |||
#include <ostream> | |||
#include "strutil.h" | |||
////////////////////////////////////////////////////////////////////// | |||
// The base abstract group class | |||
// | |||
// Subclass and override this class to create a parser for the kind of | |||
// data you want in your group. | |||
////////////////////////////////////////////////////////////////////// | |||
struct MiniINI; | |||
struct MiniINIgroup { | |||
// vals: By convention the content of the group | |||
// this is overriden to handle the parsing of lines in this group | |||
// it must ignore whitespace and remarks, if allowed in the group's | |||
// format. | |||
virtual void add(const std::string &in) = 0; | |||
// This is overridden to dump the group back out to a file | |||
virtual std::ostream &save(std::ostream &out) const = 0; | |||
// cause we need the destructor virtual too! | |||
virtual ~MiniINIgroup() {} | |||
}; | |||
inline std::ostream &operator<<(std::ostream &out, const MiniINIgroup &ini) { | |||
return ini.save(out); | |||
} | |||
////////////////////////////////////////////////////////////////////// | |||
// The typical collection of name+value pairs | |||
////////////////////////////////////////////////////////////////////// | |||
struct MiniINIvars: public MiniINIgroup { | |||
NameVal vals; // name val pairs | |||
/// Get a value setting it to def if not found | |||
/// | |||
/// This looks for name in vals. If found that value is returned. Otherwise | |||
/// the def is set in vals for the name. This means it will be saved at | |||
/// next writing. Def defaults to empty string. If you don't want to auto- | |||
/// create the value in the config file then use the appropriate methods of | |||
/// vals. | |||
virtual std::string get(const std::string &name, const std::string &def); | |||
/// Get a value NOT adding it to vals if not found | |||
/// | |||
/// This looks for name in vals. If found that value is returned. Otherwise | |||
/// "" is returned but the name is not added to vals. | |||
virtual std::string get(const std::string &name) const; | |||
/// Get an int from vals | |||
/// | |||
/// This will convert the string value, if found in vals, to an int and make | |||
/// sure its within bounds. If its not in bounds or doesn't exist or is an | |||
/// an empty string them def is returned. The vals is not modified. | |||
/// | |||
/// For safety values are limited to 9 digits plus sign, since this is a safe | |||
/// range for converting to 32 bit ints. | |||
virtual int geti(const std::string &name, int def=0, int min=-999999999, int max=999999999) const; | |||
/// parse an INI line into a name value pair | |||
virtual void add(const std::string &in); | |||
/// write all name value pairs from vals to out | |||
virtual std::ostream &save(std::ostream &out) const; | |||
}; | |||
////////////////////////////////////////////////////////////////////// | |||
// A raw collection of lines in a group | |||
////////////////////////////////////////////////////////////////////// | |||
struct MiniINIlines: public MiniINIgroup { | |||
StringList vals; // the raw lines of this group | |||
virtual void add(const std::string &in);// add an incoming line | |||
virtual std::ostream &save(std::ostream &out) const; | |||
}; | |||
////////////////////////////////////////////////////////////////////// | |||
// The main INI container that contains all groups | |||
// | |||
// Fill groups with a list of group objects and their associated | |||
// names. Call load() to read the groups from an INI file. Only the | |||
// group with names specified in groups will be read. Their content | |||
// parsed according to the group's rules. save() will write it back | |||
// out again. No attempt to preserve whitespace and remarks is made. | |||
////////////////////////////////////////////////////////////////////// | |||
typedef std::map<std::string,MiniINIgroup*> INIgroupList; | |||
struct MiniINI { | |||
// This is a list of groups allowed in the INI and after load() those | |||
// groups will contain the data from the INI and they will be written | |||
// on save(). | |||
// | |||
// This must be initialized with MiniINIgroup* objects so that it knows | |||
// how to process groups. No attempt is made to manage memory allocation. | |||
INIgroupList groups; | |||
// Load the file and parse into groups[]. If a group is encountered that | |||
// was not specified in groups[] it is ignored, and will be lost on | |||
// save(). Same for pre-group content. | |||
virtual void load(const std::string &fname); | |||
// Save the groups[] into the file. No attempt is made to preserve | |||
// whitespace or remarks. This is the "mini INI". Preserving thoee | |||
// requires a more complicated and thus less "mini" implementation. | |||
virtual void save(const std::string &fname); | |||
// A virtual NOP destructor... because its recommended. | |||
virtual ~MiniINI() {} | |||
}; | |||
#endif |
@@ -1,10 +1,12 @@ | |||
////////////////////////////////////////////////////////////////////// | |||
// String splitter | |||
// String splitter & other useful string tools | |||
// Written by Jonathan A. Foster <ChipMaster@YeOlPiShack.net> | |||
// Started April 23rd, 2021 | |||
// Copyright JF Possibilities, Inc. All rights reserved. | |||
// Copied with permission from JF Possibilities's C++ lib. | |||
////////////////////////////////////////////////////////////////////// | |||
#include <string.h> | |||
#include <stdio.h> | |||
#include <stdexcept> | |||
// Sounds an awful lot like a German pastry | |||
#include "strutil.h" | |||
@@ -12,6 +14,30 @@ | |||
////////////////////////////////////////////////////////////////////// | |||
// Generic string transformations | |||
////////////////////////////////////////////////////////////////////// | |||
std::string trim(const std::string &s) { | |||
int x, y; | |||
for(x=0; x<s.size() && s[x]<=' '; x++); | |||
for(y=s.size()-1; y>=x && s[y]<=' '; y--); | |||
if(y<x) return ""; | |||
return s.substr(x, y-x+1); | |||
} | |||
// You have to have C++11+ to get to_string() | |||
std::string str(long long n) { | |||
char s[24]; s[23]=0; | |||
snprintf(s, 23, "%lld", n); | |||
return std::string(s); | |||
} | |||
////////////////////////////////////////////////////////////////////// | |||
// Splits | |||
////////////////////////////////////////////////////////////////////// | |||
@@ -3,8 +3,7 @@ | |||
// Written by Jonathan A. Foster <ChipMaster@YeOlPiShack.net> | |||
// Started April 23rd, 2021 | |||
// Copyright JF Possibilities, Inc. All rights reserved. | |||
// | |||
// This is useful for breaking a text file line into fields. | |||
// Copied with permission from JF Possibilities's C++ lib. | |||
// | |||
// 2021-05-14 <ChipMaster@YeOlPiShack.net> | |||
// Restructure: broke out of monolithic iptraffic.cpp and made its | |||
@@ -28,6 +27,15 @@ typedef std::map<std::string,std::string> NameVal; | |||
////////////////////////////////////////////////////////////////////// | |||
// Generic string transformations | |||
////////////////////////////////////////////////////////////////////// | |||
std::string trim(const std::string &s); | |||
std::string str(long long n); | |||
////////////////////////////////////////////////////////////////////// | |||
// Splits: a util class to divide a line into space sep pieces | |||
////////////////////////////////////////////////////////////////////// | |||
// TODO: implement begin() + end() to make "for( : )" work | |||
@@ -73,7 +81,7 @@ std::istream &operator>>(std::istream &in, Splits &sp); | |||
struct TSV: public Splits { | |||
TSV() { sep='\t'; combine=false; } | |||
// Need some weird casties to make C++ remember its base class | |||
TSV(const std::string &_line): Splits(_line) {} | |||
TSV(const std::string &_line) { sep='\t'; combine=false; *this=_line; } | |||
inline TSV &operator=(const std::string &_line) { return *this=_line.c_str(); } | |||
inline TSV &operator=(const char *_line) { *((Splits *)this)=_line; return *this; } | |||
}; | |||