Browse Source

Import MiniINI and refactor config.o

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
Jon Foster 10 months ago
parent
commit
2876b4125a
8 changed files with 396 additions and 61 deletions
  1. +13
    -8
      Makefile
  2. +39
    -40
      config.cpp
  3. +37
    -5
      config.h
  4. +4
    -4
      iptraffic.cpp
  5. +129
    -0
      miniini.cpp
  6. +136
    -0
      miniini.h
  7. +27
    -1
      strutil.cpp
  8. +11
    -3
      strutil.h

+ 13
- 8
Makefile View File

@@ -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





+ 39
- 40
config.cpp View File

@@ -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;
}

+ 37
- 5
config.h View File

@@ -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

+ 4
- 4
iptraffic.cpp View File

@@ -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


+ 129
- 0
miniini.cpp View File

@@ -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';
}
}

+ 136
- 0
miniini.h View File

@@ -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

+ 27
- 1
strutil.cpp View File

@@ -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
//////////////////////////////////////////////////////////////////////



+ 11
- 3
strutil.h View File

@@ -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; }
};


Loading…
Cancel
Save