Filter work, add name and desc to netconf response, small compiler warning fix.

This commit is contained in:
Adam Ierymenko 2013-08-28 15:09:49 -04:00
parent 01a70d09db
commit 3745377872
5 changed files with 199 additions and 193 deletions

View File

@ -81,29 +81,6 @@ static Mutex stdoutWriteLock;
static Connection *dbCon = (Connection *)0;
static char mysqlHost[64],mysqlPort[64],mysqlDatabase[64],mysqlUser[64],mysqlPassword[64];
static void connectOrReconnect()
{
for(;;) {
delete dbCon;
try {
dbCon = new Connection(mysqlDatabase,mysqlHost,mysqlUser,mysqlPassword,(unsigned int)strtol(mysqlPort,(char **)0,10));
if (dbCon->connected()) {
fprintf(stderr,"(re?)-connected to mysql server successfully\n");
break;
} else {
fprintf(stderr,"unable to connect to database server (connection closed), trying again in 1s...\n");
usleep(1000000);
}
} catch (std::exception &exc) {
fprintf(stderr,"unable to connect to database server (%s), trying again in 1s...\n",exc.what());
usleep(1000000);
} catch ( ... ) {
fprintf(stderr,"unable to connect to database server (unknown exception), trying again in 1s...\n");
usleep(1000000);
}
}
}
int main(int argc,char **argv)
{
{
@ -140,7 +117,20 @@ int main(int argc,char **argv)
char buf[131072];
std::string dictBuf;
connectOrReconnect();
try {
dbCon = new Connection(mysqlDatabase,mysqlHost,mysqlUser,mysqlPassword,(unsigned int)strtol(mysqlPort,(char **)0,10));
if (dbCon->connected()) {
fprintf(stderr,"connected to mysql server successfully\n");
break;
} else {
fprintf(stderr,"unable to connect to database server\n");
return -1;
}
} catch (std::exception &exc) {
fprintf(stderr,"unable to connect to database server: %s\n",exc.what());
return -1;
}
for(;;) {
for(int l=0;l<4;) {
int n = (int)read(STDIN_FILENO,buf + l,4 - l);
@ -164,8 +154,10 @@ int main(int argc,char **argv)
Dictionary request(dictBuf);
dictBuf = "";
if (!dbCon->connected())
connectOrReconnect();
if (!dbCon->connected()) {
fprintf(stderr,"connection to database server lost\n");
return -1;
}
try {
const std::string &reqType = request.get("type");
@ -213,13 +205,16 @@ int main(int argc,char **argv)
}
bool isOpen = false;
std::string name,desc;
{
Query q = dbCon->query();
q << "SELECT isOpen FROM Network WHERE id = " << nwid;
q << "SELECT name,desc,isOpen FROM Network WHERE id = " << nwid;
StoreQueryResult rs = q.store();
if (rs.num_rows() > 0)
if (rs.num_rows() > 0) {
name = rs[0]["name"].c_str();
desc = rs[0]["desc"].c_str();
isOpen = ((int)rs[0]["isOpen"] > 0);
else {
} else {
Dictionary response;
response["peer"] = peerIdentity.address().toString();
response["nwid"] = request.get("nwid");
@ -243,6 +238,8 @@ int main(int argc,char **argv)
sprintf(buf,"%.16llx",(unsigned long long)nwid);
netconf["nwid"] = buf;
netconf["isOpen"] = (isOpen ? "1" : "0");
netconf["name"] = name;
netconf["desc"] = desc;
sprintf(buf,"%llx",(unsigned long long)Utils::now());
netconf["ts"] = buf;

View File

@ -30,6 +30,8 @@
#include <string.h>
#include <stdint.h>
#include <algorithm>
#include "RuntimeEnvironment.hpp"
#include "Logger.hpp"
#include "Filter.hpp"
@ -40,6 +42,61 @@ namespace ZeroTier {
const char *const Filter::UNKNOWN_NAME = "(unknown)";
const Range<unsigned int> Filter::ANY;
static inline Range<unsigned int> __parseRange(char *r)
throw(std::invalid_argument)
{
char *saveptr = (char *)0;
unsigned int a = 0;
unsigned int b = 0;
unsigned int fn = 0;
for(char *f=Utils::stok(r,"-",&saveptr);(f);f=Utils::stok((char *)0,"-",&saveptr)) {
if (*f) {
switch(fn++) {
case 0:
if (*f != '*')
a = b = (unsigned int)strtoul(f,(char **)0,10);
break;
case 1:
if (*f != '*')
b = (unsigned int)strtoul(f,(char **)0,10);
break;
default:
throw std::invalid_argument("rule range must be <int>, <int>-<int>, or *");
}
}
}
return Range<unsigned int>(a,b);
}
Filter::Rule::Rule(const char *s)
throw(std::invalid_argument)
{
char *saveptr = (char *)0;
char tmp[256];
if (!Utils::scopy(tmp,sizeof(tmp),s))
throw std::invalid_argument("rule string too long");
unsigned int fn = 0;
for(char *f=Utils::stok(tmp,";",&saveptr);(f);f=Utils::stok((char *)0,";",&saveptr)) {
if (*f) {
switch(fn++) {
case 0:
_etherType = __parseRange(f);
break;
case 1:
_protocol = __parseRange(f);
break;
case 2:
_port = __parseRange(f);
break;
default:
throw std::invalid_argument("rule string has unknown extra fields");
}
}
}
if (fn != 3)
throw std::invalid_argument("rule string must contain 3 fields");
}
bool Filter::Rule::operator()(unsigned int etype,const void *data,unsigned int len) const
throw(std::invalid_argument)
{
@ -166,7 +223,7 @@ std::string Filter::Rule::toString() const
s.append(buf);
break;
}
s.push_back('/');
s.push_back(';');
switch(_protocol.magnitude()) {
case 0:
s.push_back('*');
@ -180,7 +237,7 @@ std::string Filter::Rule::toString() const
s.append(buf);
break;
}
s.push_back('/');
s.push_back(';');
switch(_port.magnitude()) {
case 0:
s.push_back('*');
@ -198,37 +255,50 @@ std::string Filter::Rule::toString() const
return s;
}
void Filter::add(const Rule &r,const Action &a)
Filter::Filter(const char *s)
throw(std::invalid_argument)
{
Mutex::Lock _l(_chain_m);
for(std::vector<Entry>::iterator i(_chain.begin());i!=_chain.end();++i) {
if (i->rule == r) {
_chain.erase(i);
break;
char tmp[16384];
if (!Utils::scopy(tmp,sizeof(tmp),s))
throw std::invalid_argument("filter string too long");
char *saveptr = (char *)0;
unsigned int fn = 0;
for(char *f=Utils::stok(tmp,"-",&saveptr);(f);f=Utils::stok((char *)0,"-",&saveptr)) {
try {
_rules.push_back(Rule(f));
++fn;
} catch (std::invalid_argument &exc) {
char tmp[256];
sprintf(tmp,"invalid rule at index %u: %s",fn,exc.what());
throw std::invalid_argument(tmp);
}
}
_chain.push_back(Entry(r,a));
std::sort(_rules.begin(),_rules.end());
}
std::string Filter::toString(const char *sep) const
std::string Filter::toString() const
{
if (!sep)
sep = ",";
std::string s;
bool first = true;
Mutex::Lock _l(_chain_m);
for(std::vector<Entry>::const_iterator i(_chain.begin());i!=_chain.end();++i) {
s.append(i->rule.toString());
if (first)
first = false;
else s.append(sep);
for(std::vector<Rule>::const_iterator r(_rules.begin());r!=_rules.end();++r) {
if (s.length() > 0)
s.push_back(',');
s.append(r->toString());
}
return s;
}
void Filter::add(const Rule &r)
{
for(std::vector<Rule>::iterator rr(_rules.begin());rr!=_rules.end();++rr) {
if (r == *rr)
return;
}
_rules.push_back(r);
std::sort(_rules.begin(),_rules.end());
}
const char *Filter::etherTypeName(const unsigned int etherType)
throw()
{
@ -335,38 +405,4 @@ const char *Filter::icmp6TypeName(const unsigned int icmp6Type)
return UNKNOWN_NAME;
}
Filter::Action Filter::operator()(const RuntimeEnvironment *_r,unsigned int etherType,const void *frame,unsigned int len) const
{
Mutex::Lock _l(_chain_m);
TRACE("starting match against %d rules",(int)_chain.size());
int ruleNo = 0;
for(std::vector<Entry>::const_iterator r(_chain.begin());r!=_chain.end();++r,++ruleNo) {
try {
if (r->rule(etherType,frame,len)) {
TRACE("match: %s",r->rule.toString().c_str());
switch(r->action) {
case ACTION_ALLOW:
case ACTION_DENY:
return r->action;
default:
break;
}
} else {
TRACE("no match: %s",r->rule.toString().c_str());
}
} catch (std::invalid_argument &exc) {
LOG("filter: unable to parse packet on rule %s (%d): %s",r->rule.toString().c_str(),ruleNo,exc.what());
return ACTION_UNPARSEABLE;
} catch ( ... ) {
LOG("filter: unable to parse packet on rule %s (%d): unknown exception",r->rule.toString().c_str(),ruleNo);
return ACTION_UNPARSEABLE;
}
}
return ACTION_ALLOW;
}
} // namespace ZeroTier

View File

@ -29,13 +29,14 @@
#define _ZT_FILTER_HPP
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
#include <utility>
#include <stdexcept>
#include "Mutex.hpp"
#include "Range.hpp"
/* Ethernet frame types that might be relevant to us */
@ -125,7 +126,11 @@ namespace ZeroTier {
class RuntimeEnvironment;
/**
* A simple Ethernet frame level filter supporting basic IP port DENY
* A simple Ethernet frame level filter
*
* This doesn't specify actions, since it's used as a deny filter. The rule
* in ZT1 is "that which is not explicitly prohibited is allowed." (Except for
* ethertypes, which are handled by a whitelist.)
*/
class Filter
{
@ -145,8 +150,6 @@ public:
/**
* A filter rule
*
* This behaves as an immutable value object.
*/
class Rule
{
@ -159,6 +162,15 @@ public:
{
}
/**
* Construct a rule from a string-serialized value
*
* @param s String formatted rule, such as returned by toString()
* @throws std::invalid_argument String formatted rule is not valid
*/
Rule(const char *s)
throw(std::invalid_argument);
/**
* Construct a new rule
*
@ -191,6 +203,8 @@ public:
throw(std::invalid_argument);
/**
* Serialize rule as string
*
* @return Human readable representation of rule
*/
std::string toString() const;
@ -222,105 +236,36 @@ public:
Range<unsigned int> _port;
};
/**
* Action if a rule matches
*/
enum Action
{
ACTION_DENY = 0,
ACTION_ALLOW = 1,
ACTION_UNPARSEABLE = 2
};
Filter() {}
/**
* Entry in filter chain
* @param s String-serialized filter representation
*/
struct Entry
Filter(const char *s)
throw(std::invalid_argument);
/**
* @return Comma-delimited list of string-format rules
*/
std::string toString() const;
/**
* Add a rule to this filter
*
* @param r Rule to add to filter
*/
void add(const Rule &r);
inline bool operator()(unsigned int etype,const void *data,unsigned int len) const
throw(std::invalid_argument)
{
Entry() {}
Entry(const Rule &r,const Action &a) :
rule(r),
action(a)
{
for(std::vector<Rule>::const_iterator r(_rules.begin());r!=_rules.end();++r) {
if ((*r)(etype,data,len))
return true;
}
Rule rule;
Action action;
};
Filter() :
_chain(),
_chain_m()
{
return false;
}
Filter(const Filter &f) :
_chain(),
_chain_m()
{
Mutex::Lock _l(f._chain_m);
_chain = f._chain;
}
inline Filter &operator=(const Filter &f)
{
Mutex::Lock _l1(_chain_m);
Mutex::Lock _l2(f._chain_m);
_chain = f._chain;
return *this;
}
/**
* Remove all filter entries
*/
inline void clear()
{
Mutex::Lock _l(_chain_m);
_chain.clear();
}
/**
* Append a rule/action pair to this chain
*
* If an identical rule already exists it is removed and a new entry is
* added to the end with the new action. (Two identical rules with the
* same action wouldn't make sense.)
*
* @param r Rule to add
* @param a Action if rule matches
*/
void add(const Rule &r,const Action &a);
/**
* @return Number of rules in filter chain
*/
inline unsigned int length() const
throw()
{
Mutex::Lock _l(_chain_m);
return (unsigned int)_chain.size();
}
/**
* @return Entry in filter chain or null entry if out of bounds
*/
inline Entry operator[](const unsigned int i) const
throw()
{
Mutex::Lock _l(_chain_m);
if (i < _chain.size())
return _chain[i];
return Entry();
}
/**
* Get a string representation of this filter
*
* @param sep Separator between filter rules, or NULL for comma (default)
* @return Human-readable string
*/
std::string toString(const char *sep = (const char *)0) const;
static const char *etherTypeName(const unsigned int etherType)
throw();
static const char *ipProtocolName(const unsigned int ipp)
@ -330,20 +275,8 @@ public:
static const char *icmp6TypeName(const unsigned int icmp6Type)
throw();
/**
* Match against an Ethernet frame
*
* @param _r Runtime environment
* @param etherType Ethernet frame type
* @param frame Ethernet frame data
* @param len Length of frame in bytes
* @return Action if matched or ACTION_ALLOW if not matched
*/
Action operator()(const RuntimeEnvironment *_r,unsigned int etherType,const void *frame,unsigned int len) const;
private:
std::vector<Entry> _chain;
Mutex _chain_m;
std::vector<Rule> _rules;
};
} // namespace ZeroTier

View File

@ -64,9 +64,9 @@ public:
shutdownInProgress(false),
log((Logger *)0),
prng((CMWC4096 *)0),
demarc((Demarc *)0),
multicaster((Multicaster *)0),
sw((Switch *)0),
demarc((Demarc *)0),
topology((Topology *)0),
sysEnv((SysEnv *)0),
nc((NodeConfig *)0)

View File

@ -443,6 +443,46 @@ public:
*/
static std::vector<std::string> split(const char *s,const char *const sep,const char *esc,const char *quot);
/**
* Tokenize a string
*
* @param str String to split
* @param delim Delimiters
* @param saveptr Pointer to a char * for temporary reentrant storage
*/
static inline char *stok(char *str,const char *delim,char **saveptr)
throw()
{
#ifdef __WINDOWS__
return strtok_s(str,delim,saveptr);
#else
return strtok_r(str,delim,saveptr);
#endif
}
/**
* Perform a safe C string copy
*
* @param dest Destination buffer
* @param len Length of buffer
* @param src Source string
* @return True on success, false on overflow (buffer will still be 0-terminated)
*/
static inline bool scopy(char *dest,unsigned int len,const char *src)
throw()
{
if (!len)
return false; // sanity check
char *end = dest + len;
while ((*dest++ = *src++)) {
if (dest == end) {
dest[len - 1] = (char)0;
return false;
}
}
return true;
}
/**
* Trim whitespace from the start and end of a string
*