diff --git a/netconf-service/netconf.cpp b/netconf-service/netconf.cpp index c8da3d03..bf64fff1 100644 --- a/netconf-service/netconf.cpp +++ b/netconf-service/netconf.cpp @@ -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; diff --git a/node/Filter.cpp b/node/Filter.cpp index a0412173..d41589d6 100644 --- a/node/Filter.cpp +++ b/node/Filter.cpp @@ -30,6 +30,8 @@ #include #include +#include + #include "RuntimeEnvironment.hpp" #include "Logger.hpp" #include "Filter.hpp" @@ -40,6 +42,61 @@ namespace ZeroTier { const char *const Filter::UNKNOWN_NAME = "(unknown)"; const Range Filter::ANY; +static inline Range __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 , -, or *"); + } + } + } + return Range(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::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::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::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::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::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 diff --git a/node/Filter.hpp b/node/Filter.hpp index ac493b28..517b81c6 100644 --- a/node/Filter.hpp +++ b/node/Filter.hpp @@ -29,13 +29,14 @@ #define _ZT_FILTER_HPP #include +#include #include + #include #include #include #include -#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 _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::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 _chain; - Mutex _chain_m; + std::vector _rules; }; } // namespace ZeroTier diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index c15551d0..4be4f367 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -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) diff --git a/node/Utils.hpp b/node/Utils.hpp index 1a3adec6..79a2b04f 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -443,6 +443,46 @@ public: */ static std::vector 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 *