////////////////////////////////////////////////////////////////// // // Toolkit base class for the GnuGK // // This work is published under the GNU Public License (GPL) // see file COPYING for details. // We also explicitely grant the right to link this code // with the OpenH323 library. // // History: // 991227 initial version (Torsten Will, mediaWays) // ////////////////////////////////////////////////////////////////// #if defined(_WIN32) && (_MSC_VER <= 1200) #pragma warning(disable:4786) // warning about too long debug symbol off #pragma warning(disable:4284) #endif #include #include #include #include #include "stl_supp.h" #include "gktimer.h" #include "h323util.h" #include "gkconfig.h" #if HAS_MYSQL || HAS_PGSQL #include "gksql.h" #endif #include "clirw.h" #include "capctrl.h" #include "Toolkit.h" using namespace std; extern const char *ProxySection; extern const char *RoutedSec; namespace { const PString paddingByteConfigKey("KeyFilled"); const BYTE AnyRawAddress[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; } /* namespace */ NetworkAddress::NetworkAddress() : m_address(0), m_netmask(0) { } NetworkAddress::NetworkAddress( const PIPSocket::Address &addr ) : m_address(addr), m_netmask(addr.GetSize(), AnyRawAddress) { } NetworkAddress::NetworkAddress( const PIPSocket::Address &addr, const PIPSocket::Address &nm ) : m_netmask(nm) { // normalize the address BYTE rawdata[16]; const unsigned sz = addr.GetSize(); for (unsigned i = 0; i < sz; i++) rawdata[i] = addr[i] & nm[i]; m_address = PIPSocket::Address(sz, rawdata); } NetworkAddress::NetworkAddress( const PString &str /// an address in a form A.B.C.D, A.B.C.D/24 or A.B.C.D/255.255.255.0 ) { Toolkit::GetNetworkFromString(str, m_address, m_netmask); } unsigned NetworkAddress::GetNetmaskLen() const { unsigned len = 0; const unsigned sz = m_netmask.GetSize() * 8; const char *rawdata = m_netmask.GetPointer(); for (int b = sz - 1; b >= 0; b--) if (rawdata[b >> 3] & (0x80 >> (b & 7))) break; else len++; return sz - len; } bool NetworkAddress::operator==(const NetworkAddress &addr) const { if (m_address.GetSize() != addr.m_address.GetSize()) return false; const unsigned sz = m_address.GetSize(); for (unsigned i = 0; i < sz; i++) if (m_address[i] != addr.m_address[i] || m_netmask[i] != addr.m_netmask[i]) return false; return true; } bool NetworkAddress::operator==(const PIPSocket::Address &addr) const { if (m_address.GetSize() != addr.GetSize()) return false; const unsigned sz = m_address.GetSize(); for (unsigned i = 0; i < sz; i++) if (m_netmask[i] != 255 || m_address[i] != addr[i]) return false; return true; } bool NetworkAddress::operator>>(const NetworkAddress &addr) const { if (m_address.GetSize() != addr.m_address.GetSize()) return false; const unsigned sz = m_address.GetSize(); for (unsigned i = 0; i < sz; i++) if (m_netmask[i] != (addr.m_netmask[i] & m_netmask[i]) || m_address[i] != (addr.m_address[i] & m_netmask[i])) return false; return true; } bool NetworkAddress::operator<<(const NetworkAddress &addr) const { return addr >> *this; } bool NetworkAddress::operator>>(const PIPSocket::Address &addr) const { if (m_address.GetSize() != addr.GetSize()) return false; const unsigned sz = m_address.GetSize(); for (unsigned i = 0; i < sz; i++) if (m_address[i] != (addr[i] & m_netmask[i])) return false; return true; } int NetworkAddress::Compare(const NetworkAddress &addr) const { int diff = m_address.GetSize() - addr.m_address.GetSize(); if (diff == 0) { diff = GetNetmaskLen() - addr.GetNetmaskLen(); if (diff == 0) { const unsigned sz = m_address.GetSize(); for (unsigned i = 0; i < sz; i++) { diff = m_address[i] - addr.m_address[i]; if (diff != 0) break; } } } return diff; } PString NetworkAddress::AsString() const { return m_address.AsString() + "/" + PString(GetNetmaskLen()); } bool NetworkAddress::IsAny() const { return const_cast(this)->m_address.IsAny() ? true : false; } bool NetworkAddress::operator<(const NetworkAddress &addr) const { return Compare(addr) < 0; } bool NetworkAddress::operator<=(const NetworkAddress &addr) const { return Compare(addr) <= 0; } bool NetworkAddress::operator>(const NetworkAddress &addr) const { return Compare(addr) > 0; } bool NetworkAddress::operator>=(const NetworkAddress &addr) const { return Compare(addr) >= 0; } bool operator==(const PIPSocket::Address &addr, const NetworkAddress &net) { return net == addr; } bool operator<<(const PIPSocket::Address &addr, const NetworkAddress &net) { return net >> addr; } // class Toolkit::RouteTable::RouteEntry Toolkit::RouteTable::RouteEntry::RouteEntry( const PString & net ) : PIPSocket::RouteEntry(0) { destination = net.Tokenise("/", FALSE)[0]; GetNetworkFromString(net, network, net_mask); } Toolkit::RouteTable::RouteEntry::RouteEntry( const PIPSocket::RouteEntry & re, const InterfaceTable & it ) : PIPSocket::RouteEntry(re) { PINDEX i; for (i = 0; i < it.GetSize(); ++i) { const Address & ip = it[i].GetAddress(); if (Compare(&ip)) { destination = ip; return; } } for (i = 0; i < it.GetSize(); ++i) if (it[i].GetName() == interfaceName) { destination = it[i].GetAddress(); return; } } inline bool Toolkit::RouteTable::RouteEntry::Compare(const Address *ip) const { return (*ip == destination) || ((*ip & net_mask) == network); } // class Toolkit::RouteTable void Toolkit::RouteTable::InitTable() { // Workaround for OS doesn't support GetRouteTable PIPSocket::GetHostAddress(defAddr); ClearTable(); if (!CreateTable()) return; // Set default IP according to route table PIPSocket::Address defGW; PIPSocket::GetGatewayAddress(defGW); defAddr = GetLocalAddress(defGW); #if PTRACING for (RouteEntry *entry = rtable_begin; entry != rtable_end; ++entry) PTRACE(2, "Network=" << entry->GetNetwork() << '/' << entry->GetNetMask() << ", IP=" << entry->GetDestination()); PTRACE(2, "Default IP=" << defAddr); #endif } void Toolkit::RouteTable::ClearTable() { if (rtable_begin) { for (RouteEntry *r = rtable_begin; r != rtable_end; ++r) r->~RouteEntry(); ::free(rtable_begin); rtable_begin = 0; } } // can't pass a reference of Address, or STL complains... PIPSocket::Address Toolkit::RouteTable::GetLocalAddress(const Address & addr) const { RouteEntry *entry = find_if(rtable_begin, rtable_end, bind2nd(mem_fun_ref(&RouteEntry::Compare), &addr)); return (entry != rtable_end) ? entry->GetDestination() : defAddr; } bool Toolkit::RouteTable::CreateTable() { InterfaceTable if_table; if (!PIPSocket::GetInterfaceTable(if_table)) { PTRACE(1, "Error: Can't get interface table"); return false; } PTRACE(4, "InterfaceTable:\n" << setfill('\n') << if_table << setfill(' ')); PIPSocket::RouteTable r_table; if (!PIPSocket::GetRouteTable(r_table)) { PTRACE(1, "Error: Can't get route table"); return false; } int i = r_table.GetSize(); rtable_end = rtable_begin = static_cast(::malloc(i * sizeof(RouteEntry))); for (PINDEX r = 0; r < i ; ++r) { PIPSocket::RouteEntry & r_entry = r_table[r]; if (r_entry.GetNetMask() != INADDR_ANY) // placement operator ::new (rtable_end++) RouteEntry(r_entry, if_table); } return true; } bool Toolkit::VirtualRouteTable::CreateTable() { PString nets = GkConfig()->GetString("NetworkInterfaces", ""); if (nets.IsEmpty()) return false; PStringArray networks(nets.Tokenise(" ,;\t", FALSE)); int i = networks.GetSize(); if (i > 0) { rtable_end = rtable_begin = static_cast(::malloc(i * sizeof(RouteEntry))); for (PINDEX r = 0; r < i ; ++r) ::new (rtable_end++) RouteEntry(networks[r]); } return true; } // class Toolkit::ProxyCriterion Toolkit::ProxyCriterion::ProxyCriterion() : m_enable(false) { } Toolkit::ProxyCriterion::~ProxyCriterion() { } void Toolkit::ProxyCriterion::LoadConfig(PConfig *config) { m_networks.clear(); m_enable = AsBool(config->GetString(ProxySection, "Enable", "0")); if (!m_enable) { PTRACE(2, "GK\tH.323 Proxy disabled"); return; } PTRACE(2, "GK\tH.323 Proxy enabled"); PStringArray networks(config->GetString(ProxySection, "InternalNetwork", "").Tokenise(" ,;\t", FALSE)); for (PINDEX i = 0; i < networks.GetSize(); ++i) { m_networks.resize(m_networks.size() + 1); m_networks[m_networks.size() - 1] = NetworkAddress(networks[i]); PTRACE(2, "GK\tInternal Network " << i << " = " << m_networks.back().AsString()); } } bool Toolkit::ProxyCriterion::Required(const Address & ip1, const Address & ip2) const { return m_enable ? (m_networks.empty() || (IsInternal(ip1) != IsInternal(ip2))) : false; } bool Toolkit::ProxyCriterion::IsInternal(const Address & ip) const { std::vector::const_iterator i = m_networks.begin(); while (i != m_networks.end()) if (ip << *i++) return true; return false; } // class Toolkit::RewriteTool static const char *RewriteSection = "RasSrv::RewriteE164"; Toolkit::RewriteData::RewriteData(PConfig *config, const PString & section) { m_RewriteKey = NULL; PStringToString cfgs(config->GetAllKeyValues(section)); m_size = cfgs.GetSize(); if (m_size > 0) { std::map rules; for (PINDEX i = 0; i < m_size; ++i) { PString key = cfgs.GetKeyAt(i); if (!key && (isdigit(key[0]) || key[0]=='!' || key[0]=='.' || key[0]=='%' || key[0]=='*' || key[0]=='#')) rules[key] = cfgs.GetDataAt(i); } // now the rules are ascendantly sorted by the keys if ((m_size = rules.size()) > 0) { // replace array constructor with explicit memory allocation // and in-place new operators - workaround for VC compiler // m_RewriteKey = new PString[m_size * 2]; m_RewriteKey = (PString*)(new BYTE[sizeof(PString) * m_size * 2]); m_RewriteValue = m_RewriteKey + m_size; std::map::iterator iter = rules.begin(); // reverse the order for (int i = m_size; i-- > 0 ; ++iter) { // m_RewriteKey[i] = iter->first; ::new(m_RewriteKey + i) PString(iter->first); // m_RewriteValue[i] = iter->second; ::new(m_RewriteValue + i) PString(iter->second); } } } } Toolkit::RewriteData::~RewriteData() { // delete [] m_RewriteKey; if (m_RewriteKey) for (int i = 0; i < m_size * 2; i++) (m_RewriteKey+i)->~PString(); delete[] ((BYTE*)m_RewriteKey); } void Toolkit::RewriteTool::LoadConfig( PConfig *config ) { m_RewriteFastmatch = config->GetString(RewriteSection, "Fastmatch", ""); m_TrailingChar = config->GetString("RasSrv::ARQFeatures", "RemoveTrailingChar", " ")[0]; delete m_Rewrite; m_Rewrite = new RewriteData(config, RewriteSection); } bool Toolkit::RewriteTool::RewritePString(PString & s) const { bool changed = false; // remove trailing character if (s.GetLength() > 1 && s[s.GetLength() - 1] == m_TrailingChar) { s = s.Left(s.GetLength() - 1); changed = true; } // startsWith? if (strncmp(s, m_RewriteFastmatch, m_RewriteFastmatch.GetLength()) != 0) return changed; PString t; for (PINDEX i = 0; i < m_Rewrite->Size(); ++i) { const char *prefix = m_Rewrite->Key(i); const int len = MatchPrefix(s, prefix); // try a prefix match through all keys if (len > 0 || (len == 0 && prefix[0] == '!')) { // Rewrite to #t#. Append the suffix, too. // old: 01901234999 // 999 Suffix // 0190 Fastmatch // 01901234 prefix, Config-Rule: 01901234=0521321 // new: 0521321999 const char *newprefix = m_Rewrite->Value(i); PString result; if (len > 0) result = RewriteString(s, prefix, newprefix); else result = newprefix + s; PTRACE(2, "\tRewritePString: " << s << " to " << result); s = result; changed = true; break; } } return changed; } // class Toolkit::GWRewriteTool static const char *GWRewriteSection = "RasSrv::GWRewriteE164"; Toolkit::GWRewriteTool::~GWRewriteTool() { for (PINDEX i = 0; i < m_GWRewrite.GetSize(); ++i) { delete &(m_GWRewrite.GetDataAt(i)); } m_GWRewrite.RemoveAll(); } bool Toolkit::GWRewriteTool::RewritePString(PString gw, bool direction, PString &data) { GWRewriteEntry *gw_entry; PString key, value; // First lookup the GW in the dictionary gw_entry = m_GWRewrite.GetAt(gw); if (gw_entry == NULL) return false; std::vector >::iterator rule_iterator = direction ? gw_entry->m_entry_data.first.begin() : gw_entry->m_entry_data.second.begin(); std::vector >::iterator end_iterator = direction ? gw_entry->m_entry_data.first.end() : gw_entry->m_entry_data.second.end(); for (; rule_iterator != end_iterator; ++rule_iterator) { key = (*rule_iterator).first; const int len = MatchPrefix(data, key); if (len > 0 || (len == 0 && key[0] == '!')) { // Start rewrite value = (*rule_iterator).second; if (len > 0) value = RewriteString(data, key, value); else value = value + data; // Log PTRACE(2, "\tGWRewriteTool::RewritePString: " << data << " to " << value); // Finish rewrite data = value; return true; } } return false; } void Toolkit::GWRewriteTool::PrintData() { std::vector >::iterator rule_iterator; PTRACE(2, "GK\tLoaded per GW rewrite data:"); if (m_GWRewrite.GetSize() == 0) { PTRACE(2, "GK\tNo per GW data loaded"); return; } for (PINDEX i = 0; i < m_GWRewrite.GetSize(); ++i) { // In for (rule_iterator = m_GWRewrite.GetDataAt(i).m_entry_data.first.begin(); rule_iterator != m_GWRewrite.GetDataAt(i).m_entry_data.first.end(); ++rule_iterator) { PTRACE(3, "GK\t" << m_GWRewrite.GetKeyAt(i) << " (in): " << (*rule_iterator).first << " = " << (*rule_iterator).second); } // Out for (rule_iterator = m_GWRewrite.GetDataAt(i).m_entry_data.second.begin(); rule_iterator != m_GWRewrite.GetDataAt(i).m_entry_data.second.end(); ++rule_iterator) { PTRACE(3, "GK\t" << m_GWRewrite.GetKeyAt(i) << " (out): " << (*rule_iterator).first << " = " << (*rule_iterator).second); } } PTRACE(2, "GK\tLoaded " << m_GWRewrite.GetSize() << " GW entries with rewrite info"); } void Toolkit::GWRewriteTool::LoadConfig(PConfig *config) { PINDEX gw_size, i, j, lines_size; PString key, cfg_value; PStringArray lines, tokenised_line; GWRewriteEntry *gw_entry; std::map in_strings, out_strings; vector > sorted_in_strings, sorted_out_strings; std::map::reverse_iterator strings_iterator; pair rule; PStringToString cfgs(config->GetAllKeyValues(GWRewriteSection)); // Clear old config for (i = 0; i < m_GWRewrite.GetSize(); ++i) { delete &(m_GWRewrite.GetDataAt(i)); } m_GWRewrite.RemoveAll(); gw_size = cfgs.GetSize(); if (gw_size > 0) { for (i = 0; i < gw_size; ++i) { // Get the config keys key = cfgs.GetKeyAt(i); cfg_value = cfgs[key]; in_strings.clear(); out_strings.clear(); sorted_in_strings.clear(); sorted_out_strings.clear(); // Split the config data into seperate lines lines = cfg_value.Tokenise(PString(";")); lines_size = lines.GetSize(); for (j = 0; j < lines_size; ++j) { // Split the config line into three strings, direction, from string, to string tokenised_line = lines[j].Tokenise(PString("=")); if (tokenised_line.GetSize() < 3) { PTRACE(0, "GK\tSyntax error in the GWRewriteE164 rule - missing =, rule: " << key << " => " << lines[j] ); continue; } // Put into appropriate std::map if (tokenised_line[0] == "in") in_strings[tokenised_line[1]] = tokenised_line[2]; else if (tokenised_line[0] == "out") out_strings[tokenised_line[1]] = tokenised_line[2]; else PTRACE(0, "GK\tSyntax error in the GWRewriteE164 rule - unknown rule type (" << tokenised_line[0] << ", rule: " << key << " => " << lines[j] ); } // Put the map contents into reverse sorted vectors for (strings_iterator = in_strings.rbegin(); strings_iterator != in_strings.rend(); ++strings_iterator) { rule = *strings_iterator; sorted_in_strings.push_back(rule); } for (strings_iterator = out_strings.rbegin(); strings_iterator != out_strings.rend(); ++strings_iterator) { rule = *strings_iterator; sorted_out_strings.push_back(rule); } // Create the entry gw_entry = new GWRewriteEntry(); gw_entry->m_entry_data.first = sorted_in_strings; gw_entry->m_entry_data.second = sorted_out_strings; // Add to PDictionary hash table m_GWRewrite.Insert(key,gw_entry); } } PrintData(); } Toolkit::Toolkit() : Singleton("Toolkit"), m_Config(NULL), m_ConfigDirty(false), m_acctSessionCounter(0), m_acctSessionBase((long)time(NULL)), m_timerManager(new GkTimerManager()), m_timestampFormatStr("Cisco"), m_encKeyPaddingByte(-1), m_encryptAllPasswords(false), m_cliRewrite(NULL) { srand(time(0)); } Toolkit::~Toolkit() { if (m_Config) { delete m_Config; PFile::Remove(m_tmpconfig); PFile::Remove(m_extConfigFilePath); } delete m_timerManager; delete m_cliRewrite; } Toolkit::RouteTable *Toolkit::GetRouteTable(bool real) { return real ? &m_RouteTable : m_VirtualRouteTable.IsEmpty() ? &m_RouteTable : &m_VirtualRouteTable; } PConfig* Toolkit::Config() { // Make sure the config would not be called before SetConfig PAssert(!m_ConfigDefaultSection, "Error: Call Config() before SetConfig()!"); return (m_Config == NULL) ? ReloadConfig() : m_Config; } PConfig* Toolkit::Config(const char *section) { Config()->SetDefaultSection(section); return m_Config; } PConfig* Toolkit::SetConfig(const PFilePath &fp, const PString §ion) { m_ConfigFilePath = fp; m_ConfigDefaultSection = section; return ReloadConfig(); } void Toolkit::SetConfig(int act, const PString & sec, const PString & key, const PString & value) { // the original config PConfig cfg(m_ConfigFilePath, m_ConfigDefaultSection); switch (act) { case 1: cfg.SetString(sec, key, value); m_Config->SetString(sec, key, value); break; case 2: cfg.DeleteKey(sec, key); m_Config->DeleteKey(sec, key); break; case 3: cfg.DeleteSection(sec); m_Config->DeleteSection(sec); break; } m_ConfigDirty = true; } PString Toolkit::GetTempDir() const { PString tmpdir; #ifndef _WIN32 // check if the directory exists and is accessible (access rights) if (PFile::Exists("/tmp") && PFile::Access("/tmp", PFile::ReadWrite)) tmpdir = "/tmp"; else #endif { PConfig cfg(PConfig::Environment); if (cfg.HasKey("TMP")) tmpdir = cfg.GetString("TMP"); else if (cfg.HasKey("TEMP")) tmpdir = cfg.GetString("TEMP"); else if (cfg.HasKey("TMPDIR")) tmpdir = cfg.GetString("TMPDIR"); } if (!tmpdir.IsEmpty()) { // strip trailing separator if (tmpdir[tmpdir.GetLength()-1] == PDIR_SEPARATOR) tmpdir = tmpdir.Left(tmpdir.GetLength()-1); // check if the directory exists and is accessible (access rights) if (!(PFile::Exists(tmpdir) && PFile::Access(tmpdir, PFile::ReadWrite))) tmpdir = PString(); } return tmpdir; } void Toolkit::CreateConfig() { if (m_Config != NULL) PFile::Remove(m_tmpconfig); PString tmpdir = GetTempDir(); #ifdef _WIN32 if (tmpdir.IsEmpty()) if (PFile::Access(".", PFile::ReadWrite)) tmpdir = "."; else { const PFilePath fpath(m_ConfigFilePath); tmpdir = fpath.GetDirectory(); } #else if (tmpdir.IsEmpty()) tmpdir = "."; #endif // generate a unique name do { m_tmpconfig = tmpdir + PDIR_SEPARATOR + "gnugk.ini-" + PString(PString::Unsigned, rand()%10000); PTRACE(5, "GK\tTrying file name "<< m_tmpconfig << " for temp config"); } while (PFile::Exists(m_tmpconfig)); #ifdef _WIN32 // Does WIN32 support symlink? if (PFile::Copy(m_ConfigFilePath, m_tmpconfig)) { #else if (symlink(m_ConfigFilePath, m_tmpconfig) == 0) { #endif delete m_Config; m_Config = new PConfig(m_tmpconfig, m_ConfigDefaultSection); } else { // Oops! Create temporary config file failed, use the original one PTRACE(0, "CONFIG\tCould not create/link config to a temporary file " << m_tmpconfig); delete m_Config; m_Config = new PConfig(m_ConfigFilePath, m_ConfigDefaultSection); } if (!m_extConfigFilePath) PFile::Remove(m_extConfigFilePath); // generate a unique name do { m_extConfigFilePath = tmpdir + PDIR_SEPARATOR + "gnugk.ini-" + PString(PString::Unsigned, rand()%10000); PTRACE(5, "GK\tTrying file name "<< m_extConfigFilePath << " for external config"); } while (PFile::Exists(m_extConfigFilePath)); m_Config = new GatekeeperConfig( m_extConfigFilePath, m_ConfigDefaultSection, m_Config ); } void Toolkit::ReloadSQLConfig() { #if HAS_MYSQL || HAS_PGSQL if (m_Config->GetSections().GetStringsIndex("SQLConfig") == P_MAX_INDEX) return; const PString driverName = m_Config->GetString("SQLConfig", "Driver", ""); if (driverName.IsEmpty()) { PTRACE(0, "SQLCONF\tFailed to read config settings from SQL: no driver specified"); return; } GkSQLConnection *sqlConn = GkSQLConnection::Create(driverName, "SQLCONF"); if (sqlConn == NULL) { PTRACE(0, "SQLCONF\tFailed to create a connection: no driver found for " << driverName << " database" ); return; } if (!sqlConn->Initialize(m_Config, "SQLConfig")) { delete sqlConn; sqlConn = NULL; PTRACE(0, "SQLCONF\tFailed to read config settings from SQL: could not connect to the database"); return; } PTRACE(3, "SQLCONF\tSQL config connection established"); PString query; GkSQLResult* queryResult; query = m_Config->GetString("SQLConfig", "ConfigQuery", ""); if (!query.IsEmpty()) { PTRACE(4, "SQLCONF\tLoading config key=>value pairs from SQL database"); PStringArray params; params += GKName(); queryResult = sqlConn->ExecuteQuery(query, ¶ms); if (queryResult == NULL) PTRACE(0, "SQLCONF\tFailed to load config key=>value pairs from SQL " "database: timeout or fatal error" ); else if (!queryResult->IsValid()) PTRACE(0, "SQLCONF\tFailed to load config key=>value pairs from SQL " "database (" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage() ); else if (queryResult->GetNumFields() < 3) PTRACE(0, "SQLCONF\tFailed to load config key=>value pairs from SQL " "database: at least 3 columns must be present in the result set" ); else { while (queryResult->FetchRow(params)) if (params[0].IsEmpty() || params[1].IsEmpty()) PTRACE(1, "SQLCONF\tInvalid config key=>value pair entry found " "in the SQL database: '[" << params[0] << "] " << params[1] << '=' << params[1] << '\'' ); else { m_Config->SetString(params[0], params[1], params[2]); PTRACE(6, "SQLCONF\tConfig entry read: '[" << params[0] << "] " << params[1] << '=' << params[2] << '\'' ); } PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() << " config key=>value pairs loaded from SQL database"); } delete queryResult; queryResult = NULL; } query = m_Config->GetString("SQLConfig", "RewriteE164Query", ""); if (!query.IsEmpty()) { PTRACE(4, "SQLCONF\tLoading rewrite rules from SQL database"); PStringArray params; params += GKName(); queryResult = sqlConn->ExecuteQuery(query, ¶ms); if (queryResult == NULL) PTRACE(0, "SQLCONF\tFailed to load rewrite rules from SQL database: " "timeout or fatal error" ); else if (!queryResult->IsValid()) PTRACE(0, "SQLCONF\tFailed to load rewrite rules from SQL database (" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage() ); else if (queryResult->GetNumFields() < 2) PTRACE(0, "SQLCONF\tFailed to load rewrite rules from SQL database: " "at least 2 columns must be present in the result set" ); else { while (queryResult->FetchRow(params)) if (params[0].IsEmpty()) PTRACE(1, "SQLCONF\tInvalid rewrite rule found in the SQL " "database: '" << params[0] << '=' << params[1] << '\'' ); else { m_Config->SetString("RasSrv::RewriteE164", params[0], params[1]); PTRACE(6, "SQLCONF\tRewrite rule read: '" << params[0] << '=' << params[1] << '\'' ); } PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() << " rewrite rules " "loaded from SQL database" ); } delete queryResult; queryResult = NULL; } query = m_Config->GetString("SQLConfig", "NeighborsQuery", ""); if (!query.IsEmpty()) { PTRACE(4, "SQLCONF\tLoading neighbors from SQL database"); PStringArray params; params += GKName(); queryResult = sqlConn->ExecuteQuery(query, ¶ms); if (queryResult == NULL) PTRACE(0, "SQLCONF\tFailed to load neighbors from SQL database: " "timeout or fatal error" ); else if (!queryResult->IsValid()) PTRACE(0, "SQLCONF\tFailed to load neighbors from SQL database (" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage() ); else if (queryResult->GetNumFields() < 6) PTRACE(0, "SQLCONF\tFailed to load neighbors from SQL database: " "at least 6 columns must be present in the result set" ); else { while (queryResult->FetchRow(params)) { PString value; if (!params[5]) value = ";" + params[5]; if (!(params[4].IsEmpty() && value.IsEmpty())) value = ";" + params[4] + value; if (!(params[3].IsEmpty() && value.IsEmpty())) value = ";" + params[3] + value; if (!params[2]) value = params[1] + ":" + params[2] + value; else value = params[1] + value; if (params[0].IsEmpty() || params[1].IsEmpty()) PTRACE(1, "SQLCONF\tInvalid neighbor entry found in the SQL " "database: '" << params[0] << '=' << value << '\'' ); else { m_Config->SetString("RasSrv::Neighbors", params[0], value); PTRACE(6, "SQLCONF\tNeighbor entry read: '" << params[0] << '=' << value << '\'' ); } } PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() << " neighbor entries " "loaded from SQL database" ); } delete queryResult; queryResult = NULL; } query = m_Config->GetString("SQLConfig", "PermanentEndpointsQuery", ""); if (!query.IsEmpty()) { PTRACE(4, "SQLCONF\tLoading permanent endpoints from SQL database"); PStringArray params; params += GKName(); queryResult = sqlConn->ExecuteQuery(query, ¶ms); if (queryResult == NULL) PTRACE(0, "SQLCONF\tFailed to load permanent endpoints from SQL " "database: timeout or fatal error" ); else if (!queryResult->IsValid()) PTRACE(0, "SQLCONF\tFailed to load permanent endpoints from SQL database " "(" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage() ); else if (queryResult->GetNumFields() < 4) PTRACE(0, "SQLCONF\tFailed to load permanent endpoints from SQL database: " "at least 4 columns must be present in the result set" ); else { PString key; PString value; while (queryResult->FetchRow(params)) { key = params[0]; if (!params[1]) key += ":" + params[1]; value = params[2]; if (!params[3]) value += ";" + params[3]; if (key.IsEmpty() || value.IsEmpty()) PTRACE(1, "SQLCONF\tInvalid permanent endpoint entry found " "in the SQL database: '" << key << '=' << value << '\'' ); else { m_Config->SetString("RasSrv::PermanentEndpoints", key, value); PTRACE(6, "SQLCONF\tPermanent endpoint read: '" << key << '=' << value << '\'' ); } } PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() << " permanent " "endpoints loaded from SQL database" ); } delete queryResult; queryResult = NULL; } query = m_Config->GetString("SQLConfig", "GWPrefixesQuery", ""); if (!query.IsEmpty()) { PTRACE(4, "SQLCONF\tLoading gateway prefixes from SQL database"); PStringArray params; params += GKName(); queryResult = sqlConn->ExecuteQuery(query, ¶ms); if (queryResult == NULL) PTRACE(0, "SQLCONF\tFailed to load gateway prefixes from SQL database: " "timeout or fatal error" ); else if (!queryResult->IsValid()) PTRACE(0, "SQLCONF\tFailed to load gateway prefixes from SQL database (" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage() ); else if (queryResult->GetNumFields() < 2) PTRACE(0, "SQLCONF\tFailed to load gateway prefixes from SQL database: " "at least 2 columns must be present in the result set" ); else { while (queryResult->FetchRow(params)) if (params[0].IsEmpty() || params[1].IsEmpty()) PTRACE(1, "SQLCONF\tInvalid gateway prefixes entry found " "in the SQL database: '" << params[0] << '=' << params[1] << '\'' ); else { m_Config->SetString("RasSrv::GWPrefixes", params[0], params[1] ); PTRACE(6, "SQLCONF\tGateway prefixes read: '" << params[0] << '=' << params[1] << '\'' ); } PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() << " gateway prefixes " "loaded from SQL database" ); } delete queryResult; queryResult = NULL; } delete sqlConn; sqlConn = NULL; PTRACE(3, "SQLCONF\tSQL config connection closed"); #endif // HAS_MYSQL || HAS_PGSQL } PConfig* Toolkit::ReloadConfig() { if (!m_ConfigDirty) CreateConfig(); else // the config have been changed via status port, use it directly m_ConfigDirty = false; m_GKName = Config()->GetString("Name", "OpenH323GK"); m_encryptAllPasswords = Toolkit::AsBool( Config()->GetString("EncryptAllPasswords", "0") ); if (Config()->HasKey(paddingByteConfigKey)) m_encKeyPaddingByte = Config()->GetInteger(paddingByteConfigKey, 0); else m_encKeyPaddingByte = m_encryptAllPasswords ? 0 : -1; ReloadSQLConfig(); m_RouteTable.InitTable(); m_VirtualRouteTable.InitTable(); m_ProxyCriterion.LoadConfig(m_Config); m_Rewrite.LoadConfig(m_Config); m_GWRewrite.LoadConfig(m_Config); PString GKHome(m_Config->GetString("Home", "")); if (m_GKHome.empty() || !GKHome) SetGKHome(GKHome.Tokenise(",:;", false)); m_timestampFormatStr = Config()->GetString("TimestampFormat", "Cisco"); delete m_cliRewrite; m_cliRewrite = new CLIRewrite; CapacityControl::Instance()->LoadConfig(); LoadCauseMap(m_Config); return m_Config; } void Toolkit::LoadCauseMap( PConfig *cfg ) { memset(m_causeMap, 0, 16); if (! Toolkit::AsBool(cfg->GetString(RoutedSec, "ActivateFailover", "0"))) return; PStringArray causes(cfg->GetString(RoutedSec, "FailoverCauses", "1-15,21-127").Tokenise(", \t", FALSE)); for (PINDEX i = 0; i < causes.GetSize(); ++i) if (causes[i].Find('-') == P_MAX_INDEX) { unsigned c = causes[i].AsUnsigned() & 0x7f; m_causeMap[c >> 3] |= (1UL << (c & 7)); } else { PStringArray causeRange(causes[i].Tokenise("- ", FALSE)); if (causeRange.GetSize() == 2) { unsigned cmin = causeRange[0].AsUnsigned() & 0x7f; unsigned cmax = causeRange[1].AsUnsigned() & 0x7f; for (; cmin <= cmax; ++cmin) m_causeMap[cmin >> 3] |= (1UL << (cmin & 7)); } } } BOOL Toolkit::MatchRegex(const PString &str, const PString ®exStr) { PINDEX pos=0; PRegularExpression regex(regexStr, PRegularExpression::Extended); if(regex.GetErrorCode() != PRegularExpression::NoError) { PTRACE(2, "Errornous '"<< regex.GetErrorText() <<"' compiling regex: " << regexStr); return FALSE; } if(!regex.Execute(str, pos)) { PTRACE(5, "Gk\tRegex '"< & GKHome) const { GKHome = m_GKHome; PString result; int hsize = GKHome.size(); for (int i = 0; i < hsize; ++i) { result += GKHome[i].AsString(); if (i < hsize - 1) result += ","; } return result; } void Toolkit::SetGKHome(const PStringArray & home) { std::vector::iterator begin; m_GKHome.clear(); int n, i, size = home.GetSize(); if (size > 0) for (n = 0; n < size; ++n) m_GKHome.push_back(home[n]); PIPSocket::InterfaceTable it; if (PIPSocket::GetInterfaceTable(it)) { int is = it.GetSize(); if (size > 0) { // check if the interface is valid for (n = 0; n < size; ++n) { for (i = 0; i < is; ++i) if (m_GKHome[n] == it[i].GetAddress()) break; if (i == is) { PTRACE(1, "GK\tAddress " << m_GKHome[n] << " not found" " in the PWLib interface table" ); //begin = m_GKHome.begin(); //copy(begin + n + 1, begin + size, begin + n); //--size, --n; } } } if (size == 0) { m_GKHome.clear(); size = is; for (n = 0; n < size; ++n) m_GKHome.push_back(it[n].GetAddress()); } // remove INADDR_ANY for (n = 0; n < size; ++n) if (m_GKHome[n] == INADDR_ANY) { begin = m_GKHome.begin(); copy(begin + n + 1, begin + size, begin + n); --size, --n; } } // remove duplicate interfaces for (n = 0; n < size; ++n) for (i = 0; i < n; ++i) if (m_GKHome[n] == m_GKHome[i]) { begin = m_GKHome.begin(); copy(begin + n + 1, begin + size, begin + n); --size, --n; break; } m_GKHome.resize(size); // put the default IP to the first begin = find(m_GKHome.begin(), m_GKHome.end(), m_RouteTable.GetLocalAddress()); if (begin != m_GKHome.end()) swap(m_GKHome[0], *begin); } int Toolkit::GetInternalExtensionCode( const unsigned &country, const unsigned &extension, const unsigned &manufacturer) const { switch(country) { case t35cOpenOrg: switch(manufacturer) { case t35mOpenOrg: switch(extension) { case t35eFailoverRAS: return iecFailoverRAS; } } } // default for all other cases return iecUnknown; } int Toolkit::GetInternalExtensionCode(const H225_H221NonStandard& data) const { return GetInternalExtensionCode(data.m_t35CountryCode, data.m_t35Extension, data.m_manufacturerCode); } bool Toolkit::AsBool(const PString & str) { if (str.IsEmpty()) return false; const unsigned char c = (unsigned char)tolower(str[0]); return ( c=='t' || c=='1' || c=='y' || c=='a' ); } void Toolkit::GetNetworkFromString( const PString &s, PIPSocket::Address &network, PIPSocket::Address &netmask ) { if (s *= "ALL") { network = netmask = INADDR_ANY; return; } PINDEX slashPos = s.Find('/'); if (slashPos == P_MAX_INDEX) { // a single IP static BYTE fullNetMask[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; network = PIPSocket::Address(s); netmask = PIPSocket::Address(network.GetSize(), fullNetMask); } else { network = PIPSocket::Address(s.Left(slashPos)); const PString netmaskString = s.Mid(slashPos + 1); BYTE rawData[16]; if (netmaskString.FindOneOf(".:") != P_MAX_INDEX) { // netmask as a network address netmask = PIPSocket::Address(netmaskString); } else { // netmask as an integer const DWORD netmaskLen = netmaskString.AsUnsigned(); for (unsigned b = 0; b < (unsigned)(network.GetSize() * 8); b++) if (b < netmaskLen) rawData[b >> 3] |= 0x80U >> (b & 7); else rawData[b >> 3] &= ~(0x80U >> (b & 7)); netmask = PIPSocket::Address(network.GetSize(), rawData); } // normalize the address for (unsigned b = 0; b < (unsigned)(network.GetSize()); b++) rawData[b] = network[b] & netmask[b]; network = PIPSocket::Address(network.GetSize(), rawData); } } PString Toolkit::CypherDecode(const PString & key, const PString & crypto, int s) { size_t sz = key.GetLength(); if (sz > sizeof(PTEACypher::Key)) sz = sizeof(PTEACypher::Key); PTEACypher::Key thekey; memset(&thekey, s, sizeof(PTEACypher::Key)); memcpy(&thekey, (const char *)key, sz); PTEACypher cypher(thekey); return cypher.Decode(crypto); } PString Toolkit::GenerateAcctSessionId() { PWaitAndSignal lock( m_acctSessionMutex ); return psprintf(PString("%08x%08x"),m_acctSessionBase,++m_acctSessionCounter); } bool Toolkit::AsTimeInterval( /// formatted time interval string const char* inputString, /// variable to store calculated time interval on success PTimeInterval& interval ) { if (inputString == NULL) return false; PTimeInterval result; bool process_next = true; bool valid = false; while (process_next) { char* strend = const_cast(inputString); long val = strtol(inputString, &strend, 10); // no tokens found? if (strend == inputString) break; // integer range overflow if ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE) { valid = false; break; } valid = true; inputString = strend; // the last token if (*inputString == 0) { result += PTimeInterval(val); break; } // unit specifier switch (*inputString) { case 's': result += PTimeInterval(0,val); break; case 'm': result += PTimeInterval(0,0,val); break; case 'h': result += PTimeInterval(0,0,0,val); break; case 'd': result += PTimeInterval(0,0,0,0,val); break; case 'w': result += PTimeInterval(0,0,0,0,val*7); break; case 'M': result += PTimeInterval(0,0,0,0,val*30); break; case 'y': result += PTimeInterval(0,0,0,0,val*365); break; default: result += PTimeInterval(val); process_next = false; } if (process_next) inputString++; } if (valid) interval = result; return valid; } PString Toolkit::AsString( const PTime& tm, /// timestamp to convert into a string const PString& formatStr /// format string to use ) { PString fmtStr = !formatStr ? formatStr : m_timestampFormatStr; if (fmtStr.IsEmpty()) return PString(); if (fmtStr *= "Cisco") fmtStr = "%H:%M:%S.%u %Z %a %b %d %Y"; else if (fmtStr *= "ISO8601") return tm.AsString(PTime::LongISO8601); else if (fmtStr *= "RFC822") return tm.AsString(PTime::RFC1123); else if (fmtStr *= "MySQL" ) fmtStr = "%Y-%m-%d %H:%M:%S"; struct tm _tm; struct tm* tmptr = &_tm; time_t t = tm.GetTimeInSeconds(); #ifndef _WIN32 if (localtime_r(&t, tmptr) != tmptr) { #else tmptr = localtime(&t); if (tmptr == NULL) { #endif PTRACE(0, "TOOLKIT\tCould not apply timestamp formatting - using default"); return tm.AsString( "hh:mm:ss.uuu z www MMM d yyyy" ); } // replace %u with microseconds - this is our extension PINDEX i = 0; PINDEX length = fmtStr.GetLength(); do { i = fmtStr.Find("%u", i); if (i != P_MAX_INDEX) { if (i > 0 && fmtStr[i-1] == '%') { i += 2; continue; } const PString us(PString::Printf, "%03d", (unsigned)tm.GetMicrosecond()); fmtStr.Splice(us, i, 2); length += us.GetLength(); i += us.GetLength(); length -= 2; } } while (i != P_MAX_INDEX && i < length); PString buf; buf.SetSize(128); if (strftime(buf.GetPointer(), 128, (const char*)fmtStr, tmptr) == 0) { PTRACE(0, "TOOLKIT\tCould not apply timestamp formatting - using default"); return tm.AsString( "hh:mm:ss.uuu z www MMM d yyyy" ); } buf.MakeMinimumSize(); return buf; } PString Toolkit::ReadPassword( const PString &cfgSection, /// config section to read const PString &cfgKey, /// config key to read an encrypted password from bool forceEncrypted ) { if (cfgSection.IsEmpty() || cfgKey.IsEmpty()) return PString(); PConfig* const cfg = Config(); if (!cfg->HasKey(cfgSection, cfgKey)) return PString(); int paddingByte = m_encKeyPaddingByte; if (cfg->HasKey(cfgSection, paddingByteConfigKey)) paddingByte = cfg->GetInteger(cfgSection, paddingByteConfigKey, 0); if (paddingByte == -1) if (forceEncrypted || m_encryptAllPasswords) paddingByte = 0; else return cfg->GetString(cfgSection, cfgKey, ""); PTEACypher::Key encKey; memset(&encKey, paddingByte, sizeof(encKey)); const size_t keyLen = cfgKey.GetLength(); if (keyLen > 0) memcpy(&encKey, (const char*)cfgKey, min(keyLen, sizeof(encKey))); PTEACypher cypher(encKey); PString s; if (!cypher.Decode(cfg->GetString(cfgSection, cfgKey, ""), s)) PTRACE(1, "GK\tFailed to decode config password for [" << cfgSection << "] => " << cfgKey ); return s; } void Toolkit::RewriteCLI( SetupMsg &msg ) const { m_cliRewrite->InRewrite(msg); } void Toolkit::RewriteCLI( SetupMsg &msg, SetupAuthData &authData, const PIPSocket::Address &addr ) const { m_cliRewrite->OutRewrite(msg, authData, addr); } void Toolkit::SetRerouteCauses( unsigned char *causeMap ) { memcpy(causeMap, m_causeMap, 128/8); }