./PaxHeaders.16356/gnugk-3.40000644000175000001440000000005012223461076013604 xustar000000000000000020 atime=1380868629 20 ctime=1380868670 gnugk-3.4/0000755000175000001440000000000012223461076012465 5ustar00janusers00000000000000gnugk-3.4/PaxHeaders.16356/capctrl.cxx0000644000175000001440000000005011664677645015723 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/capctrl.cxx0000644000175000001440000005164611664677645014700 0ustar00janusers00000000000000/* * capctrl.cxx * * Module for accoutning per IP/H.323 ID/CLI/prefix inbound call volume * * $Id: capctrl.cxx,v 1.24 2011/11/28 12:33:41 willamowius Exp $ * * Copyright (c) 2006, Michal Zygmuntowicz * Copyright (c) 2008-2010, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include #include #include #include #include "h323util.h" #include "RasTbl.h" #include "RasPDU.h" #include "sigmsg.h" #include "gkacct.h" #include "gkauth.h" #include "capctrl.h" namespace { // greater operators for sorting route lists struct IpRule_greater : public std::binary_function { bool operator()(const CapacityControl::IpCallVolume &e1, const CapacityControl::IpCallVolume &e2) const { if (e1.first.IsAny()) { if (!e2.first.IsAny()) return false; } else { if (e2.first.IsAny()) return true; int diff = e1.first.Compare(e2.first); if (diff != 0) return diff > 0; } return false; } }; struct H323IdRule_greater : public std::binary_function { bool operator()(const CapacityControl::H323IdCallVolume &e1, const CapacityControl::H323IdCallVolume &e2) const { return H323GetAliasAddressString(e1.first) > H323GetAliasAddressString(e2.first); } }; struct CLIRule_greater : public std::binary_function { bool operator()(const CapacityControl::CLICallVolume &e1, const CapacityControl::CLICallVolume &e2) const { return e1.first.compare(e2.first) > 0; } }; } // end of anonymous namespace CapacityControl::InboundCallVolume::InboundCallVolume() : m_maxVolume(0) { } CapacityControl::InboundCallVolume::~InboundCallVolume() { } PString CapacityControl::InboundCallVolume::AsString() const { return PString("pfx: ") + (m_prefix.empty() ? "*" : m_prefix.c_str()) + ", vol (cur/max): " + PString(m_calls.size()) + "/" + PString(m_maxVolume); } bool CapacityControl::InboundCallVolume::operator==(const InboundCallVolume &obj) const { return m_prefix == obj.m_prefix; } bool CapacityControl::InboundIPCallVolume::operator==(const InboundIPCallVolume &obj) const { return m_sourceAddress == obj.m_sourceAddress && ((InboundCallVolume&)*this) == ((InboundCallVolume&)obj); } bool CapacityControl::InboundH323IdCallVolume::operator==(const InboundH323IdCallVolume &obj) const { return m_sourceH323Id == obj.m_sourceH323Id && ((InboundCallVolume&)*this) == ((InboundCallVolume&)obj); } bool CapacityControl::InboundCLICallVolume::operator==(const InboundCLICallVolume &obj) const { return m_sourceCLI == obj.m_sourceCLI && ((InboundCallVolume&)*this) == ((InboundCallVolume&)obj); } CapacityControl::CapacityControl( ) : Singleton("CapacityControl") { LoadConfig(); } void CapacityControl::LoadConfig() { IpCallVolumes ipCallVolumes; H323IdCallVolumes h323IdCallVolumes; CLICallVolumes cliCallVolumes; PConfig* cfg = GkConfig(); const PString cfgSec("CapacityControl"); unsigned ipRules = 0, h323IdRules = 0, cliRules = 0; const PStringToString kv = cfg->GetAllKeyValues(cfgSec); for (PINDEX i = 0; i < kv.GetSize(); ++i) { PString key = kv.GetKeyAt(i); if (key[0] == '%') { const PINDEX sepIndex = key.Find('%', 1); if (sepIndex != P_MAX_INDEX) key = key.Mid(sepIndex + 1).Trim(); } // on Unix multiple entries for the same key are concatenated // to one string and separated by a new line PStringArray dataLines = kv.GetDataAt(i).Tokenise("\n", FALSE); for (PINDEX d = 0; d < dataLines.GetSize(); d++) { PString data = dataLines[d]; InboundCallVolume *rule = NULL; bool newIpRule = false, newH323IdRule = false, newCLIRule = false; // check the rule type (ip/h323id/cli) if (key.Find("ip:") == 0) { const PString ip = key.Mid(3).Trim(); NetworkAddress addr; if (!(ip == "*" || ip == "any")) addr = NetworkAddress(ip); ipCallVolumes.push_back(IpCallVolume(addr, InboundIPCallVolume(addr))); newIpRule = true; rule = &(ipCallVolumes.back().second); } else if (key.Find("h323id:") == 0) { H225_AliasAddress alias; const PString h323id = key.Mid(7).Trim(); H323SetAliasAddress(h323id, alias, H225_AliasAddress::e_h323_ID); h323IdCallVolumes.push_back(H323IdCallVolume(alias, InboundH323IdCallVolume(alias))); newH323IdRule = true; rule = &(h323IdCallVolumes.back().second); } else if (key.Find("cli:") == 0) { const std::string cli((const char*)(key.Mid(4).Trim())); cliCallVolumes.push_back(CLICallVolume(cli, InboundCLICallVolume(cli))); newCLIRule = true; rule = &(cliCallVolumes.back().second); } else { PTRACE(1, "CAPCTRL\tUknown CapacityControl rule: " << key << '=' << kv.GetDataAt(i) ); continue; } PStringArray tokens(data.Tokenise(" \t", FALSE)); if (tokens.GetSize() < 1) { PTRACE(1, "CAPCTRL\tInvalid CapacityControl rule syntax: " << key << '=' << kv.GetDataAt(i) ); if (newIpRule) ipCallVolumes.pop_back(); else if (newH323IdRule) h323IdCallVolumes.pop_back(); else if (newCLIRule) cliCallVolumes.pop_back(); continue; } unsigned tno = 0; if (tokens.GetSize() >= 2) rule->m_prefix = string((const char*)(tokens[tno++])); rule->m_maxVolume = tokens[tno++].AsUnsigned(); if (newIpRule) ++ipRules; else if (newH323IdRule) ++h323IdRules; else if (newCLIRule) ++cliRules; } /* for (d) */ } /* for (i) */ // sort rules by IP network mask length std::stable_sort(ipCallVolumes.begin(), ipCallVolumes.end(), IpRule_greater()); std::stable_sort(h323IdCallVolumes.begin(), h323IdCallVolumes.end(), H323IdRule_greater()); std::stable_sort(cliCallVolumes.begin(), cliCallVolumes.end(), CLIRule_greater()); PWaitAndSignal lock(m_updateMutex); // update route entries that have not changed { IpCallVolumes::const_iterator rule = m_ipCallVolumes.begin(); while (rule != m_ipCallVolumes.end()) { IpCallVolumes::iterator matchingRule = find( ipCallVolumes.begin(), ipCallVolumes.end(), *rule ); if (matchingRule != ipCallVolumes.end() && matchingRule->second == rule->second) { matchingRule->second.m_calls = rule->second.m_calls; } ++rule; } } { H323IdCallVolumes::const_iterator rule = m_h323IdCallVolumes.begin(); while (rule != m_h323IdCallVolumes.end()) { H323IdCallVolumes::iterator matchingRule = find( h323IdCallVolumes.begin(), h323IdCallVolumes.end(), *rule ); if (matchingRule != h323IdCallVolumes.end() && matchingRule->second == rule->second) { matchingRule->second.m_calls = rule->second.m_calls; } ++rule; } } { CLICallVolumes::const_iterator rule = m_cliCallVolumes.begin(); while (rule != m_cliCallVolumes.end()) { CLICallVolumes::iterator matchingRule = find( cliCallVolumes.begin(), cliCallVolumes.end(), *rule ); if (matchingRule != cliCallVolumes.end() && matchingRule->second == rule->second) { matchingRule->second.m_calls = rule->second.m_calls; } ++rule; } } m_ipCallVolumes.clear(); m_h323IdCallVolumes.clear(); m_cliCallVolumes.clear(); m_ipCallVolumes = ipCallVolumes; m_h323IdCallVolumes = h323IdCallVolumes; m_cliCallVolumes = cliCallVolumes; PTRACE(5, "CAPCTRL\t" << ipRules << " IP rules loaded"); if (PTrace::CanTrace(6)) { ostream &strm = PTrace::Begin(6, __FILE__, __LINE__); strm << "Per IP call volume rules:" << endl; for (unsigned i = 0; i < m_ipCallVolumes.size(); ++i) { strm << "\tsrc " << m_ipCallVolumes[i].first.AsString() << ":" << endl; strm << "\t\t" << m_ipCallVolumes[i].second.AsString() << endl; } PTrace::End(strm); } PTRACE(5, "CAPCTRL\t" << h323IdRules << " H.323 ID rules loaded"); if (PTrace::CanTrace(6)) { ostream &strm = PTrace::Begin(6, __FILE__, __LINE__); strm << "Per H.323 ID call volume rules:" << endl; for (unsigned i = 0; i < m_h323IdCallVolumes.size(); i++) { strm << "\tsrc " << H323GetAliasAddressString(m_h323IdCallVolumes[i].first) << ":" << endl; strm << "\t\t" << m_h323IdCallVolumes[i].second.AsString() << endl; } PTrace::End(strm); } PTRACE(5, "CAPCTRL\t" << cliRules << " CLI rules loaded"); if (PTrace::CanTrace(6)) { ostream &strm = PTrace::Begin(6, __FILE__, __LINE__); strm << "Per CLI call volume rules:" << endl; for (unsigned i = 0; i < m_cliCallVolumes.size(); i++) { strm << "\tsrc " << m_cliCallVolumes[i].first << ":" << endl; strm << "\t\t" << m_cliCallVolumes[i].second.AsString() << endl; } PTrace::End(strm); } } PString CapacityControl::PrintRules() { // std::stringstream strm; // VS2005 version leaks memory!! PStringStream strm; strm << "Per IP call volume rules:" << endl; for (unsigned i = 0; i < m_ipCallVolumes.size(); ++i) { strm << " src " << m_ipCallVolumes[i].first.AsString() << ":" << endl; strm << " " << m_ipCallVolumes[i].second.AsString() << endl; } strm << "Per H.323 ID call volume rules:" << endl; for (unsigned i = 0; i < m_h323IdCallVolumes.size(); i++) { strm << " src " << H323GetAliasAddressString(m_h323IdCallVolumes[i].first) << ":" << endl; strm << " " << m_h323IdCallVolumes[i].second.AsString() << endl; } strm << "Per CLI call volume rules:" << endl; for (unsigned i = 0; i < m_cliCallVolumes.size(); i++) { strm << " src " << m_cliCallVolumes[i].first << ":" << endl; strm << " " << m_cliCallVolumes[i].second.AsString() << endl; } return strm; } CapacityControl::IpCallVolumes::iterator CapacityControl::FindByIp( const NetworkAddress &srcIp, const PString &calledStationId ) { unsigned netmaskLen = 0; PINDEX matchLen = P_MAX_INDEX; const IpCallVolumes::iterator ipEnd = m_ipCallVolumes.end(); IpCallVolumes::iterator bestIpMatch = ipEnd, i = m_ipCallVolumes.begin(); while (i != ipEnd) { if (bestIpMatch != ipEnd && i->first.GetNetmaskLen() < netmaskLen) break; if (i->first.IsAny() || srcIp << i->first) { PINDEX offset, len; if (i->second.m_prefix.empty()) len = 0; else if (!calledStationId.FindRegEx( PRegularExpression(i->second.m_prefix.c_str(), PRegularExpression::Extended), offset, len)) len = P_MAX_INDEX; if (len != P_MAX_INDEX && (matchLen == P_MAX_INDEX || len > matchLen)) { bestIpMatch = i; netmaskLen = i->first.GetNetmaskLen(); matchLen = len; } } ++i; } return bestIpMatch; } CapacityControl::H323IdCallVolumes::iterator CapacityControl::FindByH323Id( const PString &h323Id, const PString &calledStationId ) { PINDEX matchLen = P_MAX_INDEX; const H323IdCallVolumes::iterator h323IdEnd = m_h323IdCallVolumes.end(); H323IdCallVolumes::iterator bestH323IdMatch = h323IdEnd, i = m_h323IdCallVolumes.begin(); while (i != h323IdEnd) { if (h323Id.IsEmpty()) break; PString alias = H323GetAliasAddressString(i->first); if (bestH323IdMatch != h323IdEnd && alias != h323Id) break; if (alias == h323Id) { PINDEX offset, len; if (i->second.m_prefix.empty()) len = 0; else if (!calledStationId.FindRegEx(PRegularExpression(i->second.m_prefix.c_str(), PRegularExpression::Extended), offset, len)) len = P_MAX_INDEX; if (len != P_MAX_INDEX && (matchLen == P_MAX_INDEX || len > matchLen)) { bestH323IdMatch = i; matchLen = len; } } ++i; } return bestH323IdMatch; } CapacityControl::CLICallVolumes::iterator CapacityControl::FindByCli( const std::string &cli, const PString &calledStationId ) { PINDEX matchLen = P_MAX_INDEX; const CLICallVolumes::iterator cliEnd = m_cliCallVolumes.end(); CLICallVolumes::iterator bestCliMatch = cliEnd, i = m_cliCallVolumes.begin(); while (i != cliEnd) { if (cli.empty()) break; if (bestCliMatch != cliEnd && i->first != cli) break; if (i->first == cli) { PINDEX offset, len; if (i->second.m_prefix.empty()) len = 0; else if (!calledStationId.FindRegEx(PRegularExpression(i->second.m_prefix.c_str(), PRegularExpression::Extended), offset, len)) len = P_MAX_INDEX; if (len != P_MAX_INDEX && (matchLen == P_MAX_INDEX || len > matchLen)) { bestCliMatch = i; matchLen = len; } } ++i; } return bestCliMatch; } void CapacityControl::LogCall( const NetworkAddress &srcIp, const PString &srcAlias, const std::string &srcCli, const PString &calledStationId, PINDEX callNumber, bool callStart ) { if (callStart) { if (callNumber < 1) { PTRACE(1, "CAPCTRL\tInvalid call number used (" << callNumber << ")"); } // find longest matching rule by ip/h323id/cli IpCallVolumes::iterator bestIpMatch = FindByIp(srcIp, calledStationId); if (bestIpMatch != m_ipCallVolumes.end()) { PTRACE(5, "CAPCTRL\tCall #" << callNumber << " to " << calledStationId << " matched IP rule " << bestIpMatch->first.AsString() << "\t" << bestIpMatch->second.AsString() ); PWaitAndSignal lock(m_updateMutex); bestIpMatch->second.m_calls.insert(callNumber); return; } H323IdCallVolumes::iterator bestH323IdMatch = FindByH323Id(srcAlias, calledStationId); if (bestH323IdMatch != m_h323IdCallVolumes.end()) { PTRACE(5, "CAPCTRL\tCall #" << callNumber << " to " << calledStationId << " matched H323.ID rule " << H323GetAliasAddressString(bestH323IdMatch->first) << "\t" << bestH323IdMatch->second.AsString() ); PWaitAndSignal lock(m_updateMutex); bestH323IdMatch->second.m_calls.insert(callNumber); return; } CLICallVolumes::iterator bestCliMatch = FindByCli(srcCli, calledStationId); if (bestCliMatch != m_cliCallVolumes.end()) { PTRACE(5, "CAPCTRL\tCall #" << callNumber << " to " << calledStationId << " matched CLI rule " << bestCliMatch->first << "\t" << bestCliMatch->second.AsString() ); PWaitAndSignal lock(m_updateMutex); bestCliMatch->second.m_calls.insert(callNumber); return; } } else { // call stop PWaitAndSignal lock(m_updateMutex); // find the right counter by GnuGk call number IpCallVolumes::iterator bestIpMatch = m_ipCallVolumes.begin(); while (bestIpMatch != m_ipCallVolumes.end()) { std::set::iterator i = find(bestIpMatch->second.m_calls.begin(), bestIpMatch->second.m_calls.end(), callNumber ); if (i != bestIpMatch->second.m_calls.end()) { bestIpMatch->second.m_calls.erase(i); return; } ++bestIpMatch; } H323IdCallVolumes::iterator bestH323IdMatch = m_h323IdCallVolumes.begin(); while (bestH323IdMatch != m_h323IdCallVolumes.end()) { std::set::iterator i = find(bestH323IdMatch->second.m_calls.begin(), bestH323IdMatch->second.m_calls.end(), callNumber ); if (i != bestH323IdMatch->second.m_calls.end()) { bestH323IdMatch->second.m_calls.erase(i); return; } ++bestH323IdMatch; } CLICallVolumes::iterator bestCliMatch = m_cliCallVolumes.begin(); while (bestCliMatch != m_cliCallVolumes.end()) { std::set::iterator i = find(bestCliMatch->second.m_calls.begin(), bestCliMatch->second.m_calls.end(), callNumber ); if (i != bestCliMatch->second.m_calls.end()) { bestCliMatch->second.m_calls.erase(i); return; } ++bestCliMatch; } } } bool CapacityControl::CheckCall( const NetworkAddress &srcIp, const PString &srcAlias, const std::string &srcCli, const PString &calledStationId ) { IpCallVolumes::iterator bestIpMatch = FindByIp(srcIp, calledStationId); if (bestIpMatch != m_ipCallVolumes.end()) { PTRACE(5, "CAPCTRL\tCall from IP " << srcIp.AsString() << " to " << calledStationId << " matched IP rule " << bestIpMatch->first.AsString() << "\t" << bestIpMatch->second.AsString() ); PWaitAndSignal lock(m_updateMutex); return bestIpMatch->second.m_calls.size() < bestIpMatch->second.m_maxVolume; } H323IdCallVolumes::iterator bestH323IdMatch = FindByH323Id(srcAlias, calledStationId); if (bestH323IdMatch != m_h323IdCallVolumes.end()) { PTRACE(5, "CAPCTRL\tCall to " << calledStationId << " matched H323.ID rule " << H323GetAliasAddressString(bestH323IdMatch->first) << "\t" << bestH323IdMatch->second.AsString() ); PWaitAndSignal lock(m_updateMutex); return bestH323IdMatch->second.m_calls.size() < bestH323IdMatch->second.m_maxVolume; } CLICallVolumes::iterator bestCliMatch = FindByCli(srcCli, calledStationId); if (bestCliMatch != m_cliCallVolumes.end()) { PTRACE(5, "CAPCTRL\tCall to " << calledStationId << " matched CLI rule " << bestCliMatch->first << "\t" << bestCliMatch->second.AsString() ); PWaitAndSignal lock(m_updateMutex); return bestCliMatch->second.m_calls.size() < bestCliMatch->second.m_maxVolume; } return true; } namespace { class CapCtrlAcct : public GkAcctLogger { public: enum Constants { /// events recognized by this module CapCtrlAcctEvents = AcctStart | AcctStop }; /// Create a logger that updates information about inbound traffic CapCtrlAcct( /// name from Gatekeeper::Acct section const char* moduleName ); /** Log accounting event. @return Status of this logging operation (see #Status enum#) */ virtual Status Log( AcctEvent evt, /// accounting event to log const callptr& call /// additional data for the event ); private: /* No copy constructor allowed */ CapCtrlAcct(const CapCtrlAcct&); /* No operator= allowed */ CapCtrlAcct& operator=(const CapCtrlAcct&); private: CapacityControl *m_capacityControl; }; } // end of anonymous namespace CapCtrlAcct::CapCtrlAcct( const char* moduleName ) : GkAcctLogger(moduleName), m_capacityControl(CapacityControl::Instance()) { SetSupportedEvents(CapCtrlAcctEvents); } GkAcctLogger::Status CapCtrlAcct::Log( GkAcctLogger::AcctEvent evt, const callptr &call ) { if ((evt & GetEnabledEvents() & GetSupportedEvents()) == 0) return Next; if (!call) { PTRACE(1, "GKACCT\t" << GetName() << " - missing call info for event " << evt); return Fail; } PIPSocket::Address addr; WORD port; call->GetSrcSignalAddr(addr, port); PString h323Id = GetBestAliasAddressString( call->GetSourceAddress(), true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID) ); if (h323Id.IsEmpty() && call->GetCallingParty()) h323Id = GetBestAliasAddressString( call->GetCallingParty()->GetAliases(), true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID) ); std::string cli((const char*)(GetCallingStationId(call))); m_capacityControl->LogCall(addr, h323Id, cli, GetCalledStationId(call), call->GetCallNumber(), evt == AcctStart); return Ok; } namespace { /// Authenticator module for controlling inbound traffic /// To be used together with CapacityControl accounting module class CapCtrlAuth : public GkAuthenticator { public: enum SupportedChecks { CapCtrlAuthMiscChecks = e_Setup | e_SetupUnreg }; /// build authenticator reading settings from the config CapCtrlAuth( /// name for this authenticator and for the config section to read settings from const char* authName, /// RAS check events supported by this module unsigned supportedRasChecks = 0, /// Misc check events supported by this module unsigned supportedMiscChecks = CapCtrlAuthMiscChecks ); virtual ~CapCtrlAuth() {} /** Authenticate using data from Q.931 Setup message. @return: #GkAuthenticator::Status enum# with the result of authentication. */ virtual int Check( /// Q.931/H.225 Setup message to be authenticated SetupMsg &setup, /// authorization data (call duration limit, reject reason, ...) SetupAuthData& authData ); private: CapCtrlAuth(); CapCtrlAuth(const CapCtrlAuth&); CapCtrlAuth& operator=(const CapCtrlAuth&); private: CapacityControl *m_capacityControl; }; } // end of anonymous namespace CapCtrlAuth::CapCtrlAuth( const char* authName, unsigned supportedRasChecks, unsigned supportedMiscChecks ) : GkAuthenticator(authName, supportedRasChecks, supportedMiscChecks), m_capacityControl(CapacityControl::Instance()) { } int CapCtrlAuth::Check( /// Q.931/H.225 Setup message to be authenticated SetupMsg &setup, /// authorization data (call duration limit, reject reason, ...) SetupAuthData& authData ) { PString h323Id; PIPSocket::Address addr; setup.GetPeerAddr(addr); if (authData.m_call) { h323Id = GetBestAliasAddressString( authData.m_call->GetSourceAddress(), true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID) ); if (h323Id.IsEmpty() && authData.m_call->GetCallingParty()) h323Id = GetBestAliasAddressString( authData.m_call->GetCallingParty()->GetAliases(), true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID) ); } else if (setup.GetUUIEBody().HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) { h323Id = GetBestAliasAddressString( setup.GetUUIEBody().m_sourceAddress, true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID) ); } if (!m_capacityControl->CheckCall( addr, h323Id, (const char*)(authData.m_callingStationId), authData.m_calledStationId)) { authData.m_rejectCause = Q931::NoCircuitChannelAvailable; return e_fail; } else return e_ok; } namespace { GkAcctLoggerCreator CapCtrlAcctLoggerCreator("CapacityControl"); GkAuthCreator CapCtrlAuthCreator("CapacityControl"); } gnugk-3.4/PaxHeaders.16356/gk.h0000644000175000001440000000005012213370243014270 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gk.h0000644000175000001440000001024612213370243013234 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // gk.h gatekeeper process // // Copyright (c) 2000-2013, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef GK_H #define GK_H "@(#) $Id: gk.h,v 1.45 2013/09/09 16:00:35 willamowius Exp $" #include "config.h" #include "version.h" #ifdef COMPILE_AS_SERVICE #include #else #include #endif #ifdef _WIN32 #include #endif #ifdef COMPILE_AS_SERVICE #define GNUGK_NAME "GNU Gatekeeper" #else #define GNUGK_NAME "Gatekeeper" #endif extern PSemaphore ShutdownMutex; extern const char * KnownConfigEntries[][2]; // you must change PTLib 2.10.x configure.ac to set WINVER = 0x0600 to enable #define WINDOWS_VISTA 0x0600 #if defined(_WIN32) && (_WIN32_WINNT >= WINDOWS_VISTA) extern LPFN_WSASENDMSG g_pfWSASendMsg; #endif class GkTimer; #ifdef COMPILE_AS_SERVICE class Gatekeeper : public PServiceProcess { PCLASSINFO(Gatekeeper, PServiceProcess) #else class Gatekeeper : public PProcess { PCLASSINFO(Gatekeeper, PProcess) #endif public: Gatekeeper (const char * _manuf = "GNU", const char * _name = GNUGK_NAME, WORD _majorVersion = GNUGK_MAJOR_VERSION, WORD _minorVersion = GNUGK_MINOR_VERSION, CodeStatus _status = GNUGK_BUILD_TYPE, WORD _buildNumber = GNUGK_BUILD_NUMBER); virtual void Main(); #ifdef COMPILE_AS_SERVICE virtual PBoolean OnStart(); virtual void OnStop(); virtual void Terminate(); virtual PBoolean OnPause(); virtual void OnContinue(); virtual void OnControl(); #endif enum RotationIntervals { Hourly, Daily, Weekly, Monthly, RotationIntervalMax }; static bool SetLogFilename(const PString & filename); static bool RotateLogFile(); static bool ReopenLogFile(); static void CloseLogFile(); static void EnableLogFileRotation(bool enable = true); /** Rotate the log file, saving old file contents to a different file and starting with a new one. This is a callback function called when the rotation timer expires. */ static void RotateOnTimer( GkTimer * timer /// timer object that triggered rotation ); protected: /** returns the template string for which the cmommand line is parsed */ virtual const PString GetArgumentsParseString() const; /**@name Initialization * A sequence of virtual initialization methods is called from #Main# * before the fun starts. * Every method may return #FALSE# to abort #Main# and end the program. */ //@{ /** installs the signal handlers; First called init method. */ virtual bool InitHandlers(const PArgList & args); virtual bool InitConfig(const PArgList & args); /** initiates logging and tracing; Called after #InitConfig# */ virtual bool InitLogging(const PArgList & args); /** print the available command-line-options **/ void PrintOpts(); /** Set a new user and group (ownership) for the GK process. The group that will be set is the user's default group. */ virtual bool SetUserAndGroup(const PString & username); //@} private: /// parse rotation interval from the config static void GetRotateInterval( PConfig & cfg, /// the config const PString & section /// name of the config section to check ); private: /// rotate file after the specified period of time (if >= 0) static int m_rotateInterval; /// a minute when the interval based rotation should occur static int m_rotateMinute; /// an hour when the interval based rotation should occur static int m_rotateHour; /// day of the month (or of the week) for the interval based rotation static int m_rotateDay; /// timer for rotation events static GkTimer* m_rotateTimer; /// gatekeeper log file static PTextFile* m_logFile; /// filename for the logfile static PFilePath m_logFilename; /// atomic log file operations (rotation, closing) static PMutex m_logFileMutex; /// human readable names for rotation intervals static const char* const m_intervalNames[]; #ifdef COMPILE_AS_SERVICE PString savedArguments; #endif }; #endif // GK_H gnugk-3.4/PaxHeaders.16356/clirw.h0000644000175000001440000000005012131234117015005 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/clirw.h0000644000175000001440000001060512131234117013750 0ustar00janusers00000000000000/* * clirw.h * * Module for CLI/ANI manipulation. * * Copyright (c) 2005, Michal Zygmuntowicz * Copyright (c) 2007-2010, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #ifndef CLIRW_H #define CLIRW_H "#(@) $Id: clirw.h,v 1.8 2013/04/10 10:00:47 willamowius Exp $" #include #include #include "Toolkit.h" struct SetupAuthData; class SignalingMsg; template class H225SignalingMsg; class H225_Setup_UUIE; typedef H225SignalingMsg SetupMsg; class GkSQLConnection; /// Perform Calling-Party-Number-IE/Setup-UUIE.sourceAddress rewritting class CLIRewrite { public: /// a single CLI/ANI rewrite rule struct RewriteRule { RewriteRule(); PString AsString() const; /// match condition to apply the rule enum MatchType { MatchDialedNumber, /// dialed number before any rewrite MatchDestinationNumber, /// dailed number after global rewrite MatchCallerNumber /// CLI/ANI }; /// how to perform number matching and rewritting enum RewriteType { PrefixToNumber, /// match by a prefix, replace with a complete number PrefixToPrefix, /// match by a prefix, replace only the prefix part NumberToNumber, /// match by a complete number, replace with a complete number PrefixToH323Id, /// match by a prefix, replace H.323 ID only with a complete number NumberToH323Id /// match by a complete number, replace H.323 ID only with a complete number }; /// how to hide caller's number enum ScreeningType { NoScreening, /// leave as it is HideFromTerminals, /// hide only if a callee is a terminal AlwaysHide /// always hide }; /// manual CLIR (presentatio indicator) control enum CLIRType { CLIRPassthrough, /// leave PI as received from a caller RestrictPresentation, /// set PI to restricted AllowPresentation /// set PI to allowed }; /// how to process received CLIR (PI) information enum CLIRRule { IgnoreCLIR, /// use the global settings to make the decission ForwardCLIR, /// do nothing, just forward as received ApplyCLIRForTerminals, /// hide caller's number, if the callee is a terminal and PI=restricted AlwaysApplyCLIR /// always hide caller's number, if PI=restricted }; int m_matchType; /// match condition int m_rewriteType; /// number matching/rewritting rule int m_screeningType; /// caller's number hiding int m_manualCLIR; /// CLIR settings override int m_CLIRPolicy; /// how to process CLIR std::string m_prefix; /// the prefix to match std::vector m_cli; /// list of new CLIs }; typedef std::vector RewriteRules; typedef std::pair SingleIpRule; typedef std::vector SingleIpRules; typedef std::pair DoubleIpRule; typedef std::vector DoubleIpRules; CLIRewrite(); /// Rewrite CLI before any Setup message processing, like auth & routing void InRewrite( SetupMsg &msg /// Q.931 Setup message to be rewritten ); /** Rewrite CLI before the Setup is sent to the terminating party and after auth/acct/routing is performed. */ void OutRewrite( SetupMsg &msg, /// Q.931 Setup message to be rewritten SetupAuthData &authData, /// additional data const PIPSocket::Address &destAddr /// destination address ); protected: void Rewrite( SetupMsg &msg, /// Q.931 Setup message to be rewritten const SingleIpRule &ipRule, /// rule to use for rewrite bool inbound, /// rule type SetupAuthData *authData /// additional data for outbound rules ) const; // process inbound or outbound SQL queries and return a rule SingleIpRule * RunQuery(const PString & query, const SetupMsg & msg); CLIRewrite(const CLIRewrite &); CLIRewrite & operator=(const CLIRewrite &); private: SingleIpRules m_inboundRules; /// a set of inbound CLI/ANI rewrite rules DoubleIpRules m_outboundRules; /// a set of outbound CLI/ANI rewrite rules bool m_processSourceAddress; /// true to rewrite numbers in sourceAddress Setup-UUIE bool m_removeH323Id; /// true to put in the sourceAddress Setup-UUIE field only rewritten ANI/CLI int m_CLIRPolicy; /// how to process CLIR // RewriteCLI::SQL parameters GkSQLConnection * m_sqlConn; PString m_inboundQuery; PString m_outboundQuery; }; #endif gnugk-3.4/PaxHeaders.16356/gk_const.h0000644000175000001440000000005012177027270015506 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gk_const.h0000644000175000001440000001001512177027270014444 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // gk_const.h constants for gatekeeper ports etc. // // Copyright (c) 2000-2010, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef GK_CONST_H #define GK_CONST_H "@(#) $Id: gk_const.h,v 1.32 2013/08/02 22:12:08 willamowius Exp $" #include "config.h" /* all values can be set in the config file, section [Gatekeeper::] * these are just the defaults */ #define GK_DEF_UNICAST_RAS_PORT 1719 #define GK_DEF_MULTICAST_PORT 1718 #define GK_DEF_MULTICAST_GROUP "224.0.1.41" /* port used by gatekeeper for routed signaling: anything != 1720 so endpoint can be on same IP as GK */ #define GK_DEF_CALL_SIGNAL_PORT 1721 #define GK_DEF_TLS_CALL_SIGNAL_PORT 1300 /* well known signal port */ #define GK_DEF_ENDPOINT_SIGNAL_PORT 1720 #define GK_DEF_STATUS_PORT 7000 #define GK_DEF_MULTIPLEX_RTP_PORT 3000 #define GK_DEF_MULTIPLEX_RTCP_PORT 3001 #define GK_DEF_LISTEN_QUEUE_LENGTH 1024 extern const char *H225_ProtocolID; extern const char *H245_ProtocolID; //+++++++++++++++++++++++++++++++++++++++++++++ // GnuGk OID #define GnuGkOID "1.3.6.1.4.1.27938" //+++++++++++++++++++++++++++++++++++++++++++++ // ITU H.460 Standards /////////////////////////////////////////// // H.460.18/.19 #ifdef HAS_H46018 static const char * const H46018_OID = "0.0.8.460.18.0.1"; static const char * const H46019_OID = "0.0.8.460.19.0.1"; #endif /////////////////////////////////////////// // H.460.23 #define Std23_RemoteNAT 1 // bool if endpoint has remote NAT support #define Std23_AnnexA 2 // bool Support Same NAT probing (Annex A) #define Std23_IsNAT 3 // bool if endpoint is NATed #define Std23_DetRASAddr 4 // Detected RAS H225_TransportAddress #define Std23_STUNAddr 5 // transport IP address of STUN Server to test with #define Std23_NATdet 6 // integer 8 Endpoint STUN detected NAT Type #define Std23_AnnexB 7 // bool Support Proxy offload (Annex B) ////////////////////////////////////////// // H.460.24 #define Std24_ProxyNAT 1 // bool Proxy for NAT support #define Std24_RemoteNAT 2 // bool if endpoint has remote NAT support #define Std24_MustProxy 3 // bool Media must proxy #define Std24_IsNAT 4 // bool if endpoint is NATed #define Std24_NATdet 5 // integer 8 Endpoint STUN detected NAT Type #define Std24_SourceAddr 6 // transport Apparent public IP of remote #define Std24_AnnexA 7 // bool Support Same NAT probing (Annex A) #define Std24_NATInstruct 8 // integer 8 Instruction on how NAT is to be Traversed #define Std24_AnnexB 9 // bool Support Proxy offload (Annex B) #define GK_DEF_STUN_PORT 3478 #ifdef HAS_H46024A static const char * const H46024A_OID = "0.0.8.460.24.1"; #endif #ifdef HAS_H46024B static const char * const H46024B_OID = "0.0.8.460.24.2"; #endif //+++++++++++++++++++++++++++++++++++++++++++++ // Packetizer OID /////////////////////////////////////////// // Presence #define OID3 "1.3.6.1.4.1.17090.0.12" // Presence v2+ #define OID3_PDU 1 // PASN_OctetString Presence PDU ////////////////////////////////////////// // Registration Priority and Pre-Emption #define OID6 "1.3.6.1.4.1.17090.0.6" // Registration priority & pre-emption #define OID6_Priority 1 // integer 8 Priority number highest priority gets registration #define OID6_Preempt 2 // bool to instruct GK to preempt previous registration #define OID6_PriNot 3 // bool to notify EP registration RRJ (priority) UCF (higher Priority) #define OID6_PreNot 4 // bool to notify EP registration RRJ (can preempt) UCF (was preempted) /////////////////////////////////////////// // Remote Vendor Information #define OID9 "1.3.6.1.4.1.17090.0.9" // Remote Vendor Information #define VendorProdOID 1 // PASN_String of productID #define VendorVerOID 2 // PASN_String of versionID #endif gnugk-3.4/PaxHeaders.16356/Toolkit.cxx0000644000175000001440000000005012213637633015700 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/Toolkit.cxx0000644000175000001440000034533112213637633014652 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // Toolkit base class for the GnuGk // // Copyright (c) 2000-2013, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include "stl_supp.h" #include "gktimer.h" #include "h323util.h" #include "GkStatus.h" #include "gkconfig.h" #include "config.h" #if HAS_DATABASE #include "gksql.h" #endif #include "clirw.h" #include "capctrl.h" #include "RasSrv.h" #include "Toolkit.h" #include "gk_const.h" #include "SoftPBX.h" #include "snmp.h" #ifdef H323_H350 const char * H350Section = "GkH350::Settings"; #include #include "h350/h350.h" #endif using namespace std; PIPSocket::Address GNUGK_INADDR_ANY(INADDR_ANY); PReadWriteMutex ConfigReloadMutex; PSemaphore ShutdownMutex(1, 1); extern const char *ProxySection; extern const char *RoutedSec; extern const char *TLSSec; extern int g_maxSocketQueue; 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 if (addr.GetSize() == nm.GetSize()) { 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); } else { PTRACE(1, "Error: Non-matching network and netmask"); } } 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 m_address.IsAny() && (GetNetmaskLen() == 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 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) { if (net.Find('-') != P_MAX_INDEX) { // format: net/mask-dest eg. 10.0.0.0/8-20.1.1.1 destination = net.Tokenise("-", FALSE)[1]; GetNetworkFromString(net.Tokenise("-", FALSE)[0], network, net_mask); } else { // format: net/mask-dest eg. 10.1.1.1/8 destination = net.Tokenise("/", FALSE)[0]; GetNetworkFromString(net, network, net_mask); } } Toolkit::RouteTable::RouteEntry::RouteEntry( const PIPSocket::RouteEntry & re, const InterfaceTable & it ) : PIPSocket::RouteEntry(re) { // look at the interface table which local IP to use for this route entry PINDEX i; for (i = 0; i < it.GetSize(); ++i) { const Address & ip = it[i].GetAddress(); if (Toolkit::Instance()->IsGKHome(ip) && Compare(&ip)) { // skip IPs we don't listen to destination = ip; return; } } for (i = 0; i < it.GetSize(); ++i) { if ((it[i].GetName() == interfaceName) && Toolkit::Instance()->IsGKHome(it[i].GetAddress())) { destination = it[i].GetAddress(); return; } } } inline bool Toolkit::RouteTable::RouteEntry::Compare(const Address *ip) const { return (*ip == destination) || (((*ip & net_mask) == network) && (ip->GetVersion() == network.GetVersion())); } // class Toolkit::RouteTable void Toolkit::RouteTable::InitTable() { // workaround for OS that don't support GetRouteTable PIPSocket::GetHostAddress(defAddr); #ifdef hasIPV6 PIPSocket::GetHostAddress(defAddrV6); #endif ClearTable(); if (!CreateTable()) return; // get Home IPs (all detected IPs or set through config file) std::vector home; Toolkit::Instance()->GetGKHome(home); // if we only have 1 Home IP, then thats also the default IP if (home.size() == 1) { defAddr = home[0]; #ifdef hasIPV6 defAddrV6 = home[0]; #endif } // Bind= always sets the default IP PString bind = GkConfig()->GetString("Bind", ""); if (!bind.IsEmpty()) { defAddr = bind; #ifdef hasIPV6 defAddrV6 = bind; #endif } // if we do not already have a valid entry, try and retrieve the default interface if ((defAddr.GetVersion() != 4) || defAddr.IsLoopback() || !defAddr.IsValid()) { // Set default IP according to route table defAddr = GetDefaultIP(4); if ((defAddr.GetVersion() != 4) || defAddr.IsLoopback() || !defAddr.IsValid()) { // no default gateway, use first interface as default PIPSocket::GetNetworkInterface(defAddr); } } #ifdef hasIPV6 // if we do not already have a valid entry, try and retrieve the default interface if ((defAddrV6.GetVersion() != 6) || defAddrV6.IsLoopback() || !defAddrV6.IsValid()) { // Set default IP according to route table defAddrV6 = GetDefaultIP(6); if ((defAddrV6.GetVersion() != 6) || defAddrV6.IsLoopback() || !defAddrV6.IsValid()) { // no default gateway, use first interface as default PIPSocket::GetNetworkInterface(defAddrV6); } } #endif // if we have a list of Home IPs and the default address is not in it, use the first IPv4 Home IP, // unless the default IP was explicitely specified in Bind= if (bind.IsEmpty() && !home.empty() && (find(home.begin(), home.end(), defAddr) == home.end())) { for (unsigned i=0; i < home.size(); ++i) { if (home[i].GetVersion() == 4) { defAddr = home[i]; break; } } } #ifdef hasIPV6 if (bind.IsEmpty() && !home.empty() && (find(home.begin(), home.end(), defAddrV6) == home.end())) { for (unsigned i=0; i < home.size(); ++i) { if (home[i].GetVersion() == 6) { defAddrV6 = home[i]; break; } } } #endif for (RouteEntry *entry = rtable_begin; entry != rtable_end; ++entry) { PTRACE(2, "Network=" << NetworkAddress(entry->GetNetwork(), entry->GetNetMask()).AsString() << ", IP=" << entry->GetDestination()); } #ifdef hasIPV6 if (Toolkit::Instance()->IsIPv6Enabled()) PTRACE(2, "Default IP IPv4=" << defAddr << " IPv6=" << defAddrV6); else #endif { PTRACE(2, "Default IP=" << defAddr); } if (defAddr.IsLoopback()) { PTRACE(1, "WARNING: Your default IP=" << defAddr << " is a loopback address. That probably won't work!"); } } // get default route from route table, because GetGatewayAddress() is broken until PTLib 2.11.1 PIPSocket::Address Toolkit::RouteTable::GetDefaultIP(unsigned version) const { for (RouteEntry *entry = rtable_begin; entry != rtable_end; ++entry) { if ((entry->GetNetMask() == 0) && (entry->GetDestination().GetVersion() == version)) return GetLocalAddress(entry->GetDestination()); } return Address(0); } void Toolkit::RouteTable::ClearTable() { if (rtable_begin) { for (RouteEntry *r = rtable_begin; r != rtable_end; ++r) r->~RouteEntry(); ::free(rtable_begin); rtable_begin = 0; } } void Toolkit::RouteTable::AddInternalNetwork(const NetworkAddress & network) { if (find(m_internalnetworks.begin(), m_internalnetworks.end(), network) == m_internalnetworks.end()) m_internalnetworks.push_back(network); } PIPSocket::Address Toolkit::RouteTable::GetLocalAddress(unsigned version) const { #ifdef hasIPV6 if (version == 6) return defAddrV6; #endif return defAddr; } PIPSocket::Address Toolkit::RouteTable::GetLocalAddress(const Address & addr) const { // look through internal networks and make sure we don't return the external IP for them for (unsigned j = 0; j < m_internalnetworks.size(); ++j) { if (addr << m_internalnetworks[j]) { // check if internal network is in route table, but don't use the default route RouteEntry *entry = find_if(rtable_begin, rtable_end, bind2nd(mem_fun_ref(&RouteEntry::Compare), &addr)); if ((entry != rtable_end) && (entry->GetNetMask() != INADDR_ANY) #ifdef hasIPV6 && (entry->GetNetMask() != in6addr_any) #endif ) { return entry->GetDestination(); } else { #ifdef hasIPV6 if (addr.GetVersion() == 6) return defAddrV6; #endif return defAddr; } } } // if external IP is configured if (!ExtIP.IsEmpty()) { if (DynExtIP) { // if dynamic resolve DNS entry PIPSocket::Address extip; H323TransportAddress ex = H323TransportAddress(ExtIP); ex.GetIpAddress(extip); if (extip.IsValid()) { return extip; } else { PTRACE(2, "NAT\tERROR: External IP " << ExtIP << " unresolvable." ); SNMP_TRAP(10, SNMPError, Configuration, "External IP " + ExtIP + " unresolvable"); } } else { // If valid IP then use the ExtIP value PIPSocket::Address extip(ExtIP); if (extip.IsValid()) { return extip; } else { PTRACE(2, "NAT\tERROR: ExtIP " << ExtIP << " unuseable." ); SNMP_TRAP(10, SNMPError, Configuration, "External IP " + ExtIP + " unusable"); } } } RouteEntry *entry = find_if(rtable_begin, rtable_end, bind2nd(mem_fun_ref(&RouteEntry::Compare), &addr)); if (entry != rtable_end) { return entry->GetDestination(); } #ifdef hasIPV6 if (addr.GetVersion() == 6) { return defAddrV6; } #endif return defAddr; } bool Toolkit::RouteTable::CreateRouteTable(const PString & extroute) { InterfaceTable if_table; if (!PIPSocket::GetInterfaceTable(if_table)) { PTRACE(1, "Error: Can't get interface table"); SNMP_TRAP(10, SNMPError, Configuration, "Error fetching 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"); SNMP_TRAP(10, SNMPError, Configuration, "Error fetching route table"); return false; } // filter out route with destination localhost // we can't use those for routing calls, unless the net itself is localhost for(PINDEX i=0; i < r_table.GetSize(); ++i) { if ((r_table[i].GetDestination().IsLoopback()) && !r_table[i].GetNetwork().IsLoopback()) { r_table.RemoveAt(i--); } } // filter out IPv6 networks if IPv6 is not enabled for(PINDEX i=0; i < r_table.GetSize(); ++i) { if ((r_table[i].GetNetwork().GetVersion() == 6) && !Toolkit::Instance()->IsIPv6Enabled()) { r_table.RemoveAt(i--); } } if (AsBool(GkConfig()->GetString(ProxySection, "Enable", "0"))) { for (PINDEX i = 0; i < r_table.GetSize(); ++i) { if (r_table[i].GetNetwork().IsRFC1918() && (r_table[i].GetNetMask().AsString() != "255.255.255.255") && (r_table[i].GetNetMask().AsString() != "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")) { m_internalnetworks.resize( m_internalnetworks.size() + 1); m_internalnetworks[m_internalnetworks.size() - 1] = NetworkAddress(r_table[i].GetNetwork(), r_table[i].GetNetMask()); PTRACE(2, "Internal Network Detected " << m_internalnetworks.back().AsString()); } } } PString tmpRoutes = GkConfig()->GetString(ProxySection, "ExplicitRoutes", ""); PStringArray explicitRoutes; if (!tmpRoutes.IsEmpty()) { explicitRoutes = tmpRoutes.Tokenise(",", false); PString defaultRoute; PINDEX e = 0; while (e < explicitRoutes.GetSize()) { PString explicitRoute = explicitRoutes[e].Trim(); if (explicitRoute.Left(9) == "0.0.0.0/0" || PCaselessString(explicitRoute.Left(7)) == "default") { explicitRoutes.RemoveAt(e); defaultRoute = explicitRoute; } else { RouteEntry entry(explicitRoute); if (Toolkit::Instance()->IsGKHome(entry.GetDestination())) { PTRACE(2, "Adding explict route: " << entry.GetNetwork() << "/" << entry.GetNetMask() << "->" << entry.GetDestination()); e++; } else { PTRACE(1, "Ignoring explict route (invalid source IP): " << entry.GetNetwork() << "/" << entry.GetNetMask() << "->" << entry.GetDestination()); explicitRoutes.RemoveAt(e); } } } if (!defaultRoute.IsEmpty()) { // replace 0.0.0.0/0 or "default" with 2 routes (0.0.0.0/1 + 128.0.0.0/1), because "0.0.0.0/0" is also treated as invalid network if (PCaselessString(defaultRoute.Left(7)) == "default") { defaultRoute = PString("0.0.0.0/0") + defaultRoute.Mid(7); } defaultRoute.Replace("0.0.0.0/0", "0.0.0.0/1"); // 1st part // check if source is a Home= IP RouteEntry entry(defaultRoute); if (Toolkit::Instance()->IsGKHome(entry.GetDestination())) { explicitRoutes.AppendString(defaultRoute); defaultRoute.Replace("0.0.0.0/1", "128.0.0.0/1"); // 2nd part explicitRoutes.AppendString(defaultRoute); } else { PTRACE(1, "Ignoring explicit default route: Invalid source IP " << entry.GetDestination()); } } } int i = (!extroute) ? r_table.GetSize()+1 : r_table.GetSize(); i += explicitRoutes.GetSize(); rtable_end = rtable_begin = static_cast(::malloc(i * sizeof(RouteEntry))); // prepend explicit routes for (PINDEX e = 0; e < explicitRoutes.GetSize(); ++e) { ::new (rtable_end++) RouteEntry(explicitRoutes[e]); } for (PINDEX r = 0; r < (i - explicitRoutes.GetSize()); ++r) { if (!extroute && (r==r_table.GetSize())) { ::new (rtable_end++) RouteEntry(extroute); } else { PIPSocket::RouteEntry & r_entry = r_table[r]; if (!r_entry.GetNetMask().IsAny()) { ::new (rtable_end++) RouteEntry(r_entry, if_table); } } } return true; } bool Toolkit::VirtualRouteTable::CreateTable() { PString nets = GkConfig()->GetString("NetworkInterfaces", ""); if (!nets) { 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]); } } } // If we have an external IP setting then load the detected Route Table and add a route for the external IP // If dynamic IP then only store the PString value and resolve the DNS when required. PString extip = GkConfig()->GetString("ExternalIP", ""); DynExtIP = AsBool(GkConfig()->GetString("ExternalIsDynamic", "0")); PIPSocket::Address ext((DWORD)0); H323TransportAddress ex = H323TransportAddress(extip); ex.GetIpAddress(ext); if (ext.IsValid()) { ExtIP = extip; PString extroute; if (!DynExtIP) extroute = ext.AsString() + "/0"; CreateRouteTable(extroute); PTRACE(1, "External IP=" << ExtIP << " dynamic=" << DynExtIP); return true; } else DynExtIP = false; return false; } bool Toolkit::VirtualRouteTable::IsMasquerade(PIPSocket::Address & addr) const { if (!ExtIP) { H323TransportAddress ex = H323TransportAddress(ExtIP); ex.GetIpAddress(addr); return true; } return false; } // class Toolkit::ProxyCriterion Toolkit::ProxyCriterion::ProxyCriterion() : m_enable(false) { } Toolkit::ProxyCriterion::~ProxyCriterion() { } void Toolkit::ProxyCriterion::LoadConfig(PConfig *config) { // read switch for default proxy mode m_enable = AsBool(config->GetString(ProxySection, "Enable", "0")); if (!m_enable) { PTRACE(2, "GK\tH.323 Proxy not enabled by default"); } else { PTRACE(2, "GK\tH.323 Proxy enabled"); } m_internalnetworks.clear(); m_modeselection.clear(); PStringArray networks(config->GetString(ProxySection, "InternalNetwork", "").Tokenise(" ,;\t", FALSE)); // if no networks specified then use the detected values int signalRoutingMode = CallRec::Undefined; if (RasServer::Instance()->IsGKRouted()) { if (RasServer::Instance()->IsH245Routed()) { signalRoutingMode = CallRec::H245Routed; } else { signalRoutingMode = CallRec::SignalRouted; } } NetworkModes internal_netmode; internal_netmode.fromExternal = m_enable ? CallRec::Proxied : signalRoutingMode; internal_netmode.insideNetwork = signalRoutingMode; if (networks.GetSize() == 0) { m_internalnetworks = Toolkit::Instance()->GetInternalNetworks(); for (unsigned j = 0; j < m_internalnetworks.size(); ++j) { m_modeselection[m_internalnetworks[j]] = internal_netmode; PTRACE(2, "GK\tInternal Network " << j << " = " << m_internalnetworks[j].AsString() << " (" << internal_netmode.fromExternal << "," << internal_netmode.insideNetwork << ")"); } } else { for (PINDEX i = 0; i < networks.GetSize(); ++i) { m_internalnetworks.push_back(networks[i]); Toolkit::Instance()->GetRouteTable()->AddInternalNetwork(networks[i]); m_modeselection[networks[i]] = internal_netmode; PTRACE(2, "GK\tINI Internal Network " << i << " = " << m_internalnetworks[i].AsString() << " (" << internal_netmode.fromExternal << "," << internal_netmode.insideNetwork << ")"); } } // read [ModeSelection] section PStringToString mode_rules(config->GetAllKeyValues("ModeSelection")); if (mode_rules.GetSize() > 0) { // if we have ModeSelection rules, only use those, don't try to merge them with detected m_modeselection.clear(); } for (PINDEX i = 0; i < mode_rules.GetSize(); ++i) { PString network = mode_rules.GetKeyAt(i); if (!network.IsEmpty()) { PStringArray modes((mode_rules.GetDataAt(i)).Tokenise(" ,;\t", FALSE)); if (modes.GetSize() >= 1 && modes.GetSize() <= 2) { NetworkAddress addr = NetworkAddress(network); NetworkModes netmode; netmode.fromExternal = ToRoutingMode(modes[0].Trim()); netmode.insideNetwork = netmode.fromExternal; if (modes.GetSize() == 2) netmode.insideNetwork = ToRoutingMode(modes[1].Trim()); // replace 0.0.0.0/0 with 2 rules (0.0.0.0/1 + 128.0.0.0/1), because 0.0.0.0/0 is also treated as invalid network if (network == "0.0.0.0/0" || network == PCaselessString("default")) { m_modeselection[NetworkAddress("0.0.0.0/1")] = netmode; PTRACE(2, "GK\tModeSelection rule: 0.0.0.0/1=" << netmode.fromExternal << "," << netmode.insideNetwork); m_modeselection[NetworkAddress("128.0.0.0/1")] = netmode; PTRACE(2, "GK\tModeSelection rule: 128.0.0.0/1=" << netmode.fromExternal << "," << netmode.insideNetwork); } else { m_modeselection[addr] = netmode; PTRACE(2, "GK\tModeSelection rule: " << addr.AsString() << "=" << netmode.fromExternal << "," << netmode.insideNetwork); } } else { PTRACE(1, "GK\tInvalid ModeSelection rule: " << mode_rules.GetKeyAt(i) << "=" << mode_rules.GetDataAt(i) ); } } } } int Toolkit::ProxyCriterion::ToRoutingMode(const PCaselessString & mode) const { if (mode == "Routed") return CallRec::SignalRouted; else if (mode == "H245Routed") return CallRec::H245Routed; else if (mode == "Proxy") return CallRec::Proxied; else return CallRec::Undefined; } // returns network for the rule or IsAny() when no is rule found NetworkAddress Toolkit::ProxyCriterion::FindModeRule(const NetworkAddress & ip) const { NetworkAddress bestmatch; std::map::const_iterator iter = m_modeselection.begin(); while (iter != m_modeselection.end()) { if ((ip << iter->first) && (iter->first.GetNetmaskLen() >= bestmatch.GetNetmaskLen())) { bestmatch = iter->first; } ++iter; } return bestmatch; } int Toolkit::ProxyCriterion::SelectRoutingMode(const Address & ip1, const Address & ip2) const { // default mode int mode = m_enable ? CallRec::Proxied : CallRec::SignalRouted; if (mode == CallRec::SignalRouted && RasServer::Instance()->IsH245Routed()) mode = CallRec::H245Routed; PTRACE(5, "ModeSelection for " << ip1.AsString() << " -> " << ip2.AsString() << " default=" << mode); // check if we have a more specific setting // TODO: add invalid state to NetworkAddress so we can have ModeSelection rules for 0.0.0.0/0 that set a global default NetworkAddress bestMatchIP1 = FindModeRule(ip1); NetworkAddress bestMatchIP2 = FindModeRule(ip2); std::map::const_iterator iter; // check for same network if (!bestMatchIP1.IsAny() && !bestMatchIP2.IsAny()) { // rules for both IPs if (bestMatchIP1.Compare(bestMatchIP2) == 0) { // both on same network iter = m_modeselection.find(bestMatchIP1); if (iter != m_modeselection.end()) { mode = iter->second.insideNetwork; PTRACE(5, "ModeSelection: Both IPs on same network: mode=" << mode); } } else { // on different networks, use maximum poxying iter = m_modeselection.find(bestMatchIP1); int mode1 = iter->second.fromExternal; // no check, must exist iter = m_modeselection.find(bestMatchIP2); int mode2 = iter->second.fromExternal; // no check, must exist mode = max(mode1, mode2); PTRACE(5, "ModeSelection: Both IPs on different networks: mode1=" << mode1 << " mode2=" << mode2 << " => " << mode); } } else { // only one rule, use that if (!bestMatchIP1.IsAny()) { iter = m_modeselection.find(bestMatchIP1); if (iter != m_modeselection.end()) { mode = iter->second.fromExternal; PTRACE(5, "ModeSelection: Only rule for IP 1 = " << ip1.AsString() << " mode=" << mode); } } if (!bestMatchIP2.IsAny()) { iter = m_modeselection.find(bestMatchIP2); if (iter != m_modeselection.end()) { mode = iter->second.fromExternal; PTRACE(5, "ModeSelection: Only rule for IP 2 = " << ip2.AsString() << " mode=" << mode); } } } return mode; } int Toolkit::ProxyCriterion::IsInternal(const Address & ip) const { // Return the network Id. Addresses may be on different internal networks int retval = 0; std::vector::const_iterator i = m_internalnetworks.begin(); while (i != m_internalnetworks.end()) { retval++; if (ip << *i++) return retval; } return 0; } // class Toolkit::RewriteTool static const char *RewriteSection = "RasSrv::RewriteE164"; static const char *AliasRewriteSection = "RasSrv::RewriteAlias"; static const char *AssignedAliasSection = "RasSrv::AssignedAlias"; #ifdef h323v6 static const char *AssignedGatekeeperSection = "RasSrv::AssignedGatekeeper"; #endif void Toolkit::RewriteData::AddSection(PConfig * config, const PString & section) { PStringToString cfgs(config->GetAllKeyValues(section)); PINDEX n_size = cfgs.GetSize(); if (n_size > 0) { std::map rules; for (PINDEX i = 0; i < n_size; ++i) { PString key = cfgs.GetKeyAt(i); PCaselessString first = PCaselessString(key[0]); if (!key && (isdigit(static_cast(key[0])) || (first.FindOneOf("+!.%*#ABCDEFGHIGKLMNOPQRSTUVWXYZ") != P_MAX_INDEX))) rules[key] = cfgs.GetDataAt(i); } // now the rules are ascendantly sorted by the keys if ((n_size = rules.size()) > 0) { // Add any existing rules to be resorted if (m_size > 0) { for (PINDEX j = 0; j < m_size; ++j) { rules[Key(j)] = Value(j); } } m_size = m_size + n_size; // 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(PConfig *config, const PString & section) { m_RewriteKey = NULL; m_RewriteValue = NULL; m_size = 0; AddSection(config, section); } Toolkit::RewriteData::~RewriteData() { 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]; PString defDomain = config->GetString("Gatekeeper::Main", "DefaultDomain", ""); m_defaultDomain = defDomain.Tokenise(","); delete m_Rewrite; m_Rewrite = new RewriteData(config, RewriteSection); m_Rewrite->AddSection(config,AliasRewriteSection); } bool Toolkit::RewriteTool::RewritePString(PString & s) const { bool changed = false; // If URL remove the domain if default domain PINDEX at = s.Find('@'); if (at != P_MAX_INDEX) { PString num = s.Left(at); if (num.Left(5) *= "h323:") num = num.Mid(5); PString domain = s.Mid(at+1); PIPSocket::Address domIP(domain); // Check if we have a default domain and strip it for (PINDEX i=0; i < m_defaultDomain.GetSize(); i++) { if (domain == m_defaultDomain[i]) { PTRACE(2, "\tRewriteDomain: " << s << " to " << num); s = num; changed = true; break; } } // Check that the domain is not a local IP address. if (!changed && domIP.IsValid() && Toolkit::Instance()->IsGKHome(domIP)) { PTRACE(2, "\tRemoveDomain: " << domain << " to " << num); s = num; changed = true; } } // 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); if (prefix == s){ s = m_Rewrite->Value(i); return true; } 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) { PString unused; result = RewriteString(s, prefix, newprefix, unused); } 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(const PString & gw, bool direction, PString & data, callptr call) { // First lookup the GW in the dictionary GWRewriteEntry * 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(); PString key, value; for (; rule_iterator != end_iterator; ++rule_iterator) { key = (*rule_iterator).first; bool postdialmatch = false; if (key.Find("I") != P_MAX_INDEX) { postdialmatch = true; key.Replace("I", ".", true); } const int len = MatchPrefix(data, key); if (len > 0 || (len == 0 && key[0] == '!')) { // Start rewrite value = (*rule_iterator).second; PString postdialdigits; if (postdialmatch) { value.Replace("P", ".", true); } if (len > 0) { value = RewriteString(data, key, value, postdialdigits, postdialmatch); if (call && postdialmatch && !postdialdigits.IsEmpty()) { call->SetPostDialDigits(postdialdigits); } } else value = value + data; // Log PTRACE(2, "\tGWRewriteTool::RewritePString: " << data << " to " << value << " post dial digits=" << postdialdigits); // 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; } if (PTrace::CanTrace(3) && (m_GWRewrite.GetSize() < 100)) { 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) { 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 (PINDEX i = 0; i < m_GWRewrite.GetSize(); ++i) { delete &(m_GWRewrite.GetDataAt(i)); } m_GWRewrite.RemoveAll(); PINDEX gw_size = cfgs.GetSize(); if (gw_size > 0) { for (PINDEX i = 0; i < gw_size; ++i) { // Get the config keys PString key = cfgs.GetKeyAt(i); PString 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 PStringArray lines = cfg_value.Tokenise(PString(";")); PINDEX lines_size = lines.GetSize(); for (PINDEX j = 0; j < lines_size; ++j) { // Split the config line into three strings, direction, from string, to string PStringArray 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]); SNMP_TRAP(7, SNMPError, Configuration, "Invalid [GWRewriteE164] configuration"); 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]); SNMP_TRAP(7, SNMPError, Configuration, "Invalid [GWRewriteE164] configuration"); } } // 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 GWRewriteEntry * 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), m_causeCodeTranslationActive(false) { srand((unsigned int)time(0)); #ifdef HAS_TLS m_sslCtx = NULL; #endif } Toolkit::~Toolkit() { #ifdef HAS_H460P m_presence.Stop(); #endif #ifdef HAS_TLS if (m_sslCtx) SSL_CTX_free(m_sslCtx); #endif 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 if (m_ConfigDefaultSection.IsEmpty()) { PTRACE(0, "Error: Call Config() before SetConfig()!"); return NULL; } 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::Empty(); } 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 if (PFile::Copy(m_ConfigFilePath, m_tmpconfig)) { #else if (symlink(m_ConfigFilePath, m_tmpconfig) == 0) { #endif PConfig * testConfig = new PConfig(m_tmpconfig, m_ConfigDefaultSection); if (testConfig->GetSections().GetSize() > 0) { delete m_Config; m_Config = testConfig; } else { if (m_Config) { // reload PTRACE(0, "CONFIG\tFailed to read valid config - keeping old config"); SNMP_TRAP(6, SNMPError, General, "Failed to read config"); GkStatus::Instance()->SignalStatus("Failed to read valid config - keeping old config\r\n", MIN_STATUS_TRACE_LEVEL); } else { // startup m_Config = testConfig; // warning will be printed a bit later } } } else { // Oops! Create temporary config file failed, use the original one if (PFile::Exists(m_ConfigFilePath)) { PTRACE(0, "CONFIG\tCould not create/link config to a temporary file " << m_tmpconfig); SNMP_TRAP(6, SNMPError, General, "Failed to load config"); } 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_DATABASE 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"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: no driver"); return; } GkSQLConnection *sqlConn = GkSQLConnection::Create(driverName, "SQLCONF"); if (sqlConn == NULL) { PTRACE(0, "SQLCONF\tFailed to create a connection: no driver found for " << driverName << " database"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: no driver"); 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"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: can't connect to DB"); 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"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: query failed"); } else if (!queryResult->IsValid()) { PTRACE(0, "SQLCONF\tFailed to load config key=>value pairs from SQL " "database (" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage()); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: query failed"); } 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"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: query failed"); } 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] << '\''); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: query failed"); } 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; } // Rewrite E164 query = m_Config->GetString("SQLConfig", "RewriteE164Query", ""); if (!query.IsEmpty()) { PTRACE(4, "SQLCONF\tLoading E164 rewrite rules from SQL database"); PStringArray params; params += GKName(); queryResult = sqlConn->ExecuteQuery(query, ¶ms); if (queryResult == NULL) { PTRACE(0, "SQLCONF\tFailed to load E164 rewrite rules from SQL database: " "timeout or fatal error"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: E.164 query failed"); } else if (!queryResult->IsValid()) { PTRACE(0, "SQLCONF\tFailed to load E164 rewrite rules from SQL database (" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage()); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: E.164 query failed"); } else if (queryResult->GetNumFields() < 2) { PTRACE(0, "SQLCONF\tFailed to load E164 rewrite rules from SQL database: " "at least 2 columns must be present in the result set"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: E.164 query failed"); } else { while (queryResult->FetchRow(params)) if (params[0].IsEmpty()) { PTRACE(1, "SQLCONF\tInvalid E164 rewrite rule found in the SQL " "database: '" << params[0] << '=' << params[1] << '\''); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: E.164 query failed"); } else { m_Config->SetString("RasSrv::RewriteE164", params[0], params[1]); PTRACE(6, "SQLCONF\tRewriteE164 rule read: '" << params[0] << '=' << params[1] << '\''); } PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() << " E164 rewrite rules " "loaded from SQL database"); } delete queryResult; queryResult = NULL; } // Rewrite Alias Query query = m_Config->GetString("SQLConfig", "RewriteAliasQuery", ""); 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 Alias rewrite rules from SQL database: " "timeout or fatal error"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Alias rewrite query failed"); } else if (!queryResult->IsValid()) { PTRACE(0, "SQLCONF\tFailed to load Alias rewrite rules from SQL database (" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage()); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Alias rewrite query failed"); } else if (queryResult->GetNumFields() < 2) { PTRACE(0, "SQLCONF\tFailed to load Alias rewrite rules from SQL database: " "at least 2 columns must be present in the result set"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Alias rewrite query failed"); } else { while (queryResult->FetchRow(params)) if (params[0].IsEmpty()) { PTRACE(1, "SQLCONF\tInvalid Alias rewrite rule found in the SQL " "database: '" << params[0] << '=' << params[1] << '\''); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Alias rewrite query failed"); } else { m_Config->SetString("RasSrv::RewriteAlias", params[0], params[1]); PTRACE(6, "SQLCONF\tRewriteAlias rule read: '" << params[0] << '=' << params[1] << '\'' ); } PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() << " Alias rewrite rules " "loaded from SQL database" ); } delete queryResult; queryResult = NULL; } // Assigned Alias Query query = m_Config->GetString("SQLConfig", "AssignedAliasQuery", ""); if (!query.IsEmpty()) { PTRACE(4, "SQLCONF\tLoading Assigned Alias rules from SQL database"); PStringArray params; params += GKName(); queryResult = sqlConn->ExecuteQuery(query, ¶ms); if (queryResult == NULL) { PTRACE(0, "SQLCONF\tFailed to load Assigned Alias rules from SQL database: " "timeout or fatal error"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Assigned Alias rewrite query failed"); } else if (!queryResult->IsValid()) { PTRACE(0, "SQLCONF\tFailed to load Assigned Alias rules from SQL database (" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage()); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Assigned Alias rewrite query failed"); } else if (queryResult->GetNumFields() < 2) { PTRACE(0, "SQLCONF\tFailed to load Assigned Alias rules from SQL database: " "at least 2 columns must be present in the result set"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Assigned Alias rewrite query failed"); } else { while (queryResult->FetchRow(params)) if (params[0].IsEmpty()) { PTRACE(1, "SQLCONF\tInvalid Assigned Alias rule found in the SQL " "database: '" << params[0] << '=' << params[1] << '\'' ); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Assigned Alias rewrite query failed"); } else { m_Config->SetString("RasSrv::AssignedAlias", params[0], params[1]); PTRACE(6, "SQLCONF\tAssignedAlias rule read: '" << params[0] << '=' << params[1] << '\'' ); } PTRACE(4, "SQLCONF\t" << queryResult->GetNumRows() << " Assigned Alias rules " "loaded from SQL database" ); } delete queryResult; queryResult = NULL; } // Neighbor Query (old style) 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"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Neighbor query failed"); } else if (!queryResult->IsValid()) { PTRACE(0, "SQLCONF\tFailed to load neighbors from SQL database (" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage()); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Neighbor query failed"); } 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"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Neighbor query failed"); } 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 << '\''); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Neighbor query failed"); } 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; } // Neighbor Query (new style) query = m_Config->GetString("SQLConfig", "NeighborsQuery2", ""); 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"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Neighbor query failed"); } else if (!queryResult->IsValid()) { PTRACE(0, "SQLCONF\tFailed to load neighbors from SQL database (" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage()); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Neighbor query failed"); } else if (queryResult->GetNumFields() < 3) { PTRACE(0, "SQLCONF\tFailed to load neighbors from SQL database: " "at least 6 columns must be present in the result set"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Neighbor query failed"); } else { // H46018 Traversal Support int h460id = 8; int h46018traverse=0; while (queryResult->FetchRow(params)) { // GkID, Gatekeeper Identifier and Gatekeeper Type must not be empty if (params[0].IsEmpty() || params[1].IsEmpty() || params[2].IsEmpty()) { PTRACE(1, "SQLCONF\tInvalid neighbor entry found in the SQL " "database: '" << params[0] << '=' << params[0] << '\''); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Neighbor query failed"); } else { m_Config->SetString("RasSrv::Neighbors", params[0], params[1]); PString neighborSection = "Neighbor::" + params[0]; h46018traverse =0; if (queryResult->GetNumFields() > h460id ) { h46018traverse = params[h460id].AsInteger(); if (h46018traverse) { if (h46018traverse == 1) m_Config->SetString(neighborSection, "H46018Server", "1"); else if (h46018traverse == 2) m_Config->SetString(neighborSection, "H46018Client", "1"); if (queryResult->GetNumFields() > (h460id+2)) { if (!params[h460id+1]) m_Config->SetString(neighborSection, "SendAuthUser", params[h460id+1]); if (!params[h460id+2]) m_Config->SetString(neighborSection, "SendPassword", params[h460id+2]); } } } // General Neighbor settings for (PINDEX i = 0; i < queryResult->GetNumFields(); ++i) { if (!params[i]) { switch (i) { case 0 : m_Config->SetString(neighborSection, "GatekeeperIdentifier", params[i]); break; case 2 : m_Config->SetString(neighborSection, "Host", params[i]); break; case 3 : m_Config->SetString(neighborSection, "SendPrefixes", params[i]); break; case 4 : m_Config->SetString(neighborSection, "AcceptPrefixes", params[i]); break; case 5 : m_Config->SetString(neighborSection, "ForwardHopCount", params[i]); break; case 6 : m_Config->SetString(neighborSection, "AcceptForwardedLRQ", params[i]); break; case 7 : m_Config->SetString(neighborSection, "ForwardResponse", params[i]); break; default: break; } } } PTRACE(4, "SQLCONF\t" << neighborSection << " neighbor 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"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Permanent EP query failed"); } else if (!queryResult->IsValid()) { PTRACE(0, "SQLCONF\tFailed to load permanent endpoints from SQL database " "(" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage()); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Permanent EP query failed"); } 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"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Permanent EP query failed"); } 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 (params.GetSize() > 5) value += ";" + params[4] + "," + params[5]; if (key.IsEmpty() || value.IsEmpty()) { PTRACE(1, "SQLCONF\tInvalid permanent endpoint entry found " "in the SQL database: '" << key << '=' << value << '\''); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Permanent EP query failed"); } 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"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Gateway query failed"); } else if (!queryResult->IsValid()) { PTRACE(0, "SQLCONF\tFailed to load gateway prefixes from SQL database (" << queryResult->GetErrorCode() << "): " << queryResult->GetErrorMessage()); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Gateway query failed"); } 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"); SNMP_TRAP(5, SNMPError, Database, "SQLConfig: Gateway query failed"); } 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_DATABASE } void Toolkit::PrepareReloadConfig() { if (!m_ConfigDirty) CreateConfig(); else // the config has been changed via status port, use it directly m_ConfigDirty = false; } PConfig* Toolkit::ReloadConfig() { // make a new symlink if needed PrepareReloadConfig(); // read the toolkit config values // read the gatekeeper name from the config file, because it might be used as a key into the SQL config m_GKName = Config()->GetString("Name", "OpenH323GK"); PTrace::SetLevel(GkConfig()->GetInteger("TraceLevel", PTrace::GetLevel())); // set the max size of an array in an ASN encoded message (eg. max length of alias list) PINDEX maxArraySize = GkConfig()->GetInteger("MaxASNArraySize", 0); if (maxArraySize > 0) PASN_Object::SetMaximumArraySize(maxArraySize); // set max bytes to queue for a socket, before asuming its dead (probably only an issue with H.460.17) int maxSocketQueue = GkConfig()->GetInteger("MaxSocketQueue", 100); if (maxSocketQueue > 0) g_maxSocketQueue = maxSocketQueue; 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(); // update the gatekeeper name, in case it was set in the SQL config m_GKName = m_Config->GetString("Name", "OpenH323GK"); PString removeH235Call = m_Config->GetString(RoutedSec, "RemoveH235Call", "0"); m_alwaysRemoveH235Tokens = (removeH235Call.AsUnsigned() == 1); m_removeH235TokensfromNetwork.clear(); if (removeH235Call.GetLength() >= 7) { PStringArray networks = removeH235Call.Tokenise(",", FALSE); for (PINDEX n=0; n < networks.GetSize(); ++n) { if (networks[n].Find('/') == P_MAX_INDEX) networks[n] += "/32"; // add netmask to pure IPs NetworkAddress net = NetworkAddress(networks[n]); m_removeH235TokensfromNetwork.push_back(net); } } #ifdef HAS_H235_MEDIA m_H235HalfCallMediaEnabled = m_Config->GetBoolean(RoutedSec, "EnableH235HalfCallMedia", 0) || m_Config->GetBoolean(RoutedSec, "RequireH235HalfCallMedia", 0); m_H235HalfCallMediaKeyUpdatesEnabled = m_Config->GetBoolean(RoutedSec, "EnableH235HalfCallMediaKeyUpdates", 0); #endif #ifdef HAS_H46018 m_H46018Enabled = m_Config->GetBoolean(RoutedSec, "EnableH46018", 0); #endif #ifdef HAS_H46023 m_H46023Enabled = (m_Config->GetBoolean(RoutedSec, "EnableH46023", 0) && !m_Config->GetString(RoutedSec, "H46023STUN", "")); #endif // TODO/BUG: always call SetGKHome() on reload, even if we don't have a Home= setting // otherwise we won't detect new IPs on the machine PString GKHome(m_Config->GetString("Home", "")); if (GKHome == "0.0.0.0") { PTRACE(1, "Config error: Invalid Home setting (0.0.0.0), ignoring"); SNMP_TRAP(10, SNMPError, Configuration, "Invalid Home setting (0.0.0.0)"); GKHome = ""; } if (m_GKHome.empty() || !GKHome) SetGKHome(GKHome.Tokenise(",;", false)); m_RouteTable.InitTable(); m_VirtualRouteTable.InitTable(); m_ProxyCriterion.LoadConfig(m_Config); #ifdef HAS_H46023 if (m_H46023Enabled) LoadH46023STUN(); #endif #ifdef HAS_H460P m_presence.LoadConfig(m_Config); #endif m_Rewrite.LoadConfig(m_Config); m_GWRewrite.LoadConfig(m_Config); m_AssignedEPAliases.LoadConfig(m_Config); #ifdef h323v6 m_AssignedGKs.LoadConfig(m_Config); #endif #if HAS_DATABASE m_AlternateGKs.LoadConfig(m_Config); m_qosMonitor.LoadConfig(m_Config); #endif m_timestampFormatStr = m_Config->GetString("TimestampFormat", "Cisco"); delete m_cliRewrite; m_cliRewrite = new CLIRewrite(); CapacityControl::Instance()->LoadConfig(); LoadCauseMap(m_Config); LoadReasonMap(m_Config); ParseTranslationMap(m_receivedCauseMap, m_Config->GetString(RoutedSec, "TranslateReceivedQ931Cause", "")); ParseTranslationMap(m_sentCauseMap, m_Config->GetString(RoutedSec, "TranslateSentQ931Cause", "")); // read [PortNotifications] m_portOpenNotifications[RASPort] = m_Config->GetString("PortNotifications", "RASPortOpen", ""); m_portOpenNotifications[Q931Port] = m_Config->GetString("PortNotifications", "Q931PortOpen", ""); m_portOpenNotifications[H245Port] = m_Config->GetString("PortNotifications", "H245PortOpen", ""); m_portOpenNotifications[RTPPort] = m_Config->GetString("PortNotifications", "RTPPortOpen", ""); m_portOpenNotifications[T120Port] = m_Config->GetString("PortNotifications", "T120PortOpen", ""); m_portOpenNotifications[StatusPort] = m_Config->GetString("PortNotifications", "StatusPortOpen", ""); m_portOpenNotifications[RadiusPort] = m_Config->GetString("PortNotifications", "RadiusPortOpen", ""); m_portCloseNotifications[RASPort] = m_Config->GetString("PortNotifications", "RASPortClose", ""); m_portCloseNotifications[Q931Port] = m_Config->GetString("PortNotifications", "Q931PortClose", ""); m_portCloseNotifications[H245Port] = m_Config->GetString("PortNotifications", "H245PortClose", ""); m_portCloseNotifications[RTPPort] = m_Config->GetString("PortNotifications", "RTPPortClose", ""); m_portCloseNotifications[T120Port] = m_Config->GetString("PortNotifications", "T120PortClose", ""); m_portCloseNotifications[StatusPort] = m_Config->GetString("PortNotifications", "StatusPortClose", ""); m_portCloseNotifications[RadiusPort] = m_Config->GetString("PortNotifications", "RadiusPortClose", ""); 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) & 0x0f] |= (1UL << (cmin & 7)); } } } // load H.225 reason to Q.931 cause mapping void Toolkit::LoadReasonMap(PConfig *cfg) { // default to ITU-T Recommendation H.225 clause 7.2.2.8, table 5 unsigned DefaultH225ReasonToQ931Cause[] = { 34, 47, 3, 16, 88, 111, 38, 42, 28, 41, 17, 31, 16, 31, 20, 31, 47, 127, 31, 31, 31, 127 }; m_H225ReasonToQ931Cause.assign(&DefaultH225ReasonToQ931Cause[0], &DefaultH225ReasonToQ931Cause[22]); for(int reason = 0; reason < H225_ReleaseCompleteReason::e_tunnelledSignallingRejected; reason++) { PString cause = cfg->GetString("H225toQ931", PString(reason), ""); if (!cause.IsEmpty()) { m_H225ReasonToQ931Cause[reason] = cause.AsInteger(); } } } bool Toolkit::MatchRegex(const PString & str, const PString & regexStr) { if (regexStr.IsEmpty()) return false; // nothing matches an empty regex and it triggers a PTLib assertion PINDEX pos = 0; PRegularExpression regex(regexStr, PRegularExpression::Extended); if(regex.GetErrorCode() != PRegularExpression::NoError) { PTRACE(2, "Errornous '"<< regex.GetErrorText() <<"' compiling regex: " << regexStr); SNMP_TRAP(7, SNMPError, Configuration, "Invalid RegEx"); return FALSE; } if(!regex.Execute(str, pos)) { // PTRACE(6, "Gk\tRegex '" << regexStr << "' did not match '" << str << "'"); return FALSE; } return TRUE; } #if HAS_DATABASE Toolkit::AssignedAliases::AssignedAliases() : m_sqlactive(false), m_sqlConn(NULL), m_timeout(-1) { } Toolkit::AssignedAliases::~AssignedAliases() { } bool Toolkit::AssignedAliases::LoadSQL(PConfig * cfg) { delete m_sqlConn; PString authName = "AssignedAliases::SQL"; if (cfg->GetSections().GetStringsIndex(authName) == P_MAX_INDEX) return false; const PString driverName = cfg->GetString(authName, "Driver", ""); if (driverName.IsEmpty()) { PTRACE(0, "AliasSQL\tModule creation failed: " "no SQL driver selected"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); PTRACE(0, "AliasSQL\tFATAL: Shutting down"); return false; } m_sqlConn = GkSQLConnection::Create(driverName, authName); if (m_sqlConn == NULL) { PTRACE(0, "AliasSQL\tModule creation failed: " "Could not find " << driverName << " database driver"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); PTRACE(0, "AliasSQL\tFATAL: Shutting down"); return false; } m_query = cfg->GetString(authName, "Query", ""); if (m_query.IsEmpty()) { PTRACE(0, "AliasSQL\tModule creation failed: No query configured"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); PTRACE(0, "AliasSQL\tFATAL: Shutting down"); return false; } else PTRACE(4, "AliasSQL\tQuery: " << m_query); if (!m_sqlConn->Initialize(cfg, authName)) { PTRACE(0, "AliasSQL\tModule creation failed: Could not connect to the database"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); return false; } return true; } bool Toolkit::AssignedAliases::DatabaseLookup( const PString & alias, PStringArray & newAliases) { if (!m_sqlactive) return false; std::map params; params["u"] = alias; GkSQLResult* result = m_sqlConn->ExecuteQuery(m_query, params, m_timeout); if (result == NULL) { PTRACE(2, "AliasSQL\tQuery failed - timeout or fatal error"); SNMP_TRAP(5, SNMPError, Database, "AssignedAliases query failed"); return false; } if (!result->IsValid()) { PTRACE(2, "AliasSQL\tQuery failed (" << result->GetErrorCode() << ") - " << result->GetErrorMessage()); SNMP_TRAP(5, SNMPError, Database, "AssignedAliases query failed"); delete result; return false; } bool success = false; if (result->GetNumRows() < 1) PTRACE(3, "AliasSQL\tQuery returned no rows"); else if (result->GetNumFields() < 1) { PTRACE(2, "AliasSQL\tBad-formed query - " "no columns found in the result set"); SNMP_TRAP(5, SNMPError, Database, "AssignedAliases query failed"); } else { PStringArray retval; while (result->FetchRow(retval)) { if (retval[0].IsEmpty()) { PTRACE(1, "AliasSQL\tQuery Invalid value found."); SNMP_TRAP(5, SNMPError, Database, "AssignedAliases query failed"); continue; } if (!success) success = true; PTRACE(5, "AliasSQL\tQuery result: " << retval[0]); newAliases.AppendString(retval[0]); } } delete result; return success; } #endif // HAS_DATABASE void Toolkit::AssignedAliases::LoadConfig(PConfig * m_config) { gkAssignedAliases.clear(); #if HAS_DATABASE if (LoadSQL(m_config)) { m_sqlactive = true; } else #endif { const PStringToString kv = m_config->GetAllKeyValues(AssignedAliasSection); for (PINDEX i=0; i < kv.GetSize(); i++) { PString data = kv.GetDataAt(i); PStringArray datalines = data.Tokenise(" ,;\t"); for (PINDEX j=0; j < datalines.GetSize(); j++) gkAssignedAliases.push_back(std::pair(kv.GetKeyAt(i),datalines[j])); } } } #ifdef H323_H350 bool Toolkit::CreateH350Session(H350_Session * session) { PString ldap = GkConfig()->GetString(H350Section, "ServerName", "127.0.0.1"); PString port = GkConfig()->GetString(H350Section, "ServerPort", "389"); PString server = ldap + ":" + port; // IPv4 if (IsIPv6Address(ldap)) server = "[" + ldap + "]:" + port; // IPv6 PString user = GkConfig()->GetString(H350Section, "BindUserDN", ""); PString password = Toolkit::Instance()->ReadPassword(H350Section, "BindUserPW"); PCaselessString mode = GkConfig()->GetString(H350Section, "BindAuthMode", "simple"); PLDAPSession::AuthenticationMethod authMethod = PLDAPSession::AuthSimple; if (mode == "sasl") authMethod = PLDAPSession::AuthSASL; else if (mode == "kerberos") authMethod = PLDAPSession::AuthKerberos; bool startTLS = Toolkit::AsBool(GkConfig()->GetString(H350Section, "StartTLS", "0")); if (!session->Open(server)) { PTRACE(1, "H350\tCannot locate H.350 Server " << server); return false; } if (startTLS) { #ifdef hasLDAPStartTLS if (!session->StartTLS()) { PTRACE(1, "H350\tStartTLS failed"); SNMP_TRAP(7, SNMPWarning, Database, "H.350 StartTLS failed"); return false; } #else PTRACE(1, "H350\tError: LDAP StartTLS not supported in this version"); SNMP_TRAP(7, SNMPWarning, Database, "H.350 StartTLS not supported"); #endif } if (!user.IsEmpty()) session->Bind(user, password, authMethod); return true; } bool Toolkit::AssignedAliases::QueryH350Directory(const PString & alias, PStringArray & aliases) { // support Assigned Aliases if (!Toolkit::AsBool(GkConfig()->GetString(H350Section, "AssignedAliases", "0"))) return false; // search the directory PString search = GkConfig()->GetString(H350Section, "SearchBaseDN", ""); H225_AliasAddress aliasaddress; H323SetAliasAddress(alias, aliasaddress); PString filter; switch (aliasaddress.GetTag()) { case H225_AliasAddress::e_dialedDigits: filter = "h323IdentitydialedDigits=" + alias; break; case H225_AliasAddress::e_h323_ID: filter = "h323Identityh323-ID=" + alias; break; case H225_AliasAddress::e_url_ID: filter = "h323IdentityURL-ID=" + alias; break; default: PTRACE(4, "H350\tAssigned Alias: unhandled alias type " << aliasaddress.GetTagName()); return false; } H350_Session session; if (!Toolkit::Instance()->CreateH350Session(&session)) { PTRACE(1, "H350\tAssigned Alias: Could not connect to directory server"); return false; } H350_Session::LDAP_RecordList rec; int count = session.Search(search, filter, rec); if (count <= 0) { PTRACE(4, "H350\tAssigned Alias: No Record Found"); session.Close(); return false; } // locate the record for (H350_Session::LDAP_RecordList::const_iterator x = rec.begin(); x != rec.end(); ++x) { H350_Session::LDAP_Record entry = x->second; PString al; PINDEX i; if (session.GetAttribute(entry, "h323Identityh323-ID", al)) { PStringList als = al.Lines(); for (i=0; i< als.GetSize(); i++) aliases.AppendString(als[i]); } if (session.GetAttribute(entry, "h323IdentitydialedDigits", al)) { PStringList als = al.Lines(); for (i=0; i< als.GetSize(); i++) aliases.AppendString(als[i]); } if (session.GetAttribute(entry, "h323IdentityURL-ID", al)) { PStringList als = al.Lines(); for (i=0; i< als.GetSize(); i++) aliases.AppendString(als[i]); } session.Close(); if (aliases.GetSize() > 0) { PTRACE(2, "H350\tAssigned Alias: Located " << aliases.GetSize() << " aliases"); session.Close(); return true; } } PTRACE(4, "H350\tAssigned Alias: No valid Assigned Alias found"); session.Close(); return false; } #endif #ifdef HAS_TLS void apps_ssl_info_callback(const SSL * s, int where, int ret) { const char * str; int w = where & ~SSL_ST_MASK; if (w & SSL_ST_CONNECT) str = "SSL_connect"; else if (w & SSL_ST_ACCEPT) str = "SSL_accept"; else str = "undefined"; if (where & SSL_CB_LOOP) { PTRACE(5, "TLS\t" << str << ": " << SSL_state_string_long(s)); } else if (where & SSL_CB_ALERT) { str = (where & SSL_CB_READ)?"read":"write"; PTRACE(5, "TLS\tSSL3 alert " << str << ": " << SSL_alert_type_string_long(ret) << ":" << SSL_alert_desc_string_long(ret)); } else if (where & SSL_CB_EXIT) { if (ret == 0) PTRACE(5, str << ":failed in " << SSL_state_string_long(s)); else if (ret < 0) { PTRACE(5, "TLS\t" << str << ": error in " << SSL_state_string_long(s)); } } } int pem_passwd_cb(char * buf, int size, int rwflag, void * password) { strncpy(buf, (char *)(password), size); buf[size - 1] = '\0'; return(strlen(buf)); } int verify_callback(int ok, X509_STORE_CTX * store) { char data[256]; if (!ok) { X509 * cert = X509_STORE_CTX_get_current_cert(store); int depth = X509_STORE_CTX_get_error_depth(store); int err = X509_STORE_CTX_get_error(store); PTRACE(5, "TLS\tError with certificate at depth " << depth); X509_NAME_oneline(X509_get_issuer_name(cert), data, 256); PTRACE(5, "TLS\t issuer = " << data); X509_NAME_oneline(X509_get_subject_name(cert), data, 256); PTRACE(5, "TLS\t subject = " << data); PTRACE(5, "TLS\t err " << err << ": " << X509_verify_cert_error_string(err)); } return ok; } bool Toolkit::IsTLSEnabled() const { return Toolkit::AsBool(GkConfig()->GetString(TLSSec, "EnableTLS", "0")); } SSL_CTX * Toolkit::GetTLSContext() { if (!m_sslCtx) { // TODO: init OpenSSL only once in application if (!SSL_library_init()) { PTRACE(1, "TLS\tOpenSSL init failed"); return NULL; } SSL_load_error_strings(); if (!RAND_status()) { PTRACE(3, "TLS\tPRNG needs seeding"); #ifdef P_LINUX RAND_load_file("/dev/urandom", 1024); #else BYTE seed[1024]; for (size_t i = 0; i < sizeof(seed); i++) seed[i] = (BYTE)rand(); RAND_seed(seed, sizeof(seed)); #endif } m_sslCtx = SSL_CTX_new(SSLv23_method()); // allow SSLv3 + TLSv1 SSL_CTX_set_options(m_sslCtx, SSL_OP_NO_SSLv2); // remove unsafe SSLv2 SSL_CTX_set_mode(m_sslCtx, SSL_MODE_AUTO_RETRY); // handle re-negotiations automatically SSL_CTX_set_cipher_list(m_sslCtx, "ALL:!ADH:!LOW:!EXP:@STRENGTH"); SSL_CTX_set_info_callback(m_sslCtx, apps_ssl_info_callback); // enable only when debugging SSL_CTX_set_default_passwd_cb(m_sslCtx, pem_passwd_cb); m_passphrase = m_Config->GetString(TLSSec, "Passphrase", ""); SSL_CTX_set_default_passwd_cb_userdata(m_sslCtx, (void *)m_passphrase.GetPointer()); PString caFile = m_Config->GetString(TLSSec, "CAFile", ""); PString caDir = m_Config->GetString(TLSSec, "CADir", ""); const char * caFilePtr = caFile.IsEmpty() ? NULL : caFile.GetPointer(); const char * caDirPtr = caDir.IsEmpty() ? NULL : caDir.GetPointer(); if (caFilePtr || caDirPtr) { if (SSL_CTX_load_verify_locations(m_sslCtx, caFilePtr, caDirPtr) != 1) { PTRACE(1, "TLS\tError loading CA file or directory (" << caFile << " / " << caDir << ")"); } } if (SSL_CTX_use_certificate_chain_file(m_sslCtx, m_Config->GetString(TLSSec, "Certificates", "tls_certificate.pem")) != 1) { PTRACE(1, "TLS\tError loading certificate file"); } if (SSL_CTX_use_PrivateKey_file(m_sslCtx, m_Config->GetString(TLSSec, "PrivateKey", "tls_private_key.pem"), SSL_FILETYPE_PEM) != 1) { PTRACE(1, "TLS\tError loading private key file"); } SSL_CTX_set_verify(m_sslCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); // context is used both in client and server mode SSL_CTX_set_verify_depth(m_sslCtx, 5); } return m_sslCtx; } bool Toolkit::MatchHostCert(SSL * ssl, PIPSocket::Address addr) { bool found = false; if (!Toolkit::AsBool(GkConfig()->GetString(TLSSec, "CheckCertificateIP", "0"))) return true; // check disabled X509 * cert = SSL_get_peer_certificate(ssl); if (!cert) { PTRACE(1, "TLS\tError: Certificate didn't match IP: No peer certificate"); SNMP_TRAP(8, SNMPError, Authentication, ::AsString(addr) + " didn't provide a TLS certificate"); return false; } // do a reverse lookup of the peer IP char reverseLookup[256]; #ifdef hasIPV6 if (addr.GetVersion() == 6) { struct sockaddr_in6 inaddr; SetSockaddr(inaddr, addr, 0); getnameinfo((const sockaddr*)&inaddr, sizeof(inaddr), reverseLookup, sizeof(reverseLookup), NULL, 0, 0); } else #endif { struct sockaddr_in inaddr; SetSockaddr(inaddr, addr, 0); getnameinfo((const sockaddr*)&inaddr, sizeof(inaddr), reverseLookup, sizeof(reverseLookup), NULL, 0, 0); } // check all subjectAltName elements STACK_OF( GENERAL_NAME ) * altnames = (STACK_OF( GENERAL_NAME ) *) X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (altnames) { for (int i = 0; i < sk_GENERAL_NAME_num(altnames); i++) { GENERAL_NAME * gn = sk_GENERAL_NAME_value(altnames, i); if (gn && (gn->type == GEN_DNS)) { char * dns = (char *)ASN1_STRING_data(gn->d.ia5); if (dns) { PTRACE(5, "TLS\tChecking Certificate DNS " << dns); if (::AsString(addr) == dns) { found = true; // IP matched } else if ((strlen(reverseLookup) > 0) && (strcasecmp(reverseLookup, dns) == 0)) { found = true; // FQDN matched } else { struct addrinfo *result = NULL; if (getaddrinfo(dns, NULL, NULL, &result) == 0) { for (struct addrinfo * res = result; res != NULL; res = res->ai_next) { PIPSocket::Address lookup; #ifdef hasIPV6 if (res->ai_addr->sa_family == AF_INET6) { lookup = ((struct sockaddr_in6*)(res->ai_addr))->sin6_addr; } else #endif { lookup = ((struct sockaddr_in*)(res->ai_addr))->sin_addr; } if (addr == lookup) { found = true; // DNS lookup matched } } freeaddrinfo(result); } } } } if (found) break; } } sk_GENERAL_NAME_free(altnames); if (!found) { // check commonName, too X509_NAME * subject = X509_get_subject_name(cert); if (subject) { char commonname[256]; if (X509_NAME_get_text_by_NID(subject, NID_commonName, commonname, 256) > 0) { PTRACE(5, "TLS\tChecking Certificate commonName " << commonname); if (::AsString(addr) == commonname) { found = true; // IP matched } else if ((strlen(reverseLookup) > 0) && (strcasecmp(reverseLookup, commonname) == 0)) { found = true; // FQDN matched } else { struct addrinfo *result = NULL; if (getaddrinfo(commonname, NULL, NULL, &result) == 0) { for (struct addrinfo * res = result; res != NULL; res = res->ai_next) { PIPSocket::Address lookup; #ifdef hasIPV6 if (res->ai_addr->sa_family == AF_INET6) { lookup = ((struct sockaddr_in6*)(res->ai_addr))->sin6_addr; } else #endif { lookup = ((struct sockaddr_in*)(res->ai_addr))->sin_addr; } if (addr == lookup) { found = true; // DNS lookup matched } } freeaddrinfo(result); } } } } } X509_free(cert); if (!found) { PTRACE(1, "TLS\tError: Certificate didn't match IP " << ::AsString(addr) << " (" << reverseLookup << ")"); SNMP_TRAP(8, SNMPError, Authentication, ::AsString(addr) + " failed TLS certificate IP check"); } return found; // check failed } #endif bool Toolkit::AssignedAliases::QueryAssignedAliases(const PString & alias, PStringArray & aliases) { #if HAS_DATABASE if (DatabaseLookup(alias, aliases)) return true; #endif #ifdef H323_H350 if (QueryH350Directory(alias, aliases)) return true; #endif return false; } bool Toolkit::AssignedAliases::GetAliases(const H225_ArrayOf_AliasAddress & alias, H225_ArrayOf_AliasAddress & aliaslist) { if (alias.GetSize() == 0) return false; PStringArray newaliases; bool found = false; for (PINDEX h=0; h < alias.GetSize(); h++) { if (QueryAssignedAliases(H323GetAliasAddressString(alias[h]), newaliases)) found = true; } if (!found) { for (PINDEX i=0; i < alias.GetSize(); i++) { PString search = H323GetAliasAddressString(alias[i]); for (unsigned j=0; j < gkAssignedAliases.size(); j++) { PTRACE(5, "Alias\tCompare " << gkAssignedAliases[j].first << " to " << search); if (gkAssignedAliases[j].first == search) { newaliases.AppendString(gkAssignedAliases[j].second); if (!found) found = true; } } } } // Create the Assigned Alias List if (found) { // add existing items to the end of the list if (aliaslist.GetSize() > 0) { for (PINDEX l=0; l < aliaslist.GetSize(); l++) { PString a = H323GetAliasAddressString(aliaslist[l]); bool located = false; for (PINDEX m=0; m < newaliases.GetSize(); m++) { if (newaliases[m] == a) located = true; } if (!located) newaliases.AppendString(a); } } aliaslist.RemoveAll(); for (PINDEX k=0; k < newaliases.GetSize(); k++) { H225_AliasAddress * aliasaddress = new H225_AliasAddress(); H323SetAliasAddress(newaliases[k], *aliasaddress); aliaslist.Append(aliasaddress); } } return found; } //////////////////////////////////////////////////////////////////////// #ifdef h323v6 #if HAS_DATABASE Toolkit::AssignedGatekeepers::AssignedGatekeepers() : m_sqlactive(false), m_sqlConn(NULL), m_timeout(-1) { } Toolkit::AssignedGatekeepers::~AssignedGatekeepers() { } bool Toolkit::AssignedGatekeepers::LoadSQL(PConfig * cfg) { delete m_sqlConn; PString authName = "AssignedGatekeepers::SQL"; if (cfg->GetSections().GetStringsIndex(authName) == P_MAX_INDEX) return false; const PString driverName = cfg->GetString(authName, "Driver", ""); if (driverName.IsEmpty()) { PTRACE(0, "AssignSQL\tModule creation failed: " "no SQL driver selected"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); PTRACE(0, "AssignSQL\tFATAL: Shutting down"); return false; } m_sqlConn = GkSQLConnection::Create(driverName, authName); if (m_sqlConn == NULL) { PTRACE(0, "AssignSQL\tModule creation failed: " "Could not find " << driverName << " database driver"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); PTRACE(0, "AssignSQL\tFATAL: Shutting down"); return false; } m_query = cfg->GetString(authName, "Query", ""); if (m_query.IsEmpty()) { PTRACE(0, "AssignSQL\tModule creation failed: No query configured"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); PTRACE(0, "AssignSQL\tFATAL: Shutting down"); return false; } else PTRACE(4, "AssignSQL\tQuery: " << m_query); if (!m_sqlConn->Initialize(cfg, authName)) { PTRACE(0, "AssignSQL\tModule creation failed: Could not connect to the database"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); return false; } return true; } bool Toolkit::AssignedGatekeepers::DatabaseLookup( const PString & alias, const PIPSocket::Address & ipaddr, PStringArray & newGks ) { if (!m_sqlactive) return false; std::map params; params["u"] = alias; params["i"] = ipaddr.AsString(); GkSQLResult* result = m_sqlConn->ExecuteQuery(m_query, params, m_timeout); if (result == NULL) { PTRACE(2, "AssignSQL\tQuery failed - timeout or fatal error"); SNMP_TRAP(4, SNMPError, Database, "AssignedGatekeepers query failed"); return false; } if (!result->IsValid()) { PTRACE(2, "AssignSQL\tQuery failed (" << result->GetErrorCode() << ") - " << result->GetErrorMessage()); SNMP_TRAP(4, SNMPError, Database, "AssignedGatekeepers query failed"); delete result; return false; } bool success = false; if (result->GetNumRows() < 1) PTRACE(3, "AssignSQL\tQuery returned no rows"); else if (result->GetNumFields() < 1) { PTRACE(2, "AssignSQL\tBad-formed query - " "no columns found in the result set"); SNMP_TRAP(4, SNMPError, Database, "AssignedGatekeepers query failed"); } else { PStringArray retval; while (result->FetchRow(retval)) { if (retval[0].IsEmpty()) { PTRACE(1, "AssignSQL\tQuery Invalid value found."); SNMP_TRAP(4, SNMPError, Database, "AssignedGatekeepers query failed"); continue; } if (!success) success = true; PTRACE(5, "AssignSQL\tQuery result: " << retval[0]); PStringArray adr_parts = SplitIPAndPort(retval[0],GK_DEF_UNICAST_RAS_PORT); PIPSocket::Address ip; if (!IsIPAddress(adr_parts[0])) PIPSocket::GetHostAddress(adr_parts[0],ip); else ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); H323TransportAddress addr(ip,port); if (addr == H323TransportAddress(RasServer::Instance()->GetRasAddress(ip))) { PTRACE(5, "AssignSQL\tIGNORE " << retval[0] << " LRQ loop detected."); continue; } newGks.AppendString(retval[0]); } } delete result; return success; } #endif // HAS_DATABASE void Toolkit::AssignedGatekeepers::LoadConfig(PConfig * m_config) { assignedGKList.clear(); #if HAS_DATABASE if (LoadSQL(m_config)) { m_sqlactive = true; } else #endif { const PStringToString kv = m_config->GetAllKeyValues(AssignedGatekeeperSection); for (PINDEX i=0; i < kv.GetSize(); i++) assignedGKList.push_back(std::pair(kv.GetKeyAt(i),kv.GetDataAt(i))); } } #ifdef H323_H350 bool Toolkit::AssignedGatekeepers::QueryH350Directory(const PString & alias, const PIPSocket::Address & ip, PStringArray & addresses) { // support gatekeeper discovery if (!Toolkit::AsBool(GkConfig()->GetString(H350Section, "GatekeeperDiscovery", "0"))) return false; // search the directory PString search = GkConfig()->GetString(H350Section, "SearchBaseDN", ""); H225_AliasAddress aliasaddress; H323SetAliasAddress(alias, aliasaddress); PString filter; switch (aliasaddress.GetTag()) { case H225_AliasAddress::e_dialedDigits: filter = "h323IdentitydialedDigits=" + alias; break; case H225_AliasAddress::e_h323_ID: filter = "h323Identityh323-ID=" + alias; break; case H225_AliasAddress::e_url_ID: filter = "h323IdentityURL-ID=" + alias; break; default: PTRACE(4, "H350\tAssigned GK: unhandled alias type " << aliasaddress.GetTagName()); return false; } H350_Session session; if (!Toolkit::Instance()->CreateH350Session(&session)) { PTRACE(1, "H350\tAssigned GK: Could not connect to Server."); return false; } H350_Session::LDAP_RecordList rec; int count = session.Search(search, filter, rec); if (count <= 0) { PTRACE(4, "H350\tAssigned GK: No Record Found"); session.Close(); return false; } // locate the record for (H350_Session::LDAP_RecordList::const_iterator x = rec.begin(); x != rec.end(); ++x) { H350_Session::LDAP_Record entry = x->second; PString gk; if (session.GetAttribute(entry, "h323IdentityGKDomain", gk)) { PTRACE(2, "H350\tAssigned GK: GK located " << gk); addresses = gk.Lines(); session.Close(); return true; } } PTRACE(4, "H350\tAssigned GK: No valid Assigned GK found"); session.Close(); return false; } #endif bool Toolkit::AssignedGatekeepers::QueryAssignedGK(const PString & alias, const PIPSocket::Address & ip, PStringArray & addresses) { #if HAS_DATABASE if (DatabaseLookup(alias, ip, addresses)) return true; #endif #ifdef H323_H350 if (QueryH350Directory(alias, ip, addresses)) return true; #endif return false; } #ifdef hasSRV static PString DNStoIP(const PString & dns) { H323TransportAddress iface(dns); PIPSocket::Address ip; WORD port = GK_DEF_UNICAST_RAS_PORT; iface.GetIpAndPort(ip, port); return AsString(ip, port); } #endif bool Toolkit::AssignedGatekeepers::GetAssignedGK(const PString & alias, const PIPSocket::Address & ip, H225_ArrayOf_AlternateGK & gklist) { PStringArray assignedGK; bool found = QueryAssignedGK(alias, ip, assignedGK); if (!found) { for (unsigned j=0; j < assignedGKList.size(); j++) { PString match = assignedGKList[j].first.Trim(); if (match.Left(1) != "^") { // prefix match if (MatchPrefix(alias,assignedGKList[j].first)) { assignedGK.AppendString(assignedGKList[j].second); if (!found) found = true; } } else { // regex match for IP address if (MatchRegex(ip.AsString(), match)) { assignedGK.AppendString(assignedGKList[j].second); if (!found) found = true; } } } } if (found) { PStringArray ipaddresses; for (PINDEX k=0; k < assignedGK.GetSize(); k++) { PString number = assignedGK[k]; if (IsIPAddress(number)) ipaddresses.AppendString(number); #ifdef hasSRV else { PString xnum = assignedGK[k]; if (xnum.Left(5) != "h323:") xnum = "h323:user@" + xnum; PStringList str; if (PDNS::LookupSRV(xnum, "_h323rs._udp.",str)) { PTRACE(4, "AssignGK\t" << str.GetSize() << " SRV Records found" ); for (PINDEX i = 0; i < str.GetSize(); i++) { PString newhost = str[i].Right(str[i].GetLength()-5); PTRACE(4, "AssignedGK\tDNS SRV converted GK address " << number << " to " << newhost ); ipaddresses.AppendString(newhost); } } else { ipaddresses.AppendString(DNStoIP(number)); } } #endif } for (PINDEX k = 0; k < ipaddresses.GetSize(); k++) { PString num = ipaddresses[k]; PStringArray tokens = SplitIPAndPort(num, GK_DEF_UNICAST_RAS_PORT); WORD port = (WORD)tokens[1].AsUnsigned(); int sz = gklist.GetSize(); gklist.SetSize(sz+1); H225_AlternateGK & alt = gklist[sz]; alt.m_rasAddress = SocketToH225TransportAddr(PIPSocket::Address(tokens[0]),port); alt.m_needToRegister = true; alt.m_priority = k; } } return found; } #endif ///////////////////////////////////////////////////////////////////////////////////////// #if HAS_DATABASE Toolkit::AlternateGatekeepers::AlternateGatekeepers() : m_sqlactive(false), m_sqlConn(NULL), m_timeout(-1) { } Toolkit::AlternateGatekeepers::~AlternateGatekeepers() { } void Toolkit::AlternateGatekeepers::LoadConfig(PConfig * cfg) { delete m_sqlConn; PString authName = "AlternateGatekeepers::SQL"; if (cfg->GetSections().GetStringsIndex(authName) == P_MAX_INDEX) return; const PString driverName = cfg->GetString(authName, "Driver", ""); if (driverName.IsEmpty()) { PTRACE(0, "AltGKSQL\tModule creation failed: " "no SQL driver selected"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); PTRACE(0, "AltGKSQL\tFATAL: Shutting down"); return; } m_sqlConn = GkSQLConnection::Create(driverName, authName); if (m_sqlConn == NULL) { PTRACE(0, "AltGKSQL\tModule creation failed: " "Could not find " << driverName << " database driver"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); PTRACE(0, "AltGKSQL\tFATAL: Shutting down"); return; } m_query = cfg->GetString(authName, "Query", ""); if (m_query.IsEmpty()) { PTRACE(0, "AltGKSQL\tModule creation failed: No query configured"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); PTRACE(0, "AltGKSQL\tFATAL: Shutting down"); return; } else PTRACE(4, "AltGKSQL\tQuery: " << m_query); if (!m_sqlConn->Initialize(cfg, authName)) { PTRACE(0, "AltGKSQL\tModule creation failed: Could not connect to the database"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); return; } m_sqlactive = true; } bool Toolkit::AlternateGatekeepers::GetAlternateGK(const PIPSocket::Address & ip, H225_ArrayOf_AlternateGK & gklist) { PStringArray addresses; if (QueryAlternateGK(ip, addresses)) { for (PINDEX k = 0; k < addresses.GetSize(); k++) { PString num = addresses[k]; PStringArray tokens = SplitIPAndPort(num, GK_DEF_UNICAST_RAS_PORT); WORD port = (WORD)tokens[1].AsUnsigned(); PIPSocket::Address ipAddress; PIPSocket::GetHostAddress(tokens[0],ipAddress); int sz = gklist.GetSize(); gklist.SetSize(sz+1); H225_AlternateGK & alt = gklist[sz]; alt.m_rasAddress = SocketToH225TransportAddr(ipAddress,port); alt.m_needToRegister = true; alt.m_priority = k; } return true; } return false; } bool Toolkit::AlternateGatekeepers::QueryAlternateGK(const PIPSocket::Address & ip, PStringArray & addresses) { if (!m_sqlactive) return false; std::map params; params["i"] = ip.AsString(); GkSQLResult* result = m_sqlConn->ExecuteQuery(m_query, params, m_timeout); if (result == NULL) { PTRACE(2, "AltGKSQL\tQuery failed - timeout or fatal error"); SNMP_TRAP(5, SNMPError, Database, "AlternateGatekeepers query failed"); return false; } if (!result->IsValid()) { PTRACE(2, "AltGKSQL\tQuery failed (" << result->GetErrorCode() << ") - " << result->GetErrorMessage()); SNMP_TRAP(5, SNMPError, Database, "AlternateGatekeepers query failed"); delete result; return false; } bool success = false; if (result->GetNumRows() < 1) PTRACE(3, "AltGKSQL\tQuery returned no rows"); else if (result->GetNumFields() < 1) { PTRACE(2, "AltGKSQL\tBad-formed query - " "no columns found in the result set"); SNMP_TRAP(5, SNMPError, Database, "AlternateGatekeepers query failed"); } else { PStringArray retval; while (result->FetchRow(retval)) { if (retval[0].IsEmpty()) { PTRACE(1, "AltGKSQL\tQuery Invalid value found."); SNMP_TRAP(5, SNMPError, Database, "AlternateGatekeepers query failed"); continue; } if (!success) success = true; PTRACE(5, "AltGKSQL\tQuery result: " << retval[0]); addresses.AppendString(retval[0]); } } delete result; return success; } #endif #if HAS_DATABASE Toolkit::QoSMonitor::QoSMonitor() : m_sqlactive(false), m_sqlConn(NULL), m_timeout(-1) { } Toolkit::QoSMonitor::~QoSMonitor() { } void Toolkit::QoSMonitor::LoadConfig(PConfig * cfg) { delete m_sqlConn; PString authName = "GkQoSMonitor::SQL"; if (cfg->GetSections().GetStringsIndex(authName) == P_MAX_INDEX) return; const PString driverName = cfg->GetString(authName, "Driver", ""); if (driverName.IsEmpty()) { PTRACE(0, "QoSSQL\tModule creation failed: " "no SQL driver selected"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); PTRACE(0, "QoSSQL\tFATAL: Shutting down"); return; } m_sqlConn = GkSQLConnection::Create(driverName, authName); if (m_sqlConn == NULL) { PTRACE(0, "QoSSQL\tModule creation failed: " "Could not find " << driverName << " database driver"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); PTRACE(0, "QoSSQL\tFATAL: Shutting down"); return; } m_query = cfg->GetString(authName, "Query", ""); if (m_query.IsEmpty()) { PTRACE(0, "QoSSQL\tModule creation failed: No query configured"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); PTRACE(0, "QoSSQL\tFATAL: Shutting down"); return; } else PTRACE(4, "QoSSQL\tQuery: " << m_query); if (!m_sqlConn->Initialize(cfg, authName)) { PTRACE(0, "QoSSQL\tModule creation failed: Could not connect to the database"); SNMP_TRAP(4, SNMPError, Database, authName + " creation failed"); return; } m_sqlactive = true; } bool Toolkit::QoSMonitor::PostRecord(const std::map& params) { if (!m_sqlactive) return false; GkSQLResult* result = m_sqlConn->ExecuteQuery(m_query, params, m_timeout); if (result == NULL) { PTRACE(2, "QoSSQL\tFailed to store QoS Data: Timeout or fatal error"); SNMP_TRAP(5, SNMPError, Database, "QoSMonitor query failed"); return false; } if (result) { if (result->IsValid()) { if (result->GetNumRows() < 1) { PTRACE(4, "QoSSQL\tFailed to store QoS Data: No rows have been updated"); SNMP_TRAP(5, SNMPError, Database, "QoSMonitor query failed"); delete result; return false; } } else { PTRACE(2, "QoSSQL\tfailed to store QoS Data: Err(" << result->GetErrorCode() << ") " << result->GetErrorMessage() ); SNMP_TRAP(5, SNMPError, Database, "QoSMonitor query failed"); delete result; return false; } } delete result; return true; } #endif ///////////////////////////////////////////////////////////////////////////////////////// bool Toolkit::RewriteE164(H225_AliasAddress & alias) { if ((alias.GetTag() != H225_AliasAddress::e_dialedDigits) && (alias.GetTag() != H225_AliasAddress::e_h323_ID) && (alias.GetTag() != H225_AliasAddress::e_url_ID)) { if (alias.GetTag() != H225_AliasAddress::e_partyNumber) return false; H225_PartyNumber & partyNumber = alias; if (partyNumber.GetTag() != H225_PartyNumber::e_e164Number && partyNumber.GetTag() != H225_PartyNumber::e_privateNumber) return false; } PString E164 = ::AsString(alias, FALSE); bool changed = RewritePString(E164); if (changed) { if (E164.Find("@") != P_MAX_INDEX) H323SetAliasAddress(E164, alias,H225_AliasAddress::e_url_ID); else if (IsValidE164(E164)) H323SetAliasAddress(E164, alias, H225_AliasAddress::e_dialedDigits); else if (alias.GetTag() != H225_AliasAddress::e_partyNumber) H323SetAliasAddress(E164, alias, H225_AliasAddress::e_h323_ID); else { H225_PartyNumber & partyNumber = alias; if (partyNumber.GetTag() == H225_PartyNumber::e_e164Number) { H225_PublicPartyNumber &number = partyNumber; number.m_publicNumberDigits = E164; } else if (partyNumber.GetTag() == H225_PartyNumber::e_privateNumber) { H225_PrivatePartyNumber &number = partyNumber; number.m_privateNumberDigits = E164; } } } return changed; } bool Toolkit::RewriteE164(H225_ArrayOf_AliasAddress & aliases) { bool changed = false; for (PINDEX n = 0; n < aliases.GetSize(); ++n) changed |= RewriteE164(aliases[n]); return changed; } bool Toolkit::GWRewriteE164(const PString & gw, bool direction, H225_AliasAddress & alias, callptr call) { if (alias.GetTag() != H225_AliasAddress::e_dialedDigits) { if (alias.GetTag() != H225_AliasAddress::e_partyNumber) return false; H225_PartyNumber &partyNumber = alias; if (partyNumber.GetTag() != H225_PartyNumber::e_e164Number && partyNumber.GetTag() != H225_PartyNumber::e_privateNumber) return false; } PString E164 = ::AsString(alias, FALSE); bool changed = GWRewritePString(gw, direction, E164, call); if (changed) { if (alias.GetTag() == H225_AliasAddress::e_dialedDigits) H323SetAliasAddress(E164, alias, alias.GetTag()); else { H225_PartyNumber &partyNumber = alias; if (partyNumber.GetTag() == H225_PartyNumber::e_e164Number) { H225_PublicPartyNumber &number = partyNumber; number.m_publicNumberDigits = E164; } else if (partyNumber.GetTag() == H225_PartyNumber::e_privateNumber) { H225_PrivatePartyNumber &number = partyNumber; number.m_privateNumberDigits = E164; } } } return changed; } bool Toolkit::GWRewriteE164(const PString & gw, bool direction, H225_ArrayOf_AliasAddress & aliases, callptr call) { bool changed = false; for (PINDEX n = 0; n < aliases.GetSize(); ++n) { changed |= GWRewriteE164(gw, direction, aliases[n], call); } return changed; } bool Toolkit::RemoveH235TokensFrom(const PIPSocket::Address & addr) const { if (m_alwaysRemoveH235Tokens) return true; for (unsigned i=0; i < m_removeH235TokensfromNetwork.size(); ++i) { if (addr << m_removeH235TokensfromNetwork[i]) return true; } return false; } bool Toolkit::isBehindNAT(PIPSocket::Address & externalIP) const { return (m_VirtualRouteTable.IsMasquerade(externalIP)); } #ifdef HAS_H46023 bool Toolkit::IsH46023Enabled() const { return (m_H46023Enabled && ( // is enabled and #ifdef HAS_H46018 m_H46018Enabled || // used with H.460.18 or #endif m_Config->GetBoolean(RoutedSec, "SupportCallingNATedEndpoints", 1))); // GnuGk Native NAT Support } #endif #ifdef HAS_H46026 bool Toolkit::IsH46026Enabled() const { return m_Config->GetBoolean(RoutedSec, "EnableH46026", 0); } #endif #ifdef HAS_H46023 void Toolkit::LoadH46023STUN() { m_H46023STUN.clear(); PString stun = m_Config->GetString(RoutedSec, "H46023STUN", ""); PStringArray stunlist = stun.Tokenise(","); for (PINDEX i = 0; i < stunlist.GetSize(); i++) { PStringList addresses; PStringList x = stunlist[i].Tokenise(":"); PString number = "h323:user@" + x[0]; #ifdef P_DNS if (!PDNS::LookupSRV(number, "_stun._udp.", addresses)) addresses.AppendString(stunlist[i]); #endif for (PINDEX j=0; j < addresses.GetSize(); ++j) { PString newhost = addresses[j].Mid(5); PIPSocket::Address ip; WORD port; GetTransportAddress(newhost, GK_DEF_STUN_PORT, ip, port); if (ip.IsValid()) { int intID = m_ProxyCriterion.IsInternal(ip); std::map >::iterator inf = m_H46023STUN.find(intID); if (inf == m_H46023STUN.end()) { std::vector addrs; addrs.push_back(H323TransportAddress(ip, port)); m_H46023STUN.insert(pair >(intID, addrs)); } else inf->second.push_back(H323TransportAddress(ip, port)); PTRACE(4, "Std23\tSTUN Server added if:" << intID << " " << newhost); } } } } bool Toolkit::GetH46023STUN(const PIPSocket::Address & addr, H323TransportAddress & stun) { PWaitAndSignal m(m_stunMutex); int intID = m_ProxyCriterion.IsInternal(addr); std::map >::iterator inf = m_H46023STUN.find(intID); if (inf != m_H46023STUN.end()) { if (inf->second.size() > 1) std::random_shuffle(inf->second.begin(), inf->second.end()); stun = inf->second.front(); return true; } PTRACE(2, "Std23\tNo STUNserver for Interface " << intID << " disabling H.460.23"); return false; } bool Toolkit::H46023SameNetwork(const PIPSocket::Address & addr1, const PIPSocket::Address & addr2) { return (m_ProxyCriterion.IsInternal(addr1) == m_ProxyCriterion.IsInternal(addr2)); } #endif #ifdef HAS_H460P bool Toolkit::IsH460PEnabled() const { return m_presence.IsEnabled(); } GkPresence & Toolkit::GetPresenceHandler() { return m_presence; } #endif std::vector Toolkit::GetInternalNetworks() const { return !GkConfig()->GetString("ExternalIP", "").IsEmpty() ? m_VirtualRouteTable.GetInternalNetworks() : m_RouteTable.GetInternalNetworks(); } bool Toolkit::IsSNMPEnabled() const { #ifdef HAS_SNMP return AsBool(GkConfig()->GetString(SNMPSection, "EnableSNMP", "0")); #else return false; #endif } bool Toolkit::IsIPv6Enabled() const { #ifdef hasIPV6 return AsBool(GkConfig()->GetString("EnableIPv6", "0")); #else return false; #endif } bool Toolkit::IsPortNotificationActive() // not const to allow simple map access { // the feature is active if at least one action is defined return !m_portOpenNotifications[RASPort].IsEmpty() || !m_portOpenNotifications[Q931Port].IsEmpty() || !m_portOpenNotifications[H245Port].IsEmpty() || !m_portOpenNotifications[RTPPort].IsEmpty() || !m_portOpenNotifications[T120Port].IsEmpty() || !m_portOpenNotifications[StatusPort].IsEmpty() || !m_portOpenNotifications[RadiusPort].IsEmpty() || !m_portCloseNotifications[RASPort].IsEmpty() || !m_portCloseNotifications[Q931Port].IsEmpty() || !m_portCloseNotifications[H245Port].IsEmpty() || !m_portCloseNotifications[RTPPort].IsEmpty() || !m_portCloseNotifications[T120Port].IsEmpty() || !m_portCloseNotifications[StatusPort].IsEmpty() || !m_portCloseNotifications[RadiusPort].IsEmpty(); } void Toolkit::PortNotification(PortType type, PortAction action, const PString & protocol, const PIPSocket::Address & addr, WORD port, const H225_CallIdentifier & callID) { PTRACE(5, "Port Notification " << ((action == PortOpen) ? "OPEN " : "CLOSE ") << type << " " << protocol << " " << ::AsString(addr, port)); // book keeping for status port command if (callID != H225_CallIdentifier(0)) { callptr call = CallTable::Instance()->FindCallRec(callID); if (call) { if (action == PortOpen) call->AddDynamicPort(DynamicPort(type, addr, port)); else call->RemoveDynamicPort(DynamicPort(type, addr, port)); } } // execute notification command PString cmd; if (action == PortOpen) { cmd = m_portOpenNotifications[type]; } else if (action == PortClose) { cmd = m_portCloseNotifications[type]; } if (cmd.IsEmpty()) return; // set port arguments cmd.Replace("%p", protocol); cmd.Replace("%n", PString(port)); cmd.Replace("%i", ::AsString(addr)); if(system(cmd) == -1) { PTRACE(1, "Error executing port notification: " << cmd); SNMP_TRAP(6, SNMPError, General, "Error executing port notification: " + cmd); } } PString Toolkit::GetGKHome(vector & 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) { m_GKHome.clear(); for (PINDEX n = 0; n < home.GetSize(); ++n) m_GKHome.push_back(home[n]); PIPSocket::InterfaceTable it; if (PIPSocket::GetInterfaceTable(it)) { int is = it.GetSize(); // check if the interface is valid for (size_t n = 0; n < m_GKHome.size(); ++n) { PINDEX i = 0; 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 PTLib interface table"); } } // if no home interfaces specified, set all IPs from interface table // except INADDR_ANY if (m_GKHome.empty()) { for (PINDEX n = 0; n < it.GetSize(); ++n) { m_GKHome.push_back(it[n].GetAddress()); } } // remove INADDR_ANY for (size_t n = 0; n < m_GKHome.size(); ++n) { if ((m_GKHome[n] == INADDR_ANY) #ifdef hasIPV6 || m_GKHome[n].IsLinkLocal() || ((m_GKHome[n].GetVersion() == 6) && m_GKHome[n].IsLoopback()) #endif ) { m_GKHome.erase(m_GKHome.begin() + n); --n; // re-test the new element on position n } } // if IPv6 is not enabled, remove _all_ IPv6 addresses if (!IsIPv6Enabled()) { for (size_t n = 0; n < m_GKHome.size(); ++n) { if (m_GKHome[n].GetVersion() == 6) { m_GKHome.erase(m_GKHome.begin() + n); --n; // re-test the new element on position n } } } } // remove duplicate interfaces sort(m_GKHome.begin(), m_GKHome.end()); std::vector::iterator end_unique = unique(m_GKHome.begin(), m_GKHome.end()); m_GKHome.erase(end_unique, m_GKHome.end()); // move loopback interfaces to the end std::list sortedHomes; for (unsigned j=0; j < m_GKHome.size(); j++) { if (m_GKHome[j].IsLoopback()) { sortedHomes.push_back(m_GKHome[j]); } else { sortedHomes.push_front(m_GKHome[j]); } } m_GKHome.assign(sortedHomes.begin(), sortedHomes.end()); } bool Toolkit::IsGKHome(const PIPSocket::Address & addr) const { for (std::vector::const_iterator i = m_GKHome.begin(); i != m_GKHome.end(); ++i) { if (*i == addr) { return true; } } return false; } 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; } break; } break; case t35cPoland: switch (manufacturer) { case t35mGnuGk: switch (extension) { case t35eNeighborId: return iecNeighborId; case t35eNATTraversal: return iecNATTraversal; } break; } break; } // 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 = GNUGK_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]; memset(&rawData, 0, sizeof(rawData)); 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::GenerateAcctSessionId() { PWaitAndSignal lock(m_acctSessionMutex); return psprintf(PString("%08x%08x"), m_acctSessionBase, ++m_acctSessionCounter); } PString Toolkit::AsString( const PTime & tm, /// timestamp to convert into a string const PString & formatStr /// format string to use ) const { PString fmtStr = !formatStr ? formatStr : m_timestampFormatStr; if (fmtStr.IsEmpty()) return PString::Empty(); 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 SNMP_TRAP(7, SNMPError, Configuration, "Invalid timestamp format - using default"); 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); char buf[128]; if (strftime(buf, sizeof(buf), (const char*)fmtStr, tmptr) == 0) { SNMP_TRAP(7, SNMPError, Configuration, "Invalid timestamp format - using default"); PTRACE(0, "TOOLKIT\tCould not apply timestamp formatting - using default"); return tm.AsString( "hh:mm:ss.uuu z www MMM d yyyy" ); } 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::Empty(); PConfig* const cfg = Config(); if (!cfg->HasKey(cfgSection, cfgKey)) return PString::Empty(); 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); SNMP_TRAP(7, SNMPError, General, "Password decode failed"); } 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::RewriteSourceAddress(SetupMsg & setup) const { // Read RewriteSourceAddress settings PString rewriteChar = m_Config->GetString("RewriteSourceAddress", "ReplaceChar", ""); PString rules = m_Config->GetString("RewriteSourceAddress", "Rules", ""); bool matchSource = Toolkit::AsBool(m_Config->GetString("RewriteSourceAddress", "MatchSourceTypeToDestination", "0")); bool onlyE164 = Toolkit::AsBool(m_Config->GetString("RewriteSourceAddress", "OnlyE164", "0")); bool only10Dand11D = Toolkit::AsBool(m_Config->GetString("RewriteSourceAddress", "OnlyValid10Dand11D", "0")); int aliasForceType = m_Config->GetString("RewriteSourceAddress", "ForceAliasType", "-1").AsInteger(); if (aliasForceType > 2) aliasForceType = -1; // Limited only to support dialedDigits, h323_ID, url_ID, H225_Setup_UUIE & setupBody = setup.GetUUIEBody(); unsigned destType = H225_AliasAddress::e_h323_ID; if (matchSource) { if (setup.GetQ931().HasIE(Q931::CalledPartyNumberIE)) { destType = H225_AliasAddress::e_dialedDigits; } else if (setupBody.HasOptionalField(H225_Setup_UUIE::e_destinationAddress) && setupBody.m_destinationAddress.GetSize() > 0) { destType = setupBody.m_destinationAddress[0].GetTag(); } if (aliasForceType > -1) { PString destination = PString(); if (setupBody.HasOptionalField(H225_Setup_UUIE::e_destinationAddress) && setupBody.m_destinationAddress.GetSize() > 0) { destination = ::AsString(setupBody.m_destinationAddress[0],false); if (!rewriteChar) { PStringArray rewrite = rewriteChar.Tokenise(";"); for (PINDEX i=0; i < rewrite.GetSize(); ++i) { PStringArray cRule = rewrite[i].Tokenise(","); if (cRule.GetSize() == 2) destination.Replace(cRule[0],cRule[1],true); } } H323SetAliasAddress(destination,setupBody.m_destinationAddress[0],aliasForceType); } if (!destination && aliasForceType == H225_AliasAddress::e_dialedDigits) setup.GetQ931().SetCalledPartyNumber(destination); destType = aliasForceType; } } PINDEX i = 0; if (setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) { while(i < setupBody.m_sourceAddress.GetSize()) { bool remove = false; if (matchSource && setupBody.m_sourceAddress[i].GetTag() != destType) remove = true; if (onlyE164 && setupBody.m_sourceAddress[i].GetTag() != H225_AliasAddress::e_dialedDigits) remove = true; if (only10Dand11D && !Is10Dor11Dnumber(setupBody.m_sourceAddress[i])) remove = true; if (remove) setupBody.m_sourceAddress.RemoveAt(i); else { if (aliasForceType > -1 && setupBody.m_sourceAddress[i].GetTag() != (unsigned)aliasForceType) { PString source = ::AsString(setupBody.m_sourceAddress[i], false); H323SetAliasAddress(source, setupBody.m_sourceAddress[i], aliasForceType); } ++i; } } } PBoolean changed = false; PString source = PString(); if (setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceAddress) && (setupBody.m_sourceAddress.GetSize() > 0)) { source = ::AsString(setupBody.m_sourceAddress[0], false); } else { setup.GetQ931().GetCallingPartyNumber(source); } if (!rewriteChar) { PStringArray rewrite = rewriteChar.Tokenise(";"); for (PINDEX i=0; i < rewrite.GetSize(); ++i) { PStringArray cRule = rewrite[i].Tokenise(","); if (cRule.GetSize() == 2) { source.Replace(cRule[0], cRule[1],true); changed = true; } } } if (!rules) { PStringArray sRules = rules.Tokenise(";"); if (sRules.GetSize() > 0 && IsValidE164(source)) { // Only support E164 for now. for (PINDEX i=0; i < sRules.GetSize(); ++i) { PStringArray cRule = sRules[i].Tokenise(","); if (cRule.GetSize() == 2 && cRule[0] == source.Left(cRule[0].GetLength())) { PTRACE(4,"SWRITE\tSource Address " << source << " rewritten to " << cRule[1]); source = cRule[1]; changed = true; break; } } } } // TODO: Add Database rewrite here - SH if (changed) { setupBody.IncludeOptionalField(H225_Setup_UUIE::e_sourceAddress); setupBody.m_sourceAddress.SetSize(1); H323SetAliasAddress(source, setupBody.m_sourceAddress[0]); if (IsValidE164(source)) setup.GetQ931().SetCallingPartyNumber(source); else setup.GetQ931().RemoveIE(Q931::CallingPartyNumberIE); } } void Toolkit::SetRerouteCauses(unsigned char *causeMap) { memcpy(causeMap, m_causeMap, 128/8); } unsigned Toolkit::MapH225ReasonToQ931Cause(int reason) { if( reason < 0 || reason > H225_ReleaseCompleteReason::e_tunnelledSignallingRejected ) return 0; else return m_H225ReasonToQ931Cause[reason]; } void Toolkit::ParseTranslationMap(std::map & cause_map, const PString & ini) const { cause_map.clear(); PStringArray pairs(ini.Tokenise(",", false)); for (PINDEX i = 0; i < pairs.GetSize(); ++i) { PStringArray causes(pairs[i].Tokenise(":=", false)); if (causes.GetSize() == 2) { cause_map.insert(pair(causes[0].AsInteger(), causes[1].AsInteger())); } else { PTRACE(1, "Syntax error in cause mapping: " << causes[i]); SNMP_TRAP(7, SNMPError, Configuration, "Invalid cause translation configuration"); } } if (!cause_map.empty()) { // note: do not set to false, because feature might be active globally or for another endpoint Toolkit::Instance()->SetCauseCodeTranslationActive(true); } } unsigned Toolkit::TranslateReceivedCause(unsigned cause) const { std::map::const_iterator i = m_receivedCauseMap.find(cause); if (i != m_receivedCauseMap.end()) return i->second; else return cause; } unsigned Toolkit::TranslateSentCause(unsigned cause) const { std::map::const_iterator i = m_sentCauseMap.find(cause); if (i != m_sentCauseMap.end()) return i->second; else return cause; } PStringList Toolkit::GetAuthenticatorList() { PString auth = GkConfig()->GetString("Gatekeeper::Main", "Authenticators", ""); PStringArray authlist(auth.Tokenise(" ,;\t")); return authlist; } gnugk-3.4/PaxHeaders.16356/GkClient.cxx0000644000175000001440000000005012214316314015742 xustar000000000000000020 atime=1380868658 20 ctime=1380868609 gnugk-3.4/GkClient.cxx0000644000175000001440000026763512214316314014726 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // GkClient.cxx // // Copyright (c) Citron Network Inc. 2001-2003 // Copyright (c) 2002-2013, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #include #include #include #include "stl_supp.h" #include "RasPDU.h" #include "RasSrv.h" #include "ProxyChannel.h" #include "h323util.h" #include "sigmsg.h" #include "cisco.h" #include "GkClient.h" #if defined (HAS_H46018) || defined(HAS_H46023) #include #endif #ifdef HAS_H46023 #include #include #include #include #endif #if P_DNS #include #endif using std::vector; using std::multimap; using std::make_pair; using std::for_each; using std::mem_fun; using std::bind1st; using Routing::Route; namespace { const char* const EndpointSection = "Endpoint"; const char* const RewriteE164Section = "Endpoint::RewriteE164"; } class AlternateGKs { public: AlternateGKs(const PIPSocket::Address &, WORD); void Set(const H225_AlternateGK &); void Set(const H225_ArrayOf_AlternateGK &); void Set(const PString &); bool Get(PIPSocket::Address &, WORD &); private: typedef multimap GKList; GKList AltGKs; GKList::iterator index; PIPSocket::Address pgkaddr; WORD pgkport; }; AlternateGKs::AlternateGKs(const PIPSocket::Address & gkaddr, WORD gkport) : pgkaddr(gkaddr), pgkport(gkport) { } void AlternateGKs::Set(const H225_AlternateGK & agk) { AltGKs.clear(); AltGKs.insert(make_pair(int(agk.m_priority), agk.m_rasAddress)); index = AltGKs.begin(); } void AlternateGKs::Set(const H225_ArrayOf_AlternateGK & agk) { AltGKs.clear(); for (PINDEX i = 0; i < agk.GetSize(); ++i) { const H225_AlternateGK & gk = agk[i]; AltGKs.insert(make_pair(int(gk.m_priority), gk.m_rasAddress)); } index = AltGKs.begin(); } void AlternateGKs::Set(const PString & addr) { PIPSocket::Address gkaddr; WORD gkport; if (GetTransportAddress(addr, GK_DEF_UNICAST_RAS_PORT, gkaddr, gkport)) { H323TransportAddress taddr(gkaddr, gkport); H225_TransportAddress haddr; taddr.SetPDU(haddr); AltGKs.insert(make_pair(AltGKs.size()+1, haddr)); } } bool AlternateGKs::Get(PIPSocket::Address & gkaddr, WORD & gkport) { if (!AltGKs.empty()) { if (index == AltGKs.end()) { index = AltGKs.begin(); // switch back to original GK gkaddr = pgkaddr; gkport = (WORD)pgkport; return false; } const H225_TransportAddress & rasAddress = (index++)->second; if (GetIPAndPortFromTransportAddr(rasAddress, gkaddr, gkport)) return true; PTRACE(3, "GKC\tInvalid AlternateGK Address!" ); return Get(gkaddr, gkport); // try next } return false; } class NATClient : public RegularJob { public: NATClient(const H225_TransportAddress &, const H225_EndpointIdentifier &); // override from class RegularJob virtual void Stop(); private: // override from class Task virtual void Exec(); bool DetectIncomingCall(); void SendInfo(int); PIPSocket::Address gkip; WORD gkport; PString endpointId; CallSignalSocket * socket; }; NATClient::NATClient(const H225_TransportAddress & addr, const H225_EndpointIdentifier & id) { GetIPAndPortFromTransportAddr(addr, gkip, gkport); endpointId = id.GetValue(); socket = NULL; SetName("NATClient"); Execute(); } void NATClient::Stop() { PWaitAndSignal lock(m_deletionPreventer); RegularJob::Stop(); if (socket) { SendInfo(Q931::CallState_DisconnectRequest); socket->Close(); } } void NATClient::Exec() { ReadLock lockConfig(ConfigReloadMutex); #ifdef HAS_TLS if (Toolkit::AsBool(GkConfig()->GetString(EndpointSection, "UseTLS", "0"))) { socket = new TLSCallSignalSocket(); } else #endif { socket = new CallSignalSocket(); } socket->SetPort(gkport); if (socket->Connect(gkip)) { PTRACE(2, "GKC\t" << socket->GetName() << " connected, waiting for incoming call"); if (DetectIncomingCall()) { PTRACE(3, "GKC\tIncoming call detected"); CreateJob(socket, &CallSignalSocket::Dispatch, "NAT call"); socket = NULL; return; } } PWaitAndSignal lockDeletion(m_deletionPreventer); delete socket; socket = NULL; // If we lose the TCP connection then retry after 60 sec int retryInterval = GkConfig()->GetInteger(EndpointSection, "NATRetryInterval", 60); PTRACE(4, "GKC\tNAT Socket connection lost " << gkip << " retry connection in " << retryInterval << " secs."); ReadUnlock unlockConfig(ConfigReloadMutex); Wait(retryInterval * 1000); } bool NATClient::DetectIncomingCall() { while (socket->IsOpen()) { long retry = GkConfig()->GetInteger( EndpointSection, "NATKeepaliveInterval", 20 ); // keep alive interval must be less than 30 sec (from testing 20 sec seems fine) SendInfo(Q931::CallState_IncomingCallProceeding); ReadUnlock unlockConfig(ConfigReloadMutex); while (socket->IsOpen() && --retry > 0) if (socket->IsReadable(1000)) // one second return socket->IsOpen(); } return false; } void NATClient::SendInfo(int state) { Q931 information; information.BuildInformation(0, false); PBYTEArray buf, epid(endpointId, endpointId.GetLength(), false); information.SetIE(Q931::FacilityIE, epid); information.SetCallState(Q931::CallStates(state)); information.Encode(buf); if (socket) { PrintQ931(5, "Send to ", socket->GetName(), &information, NULL); socket->TransmitData(buf); } } ////////////////////////////////////////////////////////////////////// #ifdef HAS_H46023 // stuff cut from pstun.cxx #pragma pack(1) struct STUNattribute { enum Types { MAPPED_ADDRESS = 0x0001, RESPONSE_ADDRESS = 0x0002, CHANGE_REQUEST = 0x0003, SOURCE_ADDRESS = 0x0004, CHANGED_ADDRESS = 0x0005, USERNAME = 0x0006, PASSWORD = 0x0007, MESSAGE_INTEGRITY = 0x0008, ERROR_CODE = 0x0009, UNKNOWN_ATTRIBUTES = 0x000a, REFLECTED_FROM = 0x000b, MaxValidCode }; PUInt16b type; PUInt16b length; STUNattribute * GetNext() const { return (STUNattribute *)(((const BYTE *)this)+length+4); } }; class STUNaddressAttribute : public STUNattribute { public: BYTE pad; BYTE family; PUInt16b port; BYTE ip[4]; PIPSocket::Address GetIP() const { return PIPSocket::Address(4, ip); } protected: enum { SizeofAddressAttribute = sizeof(BYTE)+sizeof(BYTE)+sizeof(WORD)+sizeof(PIPSocket::Address) }; void InitAddrAttr(Types newType) { type = (WORD)newType; length = SizeofAddressAttribute; pad = 0; family = 1; } bool IsValidAddrAttr(Types checkType) const { return type == checkType && length == SizeofAddressAttribute; } }; class STUNmappedAddress : public STUNaddressAttribute { public: void Initialise() { InitAddrAttr(MAPPED_ADDRESS); } bool IsValid() const { return IsValidAddrAttr(MAPPED_ADDRESS); } }; class STUNchangedAddress : public STUNaddressAttribute { public: void Initialise() { InitAddrAttr(CHANGED_ADDRESS); } bool IsValid() const { return IsValidAddrAttr(CHANGED_ADDRESS); } }; class STUNchangeRequest : public STUNattribute { public: BYTE flags[4]; STUNchangeRequest(bool changeIP, bool changePort) { Initialise(); SetChangeIP(changeIP); SetChangePort(changePort); } void Initialise() { type = CHANGE_REQUEST; length = sizeof(flags); memset(flags, 0, sizeof(flags)); } bool IsValid() const { return type == CHANGE_REQUEST && length == sizeof(flags); } bool GetChangeIP() const { return (flags[3]&4) != 0; } void SetChangeIP(bool on) { if (on) flags[3] |= 4; else flags[3] &= ~4; } bool GetChangePort() const { return (flags[3]&2) != 0; } void SetChangePort(bool on) { if (on) flags[3] |= 2; else flags[3] &= ~2; } }; class STUNmessageIntegrity : public STUNattribute { public: BYTE hmac[20]; void Initialise() { type = MESSAGE_INTEGRITY; length = sizeof(hmac); memset(hmac, 0, sizeof(hmac)); } bool IsValid() const { return type == MESSAGE_INTEGRITY && length == sizeof(hmac); } }; struct STUNmessageHeader { PUInt16b msgType; PUInt16b msgLength; BYTE transactionId[16]; }; #pragma pack() class STUNmessage : public PBYTEArray { public: enum MsgType { BindingRequest = 0x0001, BindingResponse = 0x0101, BindingError = 0x0111, SharedSecretRequest = 0x0002, SharedSecretResponse = 0x0102, SharedSecretError = 0x0112, }; STUNmessage() { } STUNmessage(MsgType newType, const BYTE * id = NULL) : PBYTEArray(sizeof(STUNmessageHeader)) { SetType(newType, id); } void SetType(MsgType newType, const BYTE * id = NULL) { SetMinSize(sizeof(STUNmessageHeader)); STUNmessageHeader * hdr = (STUNmessageHeader *)theArray; hdr->msgType = (WORD)newType; for (PINDEX i = 0; i < ((PINDEX)sizeof(hdr->transactionId)); i++) hdr->transactionId[i] = id != NULL ? id[i] : (BYTE)PRandom::Number(); } const STUNmessageHeader * operator->() const { return (STUNmessageHeader *)theArray; } // ignore overflow warning when comparing length #if (!_WIN32) && (GCC_VERSION >= 40400) #pragma GCC diagnostic ignored "-Wstrict-overflow" #endif STUNattribute * GetFirstAttribute() { int length = ((STUNmessageHeader *)theArray)->msgLength; if (theArray == NULL || length < (int) sizeof(STUNmessageHeader)) return NULL; STUNattribute * attr = (STUNattribute *)(theArray+sizeof(STUNmessageHeader)); STUNattribute * ptr = attr; if (attr->length > GetSize() || attr->type >= STUNattribute::MaxValidCode) return NULL; while (ptr && (BYTE*) ptr < (BYTE*)(theArray+GetSize()) && length >= (int) ptr->length+4) { length -= ptr->length + 4; ptr = ptr->GetNext(); } if (length != 0) return NULL; return attr; } bool Validate() { int length = ((STUNmessageHeader *)theArray)->msgLength; STUNattribute * attrib = GetFirstAttribute(); while (attrib && length > 0) { length -= attrib->length + 4; attrib = attrib->GetNext(); } return length == 0; // Exactly correct length } void AddAttribute(const STUNattribute & attribute) { STUNmessageHeader * hdr = (STUNmessageHeader *)theArray; int oldLength = hdr->msgLength; int attrSize = attribute.length + 4; int newLength = oldLength + attrSize; hdr->msgLength = (WORD)newLength; // hdr pointer may be invalidated by next statement SetMinSize(newLength+sizeof(STUNmessageHeader)); memcpy(theArray+sizeof(STUNmessageHeader)+oldLength, &attribute, attrSize); } void SetAttribute(const STUNattribute & attribute) { int length = ((STUNmessageHeader *)theArray)->msgLength; STUNattribute * attrib = GetFirstAttribute(); while (length > 0) { if (attrib->type == attribute.type) { if (attrib->length == attribute.length) *attrib = attribute; else { // More here } return; } length -= attrib->length + 4; attrib = attrib->GetNext(); } AddAttribute(attribute); } STUNattribute * FindAttribute(STUNattribute::Types type) { int length = ((STUNmessageHeader *)theArray)->msgLength; STUNattribute * attrib = GetFirstAttribute(); while (length > 0) { if (attrib->type == type) return attrib; length -= attrib->length + 4; attrib = attrib->GetNext(); } return NULL; } bool Read(UDPSocket & socket) { if (!socket.Read(GetPointer(1000), 1000)) return false; SetSize(socket.GetLastReadCount()); return true; } bool Write(UDPSocket & socket) const { return socket.Write(theArray, ((STUNmessageHeader *)theArray)->msgLength+sizeof(STUNmessageHeader)) != FALSE; } bool Poll(UDPSocket & socket, const STUNmessage & request, PINDEX pollRetries) { for (PINDEX retry = 0; retry < pollRetries; retry++) { if (!request.Write(socket)) break; if (Read(socket) && Validate() && memcmp(request->transactionId, (*this)->transactionId, sizeof(request->transactionId)) == 0) return true; } return false; } }; // class STUNsocket : public UDPProxySocket { public: STUNsocket(const char * t, const H225_CallIdentifier & id); virtual PBoolean GetLocalAddress(PIPSocket::Address &); virtual PBoolean GetLocalAddress(PIPSocket::Address &, WORD &); PIPSocket::Address externalIP; }; STUNsocket::STUNsocket(const char * t, const H225_CallIdentifier & id) : UDPProxySocket(t,id), externalIP(0) { } PBoolean STUNsocket::GetLocalAddress(PIPSocket::Address & addr) { if (!externalIP.IsValid()) return UDPSocket::GetLocalAddress(addr); addr = externalIP; return true; } PBoolean STUNsocket::GetLocalAddress(PIPSocket::Address & addr, WORD & port) { if (!externalIP.IsValid()) return UDPSocket::GetLocalAddress(addr, port); addr = externalIP; port = GetPort(); return true; } ////// struct STUNportRange { STUNportRange() : minport(0), maxport(0) {} void LoadConfig(const char *, const char *, const char * = ""); WORD minport, maxport; }; void STUNportRange::LoadConfig(const char *sec, const char *setting, const char *def) { PStringArray cfgs = GkConfig()->GetString(sec, setting, def).Tokenise(",.:-/'", FALSE); if (cfgs.GetSize() >= 2) { minport = (WORD)cfgs[0].AsUnsigned(); maxport = (WORD)cfgs[1].AsUnsigned(); } PTRACE(3, "STUN\tPort range set " << ": " << minport << '-' << maxport); } ////// class STUNClient : public Job, public PSTUNClient { public: STUNClient(GkClient * _client, const H323TransportAddress &); virtual ~STUNClient(); virtual void Stop(); virtual void Run(); virtual bool CreateSocketPair( const H225_CallIdentifier & id, UDPProxySocket * & rtp, UDPProxySocket * & rtcp, const PIPSocket::Address & binding = PIPSocket::GetDefaultIpAny() ); protected: bool OpenSocketA(UDPSocket & socket, PortInfo & portInfo, const PIPSocket::Address & binding); private: // override from class Task virtual void Exec(); // Callback void OnDetectedNAT(int m_nattype); GkClient * m_client; NatTypes m_nattype; bool m_shutdown; PMutex m_portCreateMutex; int m_socketsForPairing; int m_pollRetries; }; STUNClient::STUNClient(GkClient * _client, const H323TransportAddress & addr) : m_client(_client), m_nattype(UnknownNat), m_shutdown(false), m_socketsForPairing(4), m_pollRetries(3) { PIPSocket::Address ip; WORD port; addr.GetIpAndPort(ip,port); #ifdef hasNewSTUN m_serverAddress = PIPSocketAddressAndPort(ip, port); #else SetServer(ip, port); #endif STUNportRange ports; ports.LoadConfig("Proxy", "RTPPortRange", "1024-65535"); SetPortRanges(ports.minport, ports.maxport, ports.minport, ports.maxport); SetName("STUNClient"); Execute(); } STUNClient::~STUNClient() { Stop(); } void STUNClient::Stop() { Job::Stop(); // disconnect from STUN Server m_shutdown = true; } void STUNClient::Run() { Exec(); } void STUNClient::Exec() { ReadLock lockConfig(ConfigReloadMutex); // Wait 500 ms until the RCF has been processed before running tests // to prevent blocking. PThread::Sleep(500); // Get a valid NAT type.... m_nattype = GetNatType(TRUE); OnDetectedNAT(m_nattype); ReadUnlock unlockConfig(ConfigReloadMutex); // make sure the STUN client doesn't permanently hog the mutex // Keep this job (thread) open so that creating STUN ports does not hold up // the processing of other calls while (!m_shutdown) { PThread::Sleep(100); } } void STUNClient::OnDetectedNAT(int nattype) { PTRACE(3, "STUN\tDetected NAT as type " << nattype << " " << GetNatTypeString((NatTypes)nattype)); // Call back to signal the GKClient to do a lightweight reregister // to notify the gatekeeper m_client->H46023_TypeDetected(nattype); } #ifdef hasNewSTUN bool STUNClient::OpenSocketA(UDPSocket & socket, PortInfo & portInfo, const PIPSocket::Address & binding) { if (!m_serverAddress.IsValid()) { PTRACE(1, "STUN\tServer port not set."); return false; } if (portInfo.basePort == 0) { if (!socket.Listen(binding, 1)) { PTRACE(3, "STUN\tCannot bind port to " << m_interface); return false; } } else { WORD startPort = portInfo.currentPort; PTRACE(3, "STUN\tUsing ports " << portInfo.basePort << " through " << portInfo.maxPort << " starting at " << startPort); for (;;) { bool status = socket.Listen(binding, 1, portInfo.currentPort); PWaitAndSignal mutex(portInfo.mutex); portInfo.currentPort++; if (portInfo.currentPort > portInfo.maxPort) portInfo.currentPort = portInfo.basePort; if (status) break; if (portInfo.currentPort == startPort) { PTRACE(3, "STUN\tListen failed on " << AsString(m_interface, portInfo.currentPort)); SNMP_TRAP(7, SNMPError, Network, "STUN failure"); return false; } } } socket.SetSendAddress(m_serverAddress.GetAddress(), m_serverAddress.GetPort()); return true; } #else bool STUNClient::OpenSocketA(UDPSocket & socket, PortInfo & portInfo, const PIPSocket::Address & binding) { if (serverPort == 0) { PTRACE(1, "STUN\tServer port not set."); return false; } if (!PIPSocket::GetHostAddress(serverHost, cachedServerAddress) || !cachedServerAddress.IsValid()) { PTRACE(2, "STUN\tCould not find host \"" << serverHost << "\"."); return false; } PWaitAndSignal mutex(portInfo.mutex); WORD startPort = portInfo.currentPort; do { portInfo.currentPort++; if (portInfo.currentPort > portInfo.maxPort) portInfo.currentPort = portInfo.basePort; if (socket.Listen(binding, 1, portInfo.currentPort)) { socket.SetSendAddress(cachedServerAddress, serverPort); socket.SetReadTimeout(replyTimeout); return true; } } while (portInfo.currentPort != startPort); PTRACE(1, "STUN\tFailed to bind to local UDP port in range " << portInfo.currentPort << '-' << portInfo.maxPort); SNMP_TRAP(7, SNMPError, Network, "STUN failure"); return false; } #endif bool STUNClient::CreateSocketPair(const H225_CallIdentifier & id, UDPProxySocket * & rtp, UDPProxySocket * & rtcp, const PIPSocket::Address & binding) { // We only create port pairs, a pair at a time. PWaitAndSignal m(m_portCreateMutex); rtp = NULL; rtcp = NULL; if (GetNatType(FALSE) != ConeNat) { PTRACE(1, "STUN\tCannot create socket pair using NAT type " << GetNatTypeName()); return FALSE; } PINDEX i; PList stunSocket; PList request; PList response; for (i = 0; i < m_socketsForPairing; i++) { PString t = (i%2 == 0 ? "rtp" : "rtcp"); PINDEX idx = stunSocket.Append(new STUNsocket(t,id)); if (!OpenSocketA(stunSocket[idx], pairedPortInfo, binding)) { PTRACE(1, "STUN\tUnable to open socket to server " << GetServer()); return false; } idx = request.Append(new STUNmessage(STUNmessage::BindingRequest)); request[idx].AddAttribute(STUNchangeRequest(false, false)); response.Append(new STUNmessage); } for (i = 0; i < m_socketsForPairing; i++) { if (!response[i].Poll(stunSocket[i], request[i], m_pollRetries)) { PTRACE(1, "STUN\tServer unexpectedly went offline." << GetServer()); return false; } } for (i = 0; i < m_socketsForPairing; i++) { STUNmappedAddress * mappedAddress = (STUNmappedAddress *)response[i].FindAttribute(STUNattribute::MAPPED_ADDRESS); if (mappedAddress == NULL) { PTRACE(2, "STUN\tExpected mapped address attribute from server " << GetServer()); return false; } if (GetNatType(FALSE) != SymmetricNat) stunSocket[i].SetPort(mappedAddress->port); stunSocket[i].externalIP = mappedAddress->GetIP(); } for (i = 0; i < m_socketsForPairing; i++) { for (PINDEX j = 0; j < m_socketsForPairing; j++) { if ((stunSocket[i].GetPort()&1) == 0 && (stunSocket[i].GetPort()+1) == stunSocket[j].GetPort()) { stunSocket[i].SetSendAddress(0, 0); stunSocket[i].SetReadTimeout(PMaxTimeInterval); stunSocket[j].SetSendAddress(0, 0); stunSocket[j].SetReadTimeout(PMaxTimeInterval); rtp = &stunSocket[i]; rtcp = &stunSocket[j]; stunSocket.DisallowDeleteObjects(); stunSocket.Remove(rtp); stunSocket.Remove(rtcp); stunSocket.AllowDeleteObjects(); return true; } } } PTRACE(2, "STUN\tCould not get a pair of adjacent port numbers from NAT"); return false; } ///////////////////////////////////////////////////////////////////// class GkClient; class H46024Socket : public UDPProxySocket { public: H46024Socket(GkClient * client, bool rtp, const H225_CallIdentifier & id, CallRec::NatStrategy strategy, WORD sessionID); enum probe_state { e_notRequired, ///< Polling has not started e_initialising, ///< We are initialising (local set but remote not) e_idle, ///< Idle (waiting for first packet from remote) e_probing, ///< Probing for direct route e_verify_receiver, ///< verified receive connectivity e_verify_sender, ///< verified send connectivity e_wait, ///< we are waiting for direct media (to set address) e_direct ///< we are going direct to detected address }; struct probe_packet { PUInt16b Length; // Length PUInt32b SSRC; // Time Stamp BYTE name[4]; // Name is limited to 32 (4 Bytes) BYTE cui[20]; // SHA-1 is always 160 (20 Bytes) }; virtual bool OnReceiveData(void *, PINDEX, Address &, WORD &); PBoolean SendRTCPFrame(RTP_ControlFrame & report, const PIPSocket::Address & ip, WORD port, unsigned id); virtual PBoolean WriteTo(const void * buf, PINDEX len, const Address & addr, WORD port); virtual PBoolean WriteTo(const void * buf, PINDEX len, const Address & addr, WORD port, unsigned id); #ifdef HAS_H46019CM PBoolean WriteSocket(const void * buf, PINDEX len, const Address & addr, WORD port, unsigned altMux = 0); #endif void SetAlternateAddresses(const H323TransportAddress & address, const PString & cui, unsigned muxID); void GetAlternateAddresses(H323TransportAddress & address, PString & cui, unsigned & muxID); // Annex A PBoolean ReceivedProbePacket(const RTP_ControlFrame & frame, bool & probe, bool & success); void BuildProbe(RTP_ControlFrame & report, bool reply); void StartProbe(); void ProbeReceived(bool probe, const PIPSocket::Address & addr, WORD & port); void SetProbeState(probe_state newstate); int GetProbeState() const; void SignalH46024Adirect(); // Annex B void H46024Bdirect(const H323TransportAddress & address, unsigned muxID); void SendRTPPing(const PIPSocket::Address & ip, const WORD & port, unsigned id); private: GkClient * m_client; CallRec::NatStrategy m_natStrategy; WORD m_sessionID; H225_CallIdentifier m_callIdentifier; bool m_rtp; PMutex probeMutex; probe_state m_state; // Addresses PString m_CUIlocal; ///< Local CUI PString m_CUIremote; ///< Remote CUI PIPSocket::Address m_locAddr; ///< local Address (address used when starting socket) PIPSocket::Address m_remAddr; WORD m_remPort; ///< Remote Address (address used when starting socket) PIPSocket::Address m_detAddr; WORD m_detPort; ///< detected remote Address (as detected from actual packets) PIPSocket::Address m_pendAddr; WORD m_pendPort; ///< detected pending RTCP Probe Address (as detected from actual packets) PIPSocket::Address m_altAddr; WORD m_altPort; ///< supplied remote Address (as supplied in Generic Information) unsigned m_altMuxID; // Probes PDECLARE_NOTIFIER(PTimer, H46024Socket, Probe); ///< Thread to probe for direct connection PTimer m_Probe; ///< Probe Timer PINDEX m_probes; ///< Probe count DWORD SSRC; ///< Random number // Annex B Probes WORD m_keepseqno; ///< Probe sequence number PTime m_keepStartTime; ///< Probe start time for TimeStamp. }; H46024Socket::H46024Socket(GkClient * client, bool rtp, const H225_CallIdentifier & id, CallRec::NatStrategy strategy, WORD sessionID) :UDPProxySocket((rtp ? "rtp" : "rtcp"), id), m_client(client), m_natStrategy(strategy), m_sessionID(sessionID), m_callIdentifier(id), m_rtp(rtp), m_state(e_notRequired), m_remPort(0), m_detPort(0), m_pendPort(0), m_altPort(0), m_altMuxID(0), m_probes(0), SSRC(0), m_keepseqno(100) { } PBoolean H46024Socket::ReceivedProbePacket(const RTP_ControlFrame & frame, bool & probe, bool & success) { success = false; //Inspect the probe packet if (frame.GetPayloadType() != RTP_ControlFrame::e_ApplDefined) return false; int cstate = GetProbeState(); if (cstate == e_notRequired) { PTRACE(6, "H46024A\ts:" << m_sessionID << " received RTCP probe packet. LOGIC ERROR!"); return false; } if (cstate > e_probing) { PTRACE(6, "H46024A\ts:" << m_sessionID << " received RTCP probe packet. IGNORING! Already authenticated."); return false; } probe = (frame.GetCount() > 0); PTRACE(4, "H46024A\ts:" << m_sessionID << " RTCP Probe " << (probe ? "Reply" : "Request") << " received."); #ifdef P_SSL BYTE * data = frame.GetPayloadPtr(); PBYTEArray bytes(20); memcpy(bytes.GetPointer(),data+12, 20); PMessageDigest::Result bin_digest; PMessageDigestSHA1::Encode(OpalGloballyUniqueID(m_callIdentifier.m_guid).AsString() + m_CUIlocal, bin_digest); PBYTEArray val(bin_digest.GetPointer(),bin_digest.GetSize()); if (bytes == val) { if (probe) // We have a reply SetProbeState(e_verify_sender); else SetProbeState(e_verify_receiver); m_Probe.Stop(); PTRACE(4, "H46024A\ts" << m_sessionID << " RTCP Probe " << (probe ? "Reply" : "Request") << " verified."); if (!m_CUIremote.IsEmpty()) success = true; else { PTRACE(4, "H46024A\ts" << m_sessionID << " Remote not ready."); } } else { PTRACE(4, "H46024A\ts" << m_sessionID << " RTCP Probe " << (probe ? "Reply" : "Request") << " verify FAILURE"); } return true; #else return false; #endif } bool H46024Socket::OnReceiveData(void * data, PINDEX datalen, Address & ipAddress, WORD & ipPort) { if (m_natStrategy != CallRec::e_natAnnexA && m_natStrategy != CallRec::e_natAnnexB) return true; int state = GetProbeState(); if (state == e_notRequired || state == e_direct) return true; // We intercept any prob packets here. if (m_natStrategy == CallRec::e_natAnnexB && ipAddress == m_altAddr && port == m_altPort) { PTRACE(4, "H46024B\ts:" << m_sessionID << " " << Type() << " Switching to " << ipAddress << ":" << port << " from " << m_remAddr << ":" << m_remPort); m_detAddr = ipAddress; m_detPort = ipPort; SetProbeState(e_direct); return false; } /// Check the probe state switch (state) { case e_initialising: // RTCP only case e_idle: // RTCP only case e_probing: // RTCP only case e_verify_receiver: // RTCP only { bool probe = false; bool success = false; RTP_ControlFrame frame(datalen); memcpy(frame.GetPointer(),data,datalen); if (ReceivedProbePacket(frame,probe,success)) { if (success) ProbeReceived(probe,ipAddress,ipPort); else { m_pendAddr = ipAddress; m_pendPort = ipPort; } return false; // don't forward on probe packets. } } break; case e_wait: if ((ipAddress == m_altAddr) && (ipPort == m_altPort)) { PTRACE(4, "H46024A\ts:" << m_sessionID << " " << Type() << " Already sending direct!"); m_detAddr = ipAddress; m_detPort = ipPort; SetProbeState(e_direct); return false; // don't forward on probe packets. } else if ((ipAddress == m_pendAddr) && (ipPort == m_pendPort)) { PTRACE(4, "H46024A\ts:" << m_sessionID << " " << Type() << " Switching to Direct " << ipAddress << ":" << ipPort); m_detAddr = ipAddress; m_detPort = ipPort; SetProbeState(e_direct); return false; // don't forward on probe packets. } else if ((ipAddress != m_remAddr) || (ipPort != m_remPort)) { PTRACE(4, "H46024A\ts:" << m_sessionID << " " << Type() << " Switching to " << ipAddress << ":" << ipPort << " from " << m_remAddr << ":" << m_remPort); m_detAddr = ipAddress; m_detPort = ipPort; SetProbeState(e_direct); return false; // don't forward on probe packets. } break; default: break; } return true; } PBoolean H46024Socket::WriteTo(const void * buf, PINDEX len, const Address & addr, WORD port) { return WriteTo(buf, len, addr, port, 0); } PBoolean H46024Socket::WriteTo(const void * buf, PINDEX len, const Address & addr, WORD port, unsigned id) { #if defined(H323_H46024A) || defined(H323_H46024B) if (GetProbeState() == e_direct) #ifdef HAS_H46019CM return WriteSocket(buf,len, m_detAddr, m_detPort, m_altMuxID); #else return UDPProxySocket::WriteTo(buf,len, m_detAddr, m_detPort); #endif // H46019CM else #endif // H46024A/B #ifdef HAS_H46019CM return WriteSocket(buf,len, addr, port, id); #else return UDPProxySocket::WriteTo(buf,len, addr, port); #endif // HAS_H46019CM } #ifdef HAS_H46019CM PBoolean H46024Socket::WriteSocket(const void * buf, PINDEX len, const Address & addr, WORD port, unsigned altMux) { unsigned mux = m_sendMultiplexID; if (altMux) mux = altMux; if (!PNatMethod_H46019::IsMultiplexed() && !mux) // No Multiplex Rec'v or Send return UDPProxySocket::WriteTo(buf,len, addr, port); else { #ifdef H323_H46024A if (m_remAddr.IsAny()) { m_remAddr = addr; m_remPort = port; } #endif PUDPSocket * muxSocket = PNatMethod_H46019::GetMultiplexSocket(rtpSocket); if (muxSocket && !mux) // Rec'v Multiplex return muxSocket->WriteTo(buf,len, addr, port); RTP_MultiDataFrame frame(mux,(const BYTE *)buf,len); if (!muxSocket) // Send Multiplex return UDPProxySocket::WriteTo(frame.GetPointer(), frame.GetSize(), addr, port); else // Send & Rec'v Multiplexed return muxSocket->WriteTo(frame.GetPointer(), frame.GetSize(), addr, port); } } #endif void H46024Socket::SetAlternateAddresses(const H323TransportAddress & address, const PString & cui, unsigned muxID) { address.GetIpAndPort(m_altAddr,m_altPort); PTRACE(6, "H46024A\ts: " << m_sessionID << (m_rtp ? " RTP " : " RTCP ") << "Remote Alt: " << m_altAddr << ":" << m_altPort << " CUI: " << cui); if (!m_rtp) { m_CUIremote = cui; if (GetProbeState() < e_idle) { SetProbeState(e_idle); StartProbe(); // We Already have a direct connection but we are waiting on the CUI for the reply } else if (GetProbeState() == e_verify_receiver) ProbeReceived(false,m_pendAddr,m_pendPort); } } #define H46024A_MAX_PROBE_COUNT 15 #define H46024A_PROBE_INTERVAL 200 void H46024Socket::StartProbe() { PTRACE(4, "H46024A\ts: " << m_sessionID << " Starting direct connection probe."); SetProbeState(e_probing); m_probes = 0; m_Probe.SetNotifier(PCREATE_NOTIFIER(Probe)); m_Probe.RunContinuous(H46024A_PROBE_INTERVAL); } void H46024Socket::BuildProbe(RTP_ControlFrame & report, bool probing) { #ifdef P_SSL report.SetPayloadType(RTP_ControlFrame::e_ApplDefined); report.SetCount((probing ? 0 : 1)); // SubType Probe report.SetPayloadSize(sizeof(probe_packet)); probe_packet data; data.SSRC = SSRC; data.Length = sizeof(probe_packet); PString id = "24.1"; PBYTEArray bytes(id,id.GetLength(), false); memcpy(&data.name[0], bytes, 4); PString m_callId = OpalGloballyUniqueID(m_callIdentifier.m_guid).AsString(); PMessageDigest::Result bin_digest; PMessageDigestSHA1::Encode(m_callId + m_CUIremote, bin_digest); memcpy(&data.cui[0], bin_digest.GetPointer(), bin_digest.GetSize()); memcpy(report.GetPayloadPtr(),&data,sizeof(probe_packet)); #endif } void H46024Socket::Probe(PTimer &, INT) { m_probes++; if (m_probes > H46024A_MAX_PROBE_COUNT) { m_Probe.Stop(); return; } if (GetProbeState() != e_probing) return; RTP_ControlFrame report; report.SetSize(4+sizeof(probe_packet)); BuildProbe(report, true); if (SendRTCPFrame(report, m_altAddr, m_altPort, m_altMuxID)) { PTRACE(6, "H46024A\ts" << m_sessionID <<" RTCP Probe sent: " << m_altAddr << ":" << m_altPort); } } PBoolean H46024Socket::SendRTCPFrame(RTP_ControlFrame & report, const PIPSocket::Address & ip, WORD port, unsigned id) { if (!WriteTo(report.GetPointer(),report.GetSize(), ip, port,id)) { switch (GetErrorNumber(PChannel::LastWriteError)) { case ECONNRESET : case ECONNREFUSED : PTRACE(2, "H46024\t" << ip << ":" << port << " not ready."); break; default: PTRACE(1, "H46024\t" << ip << ":" << port << ", Write error on port (" << GetErrorNumber(PChannel::LastWriteError) << "): " << GetErrorText(PChannel::LastWriteError)); } return false; } return true; } void H46024Socket::ProbeReceived(bool probe, const PIPSocket::Address & addr, WORD & port) { if (probe) { SignalH46024Adirect(); //< Signal direct } else { RTP_ControlFrame reply; reply.SetSize(4+sizeof(probe_packet)); BuildProbe(reply, false); if (SendRTCPFrame(reply,addr,port,m_altMuxID)) { PTRACE(4, "H46024\tRTCP Reply packet sent: " << addr << ":" << port); } } } void H46024Socket::SignalH46024Adirect() { #ifdef HAS_H46024A callptr call = CallTable::Instance()->FindCallRec(m_callIdentifier); if (call) call->H46024AMessage(); #endif } void H46024Socket::GetAlternateAddresses(H323TransportAddress & address, PString & cui, unsigned & muxID) { PIPSocket::Address tempAddr; WORD tempPort; if (GetLocalAddress(tempAddr, tempPort)) address = H323TransportAddress(tempAddr, tempPort); if (!m_rtp) cui = m_CUIlocal; else cui = PString(); if (GetProbeState() < e_idle) SetProbeState(e_initialising); PTRACE(6, "H46024A\ts:" << m_sessionID << (m_rtp ? " RTP " : " RTCP ") << " Alt:" << address << " CUI " << cui); } void H46024Socket::SetProbeState(probe_state newstate) { PWaitAndSignal m(probeMutex); PTRACE(4, "H46024\tChanging state for " << m_sessionID << " from " << m_state << " to " << newstate); m_state = newstate; } int H46024Socket::GetProbeState() const { PWaitAndSignal m(probeMutex); return m_state; } void H46024Socket::H46024Bdirect(const H323TransportAddress & address, unsigned muxID) { if (GetProbeState() == e_direct) // We might already be doing annex A return; address.GetIpAndPort(m_altAddr,m_altPort); m_altMuxID = muxID; PTRACE(6,"H46024b\ts: " << m_sessionID << " RTP Remote Alt: " << m_altAddr << ":" << m_altPort << " " << m_altMuxID); // Sending an empty RTP frame to the alternate address // will add a mapping to the router to receive RTP from // the remote for (PINDEX i=0; i<3; i++) { SendRTPPing(m_altAddr, m_altPort, m_altMuxID); PThread::Sleep(10); } } void H46024Socket::SendRTPPing(const PIPSocket::Address & ip, const WORD & port, unsigned id) { RTP_DataFrame rtp; rtp.SetSequenceNumber(m_keepseqno); rtp.SetPayloadType((RTP_DataFrame::PayloadTypes)GNUGK_KEEPALIVE_RTP_PAYLOADTYPE); rtp.SetPayloadSize(0); // determining correct timestamp PTimeInterval timePassed = PTime() - m_keepStartTime; rtp.SetTimestamp((DWORD)timePassed.GetMilliSeconds() * 8); rtp.SetMarker(TRUE); if (!WriteTo(rtp.GetPointer(), rtp.GetHeaderSize()+rtp.GetPayloadSize(), ip, port,id)) { switch (GetErrorNumber(PChannel::LastWriteError)) { case ECONNRESET : case ECONNREFUSED : PTRACE(2, "H46024b\t" << ip << ":" << port << " not ready."); break; default: PTRACE(1, "H46024b\t" << ip << ":" << port << ", Write error on port (" << GetErrorNumber(PChannel::LastWriteError) << "): " << GetErrorText(PChannel::LastWriteError)); } } else { PTRACE(6, "H46024b\tRTP KeepAlive sent: " << ip << ":" << port << " " << id << " seq: " << m_keepseqno); m_keepseqno++; } } CallH46024Sockets::CallH46024Sockets(unsigned strategy) : m_natStrategy(strategy), m_sessionID(0), m_rtpSocket(NULL), m_rtcpSocket(NULL) { } CallH46024Sockets::CallH46024Sockets(WORD sessionID, UDPProxySocket * rtp, UDPProxySocket * rtcp) : m_natStrategy(0), m_sessionID(sessionID), m_rtpSocket(rtp), m_rtcpSocket(rtcp) { } CallH46024Sockets::~CallH46024Sockets() { } void CallH46024Sockets::SetAlternate(PString cui, unsigned muxID, H323TransportAddress m_rtp, H323TransportAddress m_rtcp) { if (m_rtpSocket) ((H46024Socket*)m_rtpSocket)->SetAlternateAddresses(m_rtp,cui,muxID); if (m_rtcpSocket) ((H46024Socket*)m_rtcpSocket)->SetAlternateAddresses(m_rtcp,cui,muxID); } void CallH46024Sockets::SetAlternate(const H46024B_AlternateAddress & alternate) { int muxID = 0; if (alternate.HasOptionalField(H46024B_AlternateAddress::e_multiplexID)) muxID = alternate.m_multiplexID; if (m_rtpSocket && alternate.HasOptionalField(H46024B_AlternateAddress::e_rtpAddress)) ((H46024Socket*)m_rtpSocket)->H46024Bdirect(H323TransportAddress(alternate.m_rtpAddress), muxID); if (m_rtcpSocket && alternate.HasOptionalField(H46024B_AlternateAddress::e_rtcpAddress)) ((H46024Socket*)m_rtcpSocket)->H46024Bdirect(H323TransportAddress(alternate.m_rtcpAddress), muxID); } void CallH46024Sockets::LoadAlternate(PString & cui, unsigned & muxID, H323TransportAddress & m_rtp, H323TransportAddress & m_rtcp) { if (m_rtpSocket) ((H46024Socket*)m_rtpSocket)->GetAlternateAddresses(m_rtp,cui,muxID); if (m_rtcpSocket) ((H46024Socket*)m_rtcpSocket)->GetAlternateAddresses(m_rtp,cui,muxID); } #endif ///////////////////////////////////////////////////////////////////// class GRQRequester : public RasRequester { public: GRQRequester(const PString & gkid, H225_EndpointType & type); virtual ~GRQRequester(); // override from class RasRequester virtual bool SendRequest(const Address &, WORD, int = 2); H225_RasMessage & GetMessage(); private: // override from class RasHandler virtual bool IsExpected(const RasMsg *) const; H225_RasMessage grq_ras; }; GRQRequester::GRQRequester(const PString & gkid, H225_EndpointType & type) : RasRequester(grq_ras) { grq_ras.SetTag(H225_RasMessage::e_gatekeeperRequest); H225_GatekeeperRequest & grq = grq_ras; grq.m_requestSeqNum = GetSeqNum(); grq.m_protocolIdentifier.SetValue(H225_ProtocolID); if (!Toolkit::AsBool(GkConfig()->GetInteger(EndpointSection, "HideGk", 0))) grq.m_endpointType.IncludeOptionalField(H225_EndpointType::e_gatekeeper); if (type.HasOptionalField(H225_EndpointType::e_terminal)) grq.m_endpointType.IncludeOptionalField(H225_EndpointType::e_terminal); if (type.HasOptionalField(H225_EndpointType::e_gateway)) grq.m_endpointType.IncludeOptionalField(H225_EndpointType::e_gateway); grq.IncludeOptionalField(H225_GatekeeperRequest::e_supportsAltGK); grq.IncludeOptionalField(H225_GatekeeperRequest::e_supportsAssignedGK); if (!gkid) { grq.IncludeOptionalField(H225_GatekeeperRequest::e_gatekeeperIdentifier); grq.m_gatekeeperIdentifier = gkid; } grq.IncludeOptionalField(H225_GatekeeperRequest::e_authenticationCapability); grq.IncludeOptionalField(H225_GatekeeperRequest::e_algorithmOIDs); H235AuthSimpleMD5 md5auth; md5auth.SetPassword("dummy"); // activate it md5auth.SetCapability(grq.m_authenticationCapability, grq.m_algorithmOIDs); H235AuthCAT catauth; catauth.SetPassword("dummy"); // activate it catauth.SetCapability(grq.m_authenticationCapability, grq.m_algorithmOIDs); m_rasSrv->RegisterHandler(this); } H225_RasMessage & GRQRequester::GetMessage() { return grq_ras; } GRQRequester::~GRQRequester() { m_rasSrv->UnregisterHandler(this); } bool GRQRequester::SendRequest(const Address & addr, WORD pt, int r) { m_txAddr = addr, m_txPort = pt, m_retry = r; H225_GatekeeperRequest & grq = grq_ras; vector
GKHome; Toolkit::Instance()->GetGKHome(GKHome); for (std::vector
::iterator i = GKHome.begin(); i != GKHome.end(); ++i) { if ((IsLoopback(addr) || IsLoopback(*i)) && addr != *i) continue; if (addr.GetVersion() != i->GetVersion()) continue; GkInterface * inter = m_rasSrv->SelectInterface(*i); if (inter == NULL) return false; RasListener *socket = inter->GetRasListener(); grq.m_rasAddress = socket->GetRasAddress(addr == INADDR_BROADCAST ? *i : addr); if (addr == INADDR_BROADCAST) socket->SetOption(SO_BROADCAST, 1); socket->SendRas(grq_ras, addr, pt); if (addr == INADDR_BROADCAST) socket->SetOption(SO_BROADCAST, 0); } m_sentTime = PTime(); return true; } bool GRQRequester::IsExpected(const RasMsg *ras) const { if (ras->GetSeqNum() == GetSeqNum()) { if (ras->GetTag() == H225_RasMessage::e_gatekeeperRequest) { return m_txAddr == INADDR_BROADCAST; // catch broadcasted GRQ to avoid loop } else if (ras->GetTag() == H225_RasMessage::e_gatekeeperConfirm || ras->GetTag() == H225_RasMessage::e_gatekeeperReject) { return (m_txAddr == INADDR_BROADCAST) ? true : ras->IsFrom(m_txAddr, m_txPort); } } return false; } // handler to process requests to GkClient class GkClientHandler : public RasHandler { public: typedef bool (GkClient::*Handler)(RasMsg *); GkClientHandler(GkClient *c, Handler h, unsigned t) : client(c), handlePDU(h), tag(t) {} private: // override from class RasHandler virtual bool IsExpected(const RasMsg *ras) const; virtual void Process(RasMsg *); void OnRequest(RasMsg *ras); GkClient *client; Handler handlePDU; unsigned tag; }; bool GkClientHandler::IsExpected(const RasMsg * ras) const { return (ras->GetTag() == tag) && client->CheckFrom(ras); } void GkClientHandler::Process(RasMsg *ras) { CreateJob(this, &GkClientHandler::OnRequest, ras, ras->GetTagName()); } void GkClientHandler::OnRequest(RasMsg * ras) { ReadLock lockConfig(ConfigReloadMutex); if ((client->*handlePDU)(ras)) ras->Reply(); delete ras; } namespace { const long DEFAULT_TTL = 60; const long DEFAULT_RRQ_RETRY = 3; } // class GkClient GkClient::GkClient() : m_rasSrv(RasServer::Instance()), m_registered(false), m_discoveryComplete(false), m_useAdditiveRegistration(false), m_ttl(GkConfig()->GetInteger(EndpointSection, "TimeToLive", DEFAULT_TTL)), m_timer(0), m_retry(GkConfig()->GetInteger(EndpointSection, "RRQRetryInterval", DEFAULT_RRQ_RETRY)), m_authMode(-1), m_rewriteInfo(NULL), m_natClient(NULL), m_parentVendor(ParentVendor_GnuGk), m_endpointType(EndpointType_Gateway), m_discoverParent(true), m_enableH46018(false), m_registeredH46018(false) #ifdef HAS_H46023 , m_nattype(0), m_natnotify(false), m_enableH46023(false), m_registeredH46023(false), m_stunClient(NULL), m_algDetected(false) #endif { m_resend = m_retry; m_gkfailtime = m_retry * 128; m_gkport = 0; m_useAltGKPermanent = false; m_gkList = new AlternateGKs(m_gkaddr, m_gkport); m_handlers[0] = new GkClientHandler(this, &GkClient::OnURQ, H225_RasMessage::e_unregistrationRequest); m_handlers[1] = new GkClientHandler(this, &GkClient::OnBRQ, H225_RasMessage::e_bandwidthRequest); m_handlers[2] = new GkClientHandler(this, &GkClient::OnDRQ, H225_RasMessage::e_disengageRequest); m_handlers[3] = new GkClientHandler(this, &GkClient::OnIRQ, H225_RasMessage::e_infoRequest); m_password = PString::Empty(); m_h235Authenticators = new H235Authenticators(); PFactory::KeyList_T keyList = PFactory::GetKeyList(); PFactory::KeyList_T::const_iterator r; for (r = keyList.begin(); r != keyList.end(); ++r) { H235Authenticator * Auth = PFactory::CreateInstance(*r); m_h235Authenticators->Append(Auth); } } GkClient::~GkClient() { delete m_h235Authenticators; #ifdef HAS_H46023 m_natstrategy.clear(); #endif DeleteObjectsInArray(m_handlers, m_handlers + 4); delete m_gkList; delete m_rewriteInfo; PTRACE(1, "GKC\tDelete GkClient"); } void GkClient::OnReload() { PConfig *cfg = GkConfig(); if (IsRegistered()) { if (Toolkit::AsBool(cfg->GetString(EndpointSection, "UnregisterOnReload", "0"))) { SendURQ(); } else { Unregister(); } } m_password = Toolkit::Instance()->ReadPassword(EndpointSection, "Password"); m_retry = m_resend = cfg->GetInteger(EndpointSection, "RRQRetryInterval", DEFAULT_RRQ_RETRY); m_gkfailtime = m_retry * 128; m_authMode = -1; PCaselessString s = GkConfig()->GetString(EndpointSection, "Vendor", "GnuGk"); if (s.Find(',') != P_MAX_INDEX) s = "Generic"; // only set vendor ID, no extensions if (s == "Generic" || s == "Unknown") m_parentVendor = ParentVendor_Generic; else if (s == "Cisco") m_parentVendor = ParentVendor_Cisco; else m_parentVendor = ParentVendor_GnuGk; s = GkConfig()->GetString(EndpointSection, "Type", "Gateway"); if (s[0] == 't' || s[0] == 'T') { m_endpointType = EndpointType_Terminal; m_prefixes.RemoveAll(); } else { m_endpointType = EndpointType_Gateway; m_prefixes = cfg->GetString(EndpointSection, "Prefix", "").Tokenise(",;", FALSE); } m_discoverParent = Toolkit::AsBool(cfg->GetString(EndpointSection, "Discovery", "1")); m_h323Id = cfg->GetString(EndpointSection, "H323ID", (const char *)Toolkit::GKName()).Tokenise(" ,;\t", FALSE); m_e164 = cfg->GetString(EndpointSection, "E164", "").Tokenise(" ,;\t", FALSE); #ifdef HAS_H46018 m_enableH46018 = Toolkit::AsBool(GkConfig()->GetString(EndpointSection, "EnableH46018", "0")); if (m_enableH46018 && !Toolkit::Instance()->IsH46018Enabled()) { PTRACE(1, "H46018\tWarning: H.460.18 enabled for parent/child, but global H.460.18 switch is OFF"); } #endif #ifdef HAS_H46023 m_enableH46023 = Toolkit::AsBool(GkConfig()->GetString(EndpointSection, "EnableH46023", "0")); if (m_enableH46023 && !Toolkit::Instance()->IsH46023Enabled()) { PTRACE(1, "H46023\tWarning: H.460.23 enabled for parent/child, but global H.460.23 switch is OFF"); } #endif PIPSocket::Address gkaddr = m_gkaddr; WORD gkport = m_gkport; PCaselessString gk(cfg->GetString(EndpointSection, "Gatekeeper", "no")); PStringList gkHost; #if P_DNS if (gk != "no" ) { PString number = "h323:user@" + gk; PStringList str; if (PDNS::LookupSRV(number, "_h323rs._udp.", str)) { PTRACE(5, "EP\t" << str.GetSize() << " h323rs SRV Records found" ); for (PINDEX i = 0; i < str.GetSize(); i++) { PCaselessString newhost = str[i].Right(str[i].GetLength()-5); PTRACE(4, "EP\th323rs SRV record " << newhost ); if (i == 0) gk = newhost; else m_gkList->Set(newhost); } } } #endif if (!IsRegistered()) m_ttl = GkConfig()->GetInteger(EndpointSection, "TimeToLive", DEFAULT_TTL); if (gk == "no") { m_timer = 0; m_rrjReason = PString::Empty(); return; } else if (gk == "auto") { m_gkaddr = INADDR_BROADCAST; m_gkport = GK_DEF_UNICAST_RAS_PORT; m_discoveryComplete = false; } else if (GetTransportAddress(gk, GK_DEF_UNICAST_RAS_PORT, m_gkaddr, m_gkport)) { m_discoveryComplete = (m_gkaddr == gkaddr) && (m_gkport == gkport); } else { // unresolvable? PTRACE(1, "GKC\tWarning: Can't resolve parent GK " << gk); return; } m_timer = 100; delete m_rewriteInfo; m_rewriteInfo = new Toolkit::RewriteData(cfg, RewriteE164Section); } void GkClient::CheckRegistration() { if (m_timer > 0 && (PTime() - m_registeredTime) > m_timer) Register(); } bool GkClient::CheckFrom(const RasMsg *ras) const { return ras->IsFrom(m_gkaddr, m_gkport); } bool GkClient::CheckFrom(const PIPSocket::Address & ip) const { return m_gkaddr == ip; } bool GkClient::CheckFrom(const H225_TransportAddress & addr) const { PIPSocket::Address ip; return GetIPFromTransportAddr(addr, ip) && m_gkaddr == ip; } PString GkClient::GetParent() const { return IsRegistered() ? AsString(m_gkaddr, m_gkport) + '\t' + m_endpointId.GetValue() : "not registered\t" + m_rrjReason; } bool GkClient::OnSendingGRQ(H225_GatekeeperRequest &grq) { if (m_h323Id.GetSize() > 0) { int sz = m_h323Id.GetSize(); grq.IncludeOptionalField(H225_GatekeeperRequest::e_endpointAlias); grq.m_endpointAlias.SetSize(sz); for (PINDEX i = 0; i < sz; ++i) H323SetAliasAddress(m_h323Id[i], grq.m_endpointAlias[i]); } #ifdef HAS_H46018 if (m_enableH46018) { grq.IncludeOptionalField(H225_GatekeeperRequest::e_featureSet); H460_FeatureStd feat = H460_FeatureStd(18); grq.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = grq.m_featureSet.m_supportedFeatures; int sz = desc.GetSize(); desc.SetSize(sz + 1); desc[sz] = feat; } #endif #ifdef HAS_H46023 if (m_enableH46023) { grq.IncludeOptionalField(H225_GatekeeperRequest::e_featureSet); H460_FeatureStd feat = H460_FeatureStd(23); grq.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = grq.m_featureSet.m_supportedFeatures; int sz = desc.GetSize(); desc.SetSize(sz + 1); desc[sz] = feat; } #endif return true; } bool GkClient::OnSendingRRQ(H225_RegistrationRequest &rrq) { if ((m_parentVendor == ParentVendor_GnuGk) && !m_enableH46018) { PIPSocket::Address sigip; if (rrq.m_callSignalAddress.GetSize() > 0 && GetIPFromTransportAddr(rrq.m_callSignalAddress[0], sigip)) { rrq.IncludeOptionalField(H225_RegistrationRequest::e_nonStandardData); rrq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard &t35 = rrq.m_nonStandardData.m_nonStandardIdentifier; t35.m_t35CountryCode = Toolkit::t35cPoland; t35.m_manufacturerCode = Toolkit::t35mGnuGk; t35.m_t35Extension = Toolkit::t35eNATTraversal; rrq.m_nonStandardData.m_data = "IP=" + sigip.AsString(); } } #ifdef HAS_H46018 if (m_enableH46018) { rrq.IncludeOptionalField(H225_RegistrationRequest::e_featureSet); H460_FeatureStd feat = H460_FeatureStd(18); rrq.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = rrq.m_featureSet.m_supportedFeatures; int sz = desc.GetSize(); desc.SetSize(sz + 1); desc[sz] = feat; } #endif #ifdef HAS_H46023 if (m_enableH46023 && (!m_registered || m_registeredH46023)) { bool contents = false; rrq.IncludeOptionalField(H225_RegistrationRequest::e_featureSet); H460_FeatureStd feat = H460_FeatureStd(23); if (!m_registered) { feat.Add(Std23_RemoteNAT,H460_FeatureContent(true)); feat.Add(Std23_AnnexA ,H460_FeatureContent(true)); feat.Add(Std23_AnnexB ,H460_FeatureContent(true)); contents = true; } else if (m_algDetected) { feat.Add(Std23_RemoteNAT,H460_FeatureContent(false)); feat.Add(Std23_NATdet ,H460_FeatureContent(PSTUNClient::OpenNat,8)); m_algDetected = false; contents = true; } else { int natType = 0; if (H46023_TypeNotify(natType)) { feat.Add(Std23_NATdet,H460_FeatureContent(natType,8)); m_natnotify = false; contents = true; } } if (contents) { rrq.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = rrq.m_featureSet.m_supportedFeatures; int sz = desc.GetSize(); desc.SetSize(sz+1); desc[sz] = feat; } } #endif return true; } bool GkClient::OnSendingARQ(H225_AdmissionRequest &arq, Routing::AdmissionRequest & /* req */) { if (m_parentVendor == ParentVendor_Cisco) { Cisco_ARQnonStandardInfo nonStandardData; arq.IncludeOptionalField(H225_AdmissionRequest::e_nonStandardData); arq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard & h221 = arq.m_nonStandardData.m_nonStandardIdentifier; h221.m_manufacturerCode = Toolkit::t35mCisco; h221.m_t35CountryCode = Toolkit::t35cUSA; h221.m_t35Extension = 0; PPER_Stream buff; nonStandardData.Encode(buff); buff.CompleteEncoding(); arq.m_nonStandardData.m_data = buff; } return true; } bool GkClient::OnSendingLRQ(H225_LocationRequest &lrq, Routing::LocationRequest &/*req*/) { if (m_parentVendor == ParentVendor_Cisco) { Cisco_LRQnonStandardInfo nonStandardData; nonStandardData.m_ttl = 6; if (lrq.HasOptionalField(H225_LocationRequest::e_sourceInfo)) { nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_gatewaySrcInfo); nonStandardData.m_gatewaySrcInfo.SetSize(lrq.m_sourceInfo.GetSize()); for (PINDEX i = 0; i < lrq.m_sourceInfo.GetSize(); i++) nonStandardData.m_gatewaySrcInfo[i] = lrq.m_sourceInfo[i]; } else { if (m_h323Id.GetSize() > 0) { nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_gatewaySrcInfo); nonStandardData.m_gatewaySrcInfo.SetSize(m_h323Id.GetSize()); for (PINDEX i = 0; i < m_h323Id.GetSize(); i++) H323SetAliasAddress(m_h323Id[i], nonStandardData.m_gatewaySrcInfo[i], H225_AliasAddress::e_h323_ID); } if (m_e164.GetSize() > 0) { nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_gatewaySrcInfo); PINDEX sz = nonStandardData.m_gatewaySrcInfo.GetSize(); nonStandardData.m_gatewaySrcInfo.SetSize(sz + m_e164.GetSize()); for (PINDEX i = 0; i < m_e164.GetSize(); i++) H323SetAliasAddress(m_e164[i], nonStandardData.m_gatewaySrcInfo[sz + i]); } } lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData); lrq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard & h221 = lrq.m_nonStandardData.m_nonStandardIdentifier; h221.m_manufacturerCode = Toolkit::t35mCisco; h221.m_t35CountryCode = Toolkit::t35cUSA; h221.m_t35Extension = 0; PPER_Stream buff; nonStandardData.Encode(buff); buff.CompleteEncoding(); lrq.m_nonStandardData.m_data = buff; } return true; } bool GkClient::OnSendingARQ(H225_AdmissionRequest &arq, Routing::SetupRequest &req, bool /*answer*/) { if (m_parentVendor == ParentVendor_Cisco) { const Q931 &setup = req.GetWrapper()->GetQ931(); Cisco_ARQnonStandardInfo nonStandardData; if (setup.HasIE(Q931::CallingPartyNumberIE)) { PBYTEArray data = setup.GetIE(Q931::CallingPartyNumberIE); if (data.GetSize() >= 2 && (data[0] & 0x80) == 0x80) { nonStandardData.IncludeOptionalField(Cisco_ARQnonStandardInfo::e_callingOctet3a); nonStandardData.m_callingOctet3a = data[1]; } } arq.IncludeOptionalField(H225_AdmissionRequest::e_nonStandardData); arq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard & h221 = arq.m_nonStandardData.m_nonStandardIdentifier; h221.m_manufacturerCode = Toolkit::t35mCisco; h221.m_t35CountryCode = Toolkit::t35cUSA; h221.m_t35Extension = 0; PPER_Stream buff; nonStandardData.Encode(buff); buff.CompleteEncoding(); arq.m_nonStandardData.m_data = buff; } return true; } bool GkClient::OnSendingARQ(H225_AdmissionRequest &arq, Routing::FacilityRequest &/*req*/) { if (m_parentVendor == ParentVendor_Cisco) { Cisco_ARQnonStandardInfo nonStandardData; arq.IncludeOptionalField(H225_AdmissionRequest::e_nonStandardData); arq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard & h221 = arq.m_nonStandardData.m_nonStandardIdentifier; h221.m_manufacturerCode = Toolkit::t35mCisco; h221.m_t35CountryCode = Toolkit::t35cUSA; h221.m_t35Extension = 0; PPER_Stream buff; nonStandardData.Encode(buff); buff.CompleteEncoding(); arq.m_nonStandardData.m_data = buff; } return true; } bool GkClient::OnSendingDRQ(H225_DisengageRequest &/*drq*/, const callptr &/*call*/) { return true; } bool GkClient::OnSendingURQ(H225_UnregistrationRequest &/*urq*/) { return true; } bool GkClient::SendARQ(Routing::AdmissionRequest & arq_obj) { const H225_AdmissionRequest & oarq = arq_obj.GetRequest(); H225_RasMessage arq_ras; Requester request(arq_ras, m_loaddr); H225_AdmissionRequest & arq = BuildARQ(arq_ras); if (Toolkit::AsBool(GkConfig()->GetString(EndpointSection, "ForwardDestIp", "1"))) { if (oarq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) { arq.IncludeOptionalField(H225_AdmissionRequest::e_destCallSignalAddress); arq.m_destCallSignalAddress = oarq.m_destCallSignalAddress; } } if (oarq.HasOptionalField(H225_AdmissionRequest::e_destinationInfo)) { arq.IncludeOptionalField(H225_AdmissionRequest::e_destinationInfo); arq.m_destinationInfo = oarq.m_destinationInfo; } if (oarq.HasOptionalField(H225_AdmissionRequest::e_destExtraCallInfo)) { arq.IncludeOptionalField(H225_AdmissionRequest::e_destExtraCallInfo); arq.m_destExtraCallInfo = oarq.m_destExtraCallInfo; } arq.m_srcInfo = oarq.m_srcInfo; RewriteE164(arq.m_srcInfo, true); arq.m_bandWidth = oarq.m_bandWidth; arq.m_callReferenceValue = oarq.m_callReferenceValue; arq.m_conferenceID = oarq.m_conferenceID; if (oarq.HasOptionalField(H225_AdmissionRequest::e_callIdentifier)) { arq.IncludeOptionalField(H225_AdmissionRequest::e_callIdentifier); arq.m_callIdentifier = oarq.m_callIdentifier; } #ifdef HAS_H46023 if (m_registeredH46023) { arq.IncludeOptionalField(H225_AdmissionRequest::e_featureSet); H460_FeatureStd feat = H460_FeatureStd(24); arq.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = arq.m_featureSet.m_supportedFeatures; desc.SetSize(1); desc[0] = feat; } #endif return OnSendingARQ(arq, arq_obj) && WaitForACF(arq, request, &arq_obj); } bool GkClient::SendLRQ(Routing::LocationRequest & lrq_obj) { const H225_LocationRequest & olrq = lrq_obj.GetRequest(); H225_RasMessage lrq_ras; Requester request(lrq_ras, m_loaddr); H225_LocationRequest & lrq = lrq_ras; lrq.m_destinationInfo = olrq.m_destinationInfo; lrq.m_replyAddress = m_rasSrv->GetRasAddress(m_loaddr); lrq.IncludeOptionalField(H225_LocationRequest::e_endpointIdentifier); lrq.m_endpointIdentifier = m_endpointId; if (olrq.HasOptionalField(H225_LocationRequest::e_sourceInfo)) { lrq.IncludeOptionalField(H225_LocationRequest::e_sourceInfo); lrq.m_sourceInfo = olrq.m_sourceInfo; RewriteE164(lrq.m_sourceInfo, true); } if (olrq.HasOptionalField(H225_LocationRequest::e_canMapAlias)) { lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias); lrq.m_canMapAlias = olrq.m_canMapAlias; } if (olrq.HasOptionalField(H225_LocationRequest::e_hopCount)) { lrq.IncludeOptionalField(H225_LocationRequest::e_hopCount); lrq.m_hopCount = olrq.m_hopCount; // decrement hopCount, but don't let it go below zero if (lrq.m_hopCount > 0) lrq.m_hopCount = lrq.m_hopCount - 1; } SetNBPassword(lrq); if (!OnSendingLRQ(lrq, lrq_obj)) return false; request.SendRequest(m_gkaddr, m_gkport); if (request.WaitForResponse(5000)) { RasMsg *ras = request.GetReply(); unsigned tag = ras->GetTag(); if (tag == H225_RasMessage::e_locationConfirm) { H225_LocationConfirm & lcf = (*ras)->m_recvRAS; lrq_obj.AddRoute(Route("parent", lcf.m_callSignalAddress)); RasMsg *oras = lrq_obj.GetWrapper(); (*oras)->m_replyRAS.SetTag(H225_RasMessage::e_locationConfirm); H225_LocationConfirm & nlcf = (*oras)->m_replyRAS; if (lcf.HasOptionalField(H225_LocationConfirm::e_cryptoTokens)) { nlcf.IncludeOptionalField(H225_LocationConfirm::e_cryptoTokens); nlcf.m_cryptoTokens = lcf.m_cryptoTokens; } return true; } } return false; } bool GkClient::SendARQ(Routing::SetupRequest & setup_obj, bool answer) { H225_RasMessage arq_ras; Requester request(arq_ras, m_loaddr); H225_AdmissionRequest & arq = BuildARQ(arq_ras); H225_Setup_UUIE & setup = setup_obj.GetRequest(); arq.m_callReferenceValue = setup_obj.GetWrapper()->GetCallReference(); arq.m_conferenceID = setup.m_conferenceID; if (setup.HasOptionalField(H225_Setup_UUIE::e_callIdentifier)) { arq.IncludeOptionalField(H225_AdmissionRequest::e_callIdentifier); arq.m_callIdentifier = setup.m_callIdentifier; } if (setup.HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) { arq.m_srcInfo = setup.m_sourceAddress; if (!answer) RewriteE164(arq.m_srcInfo, true); } else { // no sourceAddress privided in Q.931 Setup? // since srcInfo is mandatory, set my aliases as the srcInfo if (m_h323Id.GetSize() > 0) { arq.m_srcInfo.SetSize(1); H323SetAliasAddress(m_h323Id[0], arq.m_srcInfo[0], H225_AliasAddress::e_h323_ID); } if (m_e164.GetSize() > 0) { PINDEX sz = arq.m_srcInfo.GetSize(); arq.m_srcInfo.SetSize(sz + 1); H323SetAliasAddress(m_e164[0], arq.m_srcInfo[sz]); } } if (setup.HasOptionalField(H225_Setup_UUIE::e_destinationAddress)) { arq.IncludeOptionalField(H225_AdmissionRequest::e_destinationInfo); arq.m_destinationInfo = setup.m_destinationAddress; if (answer) RewriteE164(arq.m_destinationInfo, true); } arq.m_answerCall = answer; // workaround for bandwidth arq.m_bandWidth = 1280; #ifdef HAS_H46023 if (answer && m_registeredH46023) { CallRec::NatStrategy natoffload = H46023_GetNATStategy(setup.m_callIdentifier); arq.IncludeOptionalField(H225_AdmissionRequest::e_featureSet); H460_FeatureStd feat = H460_FeatureStd(24); feat.Add(Std24_NATInstruct,H460_FeatureContent((unsigned)natoffload,8)); arq.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = arq.m_featureSet.m_supportedFeatures; int sz = desc.GetSize(); desc.SetSize(sz+1); desc[sz] = feat; } #endif return OnSendingARQ(arq, setup_obj, answer) && WaitForACF(arq, request, answer ? 0 : &setup_obj); } bool GkClient::SendARQ(Routing::FacilityRequest & facility_obj) { H225_RasMessage arq_ras; Requester request(arq_ras, m_loaddr); H225_AdmissionRequest & arq = BuildARQ(arq_ras); H225_Facility_UUIE & facility = facility_obj.GetRequest(); arq.m_callReferenceValue = facility_obj.GetWrapper()->GetCallReference(); if (facility.HasOptionalField(H225_Facility_UUIE::e_conferenceID)) arq.m_conferenceID = facility.m_conferenceID; if (facility.HasOptionalField(H225_Facility_UUIE::e_callIdentifier)) { arq.IncludeOptionalField(H225_AdmissionRequest::e_callIdentifier); arq.m_callIdentifier = facility.m_callIdentifier; } if (m_h323Id.GetSize() > 0) { arq.m_srcInfo.SetSize(1); H323SetAliasAddress(m_h323Id[0], arq.m_srcInfo[0], H225_AliasAddress::e_h323_ID); } if (m_e164.GetSize() > 0) { PINDEX sz = arq.m_srcInfo.GetSize(); arq.m_srcInfo.SetSize(sz + 1); H323SetAliasAddress(m_e164[0], arq.m_srcInfo[sz]); } if (facility.HasOptionalField(H225_Facility_UUIE::e_alternativeAliasAddress)) { arq.IncludeOptionalField(H225_AdmissionRequest::e_destinationInfo); arq.m_destinationInfo = facility.m_alternativeAliasAddress; } arq.m_answerCall = false; // workaround for bandwidth arq.m_bandWidth = 1280; return OnSendingARQ(arq, facility_obj) && WaitForACF(arq, request, &facility_obj); } void GkClient::SendDRQ(const callptr & call) { H225_RasMessage drq_ras; Requester request(drq_ras, m_loaddr); H225_DisengageRequest & drq = drq_ras; call->BuildDRQ(drq, H225_DisengageReason::e_normalDrop); drq.IncludeOptionalField(H225_DisengageRequest::e_gatekeeperIdentifier); drq.m_gatekeeperIdentifier = m_gatekeeperId; drq.m_endpointIdentifier = m_endpointId; drq.m_answeredCall = !call->GetCallingParty(); SetPassword(drq); if (OnSendingDRQ(drq, call)) { request.SendRequest(m_gkaddr, m_gkport); request.WaitForResponse(3000); } // ignore response } void GkClient::SendURQ() { // the order is important: build URQ, close NAT socket, then send URQ H225_RasMessage urq_ras; urq_ras.SetTag(H225_RasMessage::e_unregistrationRequest); H225_UnregistrationRequest & urq = urq_ras; urq.m_requestSeqNum = m_rasSrv->GetRequestSeqNum(); urq.IncludeOptionalField(H225_UnregistrationRequest::e_gatekeeperIdentifier); urq.m_gatekeeperIdentifier = m_gatekeeperId; urq.IncludeOptionalField(H225_UnregistrationRequest::e_endpointIdentifier); urq.m_endpointIdentifier = m_endpointId; SetCallSignalAddress(urq.m_callSignalAddress); SetPassword(urq); Unregister(); if (OnSendingURQ(urq)) m_rasSrv->SendRas(urq_ras, m_gkaddr, m_gkport, m_loaddr); } bool GkClient::RewriteE164(H225_AliasAddress & alias, bool fromInternal) { if ((alias.GetTag() != H225_AliasAddress::e_dialedDigits) && (alias.GetTag() != H225_AliasAddress::e_h323_ID)) return false; PString e164 = AsString(alias, FALSE); bool changed = RewriteString(e164, fromInternal); if (changed) H323SetAliasAddress(e164, alias); return changed; } bool GkClient::RewriteE164(H225_ArrayOf_AliasAddress & aliases, bool fromInternal) { bool changed = false; for (PINDEX i = 0; i < aliases.GetSize(); ++i) if (RewriteE164(aliases[i], fromInternal)) changed = true; return changed; } bool GkClient::RewriteE164(SetupMsg & setup, bool fromInternal) { Q931 &q931 = setup.GetQ931(); H225_Setup_UUIE &setupBody = setup.GetUUIEBody(); unsigned plan, type; PString number; bool result = false; if (fromInternal) { bool r1 = q931.GetCallingPartyNumber(number, &plan, &type); if (r1 && (result = RewriteString(number, true))) { q931.SetCallingPartyNumber(number, plan, type); setup.SetChanged(); } if ((!r1 || result) && setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) if (RewriteE164(setupBody.m_sourceAddress, true)) { result = true; setup.SetUUIEChanged(); } } else { bool r1 = q931.GetCalledPartyNumber(number, &plan, &type); if (r1 && (result = RewriteString(number, false))) { q931.SetCalledPartyNumber(number, plan, type); setup.SetChanged(); } if ((!r1 || result) && setupBody.HasOptionalField(H225_Setup_UUIE::e_destinationAddress)) if (RewriteE164(setupBody.m_destinationAddress, false)) { result = true; setup.SetUUIEChanged(); } } return result; } bool GkClient::Discovery() { m_loaddr = m_gkaddr; m_gatekeeperId = PString::Empty(); if (!m_discoverParent) return true; H225_EndpointType eptype; if (m_endpointType == EndpointType_Terminal) { eptype.IncludeOptionalField(H225_EndpointType::e_terminal); } else { eptype.IncludeOptionalField(H225_EndpointType::e_gateway); } GRQRequester request(GkConfig()->GetString(EndpointSection, "GatekeeperIdentifier", ""), eptype); OnSendingGRQ(request.GetMessage()); // the spec: timeout value 5 sec, retry count 2 request.SendRequest(m_gkaddr, m_gkport); while (request.WaitForResponse(5000)) { RasMsg *ras = request.GetReply(); if (ras->GetTag() == H225_RasMessage::e_gatekeeperConfirm) { H225_GatekeeperConfirm & gcf = (*ras)->m_recvRAS; m_loaddr = (*ras)->m_localAddr; if (gcf.HasOptionalField(H225_GatekeeperConfirm::e_gatekeeperIdentifier)) m_gatekeeperId = gcf.m_gatekeeperIdentifier; GetIPAndPortFromTransportAddr(gcf.m_rasAddress, m_gkaddr, m_gkport); if (gcf.HasOptionalField(H225_GatekeeperConfirm::e_authenticationMode)) m_authMode = gcf.m_authenticationMode.GetTag(); PTRACE(2, "GKC\tDiscover GK " << AsString(m_gkaddr, m_gkport) << " at " << m_loaddr); if (gcf.HasOptionalField(H225_RegistrationConfirm::e_assignedGatekeeper)) m_gkList->Set(gcf.m_assignedGatekeeper); else return true; } else if (ras->GetTag() == H225_RasMessage::e_gatekeeperReject) { H225_GatekeeperReject & grj = (*ras)->m_recvRAS; if (grj.HasOptionalField(H225_GatekeeperReject::e_altGKInfo)) { m_gkList->Set(grj.m_altGKInfo.m_alternateGatekeeper); m_useAltGKPermanent = grj.m_altGKInfo.m_altGKisPermanent; break; } } } return false; } void GkClient::Register() { PWaitAndSignal lock(m_rrqMutex); m_rrjReason = "no response"; if (!IsRegistered() && !m_discoveryComplete) while (!(m_discoveryComplete = Discovery())) if (!GetAltGK()) return; H225_RasMessage rrq_ras; Requester request(rrq_ras, m_loaddr); BuildRRQ(rrq_ras); OnSendingRRQ(rrq_ras); request.SendRequest(m_gkaddr, m_gkport); m_registeredTime = PTime(); if (request.WaitForResponse(m_retry * 1000)) { RasMsg *ras = request.GetReply(); switch (ras->GetTag()) { case H225_RasMessage::e_registrationConfirm: OnRCF(ras); return; case H225_RasMessage::e_registrationReject: OnRRJ(ras); break; } } GetAltGK(); } void GkClient::Unregister() { if (m_natClient) { m_natClient->Stop(); m_natClient = NULL; } for_each(m_handlers, m_handlers + 4, bind1st(mem_fun(&RasServer::UnregisterHandler), m_rasSrv)); m_registered = false; } bool GkClient::GetAltGK() { Unregister(); // always re-register bool result = false; if (Toolkit::AsBool(GkConfig()->GetString(EndpointSection, "UseAlternateGK", "1"))) result = m_gkList->Get(m_gkaddr, m_gkport); PString altgk(AsString(m_gkaddr, m_gkport)); if (m_useAltGKPermanent) Toolkit::Instance()->SetConfig(1, EndpointSection, "Gatekeeper", altgk); if (result) { m_timer = 100, m_resend = m_retry, m_discoveryComplete = false; PTRACE(1, "GKC\tUse Alternate GK " << altgk << (m_useAltGKPermanent ? " permanently" : " temporarily")); } else { // if no alternate gatekeeper found // increase our resent time to avoid flooding the parent m_timer = (m_resend *= 2) * 1000; if (m_resend >= m_gkfailtime) { Toolkit::Instance()->GetRouteTable()->InitTable(); m_resend = m_gkfailtime; } } return result; } void GkClient::BuildRRQ(H225_RegistrationRequest & rrq) { rrq.m_protocolIdentifier.SetValue(H225_ProtocolID); rrq.m_discoveryComplete = m_discoveryComplete; SetRasAddress(rrq.m_rasAddress); SetCallSignalAddress(rrq.m_callSignalAddress); rrq.IncludeOptionalField(H225_RegistrationRequest::e_supportsAltGK); IsRegistered() ? BuildLightWeightRRQ(rrq) : BuildFullRRQ(rrq); SetPassword(rrq); } void GkClient::BuildFullRRQ(H225_RegistrationRequest & rrq) { if (!Toolkit::AsBool(GkConfig()->GetInteger(EndpointSection, "HideGk", 0))) rrq.m_terminalType.IncludeOptionalField(H225_EndpointType::e_gatekeeper); PINDEX as; if (m_endpointType == EndpointType_Terminal) { rrq.m_terminalType.IncludeOptionalField(H225_EndpointType::e_terminal); } else { rrq.m_terminalType.IncludeOptionalField(H225_EndpointType::e_gateway); as = m_prefixes.GetSize(); if (as > 0) { rrq.m_terminalType.m_gateway.IncludeOptionalField(H225_GatewayInfo::e_protocol); rrq.m_terminalType.m_gateway.m_protocol.SetSize(1); H225_SupportedProtocols & protocol = rrq.m_terminalType.m_gateway.m_protocol[0]; protocol.SetTag(H225_SupportedProtocols::e_voice); H225_VoiceCaps & voicecap = (H225_VoiceCaps &)protocol; voicecap.m_supportedPrefixes.SetSize(as); for (PINDEX p = 0; p < as; ++p) H323SetAliasAddress(m_prefixes[p].Trim(), voicecap.m_supportedPrefixes[p].m_prefix); } } rrq.IncludeOptionalField(H225_RegistrationRequest::e_terminalAlias); as = m_h323Id.GetSize(); rrq.m_terminalAlias.SetSize(as); for (PINDEX p = 0; p < as; ++p) H323SetAliasAddress(m_h323Id[p], rrq.m_terminalAlias[p], H225_AliasAddress::e_h323_ID); PINDEX s = m_e164.GetSize() + as; rrq.m_terminalAlias.SetSize(s); for (PINDEX p = as; p < s; ++p) H323SetAliasAddress(m_e164[p-as], rrq.m_terminalAlias[p]); int ttl = GkConfig()->GetInteger(EndpointSection, "TimeToLive", DEFAULT_TTL); if (ttl > 0) { rrq.IncludeOptionalField(H225_RegistrationRequest::e_timeToLive); rrq.m_timeToLive = ttl; } H225_VendorIdentifier & vendor = rrq.m_endpointVendor; vendor.m_vendor.m_t35CountryCode = Toolkit::t35cPoland; vendor.m_vendor.m_manufacturerCode = Toolkit::t35mGnuGk; vendor.m_vendor.m_t35Extension = 0; PString vendorId = GkConfig()->GetString(EndpointSection, "Vendor", ""); if (vendorId.Find(',') != P_MAX_INDEX) { PStringArray ids = vendorId.Tokenise(",", FALSE); if (ids.GetSize() == 3) { vendor.m_vendor.m_t35CountryCode = ids[0].AsUnsigned(); vendor.m_vendor.m_manufacturerCode = ids[1].AsUnsigned(); vendor.m_vendor.m_t35Extension = ids[2].AsUnsigned(); } } vendor.IncludeOptionalField(H225_VendorIdentifier::e_productId); vendor.m_productId = PString(PString::Printf, "GNU Gatekeeper on %s %s %s, %s %s", (const unsigned char*)(PProcess::GetOSName()), (const unsigned char*)(PProcess::GetOSHardware()), (const unsigned char*)(PProcess::GetOSVersion()) ,__DATE__, __TIME__); vendor.IncludeOptionalField(H225_VendorIdentifier::e_versionId); vendor.m_versionId = "Version " + PProcess::Current().GetVersion(); PString productId = GkConfig()->GetString(EndpointSection, "ProductId", ""); if (!productId.IsEmpty()) vendor.m_productId = productId; PString productVersion = GkConfig()->GetString(EndpointSection, "ProductVersion", ""); if (!productVersion.IsEmpty()) vendor.m_versionId = productVersion; // set user provided endpointIdentifier, if any PString endpointId(GkConfig()->GetString(EndpointSection, "EndpointIdentifier", "")); if (!endpointId) { rrq.IncludeOptionalField(H225_RegistrationRequest::e_endpointIdentifier); rrq.m_endpointIdentifier = endpointId; } // set gatekeeperIdentifier found in discovery procedure if (!m_gatekeeperId.GetValue()) { rrq.IncludeOptionalField(H225_RegistrationRequest::e_gatekeeperIdentifier); rrq.m_gatekeeperIdentifier = m_gatekeeperId; } rrq.m_keepAlive = FALSE; // include passowrd as crypto token if (!m_password.IsEmpty()) { rrq.IncludeOptionalField(H225_RegistrationRequest::e_cryptoTokens); SetCryptoTokens(rrq.m_cryptoTokens, rrq.m_terminalAlias.GetSize() ? AsString(rrq.m_terminalAlias[0], false) : ""); } } void GkClient::BuildLightWeightRRQ(H225_RegistrationRequest & rrq) { rrq.IncludeOptionalField(H225_RegistrationRequest::e_endpointIdentifier); rrq.m_endpointIdentifier = m_endpointId; rrq.IncludeOptionalField(H225_RegistrationRequest::e_gatekeeperIdentifier); rrq.m_gatekeeperIdentifier = m_gatekeeperId; rrq.m_keepAlive = TRUE; H225_VendorIdentifier & vendor = rrq.m_endpointVendor; vendor.m_vendor.m_t35CountryCode = Toolkit::t35cPoland; vendor.m_vendor.m_manufacturerCode = Toolkit::t35mGnuGk; vendor.m_vendor.m_t35Extension = 0; PString vendorId = GkConfig()->GetString(EndpointSection, "Vendor", ""); if (vendorId.Find(',') != P_MAX_INDEX) { PStringArray ids = vendorId.Tokenise(",", FALSE); if (ids.GetSize() == 3) { vendor.m_vendor.m_t35CountryCode = ids[0].AsUnsigned(); vendor.m_vendor.m_manufacturerCode = ids[1].AsUnsigned(); vendor.m_vendor.m_t35Extension = ids[2].AsUnsigned(); } } } bool GkClient::WaitForACF(H225_AdmissionRequest &arq, RasRequester & request, Routing::RoutingRequest *robj) { request.SendRequest(m_gkaddr, m_gkport); if (request.WaitForResponse(5000)) { RasMsg *ras = request.GetReply(); if (ras->GetTag() == H225_RasMessage::e_admissionConfirm) { if (robj) { H225_AdmissionConfirm & acf = (*ras)->m_recvRAS; Route route("parent", acf.m_destCallSignalAddress); route.m_flags |= Route::e_toParent; // check if destination has changed if (acf.HasOptionalField(H225_AdmissionConfirm::e_destinationInfo)) { // signal change of destination if caller supports canMapAlias if (arq.HasOptionalField(H225_AdmissionRequest::e_canMapAlias) && arq.m_canMapAlias && acf.m_destinationInfo.GetSize() > 0 ) { robj->SetFlag(Routing::RoutingRequest::e_aliasesChanged); Routing::AdmissionRequest * orig_request = dynamic_cast(robj); if (orig_request) { orig_request->GetRequest().m_destinationInfo = acf.m_destinationInfo; } } } robj->AddRoute(route); if (acf.HasOptionalField(H225_AdmissionConfirm::e_featureSet)) { #ifdef HAS_H46023 H460_FeatureSet fs = H460_FeatureSet(acf.m_featureSet); if (m_registeredH46023 && fs.HasFeature(24)) { callptr call = arq.HasOptionalField(H225_AdmissionRequest::e_callIdentifier) ? CallTable::Instance()->FindCallRec(arq.m_callIdentifier) : CallTable::Instance()->FindCallRec(arq.m_callReferenceValue); H46023_ACF(call, (H460_FeatureStd *)fs.GetFeature(24)); } #endif } } return true; } if (ras->GetTag() == H225_RasMessage::e_admissionReject) OnARJ(ras); } return false; } H225_AdmissionRequest & GkClient::BuildARQ(H225_AdmissionRequest & arq) { arq.m_callType.SetTag(H225_CallType::e_pointToPoint); arq.m_endpointIdentifier = m_endpointId; arq.IncludeOptionalField(H225_AdmissionRequest::e_srcCallSignalAddress); arq.m_srcCallSignalAddress = m_rasSrv->GetCallSignalAddress(m_loaddr); arq.IncludeOptionalField(H225_AdmissionRequest::e_canMapAlias); arq.m_canMapAlias = TRUE; arq.IncludeOptionalField(H225_AdmissionRequest::e_gatekeeperIdentifier); arq.m_gatekeeperIdentifier = m_gatekeeperId; SetPassword(arq); return arq; } void GkClient::OnRCF(RasMsg *ras) { H225_RegistrationConfirm & rcf = (*ras)->m_recvRAS; if (!IsRegistered()) { PTRACE(2, "GKC\tRegister with " << AsString(m_gkaddr, m_gkport) << " successfully"); m_registered = true; m_endpointId = rcf.m_endpointIdentifier; m_gatekeeperId = rcf.m_gatekeeperIdentifier; if (rcf.HasOptionalField(H225_RegistrationConfirm::e_alternateGatekeeper)) m_gkList->Set(rcf.m_alternateGatekeeper); if (rcf.HasOptionalField(H225_RegistrationConfirm::e_supportsAdditiveRegistration) && Toolkit::AsBool(GkConfig()->GetString(EndpointSection, "EnableAdditiveRegistration", "0"))) m_useAdditiveRegistration = true; if (m_useAdditiveRegistration) RegistrationTable::Instance()->UpdateTable(); for_each(m_handlers, m_handlers + 4, bind1st(mem_fun(&RasServer::RegisterHandler), m_rasSrv)); } // Not all RCF contain TTL, in that case keep old value if (rcf.HasOptionalField(H225_RegistrationConfirm::e_timeToLive)) { m_ttl = PMAX((long)(rcf.m_timeToLive - m_retry), (long)30); // Have it reregister at 3/4 of TimeToLive, otherwise the parent // might go out of sync and ends up sending an URQ m_ttl = (m_ttl / 4) * 3; } m_timer = m_ttl * 1000; m_resend = m_retry; // NAT handling if (!m_enableH46018 && rcf.HasOptionalField(H225_RegistrationConfirm::e_nonStandardData)) { int iec = Toolkit::iecUnknown; if (rcf.m_nonStandardData.m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard) { iec = Toolkit::Instance()->GetInternalExtensionCode((const H225_H221NonStandard&)rcf.m_nonStandardData.m_nonStandardIdentifier); } else if (rcf.m_nonStandardData.m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_object) { PASN_ObjectId &oid = rcf.m_nonStandardData.m_nonStandardIdentifier; if (oid.GetDataLength() == 0) iec = Toolkit::iecNATTraversal; } if (iec == Toolkit::iecNATTraversal) { if (rcf.m_nonStandardData.m_data.AsString().Find("NAT=") == 0) if (!m_natClient && rcf.m_callSignalAddress.GetSize() > 0) m_natClient = new NATClient(rcf.m_callSignalAddress[0], m_endpointId); } } if (rcf.HasOptionalField(H225_RegistrationConfirm::e_featureSet)) { #if defined (HAS_H46018) || defined(HAS_H46023) H460_FeatureSet fs = H460_FeatureSet(rcf.m_featureSet); #ifdef HAS_H46018 if (m_enableH46018 && fs.HasFeature(18)) m_registeredH46018 = true; #endif #ifdef HAS_H46023 if (m_enableH46023 && fs.HasFeature(23)) H46023_RCF((H460_FeatureStd *)fs.GetFeature(23)); #endif #endif } } #ifdef HAS_H46023 void GkClient::RunSTUNTest(const H323TransportAddress & addr) { if (m_stunClient) return; m_stunClient = new STUNClient(this, addr); } void GkClient::H46023_RCF(H460_FeatureStd * feat) { if (feat->Contains(Std23_DetRASAddr)) { H323TransportAddress addr = feat->Value(Std23_DetRASAddr); PIPSocket::Address ip; addr.GetIpAddress(ip); if (m_loaddr != ip) { PTRACE(4, "GKC\tH46024 ALG Detected local IP " << m_loaddr << " reported " << ip ); m_algDetected = true; H46023_ForceReregistration(); return; } } m_registeredH46023 = true; if (feat->Contains(Std23_STUNAddr)) { H323TransportAddress addr = feat->Value(Std23_STUNAddr); RunSTUNTest(addr); } } bool GkClient::H46023_TypeNotify(int & nattype) { if (m_natnotify) nattype = m_nattype; return m_natnotify; } void GkClient::H46023_ACF(callptr m_call, H460_FeatureStd * feat) { if (feat->Contains(Std24_NATInstruct)) { unsigned NATinst = feat->Value(Std24_NATInstruct); PTRACE(4, "GKC\tH46024 strategy for call set to " << NATinst); if (m_call) H46023_SetNATStategy(m_call->GetCallIdentifier(),NATinst); } } bool GkClient::H46023_CreateSocketPair(const H225_CallIdentifier & id, WORD sessionID, UDPProxySocket * & rtp, UDPProxySocket * & rtcp, bool & nated) { if (!m_registeredH46023) return false; CallRec::NatStrategy strategy = H46023_GetNATStategy(id); switch (strategy) { case CallRec::e_natUnknown: case CallRec::e_natNoassist: case CallRec::e_natLocalProxy: case CallRec::e_natRemoteProxy: case CallRec::e_natFullProxy: // All handled by the ProxySocket return false; case CallRec::e_natAnnexA: case CallRec::e_natAnnexB: nated = false; rtp = new H46024Socket(this, true, id, strategy, sessionID); rtcp = new H46024Socket(this, false, id, strategy, sessionID); H46023_SetSocketPair(id, sessionID, rtp, rtcp); return true; case CallRec::e_natLocalMaster: nated = true; return (m_stunClient && m_stunClient->CreateSocketPair(id, rtp, rtcp)); case CallRec::e_natRemoteMaster: nated = false; return (m_stunClient && m_stunClient->CreateSocketPair(id, rtp, rtcp)); case CallRec::e_natFailure: // TODO signal the call will fail! PTRACE(1, "H46023\tNAT failure: Call will fail"); default: return false; } } void GkClient::H46023_SetNATStategy(const H225_CallIdentifier & id, unsigned nat) { PWaitAndSignal m(m_strategyMutex); std::list natSockets; natSockets.push_back(CallH46024Sockets(nat)); m_natstrategy.insert(pair >(id, natSockets)); } CallRec::NatStrategy GkClient::H46023_GetNATStategy(const H225_CallIdentifier & id) { PWaitAndSignal m(m_strategyMutex); GkNATSocketMap::const_iterator i = m_natstrategy.find(id); return (CallRec::NatStrategy)((i != m_natstrategy.end()) ? i->second.front().GetNatStrategy() : 0); } void GkClient::H46023_SetSocketPair(const H225_CallIdentifier & id, WORD sessionID, UDPProxySocket * rtp, UDPProxySocket * rtcp) { PWaitAndSignal m(m_strategyMutex); GkNATSocketMap::iterator i = m_natstrategy.find(id); if (i != m_natstrategy.end()) { i->second.push_back(CallH46024Sockets(sessionID,rtp,rtcp)); } } void GkClient::H46023_LoadAlternates(const H225_CallIdentifier & id, WORD session, PString & cui, unsigned & muxID, H323TransportAddress & m_rtp, H323TransportAddress & m_rtcp) { PWaitAndSignal m(m_strategyMutex); GkNATSocketMap::iterator i = m_natstrategy.find(id); if (i != m_natstrategy.end()) { std::list::iterator k; while (k != i->second.end()) { CallH46024Sockets & sockets = *k++; if (sockets.GetSessionID() == session) { sockets.LoadAlternate(cui, muxID, m_rtp, m_rtcp); break; } } } } void GkClient::H46023_SetAlternates(const H225_CallIdentifier & id, WORD session, PString cui, unsigned muxID, H323TransportAddress m_rtp, H323TransportAddress m_rtcp) { PWaitAndSignal m(m_strategyMutex); GkNATSocketMap::iterator i = m_natstrategy.find(id); if (i != m_natstrategy.end()) { std::list::iterator k; while (k != i->second.end()) { CallH46024Sockets & sockets = *k++; if (sockets.GetSessionID() == session) { sockets.SetAlternate(cui, muxID, m_rtp, m_rtcp); break; } } } } void GkClient::H46023_SetAlternates(const H225_CallIdentifier & id, const H46024B_ArrayOf_AlternateAddress & alternates) { PWaitAndSignal m(m_strategyMutex); GkNATSocketMap::iterator i = m_natstrategy.find(id); if (i != m_natstrategy.end()) { for (PINDEX j = 0; j < alternates.GetSize(); ++j) { unsigned session = alternates[j].m_sessionID; std::list::iterator k; while (k != i->second.end()) { CallH46024Sockets & sockets = *k++; if (sockets.GetSessionID() == session) { sockets.SetAlternate(alternates[j]); break; } } } } } void GkClient::H46023_ForceReregistration() { m_registeredTime = PTime() - PTimeInterval(m_timer); } void GkClient::H46023_TypeDetected(int nattype) { m_nattype = nattype; m_natnotify = true; H46023_ForceReregistration(); if (m_nattype != 2) { PTRACE(4, "GKC\tSTUN client disabled: Not supported for NAT type: " << nattype); m_stunClient->Stop(); m_stunClient = NULL; } } #endif void GkClient::OnRRJ(RasMsg *ras) { H225_RegistrationReject & rrj = (*ras)->m_recvRAS; m_rrjReason = "Reason: " + rrj.m_rejectReason.GetTagName(); PTRACE(1, "GKC\tRegistration Rejected: " << rrj.m_rejectReason.GetTagName()); if (rrj.HasOptionalField(H225_RegistrationReject::e_altGKInfo)) { m_gkList->Set(rrj.m_altGKInfo.m_alternateGatekeeper); m_useAltGKPermanent = rrj.m_altGKInfo.m_altGKisPermanent; GetAltGK(); } else if (rrj.m_rejectReason.GetTag() == H225_RegistrationRejectReason::e_fullRegistrationRequired) { SendURQ(); m_timer = 100; Toolkit::Instance()->GetRouteTable()->InitTable(); } } void GkClient::OnARJ(RasMsg *ras) { H225_AdmissionReject & arj = (*ras)->m_recvRAS; if (arj.HasOptionalField(H225_AdmissionReject::e_altGKInfo)) { m_gkList->Set(arj.m_altGKInfo.m_alternateGatekeeper); m_useAltGKPermanent = arj.m_altGKInfo.m_altGKisPermanent; GetAltGK(); } else if (arj.m_rejectReason.GetTag() == H225_AdmissionRejectReason::e_callerNotRegistered) { Unregister(); m_timer = 100; // re-register again } } bool GkClient::OnURQ(RasMsg *ras) { Unregister(); m_registeredTime = PTime(); H225_UnregistrationRequest & urq = (*ras)->m_recvRAS; switch (urq.m_reason.GetTag()) { case H225_UnregRequestReason::e_reregistrationRequired: case H225_UnregRequestReason::e_ttlExpired: m_timer = 500; break; default: m_timer = m_retry * 1000; if (urq.HasOptionalField(H225_UnregistrationRequest::e_alternateGatekeeper)) { m_gkList->Set(urq.m_alternateGatekeeper); GetAltGK(); } break; } (*ras)->m_replyRAS.SetTag(H225_RasMessage::e_unregistrationConfirm); H225_UnregistrationConfirm & ucf = (*ras)->m_replyRAS; ucf.m_requestSeqNum = urq.m_requestSeqNum; return true; } bool GkClient::OnDRQ(RasMsg * ras) { H225_DisengageRequest & drq = (*ras)->m_recvRAS; if (callptr call = drq.HasOptionalField(H225_DisengageRequest::e_callIdentifier) ? CallTable::Instance()->FindCallRec(drq.m_callIdentifier) : CallTable::Instance()->FindCallRec(drq.m_callReferenceValue)) call->Disconnect(true); (*ras)->m_replyRAS.SetTag(H225_RasMessage::e_disengageConfirm); H225_DisengageConfirm & dcf = (*ras)->m_replyRAS; dcf.m_requestSeqNum = drq.m_requestSeqNum; return true; } bool GkClient::OnBRQ(RasMsg *ras) { // lazy implementation, just reply confirm // TODO: integrate into bandwidth management H225_BandwidthRequest & brq = (*ras)->m_recvRAS; (*ras)->m_replyRAS.SetTag(H225_RasMessage::e_bandwidthConfirm); H225_BandwidthConfirm & bcf = (*ras)->m_replyRAS; bcf.m_requestSeqNum = brq.m_requestSeqNum; bcf.m_bandWidth = brq.m_bandWidth; return true; } bool GkClient::OnIRQ(RasMsg * ras) { H225_InfoRequest & irq = (*ras)->m_recvRAS; (*ras)->m_replyRAS.SetTag(H225_RasMessage::e_infoRequestResponse); H225_InfoRequestResponse & irr = (*ras)->m_replyRAS; irr.m_requestSeqNum = irq.m_requestSeqNum; return true; } bool GkClient::RewriteString(PString & alias, bool fromInternal) const { if (!m_rewriteInfo) return false; for (PINDEX i = 0; i < m_rewriteInfo->Size(); ++i) { PString prefix, insert; if (fromInternal) { insert = m_rewriteInfo->Key(i); prefix = m_rewriteInfo->Value(i); } else { prefix = m_rewriteInfo->Key(i); insert = m_rewriteInfo->Value(i); } int len = prefix.GetLength(); if (len == 0 || strncmp(prefix, alias, len) == 0){ PString result = insert + alias.Mid(len); PTRACE(2, "GKC\tRewritePString: " << alias << " to " << result); alias = result; return true; } } return false; } void GkClient::SetClearTokens(H225_ArrayOf_ClearToken & clearTokens, const PString & id) { clearTokens.RemoveAll(); H235AuthCAT auth; // avoid copying for thread-safely auth.SetLocalId((const char *)id); auth.SetPassword((const char *)m_password); H225_ArrayOf_CryptoH323Token dumbTokens; auth.PrepareTokens(clearTokens, dumbTokens); } void GkClient::SetCryptoTokens(H225_ArrayOf_CryptoH323Token & cryptoTokens, const PString & id) { cryptoTokens.RemoveAll(); H235AuthSimpleMD5 auth; // avoid copying for thread-safely auth.SetLocalId((const char *)id); auth.SetPassword((const char *)m_password); H225_ArrayOf_ClearToken dumbTokens; auth.PrepareTokens(dumbTokens, cryptoTokens); } void GkClient::SetRasAddress(H225_ArrayOf_TransportAddress & addr) { addr.SetSize(1); addr[0] = m_rasSrv->GetRasAddress(m_loaddr); } void GkClient::SetCallSignalAddress(H225_ArrayOf_TransportAddress & addr) { addr.SetSize(1); addr[0] = m_rasSrv->GetCallSignalAddress(m_loaddr); } void GkClient::SetNBPassword( H225_LocationRequest & lrq, /// LRQ message to be filled with tokens const PString & id // login name ) { if (!m_password) { lrq.IncludeOptionalField(H225_LocationRequest::e_cryptoTokens), SetCryptoTokens(lrq.m_cryptoTokens, id); lrq.IncludeOptionalField(H225_LocationRequest::e_tokens), SetClearTokens(lrq.m_tokens, id); } } bool GkClient::UsesAdditiveRegistration() const { return m_useAdditiveRegistration; } bool GkClient::AdditiveRegister(H225_ArrayOf_AliasAddress & aliases, int & rejectReason, H225_ArrayOf_ClearToken * tokens, H225_ArrayOf_CryptoH323Token * cryptotokens) { PWaitAndSignal lock(m_rrqMutex); m_rrjReason = "no response"; if (!m_useAdditiveRegistration || !IsRegistered()) { rejectReason = H225_RegistrationRejectReason::e_undefinedReason; return false; } H225_RasMessage rrq_ras; Requester request(rrq_ras, m_loaddr); BuildRRQ(rrq_ras); H225_RegistrationRequest & rrq = rrq_ras; rrq.IncludeOptionalField(H225_RegistrationRequest::e_additiveRegistration); rrq.IncludeOptionalField(H225_RegistrationRequest::e_terminalAlias); rrq.m_terminalAlias = aliases; if (tokens) { rrq.IncludeOptionalField(H225_RegistrationRequest::e_tokens); rrq.m_tokens = *tokens; } if (cryptotokens) { rrq.IncludeOptionalField(H225_RegistrationRequest::e_cryptoTokens); rrq.m_cryptoTokens = *cryptotokens; } OnSendingRRQ(rrq_ras); request.SendRequest(m_gkaddr, m_gkport); m_registeredTime = PTime(); if (request.WaitForResponse(m_retry * 1000)) { RasMsg *ras = request.GetReply(); switch (ras->GetTag()) { case H225_RasMessage::e_registrationConfirm: { H225_RegistrationConfirm & rcf = (*ras)->m_recvRAS; AppendLocalAlias(rcf.m_terminalAlias); aliases = rcf.m_terminalAlias; return true; } case H225_RasMessage::e_registrationReject: { H225_RegistrationReject & rrj = (*ras)->m_recvRAS; rejectReason = rrj.m_rejectReason.GetTag(); return false; } } } rejectReason = H225_RegistrationRejectReason::e_undefinedReason; return false; } bool GkClient::AdditiveUnRegister(const H225_ArrayOf_AliasAddress & aliases) { RemoveLocalAlias(aliases); H225_RasMessage urq_ras; urq_ras.SetTag(H225_RasMessage::e_unregistrationRequest); H225_UnregistrationRequest & urq = urq_ras; urq.m_requestSeqNum = m_rasSrv->GetRequestSeqNum(); urq.IncludeOptionalField(H225_UnregistrationRequest::e_gatekeeperIdentifier); urq.m_gatekeeperIdentifier = m_gatekeeperId; urq.IncludeOptionalField(H225_UnregistrationRequest::e_endpointIdentifier); urq.m_endpointIdentifier = m_endpointId; SetCallSignalAddress(urq.m_callSignalAddress); urq.IncludeOptionalField(H225_UnregistrationRequest::e_endpointAlias); urq.m_endpointAlias = aliases; m_rasSrv->SendRas(urq_ras, m_gkaddr, m_gkport, m_loaddr); return true; } void GkClient::AppendLocalAlias(const H225_ArrayOf_AliasAddress & aliases) { for (PINDEX i=0; i < aliases.GetSize(); ++i) m_h323Id.AppendString(AsString(aliases[i], false)); } void GkClient::RemoveLocalAlias(const H225_ArrayOf_AliasAddress & aliases) { PStringArray newAliasList; for (PINDEX j=0; j < m_h323Id.GetSize(); ++j) { int found = false; for (PINDEX i=0; i < aliases.GetSize(); ++i) { if (AsString(aliases[i], false) == m_h323Id[j]) { found = true; break; } } if (!found) newAliasList.AppendString(m_h323Id[j]); } m_h323Id = newAliasList; } bool GkClient::HandleSetup(SetupMsg & setup, bool fromInternal) { RewriteE164(setup, fromInternal); H225_Setup_UUIE &setupBody = setup.GetUUIEBody(); if (!fromInternal) { if (setupBody.HasOptionalField(H225_Setup_UUIE::e_supportedFeatures)) { #ifdef HAS_H46023 H225_ArrayOf_FeatureDescriptor & fs = setupBody.m_supportedFeatures; unsigned location = 0; if (m_registeredH46023 && FindH460Descriptor(24, fs, location)) { H460_Feature feat = H460_Feature(fs[location]); H460_FeatureStd & std24 = (H460_FeatureStd &)feat; if (std24.Contains(Std24_NATInstruct)) { unsigned natstat = std24.Value(Std24_NATInstruct); H46023_SetNATStategy(setup.GetUUIEBody().m_callIdentifier,natstat); } RemoveH460Descriptor(24, fs); } #endif } } else { #ifdef HAS_H46023 unsigned nonce = 0; if (setupBody.HasOptionalField(H225_Setup_UUIE::e_supportedFeatures) && FindH460Descriptor(24,setupBody.m_supportedFeatures, nonce)) RemoveH460Descriptor(24,setupBody.m_supportedFeatures); if (m_registeredH46023) { CallRec::NatStrategy natoffload = H46023_GetNATStategy(setupBody.m_callIdentifier); H460_FeatureStd feat = H460_FeatureStd(24); feat.Add(Std24_NATInstruct,H460_FeatureContent((unsigned)natoffload,8)); H225_ArrayOf_FeatureDescriptor & desc = setupBody.m_supportedFeatures; int sz = desc.GetSize(); desc.SetSize(sz + 1); desc[sz] = feat; } #endif if (setupBody.m_supportedFeatures.GetSize() > 0) setupBody.IncludeOptionalField(H225_Setup_UUIE::e_supportedFeatures); } return true; } gnugk-3.4/PaxHeaders.16356/Makefile.in0000644000175000001440000000005012212271011015553 xustar000000000000000020 atime=1380868658 20 ctime=1380868609 gnugk-3.4/Makefile.in0000644000175000001440000001443512212271011014523 0ustar00janusers00000000000000# -*- mode: Makefile -*- # Copyright (C) 2002-2013 by its various Authors, see CVS-log # # PURPOSE OF THIS FILE: Make file for GNU Gatekeeper # # - Automatic Version Information via RCS: # $Id: Makefile.in,v 1.70 2013/09/06 06:11:21 willamowius Exp $ # $Source: /cvsroot/openh323gk/openh323gk/Makefile.in,v $ # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # PROG = gnugk SOURCES = singleton.cxx job.cxx yasocket.cxx h323util.cxx \ Toolkit.cxx SoftPBX.cxx GkStatus.cxx RasTbl.cxx Routing.cxx \ Neighbor.cxx GkClient.cxx gkauth.cxx RasSrv.cxx ProxyChannel.cxx \ gk.cxx version.cxx gkacct.cxx gktimer.cxx gkconfig.cxx \ sigmsg.cxx clirw.cxx cisco.cxx ipauth.cxx statusacct.cxx \ syslogacct.cxx capctrl.cxx MakeCall.cxx h460presence.cxx \ forwarding.cxx snmp.cxx lua.cxx ldap.cxx \ @SOURCES@ HEADERS = GkClient.h GkStatus.h Neighbor.h ProxyChannel.h RasPDU.h \ RasSrv.h RasTbl.h Routing.h SoftPBX.h Toolkit.h factory.h \ gk.h gk_const.h gkacct.h gkauth.h job.h name.h rasinfo.h rwlock.h \ singleton.h stl_supp.h version.h yasocket.h gktimer.h \ gkconfig.h configure Makefile sigmsg.h clirw.h cisco.h ipauth.h \ statusacct.h syslogacct.h capctrl.h MakeCall.h h460presence.h snmp.h \ @HEADERS@ # add cleanup files for non-default targets CLEAN_FILES += docs/manual/*.html # add cleanup files for autoconf #CLEAN_FILES += Makefile config.* # colon, the empty variable and a single space are special characters to # MAKE and may cause trouble. Let's 'quote' the little bastards by # assigning it to a variable colon:=: comma:=, empty:= space:=$(empty) $(empty) # remove half updated or corrupt files .DELETE_ON_ERROR: # setup various important paths PTLIBDIR=@PTLIBDIR@ OPENH323DIR=@OPENH323DIR@ OPENH323MAKEDIR=@OPENH323MAKEDIR@ ifndef TMP TMP=/tmp endif ifeq (@NEEDOPENH323PREFIX@,1) ifndef PREFIX PREFIX=@OPENH323INSTALLPREFIX@ endif endif CWD:=$(shell pwd) # having an own idea about default targets .PHONY: bothdepend optnoshared gkdefault .DEFAULT: gkdefault gkdefault: bothdepend optnoshared # LD_RUN_LIST is the list form of the LD_RUN_PATH LD_RUN_LIST := $(subst $(colon),$(space),$(LD_RUN_PATH)) LD_RUN_LIST += $(PTLIBDIR)/lib $(OPENH323DIR)/lib # compiler/linker flags set by configure script STDCCFLAGS += @STDCCFLAGS@ LDFLAGS += @LDFLAGS@ ENDLDLIBS += @ENDLDLIBS@ ENDLDFLAGS += @ENDLDFLAGS@ STDCCFLAGS += -D'MANUFACTURER=@MANUFACTURER@' STDCCFLAGS += -D'PROGRAMMNAME=@PROGRAMNAME@' # automatically include debugging code or not ifdef PASN_NOPRINT STDCCFLAGS += -DPASN_NOPRINT endif ### ### Including the general make rules of OpenH323/H323Plus ### include $(OPENH323MAKEDIR)/openh323u.mak #CXX = callcatcher g++ ### Remove -fdata-sections gcc option that cause problems during link step temp_STDCCFLAGS := $(subst -fdata-sections,,$(STDCCFLAGS)) STDCCFLAGS = $(temp_STDCCFLAGS) ifeq "$(OSTYPE)" "solaris" ### Remove references to gcc 3.x libs that come with Solaris 10 temp_ENDLDLIBS := $(subst -R/usr/sfw/lib,,$(ENDLDLIBS)) ENDLDLIBS = $(temp_ENDLDLIBS) temp_ENDLDLIBS := $(subst -L/usr/sfw/lib,,$(ENDLDLIBS)) ENDLDLIBS = $(temp_ENDLDLIBS) ENDLDLIBS += -L/opt/csw/lib endif # solaris # GK version infomation STDCCFLAGS += -DMAJOR_VERSION=@GNUGK_MAJOR_VERSION@ -DMINOR_VERSION=@GNUGK_MINOR_VERSION@ -DBUILD_NUMBER=@GNUGK_BUILD_NUMBER@ STDCCFLAGS += -fno-strict-aliasing # get gcc version GCCMAJOREQ4 := $(shell expr 4 = `$(CC) -dumpversion | cut -f1 -d.`) GCCMAJORGT4 := $(shell expr 4 \< `$(CC) -dumpversion | cut -f1 -d.`) GCCMAJORGTEQ4 := $(shell expr 4 \<= `$(CC) -dumpversion | cut -f1 -d.`) GCCMINORGTEQ4 := $(shell expr 4 \<= `$(CC) -dumpversion | cut -f2 -d.`) # enable stack protection and RELRO + BIND_NOW on Linux with gcc >= 4.x.x ifeq "$(OSTYPE)" "linux" ifeq "$(GCCMAJORGTEQ4)" "1" # security flags STDCCFLAGS += -fstack-protector --param ssp-buffer-size=4 LDFLAGS += -Wl,-z,relro,-z,now endif # gcc >= 4.x.x endif # linux # enable position independent code (PIE) for address space randomization # can only be used with make opt, otherwise H323Plus + PTLib also need to be compiled with PIE # 5-10% perfomance penalty on x86, less on x64_64 #STDCCFLAGS += -fPIE -pie #LDFLAGS += -fPIE -pie # enable more warnings when using gcc >= 4.4 ifeq "$(GCCMAJOREQ4)" "1" ifeq "$(GCCMINORGTEQ4)" "1" STDCCFLAGS += -Wtype-limits -Wstrict-overflow=5 -fstrict-overflow -Wsign-compare endif endif ifeq "$(GCCMAJORGT4)" "1" STDCCFLAGS += -Wtype-limits -Wstrict-overflow=5 -fstrict-overflow -Wsign-compare endif # special dependency to ensure version.cxx is rebuilt each time gnugk is recompiled # so the proper build timestamp is included versionts.h: $(subst version.cxx,,$(SOURCES)) $(HEADERS) $(OH323_LIBDIR)/$(OH323_FILE) $(PT_LIBDIR)/$(PTLIB_FILE) @touch $@ doc: $(MAKE) -C docs/manual html # test support using Google C++ Test Framework TESTCASES = h323util.t.cxx Toolkit.t.cxx temp_TESTOBJS := $(subst $(OBJDIR)/gk.o,,$(OBJS)) TESTOBJS = $(temp_TESTOBJS) libgtest.a: $(CXX) -I${GTEST_DIR}/include -I${GTEST_DIR} -c ${GTEST_DIR}/src/gtest-all.cc $(AR) -rv libgtest.a gtest-all.o test: libgtest.a testrunner.cxx $(TESTCASES) $(TESTOBJS) @rm -f ./testrunner $(OBJDIR)/GkStatus.o @$(CXX) $(STDCCFLAGS) $(STDCXXFLAGS) $(CFLAGS) -DUNIT_TEST=1 -x c++ -c GkStatus.cxx -o $(OBJDIR)/GkStatus.o $(CXX) -I${GTEST_DIR}/include $(STDCCFLAGS) testrunner.cxx $(TESTCASES) -o testrunner libgtest.a $(TESTOBJS) $(LDFLAGS) -lh323_linux_x86_64__s -lpt_s $(ENDLDLIBS) ./testrunner # special configure dependencies configure: configure.in autoconf Makefile: Makefile.in config.status ./config.status config.status: configure ./config.status --recheck gnugk-3.4/PaxHeaders.16356/gk_2010.sln0000644000175000001440000000005011703007451015300 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gk_2010.sln0000644000175000001440000000500111703007451014235 0ustar00janusers00000000000000Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C++ Express 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gnugk", "gk_2010.vcxproj", "{C5052DDD-FADC-4415-B235-92A9761BB21D}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "addpasswd", "addpasswd_2010.vcxproj", "{2C306BF0-7FBE-4136-B021-AFA258898341}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug (h323plus)|Win32 = Debug (h323plus)|Win32 Debug as Service (h323plus)|Win32 = Debug as Service (h323plus)|Win32 Release (h323plus)|Win32 = Release (h323plus)|Win32 Release as Service (h323plus)|Win32 = Release as Service (h323plus)|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {C5052DDD-FADC-4415-B235-92A9761BB21D}.Debug (h323plus)|Win32.ActiveCfg = Debug (h323plus)|Win32 {C5052DDD-FADC-4415-B235-92A9761BB21D}.Debug (h323plus)|Win32.Build.0 = Debug (h323plus)|Win32 {C5052DDD-FADC-4415-B235-92A9761BB21D}.Debug as Service (h323plus)|Win32.ActiveCfg = Debug as Service (h323plus)|Win32 {C5052DDD-FADC-4415-B235-92A9761BB21D}.Debug as Service (h323plus)|Win32.Build.0 = Debug as Service (h323plus)|Win32 {C5052DDD-FADC-4415-B235-92A9761BB21D}.Release (h323plus)|Win32.ActiveCfg = Release (h323plus)|Win32 {C5052DDD-FADC-4415-B235-92A9761BB21D}.Release (h323plus)|Win32.Build.0 = Release (h323plus)|Win32 {C5052DDD-FADC-4415-B235-92A9761BB21D}.Release as Service (h323plus)|Win32.ActiveCfg = Release as Service (h323plus)|Win32 {C5052DDD-FADC-4415-B235-92A9761BB21D}.Release as Service (h323plus)|Win32.Build.0 = Release as Service (h323plus)|Win32 {2C306BF0-7FBE-4136-B021-AFA258898341}.Debug (h323plus)|Win32.ActiveCfg = Debug|Win32 {2C306BF0-7FBE-4136-B021-AFA258898341}.Debug (h323plus)|Win32.Build.0 = Debug|Win32 {2C306BF0-7FBE-4136-B021-AFA258898341}.Debug as Service (h323plus)|Win32.ActiveCfg = Debug|Win32 {2C306BF0-7FBE-4136-B021-AFA258898341}.Debug as Service (h323plus)|Win32.Build.0 = Debug|Win32 {2C306BF0-7FBE-4136-B021-AFA258898341}.Release (h323plus)|Win32.ActiveCfg = Release|Win32 {2C306BF0-7FBE-4136-B021-AFA258898341}.Release (h323plus)|Win32.Build.0 = Release|Win32 {2C306BF0-7FBE-4136-B021-AFA258898341}.Release as Service (h323plus)|Win32.ActiveCfg = Release|Win32 {2C306BF0-7FBE-4136-B021-AFA258898341}.Release as Service (h323plus)|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal gnugk-3.4/PaxHeaders.16356/gk_2010.vcxproj0000644000175000001440000000005012210737364016206 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gk_2010.vcxproj0000644000175000001440000011767112210737364015164 0ustar00janusers00000000000000 Debug (h323plus) Win32 Debug (ptlib 1.11) Win32 Debug as Service (h323plus) Win32 Debug Win32 Release (h323plus) Win32 Release (ptlib +1.11) Win32 Release as Service (h323plus) Win32 Release Win32 gnugk {C5052DDD-FADC-4415-B235-92A9761BB21D} Application false Application false Application false Application false Application false Application false Application false Application false <_ProjectFileVersion>10.0.21006.1 .\Debug\ .\Debug\ .\Release\ .\Release\ false $(Configuration)\ $(Configuration)\ $(Configuration)\ $(Configuration)\ false $(Configuration)\ $(Configuration)\ $(Configuration)\ $(Configuration)\ false $(Configuration)\ $(Configuration)\ false $(Configuration)\ $(Configuration)\ AllRules.ruleset AllRules.ruleset AllRules.ruleset AllRules.ruleset AllRules.ruleset AllRules.ruleset AllRules.ruleset AllRules.ruleset $(ProjectDir)..\h323plus\include;$(ProjectDir)..\ptlib\include;$(IncludePath) $(ProjectDir)..\h323plus\include;$(ProjectDir)..\ptlib\include;$(IncludePath) $(ProjectDir)..\h323plus\include;$(ProjectDir)..\ptlib\include;$(IncludePath) $(ProjectDir)..\h323plus\include;$(ProjectDir)..\ptlib\include;$(IncludePath) $(ProjectDir)..\h323plus\include;$(ProjectDir)..\ptlib\include;$(IncludePath) $(ProjectDir)..\h323plus\include;$(ProjectDir)..\ptlib\include;$(IncludePath) $(ProjectDir)..\h323plus\include;$(ProjectDir)..\ptlib\include;$(IncludePath) $(ProjectDir)..\h323plus\include;$(ProjectDir)..\ptlib\include;$(IncludePath) $(ProjectDir)..\h323plus\lib;$(ProjectDir)..\ptlib\lib;$(LibraryPath) $(ProjectDir)..\h323plus\lib;$(ProjectDir)..\ptlib\lib;$(LibraryPath) $(ProjectDir)..\h323plus\lib;$(ProjectDir)..\ptlib\lib;$(LibraryPath) $(ProjectDir)..\h323plus\lib;$(ProjectDir)..\ptlib\lib;$(LibraryPath) $(ProjectDir)..\h323plus\lib;$(ProjectDir)..\ptlib\lib;$(LibraryPath) $(ProjectDir)..\h323plus\lib;$(ProjectDir)..\ptlib\lib;$(LibraryPath) $(ProjectDir)..\h323plus\lib;$(ProjectDir)..\ptlib\lib;$(LibraryPath) $(ProjectDir)..\h323plus\lib;$(ProjectDir)..\ptlib\lib;$(LibraryPath) .\Debug/gk.tlb Disabled _DEBUG;PTRACING;HAS_RADIUS=1;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Default true .\Debug/gk.pch .\Debug/ .\Debug/ .\Debug/ true Level3 true ProgramDatabase Default _DEBUG;%(PreprocessorDefinitions) 0x0409 openh323d.lib;ptclibd.lib;ptlibd.lib;snmpapi.lib;Winmm.lib;mpr.lib;wsock32.lib;%(AdditionalDependencies) Debug/gnugk.exe true true .\Debug/gnugk.pdb Console true true MachineX86 .\Release/gk.tlb Disabled AnySuitable true Speed NDEBUG;PTRACING;HAS_RADIUS=1;%(PreprocessorDefinitions) MultiThreadedDLL Default false true .\Release/gk.pch .\Release/ .\Release/ .\Release/ Level3 true Default NDEBUG;%(PreprocessorDefinitions) 0x0409 h323plus.lib;ptclib.lib;ptlibs.lib;snmpapi.lib;odbc32.lib;odbccp32.lib;wsock32.lib;mpr.lib;winmm.lib;%(AdditionalDependencies) Release/gnugk.exe true .\Release/gnugk.pdb Console true true MachineX86 .\Debug/gk.tlb Disabled _DEBUG;PTRACING;HAS_RADIUS=1;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Default true .\Debug/gk.pch .\Debug/ .\Debug/ .\Debug/ true Level3 true ProgramDatabase Default _DEBUG;%(PreprocessorDefinitions) 0x0409 openh323sd.lib;ptlibsd.lib;snmpapi.lib;Winmm.lib;mpr.lib;wsock32.lib;%(AdditionalDependencies) Debug/gnugk.exe true true .\Debug/gnugk.pdb Console true true MachineX86 .\Release/gk.tlb MaxSpeed AnySuitable true Speed true NDEBUG;PTRACING;HAS_RADIUS=1;%(PreprocessorDefinitions) MultiThreadedDLL Default false true .\Release/gk.pch .\Release/ .\Release/ .\Release/ Level3 true Default NDEBUG;%(PreprocessorDefinitions) 0x0409 OpenH323s.lib;ptlibs.lib;snmpapi.lib;odbc32.lib;odbccp32.lib;wsock32.lib;mpr.lib;winmm.lib;%(AdditionalDependencies) Release/gnugk.exe true .\Release/gnugk.pdb Console UseLinkTimeCodeGeneration true true MachineX86 .\Debug/gk.tlb Disabled _DEBUG;PTRACING;H323PLUS_LIB;HAS_RADIUS=1;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Default true .\Debug/gk.pch .\Debug/ .\Debug/ .\Debug/ true Level3 true ProgramDatabase Default _DEBUG;%(PreprocessorDefinitions) 0x0409 h323plusd.lib;ptlibsd.lib;snmpapi.lib;Winmm.lib;mpr.lib;wsock32.lib;%(AdditionalDependencies) Debug/gnugk.exe true true .\Debug/gnugk.pdb Console true true MachineX86 .\Release/gk.tlb MaxSpeed AnySuitable true Speed true NDEBUG;PTRACING;H323PLUS_LIB;HAS_RADIUS=1;%(PreprocessorDefinitions) false Sync MultiThreadedDLL Default false false true $(TargetDir)gk.pch $(TargetDir) $(TargetDir) $(TargetDir) Level3 true Default NDEBUG;%(PreprocessorDefinitions) 0x0409 h323plus.lib;ptlibs.lib;snmpapi.lib;odbc32.lib;odbccp32.lib;wsock32.lib;mpr.lib;winmm.lib;%(AdditionalDependencies) $(TargetDir)gnugk.exe true $(TargetDir)gnugk.pdb Console UseLinkTimeCodeGeneration true true MachineX86 .\Release/gk.tlb MaxSpeed AnySuitable true Speed true NDEBUG;PTRACING;H323PLUS_LIB;HAS_RADIUS=1;COMPILE_AS_SERVICE=1;%(PreprocessorDefinitions) false Sync MultiThreadedDLL Default false false true $(TargetDir)gk.pch $(TargetDir) $(TargetDir) $(TargetDir) Level3 true Default NDEBUG;%(PreprocessorDefinitions) 0x0409 h323plus.lib;ptlibs.lib;snmpapi.lib;odbc32.lib;odbccp32.lib;wsock32.lib;mpr.lib;winmm.lib;%(AdditionalDependencies) $(TargetDir)gnugk.exe true $(TargetDir)gnugk.pdb Windows UseLinkTimeCodeGeneration true true MachineX86 .\Debug/gk.tlb Disabled _DEBUG;PTRACING;H323PLUS_LIB;HAS_RADIUS=1;COMPILE_AS_SERVICE=1;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Default true .\Debug/gk.pch .\Debug/ .\Debug/ .\Debug/ true Level3 true ProgramDatabase Default _DEBUG;%(PreprocessorDefinitions) 0x0409 h323plusd.lib;ptlibsd.lib;snmpapi.lib;Winmm.lib;mpr.lib;wsock32.lib;%(AdditionalDependencies) Debug\gnugk.exe true true .\Debug\gnugk.pdb Windows true true MachineX86 Configuring Build Options cd $(ProjectDir) configure --exclude-env=VSNET2005_PWLIB_CONFIGURE_EXCLUDE_DIRS %PWLIB_CONFIGURE_OPTIONS%; $(SolutionDir)/configure.in;%(AdditionalInputs) %(RootDir)%(Directory)gnugkbuildopts.h;%(Outputs) Configuring Build Options cd $(ProjectDir) configure --exclude-env=VSNET2005_PWLIB_CONFIGURE_EXCLUDE_DIRS %PWLIB_CONFIGURE_OPTIONS%; $(SolutionDir)/configure.in;%(AdditionalInputs) %(RootDir)%(Directory)gnugkbuildopts.h;%(Outputs) Configuring Build Options cd $(ProjectDir) configure --exclude-env=VSNET2005_PWLIB_CONFIGURE_EXCLUDE_DIRS %PWLIB_CONFIGURE_OPTIONS%; $(SolutionDir)/configure.in;%(AdditionalInputs) %(RootDir)%(Directory)gnugkbuildopts.h;%(Outputs) Configuring Build Options cd $(ProjectDir) configure --exclude-env=VSNET2005_PWLIB_CONFIGURE_EXCLUDE_DIRS %PWLIB_CONFIGURE_OPTIONS%; $(SolutionDir)/configure.in;%(AdditionalInputs) %(RootDir)%(Directory)gnugkbuildopts.h;%(Outputs) Configuring Build Options cd $(ProjectDir) configure --exclude-env=VSNET2005_PWLIB_CONFIGURE_EXCLUDE_DIRS %PWLIB_CONFIGURE_OPTIONS%; $(SolutionDir)/configure.in;%(AdditionalInputs) %(RootDir)%(Directory)gnugkbuildopts.h;%(Outputs) Configuring Build Options cd $(ProjectDir) configure --exclude-env=VSNET2005_PWLIB_CONFIGURE_EXCLUDE_DIRS %PWLIB_CONFIGURE_OPTIONS%; $(SolutionDir)/configure.in;%(AdditionalInputs) %(RootDir)%(Directory)gnugkbuildopts.h;%(Outputs) Configuring Build Options cd $(ProjectDir) configure --exclude-env=VSNET2005_PWLIB_CONFIGURE_EXCLUDE_DIRS %PWLIB_CONFIGURE_OPTIONS% %GNUGK_CONFIGURE_OPTIONS%; $(SolutionDir)/configure.in;%(AdditionalInputs) %(RootDir)%(Directory)gnugkbuildopts.h;%(Outputs) Configuring Build Options cd $(ProjectDir) configure --exclude-env=VSNET2005_PWLIB_CONFIGURE_EXCLUDE_DIRS %PWLIB_CONFIGURE_OPTIONS%; $(SolutionDir)/configure.in;%(AdditionalInputs) %(RootDir)%(Directory)gnugkbuildopts.h;%(Outputs) gnugk-3.4/PaxHeaders.16356/rwlock.h0000644000175000001440000000005011461315737015203 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/rwlock.h0000644000175000001440000000243411461315737014147 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // rwlock.h // // Utilities for PReadWriteMutex usage // // Copyright (c) Citron Network Inc. 2002 // Copyright (c) 2006-2010, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef RWLOCK_H #define RWLOCK_H "@(#) $Id: rwlock.h,v 1.7 2010/10/25 15:01:51 willamowius Exp $" class ReadLock { PReadWriteMutex & mutex; public: ReadLock(PReadWriteMutex & m) : mutex(m) { mutex.StartRead(); } ~ReadLock() { mutex.EndRead(); } }; class WriteLock { PReadWriteMutex & mutex; public: WriteLock(PReadWriteMutex & m) : mutex(m) { mutex.StartWrite(); } ~WriteLock() { mutex.EndWrite(); } }; class ReadUnlock { PReadWriteMutex & mutex; public: ReadUnlock(PReadWriteMutex & m) : mutex(m) { mutex.EndRead(); } ~ReadUnlock() { mutex.StartRead(); } }; class WriteUnlock { PReadWriteMutex & mutex; public: WriteUnlock(PReadWriteMutex & m) : mutex(m) { mutex.EndWrite(); } ~WriteUnlock() { mutex.StartWrite(); } }; extern PReadWriteMutex ConfigReloadMutex; #endif // RWLOCK_H gnugk-3.4/PaxHeaders.16356/Routing.h0000644000175000001440000000005012211373117015317 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/Routing.h0000644000175000001440000005564212211373117014274 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // Routing.h // // Routing Mechanism for GNU Gatekeeper // // Copyright (c) Citron Network Inc. 2003 // Copyright (c) 2004-2012, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef ROUTING_H #define ROUTING_H "@(#) $Id: Routing.h,v 1.81 2013/09/03 14:46:39 willamowius Exp $" #include #include #include "singleton.h" #include "factory.h" #include "RasTbl.h" #include "stl_supp.h" // forward references to avoid includes class H225_AdmissionRequest; class H225_LocationRequest; class H225_Setup_UUIE; class H225_Facility_UUIE; class H225_TransportAddress; class H225_ArrayOf_AliasAddress; class Q931; class SignalingMsg; template class H225SignalingMsg; typedef H225SignalingMsg SetupMsg; typedef H225SignalingMsg FacilityMsg; class RasMsg; class GkClient; class GkSQLConnection; namespace Routing { class VirtualQueue; /// An entry for a single call destination route class Route { public: // a policy can set flags to indicate extra status of a processed request enum Flags { e_toParent = 1, e_toNeighbor = 2, e_Reject = 4 }; Route(); Route( const PString & policyName, const endptr & destEndpoint, unsigned priority = 1 ); Route( const PString & policyName, const H225_TransportAddress & destAddr, unsigned priority = 1 ); Route( const PString & policyName, const PIPSocket::Address & destIpAddr, WORD destPort, unsigned priority = 1 ); bool operator< (const Route & rhs) { return m_priority < rhs.m_priority; } unsigned GetPriority() const { return m_priority; } void SetPriority(unsigned p) { m_priority = p; } PString AsString() const; bool IsFailoverActive(unsigned cause) const; H225_TransportAddress m_destAddr; /// destination address for signaling endptr m_destEndpoint; /// destination endpoint record (if available) PString m_policy; /// name of the policy that found the route PString m_routeId; /// optional policy-specific route identifier int m_proxyMode; /// per-route proxy mode flag unsigned m_flags; /// additional route specific flags PString m_destNumber; /// rewritten number (corresponds to Toolkit::RewriteE164) PString m_destOutNumber; /// number actually sent to the called party (corresponds to Toolkit::GWRewriteE164) protected: unsigned char m_rerouteCauses[16]; /// bit flags to trigger rerouting on particular Q931 causes unsigned m_priority; /// priority of this route (less is more important) }; class RoutingRequest { public: enum Flags { e_aliasesChanged = 1, e_fromInternal = 2, // currently not used e_fromParent = 4, e_fromNeighbor = 8, // currently not used e_Reject = 16 }; RoutingRequest(); RoutingRequest(const std::list & failedRoutes); virtual ~RoutingRequest(); bool AddRoute(const Route & route); bool GetFirstRoute(Route & route); void RemoveAllRoutes(); bool HasRoutes() const { return !m_routes.empty(); } std::list & GetRoutes() { return m_routes; } void SetRejectReason(unsigned reason) { m_reason = reason; } void SetFlag(unsigned f) { m_flags |= f; } unsigned GetRejectReason() const { return m_reason; } unsigned GetFlags() const { return m_flags; } void SetSourceIP(const PString & ip) { m_sourceIP = ip; } PString GetSourceIP() const { return m_sourceIP; } void SetCallerID(const PString & id) { m_callerID = id; } PString GetCallerID() const { return m_callerID; } void SetGatewayDestination(const H225_TransportAddress & gw) { m_gwDestination = gw; } bool GetGatewayDestination(H225_TransportAddress & gw ) const; private: RoutingRequest(const RoutingRequest &); RoutingRequest& operator=(const RoutingRequest &); private: int m_reason; /// reject reason, if no routes are found unsigned m_flags; /// request specific flags std::list m_routes; std::list m_failedRoutes; PString m_sourceIP; PString m_callerID; H225_TransportAddress m_gwDestination; }; template class Request : public RoutingRequest { public: typedef R ReqObj; typedef W Wrapper; Request(ReqObj & r, Wrapper *w) : m_request(r), m_wrapper(w), m_clientAuthId(0) {} Request(ReqObj & r, Wrapper *w, const PString & id, PUInt64 authid) : m_request(r), m_wrapper(w), m_callingStationId(id), m_clientAuthId(authid) {} Request(ReqObj & r, Wrapper *w, const std::list &failedRoutes) : RoutingRequest(failedRoutes), m_request(r), m_wrapper(w), m_clientAuthId(0) {} bool Process(); ReqObj & GetRequest() { return m_request; } Wrapper *GetWrapper() { return m_wrapper; } H225_ArrayOf_AliasAddress *GetAliases(); void SetAliases(H225_ArrayOf_AliasAddress & aliases); const H225_TransportAddress * GetDestIP() const; const ReqObj & GetRequest() const { return m_request; } const Wrapper *GetWrapper() const { return m_wrapper; } PString GetCallingStationId() const { return m_callingStationId; } PUInt64 GetClientAuthId() const { return m_clientAuthId; } const H225_ArrayOf_AliasAddress * GetAliases() const { return const_cast *>(this)->GetAliases(); } private: ReqObj & m_request; Wrapper * m_wrapper; PString m_callingStationId; /// ID provided by client during authentication PUInt64 m_clientAuthId; }; typedef Request AdmissionRequest; typedef Request LocationRequest; typedef Request SetupRequest; typedef Request FacilityRequest; template class PolicyList { public: typedef T Base; // for SimpleCreator template PolicyList() : m_next(NULL) { } virtual ~PolicyList() { delete m_next; } // delete whole list recursively static T * Create(const PStringArray & rules); protected: T * m_next; }; // ignore overflow warning when comparing size #if (!_WIN32) && (GCC_VERSION >= 40400) #pragma GCC diagnostic ignored "-Wstrict-overflow" #endif template T *PolicyList::Create(const PStringArray & rules) { T * next = NULL; for (int i = rules.GetSize(); --i >= 0; ) { PStringArray id = rules[i].Tokenise("::", false); // check for :: if (T * current = Factory::Create(id[0])) { if (id.GetSize() > 1) current->SetInstance(id[1]); else current->SetInstance(""); current->m_next = next; next = current; } } return next; } class Policy : public PolicyList { public: Policy() : m_name("Undefined"), m_iniSection("Routing::Undefined") { } virtual ~Policy() { } template bool HandleRas(Request & request) { if( IsActive() ) { const char * tagname = request.GetWrapper() ? request.GetWrapper()->GetTagName() : "unknown"; const unsigned seqnum = request.GetRequest().m_requestSeqNum.GetValue(); PTRACE(5, "ROUTING\tChecking policy " << m_name << " for the request " << tagname << ' ' << seqnum); if( OnRequest(request) ) { PTRACE(5, "ROUTING\tPolicy " << m_name << " applied to the request " << tagname << ' ' << seqnum); return true; } } return m_next && m_next->HandleRas(request); } bool Handle(SetupRequest & request); bool Handle(FacilityRequest & request); void SetInstance(const PString & instance); protected: // new virtual function // if return false, the policy is disable virtual bool IsActive() const { return true; } // methods to handle the request // return true: fate of the request is determined (confirm or reject) // return false: undetermined, try next virtual bool OnRequest(AdmissionRequest &) { return false; } virtual bool OnRequest(LocationRequest &) { return false; } virtual bool OnRequest(SetupRequest &) { return false; } virtual bool OnRequest(FacilityRequest &) { return false; } virtual void LoadConfig(const PString & instance) { } // should be used to load config, always called after the policy object is created protected: /// human readable name for the policy - it should be set inside constructors /// of derived policies, default value is "undefined" const char* m_name; PString m_iniSection; }; // the simplest policy, the destination has been explicitly specified class ExplicitPolicy : public Policy { public: ExplicitPolicy(); static void MapDestination(H225_AdmissionRequest & arq); static void MapDestination(H225_Setup_UUIE & setupBody); static void MapDestination(H225_Facility_UUIE & facility); static void OnReload(); protected: virtual bool OnRequest(AdmissionRequest &); // the policy doesn't apply to LocationRequest virtual bool OnRequest(SetupRequest &); virtual bool OnRequest(FacilityRequest &); static std::map m_destMap; }; class AliasesPolicy : public Policy { public: AliasesPolicy() { m_name = "Aliases"; } protected: // override from class Policy virtual bool OnRequest(AdmissionRequest &); virtual bool OnRequest(LocationRequest &); virtual bool OnRequest(SetupRequest &); virtual bool OnRequest(FacilityRequest &); // new virtual function virtual bool FindByAliases(RoutingRequest &, H225_ArrayOf_AliasAddress &) = 0; virtual bool FindByAliases(LocationRequest &, H225_ArrayOf_AliasAddress &) = 0; }; // this class passes incoming requests through the chain of routing policies class Analyzer : public Singleton { public: Analyzer(); ~Analyzer(); void OnReload(); bool Parse(AdmissionRequest &); bool Parse(LocationRequest &); bool Parse(SetupRequest &); bool Parse(FacilityRequest &); private: typedef std::map Rules; Policy *Create(const PString & policy); Policy *ChoosePolicy(const H225_ArrayOf_AliasAddress *, Rules &); Rules m_rules[4]; PReadWriteMutex m_reloadMutex; }; // the classical policy, find the dstination from the RegistrationTable class InternalPolicy : public AliasesPolicy { public: InternalPolicy(); protected: virtual bool OnRequest(AdmissionRequest &); virtual bool OnRequest(SetupRequest &); virtual bool FindByAliases(RoutingRequest &, H225_ArrayOf_AliasAddress &); virtual bool FindByAliases(LocationRequest &, H225_ArrayOf_AliasAddress &); virtual bool FindByAliases(SetupRequest &, H225_ArrayOf_AliasAddress &); virtual bool FindByAliases(AdmissionRequest &, H225_ArrayOf_AliasAddress &); private: bool roundRobin; }; // a policy to route call to parent class ParentPolicy : public Policy { public: ParentPolicy(); private: // override from class Policy virtual bool IsActive() const; virtual bool OnRequest(AdmissionRequest &); virtual bool OnRequest(LocationRequest &); virtual bool OnRequest(SetupRequest &); virtual bool OnRequest(FacilityRequest &); GkClient *m_gkClient; }; // a policy to look up the destination from DNS class DNSPolicy : public AliasesPolicy { public: DNSPolicy(); protected: virtual bool FindByAliases(RoutingRequest &, H225_ArrayOf_AliasAddress &); virtual bool FindByAliases(LocationRequest &, H225_ArrayOf_AliasAddress &); virtual bool DNSLookup(const PString & hostname, PIPSocket::Address & addr) const; virtual void LoadConfig(const PString & instance); bool m_resolveNonLocalLRQs; }; // a policy to route call via external program class VirtualQueuePolicy : public Policy { public: VirtualQueuePolicy(); protected: // override from class Policy virtual bool IsActive() const; virtual bool OnRequest(AdmissionRequest &); virtual bool OnRequest(LocationRequest &); virtual bool OnRequest(SetupRequest &); private: VirtualQueue *m_vqueue; }; class NumberAnalysisPolicy : public Policy { public: struct PrefixEntry { string m_prefix; int m_minLength; int m_maxLength; }; NumberAnalysisPolicy(); protected: virtual bool OnRequest(AdmissionRequest &); virtual bool OnRequest(SetupRequest &); virtual void LoadConfig(const PString & instance); private: NumberAnalysisPolicy(const NumberAnalysisPolicy &); NumberAnalysisPolicy& operator=(const NumberAnalysisPolicy &); private: typedef vector Prefixes; /// list of number prefixes, with min/max number length as values Prefixes m_prefixes; }; // a policy to look up the destination from ENUM Name Server class ENUMPolicy : public AliasesPolicy { public: ENUMPolicy(); protected: virtual bool FindByAliases(RoutingRequest &, H225_ArrayOf_AliasAddress &); virtual bool FindByAliases(LocationRequest &, H225_ArrayOf_AliasAddress &); virtual bool FindByAliasesInternal(const PString & schema, RoutingRequest &, H225_ArrayOf_AliasAddress &, PBoolean &); virtual void LoadConfig(const PString & instance); bool m_resolveLRQs; PStringToString m_enum_schema; }; class DestinationRoutes { public: DestinationRoutes(); ~DestinationRoutes() { } bool EndPolicyChain() const { return m_endChain; } bool RejectCall() const { return m_reject; } void SetRejectCall(bool reject) { m_reject = reject; m_endChain = true; } unsigned int GetRejectReason() const { return m_rejectReason; } void SetRejectReason(unsigned int reason) { m_rejectReason = reason; } bool ChangeAliases() const { return m_aliasesChanged; } void SetChangedAliases(bool success) { m_aliasesChanged = success; } H225_ArrayOf_AliasAddress GetNewAliases() const { return m_newAliases; } void SetNewAliases(const H225_ArrayOf_AliasAddress & aliases) { m_newAliases = aliases; m_aliasesChanged = true; } void AddRoute(const Route & route, bool endChain = true); std::list m_routes; protected: bool m_endChain; bool m_reject; unsigned int m_rejectReason; bool m_aliasesChanged; H225_ArrayOf_AliasAddress m_newAliases; }; // superclass for dynamic policies like sql and lua scripring class DynamicPolicy : public Policy { public: DynamicPolicy(); virtual ~DynamicPolicy() { } protected: virtual bool IsActive() const { return m_active; } virtual bool OnRequest(AdmissionRequest &); virtual bool OnRequest(LocationRequest &); virtual bool OnRequest(SetupRequest &); virtual void RunPolicy( /*in */ const PString & source, const PString & calledAlias, const PString & calledIP, const PString & caller, const PString & callingStationId, const PString & callid, const PString & messageType, const PString & clientauthid, /* out: */ DestinationRoutes & destination) = 0; virtual bool ResolveRoute( RoutingRequest & request, DestinationRoutes & destination ) { return true; } protected: // active ? bool m_active; }; // a policy to route calls via an SQL database class SqlPolicy : public DynamicPolicy { public: SqlPolicy(); virtual ~SqlPolicy(); protected: virtual void LoadConfig(const PString & instance); virtual void RunPolicy( /*in */ const PString & source, const PString & calledAlias, const PString & calledIP, const PString & caller, const PString & callingStationId, const PString & callid, const PString & messageType, const PString & clientauthid, /* out: */ DestinationRoutes & destination); protected: // connection to the SQL database GkSQLConnection* m_sqlConn; // parametrized query string for the routing query PString m_query; // query timeout long m_timeout; }; // a policy to route all calls to one default endpoint class CatchAllPolicy : public Policy { public: CatchAllPolicy(); virtual ~CatchAllPolicy() { } protected: virtual bool OnRequest(AdmissionRequest & request) { return CatchAllRoute(request); } virtual bool OnRequest(LocationRequest & request) { return CatchAllRoute(request); } virtual bool OnRequest(SetupRequest & request) { return CatchAllRoute(request); } bool CatchAllRoute(RoutingRequest & request) const; virtual void LoadConfig(const PString & instance); PString m_catchAllAlias; PString m_catchAllIP; }; template inline bool Request::Process() { return Analyzer::Instance()->Parse(*this); } /** A class that supports ACD (Automatic Call Distribution). A call made to specified alias(-es) (called virtual queue) is signalled via the GK status line to an external application (an ACD application) that decides where the call should be routed (e.g. what agent should answe the call). Basically, it rewrites virtual queue alias into the alias of the specified agent. The route request is uniquely identified by (EndpointIdentifier,CRV) values pair. */ class VirtualQueue { public: VirtualQueue(); ~VirtualQueue(); /// reload settings from the config file void OnReload(); /** @return True if there is at least one virtual queue configured. */ bool IsActive() const { return m_active; } /** Send RouteRequest to the GK status line and wait for a routing decision to be made by some external application (ACD application). @return True if the external application routed the call (either by specifying an alias or by rejecting the call), false if timed out waiting for the routing decision. If the request was rejected, destinationInfo is set to an epmty array (0 elements). */ bool SendRouteRequest( /// source IP of the request (endpoint for ARQ, gatekeeper for LRQ) const PString & source, /// calling endpoint const PString & epid, /// CRV (Call Reference Value) of the call associated with this request unsigned crv, /// destination (virtual queue) aliases as specified /// by the calling endpoint (modified by this function on successful return) H225_ArrayOf_AliasAddress * destinationInfo, /// destinationCallSignalAddr (optionally set by this function on successful return) PString * callSigAdr, /// bind IP for BindAndRouteToGateway PString * bindIP, /// caller ID PString * callerID, /// should the call be rejected modified by this function on return) bool & reject, /// an actual virtual queue name (should be present in destinationInfo too) const PString& vqueue, /// a sequence of aliases for the calling endpoint /// (in the "alias:type[=alias:type]..." format) const PString & sourceInfo, /// the callID as string const PString & callID, /// the called IP for unregistered calls const PString & calledip = "unknown", /// vendor string of caller const PString & vendorString = "unknown" ); /** Make a routing decision for a pending route request (inserted by SendRequest). @return True if the matching pending request has been found, false otherwise. */ bool RouteToAlias( /// aliases for the routing target (an agent that the call will be routed to) /// that will replace the original destination info const H225_ArrayOf_AliasAddress & agent, /// ip that will replace the destinationCallSignalAddress (RouteToGateway) /// used only if set (port != 0) const PString & destinationip, /// identifier of the endpoint associated with the route request const PString & callingEpId, /// CRV of the call associated with the route request unsigned crv, /// callID of the call associated with the route request const PString & callID, // outgoing IP or empty const PString & bindIP, // callerID or empty const PString & callerID, /// should this call be rejected bool reject = false ); /** Make a routing decision for a pending route request (inserted by SendRequest). @return True if the matching pending request has been found, false otherwise. */ bool RouteToAlias( /// alias for the routing target that /// will replace the original destination info const PString & agent, /// will replace the original destinationCallSignallAddress const PString & destinationip, /// identifier of the endpoint associated with the route request const PString & callingEpId, /// CRV of the call associated with the route request unsigned crv, /// callID of the call associated with the route request const PString & callID, // outgoing IP or empty const PString & bindIP, // callerID or empty const PString & callerID, /// should this call be rejected bool reject = false ); /** Reject a pending route request (inserted by SendRequest). @return True if the matching pending request has been found, false otherwise. */ bool RouteReject( /// identifier of the endpoint associated with the route request const PString & callingEpId, /// CRV of the call associated with the route request unsigned crv, /// callID of the call associated with the route request const PString & callID ); /** @return True if the specified alias matches a name of an existing virtual queue. */ bool IsDestinationVirtualQueue( const PString & destinationAlias /// alias to be matched ) const; private: /// a holder for a pending route request struct RouteRequest { RouteRequest( const PString & callingEpId, unsigned crv, const PString & callID, H225_ArrayOf_AliasAddress * agent, PString * callsignaladdr, PString * bindIP, PString * callerID ) : m_callingEpId((const char*)callingEpId), m_crv(crv), m_callID(callID), m_agent(agent), m_callsignaladdr(callsignaladdr), m_sourceIP(bindIP), m_callerID(callerID), m_reject(false) { } /// identifier for the endpoint associated with this request PString m_callingEpId; /// CRV for the call associated with this request unsigned m_crv; /// callID for the call associated with this request PString m_callID; /// aliases for the virtual queue matched (on input) /// aliases for the target agent - target route (on output) H225_ArrayOf_AliasAddress * m_agent; /// destinationCallSignallAddress for the target agent - target route IF NOT NULL PString * m_callsignaladdr; /// bindIP or empty PString * m_sourceIP; /// callerID or empty PString * m_callerID; /// should this call be rejected bool m_reject; /// a synchronization point for signaling that routing decision /// has been made by the external application PSyncPoint m_sync; }; typedef std::list RouteRequests; RouteRequest * InsertRequest( /// identifier for the endpoint associated with this request const PString & callingEpId, /// CRV for the call associated with this request unsigned crv, /// callID for the call associated with this request const PString & callID, /// a pointer to an array to be filled with agent aliases /// when the routing decision has been made H225_ArrayOf_AliasAddress * agent, /// a pointer to a string to be filled with a callSignalAddress /// when the routing decision has been made (optional) PString * callSigAdr, /// bind IP for BindAndRouteToGateway PString * bindIP, /// caller ID PString * callerID, /// set by the function to true if another route request for the same /// call is pending bool & duplicate ); /// an array of names (aliases) for the virtual queues PStringArray m_virtualQueueAliases; /// an array of prefixes for the virtual queues PStringArray m_virtualQueuePrefixes; /// a regular expression for the virtual queues PString m_virtualQueueRegex; /// virtual queues enabled/disabled bool m_active; /// time (in milliseconds) to wait for a routing decision to be made long m_requestTimeout; /// a list of active (pending) route requests RouteRequests m_pendingRequests; /// a mutex protecting pending requests and virtual queues lists PMutex m_listMutex; }; } // end of namespace Routing #endif // ROUTING_H gnugk-3.4/PaxHeaders.16356/Neighbor.h0000644000175000001440000000005012200732037015423 xustar000000000000000020 atime=1380868658 20 ctime=1380868609 gnugk-3.4/Neighbor.h0000644000175000001440000001456412200732037014376 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // Neighboring System for GNU Gatekeeper // // Copyright (c) Citron Network Inc. 2002-2003 // Copyright (c) 2004-2012, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef NEIGHBOR_H #define NEIGHBOR_H "@(#) $Id: Neighbor.h,v 1.35 2013/08/08 15:07:11 willamowius Exp $" #include #include #include "Routing.h" #include "gktimer.h" class H225_RasMessage; class H225_AdmissionRequest; class H225_LocationRequest; class H225_Setup_UUIE; class H225_Facility_UUIE; class H225_TransportAddress; class H225_AliasAddress; class H225_ArrayOf_AliasAddress; class H225_CryptoH323Token; class RasMsg; class RasServer; namespace Neighbors { using Routing::AdmissionRequest; using Routing::LocationRequest; using Routing::SetupRequest; using Routing::FacilityRequest; struct PrefixInfo { PrefixInfo() : m_length(0), m_priority(0) { } PrefixInfo(short int l, short int p) : m_length(l), m_priority(p) { } operator bool() const { return m_length >= 0; } bool operator<(PrefixInfo) const; short int m_length; // length of matched prefix short int m_priority; }; inline bool PrefixInfo::operator<(PrefixInfo o) const { return m_length > o.m_length || (m_length == o.m_length && m_priority < o.m_priority); } class Neighbor { public: typedef Neighbor Base; // for SimpleCreator template Neighbor(); virtual ~Neighbor(); bool SendLRQ(H225_RasMessage &); bool IsFrom(const PIPSocket::Address *ip) const { return GetIP() == *ip; } bool IsTraversalUser(const PString * user) const { return m_H46018Server && (m_authUser == *user); } bool IsTraversalZone(const PIPSocket::Address *ip) const { return (GetIP() == *ip) && (m_H46018Server || m_H46018Client); } bool IsTraversalClient(const PIPSocket::Address *ip) const { return (GetIP() == *ip) && m_H46018Server; } // it is from a client, if we are the server bool IsTraversalServer(const PIPSocket::Address *ip) const { return (GetIP() == *ip) && m_H46018Client; } // it is from a server, if we are the client void SetApparentIP(const PIPSocket::Address & ip) { m_ip = ip; } bool ForwardResponse() const { return m_forwardResponse; } int ForwardLRQ() const { return m_forwardto; } WORD GetDefaultHopCount() const { return m_forwardHopCount; } PString GetId() const { return m_id; } PString GetGkId() const { return m_gkid; } PIPSocket::Address GetIP() const; WORD GetPort() const; H225_LocationRequest & BuildLRQ(H225_RasMessage &, WORD, const H225_ArrayOf_AliasAddress &); // new virtual functions // the real constructor, get profile of this neighbor virtual bool SetProfile(const PString &, const PString &); // Sent profile based on SRV Record virtual bool SetProfile(const PString &, const H323TransportAddress &); void SetH46018Server(bool val) { m_H46018Server = val; } bool IsH46018Server() const { return m_H46018Server; } bool IsH46018Client() const { return m_H46018Client; } // send a H.460.18 keepAlive (triggered by a timer) void SendH46018GkKeepAlive(GkTimer* timer); void SetH46018GkKeepAliveInterval(int interval); // get PrefixInfo for a given aliases // if an alias is matched, set dest to the alias virtual PrefixInfo GetPrefixInfo(const H225_ArrayOf_AliasAddress &, H225_ArrayOf_AliasAddress & dest); virtual PrefixInfo GetIPInfo(const H225_TransportAddress & ip, H225_ArrayOf_AliasAddress & dest) const; // callbacks before sending LRQ // LRQ will not be sent if false is returned virtual bool OnSendingLRQ(H225_LocationRequest &); virtual bool OnSendingLRQ(H225_LocationRequest &, const AdmissionRequest &); virtual bool OnSendingLRQ(H225_LocationRequest &, const LocationRequest &); virtual bool OnSendingLRQ(H225_LocationRequest &, const SetupRequest &); virtual bool OnSendingLRQ(H225_LocationRequest &, const FacilityRequest &); // check if the given message is a valid reply from this neighbor virtual bool CheckReply(RasMsg *) const; // check if we require a password and if its correct virtual bool Authenticate(RasMsg *ras) const; // check if the given LRQ is acceptable virtual bool IsAcceptable(RasMsg *ras) const; virtual bool UseTLS() const { return m_useTLS; } protected: void SetForwardedInfo(const PString &); typedef std::map Prefixes; RasServer * m_rasSrv; PString m_id, m_gkid, m_password, m_name, m_authUser; mutable PIPSocket::Address m_ip; mutable WORD m_port; WORD m_forwardHopCount; bool m_dynamic; bool m_acceptForwarded; bool m_forwardResponse; int m_forwardto; Prefixes m_sendPrefixes; PString m_sendIPs; PStringArray m_sendAliases; PStringArray m_acceptPrefixes; bool m_externalGK; PString m_sendAuthUser, m_sendPassword; // user + password to send to neighbor int m_keepAliveTimerInterval; GkTimerManager::GkTimerHandle m_keepAliveTimer; bool m_H46018Server; bool m_H46018Client; bool m_useTLS; }; class NeighborList { public: typedef std::list List; NeighborList(); virtual ~NeighborList(); void OnReload(); bool CheckLRQ(RasMsg *) const; bool CheckIP(const PIPSocket::Address &) const; bool IsTraversalClient(const PIPSocket::Address &) const; bool IsTraversalServer(const PIPSocket::Address &) const; // return the neighbor's ID from the list by signal address PString GetNeighborIdBySigAdr(const H225_TransportAddress & sigAd); PString GetNeighborIdBySigAdr(const PIPSocket::Address & sigAd); // return the neighbor's gatekeeper ID from the list by signal address PString GetNeighborGkIdBySigAdr(const H225_TransportAddress & sigAd); PString GetNeighborGkIdBySigAdr(const PIPSocket::Address & sigAd); // return the neighbor's use eof TLS from the list by signal address bool GetNeighborTLSBySigAdr(const H225_TransportAddress & sigAd); bool GetNeighborTLSBySigAdr(const PIPSocket::Address & sigAd); operator List & () { return m_neighbors; } operator const List & () const { return m_neighbors; } private: List m_neighbors; }; /* Not used currently H225_CryptoH323Token BuildAccessToken(const H225_TransportAddress &, const PIPSocket::Address &); */ bool DecodeAccessToken(const H225_CryptoH323Token &, const PIPSocket::Address &, H225_TransportAddress &); } // end of namespace Neighbors #endif // NEIGHBOR_H gnugk-3.4/PaxHeaders.16356/singleton.h0000644000175000001440000000005012104466620015675 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/singleton.h0000644000175000001440000000571412104466620014645 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // singleton.h // // Copyright (c) 2001-2013, Jan Willamowius // // All singleton objects are put into a list // so that it would be delete when program exits. // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef SINGLETON_H #define SINGLETON_H "@(#) $Id: singleton.h,v 1.25 2013/02/06 14:49:52 willamowius Exp $" // STL #include #include // // a list of pointers that would delete all objects // referred by the pointers in the list on destruction // template class listptr : public std::list { public: listptr() : clear_list(false) { } ~listptr(); bool clear_list; private: static void delete_obj(void *t) { delete static_cast(t); } }; template listptr::~listptr() { // TODO: deleting all singletons automatically like this at shutdown doesn't work! // thats why currently all singletons must be deleted manually, eg. in ShutdownHandler() clear_list = true; std::for_each(begin(), end(), delete_obj); } // Base class for all singletons class SingletonBase { public: SingletonBase(const char *); virtual ~SingletonBase(); private: const char *m_name; // Note the SingletonBase instance is not singleton itself :p // However, list of singletons *are* singleton // But we can't put the singleton into the list :( static listptr _instance_list; }; // // A singleton class should be derived from this template. // class Ts : public Singleton { // ... // }; // // If the class is instantiated more than once, // a runtime error would be thrown // // To access the singleton use T::Instance() // template class Singleton : public SingletonBase { public: static T *Instance(); static bool InstanceExists(); protected: Singleton(const char *); virtual ~Singleton(); public: static T *m_Instance; static PMutex m_CreationLock; }; template Singleton::Singleton(const char *n) : SingletonBase(n) { if (m_Instance != NULL) { PTRACE(0, "Runtime error: Duplicate singleton instances of " << n); } } template Singleton::~Singleton() { PWaitAndSignal lock(m_CreationLock); m_Instance = NULL; } // Function to access the singleton template T *Singleton::Instance() { if (m_Instance == NULL) { PWaitAndSignal lock(m_CreationLock); // We have to check it again after we got the lock if (m_Instance == NULL) m_Instance = new T; } return m_Instance; } // Function to check for existance of singleton template bool Singleton::InstanceExists() { return (m_Instance != NULL); } // static members template T *Singleton::m_Instance = NULL; template PMutex Singleton::m_CreationLock; #endif // SINGLETON_H gnugk-3.4/PaxHeaders.16356/cmake0000644000175000001440000000005012223461003014515 xustar000000000000000020 atime=1380868612 20 ctime=1380868611 gnugk-3.4/cmake/0000755000175000001440000000000012223461003013533 5ustar00janusers00000000000000gnugk-3.4/cmake/PaxHeaders.16356/FindPTLIB.cmake0000644000175000001440000000005011325320457017260 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/cmake/FindPTLIB.cmake0000644000175000001440000000064711325320457016230 0ustar00janusers00000000000000string(TOLOWER "${CMAKE_SYSTEM_NAME}_${CMAKE_SYSTEM_PROCESSOR}" PLATFORM) find_path(PTLIB_INCLUDE ptlib.h PATHS $ENV{PTLIBDIR}/include) find_library(PTLIB_LIBRARY NAME pt_s PATHS $ENV{PTLIBDIR}/lib_${PLATFORM}) if (PTLIB_INCLUDE AND PTLIB_LIBRARY) MESSAGE(STATUS "PTLib found: ${PTLIB_INCLUDE}, ${PTLIB_LIBRARY}") else () MESSAGE(FATAL_ERROR "PTLib not found") endif () #mark_as_advanced(PTLIB_LIBRARY PTLIB_INCLUDE) gnugk-3.4/cmake/PaxHeaders.16356/FindODBC.cmake0000644000175000001440000000005011325320457017115 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/cmake/FindODBC.cmake0000644000175000001440000000070711325320457016062 0ustar00janusers00000000000000find_path(ODBC_INCLUDE_DIR sqlext.h PATHS /usr/include/unixodbc /usr/local/include /usr/local/include/unixodbc /usr/local/unixodbc/include) find_library(ODBC_LIBRARY NAME odbc PATH_SUFFIXES lib64 lib PATHS /usr /usr/lib/unixodbc /usr/lib64/unixodbc /usr/local /usr/local/lib/unixodbc /usr/local/lib64/unixodbc /usr/local/unixodbc) #message(STATUS "${ODBC_INCLUDE_DIR} ${ODBC_LIBRARY}") if (ODBC_INCLUDE_DIR AND ODBC_LIBRARY) set(ODBC_FOUND 1) endif () gnugk-3.4/cmake/PaxHeaders.16356/FindSQLITE.cmake0000644000175000001440000000005011325320457017407 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/cmake/FindSQLITE.cmake0000644000175000001440000000051511325320457016351 0ustar00janusers00000000000000find_path(SQLITE_INCLUDE_DIR sqlite3.h PATHS /usr/include/sqlite /usr/local/include /usr/local/include/sqlite) find_library(SQLITE_LIBRARY NAME sqlite3 PATH_SUFFIXES lib64 lib PATHS /usr /usr/local) #message(STATUS "${SQLITE_INCLUDE_DIR} ${SQLITE_LIBRARY}") if (SQLITE_INCLUDE_DIR AND SQLITE_LIBRARY) set(SQLITE_FOUND 1) endif () gnugk-3.4/cmake/PaxHeaders.16356/FindH323PLUS.cmake0000644000175000001440000000005011325320457017531 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/cmake/FindH323PLUS.cmake0000644000175000001440000000064311325320457016475 0ustar00janusers00000000000000string(TOLOWER "${CMAKE_SYSTEM_NAME}_${CMAKE_SYSTEM_PROCESSOR}" PLATFORM) find_path(H323PLUS_INCLUDE openh323buildopts.h PATHS $ENV{OPENH323DIR}/include) find_library(H323PLUS_LIBRARY NAME h323_${PLATFORM}__s PATHS $ENV{OPENH323DIR}/lib) if (H323PLUS_INCLUDE AND H323PLUS_LIBRARY) MESSAGE(STATUS "H323Plus found: ${H323PLUS_INCLUDE}, ${H323PLUS_LIBRARY}") else () MESSAGE(FATAL_ERROR "H323Plus not found") endif () gnugk-3.4/cmake/PaxHeaders.16356/FindPOSTGRESQL.cmake0000644000175000001440000000005011325320457020111 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/cmake/FindPOSTGRESQL.cmake0000644000175000001440000000061511325320457017054 0ustar00janusers00000000000000find_path(POSTGRESQL_INCLUDE_DIR libpq-fe.h PATHS /usr/include/pgsql /usr/local/include /usr/local/include/pgsql /usr/local/pgsql/include) find_library(POSTGRESQL_LIBRARY NAME pq PATH_SUFFIXES lib64 lib PATHS /usr /usr/local /usr/local/pgsql) #message(STATUS "${POSTGRESQL_INCLUDE_DIR} ${POSTGRESQL_LIBRARY}") if (POSTGRESQL_INCLUDE_DIR AND POSTGRESQL_LIBRARY) set(POSTGRESQL_FOUND 1) endif () gnugk-3.4/cmake/PaxHeaders.16356/FindMYSQL.cmake0000644000175000001440000000005011325320457017313 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/cmake/FindMYSQL.cmake0000644000175000001440000000046311325320457016257 0ustar00janusers00000000000000find_path(MYSQL_INCLUDE_DIR mysql.h PATHS /usr/local/include/mysql /usr/include/mysql) find_library(MYSQL_LIBRARY NAME mysqlclient PATH_SUFFIXES lib64 lib PATHS /usr/local /usr) #message(STATUS "${MYSQL_INCLUDE_DIR} ${MYSQL_LIBRARY}") if (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) set(MYSQL_FOUND 1) endif () gnugk-3.4/cmake/PaxHeaders.16356/FindFIREBIRD.cmake0000644000175000001440000000005011325320457017574 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/cmake/FindFIREBIRD.cmake0000644000175000001440000000061611325320457016540 0ustar00janusers00000000000000find_path(FIREBIRD_INCLUDE_DIR ibase.h PATHS /usr/include/firebird /usr/local/include /usr/local/include/firebird /usr/local/firebird/include) find_library(FIREBIRD_LIBRARY NAME fbclient PATH_SUFFIXES lib64 lib PATHS /usr /usr/local /usr/local/firebird) #message(STATUS "${FIREBIRD_INCLUDE_DIR} ${FIREBIRD_LIBRARY}") if (FIREBIRD_INCLUDE_DIR AND FIREBIRD_LIBRARY) set(FIREBIRD_FOUND 1) endif () gnugk-3.4/PaxHeaders.16356/addpasswd_2010.vcxproj0000644000175000001440000000005011451722072017553 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/addpasswd_2010.vcxproj0000644000175000001440000002220011451722072016510 0ustar00janusers00000000000000 Debug Win32 Release Win32 addpasswd {2C306BF0-7FBE-4136-B021-AFA258898341} Application false MultiByte Application false MultiByte <_ProjectFileVersion>10.0.21006.1 $(Configuration)\ $(Configuration)\ true $(Configuration)\ $(Configuration)\ false AllRules.ruleset AllRules.ruleset $(ProjectDir)..\h323plus\include;$(ProjectDir)..\ptlib\include;$(IncludePath) $(ProjectDir)..\h323plus\lib;$(ProjectDir)..\ptlib\lib;$(LibraryPath) $(ProjectDir)..\h323plus\include;$(ProjectDir)..\ptlib\include;$(IncludePath) $(ProjectDir)..\h323plus\lib;$(ProjectDir)..\ptlib\lib;$(LibraryPath) _DEBUG;%(PreprocessorDefinitions) true true Win32 .\Debug/addpasswd.tlb Disabled WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL $(TargetDir)addpasswd.pch $(TargetDir) $(TargetDir) $(TargetDir) Level4 true EditAndContinue _DEBUG;_AFXDLL;%(PreprocessorDefinitions) 0x0409 ptlibsd.lib;%(AdditionalDependencies) $(TargetDir)addpasswd.exe true true $(TargetDir)addpasswd.pdb Console false MachineX86 true .\Debug/addpasswd.bsc NDEBUG;%(PreprocessorDefinitions) true true Win32 .\Release/addpasswd.tlb MaxSpeed AnySuitable true Speed NDEBUG;PTRACING;%(PreprocessorDefinitions) false MultiThreadedDLL true Release/addpasswd.pch Release/ Release/ Release/ Level4 true ProgramDatabase true NDEBUG;_AFXDLL;%(PreprocessorDefinitions) 0x0409 ptlibs.lib;mpr.lib;winmm.lib;%(AdditionalDependencies) Release/addpasswd.exe true Release/addpasswd.pdb Console false MachineX86 UseLinkTimeCodeGeneration true .\Release/addpasswd.bsc %(PreprocessorDefinitions) %(PreprocessorDefinitions) gnugk-3.4/PaxHeaders.16356/gksql_pgsql.cxx0000644000175000001440000000005012204163171016571 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gksql_pgsql.cxx0000644000175000001440000002775612204163171015553 0ustar00janusers00000000000000/* * gksql_pgsql.cxx * * PostgreSQL driver module for GnuGk * * Copyright (c) 2004, Michal Zygmuntowicz * Copyright (c) 2006-2012, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include "config.h" #if HAS_PGSQL #include #include #include "gksql.h" static PDynaLink g_sharedLibrary; static void (*g_PQclear)(PGresult *res) = NULL; static char * (*g_PQcmdTuples)(PGresult *res) = NULL; static char * (*g_PQerrorMessage)(const PGconn *conn) = NULL; static size_t (*g_PQescapeStringConn)(PGconn *conn, char *to, const char *from, size_t length, int *error) = NULL; static PGresult * (*g_PQexec)(PGconn *conn, const char *query) = NULL; static void (*g_PQfinish)(PGconn *conn) = NULL; static char * (*g_PQfname)(const PGresult *res, int field_num) = NULL; static int (*g_PQgetlength)(const PGresult *res, int tup_num, int field_num) = NULL; static char * (*g_PQgetvalue)(const PGresult *res, int tup_num, int field_num) = NULL; static int (*g_PQnfields)(const PGresult *res) = NULL; static int (*g_PQntuples)(const PGresult *res) = NULL; static char * (*g_PQresultErrorMessage)(const PGresult *res) = NULL; static ExecStatusType (*g_PQresultStatus)(const PGresult *res) = NULL; static PGconn * (*g_PQsetdbLogin)(const char *pghost, const char *pgport, const char *pgoptions, const char *pgtty, const char *dbName, const char *login, const char *pwd) = NULL; static ConnStatusType (*g_PQstatus)(const PGconn *conn) = NULL; /** Class that encapsulates SQL query result for PostgreSQL backend. It does not provide any multithread safety, so should be accessed from a single thread at time. */ class GkPgSQLResult : public GkSQLResult { public: /// Build the result from SELECT type query GkPgSQLResult( /// SELECT type query result PGresult* selectResult ); /// Build the result from INSERT, DELETE or UPDATE query GkPgSQLResult( /// number of rows affected by the query long numRowsAffected ); /// Build the empty result and store query execution error information GkPgSQLResult( /// PostgreSQL specific error code unsigned int errorCode, /// PostgreSQL specific error message text const char* errorMsg ); virtual ~GkPgSQLResult(); /** @return Backend specific error message, if the query failed. */ virtual PString GetErrorMessage(); /** @return Backend specific error code, if the query failed. */ virtual long GetErrorCode(); /** Fetch a single row from the result set. After each row is fetched, cursor position is moved to a next row. @return True if the row has been fetched, false if no more rows are available. */ virtual bool FetchRow( /// array to be filled with string representations of the row fields PStringArray& result ); virtual bool FetchRow( /// array to be filled with string representations of the row fields ResultRow& result ); private: GkPgSQLResult(); GkPgSQLResult(const GkPgSQLResult&); GkPgSQLResult& operator=(const GkPgSQLResult&); protected: /// query result for SELECT type queries, NULL otherwise PGresult* m_sqlResult; /// the most recent row returned by fetch operation int m_sqlRow; /// PgSQL specific error code (if the query failed) unsigned int m_errorCode; /// PgSQL specific error message text (if the query failed) PString m_errorMessage; }; /// PostgreSQL backend connection implementation. class GkPgSQLConnection : public GkSQLConnection { public: /// Build a new PgSQL connection object GkPgSQLConnection( /// name to use in the log const char* name = "PostgreSQL" ); virtual ~GkPgSQLConnection(); protected: class PgSQLConnWrapper : public GkSQLConnection::SQLConnWrapper { public: PgSQLConnWrapper( /// unique identifier for this connection int id, /// host:port this connection is made to const PString& host, /// PostgreSQL connection object PGconn* conn ) : SQLConnWrapper(id, host), m_conn(conn) {} virtual ~PgSQLConnWrapper(); private: PgSQLConnWrapper(); PgSQLConnWrapper(const PgSQLConnWrapper&); PgSQLConnWrapper& operator=(const PgSQLConnWrapper&); public: PGconn* m_conn; }; /** Create a new SQL connection using parameters stored in this object. When the connection is to be closed, the object is simply deleted using delete operator. @return NULL if database connection could not be established or an object of PgSQLConnWrapper class. */ virtual SQLConnPtr CreateNewConnection( /// unique identifier for this connection int id ); /** Execute the query using specified SQL connection. @return Query execution result. */ virtual GkSQLResult* ExecuteQuery( /// SQL connection to use for query execution SQLConnPtr conn, /// query string const char* queryStr, /// maximum time (ms) for the query execution, -1 means infinite long timeout = -1 ); /** Escape any special characters in the string, so it can be used in a SQL query. @return Escaped string. */ virtual PString EscapeString( /// SQL connection to get escaping parameters from SQLConnPtr conn, /// string to be escaped const char* str ); private: GkPgSQLConnection(const GkPgSQLConnection&); GkPgSQLConnection& operator=(const GkPgSQLConnection&); }; GkPgSQLResult::GkPgSQLResult( /// SELECT type query result PGresult* selectResult ) : GkSQLResult(false), m_sqlResult(selectResult), m_sqlRow(-1), m_errorCode(0) { if (m_sqlResult) { m_numRows = (*g_PQntuples)(m_sqlResult); m_numFields = (*g_PQnfields)(m_sqlResult); } else m_queryError = true; } GkPgSQLResult::GkPgSQLResult( /// number of rows affected by the query long numRowsAffected ) : GkSQLResult(false), m_sqlResult(NULL), m_sqlRow(-1), m_errorCode(0) { m_numRows = numRowsAffected; } GkPgSQLResult::GkPgSQLResult( /// PostgreSQL specific error code unsigned int errorCode, /// PostgreSQL specific error message text const char* errorMsg ) : GkSQLResult(true), m_sqlResult(NULL), m_sqlRow(-1), m_errorCode(errorCode), m_errorMessage(errorMsg) { } GkPgSQLResult::~GkPgSQLResult() { if (m_sqlResult) (*g_PQclear)(m_sqlResult); } PString GkPgSQLResult::GetErrorMessage() { return m_errorMessage; } long GkPgSQLResult::GetErrorCode() { return m_errorCode; } bool GkPgSQLResult::FetchRow( /// array to be filled with string representations of the row fields PStringArray& result ) { if (m_sqlResult == NULL || m_numRows <= 0) return false; if (m_sqlRow < 0) m_sqlRow = 0; if (m_sqlRow >= m_numRows) return false; result.SetSize(m_numFields); for (PINDEX i = 0; i < m_numFields; i++) result[i] = PString( (*g_PQgetvalue)(m_sqlResult, m_sqlRow, i), (*g_PQgetlength)(m_sqlResult, m_sqlRow, i) ); m_sqlRow++; return true; } bool GkPgSQLResult::FetchRow( /// array to be filled with string representations of the row fields ResultRow& result ) { if (m_sqlResult == NULL || m_numRows <= 0) return false; if (m_sqlRow < 0) m_sqlRow = 0; if (m_sqlRow >= m_numRows) return false; result.resize(m_numFields); for (PINDEX i = 0; i < m_numFields; i++) { result[i].first = PString( (*g_PQgetvalue)(m_sqlResult, m_sqlRow, i), (*g_PQgetlength)(m_sqlResult, m_sqlRow, i) ); result[i].second = (*g_PQfname)(m_sqlResult, i); } m_sqlRow++; return true; } GkPgSQLConnection::GkPgSQLConnection( /// name to use in the log const char* name ) : GkSQLConnection(name) { } GkPgSQLConnection::~GkPgSQLConnection() { } GkPgSQLConnection::PgSQLConnWrapper::~PgSQLConnWrapper() { (*g_PQfinish)(m_conn); } GkSQLConnection::SQLConnPtr GkPgSQLConnection::CreateNewConnection( /// unique identifier for this connection int id ) { if (!g_sharedLibrary.IsLoaded()) { if (m_library.IsEmpty()) { m_library = "libpq" + g_sharedLibrary.GetExtension(); } if (!g_sharedLibrary.Open(m_library)) { PTRACE (1, GetName() << "\tCan't load library " << m_library); return NULL; } if (!g_sharedLibrary.GetFunction("PQclear", (PDynaLink::Function &)g_PQclear) || !g_sharedLibrary.GetFunction("PQcmdTuples", (PDynaLink::Function &)g_PQcmdTuples) || !g_sharedLibrary.GetFunction("PQerrorMessage", (PDynaLink::Function &)g_PQerrorMessage) || !g_sharedLibrary.GetFunction("PQescapeStringConn", (PDynaLink::Function &)g_PQescapeStringConn) || !g_sharedLibrary.GetFunction("PQexec", (PDynaLink::Function &)g_PQexec) || !g_sharedLibrary.GetFunction("PQfinish", (PDynaLink::Function &)g_PQfinish) || !g_sharedLibrary.GetFunction("PQfname", (PDynaLink::Function &)g_PQfname) || !g_sharedLibrary.GetFunction("PQgetlength", (PDynaLink::Function &)g_PQgetlength) || !g_sharedLibrary.GetFunction("PQgetvalue", (PDynaLink::Function &)g_PQgetvalue) || !g_sharedLibrary.GetFunction("PQnfields", (PDynaLink::Function &)g_PQnfields) || !g_sharedLibrary.GetFunction("PQntuples", (PDynaLink::Function &)g_PQntuples) || !g_sharedLibrary.GetFunction("PQresultErrorMessage", (PDynaLink::Function &)g_PQresultErrorMessage) || !g_sharedLibrary.GetFunction("PQresultStatus", (PDynaLink::Function &)g_PQresultStatus) || !g_sharedLibrary.GetFunction("PQsetdbLogin", (PDynaLink::Function &)g_PQsetdbLogin) || !g_sharedLibrary.GetFunction("PQstatus", (PDynaLink::Function &)g_PQstatus) ) { #ifdef hasDynaLinkGetLastError PTRACE (1, GetName() << "\tFailed to load shared database library: " << g_sharedLibrary.GetLastError()); #else PTRACE (1, GetName() << "\tFailed to load shared database library: unknown error"); #endif g_sharedLibrary.Close(); SNMP_TRAP(5, SNMPError, Database, GetName() + " DLL load error"); return NULL; } } PGconn* conn; const PString portStr(m_port); // const PString optionsStr("connect_timeout=10000"); if ((conn = (*g_PQsetdbLogin)(m_host, m_port ? (const char*)portStr : (const char*)NULL, NULL /*(const char*)optionsStr*/, NULL, m_database, m_username, m_password.IsEmpty() ? (const char*)NULL : (const char*)m_password )) && (*g_PQstatus)(conn) == CONNECTION_OK) { PTRACE(5, GetName() << "\tPgSQL connection to " << m_username << '@' << m_host << '[' << m_database << "] established successfully"); return new PgSQLConnWrapper(id, m_host, conn); } else { PTRACE(2, GetName() << "\tPgSQL connection to " << m_username << '@' << m_host << '[' << m_database << "] failed (PQsetdbLogin failed): " << (conn ? (*g_PQerrorMessage)(conn) : "")); SNMP_TRAP(5, SNMPError, Database, GetName() + " connection failed") if (conn) (*g_PQfinish)(conn); } return NULL; } GkSQLResult* GkPgSQLConnection::ExecuteQuery( /// SQL connection to use for query execution GkSQLConnection::SQLConnPtr conn, /// query string const char* queryStr, /// maximum time (ms) for the query execution, -1 means infinite long /*timeout*/ ) { PGconn* pgsqlconn = ((PgSQLConnWrapper*)conn)->m_conn; PGresult* result = (*g_PQexec)(pgsqlconn, queryStr); if (result == NULL) { GkSQLResult * sqlResult = new GkPgSQLResult(PGRES_FATAL_ERROR, (*g_PQerrorMessage)(pgsqlconn)); Disconnect(); return sqlResult; } ExecStatusType resultInfo = (*g_PQresultStatus)(result); switch (resultInfo) { case PGRES_COMMAND_OK: return new GkPgSQLResult( (*g_PQcmdTuples)(result) ? atoi((*g_PQcmdTuples)(result)) : 0 ); case PGRES_TUPLES_OK: return new GkPgSQLResult(result); default: GkSQLResult * sqlResult = new GkPgSQLResult(resultInfo, (*g_PQresultErrorMessage)(result)); Disconnect(); return sqlResult; } } PString GkPgSQLConnection::EscapeString( /// SQL connection to get escaping parameters from SQLConnPtr conn, /// string to be escaped const char* str ) { PString escapedStr; const size_t numChars = str ? strlen(str) : 0; int err = 0; if (numChars) { char * buf = (char *)malloc(numChars * 2 + 1); (*g_PQescapeStringConn) (((PgSQLConnWrapper*)conn)->m_conn, buf, str, numChars, &err); escapedStr = buf; free(buf); } return escapedStr; } namespace { GkSQLCreator PgSQLCreator("PostgreSQL"); } #endif /* HAS_PGSQL */ gnugk-3.4/PaxHeaders.16356/gk_2008.vcproj0000644000175000001440000000005012210737364016025 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gk_2008.vcproj0000644000175000001440000004315212210737364014773 0ustar00janusers00000000000000 gnugk-3.4/PaxHeaders.16356/rasinfo.h0000644000175000001440000000005011461315737015343 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/rasinfo.h0000644000175000001440000002171711461315737014314 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // rasinfo.h // // RAS type traits // Define template classes that associate RAS tags and types // // Copyright (c) Citron Network Inc. 2003 // Copyright (c) 2006-2010, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef RASINFO_H #define RASINFO_H "@(#) $Id: rasinfo.h,v 1.7 2010/10/25 15:01:51 willamowius Exp $" // define a type for an RAS tag template struct RasTag { operator unsigned() const { return I; } }; // the template classes map RAS tags to its corresponding RAS types template struct RasType; template<> struct RasType { typedef H225_GatekeeperRequest Type; }; template<> struct RasType { typedef H225_GatekeeperConfirm Type; }; template<> struct RasType { typedef H225_GatekeeperReject Type; }; template<> struct RasType { typedef H225_RegistrationRequest Type; }; template<> struct RasType { typedef H225_RegistrationConfirm Type; }; template<> struct RasType { typedef H225_RegistrationReject Type; }; template<> struct RasType { typedef H225_UnregistrationRequest Type; }; template<> struct RasType { typedef H225_UnregistrationConfirm Type; }; template<> struct RasType { typedef H225_UnregistrationReject Type; }; template<> struct RasType { typedef H225_AdmissionRequest Type; }; template<> struct RasType { typedef H225_AdmissionConfirm Type; }; template<> struct RasType { typedef H225_AdmissionReject Type; }; template<> struct RasType { typedef H225_BandwidthRequest Type; }; template<> struct RasType { typedef H225_BandwidthConfirm Type; }; template<> struct RasType { typedef H225_BandwidthReject Type; }; template<> struct RasType { typedef H225_DisengageRequest Type; }; template<> struct RasType { typedef H225_DisengageConfirm Type; }; template<> struct RasType { typedef H225_DisengageReject Type; }; template<> struct RasType { typedef H225_LocationRequest Type; }; template<> struct RasType { typedef H225_LocationConfirm Type; }; template<> struct RasType { typedef H225_LocationReject Type; }; template<> struct RasType { typedef H225_InfoRequest Type; }; template<> struct RasType { typedef H225_InfoRequestResponse Type; }; template<> struct RasType { typedef H225_NonStandardMessage Type; }; template<> struct RasType { typedef H225_UnknownMessageResponse Type; }; template<> struct RasType { typedef H225_RequestInProgress Type; }; template<> struct RasType { typedef H225_ResourcesAvailableIndicate Type; }; template<> struct RasType { typedef H225_ResourcesAvailableConfirm Type; }; template<> struct RasType { typedef H225_InfoRequestAck Type; }; template<> struct RasType { typedef H225_InfoRequestNak Type; }; template<> struct RasType { typedef H225_ServiceControlIndication Type; }; template<> struct RasType { typedef H225_ServiceControlResponse Type; }; // associate a tag and its type template struct TagInfo { typedef RasTag Tag; typedef typename RasType::Type Type; enum { tag = I, // there are just 32 types of RAS, lucky! flag = (1 << I) }; }; // a dirty trick, but works :p template struct RequestInfo : public TagInfo { typedef RasTag ConfirmTag; typedef RasTag RejectTag; typedef typename RasType::Type ConfirmType; typedef typename RasType::Type RejectType; }; template struct ConfirmInfo : public TagInfo { typedef RasTag RequestTag; typedef typename RasType::Type RequestType; }; template struct RejectInfo : public TagInfo { typedef RasTag RequestTag; typedef typename RasType::Type RequestType; }; // define an RAS request and all its associated types template struct RasInfo; // RAS request template<> struct RasInfo : public RequestInfo {}; template<> struct RasInfo : public RequestInfo {}; template<> struct RasInfo : public RequestInfo {}; template<> struct RasInfo : public RequestInfo {}; template<> struct RasInfo : public RequestInfo {}; template<> struct RasInfo : public RequestInfo {}; template<> struct RasInfo : public RequestInfo {}; // RAS confirm template<> struct RasInfo : public ConfirmInfo {}; template<> struct RasInfo : public ConfirmInfo {}; template<> struct RasInfo : public ConfirmInfo {}; template<> struct RasInfo : public ConfirmInfo {}; template<> struct RasInfo : public ConfirmInfo {}; template<> struct RasInfo : public ConfirmInfo {}; template<> struct RasInfo : public ConfirmInfo {}; // RAS reject template<> struct RasInfo : public RejectInfo {}; template<> struct RasInfo : public RejectInfo {}; template<> struct RasInfo : public RejectInfo {}; template<> struct RasInfo : public RejectInfo {}; template<> struct RasInfo : public RejectInfo {}; template<> struct RasInfo : public RejectInfo {}; template<> struct RasInfo : public RejectInfo {}; // others template<> struct RasInfo : public TagInfo {}; template<> struct RasInfo : public TagInfo { typedef RasTag ConfirmTag; typedef RasTag RejectTag; typedef RasType::Type ConfirmType; typedef RasType::Type RejectType; }; template<> struct RasInfo : public TagInfo {}; template<> struct RasInfo : public TagInfo {}; template<> struct RasInfo : public TagInfo {}; template<> struct RasInfo : public TagInfo { typedef RasTag ConfirmTag; typedef RasType::Type ConfirmType; }; template<> struct RasInfo : public TagInfo {}; template<> struct RasInfo : public TagInfo {}; template<> struct RasInfo : public TagInfo {}; template<> struct RasInfo : public TagInfo { typedef RasTag ConfirmTag; typedef RasType::Type ConfirmType; }; template<> struct RasInfo : public TagInfo {}; #endif // RASINFO_H gnugk-3.4/PaxHeaders.16356/clirw.cxx0000644000175000001440000000005012211166636015371 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/clirw.cxx0000644000175000001440000007565012211166636014347 0ustar00janusers00000000000000/* * clirw.cxx * * Module for CLI/ANI manipulation. * * $Id: clirw.cxx,v 1.31 2013/09/02 19:57:18 willamowius Exp $ * * Copyright (c) 2005, Michal Zygmuntowicz * Copyright (c) 2007-2013, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include #include #include "clirw.h" #include #include #include #include "sigmsg.h" #include "stl_supp.h" #include "h323util.h" #include "RasTbl.h" #include "RasPDU.h" #include "gkauth.h" #include "Toolkit.h" #include "gksql.h" namespace { const char * const CLIRewriteSection = "RewriteCLI"; const char * const CLIRewriteSQLSection = "RewriteCLI::SQL"; const char * const ProcessSourceAddress = "ProcessSourceAddress"; const char * const RemoveH323Id = "RemoveH323Id"; const char * const CLIRPolicy = "CLIRPolicy"; const char * const ReservedKeys[] = { ProcessSourceAddress, RemoveH323Id, CLIRPolicy, NULL }; } CLIRewrite::RewriteRule::RewriteRule() : m_matchType(MatchDialedNumber), m_rewriteType(PrefixToNumber), m_screeningType(NoScreening), m_manualCLIR(CLIRPassthrough), m_CLIRPolicy(IgnoreCLIR) { } PString CLIRewrite::RewriteRule::AsString() const { PString s; if (m_manualCLIR == RestrictPresentation) s = "restrict"; else if (m_manualCLIR == AllowPresentation) s = "allow"; if (m_CLIRPolicy != IgnoreCLIR) { if (!s) s += ","; if (m_CLIRPolicy == ForwardCLIR) s += "forward"; else if (m_CLIRPolicy == ApplyCLIRForTerminals) s += "applyforterminals"; else s += "apply"; } if (!s) s = "pi=" + s + " "; if (m_matchType == MatchDialedNumber) s += "dialed number: "; else if (m_matchType == MatchDestinationNumber) s += "destination number: "; else s += "caller number: "; s += m_prefix.empty() ? "any" : m_prefix.c_str(); if (m_rewriteType == PrefixToNumber) s += " = "; else if (m_rewriteType == PrefixToPrefix) s += " *= "; else if (m_rewriteType == NumberToNumber) s += " ~= "; else if (m_rewriteType == PrefixToH323Id) s += " ^= "; else if (m_rewriteType == NumberToH323Id) s += " /= "; else s += " ?unknown rewrite rule type? "; std::vector::const_iterator cli = m_cli.begin(); while (cli != m_cli.end()) { s += cli->c_str(); if (++cli != m_cli.end()) s += ", "; } if (m_cli.empty()) s += (m_screeningType == HideFromTerminals) ? "hide from terminals only" : "hide"; return s; } namespace { struct RewriteRule_greater : public std::binary_function { bool operator()(const CLIRewrite::RewriteRule &e1, const CLIRewrite::RewriteRule &e2) const { long long int diff = e1.m_matchType - e2.m_matchType; if (diff != 0) return diff < 0; diff = e1.m_prefix.length() - e2.m_prefix.length(); if (diff != 0) return diff > 0; return e1.m_prefix.compare(e2.m_prefix) > 0; } }; struct SingleIpRule_greater : public std::binary_function { bool operator()(const CLIRewrite::SingleIpRule &e1, const CLIRewrite::SingleIpRule &e2) const { if (e1.first.IsAny()) { if (!e2.first.IsAny()) return false; } else { if (e2.first.IsAny()) return true; int diff = e1.first.Compare(e2.first); if (diff != 0) return diff > 0; } return false; } }; struct DoubleIpRule_greater : public std::binary_function { bool operator()(const CLIRewrite::DoubleIpRule &e1, const CLIRewrite::DoubleIpRule &e2) const { if (e1.first.IsAny()) { if (!e2.first.IsAny()) return false; } else { if (e2.first.IsAny()) return true; int diff = e1.first.Compare(e2.first); if (diff != 0) return diff > 0; } return false; } }; } /* namespace */ CLIRewrite::CLIRewrite() : m_processSourceAddress(true), m_removeH323Id(false), m_CLIRPolicy(RewriteRule::IgnoreCLIR), m_sqlConn(NULL) { PConfig * cfg = GkConfig(); unsigned inboundRules = 0, outboundRules = 0; SingleIpRules::iterator siprule = m_inboundRules.end(); DoubleIpRules::iterator diprule = m_outboundRules.end(); const PStringToString kv = cfg->GetAllKeyValues(CLIRewriteSection); for (PINDEX i = 0; i < kv.GetSize(); i++) { PString key = kv.GetKeyAt(i); unsigned j = 0; while (ReservedKeys[j] != NULL) { if (key == ReservedKeys[j++]) { break; } } if (ReservedKeys[j] != NULL) continue; if (key[0] == '%') { const PINDEX sepIndex = key.Find('%', 1); if (sepIndex != P_MAX_INDEX) key = key.Mid(sepIndex + 1).Trim(); } // on Unix multiple entries for the same key are concatenated // to one string and separated by a new line PStringArray dataLines = kv.GetDataAt(i).Tokenise("\n", FALSE); for (PINDEX d = 0; d < dataLines.GetSize(); d++) { PString data = dataLines[d]; RewriteRules *rules = NULL; bool newsiprule = false, newdiprule = false, inbound = false; NetworkAddress addr; // check the rule type (inbound/outbound) if (key.Find("in:") == 0) { const PString ip = key.Mid(3).Trim(); inbound = true; if (!(ip == "*" || ip == "any")) addr = NetworkAddress(ip); // check if the IP is already on the list siprule = m_inboundRules.begin(); while (siprule != m_inboundRules.end()) { if (siprule->first.m_address.IsAny() || addr.m_address.IsAny()) { if (siprule->first.m_address.IsAny() && addr.m_address.IsAny()) break; } else if (siprule->first == addr) break; ++siprule; } // append the new IP to the list if (siprule == m_inboundRules.end()) { m_inboundRules.resize(m_inboundRules.size() + 1); siprule = m_inboundRules.end() - 1; siprule->first = addr; newsiprule = true; } rules = &(siprule->second); } else if (key.Find("out:") == 0) { const PString ip = key.Mid(4).Trim(); inbound = false; if (!(ip == "*" || ip == "any")) addr = NetworkAddress(ip); // check if the IP is already on the list diprule = m_outboundRules.begin(); while (diprule != m_outboundRules.end()) { if (diprule->first.m_address.IsAny() || addr.m_address.IsAny()) { if (diprule->first.m_address.IsAny() && addr.m_address.IsAny()) break; } else if (diprule->first == addr) break; ++diprule; } // append the new IP to the list if (diprule == m_outboundRules.end()) { m_outboundRules.resize(m_outboundRules.size() + 1); diprule = m_outboundRules.end() - 1; diprule->first = addr; newdiprule = true; } // separate and extract callee IP address, if present // no address means wildcard "match any" addr = NetworkAddress(); PINDEX sepIndex = data.Find('='); if (sepIndex != P_MAX_INDEX) { PString lhs = data.Left(sepIndex).Trim(); sepIndex = lhs.FindOneOf(" \t"); if (sepIndex != P_MAX_INDEX) { lhs = lhs.Left(sepIndex).Trim(); data = data.Mid(sepIndex + 1); if (!(lhs == "*" || lhs == "any")) addr = NetworkAddress(lhs); } } // check if the address is already on the list siprule = diprule->second.begin(); while (siprule != diprule->second.end()) { if (siprule->first.m_address.IsAny() || addr.m_address.IsAny()) { if (siprule->first.m_address.IsAny() && addr.m_address.IsAny()) break; } else if (siprule->first == addr) break; ++siprule; } // append the new callee address, if not found if (siprule == diprule->second.end()) { diprule->second.resize(diprule->second.size() + 1); siprule = diprule->second.end() - 1; siprule->first = addr; newsiprule = true; } rules = &(siprule->second); } else { PTRACE(1, "CLIRW\tUknown CLI rewrite rule: " << key << '=' << kv.GetDataAt(i) ); continue; } // process CLIR options int manualCLIR = RewriteRule::CLIRPassthrough; int CLIRPolicy = RewriteRule::IgnoreCLIR; data = data.Trim(); if (data.Find("pi=") == 0) { data = data.Mid(3); PINDEX sepIndex = data.FindOneOf(" \t"); if (sepIndex != P_MAX_INDEX) { PString lhs = data.Left(sepIndex).Trim(); data = data.Mid(sepIndex + 1).Trim(); PStringArray tokens = lhs.Tokenise(",;", FALSE); for (PINDEX k = 0; k < tokens.GetSize(); ++k) if (tokens[k] *= "allow") manualCLIR = RewriteRule::AllowPresentation; else if (tokens[k] *= "restrict") manualCLIR = RewriteRule::RestrictPresentation; else if (tokens[k] *= "forward") CLIRPolicy = RewriteRule::ForwardCLIR; else if (tokens[k] *= "apply") CLIRPolicy = RewriteRule::AlwaysApplyCLIR; else if (tokens[k] *= "applyforterminals") CLIRPolicy = RewriteRule::ApplyCLIRForTerminals; else PTRACE(1, "CLIRW\tInvalid CLI rewrite rule syntax: " << key << '=' << kv.GetDataAt(k) << ", unreconized pi option '" << tokens[k] << "'" ); } } // process CLI/ANI rewrite rule const PINDEX sepIndex = data.Find('='); if (sepIndex == P_MAX_INDEX) { PTRACE(1, "CLIRW\tInvalid CLI rewrite rule syntax: " << key << '=' << kv.GetDataAt(i) ); if (newsiprule) { if (inbound) { m_inboundRules.erase(siprule); } else { diprule->second.erase(siprule); } } if (newdiprule) m_outboundRules.erase(diprule); continue; } // extract match condition PINDEX keyIndex = 0; int matchType = inbound ? RewriteRule::MatchDialedNumber : RewriteRule::MatchDestinationNumber; if (!inbound && data.Find("cno:") == 0) { matchType = RewriteRule::MatchDestinationNumber; keyIndex += 4; } else if (data.Find("dno:") == 0) { matchType = RewriteRule::MatchDialedNumber; keyIndex += 4; } else if (data.Find("cli:") == 0) { matchType = RewriteRule::MatchCallerNumber; keyIndex += 4; } // extract prefix to be matched PINDEX keyChars = sepIndex - keyIndex; int rewriteType = RewriteRule::PrefixToNumber; if (sepIndex > keyIndex) { if (data[sepIndex-1] == '*') { rewriteType = RewriteRule::PrefixToPrefix; --keyChars; } else if (data[sepIndex-1] == '~') { rewriteType = RewriteRule::NumberToNumber; --keyChars; } else if (data[sepIndex-1] == '^') { rewriteType = RewriteRule::PrefixToH323Id; --keyChars; } else if (data[sepIndex-1] == '/') { rewriteType = RewriteRule::NumberToH323Id; --keyChars; } } if (rewriteType == RewriteRule::PrefixToPrefix && matchType != RewriteRule::MatchCallerNumber) { PTRACE(1, "CLIRW\tInvalid CLI rewrite rule syntax - cannot perform " "*= rewrite on non 'cli:' rules: " << key << '=' << kv.GetDataAt(i) ); if (newsiprule) { if (inbound) { m_inboundRules.erase(siprule); } else { diprule->second.erase(siprule); } } if (newdiprule) m_outboundRules.erase(diprule); continue; } std::string prefix((const char*)(data.Mid(keyIndex, keyChars).Trim())); if (prefix == "any") prefix.erase(); // check if the rule already exists RewriteRules::iterator rule = rules->begin(); while (rule != rules->end()) if (rule->m_matchType == matchType && rule->m_rewriteType == rewriteType && rule->m_prefix.compare(prefix) == 0) break; else ++rule; if (rule == rules->end()) { rules->resize(rules->size() + 1); rule = rules->end() - 1; } rule->m_screeningType = RewriteRule::NoScreening; rule->m_matchType = matchType; rule->m_rewriteType = rewriteType; rule->m_prefix = prefix; rule->m_manualCLIR = manualCLIR; rule->m_CLIRPolicy = CLIRPolicy; // get RHS of the rewrite rule, multiple targets will be selected // in random order PStringArray clis = data.Mid(sepIndex + 1).Tokenise(", ", FALSE); if (clis.GetSize() < 1) rule->m_screeningType = RewriteRule::AlwaysHide; else if (clis[0] *= PString("hide")) rule->m_screeningType = RewriteRule::AlwaysHide; else if (clis[0] *= PString("hidefromterminals")) rule->m_screeningType = RewriteRule::HideFromTerminals; if (rule->m_screeningType == RewriteRule::NoScreening) { rule->m_cli.resize(clis.GetSize()); for (PINDEX k = 0; k < clis.GetSize(); k++) rule->m_cli[k] = (string)((const char *)(clis[k])); } else rule->m_cli.clear(); if (inbound) inboundRules++; else outboundRules++; } /* for (d) */ } /* for (i) */ // sort rules by IP network mask length std::stable_sort(m_inboundRules.begin(), m_inboundRules.end(), SingleIpRule_greater()); std::stable_sort(m_outboundRules.begin(), m_outboundRules.end(), DoubleIpRule_greater()); siprule = m_inboundRules.begin(); while (siprule != m_inboundRules.end()) { std::stable_sort(siprule->second.begin(), siprule->second.end(), RewriteRule_greater()); ++siprule; } diprule = m_outboundRules.begin(); while (diprule != m_outboundRules.end()) { std::stable_sort(diprule->second.begin(), diprule->second.end(), SingleIpRule_greater()); siprule = diprule->second.begin(); while (siprule != diprule->second.end()) { std::stable_sort(siprule->second.begin(), siprule->second.end(), RewriteRule_greater()); ++siprule; } ++diprule; } PTRACE(5, "CLIRW\t" << inboundRules << " inbound rules loaded"); if (PTrace::CanTrace(6)) { ostream &strm = PTrace::Begin(6, __FILE__, __LINE__); strm << "Inbound CLI rewrite rules:" << endl; for (unsigned i = 0; i < m_inboundRules.size(); i++) { strm << "\tsrc " << m_inboundRules[i].first.AsString() << ":" << endl; for (unsigned j = 0; j < m_inboundRules[i].second.size(); j++) strm << "\t\t" << m_inboundRules[i].second[j].AsString() << endl; } PTrace::End(strm); } PTRACE(5, "CLIRW\t" << outboundRules << " outbound rules loaded"); if (PTrace::CanTrace(6)) { ostream &strm = PTrace::Begin(6, __FILE__, __LINE__); strm << "Outbound CLI rewrite rules:" << endl; for (unsigned i = 0; i < m_outboundRules.size(); i++) for (unsigned j = 0; j < m_outboundRules[i].second.size(); j++) { strm << "\tsrc " << m_outboundRules[i].first.AsString() << " dst " << m_outboundRules[i].second[j].first.AsString() << ":" << endl; for (unsigned k = 0; k < m_outboundRules[i].second[j].second.size(); k++) strm << "\t\t" << m_outboundRules[i].second[j].second[k].AsString() << endl; } PTrace::End(strm); } m_processSourceAddress = Toolkit::AsBool(cfg->GetString(CLIRewriteSection, ProcessSourceAddress, "1")); m_removeH323Id = Toolkit::AsBool(cfg->GetString(CLIRewriteSection, RemoveH323Id, "1")); const PString clirPolicy = cfg->GetString(CLIRewriteSection, CLIRPolicy, ""); if (clirPolicy *= "applyforterminals") m_CLIRPolicy = RewriteRule::ApplyCLIRForTerminals; else if (clirPolicy *= "apply") m_CLIRPolicy = RewriteRule::AlwaysApplyCLIR; else if (clirPolicy *= "forward") m_CLIRPolicy = RewriteRule::ForwardCLIR; else if (clirPolicy.IsEmpty()) m_CLIRPolicy = RewriteRule::IgnoreCLIR; else { PTRACE(1, "CLIRW\tSyntax error in the config - an unrecognized " "CLIRPolicy value: '" << clirPolicy << "'"); SNMP_TRAP(7, SNMPError, Configuration, "Invalid CLIRW rule"); } // read [RewriteCLI::SQL] #if HAS_DATABASE m_inboundQuery = cfg->GetString(CLIRewriteSQLSection, "InboundQuery", ""); if (!m_inboundQuery.IsEmpty()) { PTRACE(4, CLIRewriteSQLSection << "\tInboundQuery: " << m_inboundQuery); } m_outboundQuery = cfg->GetString(CLIRewriteSQLSection, "OutboundQuery", ""); if (!m_outboundQuery.IsEmpty()) { PTRACE(4, CLIRewriteSQLSection << "\tOutboundQuery: " << m_outboundQuery); } if (!(m_inboundQuery.IsEmpty() && m_outboundQuery.IsEmpty())) { // if we have either Inbound or Outboudnd query, read DB parameters const PString driverName = cfg->GetString(CLIRewriteSQLSection, "Driver", ""); if (driverName.IsEmpty()) { PTRACE(1, CLIRewriteSQLSection << ": no SQL driver selected - disabled"); SNMP_TRAP(4, SNMPError, Database, PString(CLIRewriteSQLSection) + ": no SQL driver selected"); m_inboundQuery = m_outboundQuery = ""; return; } m_sqlConn = GkSQLConnection::Create(driverName, CLIRewriteSQLSection); if (m_sqlConn == NULL) { PTRACE(1, CLIRewriteSQLSection << ": could not find " << driverName << " database driver - disabled"); SNMP_TRAP(4, SNMPError, Database, PString(CLIRewriteSQLSection) + ": could not find " + driverName + " database driver"); m_inboundQuery = m_outboundQuery = ""; return; } if (!m_sqlConn->Initialize(cfg, CLIRewriteSQLSection)) { PTRACE(1, CLIRewriteSQLSection << ": could not connect to the database - disabled"); SNMP_TRAP(4, SNMPError, Database, PString(CLIRewriteSQLSection) + ": could not connect to the database"); m_inboundQuery = m_outboundQuery = ""; delete m_sqlConn; m_sqlConn = NULL; return; } } #endif // HAS_DATABASE } void CLIRewrite::InRewrite( SetupMsg & msg /// Q.931 Setup message to be rewritten ) { PIPSocket::Address addr; msg.GetPeerAddr(addr); // apply [RewriteCLI::SQL] InboundQuery if (!m_inboundQuery.IsEmpty()) { SingleIpRule * rule = CLIRewrite::RunQuery(m_inboundQuery, msg); if (rule) { Rewrite(msg, *rule, true, NULL); delete rule; } } // find a config file rule that matches caller's IP SingleIpRules::const_iterator i = m_inboundRules.begin(); while (i != m_inboundRules.end()) if (i->first.IsAny() || (addr << i->first)) break; else ++i; if (i == m_inboundRules.end()) return; Rewrite(msg, *i, true, NULL); } void CLIRewrite::OutRewrite( SetupMsg & msg, /// Q.931 Setup message to be rewritten SetupAuthData & authData, /// additional data const PIPSocket::Address & destAddr /// destination address ) { PIPSocket::Address addr; msg.GetPeerAddr(addr); // apply [RewriteCLI::SQL] OutboundQuery if (!m_outboundQuery.IsEmpty()) { SingleIpRule * rule = CLIRewrite::RunQuery(m_inboundQuery, msg); if (rule) { Rewrite(msg, *rule, false, &authData); delete rule; } } // find a config file rule that matches caller's IP DoubleIpRules::const_iterator diprule = m_outboundRules.begin(); while (diprule != m_outboundRules.end()) if (diprule->first.IsAny() || (addr << diprule->first)) break; else ++diprule; if (diprule == m_outboundRules.end()) return; // now find a rule that also matches callee's IP SingleIpRules::const_iterator siprule = diprule->second.begin(); while (siprule != diprule->second.end()) if (siprule->first.IsAny() || (destAddr << siprule->first)) break; else ++siprule; if (siprule == diprule->second.end()) return; Rewrite(msg, *siprule, false, &authData); } void CLIRewrite::Rewrite( SetupMsg & msg, /// Q.931 Setup message to be rewritten const SingleIpRule & ipRule, bool inbound, SetupAuthData * authData ) const { unsigned plan = Q931::ISDNPlan, type = Q931::UnknownType; unsigned presentation = (unsigned)-1, screening = (unsigned)-1; PString cli, dno, cno; // get ANI/CLI msg.GetQ931().GetCallingPartyNumber(cli, &plan, &type, &presentation, &screening, (unsigned)-1, (unsigned)-1); if (cli.IsEmpty() && msg.GetUUIEBody().HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) cli = GetBestAliasAddressString(msg.GetUUIEBody().m_sourceAddress, true, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); // get dialed number if (inbound) { msg.GetQ931().GetCalledPartyNumber(dno); if (dno.IsEmpty() && msg.GetUUIEBody().HasOptionalField(H225_Setup_UUIE::e_destinationAddress)) dno = GetBestAliasAddressString(msg.GetUUIEBody().m_destinationAddress, true, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); } else if (authData != NULL) dno = authData->m_dialedNumber; // get destination number if (!inbound) { msg.GetQ931().GetCalledPartyNumber(cno); if (cno.IsEmpty() && msg.GetUUIEBody().HasOptionalField(H225_Setup_UUIE::e_destinationAddress)) cno = GetBestAliasAddressString(msg.GetUUIEBody().m_destinationAddress, true, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); } // find ANI/CLI condition/prefix match PString newcli; RewriteRules::const_iterator rule = ipRule.second.begin(); while (rule != ipRule.second.end()) { if (!rule->m_prefix.empty()) { int matchLen = 0; const char *number = NULL; if (rule->m_matchType == RewriteRule::MatchCallerNumber) number = cli; else if (rule->m_matchType == RewriteRule::MatchDialedNumber) number = dno; else if (!inbound && rule->m_matchType == RewriteRule::MatchDestinationNumber) number = cno; if (number != NULL) { matchLen = MatchPrefix(number, rule->m_prefix.c_str()); if (matchLen > 0 && rule->m_rewriteType == RewriteRule::NumberToNumber && strlen(number) != (unsigned)matchLen) matchLen = 0; } if (matchLen <= 0) { ++rule; continue; } } if (rule->m_screeningType != RewriteRule::NoScreening) break; if (!rule->m_cli.empty()) { // get the new ANI/CLI newcli = rule->m_cli[rand() % rule->m_cli.size()].c_str(); // if this is a number range, choose the new ANI/CLI from the range const PINDEX sepIndex = newcli.Find('-'); if (sepIndex != P_MAX_INDEX) { PString lowStr(newcli.Left(sepIndex).Trim()); PString highStr(newcli.Mid(sepIndex + 1).Trim()); PUInt64 low = lowStr.AsUnsigned64(); PUInt64 high = highStr.AsUnsigned64(); PUInt64 diff = (low < high) ? (high - low) : (low - high); int numLeadingZeros1 = 0; while (numLeadingZeros1 < lowStr.GetLength() && lowStr[numLeadingZeros1] == '0') ++numLeadingZeros1; int numLeadingZeros2 = 0; while (numLeadingZeros2 < highStr.GetLength() && highStr[numLeadingZeros2] == '0') ++numLeadingZeros2; if (diff >= RAND_MAX) diff = PUInt64(rand()); else diff = PUInt64(rand() % ((unsigned)diff + 1)); diff = (low < high) ? (low + diff) : (high + diff); newcli = PString(diff); if (lowStr.GetLength() == highStr.GetLength() && (numLeadingZeros1 > 0 || numLeadingZeros2 > 0)) { while (newcli.GetLength() < highStr.GetLength()) newcli = PString("0") + newcli; } PTRACE(5, "CLIRW\t" << (inbound ? "Inbound" : "Outbound") << " CLI range rewrite target is '" << newcli << "' selected by the rule " << rule->AsString() ); } if (rule->m_rewriteType == RewriteRule::PrefixToPrefix && rule->m_matchType == RewriteRule::MatchCallerNumber) { PString unused; newcli = RewriteString(cli, rule->m_prefix.c_str(), newcli, unused); } PTRACE(5, "CLIRW\t" << (inbound ? "Inbound" : "Outbound") << " CLI rewrite to '" << newcli << "' by the rule " << rule->AsString() ); break; } ++rule; } if (rule == ipRule.second.end()) return; bool isTerminal = false; if (authData && authData->m_call) { endptr callee = authData->m_call->GetCalledParty(); if (callee && callee->GetEndpointType().HasOptionalField(H225_EndpointType::e_terminal)) isTerminal = true; } if (rule->m_manualCLIR == RewriteRule::RestrictPresentation) { presentation = 1; PTRACE(5, "CLIRW\tCLIR forced to 'restricted' by the " << (inbound ? "inbound" : "outbound") << " rule " << rule->AsString() ); } else if (rule->m_manualCLIR == RewriteRule::AllowPresentation) { presentation = 0; PTRACE(5, "CLIRW\tCLIR forced to 'allowed' by the " << (inbound ? "inbound" : "outbound") << " rule " << rule->AsString() ); } int screeningType = rule->m_screeningType; if (rule->m_CLIRPolicy == RewriteRule::AlwaysApplyCLIR || (rule->m_CLIRPolicy == RewriteRule::ApplyCLIRForTerminals && isTerminal)) { if (presentation == 1) screeningType = RewriteRule::AlwaysHide; else if (presentation == (unsigned)-1) if (msg.GetUUIEBody().HasOptionalField(H225_Setup_UUIE::e_presentationIndicator) && msg.GetUUIEBody().m_presentationIndicator.GetTag() == H225_PresentationIndicator::e_presentationRestricted) screeningType = RewriteRule::AlwaysHide; } if (screeningType == RewriteRule::AlwaysHide) { presentation = 1; screening = 3; newcli = ""; plan = Q931::UnknownPlan; type = Q931::UnknownType; if (msg.GetQ931().HasIE((Q931::InformationElementCodes)0x6d)) // calling party subaddress IE msg.GetQ931().RemoveIE((Q931::InformationElementCodes)0x6d); if (msg.GetQ931().HasIE(Q931::DisplayIE)) msg.GetQ931().RemoveIE(Q931::DisplayIE); PTRACE(5, "CLIRW\tCLI hidden by the " << (inbound ? "inbound" : "outbound") << " rule " << rule->AsString() ); } else if (screeningType == RewriteRule::HideFromTerminals) { presentation = 1; msg.GetQ931().GetCallingPartyNumber(newcli); if (isTerminal) { isTerminal = true; screening = 3; newcli = ""; plan = Q931::UnknownPlan; type = Q931::UnknownType; if (msg.GetQ931().HasIE((Q931::InformationElementCodes)0x6d)) // calling party subaddress IE msg.GetQ931().RemoveIE((Q931::InformationElementCodes)0x6d); if (msg.GetQ931().HasIE(Q931::DisplayIE)) msg.GetQ931().RemoveIE(Q931::DisplayIE); PTRACE(5, "CLIRW\tCLI hidden by the " << (inbound ? "inbound" : "outbound") << " rule " << rule->AsString() ); } } else if (newcli.IsEmpty()) return; if (presentation != (unsigned)-1 && screening == (unsigned)-1) screening = 0; if (rule->m_rewriteType != RewriteRule::PrefixToH323Id && rule->m_rewriteType != RewriteRule::NumberToH323Id) { msg.GetQ931().SetCallingPartyNumber(newcli, plan, type, presentation, screening); msg.SetChanged(); } if (m_processSourceAddress && msg.GetUUIEBody().HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) { H225_ArrayOf_AliasAddress & sourceAddress = msg.GetUUIEBody().m_sourceAddress; if (m_removeH323Id) sourceAddress.SetSize(1); else { PINDEX aliasIndex = 0; while (aliasIndex < sourceAddress.GetSize()) if (sourceAddress[aliasIndex].GetTag() == H225_AliasAddress::e_h323_ID) { if (rule->m_rewriteType == RewriteRule::PrefixToH323Id || rule->m_rewriteType == RewriteRule::NumberToH323Id) { sourceAddress.RemoveAt(aliasIndex); } else { aliasIndex++; continue; } } else if (sourceAddress[aliasIndex].GetTag() == H225_AliasAddress::e_url_ID || sourceAddress[aliasIndex].GetTag() == H225_AliasAddress::e_email_ID) { aliasIndex++; continue; } else sourceAddress.RemoveAt(aliasIndex); sourceAddress.SetSize(sourceAddress.GetSize() + 1); } if (presentation != (unsigned)-1) { msg.GetUUIEBody().IncludeOptionalField(H225_Setup_UUIE::e_presentationIndicator); msg.GetUUIEBody().m_presentationIndicator.SetTag(presentation); msg.GetUUIEBody().IncludeOptionalField(H225_Setup_UUIE::e_screeningIndicator); msg.GetUUIEBody().m_screeningIndicator.SetValue(screening); } if (screeningType == RewriteRule::AlwaysHide) { msg.GetUUIEBody().IncludeOptionalField(H225_Setup_UUIE::e_presentationIndicator); msg.GetUUIEBody().m_presentationIndicator.SetTag(H225_PresentationIndicator::e_presentationRestricted); msg.GetUUIEBody().IncludeOptionalField(H225_Setup_UUIE::e_screeningIndicator); msg.GetUUIEBody().m_screeningIndicator.SetValue(H225_ScreeningIndicator::e_networkProvided); msg.GetUUIEBody().RemoveOptionalField(H225_Setup_UUIE::e_sourceAddress); } else if (screeningType == RewriteRule::HideFromTerminals) { msg.GetUUIEBody().IncludeOptionalField(H225_Setup_UUIE::e_presentationIndicator); msg.GetUUIEBody().m_presentationIndicator.SetTag(H225_PresentationIndicator::e_presentationRestricted); if (isTerminal && !newcli) { msg.GetUUIEBody().IncludeOptionalField(H225_Setup_UUIE::e_screeningIndicator); msg.GetUUIEBody().m_screeningIndicator.SetValue(H225_ScreeningIndicator::e_networkProvided); if (rule->m_rewriteType == RewriteRule::PrefixToH323Id || rule->m_rewriteType == RewriteRule::NumberToH323Id) H323SetAliasAddress(newcli, sourceAddress[sourceAddress.GetSize() - 1], H225_AliasAddress::e_h323_ID); else H323SetAliasAddress(newcli, sourceAddress[sourceAddress.GetSize() - 1]); } else msg.GetUUIEBody().RemoveOptionalField(H225_Setup_UUIE::e_sourceAddress); } else { if (rule->m_rewriteType == RewriteRule::PrefixToH323Id || rule->m_rewriteType == RewriteRule::NumberToH323Id) H323SetAliasAddress(newcli, sourceAddress[sourceAddress.GetSize() - 1], H225_AliasAddress::e_h323_ID); else H323SetAliasAddress(newcli, sourceAddress[sourceAddress.GetSize() - 1]); } msg.SetUUIEChanged(); } if (!msg.GetUUIEBody().HasOptionalField(H225_Setup_UUIE::e_sourceAddress) && (rule->m_rewriteType == RewriteRule::PrefixToH323Id || rule->m_rewriteType == RewriteRule::NumberToH323Id)) { msg.GetUUIEBody().IncludeOptionalField(H225_Setup_UUIE::e_sourceAddress); msg.GetUUIEBody().m_sourceAddress.SetSize(1); H323SetAliasAddress(newcli, msg.GetUUIEBody().m_sourceAddress[0], H225_AliasAddress::e_h323_ID); msg.SetUUIEChanged(); } } CLIRewrite::SingleIpRule * CLIRewrite::RunQuery(const PString & query, const SetupMsg & msg) { #if HAS_DATABASE GkSQLResult::ResultRow resultRow; std::map params; PIPSocket::Address addr; unsigned plan = Q931::ISDNPlan, type = Q931::UnknownType; unsigned presentation = (unsigned)-1, screening = (unsigned)-1; PString cli, called; msg.GetPeerAddr(addr); params["callerip"] = addr.AsString(); msg.GetQ931().GetCalledPartyNumber(called); params["called"] = called; msg.GetQ931().GetCallingPartyNumber(cli, &plan, &type, &presentation, &screening, (unsigned)-1, (unsigned)-1); if (cli.IsEmpty() && msg.GetUUIEBody().HasOptionalField(H225_Setup_UUIE::e_sourceAddress) && msg.GetUUIEBody().m_sourceAddress.GetSize() > 0) { cli = AsString(msg.GetUUIEBody().m_sourceAddress[0], false); } params["cli"] = cli; GkSQLResult * result = m_sqlConn->ExecuteQuery(query, params, -1); if (result == NULL) { PTRACE(2, CLIRewriteSQLSection << ": query failed - timeout or fatal error"); SNMP_TRAP(4, SNMPError, Database, PString(CLIRewriteSQLSection) + " query failed"); return NULL; } if (!result->IsValid()) { PTRACE(2, CLIRewriteSQLSection << ": query failed (" << result->GetErrorCode() << ") - " << result->GetErrorMessage()); SNMP_TRAP(4, SNMPError, Database, PString(CLIRewriteSQLSection) + " query failed"); delete result; return NULL; } if (result->GetNumRows() != 1) PTRACE(3, CLIRewriteSQLSection << ": query returned no rows"); else if (result->GetNumFields() < 1) PTRACE(2, CLIRewriteSQLSection << ": bad query - no columns found in the result set"); else if (!result->FetchRow(resultRow) || resultRow.empty()) { PTRACE(2, CLIRewriteSQLSection << ": query failed - could not fetch the result row"); SNMP_TRAP(4, SNMPError, Database, PString(CLIRewriteSQLSection) + " query failed"); } else { PString newCLI = resultRow[0].first; PTRACE(5, CLIRewriteSQLSection << "\tQuery result : " << newCLI); RewriteRules rules; RewriteRule rule; //if ((result->GetNumFields() == 1) // rule.m_manualCLIR = CLIRPassthrough; // rule.m_CLIRPolicy = IgnoreCLIR; rule.m_cli.push_back(newCLI); rules.push_back(rule); delete result; return new SingleIpRule(addr, rules); } delete result; #endif // HAS_DATABASE return NULL; } gnugk-3.4/PaxHeaders.16356/h323util.cxx0000644000175000001440000000005012214065167015626 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/h323util.cxx0000644000175000001440000005744312214065167014604 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // H.323 utility functions // // Copyright (c) 2000-2013, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #include #include #include "gk_const.h" #include "config.h" #include "h323util.h" #ifdef HAS_H460 #include #endif #if defined(_WIN32) && defined(hasIPV6) // Windows doesn't have inet_ntop, so we fake it const char * inet_ntop(int family, const void *src, char *dst, socklen_t cnt) { if (family == AF_INET) { struct sockaddr_in in; memset(&in, 0, sizeof(in)); in.sin_family = AF_INET; memcpy(&in.sin_addr, src, sizeof(struct in_addr)); getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } else if (family == AF_INET6) { struct sockaddr_in6 in; memset(&in, 0, sizeof(in)); in.sin6_family = AF_INET6; memcpy(&in.sin6_addr, src, sizeof(struct in_addr6)); getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } return NULL; } #endif PString AsString(const PIPSocket::Address & ip) { if (ip.GetVersion() == 4) return PString(PString::Printf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); #ifdef hasIPV6 if (ip.GetVersion() == 6) { char buf[INET6_ADDRSTRLEN]; sockaddr_in6 ipv6addr; memset(&ipv6addr, 0, sizeof(ipv6addr)); ipv6addr.sin6_addr = ip; return inet_ntop(AF_INET6, (const void *)&ipv6addr.sin6_addr, buf, INET6_ADDRSTRLEN); } #endif return ip.AsString(); } PString AsString(const PIPSocket::Address & ip, WORD pt) { if (ip.GetVersion() == 4) return PString(PString::Printf, "%d.%d.%d.%d:%u", ip[0], ip[1], ip[2], ip[3], pt); if (ip.GetVersion() == 6) { return "[" + AsString(ip) + "]:" + PString(pt); } return ip.AsString() + ":" + PString(pt); } PString AsString(const H245_UnicastAddress & ip) { if (ip.GetTag() == H245_UnicastAddress::e_iPAddress) { const H245_UnicastAddress_iPAddress & ipv4 = ip; return AsString(ipv4); } else if (ip.GetTag() == H245_UnicastAddress::e_iP6Address) { const H245_UnicastAddress_iP6Address & ipv6 = ip; return AsString(ipv6); } else { return "unsupported H.245 address (" + ip.GetTagName() + ")"; } } PString AsString(const H245_UnicastAddress_iPAddress & ipv4) { return AsString( PIPSocket::Address(ipv4.m_network.GetSize(), ipv4.m_network.GetValue()), ipv4.m_tsapIdentifier); } PString AsString(const H245_UnicastAddress_iP6Address & ipv6) { return AsString( PIPSocket::Address(ipv6.m_network.GetSize(), ipv6.m_network.GetValue()), ipv6.m_tsapIdentifier); } WORD GetH245Port(const H245_UnicastAddress & addr) { if (addr.GetTag() == H245_UnicastAddress::e_iPAddress) { const H245_UnicastAddress_iPAddress & ipv4 = addr; return ipv4.m_tsapIdentifier; } else if (addr.GetTag() == H245_UnicastAddress::e_iP6Address) { const H245_UnicastAddress_iP6Address & ipv6 = addr; return ipv6.m_tsapIdentifier; } else { return 0; } } void SetH245Port(H245_UnicastAddress & addr, WORD port) { if (addr.GetTag() == H245_UnicastAddress::e_iPAddress) { H245_UnicastAddress_iPAddress & ipv4 = addr; ipv4.m_tsapIdentifier = port; } else if (addr.GetTag() == H245_UnicastAddress::e_iP6Address) { H245_UnicastAddress_iP6Address & ipv6 = addr; ipv6.m_tsapIdentifier = port; } } PString AsString(const H225_TransportAddress & ta) { PStringStream stream; stream << ta; return stream; } PString AsDotString(const H225_TransportAddress & addr, bool showPort) { PString result = "invalid address"; PIPSocket::Address ip; WORD port = 0; if (GetIPAndPortFromTransportAddr(addr, ip, port)) { if (showPort) result = AsString(ip, port); else result = AsString(ip); } return result; } PString AsString(const H323TransportAddress & ta) { if (!IsSet(ta)) return ""; PIPSocket::Address ip; WORD port = 0; if (ta.GetIpAndPort(ip, port)) return AsString(ip, port); else return ""; } PString AsString(const H225_EndpointType & terminalType) { PString terminalTypeString; if (terminalType.HasOptionalField(H225_EndpointType::e_terminal)) terminalTypeString = ",terminal"; if (terminalType.HasOptionalField(H225_EndpointType::e_gateway)) terminalTypeString += ",gateway"; if (terminalType.HasOptionalField(H225_EndpointType::e_mcu)) terminalTypeString += ",mcu"; if (terminalType.HasOptionalField(H225_EndpointType::e_gatekeeper)) terminalTypeString += ",gatekeeper"; /* vendor seems always to be set - this clutters up the display if (terminalType.HasOptionalField(H225_EndpointType::e_vendor)) terminalTypeString += ",vendor"; */ if (terminalTypeString.IsEmpty()) terminalTypeString = ",unknown"; return terminalTypeString.Mid(1); } PString AsString(const H225_AliasAddress & terminalAlias, bool includeAliasType) { if(!terminalAlias.IsValid()) return includeAliasType ? "invalid:UnknownType" : "invalid"; switch (terminalAlias.GetTag()) { case H225_AliasAddress::e_dialedDigits: case H225_AliasAddress::e_url_ID: case H225_AliasAddress::e_email_ID: case H225_AliasAddress::e_h323_ID: case H225_AliasAddress::e_transportID: case H225_AliasAddress::e_partyNumber: PString aliasString = H323GetAliasAddressString(terminalAlias); // OpenH323 prepends a special prefix to partyNumbers // to distinguish a number subtype - we don't need this if (terminalAlias.GetTag() == H225_AliasAddress::e_partyNumber) { const PINDEX prefixIndex = aliasString.Find(':'); if (prefixIndex != P_MAX_INDEX) aliasString = aliasString.Mid(prefixIndex + 1); } // simplify URL aliases to match "h323:foo@bar" with "foo@bar" if ((terminalAlias.GetTag() == H225_AliasAddress::e_url_ID) && (aliasString.Left(5) == "h323:")) { aliasString = aliasString.Mid(5); } if (includeAliasType) { aliasString += ":" + terminalAlias.GetTagName(); } return aliasString; } return includeAliasType ? "none:UnknownType" : "none"; } PString AsString(const H225_ArrayOf_AliasAddress & terminalAlias, bool includeAliasTypeName) { PString aliasListString; for(PINDEX cnt = 0; cnt < terminalAlias.GetSize(); cnt++ ) { aliasListString += AsString(terminalAlias[cnt], includeAliasTypeName); if (cnt < (terminalAlias.GetSize() - 1)) { aliasListString += "="; } } return (aliasListString); } PString AsString(const PASN_OctetString & Octets) { PString result; if (Octets.GetDataLength() > 0) { result = PString(PString::Printf, "%02x", Octets[0]); for (PINDEX i = 1; i < Octets.GetDataLength(); ++i) result += PString(PString::Printf, " %02x", Octets[i]); } return result; } PString AsString(const PBYTEArray & array) { PString result; for (PINDEX i = 0; i < array.GetSize(); i++) { if (isprint(array[i])) result += array[i]; } return result; } PString StripAliasType(const PString & alias) { const PINDEX nameIndex = alias.FindLast(':'); if (nameIndex != P_MAX_INDEX) { return alias.Left(nameIndex); } else { // nothing to strip return alias; } } // check if an alias is a valid US 10D or 11D number bool Is10Dor11Dnumber(const H225_AliasAddress & alias) { PString num = AsString(alias, false); if (num.GetLength() < 10 || num.GetLength() > 11) return false; // must start with 1 if its 11D if (num.GetLength() == 11) { if (num.Left(1) != "1") return false; else num = num.Right(10); } // check parts PString area = num.Left(3); PString local = num.Right(7); // area code must start with 2..9 if (area.Left(1) == "0" || area.Left(1) == "1") return false; if (local == "0000000") return false; // OK, all checks passed return true; } H245_TransportAddress IPToH245TransportAddr(const PIPSocket::Address & ip, WORD Port) { H245_TransportAddress Result; Result.SetTag(H245_TransportAddress::e_unicastAddress); H245_UnicastAddress & uniaddr = Result; if (ip.GetVersion() == 6) { uniaddr.SetTag(H245_UnicastAddress::e_iP6Address); H245_UnicastAddress_iP6Address & ipaddr = uniaddr; for (int i = 0; i < 16; ++i) ipaddr.m_network[i] = ip[i]; ipaddr.m_tsapIdentifier = Port; } else { uniaddr.SetTag(H245_UnicastAddress::e_iPAddress); H245_UnicastAddress_iPAddress & ipaddr = uniaddr; for (int i = 0; i < 4; ++i) ipaddr.m_network[i] = ip[i]; ipaddr.m_tsapIdentifier = Port; } return Result; } // convert a string (dot notation without port) into an H245 transport address //H245_TransportAddress StringToH245TransportAddr(const PString & Addr, WORD Port) //{ // H245_TransportAddress Result; // // Result.SetTag(H245_TransportAddress::e_unicastAddress); // H245_UnicastAddress & uniaddr = Result; // uniaddr.SetTag(H245_UnicastAddress::e_iPAddress); // H245_UnicastAddress_iPAddress & ipaddr = uniaddr; // PIPSocket::Address ip(Addr); // for (int i = 0; i < 4; ++i) // ipaddr.m_network[i] = ip[i]; // ipaddr.m_tsapIdentifier = Port; // // return Result; //} // convert a socket IP address into an H225 transport address H225_TransportAddress SocketToH225TransportAddr(const PIPSocket::Address & Addr, WORD Port) { H225_TransportAddress Result; if (Addr.GetVersion() == 6) { Result.SetTag( H225_TransportAddress::e_ip6Address ); H225_TransportAddress_ip6Address & ResultIP = Result; for (int i = 0; i < 16; ++i) ResultIP.m_ip[i] = Addr[i]; ResultIP.m_port = Port; } else { Result.SetTag( H225_TransportAddress::e_ipAddress ); H225_TransportAddress_ipAddress & ResultIP = Result; for (int i = 0; i < 4; ++i) ResultIP.m_ip[i] = Addr[i]; ResultIP.m_port = Port; } return Result; } // convert a H.245 unicast address into an H.323 transport address H323TransportAddress H245UnicastToH323TransportAddr(const H245_UnicastAddress & h245unicast) { if (h245unicast.GetTag() == H245_UnicastAddress::e_iPAddress) { const H245_UnicastAddress_iPAddress & ipv4 = h245unicast; return H323TransportAddress(PIPSocket::Address(ipv4.m_network.GetSize(), ipv4.m_network.GetValue()), ipv4.m_tsapIdentifier); } else if (h245unicast.GetTag() == H245_UnicastAddress::e_iP6Address) { const H245_UnicastAddress_iP6Address & ipv6 = h245unicast; return H323TransportAddress(PIPSocket::Address(ipv6.m_network.GetSize(), ipv6.m_network.GetValue()), ipv6.m_tsapIdentifier); } PTRACE(1, "Unsupported H245_UnicastAddress: " << h245unicast.GetTagName()); return H323TransportAddress(); } bool GetTransportAddress(const PString & addr, WORD def_port, PIPSocket::Address & ip, WORD & port) { PStringArray adr_parts = SplitIPAndPort(addr.Trim(), def_port); port = WORD(adr_parts[1].AsUnsigned()); return PIPSocket::GetHostAddress(adr_parts[0], ip) != 0; } bool GetTransportAddress(const PString & addr, WORD def_port, H225_TransportAddress & Result) { PIPSocket::Address ip; WORD port; bool res = GetTransportAddress(addr, def_port, ip, port); if (res) Result = SocketToH225TransportAddr(ip, port); return res; } bool GetIPFromTransportAddr(const H225_TransportAddress & addr, PIPSocket::Address & ip) { WORD port; return GetIPAndPortFromTransportAddr(addr, ip, port); } bool GetIPAndPortFromTransportAddr(const H225_TransportAddress & addr, PIPSocket::Address & ip, WORD & port) { if (!addr.IsValid()) return false; if (addr.GetTag() == H225_TransportAddress::e_ipAddress) { const H225_TransportAddress_ipAddress & ipaddr = addr; ip = PIPSocket::Address(ipaddr.m_ip.GetSize(), (const BYTE*)ipaddr.m_ip); port = (WORD)ipaddr.m_port; return true; } if (addr.GetTag() == H225_TransportAddress::e_ip6Address) { const H225_TransportAddress_ip6Address & ipaddr = addr; ip = PIPSocket::Address(ipaddr.m_ip.GetSize(), (const BYTE*)ipaddr.m_ip); port = (WORD)ipaddr.m_port; return true; } return false; } PStringArray SplitIPAndPort(const PString & str, WORD default_port) { PStringArray result; if (!IsIPv6Address(str)) { result = str.Tokenise(":", FALSE); if (result.GetSize() == 1) { result.SetSize(2); result[1] = PString(PString::Unsigned, default_port); } return result; } else { if (str.Left(1) == "[") { result.SetSize(2); PINDEX n = str.FindLast(']'); result[0] = str.Left(n); result[0].Replace("[", "", true); result[0].Replace("]", "", true); result[1] = str.Mid(n+2); if (result[1].GetLength() == 0) result[1] = PString(PString::Unsigned, default_port); } else { result.SetSize(2); result[0] = str; result[1] = PString(PString::Unsigned, default_port); } return result; } } void SetSockaddr(sockaddr_in & sin, const PIPSocket::Address & ip, WORD port) { memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr = ip; sin.sin_port = htons(port); } void SetSockaddr(sockaddr_in & sin, const H323TransportAddress & addr) { PIPSocket::Address ip; WORD port = 0; addr.GetIpAndPort(ip, port); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr = ip; sin.sin_port = htons(port); } void SetSockaddr(sockaddr_in & sin, const H245_UnicastAddress & addr) { PStringArray parts = SplitIPAndPort(AsString(addr), 0); PIPSocket::Address ip(parts[0]); WORD port = (WORD)parts[1].AsUnsigned(); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr = ip; sin.sin_port = htons(port); } #ifdef hasIPV6 void SetSockaddr(sockaddr_in6 & sin6, const PIPSocket::Address & ip, WORD port) { memset(&sin6, 0, sizeof(sin6)); if (ip.GetVersion() == 6) { sin6.sin6_family = AF_INET6; sin6.sin6_addr = ip; sin6.sin6_port = htons(port); } else { ((struct sockaddr_in*)&sin6)->sin_family = AF_INET; ((struct sockaddr_in*)&sin6)->sin_addr = ip; ((struct sockaddr_in*)&sin6)->sin_port = htons(port); } } void SetSockaddr(sockaddr_in6 & sin6, const H323TransportAddress & addr) { PIPSocket::Address ip; WORD port = 0; addr.GetIpAndPort(ip, port); memset(&sin6, 0, sizeof(sin6)); if (ip.GetVersion() == 6) { sin6.sin6_family = AF_INET6; sin6.sin6_addr = ip; sin6.sin6_port = htons(port); } else { ((struct sockaddr_in*)&sin6)->sin_family = AF_INET; ((struct sockaddr_in*)&sin6)->sin_addr = ip; ((struct sockaddr_in*)&sin6)->sin_port = htons(port); } } void SetSockaddr(sockaddr_in6 & sin6, const H245_UnicastAddress & addr) { PStringArray parts = SplitIPAndPort(AsString(addr), 0); PIPSocket::Address ip(parts[0]); WORD port = (WORD)parts[1].AsUnsigned(); memset(&sin6, 0, sizeof(sin6)); if (ip.GetVersion() == 6) { sin6.sin6_family = AF_INET6; sin6.sin6_addr = ip; sin6.sin6_port = htons(port); } else { ((struct sockaddr_in*)&sin6)->sin_family = AF_INET; ((struct sockaddr_in*)&sin6)->sin_addr = ip; ((struct sockaddr_in*)&sin6)->sin_port = htons(port); } } #endif bool IsIPAddress(const PString & addr) { return (IsIPv4Address(addr) || IsIPv6Address(addr)); } bool IsIPv4Address(const PString & addr) { static PRegularExpression ipPattern("^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$", PRegularExpression::Extended); static PRegularExpression ipAndPortPattern("^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+:[0-9]+$", PRegularExpression::Extended); return ((addr.FindRegEx(ipPattern) != P_MAX_INDEX) || (addr.FindRegEx(ipAndPortPattern) != P_MAX_INDEX)); } bool IsIPv6Address(PString addr) { static PRegularExpression ipV6PortPattern("\\]:[0-9]+$", PRegularExpression::Extended); if (addr.Left(1) == "[") { if (addr.Right(1) == "]") { addr = addr.Mid(1, addr.GetLength() - 2); } else if (addr.FindRegEx(ipV6PortPattern) != P_MAX_INDEX) { addr = addr.Mid(1, addr.FindLast(']') - 1); } } struct addrinfo * result = NULL; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET6; hints.ai_protocol = IPPROTO_TCP; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST; bool isValid = (getaddrinfo((const char *)addr, NULL, &hints, &result) == 0); if (result) freeaddrinfo(result); return isValid; } unsigned GetVersion(const H225_TransportAddress & ta) { if (ta.GetTag() == H225_TransportAddress::e_ipAddress) { return 4; } if (ta.GetTag() == H225_TransportAddress::e_ip6Address) { return 6; } return 0; } // convert an IPv4-mapped-IPv6 address into an IPv4 address, otherwise leave unchanged void UnmapIPv4Address(PIPSocket::Address & addr) { #ifdef hasIPV6 if ((addr.GetVersion() == 6) && addr.IsV4Mapped()) { PIPSocket::Address newAddr(addr[12], addr[13], addr[14], addr[15]); addr = newAddr; } #endif } // convert an IPv4 address into an IPv4-mapped-IPv6 address, // leave unchanged if IPv6 disabled or is already an IPv6 address void MapIPv4Address(PIPSocket::Address & addr) { #ifdef hasIPV6 if (addr.GetVersion() == 4) { BYTE mappedIP[16]; memset(mappedIP, 0, sizeof(mappedIP)); mappedIP[10] = 0xff; mappedIP[11] = 0xff; mappedIP[12] = addr[0]; mappedIP[13] = addr[1]; mappedIP[14] = addr[2]; mappedIP[15] = addr[3]; PIPSocket::Address newAddr(16, mappedIP); addr = newAddr; } #endif } bool IsLoopback(const PIPSocket::Address & addr) { return addr.IsLoopback() != 0; } bool IsSet(const H323TransportAddress & addr) { return (addr != H323TransportAddress()); } bool IsValidE164(const PString & s) { return (!s.IsEmpty() && strspn(s, "1234567890*#+,") == strlen(s)); } PString GetGUIDString( const H225_GloballyUniqueID & id, /// 128-bit identifier to convert bool fixedLength /// skip leading zeros (false) or not (true) ) { if (id.GetSize() < 16) return "Invalid"; PString idstr; for (int j = 0, i = 0; j < 4; j++) { const unsigned hex = ((unsigned)(id[i])<<24) | ((unsigned)(id[i+1])<<16) | ((unsigned)(id[i+2])<<8) | ((unsigned)(id[i+3])); i += 4; idstr += fixedLength ? PString(PString::Printf, "%08x", hex) : PString(PString::Unsigned, (long)hex, 16); if (j < 3) idstr += ' '; } return idstr; } /** convert a string into a call-id */ H225_CallIdentifier StringToCallId(PString CallId) { H225_CallIdentifier result; CallId.Replace("-", "", true); CallId.Replace(" ", "", true); OpalGloballyUniqueID tmp_guid(CallId); result.m_guid = tmp_guid; return result; } PINDEX GetBestAliasAddressIndex( const H225_ArrayOf_AliasAddress & aliases, /// aliases to be searched bool exactMatch, /// search only specified tags or find any alias unsigned primaryTags, /// ORed tag flags (BestAliasTagMask) unsigned secondaryTags /// ORed tag flags (BestAliasTagMask) ) { if (primaryTags) for (PINDEX i = 0; i < aliases.GetSize(); i++) if (primaryTags & (1U << aliases[i].GetTag())) return i; if (secondaryTags) for (PINDEX i = 0; i < aliases.GetSize(); i++) if (secondaryTags & (1U << aliases[i].GetTag())) return i; if (!exactMatch && aliases.GetSize() > 0) return 0; return P_MAX_INDEX; } PString GetBestAliasAddressString( const H225_ArrayOf_AliasAddress & aliases, /// aliases to be searched bool exactMatch, /// search only specified tags or find any alias unsigned primaryTags, /// ORed tag flags (BestAliasTagMask) unsigned secondaryTags /// ORed tag flags (BestAliasTagMask) ) { const PINDEX i = GetBestAliasAddressIndex(aliases, exactMatch, primaryTags, secondaryTags); if (i != P_MAX_INDEX) return AsString(aliases[i], FALSE); else return PString::Empty(); } PINDEX FindAlias( const H225_ArrayOf_AliasAddress & aliases, /// the list of aliases to check const PString & alias /// alias to find on the list ) { const PINDEX sz = aliases.GetSize(); for (PINDEX i = 0; i < sz; i++) if (alias == AsString(aliases[i], FALSE)) return i; return P_MAX_INDEX; } int MatchPrefix( const char* alias, const char* prefix ) { if (alias == NULL || prefix == NULL) return 0; const bool negative = (prefix[0] == '!'); int i = 0; int j = (negative ? 1 : 0); while (prefix[j] != 0) { const char c = prefix[j]; if (alias[i] == 0 || (c != '.' && c != '%' && c != alias[i])) return 0; i++; j++; } return negative ? -j + 1 : j; } PString RewriteString( const PString & s, /// original string to rewrite const char* prefix, /// prefix string that matched const char* value, /// new string that replaces the prefix string PString & postdialdigits, bool postdialmatch ) { if (prefix == NULL || value == NULL) return s; PString result = value + s.Mid(strlen(prefix)); if (postdialmatch) postdialdigits.MakeEmpty(); // clear postdial digits before assigning new digits const char *lastSrcDot = prefix; const char *lastDstDot = strchr(value, '.'); while (lastDstDot != NULL) { lastSrcDot = strchr(lastSrcDot, '.'); if (lastSrcDot == NULL) { PTRACE(1, "GK\tInvalid rewrite rule (dots do not match) - " << prefix << " = " << value); break; } int dotDstOffset = (long)lastDstDot - (long)value; int dotSrcOffset = (long)lastSrcDot - (long)prefix; while (*lastDstDot++ == '.' && *lastSrcDot++ == '.') { if (postdialmatch) { postdialdigits += s[dotSrcOffset++]; } else { result[dotDstOffset++] = s[dotSrcOffset++]; } } lastDstDot = strchr(lastDstDot, '.'); } if (postdialmatch) result.Replace(".", "", true); return result; } PString RewriteWildcard(const PString & s, const PString & expression) { PString o = expression; if (o.IsEmpty()) return s; int b1 = o.Find('{'); if (b1 == P_MAX_INDEX) return o; /// No rewrite int b2 = o.Find('}'); if (b2 == P_MAX_INDEX || b1 > b2) return s; /// Logic error ignore rewrite PString l = o.Left(b1); PString r = o.Mid(b2+1); PString exp = o.Mid(b1+1,b2-b1-1); PString rewrite; if (exp *= "\\1") rewrite = s; else { int start = -1; if (exp.Find('^') != P_MAX_INDEX) start = 1; else if (exp.Find('$') != P_MAX_INDEX) start = 0; if (start < 0) return s; /// Logic error ignore rewrite int c1 = exp.Find('('); if (c1 == P_MAX_INDEX) return s; /// Logic error ignore rewrite int c2 = exp.Find(')'); if (c2 == P_MAX_INDEX || c1 > c2) return s; /// Logic error ignore rewrite int i = exp.Mid(c1+1,c2-c1-1).AsInteger(); if (i == 0) return s; /// Logic error ignore rewrite if (start) rewrite = s.Left(i); else rewrite = s.Mid(s.GetLength()-i); } return l + rewrite + r; } PString ReplaceParameters(const PString & queryStr, const std::map & queryParams) { PString finalQuery = queryStr; PINDEX pos = 0; std::map::const_iterator it; PString var; while (pos != P_MAX_INDEX) { pos = finalQuery.Find('%', pos); if (pos++ == P_MAX_INDEX) break; const char c = finalQuery[pos]; // char next after '%' if (c == '{') { const PINDEX closingBrace = finalQuery.Find('}', ++pos); if (closingBrace != P_MAX_INDEX) { PString var = finalQuery.Mid(pos,closingBrace-pos); it = queryParams.find(var); if (it!=queryParams.end()) { finalQuery = finalQuery.Left(pos-2) + it->second + finalQuery.Mid(closingBrace+1); pos = pos-2 + it->second.GetLength(); } else pos = closingBrace; } } } return finalQuery; } bool FindH460Descriptor(unsigned feat, H225_ArrayOf_FeatureDescriptor & features, unsigned & location) { for (PINDEX i = 0; i < features.GetSize(); i++) { H225_GenericIdentifier & id = features[i].m_id; if (id.GetTag() == H225_GenericIdentifier::e_standard) { PASN_Integer & asnInt = id; if (asnInt.GetValue() == feat) { location = i; return true; } } } return false; } void RemoveH460Descriptor(unsigned feat, H225_ArrayOf_FeatureDescriptor & features) { for(PINDEX i = 0; i < features.GetSize(); i++) { H225_GenericIdentifier & id = features[i].m_id; if (id.GetTag() == H225_GenericIdentifier::e_standard) { PASN_Integer & asnInt = id; if (asnInt.GetValue() == feat) { for(PINDEX j=i+1; j < features.GetSize(); j++) features[j-1] = features[j]; features.SetSize(features.GetSize() - 1); return; } } } } #ifdef HAS_H460 void AddH460Feature(H225_ArrayOf_FeatureDescriptor & desc, const H460_Feature & newFeat) { PINDEX lastpos = desc.GetSize(); desc.SetSize(lastpos+1); desc[lastpos] = newFeat; } #endif gnugk-3.4/PaxHeaders.16356/Toolkit.t.cxx0000644000175000001440000000005012176327201016135 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/Toolkit.t.cxx0000644000175000001440000000162512176327201015102 0ustar00janusers00000000000000/* * Toolkit.t.cxx * * unit tests for Toolkit.cxx * * Copyright (c) 2013, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include "config.h" #include "Toolkit.h" #include "gtest/gtest.h" namespace { class ToolkitTest : public ::testing::Test { protected: ToolkitTest() { } NetworkAddress na; NetworkAddress na2; }; TEST_F(ToolkitTest, NetworkAddress) { na = NetworkAddress(); EXPECT_STREQ("0.0.0.0/0", na.AsString()); EXPECT_TRUE(na.IsAny()); na = NetworkAddress("1.2.3.4/24"); EXPECT_STREQ("1.2.3.0/24", na.AsString()); EXPECT_EQ(24u, na.GetNetmaskLen()); EXPECT_FALSE(na.IsAny()); na = NetworkAddress("9.9.9.0/24"); na2 = NetworkAddress("9.9.10.0/24"); EXPECT_FALSE(na == na2); } } // namespace gnugk-3.4/PaxHeaders.16356/GkClient.h0000644000175000001440000000005012177320272015375 xustar000000000000000020 atime=1380868658 20 ctime=1380868609 gnugk-3.4/GkClient.h0000644000175000001440000002410612177320272014341 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // GkClient.h // // Copyright (c) Citron Network Inc. 2001-2003 // Copyright (c) 2002-2011, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef GKCLIENT_H #define GKCLIENT_H "@(#) $Id: GkClient.h,v 1.55 2013/08/04 00:30:50 willamowius Exp $" #include "gk_const.h" #include "Toolkit.h" #include "Routing.h" #include "config.h" #include class Q931; class H225_AliasAddress; class H225_ArrayOf_AliasAddress; class H225_TransportAddress; class H225_ArrayOf_TransportAddress; class H225_EndpointIdentifier; class H225_GatekeeperIdentifier; class H225_RegistrationRequest; class H225_AdmissionRequest; class H225_LocationRequest; class H225_Setup_UUIE; class H225_ArrayOf_ClearToken; class H225_ArrayOf_CryptoH323Token; class RasMsg; class RasServer; class AlternateGKs; class RasRequester; class NATClient; class GkClientHandler; class CallRec; template class SmartPtr; typedef SmartPtr callptr; class SignalingMsg; template class H225SignalingMsg; typedef H225SignalingMsg SetupMsg; #ifdef HAS_H46023 class H460_FeatureStd; class STUNClient; class UDPProxySocket; class H46024B_AlternateAddress; class H46024B_ArrayOf_AlternateAddress; class CallH46024Sockets { public: CallH46024Sockets(unsigned strategy); CallH46024Sockets(WORD sessionID, UDPProxySocket * rtp, UDPProxySocket * rtcp); ~CallH46024Sockets(); unsigned GetNatStrategy() const { return m_natStrategy; } unsigned GetSessionID() const { return m_sessionID; } void SetAlternate(PString cui, unsigned muxID, H323TransportAddress m_rtp, H323TransportAddress m_rtcp); void SetAlternate(const H46024B_AlternateAddress & alternate); void LoadAlternate(PString & cui, unsigned & muxID, H323TransportAddress & m_rtp, H323TransportAddress & m_rtcp); protected: unsigned m_natStrategy; WORD m_sessionID; UDPProxySocket * m_rtpSocket; UDPProxySocket * m_rtcpSocket; }; typedef std::map > GkNATSocketMap; #endif class GkClient { public: typedef GkClient Base; GkClient(); virtual ~GkClient(); void OnReload(); void CheckRegistration(); bool CheckFrom(const RasMsg *) const; bool CheckFrom(const PIPSocket::Address & ip) const; bool CheckFrom(const H225_TransportAddress & addr) const; bool IsRegistered() const { return m_registered; } bool IsNATed() const { return m_natClient != NULL; } bool UsesH46018() const { return m_registeredH46018; } PString GetParent() const; bool UsesAdditiveRegistration() const; bool AdditiveRegister(H225_ArrayOf_AliasAddress & aliases, int & rejectReason, H225_ArrayOf_ClearToken * tokens, H225_ArrayOf_CryptoH323Token * cryptotokens); bool AdditiveUnRegister(const H225_ArrayOf_AliasAddress & aliases); void AppendLocalAlias(const H225_ArrayOf_AliasAddress & aliases); void RemoveLocalAlias(const H225_ArrayOf_AliasAddress & aliases); bool OnSendingGRQ(H225_GatekeeperRequest &grq); bool OnSendingRRQ(H225_RegistrationRequest &rrq); bool OnSendingARQ(H225_AdmissionRequest &arq, Routing::AdmissionRequest &req); bool OnSendingLRQ(H225_LocationRequest &lrq, Routing::LocationRequest &req); bool OnSendingARQ(H225_AdmissionRequest &arq, Routing::SetupRequest &req, bool answer = false); bool OnSendingARQ(H225_AdmissionRequest &arq, Routing::FacilityRequest &req); bool OnSendingDRQ(H225_DisengageRequest &drq, const callptr &call); bool OnSendingURQ(H225_UnregistrationRequest &urq); bool SendARQ(Routing::AdmissionRequest &); bool SendLRQ(Routing::LocationRequest &); bool SendARQ(Routing::SetupRequest &, bool answer = false); bool SendARQ(Routing::FacilityRequest &); void SendDRQ(const callptr &); void SendURQ(); // Signalling Messages bool HandleSetup(SetupMsg & setup, bool fromInternal); bool RewriteE164(H225_AliasAddress & alias, bool); bool RewriteE164(H225_ArrayOf_AliasAddress & alias, bool); bool RewriteE164(SetupMsg &setup, bool); /** Fills LRQ with approtiation tokens/cryptoTokens containing configured username/password data. Declared outside SetPassword template because it should not depend on authMode. */ void SetNBPassword( H225_LocationRequest& lrq, /// LRQ message to be filled with tokens const PString& id // username to be put inside tokens/cryptoTokens ); /** Fills LRQ with approtiation tokens/cryptoTokens containing, taking both the username and the password from config [Endpoint] section. */ void SetNBPassword( H225_LocationRequest& lrq /// LRQ message to be filled with tokens ) { SetPassword(lrq); } template void SetPassword(RAS & rasmsg, const PString & id) { for (PINDEX i = 0; i < m_h235Authenticators->GetSize(); i++) { H235Authenticator * authenticator = (H235Authenticator *)(*m_h235Authenticators)[i].Clone(); if (authenticator && authenticator->IsSecuredPDU(rasmsg.GetTag(), FALSE)) { authenticator->SetLocalId(id); authenticator->SetPassword(m_password); if (authenticator->PrepareTokens(rasmsg.m_tokens, rasmsg.m_cryptoTokens)) { PTRACE(4, "GKClient\tPrepared PDU with authenticator " << authenticator); } } delete authenticator; } if (rasmsg.m_tokens.GetSize() > 0) rasmsg.IncludeOptionalField(RAS::e_tokens); if (rasmsg.m_cryptoTokens.GetSize() > 0) rasmsg.IncludeOptionalField(RAS::e_cryptoTokens); } template void SetPassword(RAS & rasmsg) { SetPassword(rasmsg, m_h323Id.GetSize() > 0 ? m_h323Id[0] : (m_e164.GetSize() > 0 ? m_e164[0] : PString::Empty()) ); } private: bool Discovery(); void Register(); void Unregister(); bool GetAltGK(); void BuildRRQ(H225_RegistrationRequest &); void BuildFullRRQ(H225_RegistrationRequest &); void BuildLightWeightRRQ(H225_RegistrationRequest &); bool WaitForACF(H225_AdmissionRequest &, RasRequester &, Routing::RoutingRequest *); H225_AdmissionRequest & BuildARQ(H225_AdmissionRequest &); void OnRCF(RasMsg *); void OnRRJ(RasMsg *); void OnARJ(RasMsg *); bool OnURQ(RasMsg *); bool OnDRQ(RasMsg *); bool OnBRQ(RasMsg *); bool OnIRQ(RasMsg *); bool RewriteString(PString &, bool) const; void SetClearTokens(H225_ArrayOf_ClearToken &, const PString &); void SetCryptoTokens(H225_ArrayOf_CryptoH323Token &, const PString &); void SetRasAddress(H225_ArrayOf_TransportAddress &); void SetCallSignalAddress(H225_ArrayOf_TransportAddress &); RasServer *m_rasSrv; PIPSocket::Address m_gkaddr, m_loaddr; WORD m_gkport; /// status of the registration with the parent gatekeeper bool m_registered; /// parent discovery status (DNS resolved, GRQ/GCF exchanged) bool m_discoveryComplete; PString m_password, m_rrjReason; H225_EndpointIdentifier m_endpointId; H225_GatekeeperIdentifier m_gatekeeperId; PMutex m_rrqMutex; /// Use Additive Registration PBoolean m_useAdditiveRegistration; /// reregistration timeout (seconds) long m_ttl; /// timeout to send a next RRQ (milliseconds) long m_timer; /// intial interval (seconds) between resending an RRQ message (seconds) long m_retry; /// current RRQ resend interval (double with each failure) (seconds) long m_resend; /// maximun RRQ resend interval (seconds) long m_gkfailtime; PTime m_registeredTime; AlternateGKs *m_gkList; bool m_useAltGKPermanent; int m_authMode; Toolkit::RewriteData *m_rewriteInfo; GkClientHandler *m_handlers[4]; NATClient *m_natClient; enum ParentVendors { ParentVendor_Generic, ParentVendor_GnuGk, ParentVendor_Cisco }; /// vendor of the parent gatekeeper int m_parentVendor; enum EndpointTypes { EndpointType_Terminal, EndpointType_Gateway }; /// endpoint type to set in RRQs int m_endpointType; /// send GRQ prior to registration bool m_discoverParent; /// list of local prefixes, if #m_endpointType# is set to gateway PStringArray m_prefixes; /// list of H.323ID aliases to register with PStringArray m_h323Id; /// list of E.164 aliases to register with PStringArray m_e164; /// list of Authenticators H235Authenticators * m_h235Authenticators; // enable H.460.18 (offer to parent) bool m_enableH46018; // registered with H.460.18 support bool m_registeredH46018; #ifdef HAS_H46023 // Handle H46023 RCF void H46023_RCF(H460_FeatureStd * feat); // Handle H46023 ACF void H46023_ACF(callptr m_call, H460_FeatureStd * feat); // Run STUN test void RunSTUNTest(const H323TransportAddress & addr); // Force Reregistration void H46023_ForceReregistration(); // Notify NAT type bool H46023_TypeNotify(int & nattype); // detected NAT type int m_nattype; // notify of NAT type bool m_natnotify; // enable H.460.23 (offer to parent) bool m_enableH46023; // registered with H.460.23 support bool m_registeredH46023; // STUN Client STUNClient * m_stunClient; // ALG Detected bool m_algDetected; // Call NAT Strategy Map PMutex m_strategyMutex; GkNATSocketMap m_natstrategy; public: // NAT type detected void H46023_TypeDetected(int nattype); // Create socket pair bool H46023_CreateSocketPair(const H225_CallIdentifier & id, WORD sessionID, UDPProxySocket * & rtp, UDPProxySocket * & rtcp, bool & nated); // Set the NAT Strategy void H46023_SetNATStategy(const H225_CallIdentifier & id, unsigned nat); // Find the NAT Strategy CallRec::NatStrategy H46023_GetNATStategy(const H225_CallIdentifier & id); // Set the socketPair void H46023_SetSocketPair(const H225_CallIdentifier & id, WORD sessionID, UDPProxySocket * rtp, UDPProxySocket * rtcp); // Set Alternates (Annex A) void H46023_SetAlternates(const H225_CallIdentifier & id, WORD session, PString cui, unsigned muxID, H323TransportAddress m_rtp, H323TransportAddress m_rtcp); void H46023_LoadAlternates(const H225_CallIdentifier & id, WORD session, PString & cui, unsigned & muxID, H323TransportAddress & m_rtp, H323TransportAddress & m_rtcp); // Set Alternates (Annex B) void H46023_SetAlternates(const H225_CallIdentifier & id, const H46024B_ArrayOf_AlternateAddress & alternates); #endif }; #endif // GKCLIENT_H gnugk-3.4/PaxHeaders.16356/ProxyChannel.h0000644000175000001440000000005012216625347016314 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/ProxyChannel.h0000644000175000001440000006361112216625347015264 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // ProxyChannel.h // // Copyright (c) Citron Network Inc. 2001-2003 // Copyright (c) 2002-2013, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef PROXYCHANNEL_H #define PROXYCHANNEL_H "@(#) $Id: ProxyChannel.h,v 1.155.2.1 2013/09/19 16:48:39 willamowius Exp $" #include #include #include #include "yasocket.h" #include "RasTbl.h" #include "gktimer.h" #include "config.h" #ifdef HAS_H46026 #include #include #endif class Q931; class PASN_OctetString; class H225_CallTerminationCause; class H225_H323_UserInformation; class H225_H323_UU_PDU_h323_message_body; class H225_Setup_UUIE; class H225_CallProceeding_UUIE; class H225_Connect_UUIE; class H225_Alerting_UUIE; class H225_Information_UUIE; class H225_ReleaseComplete_UUIE; class H225_Facility_UUIE; class H225_Progress_UUIE; class H225_Status_UUIE; class H225_StatusInquiry_UUIE; class H225_SetupAcknowledge_UUIE; class H225_Notify_UUIE; class H225_TransportAddress; class H245Handler; class H245Socket; class UDPProxySocket; class ProxyHandler; class HandlerList; class SignalingMsg; template class H225SignalingMsg; typedef H225SignalingMsg SetupMsg; typedef H225SignalingMsg FacilityMsg; struct SetupAuthData; #ifdef _WIN32 typedef int ssize_t; #endif extern const char *RoutedSec; extern const char *TLSSec; extern const char *ProxySection; const WORD DEFAULT_PACKET_BUFFER_SIZE = 2048; void PrintQ931(int, const char *, const char *, const Q931 *, const H225_H323_UserInformation *); bool GetUUIE(const Q931 & q931, H225_H323_UserInformation & uuie); void SetUUIE(Q931 & q931, const H225_H323_UserInformation & uuie); ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const H323TransportAddress & toAddress); ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const PIPSocket::Address & ip, WORD port); class ProxySocket : public USocket { public: enum Result { NoData, Connecting, Forwarding, Closing, Error, DelayedConnecting // H.460.18 }; ProxySocket( IPSocket *self, const char *type, WORD buffSize = DEFAULT_PACKET_BUFFER_SIZE ); virtual ~ProxySocket() = 0; // abstract class // new virtual function virtual Result ReceiveData(); virtual bool ForwardData(); virtual bool EndSession(); virtual void OnError() {} bool IsConnected() const { return connected; } void SetConnected(bool c) { connected = c; } bool IsDeletable() const { return deletable; } void SetDeletable() { deletable = true; } ProxyHandler * GetHandler() const { return handler; } void SetHandler(ProxyHandler * h) { handler = h; } private: ProxySocket(); ProxySocket(const ProxySocket&); ProxySocket& operator=(const ProxySocket&); protected: BYTE *wbuffer; WORD wbufsize, buflen; private: bool connected, deletable; ProxyHandler *handler; }; class TCPProxySocket : public ServerSocket, public ProxySocket { public: TCPProxySocket(const char *, TCPProxySocket * = NULL, WORD = 0); virtual ~TCPProxySocket(); #ifndef LARGE_FDSET PCLASSINFO( TCPProxySocket, ServerSocket ) // override from class PTCPSocket virtual PBoolean Accept(PSocket &); virtual PBoolean Connect(const Address &, WORD, const Address &); virtual PBoolean Connect(const Address &); #endif // override from class ProxySocket virtual bool ForwardData(); virtual bool TransmitData(const PBYTEArray &); void DetachRemote(); void RemoveRemoteSocket(); void SetRemoteSocket(TCPProxySocket * ret) { remote=ret; } void LockRemote() { m_remoteLock.Wait(); } void UnlockRemote() { m_remoteLock.Signal(); } private: TCPProxySocket(); TCPProxySocket(const TCPProxySocket&); TCPProxySocket& operator=(const TCPProxySocket&); protected: struct TPKTV3 { TPKTV3(WORD); BYTE header, padding; WORD length; }; bool ReadTPKT(); PMutex m_remoteLock; // protect the remote member TCPProxySocket *remote; PBYTEArray buffer; public: bool InternalWrite(const PBYTEArray &); protected: bool SetMinBufSize(WORD); BYTE *bufptr; TPKTV3 tpkt; unsigned tpktlen; }; class RTPLogicalChannel; class UDPProxySocket : public UDPSocket, public ProxySocket { public: #ifndef LARGE_FDSET PCLASSINFO( UDPProxySocket, UDPSocket ) #endif UDPProxySocket(const char * t, const H225_CallIdentifier & id); ~UDPProxySocket(); void UpdateSocketName(); void RemoveCallPtr() { m_call = NULL; } void SetDestination(H245_UnicastAddress &, callptr &); void SetForwardDestination(const Address &, WORD, H245_UnicastAddress *, callptr &); void SetReverseDestination(const Address &, WORD, H245_UnicastAddress *, callptr &); typedef void (UDPProxySocket::*pMem)(const Address &, WORD, H245_UnicastAddress *, callptr &); bool Bind(const Address &localAddr, WORD pt); int GetOSSocket() const { return os_handle; } void SetNAT(bool); bool isMute() { return mute; } void SetMute(bool toMute) { mute = toMute; } void OnHandlerSwapped() { std::swap(fnat, rnat); } void SetRTPSessionID(WORD id) { m_sessionID = id; } #ifdef HAS_H235_MEDIA void SetEncryptingRTPChannel(RTPLogicalChannel * lc) { m_encryptingLC = lc; } void RemoveEncryptingRTPChannel(RTPLogicalChannel * lc) { if (m_encryptingLC == lc) m_encryptingLC = NULL; } void SetDecryptingRTPChannel(RTPLogicalChannel * lc) { m_decryptingLC = lc; } void RemoveDecryptingRTPChannel(RTPLogicalChannel * lc) { if (m_decryptingLC == lc) m_decryptingLC = NULL; } #endif #ifdef HAS_H46018 void SetUsesH46019fc(bool fc) { m_h46019fc = fc; } // same socket is used for all directions; set if at least one side uses H.460.19 void SetUsesH46019() { m_useH46019 = true; } bool UsesH46019() const { return m_useH46019; } void SetH46019UniDirectional(bool val) { m_h46019uni = val; } void SetMultiplexDestination(const H323TransportAddress & toAddress, H46019Side side); void SetMultiplexID(PUInt32b multiplexID, H46019Side side); void SetMultiplexSocket(int multiplexSocket, H46019Side side); #endif // override from class ProxySocket virtual Result ReceiveData(); virtual bool OnReceiveData(void *, PINDEX, Address &, WORD &) { return true; } protected: virtual bool WriteData(const BYTE *, int); virtual bool Flush(); virtual bool ErrorHandler(PSocket::ErrorGroup); void SetMediaIP(const PString & direction, const Address & ip); // RTCP handler void BuildReceiverReport(const RTP_ControlFrame & frame, PINDEX offset, bool dst); H225_CallIdentifier m_callID; callptr * m_call; private: UDPProxySocket(); UDPProxySocket(const UDPProxySocket&); UDPProxySocket& operator=(const UDPProxySocket&); protected: Address fSrcIP, fDestIP, rSrcIP, rDestIP; WORD fSrcPort, fDestPort, rSrcPort, rDestPort; bool fnat, rnat; bool mute; bool m_isRTPType; bool m_isRTCPType; bool m_dontQueueRTP; bool m_EnableRTCPStats; WORD m_sessionID; RTPLogicalChannel * m_encryptingLC; RTPLogicalChannel * m_decryptingLC; #ifdef HAS_H46018 bool m_h46019fc; bool m_useH46019; bool m_h46019uni; bool m_h46019DetectionDone; PMutex m_h46019DetectionLock; // two (!), one or zero parties in a call through a UDPProxySocket may by multiplexed // UDPProxySocket always receives regular RTP, but may send out multiplexed H323TransportAddress m_multiplexDestination_A; // OLC side of first logical channel in this session PUInt32b m_multiplexID_A; // ID _to_ A side (only valid if m_multiplexDestination_A is set) int m_multiplexSocket_A; // only valid if m_multiplexDestination_A is set H323TransportAddress m_multiplexDestination_B; // OLCAck side of first logical channel in this session PUInt32b m_multiplexID_B; // ID _to_ B side (only valid if m_multiplexDestination_B is set) int m_multiplexSocket_B; // only valid if m_multiplexDestination_ is set) PMutex m_multiplexMutex; // protect multiplex IDs, addresses and sockets against access from concurrent threads #endif }; #if H323_H450 class X880_Invoke; class H4501_InterpretationApdu; #endif class CallSignalSocket : public TCPProxySocket { public: CallSignalSocket(); CallSignalSocket(CallSignalSocket *, WORD _port); virtual ~CallSignalSocket(); #ifdef LARGE_FDSET // override from class TCPProxySocket virtual bool Connect(const Address &); #else PCLASSINFO ( CallSignalSocket, TCPProxySocket ) // override from class TCPProxySocket virtual PBoolean Connect(const Address &); #endif // override from class ProxySocket virtual Result ReceiveData(); virtual bool EndSession(); virtual void OnError(); // override from TCPProxySocket virtual bool ForwardData(); void SendReleaseComplete(const H225_CallTerminationCause * = NULL); void SendReleaseComplete(H225_ReleaseCompleteReason::Choices); bool IsH245Tunneling() const { return m_h245Tunneling; } bool IsH245TunnelingTranslation() const { return m_h245TunnelingTranslation; } PASN_OctetString * GetNextQueuedH245Message(); bool HandleH245Mesg(PPER_Stream &, bool & suppress, H245Socket * h245sock = NULL); void SendPostDialDigits(); void OnH245ChannelClosed() { m_h245socket = NULL; } Address GetLocalAddr() { return localAddr; } Address GetPeerAddr() { return peerAddr; } Address GetMasqAddr() { return masqAddr; } PINDEX GetCallNumber() const { return m_call ? m_call->GetCallNumber() : 0; } H225_CallIdentifier GetCallIdentifier() const { return m_call ? m_call->GetCallIdentifier() : 0; } void BuildFacilityPDU(Q931 &, int, const PObject * = NULL); void BuildProgressPDU(Q931 &, PBoolean fromDestination); void BuildProceedingPDU(Q931 & ProceedingPDU, const H225_CallIdentifier & callId, unsigned crv); void BuildSetupPDU(Q931 &, const H225_CallIdentifier & callid, unsigned crv, const PString & destination, bool h245tunneling); void RemoveCall(); bool RerouteCall(CallLeg which, const PString & destination, bool h450transfer); void RerouteCaller(PString destination); void RerouteCalled(PString destination); void PerformConnecting(); // override from class ServerSocket virtual void Dispatch(); void DispatchNextRoute(); Result RetrySetup(); void TryNextRoute(); void RemoveH245Handler(); void SaveTCS(const H245_TerminalCapabilitySet & tcs) { m_savedTCS = tcs; } H245_TerminalCapabilitySet GetSavedTCS() const { return m_savedTCS; } bool SendTunneledH245(const PPER_Stream & strm); bool SendTunneledH245(const H245_MultimediaSystemControlMessage & h245msg); #ifdef HAS_H235_MEDIA bool IsH245Master() const { return m_isH245Master; } bool HandleH235TCS(H245_TerminalCapabilitySet & tcs); bool HandleH235OLC(H245_OpenLogicalChannel & olc); void SendEncryptionUpdateCommand(WORD flcn, BYTE oldPT, BYTE plainPT); void SendEncryptionUpdateRequest(WORD flcn, BYTE oldPT, BYTE plainPT); #endif H245Socket * GetH245Socket() const { return m_h245socket; } void SetH245Socket(H245Socket * sock) { m_h245socket = sock; } bool CompareH245Socket(H245Socket * sock) const { return sock == m_h245socket; } // intentionally comparing pointers protected: void SetRemote(CallSignalSocket *); bool CreateRemote(H225_Setup_UUIE &setupBody); public: #ifdef HAS_H46017 bool SendH46017Message(const H225_RasMessage & ras); void CleanupCall(); #endif #ifdef HAS_H46026 bool SendH46026RTP(unsigned sessionID, bool isRTP, const void * data, unsigned len); void PollPriorityQueue(); #endif bool MaintainConnection() const { return m_maintainConnection; } #ifdef HAS_H46018 bool IsCaller() const { return m_callerSocket; } bool IsTraversalClient() const; bool IsTraversalServer() const; bool CreateRemote(const H225_TransportAddress & addr); bool OnSCICall(const H225_CallIdentifier & callID, H225_TransportAddress sigAdr); bool IsCallFromTraversalServer() const { return m_callFromTraversalServer; } bool IsCallToTraversalServer() const { return m_callToTraversalServer; } void SetSessionMultiplexDestination(WORD session, bool isRTCP, const H323TransportAddress & toAddress, H46019Side side); const H245Handler * GetH245Handler() const { return m_h245handler; } #endif void LockH245Handler() { m_h245handlerLock.Wait(); } void UnlockH245Handler() { m_h245handlerLock.Signal(); } #ifdef HAS_H46023 bool IsH46024Call(const H225_Setup_UUIE & setupBody); #endif CallSignalSocket * GetRemote() const { return dynamic_cast(remote); } protected: void ForwardCall(FacilityMsg *msg); /// signaling message handlers void OnSetup(SignalingMsg *msg); void OnCallProceeding(SignalingMsg *msg); void OnConnect(SignalingMsg *msg); void OnAlerting(SignalingMsg *msg); void OnReleaseComplete(SignalingMsg *msg); void OnFacility(SignalingMsg *msg); void OnProgress(SignalingMsg *msg); void OnInformation(SignalingMsg *msg); bool OnTunneledH245(H225_ArrayOf_PASN_OctetString &, bool & suppress); bool OnFastStart(H225_ArrayOf_PASN_OctetString &, bool); #if H323_H450 bool OnH450PDU(H225_ArrayOf_PASN_OctetString &); bool OnH450Invoke(X880_Invoke &, H4501_InterpretationApdu &); bool OnH450CallTransfer(PASN_OctetString *); #endif template bool HandleH245Address(UUIE & uu) { if (uu.HasOptionalField(UUIE::e_h245Address)) { if (m_call) m_call->SetH245ResponseReceived(); if (SetH245Address(uu.m_h245Address)) return (m_h245handler != NULL); uu.RemoveOptionalField(UUIE::e_h245Address); return true; } return false; } template bool HandleFastStart(UUIE & uu, bool fromCaller) { if (!uu.HasOptionalField(UUIE::e_fastStart)) return false; if (!fromCaller && m_call) m_call->SetFastStartResponseReceived(); return m_h245handler != NULL ? OnFastStart(uu.m_fastStart, fromCaller) : false; } private: CallSignalSocket(const CallSignalSocket&); CallSignalSocket& operator=(const CallSignalSocket&); void InternalInit(); void BuildReleasePDU(Q931 &, const H225_CallTerminationCause *) const; // if return false, the h245Address field will be removed bool SetH245Address(H225_TransportAddress &); bool InternalConnectTo(); bool ForwardCallConnectTo(); /** @return A string that can be used to identify a calling number. */ PString GetCallingStationId( /// Q.931/H.225 Setup message with additional data const SetupMsg& setup, /// additional data SetupAuthData& authData ) const; /** @return A string that can be used to identify a calling number. */ PString GetCalledStationId( /// Q.931/H.225 Setup message with additional data const SetupMsg& setup, /// additional data SetupAuthData& authData ) const; /// @return a number dialed by the user PString GetDialedNumber( /// Q.931/H.225 Setup message with additional data const SetupMsg& setup ) const; void SetCallTypePlan(Q931 *q931); protected: callptr m_call; // localAddr is NOT the local address the socket bind to, // but the local address that remote socket bind to // they may be different in multi-homed environment Address localAddr, peerAddr, masqAddr; WORD peerPort; private: WORD m_crv; PMutex m_h245handlerLock; H245Handler * m_h245handler; H245Socket * m_h245socket; bool m_h245Tunneling; bool m_h245TunnelingTranslation; std::queue m_h245Queue; bool m_isnatsocket; bool m_maintainConnection; // eg. for H.460.17 Result m_result; /// stored for use by ForwardCall, NULL if ForwardOnFacility is disabled Q931 * m_setupPdu; #ifdef HAS_H235_MEDIA H225_ArrayOf_ClearToken * m_setupClearTokens; #endif /// true if the socket is connected to the caller, false if to the callee bool m_callerSocket; /// H.225.0 protocol version in use by the remote party unsigned m_h225Version; /// raw Setup data as received from the caller (for failover) PBYTEArray m_rawSetup; PMutex infomutex; // Information PDU processing Mutex H245_TerminalCapabilitySet m_savedTCS; // saved tcs to re-send #ifdef HAS_H46017 bool m_h46017Enabled; TCPProxySocket * rc_remote; // copy of the remote pointer that may be only used to send RC on call end #endif #ifdef HAS_H46018 bool m_callFromTraversalServer; // is this call from a traversal server ? bool m_callToTraversalServer; bool m_senderSupportsH46019Multiplexing; #endif #ifdef HAS_H235_MEDIA bool m_isH245Master; #endif #ifdef HAS_H46026 H46026ChannelManager * m_h46026PriorityQueue; #endif }; class CallSignalListener : public TCPListenSocket { #ifndef LARGE_FDSET PCLASSINFO ( CallSignalListener, TCPListenSocket ) #endif public: CallSignalListener(const Address &, WORD); ~CallSignalListener(); // override from class TCPListenSocket virtual ServerSocket *CreateAcceptor() const; protected: Address m_addr; }; #ifdef HAS_TLS class TLSCallSignalListener : public CallSignalListener { public: TLSCallSignalListener(const Address &, WORD); ~TLSCallSignalListener(); // override from class CallSignalListener virtual ServerSocket *CreateAcceptor() const; }; class TLSCallSignalSocket : public CallSignalSocket { public: TLSCallSignalSocket(); TLSCallSignalSocket(CallSignalSocket * s, WORD port); virtual ~TLSCallSignalSocket(); virtual bool Connect(const Address & addr); virtual PBoolean Connect(const Address & iface, WORD localPort, const Address & addr); // override from TCPProxySocket virtual bool Read(void * buf, int sz); virtual int GetLastReadCount() const { return m_lastReadCount; } virtual bool Write(const void * buf, int sz); virtual void Dispatch(); protected: SSL * m_ssl; int m_lastReadCount; }; #endif // HAS_TLS #ifdef HAS_H46018 class MultiplexRTPListener : public UDPSocket { #ifndef LARGE_FDSET PCLASSINFO ( MultiplexRTPListener, UDPSocket ) #endif public: MultiplexRTPListener(WORD pt, WORD buffSize = DEFAULT_PACKET_BUFFER_SIZE); virtual ~MultiplexRTPListener(); virtual int GetOSSocket() const { return os_handle; } virtual void ReceiveData(); protected: BYTE * wbuffer; WORD wbufsize; }; class RTPLogicalChannel; // class for a H.460.19 session: it includes both directions (the full RTP session) // when stored in the channel list in MultiplexedRTPHandler, // side A is the OLC side, side B is the OLCAck side of the first channel in the session // when the channel is handed out via GetChannelSwapped(), side A is the OLC side of _that_ channel class H46019Session { public: H46019Session(const H225_CallIdentifier & callid, WORD session, void * openedBy); void Dump() const; bool IsValid() const { return (m_session != INVALID_RTP_SESSION) || (m_flcn > 0); } bool sideAReady(bool isRTCP) const { return isRTCP ? IsSet(m_addrA_RTCP) : IsSet(m_addrA); } bool sideBReady(bool isRTCP) const { return isRTCP ? IsSet(m_addrB_RTCP) : IsSet(m_addrB); } H46019Session SwapSides() const; // return a copy with side A and B swapped bool IsKeepAlive(void * data, unsigned len, bool isRTCP) const { return isRTCP ? true : (len == 12); } void HandlePacket(PUInt32b receivedMultiplexID, const H323TransportAddress & fromAddress, void * data, unsigned len, bool isRTCP); static void Send(PUInt32b sendMultiplexID, const H323TransportAddress & toAddress, int ossocket, void * data, unsigned len, bool bufferHasRoomForID=false); //protected: H225_CallIdentifier m_callid; WORD m_session; WORD m_flcn; // only used to assign master assigned RTP session IDs void * m_openedBy; // side A (pointer to H245ProxyHandler used as an ID) void * m_otherSide; // side B (pointer to H245ProxyHandler used as an ID) H323TransportAddress m_addrA; H323TransportAddress m_addrA_RTCP; H323TransportAddress m_addrB; H323TransportAddress m_addrB_RTCP; PUInt32b m_multiplexID_fromA; PUInt32b m_multiplexID_toA; PUInt32b m_multiplexID_fromB; PUInt32b m_multiplexID_toB; int m_osSocketToA; int m_osSocketToA_RTCP; int m_osSocketToB; int m_osSocketToB_RTCP; bool m_EnableRTCPStats; #ifdef HAS_H235_MEDIA RTPLogicalChannel * m_encryptingLC; RTPLogicalChannel * m_decryptingLC; PUInt32b m_encryptMultiplexID; PUInt32b m_decryptMultiplexID; #endif }; class MultiplexedRTPReader : public SocketsReader { public: MultiplexedRTPReader(); virtual ~MultiplexedRTPReader(); virtual int GetRTPOSSocket() const { return m_multiplexRTPListener ? m_multiplexRTPListener->GetOSSocket() : INVALID_OSSOCKET; } virtual int GetRTCPOSSocket() const { return m_multiplexRTCPListener ? m_multiplexRTCPListener->GetOSSocket() : INVALID_OSSOCKET; } protected: virtual void OnStart(); virtual void ReadSocket(IPSocket * socket); MultiplexRTPListener * m_multiplexRTPListener; MultiplexRTPListener * m_multiplexRTCPListener; }; // handles multiplexed RTP and keepAlives class MultiplexedRTPHandler : public Singleton { public: MultiplexedRTPHandler(); virtual ~MultiplexedRTPHandler(); virtual void OnReload() { /* currently not runtime changable */ } virtual void AddChannel(const H46019Session & cha); virtual void UpdateChannelSession(const H225_CallIdentifier & callid, WORD flcn, void * openedBy, WORD session); virtual void UpdateChannel(const H46019Session & cha); virtual H46019Session GetChannel(const H225_CallIdentifier & callid, WORD session) const; virtual H46019Session GetChannelSwapped(const H225_CallIdentifier & callid, WORD session, void * openedBy) const; virtual void RemoveChannels(H225_CallIdentifier callid); // pass by value in case call gets removed #ifdef HAS_H235_MEDIA virtual void RemoveChannel(H225_CallIdentifier callid, RTPLogicalChannel * rtplc); #endif virtual void DumpChannels(const PString & msg = "") const; virtual bool HandlePacket(PUInt32b receivedMultiplexID, const H323TransportAddress & fromAddress, void * data, unsigned len, bool isRTCP); #ifdef HAS_H46026 virtual bool HandlePacket(const H225_CallIdentifier & callid, const H46026_UDPFrame & data); #endif virtual int GetRTPOSSocket() const { return m_reader ? m_reader->GetRTPOSSocket() : INVALID_OSSOCKET; } virtual int GetRTCPOSSocket() const { return m_reader ? m_reader->GetRTCPOSSocket() : INVALID_OSSOCKET; } virtual PUInt32b GetMultiplexID(const H225_CallIdentifier & callid, WORD session, void * to); virtual PUInt32b GetNewMultiplexID(); protected: MultiplexedRTPReader * m_reader; mutable PReadWriteMutex m_listLock; list m_h46019channels; PUInt32b idCounter; // we should make sure this counter is _not_ reset on reload }; #endif #ifdef HAS_H46026 // class for a H.460.26 session: it includes only the plain RTP side (multiplexed RTP is handled in H46019Session class H46026Session { public: H46026Session() : m_isValid(false) { } H46026Session(const H225_CallIdentifier & callid, WORD session, int osRTPSocket, int osRTCPSocket, const H323TransportAddress & toRTP, const H323TransportAddress & toRTCP); void Send(void * data, unsigned len, bool isRTCP); bool IsValid() const { return m_isValid; } void Dump() const; //protected: bool m_isValid; H225_CallIdentifier m_callid; WORD m_session; int m_osRTPSocket; int m_osRTCPSocket; H323TransportAddress m_toAddressRTP; H323TransportAddress m_toAddressRTCP; #ifdef HAS_H235_MEDIA RTPLogicalChannel * m_encryptingLC; RTPLogicalChannel * m_decryptingLC; #endif }; // handles stores H.460.26 RTP sessions class H46026RTPHandler : public Singleton { public: H46026RTPHandler(); virtual ~H46026RTPHandler(); virtual void OnReload() { /* currently not runtime changable */ } virtual void AddChannel(const H46026Session & chan); virtual void ReplaceChannel(const H46026Session & chan); virtual void UpdateChannelRTP(const H225_CallIdentifier & callid, WORD session, H323TransportAddress toRTP); virtual void UpdateChannelRTCP(const H225_CallIdentifier & callid, WORD session, H323TransportAddress toRTCP); virtual void RemoveChannels(H225_CallIdentifier callid); // pass by value in case call gets removed H46026Session FindSession(const H225_CallIdentifier & callid, WORD session) const; #ifdef HAS_H235_MEDIA virtual void UpdateChannelEncryptingLC(const H225_CallIdentifier & callid, WORD session, RTPLogicalChannel * lc); virtual void UpdateChannelDecryptingLC(const H225_CallIdentifier & callid, WORD session, RTPLogicalChannel * lc); #endif virtual void DumpChannels(const PString & msg = "") const; virtual bool HandlePacket(const H225_CallIdentifier & callid, H46026_UDPFrame & data); protected: mutable PReadWriteMutex m_listLock; list m_h46026channels; }; #endif class ProxyHandler : public SocketsReader { public: ProxyHandler(const PString& name); virtual ~ProxyHandler(); void Insert(TCPProxySocket *); void Insert(TCPProxySocket *, TCPProxySocket *); void Insert(UDPProxySocket *, UDPProxySocket *); void MoveTo(ProxyHandler *, TCPProxySocket *); bool IsEmpty() const { return m_socksize == 0; } void LoadConfig(); bool Detach(TCPProxySocket *); void Remove(TCPProxySocket *); private: // override from class RegularJob virtual void OnStart(); // override from class SocketsReader virtual bool BuildSelectList(SocketSelectList &); virtual void ReadSocket(IPSocket *); virtual void CleanUp(); void AddPairSockets(IPSocket *, IPSocket *); void FlushSockets(); void Remove(iterator); void DetachSocket(IPSocket *socket); ProxyHandler(); ProxyHandler(const ProxyHandler&); ProxyHandler& operator=(const ProxyHandler&); private: std::list m_removedTime; /// time to wait before deleting a closed socket PTimeInterval m_socketCleanupTimeout; #ifdef HAS_H46017 bool m_h46017Enabled; #endif }; class HandlerList { public: HandlerList(); virtual ~HandlerList(); /** @return Signaling proxy thread to handle a new signaling/H.245/T.120 socket. */ ProxyHandler* GetSigHandler(); /** @return RTP proxy thread to handle a pair of new RTP sockets. */ ProxyHandler* GetRtpHandler(); void LoadConfig(); private: HandlerList(const HandlerList&); HandlerList& operator=(const HandlerList&); private: /// signaling/H.245/T.120 proxy handling threads std::vector m_sigHandlers; /// RTP proxy handling threads std::vector m_rtpHandlers; /// number of signaling handlers unsigned m_numSigHandlers; /// number of RTP handlers unsigned m_numRtpHandlers; /// next available signaling handler unsigned m_currentSigHandler; /// next available RTP handler unsigned m_currentRtpHandler; /// atomic access to the handler lists PMutex m_handlerMutex; }; #endif // PROXYCHANNEL_H gnugk-3.4/PaxHeaders.16356/gk.cbp0000644000175000001440000000005012052221564014607 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gk.cbp0000644000175000001440000001771412052221564013562 0ustar00janusers00000000000000 gnugk-3.4/PaxHeaders.16356/cisco.cxx0000644000175000001440000000005011366541365015357 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/cisco.cxx0000644000175000001440000002023111366541365014316 0ustar00janusers00000000000000// // cisco.cxx // // Code automatically generated by asnparse. // #ifdef P_USE_PRAGMA #pragma implementation "cisco.h" #endif #include #include "cisco.h" #if ! H323_DISABLE_CISCO // // ArrayOf_AliasAddress // Cisco_ArrayOf_AliasAddress::Cisco_ArrayOf_AliasAddress(unsigned _tag, PASN_Object::TagClass _tagClass) : PASN_Array(_tag, _tagClass) { } PASN_Object * Cisco_ArrayOf_AliasAddress::CreateObject() const { return new H225_AliasAddress; } H225_AliasAddress & Cisco_ArrayOf_AliasAddress::operator[](PINDEX i) const { return (H225_AliasAddress &)array[i]; } PObject * Cisco_ArrayOf_AliasAddress::Clone() const { #ifndef PASN_LEANANDMEAN PAssert(IsClass(Cisco_ArrayOf_AliasAddress::Class()), PInvalidCast); #endif return new Cisco_ArrayOf_AliasAddress(*this); } // // ARQnonStandardInfo // Cisco_ARQnonStandardInfo::Cisco_ARQnonStandardInfo(unsigned _tag, PASN_Object::TagClass _tagClass) : PASN_Sequence(_tag, _tagClass, 0, TRUE, 5) { m_redirectIEinfo.SetConstraints(PASN_Object::FixedConstraint, 0, 255); m_callingOctet3a.SetConstraints(PASN_Object::FixedConstraint, 0, 255); m_displayInformationElement.SetConstraints(PASN_Object::FixedConstraint, 1, 128); m_interfaceSpecificBillingId.SetConstraints(PASN_Object::FixedConstraint, 1, 128); m_interfaceDescription.SetConstraints(PASN_Object::FixedConstraint, 1, 128); } #ifndef PASN_NOPRINTON void Cisco_ARQnonStandardInfo::PrintOn(ostream & strm) const { std::streamsize indent = strm.precision() + 2; strm << "{\n"; strm << setw(indent+14) << "sourceAlias = " << setprecision(indent) << m_sourceAlias << '\n'; strm << setw(indent+17) << "sourceExtAlias = " << setprecision(indent) << m_sourceExtAlias << '\n'; if (HasOptionalField(e_redirectIEinfo)) strm << setw(indent+17) << "redirectIEinfo = " << setprecision(indent) << m_redirectIEinfo << '\n'; if (HasOptionalField(e_callingOctet3a)) strm << setw(indent+17) << "callingOctet3a = " << setprecision(indent) << m_callingOctet3a << '\n'; if (HasOptionalField(e_displayInformationElement)) strm << setw(indent+28) << "displayInformationElement = " << setprecision(indent) << m_displayInformationElement << '\n'; if (HasOptionalField(e_interfaceSpecificBillingId)) strm << setw(indent+29) << "interfaceSpecificBillingId = " << setprecision(indent) << m_interfaceSpecificBillingId << '\n'; if (HasOptionalField(e_interfaceDescription)) strm << setw(indent+23) << "interfaceDescription = " << setprecision(indent) << m_interfaceDescription << '\n'; strm << setw(indent-1) << "}"; } #endif PObject::Comparison Cisco_ARQnonStandardInfo::Compare(const PObject & obj) const { #ifndef PASN_LEANANDMEAN PAssert(PIsDescendant(&obj, Cisco_ARQnonStandardInfo), PInvalidCast); #endif const Cisco_ARQnonStandardInfo & other = (const Cisco_ARQnonStandardInfo &)obj; Comparison result; if ((result = m_sourceAlias.Compare(other.m_sourceAlias)) != EqualTo) return result; if ((result = m_sourceExtAlias.Compare(other.m_sourceExtAlias)) != EqualTo) return result; return PASN_Sequence::Compare(other); } PINDEX Cisco_ARQnonStandardInfo::GetDataLength() const { PINDEX length = 0; length += m_sourceAlias.GetObjectLength(); length += m_sourceExtAlias.GetObjectLength(); return length; } PBoolean Cisco_ARQnonStandardInfo::Decode(PASN_Stream & strm) { if (!PreambleDecode(strm)) return FALSE; if (!m_sourceAlias.Decode(strm)) return FALSE; if (!m_sourceExtAlias.Decode(strm)) return FALSE; if (!KnownExtensionDecode(strm, e_redirectIEinfo, m_redirectIEinfo)) return FALSE; if (!KnownExtensionDecode(strm, e_callingOctet3a, m_callingOctet3a)) return FALSE; if (!KnownExtensionDecode(strm, e_displayInformationElement, m_displayInformationElement)) return FALSE; if (!KnownExtensionDecode(strm, e_interfaceSpecificBillingId, m_interfaceSpecificBillingId)) return FALSE; if (!KnownExtensionDecode(strm, e_interfaceDescription, m_interfaceDescription)) return FALSE; return UnknownExtensionsDecode(strm); } void Cisco_ARQnonStandardInfo::Encode(PASN_Stream & strm) const { PreambleEncode(strm); m_sourceAlias.Encode(strm); m_sourceExtAlias.Encode(strm); KnownExtensionEncode(strm, e_redirectIEinfo, m_redirectIEinfo); KnownExtensionEncode(strm, e_callingOctet3a, m_callingOctet3a); KnownExtensionEncode(strm, e_displayInformationElement, m_displayInformationElement); KnownExtensionEncode(strm, e_interfaceSpecificBillingId, m_interfaceSpecificBillingId); KnownExtensionEncode(strm, e_interfaceDescription, m_interfaceDescription); UnknownExtensionsEncode(strm); } PObject * Cisco_ARQnonStandardInfo::Clone() const { #ifndef PASN_LEANANDMEAN PAssert(IsClass(Cisco_ARQnonStandardInfo::Class()), PInvalidCast); #endif return new Cisco_ARQnonStandardInfo(*this); } // // LRQnonStandardInfo // Cisco_LRQnonStandardInfo::Cisco_LRQnonStandardInfo(unsigned _tag, PASN_Object::TagClass _tagClass) : PASN_Sequence(_tag, _tagClass, 0, TRUE, 5) { m_ttl.SetConstraints(PASN_Object::FixedConstraint, 1, 255); m_redirectIEinfo.SetConstraints(PASN_Object::FixedConstraint, 0, 255); m_callingOctet3a.SetConstraints(PASN_Object::FixedConstraint, 0, 255); m_displayInformationElement.SetConstraints(PASN_Object::FixedConstraint, 1, 128); } #ifndef PASN_NOPRINTON void Cisco_LRQnonStandardInfo::PrintOn(ostream & strm) const { std::streamsize indent = strm.precision() + 2; strm << "{\n"; strm << setw(indent+6) << "ttl = " << setprecision(indent) << m_ttl << '\n'; if (HasOptionalField(e_callIdentifier)) strm << setw(indent+17) << "callIdentifier = " << setprecision(indent) << m_callIdentifier << '\n'; if (HasOptionalField(e_redirectIEinfo)) strm << setw(indent+17) << "redirectIEinfo = " << setprecision(indent) << m_redirectIEinfo << '\n'; if (HasOptionalField(e_callingOctet3a)) strm << setw(indent+17) << "callingOctet3a = " << setprecision(indent) << m_callingOctet3a << '\n'; if (HasOptionalField(e_gatewaySrcInfo)) strm << setw(indent+17) << "gatewaySrcInfo = " << setprecision(indent) << m_gatewaySrcInfo << '\n'; if (HasOptionalField(e_displayInformationElement)) strm << setw(indent+28) << "displayInformationElement = " << setprecision(indent) << m_displayInformationElement << '\n'; strm << setw(indent-1) << "}"; } #endif PObject::Comparison Cisco_LRQnonStandardInfo::Compare(const PObject & obj) const { #ifndef PASN_LEANANDMEAN PAssert(PIsDescendant(&obj, Cisco_LRQnonStandardInfo), PInvalidCast); #endif const Cisco_LRQnonStandardInfo & other = (const Cisco_LRQnonStandardInfo &)obj; Comparison result; if ((result = m_ttl.Compare(other.m_ttl)) != EqualTo) return result; return PASN_Sequence::Compare(other); } PINDEX Cisco_LRQnonStandardInfo::GetDataLength() const { PINDEX length = 0; length += m_ttl.GetObjectLength(); return length; } PBoolean Cisco_LRQnonStandardInfo::Decode(PASN_Stream & strm) { if (!PreambleDecode(strm)) return FALSE; if (!m_ttl.Decode(strm)) return FALSE; if (!KnownExtensionDecode(strm, e_callIdentifier, m_callIdentifier)) return FALSE; if (!KnownExtensionDecode(strm, e_redirectIEinfo, m_redirectIEinfo)) return FALSE; if (!KnownExtensionDecode(strm, e_callingOctet3a, m_callingOctet3a)) return FALSE; if (!KnownExtensionDecode(strm, e_gatewaySrcInfo, m_gatewaySrcInfo)) return FALSE; if (!KnownExtensionDecode(strm, e_displayInformationElement, m_displayInformationElement)) return FALSE; return UnknownExtensionsDecode(strm); } void Cisco_LRQnonStandardInfo::Encode(PASN_Stream & strm) const { PreambleEncode(strm); m_ttl.Encode(strm); KnownExtensionEncode(strm, e_callIdentifier, m_callIdentifier); KnownExtensionEncode(strm, e_redirectIEinfo, m_redirectIEinfo); KnownExtensionEncode(strm, e_callingOctet3a, m_callingOctet3a); KnownExtensionEncode(strm, e_gatewaySrcInfo, m_gatewaySrcInfo); KnownExtensionEncode(strm, e_displayInformationElement, m_displayInformationElement); UnknownExtensionsEncode(strm); } PObject * Cisco_LRQnonStandardInfo::Clone() const { #ifndef PASN_LEANANDMEAN PAssert(IsClass(Cisco_LRQnonStandardInfo::Class()), PInvalidCast); #endif return new Cisco_LRQnonStandardInfo(*this); } #endif // if ! H323_DISABLE_CISCO // End of cisco.cxx gnugk-3.4/PaxHeaders.16356/h323util.t.cxx0000644000175000001440000000005012162135306016063 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/h323util.t.cxx0000644000175000001440000003005212162135306015024 0ustar00janusers00000000000000/* * h323util.t.cxx * * unit tests for h323util.cxx * * Copyright (c) 2011-2013, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include "config.h" #include "h323util.h" #include #include "gtest/gtest.h" namespace { class H323UtilTest : public ::testing::Test { protected: H323UtilTest() { // H.245 IPs h245unicast.SetTag(H245_UnicastAddress::e_iPAddress); H245_UnicastAddress_iPAddress & ip = h245unicast; ip.m_network.SetSize(4); ip.m_network[0] = 1; ip.m_network[1] = 2; ip.m_network[2] = 3; ip.m_network[3] = 4; ip.m_tsapIdentifier = 555; h245ipv4 = ip; // sockets ipv4socket = "3.4.5.6"; ipv6socket = "2001:0db8:85a3:08d3:1319:8a2e:0370:7344"; ipv6socket_localhost = "::1"; ipv6socket_ipv4mapped = "::ffff:192.168.1.100"; // H.225 IPs h225transport_withipv4 = SocketToH225TransportAddr(ipv4socket, 999); h225transport_withipv6 = SocketToH225TransportAddr(ipv6socket, 1111); h225transport_withipv6localhost = SocketToH225TransportAddr(ipv6socket_localhost, 1111); h323transport_withipv4 = H323TransportAddress(PIPSocket::Address("6.7.8.9"), 4567); h323transport_withipv6 = H323TransportAddress(PIPSocket::Address("2001:0db8:85a3:08d3:1319:8a2e:0370:7344"), 5678); } H245_UnicastAddress h245unicast; H245_UnicastAddress_iPAddress h245ipv4; PIPSocket::Address ipv4socket; PIPSocket::Address ipv6socket; PIPSocket::Address ipv6socket_localhost; PIPSocket::Address ipv6socket_ipv4mapped; H225_TransportAddress h225transport_withipv4; H225_TransportAddress h225transport_withipv6; H225_TransportAddress h225transport_withipv6localhost; H323TransportAddress h323transport_withipv4; H323TransportAddress h323transport_withipv6; }; TEST_F(H323UtilTest, PIPSocketAddressAsString) { EXPECT_STREQ("3.4.5.6:777", AsString(ipv4socket, 777)); EXPECT_STREQ("[2001:db8:85a3:8d3:1319:8a2e:370:7344]:888", AsString(ipv6socket, 888)); EXPECT_STREQ("[::1]:888", AsString(ipv6socket_localhost, 888)); EXPECT_STREQ("::1", AsString(ipv6socket_localhost)); } TEST_F(H323UtilTest, H245UnicastIPAddressAsString) { EXPECT_STREQ("1.2.3.4:555", AsString(h245unicast)); EXPECT_STREQ("1.2.3.4:555", AsString(h245ipv4)); } TEST_F(H323UtilTest, H225TransportAddressAsString) { EXPECT_TRUE(AsString(h225transport_withipv4).Find("03 04 05 06") != P_MAX_INDEX); EXPECT_STREQ("3.4.5.6:999", AsDotString(h225transport_withipv4)); EXPECT_STREQ("3.4.5.6:999", AsDotString(h225transport_withipv4)); EXPECT_STREQ("3.4.5.6:999", AsDotString(h225transport_withipv4, true)); EXPECT_STREQ("3.4.5.6", AsDotString(h225transport_withipv4, false)); EXPECT_STREQ("[2001:db8:85a3:8d3:1319:8a2e:370:7344]:1111", AsDotString(h225transport_withipv6)); EXPECT_STREQ("[::1]:1111", AsDotString(h225transport_withipv6localhost)); EXPECT_STREQ("[2001:db8:85a3:8d3:1319:8a2e:370:7344]:1111", AsDotString(h225transport_withipv6, true)); EXPECT_STREQ("2001:db8:85a3:8d3:1319:8a2e:370:7344", AsDotString(h225transport_withipv6, false)); } TEST_F(H323UtilTest, H323TransportAddressAsString) { EXPECT_STREQ("", AsString(H323TransportAddress())); EXPECT_STREQ("6.7.8.9:4567", AsString(h323transport_withipv4)); EXPECT_STREQ("[2001:db8:85a3:8d3:1319:8a2e:370:7344]:5678", AsString(h323transport_withipv6)); } TEST_F(H323UtilTest, H225TransportAddressGetVersion) { EXPECT_EQ(4u, GetVersion(h225transport_withipv4)); EXPECT_EQ(4u, GetVersion(H225_TransportAddress())); // empty H225_TransportAddress defaults to IPv4 EXPECT_EQ(6u, GetVersion(h225transport_withipv6)); EXPECT_EQ(6u, GetVersion(h225transport_withipv6localhost)); } TEST_F(H323UtilTest, EndpointTypeAsString) { H225_EndpointType ep_type; EXPECT_STREQ("unknown", AsString(ep_type)); ep_type.IncludeOptionalField(H225_EndpointType::e_terminal); EXPECT_STREQ("terminal", AsString(ep_type)); ep_type.IncludeOptionalField(H225_EndpointType::e_gateway); ep_type.IncludeOptionalField(H225_EndpointType::e_mcu); ep_type.IncludeOptionalField(H225_EndpointType::e_gatekeeper); EXPECT_STREQ("terminal,gateway,mcu,gatekeeper", AsString(ep_type)); } TEST_F(H323UtilTest, AliasAsString) { H225_AliasAddress alias; EXPECT_STREQ("invalid:UnknownType", AsString(alias, true)); EXPECT_STREQ("invalid", AsString(alias, false)); H323SetAliasAddress(PString("jan"), alias); EXPECT_STREQ("jan:h323_ID", AsString(alias, true)); EXPECT_STREQ("jan", AsString(alias, false)); EXPECT_STREQ(AsString(alias, false), StripAliasType(AsString(alias, true))); H323SetAliasAddress(PString("1234"), alias); EXPECT_STREQ("1234:dialedDigits", AsString(alias, true)); EXPECT_STREQ("1234", AsString(alias, false)); EXPECT_STREQ(AsString(alias, false), StripAliasType(AsString(alias, true))); H323SetAliasAddress(PString("1234@example.com"), alias); EXPECT_STREQ("1234@example.com:url_ID", AsString(alias, true)); EXPECT_STREQ("1234@example.com", AsString(alias, false)); H323SetAliasAddress(PString("h323:1234@example.com"), alias); EXPECT_STREQ("1234@example.com:url_ID", AsString(alias, true)); EXPECT_STREQ("1234@example.com", AsString(alias, false)); } TEST_F(H323UtilTest, ArrayOfAliasAsString) { H225_ArrayOf_AliasAddress aliases; EXPECT_STREQ("", AsString(aliases, true)); EXPECT_STREQ("", AsString(aliases, false)); aliases.SetSize(1); H323SetAliasAddress(PString("jan"), aliases[0]); EXPECT_STREQ("jan:h323_ID", AsString(aliases, true)); EXPECT_STREQ("jan", AsString(aliases, false)); aliases.SetSize(2); H323SetAliasAddress(PString("1234"), aliases[1]); EXPECT_STREQ("jan:h323_ID=1234:dialedDigits", AsString(aliases, true)); EXPECT_STREQ("jan=1234", AsString(aliases, false)); } TEST_F(H323UtilTest, OctetStringAsString) { PASN_OctetString bytes; bytes.SetSize(4); bytes[0] = 1; bytes[1] = 2; bytes[2] = 3; bytes[3] = 10; EXPECT_STREQ("01 02 03 0a", AsString(bytes)); } TEST_F(H323UtilTest, ByteArrayAsString) { PBYTEArray bytes; bytes.SetSize(4); bytes[0] = 'a'; bytes[1] = 'b'; bytes[2] = 07; bytes[3] = 'c'; EXPECT_STREQ("abc", AsString(bytes)); } TEST_F(H323UtilTest, Is10Dor11Dnumber) { H225_AliasAddress alias; H323SetAliasAddress(PString("jan"), alias); EXPECT_FALSE(Is10Dor11Dnumber(alias)); H323SetAliasAddress(PString("123456789"), alias); EXPECT_FALSE(Is10Dor11Dnumber(alias)); H323SetAliasAddress(PString("123456789012"), alias); EXPECT_FALSE(Is10Dor11Dnumber(alias)); H323SetAliasAddress(PString("10000000000"), alias); EXPECT_FALSE(Is10Dor11Dnumber(alias)); H323SetAliasAddress(PString("0000000000"), alias); EXPECT_FALSE(Is10Dor11Dnumber(alias)); H323SetAliasAddress(PString("1238888777"), alias); EXPECT_FALSE(Is10Dor11Dnumber(alias)); H323SetAliasAddress(PString("2238888777"), alias); EXPECT_TRUE(Is10Dor11Dnumber(alias)); H323SetAliasAddress(PString("2008888777"), alias); EXPECT_TRUE(Is10Dor11Dnumber(alias)); H323SetAliasAddress(PString("2008000000"), alias); EXPECT_TRUE(Is10Dor11Dnumber(alias)); } TEST_F(H323UtilTest, SetSockaddr) { // H323TransportAddress IPv4 version struct sockaddr_in sin; SetSockaddr(sin, h323transport_withipv4); WORD port = ntohs(sin.sin_port); EXPECT_EQ(port, 4567); // H245_UnicastAddress IPv4 version SetSockaddr(sin, h245unicast); port = ntohs(sin.sin_port); EXPECT_EQ(port, 555); // PIPSocket::Address IPv4 version SetSockaddr(sin, ipv4socket, 333); port = ntohs(sin.sin_port); EXPECT_EQ(port, 333); #ifdef hasIPV6 // H323TransportAddress IPv6 version struct sockaddr_in6 sin6; SetSockaddr(sin6, h323transport_withipv4); port = ntohs(sin6.sin6_port); EXPECT_EQ(port, 4567); SetSockaddr(sin6, h323transport_withipv6); port = ntohs(sin6.sin6_port); EXPECT_EQ(port, 5678); // PIPSocket::Address IPv6 version SetSockaddr(sin6, ipv6socket, 444); port = ntohs(sin6.sin6_port); EXPECT_EQ(port, 444); #endif } TEST_F(H323UtilTest, IsIPAddress) { EXPECT_TRUE(IsIPAddress("1.2.3.4")); EXPECT_TRUE(IsIPAddress("1.2.3.4:999")); EXPECT_TRUE(IsIPAddress("255.255.255.255")); EXPECT_TRUE(IsIPAddress("2001:0db8:85a3:08d3:1319:8a2e:0370:7344")); EXPECT_TRUE(IsIPAddress("::1")); EXPECT_FALSE(IsIPAddress("a.b.c.d")); EXPECT_FALSE(IsIPAddress("1.2.3.4.5")); EXPECT_FALSE(IsIPAddress("1.2.3.4:")); } TEST_F(H323UtilTest, IsIPv6Address) { EXPECT_TRUE(IsIPv6Address("2001:0db8:85a3:08d3:1319:8a2e:0370:7344")); EXPECT_TRUE(IsIPv6Address("2001:0db8:0000:08d3:0000:8a2e:0070:7344")); EXPECT_TRUE(IsIPv6Address("2001:db8:0:8d3:0:8a2e:70:7344")); EXPECT_TRUE(IsIPv6Address("[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]")); EXPECT_TRUE(IsIPv6Address("[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]:1234")); EXPECT_TRUE(IsIPv6Address("::1")); EXPECT_FALSE(IsIPv6Address("1.2.3.4")); EXPECT_FALSE(IsIPv6Address("abcd")); EXPECT_FALSE(IsIPv6Address("")); } TEST_F(H323UtilTest, UnmapIPv4Address) { PIPSocket::Address ip = ipv6socket_ipv4mapped; UnmapIPv4Address(ip); EXPECT_EQ(4u, ip.GetVersion()); ip = ipv4socket; UnmapIPv4Address(ip); EXPECT_EQ(4u, ip.GetVersion()); ip = ipv6socket; UnmapIPv4Address(ip); EXPECT_EQ(6u, ip.GetVersion()); } TEST_F(H323UtilTest, MapIPv4Address) { PIPSocket::Address ip = ipv4socket; MapIPv4Address(ip); EXPECT_EQ(6u, ip.GetVersion()); UnmapIPv4Address(ip); EXPECT_EQ(ip, ipv4socket); ip = ipv6socket; MapIPv4Address(ip); EXPECT_EQ(6u, ip.GetVersion()); } TEST_F(H323UtilTest, IsLoopback) { PIPSocket::Address ip; EXPECT_TRUE(IsLoopback(ip)); } TEST_F(H323UtilTest, SplitIPAndPort) { PStringArray parts; parts = SplitIPAndPort("1.2.3.4", 1234); EXPECT_STREQ(parts[0], "1.2.3.4"); EXPECT_STREQ(parts[1], "1234"); parts = SplitIPAndPort("1.2.3.4:1234", 1235); EXPECT_STREQ(parts[0], "1.2.3.4"); EXPECT_STREQ(parts[1], "1234"); parts = SplitIPAndPort("2001:0db8:85a3:08d3:1319:8a2e:0370:7344", 1234); EXPECT_STREQ(parts[0], "2001:0db8:85a3:08d3:1319:8a2e:0370:7344"); EXPECT_STREQ(parts[1], "1234"); parts = SplitIPAndPort("[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]:1234", 1235); EXPECT_STREQ(parts[0], "2001:0db8:85a3:08d3:1319:8a2e:0370:7344"); EXPECT_STREQ(parts[1], "1234"); } TEST_F(H323UtilTest, GetGUIDString) { H225_GloballyUniqueID id; EXPECT_STREQ("00000000 00000000 00000000 00000000", GetGUIDString(id, true)); EXPECT_STREQ("0 0 0 0", GetGUIDString(id, false)); EXPECT_EQ(35, GetGUIDString(id, true).GetLength()); } TEST_F(H323UtilTest, StringToCallId) { PString str = "00000000 00000000 00000000 00000000"; H225_CallIdentifier callid = StringToCallId(str); EXPECT_STREQ("00000000 00000000 00000000 00000000", GetGUIDString(callid.m_guid, true)); str = "00000001 00000020 00000300 00004000"; callid = StringToCallId(str); EXPECT_STREQ("00000001 00000020 00000300 00004000", GetGUIDString(callid.m_guid, true)); } TEST_F(H323UtilTest, FindAlias) { H225_ArrayOf_AliasAddress aliases; aliases.SetSize(2); H323SetAliasAddress(PString("jan"), aliases[0]); H323SetAliasAddress(PString("1234"), aliases[1]); EXPECT_EQ(0, FindAlias(aliases, "jan")); EXPECT_EQ(1, FindAlias(aliases, "1234")); EXPECT_EQ(P_MAX_INDEX, FindAlias(aliases, "9999")); } TEST_F(H323UtilTest, MatchPrefix) { EXPECT_EQ(0, MatchPrefix("123456789", "44")); EXPECT_EQ(3, MatchPrefix("123456789", "123")); EXPECT_EQ(-3, MatchPrefix("123456789", "!123")); EXPECT_EQ(3, MatchPrefix("123456789", "1.3")); EXPECT_EQ(0, MatchPrefix("123456789", "1.4")); EXPECT_EQ(3, MatchPrefix("123456789", "1%3")); EXPECT_EQ(0, MatchPrefix("123456789", "1%4")); } TEST_F(H323UtilTest, RewriteString) { PString unused; EXPECT_STREQ("1111497654321", RewriteString("497654321", "49", "111149", unused)); EXPECT_STREQ("111149771777", RewriteString("49771777", "49..1", "111149..1", unused)); EXPECT_STREQ("49123456", RewriteString("777749123456", "%%%%49", "49", unused)); PString postdial; PString result = RewriteString("49123", "49...", "49...", postdial, true); EXPECT_STREQ("49", result); EXPECT_STREQ("123", postdial); } TEST_F(H323UtilTest, RewriteWildcard) { EXPECT_STREQ("12345678@mydomain.com", RewriteWildcard("12345678", "{\\1}@mydomain.com")); EXPECT_STREQ("1234@mydomain.com", RewriteWildcard("12345678", "{^\\d(4)}@mydomain.com")); EXPECT_STREQ("5678@mydomain.com", RewriteWildcard("12345678", "{\\d(4)$}@mydomain.com")); } } // namespace gnugk-3.4/PaxHeaders.16356/gksql_sqlite.cxx0000644000175000001440000000005012214316315016745 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gksql_sqlite.cxx0000644000175000001440000002443112214316315015712 0ustar00janusers00000000000000/* * gksql_sqlite.cxx * * SQLite driver module for GnuGk * * Copyright (c) 2007-2013, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include "config.h" #if HAS_SQLITE #include #include #include "gksql.h" static PDynaLink g_sharedLibrary; static int (*g_sqlite3_changes)(sqlite3*) = NULL; static int (*g_sqlite3_close)(sqlite3 *) = NULL; static const char * (*g_sqlite3_errmsg)(sqlite3*) = NULL; static int (*g_sqlite3_exec)(sqlite3*, const char *sql, int (*callback)(void*,int,char**,char**), void *, char **errmsg) = NULL; static void (*g_sqlite3_free)(void*) = NULL; static char * (*g_sqlite3_mprintf)(const char*,...) = NULL; static int (*g_sqlite3_open)(const char *filename, sqlite3 **ppDb) = NULL; /** Class that encapsulates SQL query result for SQLite backend. It does not provide any multithread safety, so should be accessed from a single thread at time. */ class GkSQLiteResult : public GkSQLResult { public: /// Build the result from SELECT type query GkSQLiteResult( /// number of rows affected by the query long numRowsAffected, /// query result vector * resultRows ); /// Build the empty result and store query execution error information GkSQLiteResult( /// SQLite specific error code unsigned int errorCode, /// SQLite specific error message text const char* errorMsg ); virtual ~GkSQLiteResult(); /** @return Backend specific error message, if the query failed. */ virtual PString GetErrorMessage(); /** @return Backend specific error code, if the query failed. */ virtual long GetErrorCode(); /** Fetch a single row from the result set. After each row is fetched, cursor position is moved to a next row. @return True if the row has been fetched, false if no more rows are available. */ virtual bool FetchRow( /// array to be filled with string representations of the row fields PStringArray & result ); virtual bool FetchRow( /// array to be filled with string representations of the row fields ResultRow & result ); private: GkSQLiteResult(); GkSQLiteResult(const GkSQLiteResult&); GkSQLiteResult& operator=(const GkSQLiteResult&); protected: /// query result for SELECT type queries vector * m_sqlResult; /// the most recent row returned by fetch operation int m_sqlRow; /// SQLite specific error code (if the query failed) unsigned int m_errorCode; /// SQLite specific error message text (if the query failed) PString m_errorMessage; }; /// SQLite backend connection implementation. class GkSQLiteConnection : public GkSQLConnection { public: /// Build a new SQLite connection object GkSQLiteConnection( /// name to use in the log const char* name = "SQLite" ); virtual ~GkSQLiteConnection(); protected: class GkSQLiteConnWrapper : public GkSQLConnection::SQLConnWrapper { public: GkSQLiteConnWrapper( /// unique identifier for this connection int id, /// SQLite connection object sqlite3 * conn ) : SQLConnWrapper(id, "localhost"), m_conn(conn) { } virtual ~GkSQLiteConnWrapper(); private: GkSQLiteConnWrapper(); GkSQLiteConnWrapper(const GkSQLiteConnWrapper&); GkSQLiteConnWrapper& operator=(const GkSQLiteConnWrapper&); public: sqlite3 * m_conn; }; /** Create a new SQL connection using parameters stored in this object. When the connection is to be closed, the object is simply deleted using delete operator. @return NULL if database connection could not be established or an object of GkSQLiteConnWrapper class. */ virtual SQLConnPtr CreateNewConnection( /// unique identifier for this connection int id ); /** Execute the query using specified SQL connection. @return Query execution result. */ virtual GkSQLResult* ExecuteQuery( /// SQL connection to use for query execution SQLConnPtr conn, /// query string const char* queryStr, /// maximum time (ms) for the query execution, -1 means infinite long timeout = -1 ); /** Escape any special characters in the string, so it can be used in a SQL query. @return Escaped string. */ virtual PString EscapeString( /// SQL connection to get escaping parameters from SQLConnPtr conn, /// string to be escaped const char* str ); private: GkSQLiteConnection(const GkSQLiteConnection&); GkSQLiteConnection& operator=(const GkSQLiteConnection&); bool m_escapeDoubleQuotes; }; GkSQLiteResult::GkSQLiteResult( /// number of rows affected by the query long numRowsAffected, /// query result vector * selectResult ) : GkSQLResult(false), m_sqlResult(selectResult), m_sqlRow(-1), m_errorCode(0) { m_numRows = numRowsAffected; if ((long)selectResult->size() > numRowsAffected) m_numRows = selectResult->size(); if (!selectResult->empty()) m_numFields = (*selectResult)[0]->size(); else m_numFields = 0; m_queryError = false; } GkSQLiteResult::GkSQLiteResult( /// SQLite specific error code unsigned int errorCode, /// SQLite specific error message text const char* errorMsg ) : GkSQLResult(true), m_sqlResult(NULL), m_sqlRow(-1), m_errorCode(errorCode), m_errorMessage(errorMsg) { m_queryError = true; } GkSQLiteResult::~GkSQLiteResult() { if (m_sqlResult != NULL) { for(unsigned i=0; i < m_sqlResult->size(); i++){ delete (*m_sqlResult)[i]; } delete m_sqlResult; } } PString GkSQLiteResult::GetErrorMessage() { return m_errorMessage; } long GkSQLiteResult::GetErrorCode() { return m_errorCode; } bool GkSQLiteResult::FetchRow( /// array to be filled with string representations of the row fields PStringArray & result ) { if (m_sqlResult == NULL || m_numRows <= 0) return false; if (m_sqlRow < 0) m_sqlRow = 0; if (m_sqlRow >= m_numRows) return false; for (int i=0; i < m_numFields; i++) { result[i] = (*((*m_sqlResult)[m_sqlRow]))[i].first; } m_sqlRow++; return true; } bool GkSQLiteResult::FetchRow( /// array to be filled with string representations of the row fields ResultRow & result ) { if (m_sqlResult == NULL || m_numRows <= 0) return false; if (m_sqlRow < 0) m_sqlRow = 0; if (m_sqlRow >= m_numRows) return false; result = *((*m_sqlResult)[m_sqlRow]); m_sqlRow++; return true; } GkSQLiteConnection::GkSQLiteConnection( /// name to use in the log const char* name ) : GkSQLConnection(name) { m_escapeDoubleQuotes = true; // TODO: add config switch to turn this off when user can assure no double qoutes are used for literals } GkSQLiteConnection::~GkSQLiteConnection() { } GkSQLiteConnection::GkSQLiteConnWrapper::~GkSQLiteConnWrapper() { (*g_sqlite3_close)(m_conn); } GkSQLConnection::SQLConnPtr GkSQLiteConnection::CreateNewConnection( /// unique identifier for this connection int id ) { if (!g_sharedLibrary.IsLoaded()) { if (m_library.IsEmpty()) { #ifdef _WIN32 m_library = "sqlite3" + g_sharedLibrary.GetExtension(); #else m_library = "libsqlite3" + g_sharedLibrary.GetExtension(); #endif } if (!g_sharedLibrary.Open(m_library)) { PTRACE (1, GetName() << "\tCan't load library " << m_library); return NULL; } if (!g_sharedLibrary.GetFunction("sqlite3_changes", (PDynaLink::Function &)g_sqlite3_changes) || !g_sharedLibrary.GetFunction("sqlite3_close", (PDynaLink::Function &)g_sqlite3_close) || !g_sharedLibrary.GetFunction("sqlite3_errmsg", (PDynaLink::Function &)g_sqlite3_errmsg) || !g_sharedLibrary.GetFunction("sqlite3_exec", (PDynaLink::Function &)g_sqlite3_exec) || !g_sharedLibrary.GetFunction("sqlite3_free", (PDynaLink::Function &)g_sqlite3_free) || !g_sharedLibrary.GetFunction("sqlite3_mprintf", (PDynaLink::Function &)g_sqlite3_mprintf) || !g_sharedLibrary.GetFunction("sqlite3_open", (PDynaLink::Function &)g_sqlite3_open) ) { #ifdef hasDynaLinkGetLastError PTRACE (1, GetName() << "\tFailed to load shared database library: " << g_sharedLibrary.GetLastError()); #else PTRACE (1, GetName() << "\tFailed to load shared database library: unknown error"); #endif g_sharedLibrary.Close(); SNMP_TRAP(5, SNMPError, Database, GetName() + " DLL load error"); return NULL; } } sqlite3 *conn; int rc = (*g_sqlite3_open)(m_database, &conn); if (rc) { PTRACE(2, GetName() << "\tSQLite connection to " << m_database << " failed (sqlite3_open failed): " << (*g_sqlite3_errmsg)(conn)); (*g_sqlite3_close)(conn); SNMP_TRAP(5, SNMPError, Database, GetName() + " connection failed") return NULL; } PTRACE(5, GetName() << "\tSQLite connection to " << m_database << " established successfully"); return new GkSQLiteConnWrapper(id, conn); } static int sqlite_callback(void * result, int argc, char **argv, char **azColName) { GkSQLResult::ResultRow * row = new GkSQLResult::ResultRow(); ((vector *)result)->push_back(row); for(int i=0; i < argc; i++){ row->push_back(pair(argv[i] ? argv[i] : "NULL", azColName[i])); } return 0; } GkSQLResult* GkSQLiteConnection::ExecuteQuery( /// SQL connection to use for query execution GkSQLConnection::SQLConnPtr con, /// query string const char* queryStr, /// maximum time (ms) for the query execution, -1 means infinite long /*timeout*/ ) { sqlite3 * conn = ((GkSQLiteConnWrapper*)con)->m_conn; vector * resultRows = new vector(); char *errormsg = NULL; int rc = (*g_sqlite3_exec)(conn, queryStr, sqlite_callback, resultRows, &errormsg); if (rc != SQLITE_OK) { delete resultRows; Disconnect(); return new GkSQLiteResult(rc, errormsg); } return new GkSQLiteResult((*g_sqlite3_changes)(conn), resultRows); } PString GkSQLiteConnection::EscapeString( /// SQL connection to get escaping parameters from SQLConnPtr /*conn*/, /// string to be escaped const char* str ) { PString s(str); // remove all special characters 0x00-0x1f, except TAB PINDEX i = 0; while (i < s.GetLength()) { if (s.Mid(i, 1) < PString(' ') && s.Mid(i, 1) != PString('\t')) { s.Delete(i, 1); } else { ++i; } } s.Replace("'", "''", TRUE); if (m_escapeDoubleQuotes) { // causes double double-quotes when used inside literal in single-quotes s.Replace("\"", "\"\"", TRUE); } return s; } namespace { GkSQLCreator SQLiteCreator("SQLite"); } #endif /* HAS_SQLITE */ gnugk-3.4/PaxHeaders.16356/yasocket.h0000644000175000001440000000005012076103042015507 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/yasocket.h0000644000175000001440000003227112076103042014455 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // yasocket.h // // Copyright (c) Citron Network Inc. 2002-2003 // Copyright (c) 2004-2012, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef YASOCKET_H #define YASOCKET_H "@(#) $Id: yasocket.h,v 1.52 2013/01/17 23:15:46 willamowius Exp $" #include #include #include #include "job.h" #include "config.h" #ifdef LARGE_FDSET // yet another socket class to replace PSocket (Unix only) class YaSocket : public NamedObject { public: typedef PIPSocket::Address Address; YaSocket(); virtual ~YaSocket(); int GetHandle() const { return os_handle; } bool IsOpen() const { return os_handle > 0; } bool Close(); void SetReadTimeout(const PTimeInterval & time) { readTimeout = time; } bool Read(void *, int); bool ReadBlock(void *, int); int GetLastReadCount() const { return lastReadCount; } void SetWriteTimeout(const PTimeInterval & time) { writeTimeout = time; } bool Write(const void *, int); int GetLastWriteCount() const { return lastWriteCount; } void SetPort(WORD pt) { port = pt; } WORD GetPort() const { return port; } virtual PBoolean GetLocalAddress(Address &) const; virtual PBoolean GetLocalAddress(Address &, WORD &) const; bool SetOption(int, int, int = SOL_SOCKET); bool SetOption(int, const void *, int, int = SOL_SOCKET); bool GetOption(int, int &, int = SOL_SOCKET); bool GetOption(int, void *, PINDEX, int); PSocket::Errors GetErrorCode(PSocket::ErrorGroup group) const { return lastErrorCode[group]; } int GetErrorNumber(PSocket::ErrorGroup group) const { return lastErrorNumber[group]; } PString GetErrorText(PSocket::ErrorGroup) const; bool ConvertOSError(int libReturnValue, PSocket::ErrorGroup = PSocket::LastGeneralError); bool CanRead( long timeout ) const; bool CanWrite( long timeout ) const; protected: virtual int os_recv(void *, int) = 0; virtual int os_send(const void *, int) = 0; bool SetNonBlockingMode(); bool Bind(const Address &, WORD); private: YaSocket(const YaSocket&); YaSocket& operator=(const YaSocket&); protected: int os_handle; int lastReadCount, lastWriteCount; WORD port; PTimeInterval readTimeout, writeTimeout; PSocket::Errors lastErrorCode[PSocket::NumErrorGroups]; int lastErrorNumber[PSocket::NumErrorGroups]; }; class YaTCPSocket : public YaSocket { public: YaTCPSocket(WORD = 0); virtual ~YaTCPSocket() { } void GetPeerAddress(Address &) const; void GetPeerAddress(Address &, WORD &) const; bool SetLinger(); bool Listen(unsigned, WORD, PSocket::Reusability reuse = PSocket::AddressIsExclusive); bool Listen(const Address &, unsigned, WORD, PSocket::Reusability reuse = PSocket::AddressIsExclusive); #ifdef hasIPV6 bool DualStackListen(WORD port); #endif // new virtual function virtual bool Accept(YaTCPSocket &); virtual bool Connect(const Address &, WORD, const Address &); virtual bool Connect(const Address &); protected: // override from class YaSocket virtual int os_recv(void *, int); virtual int os_send(const void *, int); private: YaTCPSocket(const YaTCPSocket&); YaTCPSocket& operator=(const YaTCPSocket&); private: #ifdef hasIPV6 sockaddr_in6 peeraddr; #else sockaddr_in peeraddr; #endif }; class YaUDPSocket : public YaSocket, public PObject { public: YaUDPSocket(WORD port=0, int iAddressFamily=AF_INET); bool Listen(unsigned, WORD, PSocket::Reusability reuse = PSocket::AddressIsExclusive); bool Listen(const Address &, unsigned, WORD, PSocket::Reusability reuse = PSocket::AddressIsExclusive); #ifdef hasIPV6 bool DualStackListen(const Address & localAddr, WORD port); #endif void GetLastReceiveAddress(Address &, WORD &) const; void SetSendAddress(const Address &, WORD); /// Get the address to use for connectionless Write(). void GetSendAddress( Address& address, /// IP address to send packets. WORD& port /// Port to send packets. ); virtual bool ReadFrom(void *, PINDEX, Address &, WORD); virtual bool WriteTo(const void *, PINDEX, const Address &, WORD); protected: // override from class YaSocket virtual int os_recv(void *, int); virtual int os_send(const void *, int); private: YaUDPSocket(const YaUDPSocket&); YaUDPSocket& operator=(const YaUDPSocket&); private: #ifdef hasIPV6 sockaddr_in6 recvaddr, sendaddr; #else sockaddr_in recvaddr, sendaddr; #endif }; class YaSelectList { public: typedef std::vector::iterator iterator; typedef std::vector::const_iterator const_iterator; /// build a select list for more than one socket YaSelectList( /// estimated number of sockets to be put in this select list size_t reserve = 512 ) : maxfd(0) { fds.reserve(reserve); } /// build a select list for more than one socket YaSelectList( /// name for this select list const PString &name, /// estimated number of sockets to be put in this select list size_t reserve = 512 ) : maxfd(0), m_name(name) { fds.reserve(reserve); } /// build a select list for signle socket only YaSelectList( YaSocket* singleSocket /// socket to be put on the list ) : fds(1, singleSocket), maxfd(singleSocket->GetHandle()) {} /// build a select list for signle socket only YaSelectList( /// name for this select list const PString &name, YaSocket* singleSocket /// socket to be put on the list ) : fds(1, singleSocket), maxfd(singleSocket->GetHandle()), m_name(name) {} void Append( YaSocket* s /// the socket to be appended ) { if (s && s->IsOpen()) { fds.push_back(s); if (s->GetHandle() > maxfd) maxfd = s->GetHandle(); } } bool IsEmpty() const { return fds.empty(); } int GetSize() const { return fds.size(); } YaSocket *operator[](int i) const { return fds[i]; } enum SelectType { Read, Write }; bool Select(SelectType, const PTimeInterval &); struct large_fd_set { large_fd_set() { memset(this, 0, sizeof(large_fd_set)); } void add(int fd) { if (fd >= 0 && fd < LARGE_FDSET) FD_SET(fd, &__fdset__); } bool has(int fd) { return (fd >= 0 && fd < LARGE_FDSET) ? FD_ISSET(fd, &__fdset__) : false; } operator fd_set *() { return &__fdset__; } union { fd_set __fdset__; char __mem__[LARGE_FDSET / 8]; }; }; PString GetName() const { return m_name; } private: std::vector fds; int maxfd; PString m_name; }; typedef YaSelectList SocketSelectList; typedef YaSocket IPSocket; typedef YaTCPSocket TCPSocket; typedef YaUDPSocket UDPSocket; #else // not LARGE_FDSET class SocketSelectList : public PSocket::SelectList { public: enum SelectType { Read, Write }; SocketSelectList(size_t) { } SocketSelectList(PIPSocket * s = NULL) { if (s && s->IsOpen()) Append(s); } SocketSelectList(const PString &name, size_t) : m_name(name) { } SocketSelectList(const PString &name, PIPSocket * s = NULL) : m_name(name) { if (s && s->IsOpen()) Append(s); } bool Select(SelectType, const PTimeInterval &); PSocket *operator[](int i) const; PString GetName() const { return m_name; } private: PString m_name; }; typedef PIPSocket IPSocket; class TCPSocket : public PTCPSocket, public NamedObject { public: PCLASSINFO(TCPSocket, PTCPSocket) TCPSocket(WORD pt = 0) : PTCPSocket(pt) { } virtual ~TCPSocket() { } // override from class PIPSocket PString GetName() const { return (const char *)NamedObject::GetName(); } #ifdef hasIPV6 bool DualStackListen(WORD port); #endif private: TCPSocket(const TCPSocket&); TCPSocket& operator=(const TCPSocket&); }; class UDPSocket : public PUDPSocket, public NamedObject { public: PCLASSINFO(UDPSocket, PUDPSocket) UDPSocket(WORD port = 0, int iAddressFamily = AF_INET); virtual ~UDPSocket() { } // override from class PIPSocket PString GetName() const { return (const char *)NamedObject::GetName(); } #ifdef hasIPV6 bool DualStackListen(const PIPSocket::Address & localAddr, WORD port); #endif private: UDPSocket(const UDPSocket&); UDPSocket& operator=(const UDPSocket&); }; #endif // LARGE_FDSET // abstract interface of utilities of a socket class USocket { public: USocket(IPSocket *, const char *); virtual ~USocket() = 0; // abstract class const char* Type() const { return type; } #ifdef LARGE_FDSET const PString& Name() const { return self->GetName(); } #else PString Name() const { return self->GetName(); } #endif // new virtual function virtual bool TransmitData(const PString &); virtual bool TransmitData(const PBYTEArray &); virtual bool TransmitData(const BYTE *, int); bool IsSocketOpen() const { return self->IsOpen(); } bool CloseSocket() { return IsSocketOpen() ? self->Close() : false; } virtual bool Flush(); bool CanFlush() const { return (qsize > 0) && IsSocketOpen(); } bool IsBlocked() const { return blocked; } void MarkBlocked(bool b) { blocked = b; } class MarkSocketBlocked { public: MarkSocketBlocked(USocket *_s) : s(_s) { s->MarkBlocked(true); } ~MarkSocketBlocked() { s->MarkBlocked(false); } private: USocket *s; }; #ifdef LARGE_FDSET bool IsReadable(long timeout = 0) { return self->CanRead(timeout); } bool IsWriteable(long timeout = 0) { return self->CanWrite(timeout); } #else bool IsReadable(int milisec = 0) { return self->IsOpen() && SocketSelectList(self).Select(SocketSelectList::Read, milisec); } bool IsWriteable(int milisec = 0) { return self->IsOpen() && SocketSelectList(self).Select(SocketSelectList::Write, milisec); } #endif IPSocket* Self() const { return self; } protected: virtual bool WriteData(const BYTE *, int); bool InternalWriteData(const BYTE *, int); int GetQueueSize() const { return qsize; } void QueuePacket(const BYTE* buf, int len) { queueMutex.Wait(); queue.push_back(new PBYTEArray(buf, len)); ++qsize; queueMutex.Signal(); } PBYTEArray* PopQueuedPacket() { PBYTEArray* packet = NULL; queueMutex.Wait(); if (!queue.empty()) { packet = queue.front(); queue.pop_front(); --qsize; } queueMutex.Signal(); return packet; } void ClearQueue(); virtual bool ErrorHandler(PSocket::ErrorGroup); IPSocket *self; private: USocket(); USocket(const USocket&); USocket& operator=(const USocket&); private: std::list queue; int qsize; bool blocked; PTimedMutex writeMutex, queueMutex; const char *type; }; class SocketsReader : public RegularJob { public: SocketsReader(int selectTimeout = 1000); virtual ~SocketsReader(); // override from class RegularJob virtual void Stop(); // override from class Task virtual void Exec(); protected: // the derived classes should provide new interface to add sockets void AddSocket(IPSocket *); // new virtual function // build a list of sockets for selecting // return true if the list is not empty // default behavior: put all sockets into the list virtual bool BuildSelectList(SocketSelectList &); // read data from the specified socket virtual void ReadSocket(IPSocket *) = 0; // clean up routine // default behavior: delete sockets in m_removed virtual void CleanUp(); bool SelectSockets(SocketSelectList &); typedef std::list::iterator iterator; typedef std::list::const_iterator const_iterator; // remove closed sockets void RemoveClosed(bool); private: SocketsReader(const SocketsReader&); SocketsReader& operator=(const SocketsReader&); protected: PTimeInterval m_timeout; std::list m_sockets, m_removed; // keep the size of list since list::size() is not thread-safe volatile int m_socksize; volatile int m_rmsize; mutable PReadWriteMutex m_listmutex; mutable PMutex m_rmutex; }; class ServerSocket : public TCPSocket { #ifndef LARGE_FDSET PCLASSINFO ( ServerSocket, TCPSocket ) #endif public: ServerSocket(WORD pt = 0) : TCPSocket(pt) {} virtual ~ServerSocket() {} // new virtual function // dispatch this socket to an appropriate handler virtual void Dispatch() = 0; private: ServerSocket(const ServerSocket&); ServerSocket& operator=(const ServerSocket&); }; class TCPListenSocket : public TCPSocket { #ifndef LARGE_FDSET PCLASSINFO ( TCPListenSocket, TCPSocket ) #endif public: TCPListenSocket(int seconds = 0); virtual ~TCPListenSocket(); bool IsTimeout(const PTime *) const; // new virtual function // create an appropriate socket to accept the request virtual ServerSocket *CreateAcceptor() const = 0; private: TCPListenSocket(const TCPListenSocket&); TCPListenSocket& operator=(const TCPListenSocket&); private: PTime start; }; class TCPServer : public SocketsReader { public: TCPServer(); // read config settings void LoadConfig(); // add a TCP listener void AddListener(TCPListenSocket *socket) { AddSocket(socket); } // since listeners may be closed and deleted unexpectedly, // the method provides a thread-safe way to close a listener bool CloseListener(TCPListenSocket *socket); private: TCPServer(const TCPServer&); TCPServer& operator=(const TCPServer&); // override from class SocketsReader virtual void ReadSocket(IPSocket *); virtual void CleanUp(); private: // rate limiting unsigned int cps_limit; // max cps per one sec unsigned int check_interval; // number of sec. to check if cps_limit applies std::list one_sec; std::list many_sec; }; #endif // YASOCKET_H gnugk-3.4/PaxHeaders.16356/gksql_odbc.cxx0000644000175000001440000000005012141501134016345 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gksql_odbc.cxx0000644000175000001440000005024712141501134015316 0ustar00janusers00000000000000/* * gksql_odbc.cxx * * native ODBC / unixODBC driver module for GnuGk * * Copyright (c) 2008-2013, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include "config.h" #if HAS_ODBC #include #include "gksql.h" #ifdef P_SOLARIS #ifdef P_64BIT #define SIZEOF_LONG_INT 8 #else #define SIZEOF_LONG_INT 4 #endif #endif #ifndef BOOL #define BOOL int #endif namespace nativeodbc { #include #include #include } using namespace nativeodbc; namespace { static PDynaLink g_sharedLibrary; static SQLRETURN (SQL_API *g_SQLAllocHandle)(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandle) = NULL; static SQLRETURN (SQL_API *g_SQLConnect)(SQLHDBC ConnectionHandle, SQLCHAR *ServerName, SQLSMALLINT NameLength1, SQLCHAR *UserName, SQLSMALLINT NameLength2, SQLCHAR *Authentication, SQLSMALLINT NameLength3) = NULL; static SQLRETURN (SQL_API *g_SQLDescribeCol)(SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName, SQLSMALLINT BufferLength, SQLSMALLINT *NameLength, SQLSMALLINT *DataType, SQLULEN *ColumnSize, SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable) = NULL; static SQLRETURN (SQL_API *g_SQLDisconnect)(SQLHDBC ConnectionHandle) = NULL; static SQLRETURN (SQL_API *g_SQLExecDirect)(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) = NULL; static SQLRETURN (SQL_API *g_SQLFetch)(SQLHSTMT StatementHandle) = NULL; static SQLRETURN (SQL_API *g_SQLFreeHandle)(SQLSMALLINT HandleType, SQLHANDLE Handle) = NULL; static SQLRETURN (SQL_API *g_SQLGetData)(SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLPOINTER TargetValue, SQLLEN BufferLength, SQLLEN *StrLen_or_Ind) = NULL; static SQLRETURN (SQL_API *g_SQLGetDiagRec)(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, SQLINTEGER *NativeError, SQLCHAR *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLength) = NULL; static SQLRETURN (SQL_API *g_SQLNumResultCols)(SQLHSTMT StatementHandle, SQLSMALLINT *ColumnCount) = NULL; static SQLRETURN (SQL_API *g_SQLRowCount)(SQLHSTMT StatementHandle, SQLLEN *RowCount) = NULL; static SQLRETURN (SQL_API *g_SQLSetConnectAttr)(SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength) = NULL; static SQLRETURN (SQL_API *g_SQLSetEnvAttr)(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength); static SQLRETURN (SQL_API *g_SQLSetStmtAttr)(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength) = NULL; PString GetODBCDiagMsg( SQLRETURN result, SQLSMALLINT handleType, SQLHANDLE handle ) { if (result == SQL_SUCCESS) return "Sucesss"; if (result == SQL_INVALID_HANDLE) return "Invalid ODBC handle passed"; if (result == SQL_NO_DATA) return "No more data to fetch"; SQLCHAR sqlState[6]; SQLINTEGER nativeError = 0; SQLCHAR *msg = new SQLCHAR[SQL_MAX_MESSAGE_LENGTH]; SQLSMALLINT msgSize; SQLRETURN r; memset(msg, 0, SQL_MAX_MESSAGE_LENGTH); r = (*g_SQLGetDiagRec)(handleType, handle, 1, reinterpret_cast(sqlState), &nativeError, msg, SQL_MAX_MESSAGE_LENGTH, &msgSize ); if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) { sqlState[5] = 0; PString text(reinterpret_cast(sqlState)); text += " ("; text += PString(nativeError); text += ") "; text += reinterpret_cast(msg); delete[] msg; return text; } else { delete[] msg; return "Could not retrieve a diagnostic message"; } } } /** Class that encapsulates SQL query result for the ODBC backend. It does not provide any multithread safety, so should be accessed from a single thread at time. */ class GkODBCResult : public GkSQLResult { public: /// Build the result from SELECT type query GkODBCResult( /// number of rows affected by the query long numRowsAffected, /// number of columns in the result set long numFields, /// query result std::list * resultRows ); /// Build the empty result and store query execution error information GkODBCResult( /// error code unsigned int errorCode, /// error message text const char* errorMsg ); virtual ~GkODBCResult(); /** @return Backend specific error message, if the query failed. */ virtual PString GetErrorMessage(); /** @return Backend specific error code, if the query failed. */ virtual long GetErrorCode(); /** Fetch a single row from the result set. After each row is fetched, cursor position is moved to a next row. @return True if the row has been fetched, false if no more rows are available. */ virtual bool FetchRow( /// array to be filled with string representations of the row fields PStringArray& result ); virtual bool FetchRow( /// array to be filled with string representations of the row fields ResultRow& result ); private: GkODBCResult(); GkODBCResult(const GkODBCResult&); GkODBCResult& operator=(const GkODBCResult&); protected: /// query result for SELECT type queries std::list *m_sqlResult; /// iterator to the most recent row returned by fetch operation std::list::iterator m_sqlRowIter; /// the most recent row returned by fetch operation int m_sqlRow; /// error code (if the query failed) unsigned int m_errorCode; /// error message text (if the query failed) PString m_errorMessage; }; /// ODBC backend connection implementation. class GkODBCConnection : public GkSQLConnection { public: /// Build a new connection object GkODBCConnection( /// name to use in the log const char* name = "ODBC" ); virtual ~GkODBCConnection(); protected: class GkODBCConnWrapper : public GkSQLConnection::SQLConnWrapper { public: GkODBCConnWrapper( /// unique identifier for this connection int id, /// ODBC connection handle SQLHDBC conn, /// DSN const PString &dsn ) : SQLConnWrapper(id, dsn), m_conn(conn) {} virtual ~GkODBCConnWrapper(); private: GkODBCConnWrapper(); GkODBCConnWrapper(const GkODBCConnWrapper&); GkODBCConnWrapper& operator=(const GkODBCConnWrapper&); public: SQLHDBC m_conn; }; /** Create a new SQL connection using parameters stored in this object. When the connection is to be closed, the object is simply deleted using delete operator. @return NULL if database connection could not be established or an object of GkODBCConnWrapper class. */ virtual SQLConnPtr CreateNewConnection( /// unique identifier for this connection int id ); /** Execute the query using specified SQL connection. @return Query execution result. */ virtual GkSQLResult* ExecuteQuery( /// SQL connection to use for query execution SQLConnPtr conn, /// query string const char* queryStr, /// maximum time (ms) for the query execution, -1 means infinite long timeout = -1 ); /** Escape any special characters in the string, so it can be used in a SQL query. @return Escaped string. */ virtual PString EscapeString( /// SQL connection to get escaping parameters from SQLConnPtr conn, /// string to be escaped const char* str ); private: GkODBCConnection(const GkODBCConnection&); GkODBCConnection& operator=(const GkODBCConnection&); private: SQLHENV m_env; }; GkODBCResult::GkODBCResult( /// number of rows affected by the query long numRowsAffected, /// number of columns in the result set long numFields, /// query result std::list *selectResult ) : GkSQLResult(false), m_sqlResult(selectResult), m_sqlRow(-1), m_errorCode(0) { m_numRows = numRowsAffected; m_numFields = numFields; if (m_sqlResult != NULL && !m_sqlResult->empty()) m_numRows = m_sqlResult->size(); m_queryError = false; } GkODBCResult::GkODBCResult( /// error code unsigned int errorCode, /// error message text const char* errorMsg ) : GkSQLResult(true), m_sqlResult(NULL), m_sqlRow(-1), m_errorCode(errorCode), m_errorMessage(errorMsg) { m_queryError = true; } GkODBCResult::~GkODBCResult() { if (m_sqlResult != NULL) { std::list::iterator i = m_sqlResult->begin(); while (i != m_sqlResult->end()) { delete *i; ++i; } delete m_sqlResult; m_sqlResult = NULL; } } PString GkODBCResult::GetErrorMessage() { return m_errorMessage; } long GkODBCResult::GetErrorCode() { return m_errorCode; } bool GkODBCResult::FetchRow( /// array to be filled with string representations of the row fields PStringArray& result ) { if (m_sqlResult == NULL || m_numRows <= 0) return false; if (m_sqlRow < 0) { m_sqlRow = 0; m_sqlRowIter = m_sqlResult->begin(); } if (m_sqlRow >= m_numRows) return false; result.SetSize(m_numFields); for (int i = 0; i < m_numFields; ++i) result[i] = (**m_sqlRowIter)[i].first; ++m_sqlRow; ++m_sqlRowIter; return true; } bool GkODBCResult::FetchRow( /// array to be filled with string representations of the row fields ResultRow& result ) { if (m_sqlResult == NULL || m_numRows <= 0) return false; if (m_sqlRow < 0) { m_sqlRow = 0; m_sqlRowIter = m_sqlResult->begin(); } if (m_sqlRow >= m_numRows) return false; result = *(*m_sqlRowIter); ++m_sqlRow; ++m_sqlRowIter; return true; } GkODBCConnection::GkODBCConnection( /// name to use in the log const char* name ) : GkSQLConnection(name), m_env(SQL_NULL_HENV) { } GkODBCConnection::~GkODBCConnection() { if (m_env != SQL_NULL_HENV) { (*g_SQLFreeHandle)(SQL_HANDLE_ENV, m_env); m_env = SQL_NULL_HENV; } } GkODBCConnection::GkODBCConnWrapper::~GkODBCConnWrapper() { if (m_conn != SQL_NULL_HDBC) { SQLRETURN r = (*g_SQLDisconnect)(m_conn); if (!SQL_SUCCEEDED(r)) { PTRACE(1, "ODBC disconnect failed: " << GetODBCDiagMsg(r, SQL_HANDLE_DBC, m_conn)); SNMP_TRAP(5, SNMPError, Database, "ODBC disconnection failed"); } (*g_SQLFreeHandle)(SQL_HANDLE_DBC, m_conn); m_conn = SQL_NULL_HDBC; } } GkSQLConnection::SQLConnPtr GkODBCConnection::CreateNewConnection( /// unique identifier for this connection int id ) { if (!g_sharedLibrary.IsLoaded()) { if (m_library.IsEmpty()) { #ifdef _WIN32 m_library = "odbc32" + g_sharedLibrary.GetExtension(); #else m_library = "libodbc" + g_sharedLibrary.GetExtension(); #endif } if (!g_sharedLibrary.Open(m_library)) { PTRACE (1, GetName() << "\tCan't load library " << m_library); return NULL; } if (!g_sharedLibrary.GetFunction("SQLAllocHandle", (PDynaLink::Function &)g_SQLAllocHandle) || !g_sharedLibrary.GetFunction("SQLConnect", (PDynaLink::Function &)g_SQLConnect) || !g_sharedLibrary.GetFunction("SQLDescribeCol", (PDynaLink::Function &)g_SQLDescribeCol) || !g_sharedLibrary.GetFunction("SQLDisconnect", (PDynaLink::Function &)g_SQLDisconnect) || !g_sharedLibrary.GetFunction("SQLExecDirect", (PDynaLink::Function &)g_SQLExecDirect) || !g_sharedLibrary.GetFunction("SQLFetch", (PDynaLink::Function &)g_SQLFetch) || !g_sharedLibrary.GetFunction("SQLFreeHandle", (PDynaLink::Function &)g_SQLFreeHandle) || !g_sharedLibrary.GetFunction("SQLGetData", (PDynaLink::Function &)g_SQLGetData) || !g_sharedLibrary.GetFunction("SQLGetDiagRec", (PDynaLink::Function &)g_SQLGetDiagRec) || !g_sharedLibrary.GetFunction("SQLNumResultCols", (PDynaLink::Function &)g_SQLNumResultCols) || !g_sharedLibrary.GetFunction("SQLRowCount", (PDynaLink::Function &)g_SQLRowCount) || !g_sharedLibrary.GetFunction("SQLSetConnectAttr", (PDynaLink::Function &)g_SQLSetConnectAttr) || !g_sharedLibrary.GetFunction("SQLSetEnvAttr", (PDynaLink::Function &)g_SQLSetEnvAttr) || !g_sharedLibrary.GetFunction("SQLSetStmtAttr", (PDynaLink::Function &)g_SQLSetStmtAttr) ) { #ifdef hasDynaLinkGetLastError PTRACE (1, GetName() << "\tFailed to load shared database library: " << g_sharedLibrary.GetLastError()); #else PTRACE (1, GetName() << "\tFailed to load shared database library: unknown error"); #endif g_sharedLibrary.Close(); SNMP_TRAP(5, SNMPError, Database, GetName() + " DLL load error"); return NULL; } // allocate Environment handle and register version SQLRETURN r = (*g_SQLAllocHandle)(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_env); if (!SQL_SUCCEEDED(r)) { PTRACE(1, GetName() << "\tFailed to allocate an ODBC environment handle: " << GetODBCDiagMsg(r, SQL_HANDLE_ENV, SQL_NULL_HENV)); SNMP_TRAP(5, SNMPError, Database, GetName() + " connection failed"); return NULL; } r = (*g_SQLSetEnvAttr)(m_env, SQL_ATTR_ODBC_VERSION, reinterpret_cast(SQL_OV_ODBC3), 0); if (!SQL_SUCCEEDED(r)) { PTRACE(1, GetName() << "\tFailed to request ODBC interface version 3.0: " << GetODBCDiagMsg(r, SQL_HANDLE_ENV, m_env)); (*g_SQLFreeHandle)(SQL_HANDLE_ENV, m_env); m_env = SQL_NULL_HENV; SNMP_TRAP(5, SNMPError, Database, GetName() + " connection failed"); return NULL; } PTRACE(5, GetName() << "\tODBC environment created"); } SQLHDBC conn = SQL_NULL_HDBC; // allocate connection handle, set timeout SQLRETURN r = (*g_SQLAllocHandle)(SQL_HANDLE_DBC, m_env, &conn); if (!SQL_SUCCEEDED(r)) { PTRACE(1, GetName() << "\tFailed to allocate an ODBC connection handle: " << GetODBCDiagMsg(r, SQL_HANDLE_ENV, m_env)); SNMP_TRAP(5, SNMPError, Database, GetName() + " connection failed"); return NULL; } r = (g_SQLSetConnectAttr)(conn, SQL_ATTR_LOGIN_TIMEOUT, reinterpret_cast(10), 0); if (!SQL_SUCCEEDED(r)) { PTRACE(1, GetName() << "\tFailed to set ODBC connection login timeout: " << GetODBCDiagMsg(r, SQL_HANDLE_DBC, conn)); SNMP_TRAP(5, SNMPError, Database, GetName() + " connection failed"); } r = (*g_SQLSetConnectAttr)(conn, SQL_ATTR_CONNECTION_TIMEOUT, reinterpret_cast(10), 0); if (!SQL_SUCCEEDED(r)) { PTRACE(1, GetName() << "\tFailed to set ODBC connection request timeout: " << GetODBCDiagMsg(r, SQL_HANDLE_DBC, conn)); SNMP_TRAP(5, SNMPError, Database, GetName() + " connection failed"); } // Connect to datasource r = (*g_SQLConnect)(conn, reinterpret_cast(const_cast(m_database.IsEmpty() ? "" : (const char*)m_database)), SQL_NTS, reinterpret_cast(const_cast(m_username.IsEmpty() ? "" : (const char*)m_username)), SQL_NTS, reinterpret_cast(const_cast(m_password.IsEmpty() ? "" : (const char*)m_password)), SQL_NTS ); if (!SQL_SUCCEEDED(r)) { PTRACE(1, GetName() << "\tODBC connect to " << m_database << " failed: " << GetODBCDiagMsg(r, SQL_HANDLE_DBC, conn)); (*g_SQLFreeHandle)(SQL_HANDLE_DBC, conn); SNMP_TRAP(5, SNMPError, Database, GetName() + " connection failed"); return NULL; } PTRACE(5, GetName() << "\tODBC connection to " << m_database << " established successfully"); return new GkODBCConnWrapper(id, conn, m_database); } GkSQLResult* GkODBCConnection::ExecuteQuery( /// SQL connection to use for query execution GkSQLConnection::SQLConnPtr con, /// query string const char* queryStr, /// maximum time (ms) for the query execution, -1 means infinite long timeout ) { SQLHDBC conn = ((GkODBCConnWrapper*)con)->m_conn; SQLHSTMT stmt = SQL_NULL_HSTMT; SQLRETURN r = (*g_SQLAllocHandle)(SQL_HANDLE_STMT, conn, &stmt); if (!SQL_SUCCEEDED(r)) { PString errmsg(GetODBCDiagMsg(r, SQL_HANDLE_DBC, conn)); PTRACE(1, GetName() << "\tFailed to allocate an ODBC statement handle: " << errmsg); SNMP_TRAP(5, SNMPError, Database, GetName() + " connection failed"); Disconnect(); return new GkODBCResult(r, errmsg); } r = (*g_SQLSetStmtAttr)(stmt, SQL_ATTR_QUERY_TIMEOUT, reinterpret_cast(timeout == -1 ? 10 : ((timeout + 999) / 1000)), 0); if (!SQL_SUCCEEDED(r)) { PTRACE(1, GetName() << "\tSQL query timeout not set: " << GetODBCDiagMsg(r, SQL_HANDLE_STMT, stmt)); } r = (*g_SQLExecDirect)(stmt, reinterpret_cast(const_cast((const char*)queryStr)), SQL_NTS); bool nodata = (r == SQL_NO_DATA); if (r != SQL_NO_DATA && !SQL_SUCCEEDED(r)) { PString errmsg(GetODBCDiagMsg(r, SQL_HANDLE_STMT, stmt)); PTRACE(1, GetName() << "\tFailed to execute an ODBC query: " << errmsg); (*g_SQLFreeHandle)(SQL_HANDLE_STMT, stmt); SNMP_TRAP(5, SNMPError, Database, GetName() + " connection failed"); Disconnect(); return new GkODBCResult(r, errmsg + ", query: " + queryStr); } SQLSMALLINT columns = 0; r = (*g_SQLNumResultCols)(stmt, &columns); if (!SQL_SUCCEEDED(r)) { PString errmsg(GetODBCDiagMsg(r, SQL_HANDLE_STMT, stmt)); PTRACE(1, GetName() << "\tFailed to get ODBC number of result columns: " << errmsg); (*g_SQLFreeHandle)(SQL_HANDLE_STMT, stmt); SNMP_TRAP(5, SNMPError, Database, GetName() + " query failed"); Disconnect(); return new GkODBCResult(r, errmsg + ", query: " + queryStr); } SQLLEN rows = 0; if (columns == 0) { if (nodata) { (*g_SQLFreeHandle)(SQL_HANDLE_STMT, stmt); return new GkODBCResult(0, 0, NULL); } else { r = (*g_SQLRowCount)(stmt, &rows); if (!SQL_SUCCEEDED(r)) { PTRACE(1, GetName() << "\tFailed to get ODBC number of rows affected by a query: " << GetODBCDiagMsg(r, SQL_HANDLE_STMT, stmt)); SNMP_TRAP(5, SNMPError, Database, GetName() + " query failed") } (*g_SQLFreeHandle)(SQL_HANDLE_STMT, stmt); return new GkODBCResult(rows, 0, NULL); } } if (nodata) return new GkODBCResult(0, columns, NULL); std::vector fieldNames(columns); for (SQLUSMALLINT i = 1; i <= columns; ++i) { SQLCHAR colname[64]; r = (*g_SQLDescribeCol)(stmt, i, colname, sizeof(colname), NULL, NULL, NULL, NULL, NULL); if (!SQL_SUCCEEDED(r)) { PTRACE(1, GetName() << "\tFailed to get an ODBC result set column #" << i << " name: " << GetODBCDiagMsg(r, SQL_HANDLE_STMT, stmt)); SNMP_TRAP(5, SNMPError, Database, GetName() + " query failed") } else fieldNames[i-1] = PString(reinterpret_cast(colname)); } std::list *resultRows = new std::list(); do { r = (*g_SQLFetch)(stmt); if (r == SQL_NO_DATA) break; if (!SQL_SUCCEEDED(r)) { PTRACE(1, GetName() << "\tFailed to fetch an ODBC result row: " << GetODBCDiagMsg(r, SQL_HANDLE_STMT, stmt)); SNMP_TRAP(5, SNMPError, Database, GetName() + " query failed") // we should return an error instead break; } GkSQLResult::ResultRow * row = new GkSQLResult::ResultRow(columns); for (SQLUSMALLINT i = 1; i <= columns; ++i) { SQLLEN indicator; char data[512]; /* retrieve column data as a string */ SQLRETURN result = (*g_SQLGetData)(stmt, i, SQL_C_CHAR, data, sizeof(data), &indicator); if (SQL_SUCCEEDED(result)) { /* Handle null columns */ if (indicator == SQL_NULL_DATA) data[0] = 0; } else { PTRACE(1, GetName() << "\tFailed to get ODBC result set column #" << i << " data: " << GetODBCDiagMsg(r, SQL_HANDLE_STMT, stmt)); SNMP_TRAP(5, SNMPError, Database, GetName() + " query failed") data[0] = 0; } (*row)[i-1] = pair(data, fieldNames[i-1]); } resultRows->push_back(row); ++rows; } while (SQL_SUCCEEDED(r)); (*g_SQLFreeHandle)(SQL_HANDLE_STMT, stmt); return new GkODBCResult(rows, columns, resultRows); } PString GkODBCConnection::EscapeString( /// SQL connection to get escaping parameters from SQLConnPtr /*conn*/, /// string to be escaped const char* str ) { PString s(str); // remove all special characters 0x00-0x1f, except TAB PINDEX i = 0; while (i < s.GetLength()) { if (s.Mid(i, 1) < PString(' ') && s.Mid(i, 1) != PString('\t')) { s.Delete(i, 1); } else { ++i; } } s.Replace("'", "''", TRUE); return s; } namespace { GkSQLCreator ODBCCreator("ODBC"); } #endif /* HAS_ODBC */ gnugk-3.4/PaxHeaders.16356/ipauth.cxx0000644000175000001440000000005012177173353015550 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/ipauth.cxx0000644000175000001440000002246012177173353014515 0ustar00janusers00000000000000/* * ipauth.cxx * * IP based authentication modules * * @(#) $Id: ipauth.cxx,v 1.15 2013/08/03 12:26:19 willamowius Exp $ * * Copyright (c) 2005, Michal Zygmuntowicz * Copyright (c) 2006-2013, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include #include #include "gk_const.h" #include "h323util.h" #include "stl_supp.h" #include "RasPDU.h" #include "RasTbl.h" #include "sigmsg.h" #include "ipauth.h" class IPAuthPrefix { public: IPAuthPrefix(); IPAuthPrefix(bool a, const PString &, bool tls = false); IPAuthPrefix(const IPAuthPrefix &); void AddPrefix(const PString &); void SortPrefix(bool greater = true); int PrefixMatch(const PString &) const; std::string PrintOn() const; std::string PrintPrefix() const; IPAuthPrefix& operator=(const IPAuthPrefix&); IPAuthPrefix& operator=(bool); typedef std::vector::iterator prefix_iterator; typedef std::vector::const_iterator const_prefix_iterator; bool auth; bool onlyTLS; protected: std::vector Prefixs; }; /// Text file based IP authentication class FileIPAuth : public IPAuthBase { public: typedef std::pair IPAuthEntry; /// Create text file based authenticator FileIPAuth( /// authenticator name from Gatekeeper::Auth section const char * authName ); /// Destroy the authenticator virtual ~FileIPAuth(); protected: /// Overriden from IPAuthBase virtual int CheckAddress( const PIPSocket::Address & addr, /// IP address the request comes from WORD port, /// port number the request comes from const PString & number, bool overTLS); private: FileIPAuth(); /* No copy constructor allowed */ FileIPAuth(const FileIPAuth &); /* No operator= allowed */ FileIPAuth& operator=(const FileIPAuth &); private: typedef std::vector IPAuthList; IPAuthList m_authList; }; IPAuthBase::IPAuthBase( /// authenticator name from Gatekeeper::Auth section const char * authName, /// bitmask with supported RAS checks unsigned supportedRasChecks, /// bitmask with supported non-RAS checks unsigned supportedMiscChecks ) : GkAuthenticator(authName, supportedRasChecks, supportedMiscChecks) { } IPAuthBase::~IPAuthBase() { } int IPAuthBase::Check( /// GRQ RAS message to be authenticated RasPDU & grqPdu, /// gatekeeper request reject reason unsigned & /*rejectReason*/ ) { return CheckAddress(grqPdu->m_peerAddr, grqPdu->m_peerPort, PString::Empty()); } int IPAuthBase::Check( /// RRQ RAS message to be authenticated RasPDU & rrqPdu, /// authorization data (reject reason, ...) RRQAuthData & /*authData*/ ) { return CheckAddress(rrqPdu->m_peerAddr, rrqPdu->m_peerPort, PString::Empty()); } int IPAuthBase::Check( /// LRQ nessage to be authenticated RasPDU & lrqPdu, /// location request reject reason unsigned & /*rejectReason*/ ) { return CheckAddress(lrqPdu->m_peerAddr, lrqPdu->m_peerPort, PString::Empty()); } int IPAuthBase::Check( /// Q.931/H.225 Setup message to be authenticated SetupMsg & setup, /// authorization data (call duration limit, reject reason, ...) SetupAuthData & authData ) { PIPSocket::Address addr; WORD port = 0; setup.GetPeerAddr(addr, port); PString number; setup.GetQ931().GetCalledPartyNumber(number); return CheckAddress(addr, port, number, authData.m_overTLS); } ///////////// // FileIPAuth ///////////// namespace { const char *FileIPAuthSecName = "FileIPAuth"; struct IPAuthEntry_greater : public binary_function { bool operator()( const FileIPAuth::IPAuthEntry & a, const FileIPAuth::IPAuthEntry & b ) const { const int diff = a.first.Compare(b.first); if (diff == 0) return !a.second.auth; return diff > 0; } }; } /* anonymous namespace */ FileIPAuth::FileIPAuth( /// authenticator name from Gatekeeper::Auth section const char * authName ) : IPAuthBase(authName) { bool dynamicCfg = false; PConfig *cfg = GkConfig(); if (cfg->HasKey(FileIPAuthSecName, "include")) { const PFilePath fp(cfg->GetString(FileIPAuthSecName, "include", "")); if (!PFile::Exists(fp)) { PTRACE(1, GetName() << "\tCould not read the include file '" << fp << "': the file does not exist"); return; } cfg = new PConfig(fp, authName); dynamicCfg = true; } PStringToString kv = cfg->GetAllKeyValues(FileIPAuthSecName); m_authList.reserve(kv.GetSize()); for (PINDEX i = 0; i < kv.GetSize(); i++) { const PString &key = kv.GetKeyAt(i); int position = 0; if (key[0] == '#') continue; IPAuthEntry entry; entry.first = (key == "*" || key == "any") ? NetworkAddress() : NetworkAddress(key); PString auth(kv.GetDataAt(i)); PString prefix("."); if ((position = auth.Find(';', position)) != P_MAX_INDEX) { position++; prefix = auth(position, auth.GetLength()); position--; auth.Delete(position, auth.GetLength() - position); } entry.second.auth = PCaselessString("allow") == auth; entry.second.AddPrefix(prefix); entry.second.SortPrefix(false); if (PCaselessString("onlyTLS") == auth) { entry.second.auth = true; entry.second.onlyTLS = true; } m_authList.push_back(entry); } std::stable_sort(m_authList.begin(), m_authList.end(), IPAuthEntry_greater()); PTRACE(5, GetName() << "\t" << m_authList.size() << " entries loaded"); if (PTrace::CanTrace(6)) { ostream &strm = PTrace::Begin(6, __FILE__, __LINE__); strm << GetName() << " entries:\n"; IPAuthList::const_iterator entry = m_authList.begin(); while (entry != m_authList.end()) { strm << "\t" << entry->first.AsString() << " = " << (entry->second.auth ? "allow" : "reject") << (entry->second.auth ? entry->second.PrintOn() : "") << endl; entry++; } PTrace::End(strm); } if (dynamicCfg) { delete cfg; cfg = NULL; } } FileIPAuth::~FileIPAuth() { } int FileIPAuth::CheckAddress( const PIPSocket::Address & addr, /// IP address the request comes from WORD /*port*/, /// port number the request comes from const PString & number, bool overTLS) { IPAuthList::const_iterator entry = m_authList.begin(); while (entry != m_authList.end()) { if (entry->first.IsAny() || addr << entry->first) { if (entry->second.onlyTLS && !overTLS) { PTRACE(5, GetName() << "\tIP " << addr.AsString() << " rejected (no TLS)"); return e_fail; } if (entry->second.auth && !number.IsEmpty()) { int len = entry->second.PrefixMatch(number); PTRACE(5, GetName() << "\tIP " << addr.AsString() << (len ? " accepted" : " rejected") << " for Called " << number); return len ? e_ok : e_fail; } return entry->second.auth ? e_ok : e_fail; } entry++; } return GetDefaultStatus(); } IPAuthPrefix::IPAuthPrefix() : auth(false), onlyTLS(false) { } IPAuthPrefix::IPAuthPrefix(bool a, const PString & prefixes, bool tls) { auth = a; AddPrefix(prefixes); onlyTLS = tls; } IPAuthPrefix::IPAuthPrefix(const IPAuthPrefix & obj) { auth = obj.auth; onlyTLS = obj.onlyTLS; const_prefix_iterator Iter = obj.Prefixs.begin(); const_prefix_iterator eIter = obj.Prefixs.end(); while (Iter != eIter) { Prefixs.push_back(Iter->c_str()); ++Iter; } } IPAuthPrefix& IPAuthPrefix::operator=(const IPAuthPrefix & obj) { if (this != &obj) { auth = obj.auth; onlyTLS = obj.onlyTLS; Prefixs.clear(); const_prefix_iterator Iter = obj.Prefixs.begin(); const_prefix_iterator eIter = obj.Prefixs.end(); while (Iter != eIter) { Prefixs.push_back(Iter->c_str()); ++Iter; } } return *this; } IPAuthPrefix& IPAuthPrefix::operator=(bool a) { auth = a; return *this; } void IPAuthPrefix::AddPrefix(const PString & prefixes) { PStringArray p(prefixes.Tokenise(" ,;\t\n", false)); for (PINDEX i = 0; i < p.GetSize(); ++i) Prefixs.push_back((const char *)p[i]); } void IPAuthPrefix::SortPrefix(bool greater) { // remove duplicate aliases if (greater) sort(Prefixs.begin(), Prefixs.end(), str_prefix_greater()); else sort(Prefixs.begin(), Prefixs.end(), str_prefix_lesser()); prefix_iterator Iter = std::unique(Prefixs.begin(), Prefixs.end()); Prefixs.erase(Iter, Prefixs.end()); } int IPAuthPrefix::PrefixMatch(const PString & number) const { if (number.IsEmpty()) return 0; const char * alias = (const char*)(number); if (!alias) return 0; const_prefix_iterator Iter = Prefixs.begin(); const_prefix_iterator eIter = Prefixs.end(); if (Iter == eIter) return 1; while (Iter != eIter) { const int len = MatchPrefix(alias, Iter->c_str()); if (len > 0) { return len; } ++Iter; } return 0; } std::string IPAuthPrefix::PrintOn() const { if (!auth) return std::string(" to called any"); std::string prefix = PrintPrefix(); if (prefix == ".") prefix = "any"; std::string ret(" to called "); ret += prefix; if (onlyTLS) ret += " (only TLS)"; return ret; } std::string IPAuthPrefix::PrintPrefix() const { std::string prefix; const_prefix_iterator Iter = Prefixs.begin(); const_prefix_iterator eIter = Prefixs.end(); while (Iter != eIter) { prefix += *Iter; prefix += ","; ++Iter; } return prefix; } namespace { // anonymous namespace GkAuthCreator FileIPAuthCreator("FileIPAuth"); } // end of anonymous namespace gnugk-3.4/PaxHeaders.16356/syslogacct.cxx0000644000175000001440000000005012016656506016427 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/syslogacct.cxx0000644000175000001440000001122312016656506015367 0ustar00janusers00000000000000/* * syslogacct.cxx * * accounting module for GNU Gatekeeper for the syslog. * * Copyright (c) 2006-2010, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #ifndef _WIN32 #include #include #include #include "GkStatus.h" #include "syslogacct.h" #include "Toolkit.h" const char* const SyslogSec = "SyslogAcct"; static int syslog_level = LOG_INFO; static int syslog_facility = LOG_USER; SyslogAcct::SyslogAcct( const char* moduleName, const char* cfgSecName ) : GkAcctLogger(moduleName, cfgSecName) { // it is very important to set what type of accounting events // are supported for each accounting module, otherwise the Log method // will no get called SetSupportedEvents(SyslogAcctEvents); PConfig* cfg = GetConfig(); const PString& cfgSec = GetConfigSectionName(); m_timestampFormat = cfg->GetString(cfgSec, "TimestampFormat", ""); m_startEvent = cfg->GetString(cfgSec, "StartEvent", "CALL|Start|%{caller-ip}:%{caller-port}|%{callee-ip}:%{callee-port}|%{CallId}"); m_stopEvent = cfg->GetString(cfgSec, "StopEvent", "CALL|Stop|%{caller-ip}:%{caller-port}|%{callee-ip}:%{callee-port}|%{CallId}"); m_updateEvent = cfg->GetString(cfgSec, "UpdateEvent", "CALL|Update|%{caller-ip}:%{caller-port}|%{callee-ip}:%{callee-port}|%{CallId}"); m_connectEvent = cfg->GetString(cfgSec, "ConnectEvent", "CALL|Connect|%{caller-ip}:%{caller-port}|%{callee-ip}:%{callee-port}|%{CallId}"); } SyslogAcct::~SyslogAcct() { } GkAcctLogger::Status SyslogAcct::Log( GkAcctLogger::AcctEvent evt, const callptr& call ) { // a workaround to prevent processing end on "sufficient" module // if it is not interested in this event type if ((evt & GetEnabledEvents() & GetSupportedEvents()) == 0) return Next; if (!call) { PTRACE(1, "SYSLOGACCT\t" << GetName() << " - missing call info for event " << evt); return Fail; } PString sysloglevelconfig = GkConfig()->GetString(SyslogSec, "SyslogLevel", "LOG_INFO"); PString syslogfacilityconfig = GkConfig()->GetString(SyslogSec, "SyslogFacility", "LOG_USER"); if (sysloglevelconfig == "LOG_EMERG") { syslog_level = LOG_EMERG; } else if (sysloglevelconfig == "LOG_ALERT") { syslog_level = LOG_ALERT; } else if (sysloglevelconfig == "LOG_CRIT") { syslog_level = LOG_CRIT; } else if (sysloglevelconfig == "LOG_ERR") { syslog_level = LOG_ERR; } else if (sysloglevelconfig == "LOG_WARNING") { syslog_level = LOG_WARNING; } else if (sysloglevelconfig == "LOG_NOTICE") { syslog_level = LOG_NOTICE; } else if (sysloglevelconfig == "LOG_INFO") { syslog_level = LOG_INFO; } else if (sysloglevelconfig == "LOG_DEBUG") { syslog_level = LOG_DEBUG; } else { syslog_level = LOG_INFO; } if (syslogfacilityconfig == "LOG_DAEMON") { syslog_facility = LOG_DAEMON; } else if (syslogfacilityconfig == "LOG_USER") { syslog_facility = LOG_USER; } else if (syslogfacilityconfig == "LOG_AUTH") { syslog_facility = LOG_AUTH; } else if (syslogfacilityconfig == "LOG_LOCAL0") { syslog_facility = LOG_LOCAL0; } else if (syslogfacilityconfig == "LOG_LOCAL1") { syslog_facility = LOG_LOCAL1; } else if (syslogfacilityconfig == "LOG_LOCAL2") { syslog_facility = LOG_LOCAL2; } else if (syslogfacilityconfig == "LOG_LOCAL3") { syslog_facility = LOG_LOCAL3; } else if (syslogfacilityconfig == "LOG_LOCAL4") { syslog_facility = LOG_LOCAL4; } else if (syslogfacilityconfig == "LOG_LOCAL5") { syslog_facility = LOG_LOCAL5; } else if (syslogfacilityconfig == "LOG_LOCAL6") { syslog_facility = LOG_LOCAL6; } else if (syslogfacilityconfig == "LOG_LOCAL7") { syslog_facility = LOG_LOCAL7; } else { syslog_facility = LOG_USER; } PString eventTmpl; if (evt == AcctStart) { eventTmpl = m_startEvent; } else if (evt == AcctConnect) { eventTmpl = m_connectEvent; } else if (evt == AcctUpdate) { eventTmpl = m_updateEvent; } else if (evt == AcctStop) { eventTmpl = m_stopEvent; } if (!eventTmpl.IsEmpty()) { // don't send event if the template string is empty std::map params; SetupAcctParams(params, call, m_timestampFormat); PString msg = ReplaceAcctParams(eventTmpl, params); openlog("GnuGk", LOG_PID, syslog_facility); syslog(syslog_facility | syslog_level, "%s", (const char *)msg); closelog(); } return Ok; } PString SyslogAcct::EscapeAcctParam(const PString& param) const { return "\"" + param + "\""; // test: quote } namespace { // append syslog accounting logger to the global list of loggers GkAcctLoggerCreator SyslogAcctCreator("SyslogAcct"); } #endif // not _WIN32 gnugk-3.4/PaxHeaders.16356/radproto.h0000644000175000001440000000005012006747667015543 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/radproto.h0000644000175000001440000012007112006747667014505 0ustar00janusers00000000000000/* * radproto.h * * RADIUS protocol client classes that offer good performance, * scalability and multithreading access. * * Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz * Copyright (c) 2003-2011, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #if HAS_RADIUS #ifndef __RADPROTO_H #define __RADPROTO_H "@(#) $Id: radproto.h,v 1.31 2012/08/03 13:18:47 willamowius Exp $" #include #include #include "config.h" class PRandom; class PMessageDigest5; /// Encapsulates RADIUS Attribute structure. class RadiusAttr { public: friend class RadiusPDU; enum Constants { /// max length of the attribute raw data MaxLength = 255, /// length of the attribute fixed header (Type+Length) FixedHeaderLength = 2, /// max length of the Value field MaxValueLength = (MaxLength - FixedHeaderLength), /// length of the fixed header for VSA /// (standard header + VendorId field) VsaFixedHeaderLength = FixedHeaderLength + 4, /// max length of the VSA Value field VsaMaxValueLength = (MaxLength - VsaFixedHeaderLength), /// length of the fixed header for RFC2865 conformant VSA /// (standard header + VendorId, VendorType and VendorLength fields) VsaRfc2865FixedHeaderLength = VsaFixedHeaderLength + 2, /// max length of the VSA RFC2865 conformant Value field VsaMaxRfc2865ValueLength = (MaxLength - VsaRfc2865FixedHeaderLength) }; /// Constants for RADIUS Attribute Type field enum AttrTypes { Invalid = 0, UserName, UserPassword, ChapPassword, NasIpAddress = 4, NasPort, ServiceType, FramedProtocol, FramedIpAddress = 8, FramedIpNetmask, FramedRouting, FilterId, FramedMtu = 12, FramedCompression, LoginIpHost, LoginService, LoginTcpPort = 16, OldPassword, ReplyMessage, CallbackNumber, CallbackId = 20, Expiration, FramedRoute, FramedIpxNet, State = 24, AttrTypeClass, VendorSpecific, SessionTimeout, IdleTimeout = 28, TerminationAction, CalledStationId, CallingStationId, NasIdentifier = 32, ProxyState, LoginLatService, LoginLatNode, LoginLatGroup = 36, FramedAppleTalkLink, FramedAppleTalkNetwork, FramedAppleTalkZone, AcctStatusType = 40, AcctDelayTime, AcctInputOctets, AcctOutputOctets, AcctSessionId = 44, AcctAuthentic, AcctSessionTime, AcctInputPackets, AcctOutputPackets = 48, AcctTerminateCause, AcctMultiSessionId, AcctLinkCount, AcctInputGigawords = 52, AcctOutputGigawords, EventTimestamp = 55, ChapChallenge = 60, NasPortType, PortLimit, LoginLatPort, TunnelType = 64, TunnelMediumType, TunnelClientEndpoint, TunnelServerEndpoint, AcctTunnelConnectionId = 68, TunnelPassword, PasswordRetry = 75, Prompt = 76, ConnectInfo, ConfigurationToken, AcctInterimInterval = 85, AcctTunnelPacketsLost, NasPortId, // RFC 3162 NasIpv6Address = 95, FramedInterfaceId, FramedIpv6Prefix, LoginIpv6Host = 98, FramedIpv6Route, FramedIpv6Pool }; /// Constants for Service-Type attribute values enum ServiceTypes { ST_Login = 1, ST_Framed, ST_CallbackLogin, ST_CallbackFramed = 4, ST_Outbound, ST_Administrative, ST_NasPrompt = 7, ST_AuthenticateOnly = 8, ST_CallbackNasPrompt, ST_CallCheck, ST_CallbackAdministrative = 9 }; /// Constants for Framed-Protocol attribute values enum FramedProtocols { FP_Ppp = 1, FP_Slip }; /// Constants for Framed-Compression attribute values enum FramedCompressionTypes { FC_None = 0, FC_VJTcpIp, FC_Ipx, FC_StacLZS }; /// Constants for Login-Service attribute values enum LoginServiceTypes { LS_Telnet = 0, LS_Rlogin, LS_TcpClear, LS_PortMaster, LS_Lat, LS_X25_PAD, LS_X25_T3POS, LS_TcpClearQuiet }; /// Constants for NAS-Port-Type attribute values enum NASPortTypes { NasPort_Asynchronous = 0, NasPort_Synchronous, NasPort_IsdnSynchronous, NasPort_IsdnAsynchronousV120 = 3, NasPort_IsdnAsynchronousV110, NasPort_Virtual = 5, NasPort_Piafs, NasPort_HdlcClearChannel, NasPort_X25 = 8, NasPort_X75, NasPort_G3Fax, NasPort_SDSL, NasPort_AdslCap = 12, NasPort_AdslDmt, NasPort_Idsl, NasPort_Ehternet, NasPort_xDsl = 16, NasPort_Cable, NasPort_WirelessOther, NasPort_WirelessIeee8021 = 19 }; /// Constants for Acct-Status-Type atribute values enum AcctStatusTypes { AcctStatus_Start = 1, AcctStatus_Stop = 2, AcctStatus_InterimUpdate = 3, AcctStatus_AccountingOn = 7, AcctStatus_AccountingOff = 8 }; /// Constants for VendorId VSA field enum VendorIdentifiers { CiscoVendorId = 9 }; /// Contants for Cisco VSA types enum CiscoVSA { CiscoVSA_AV_Pair = 1, CiscoVSA_h323_remote_address = 23, CiscoVSA_h323_conf_id = 24, CiscoVSA_h323_setup_time = 25, CiscoVSA_h323_call_origin = 26, CiscoVSA_h323_call_type = 27, CiscoVSA_h323_connect_time = 28, CiscoVSA_h323_disconnect_time = 29, CiscoVSA_h323_disconnect_cause = 30, CiscoVSA_h323_voice_quality = 31, CiscoVSA_h323_gw_id = 33, CiscoVSA_h323_incoming_conf_id = 35, CiscoVSA_h323_credit_amount = 101, CiscoVSA_h323_credit_time = 102, CiscoVSA_h323_return_code = 103, CiscoVSA_h323_redirect_number = 106, CiscoVSA_h323_preferred_lang = 107, CiscoVSA_h323_redirect_ip_address = 108, CiscoVSA_h323_billing_model = 109, CiscoVSA_h323_currency = 110, CiscoVSA_release_source = 115, CiscoVSA_preferred_codec = 116, CiscoVSA_rewritten_e164_num = 117 }; /** Construct uninitialized attribute. It should be initialized later by other means (operator=, etc.) */ RadiusAttr(); /** Create TLV RADIUS attribute of a given type, initializing #value# field with 'attrLength' bytes of data pointed to by 'attrValue'. In case of VSA, #vsaVendorId# is read from the attribute's value data. */ RadiusAttr( unsigned char attrType, /// Attribute Type (see #enum AttrTypes#) const void* attrValue, /// buffer with attribute Value data PINDEX valueLength /// length of attribute Value data ); /** Copy constructor for RADIUS Attribute. It simply does byte copy. */ RadiusAttr( const RadiusAttr& attr /// atrribute to copy from ) { memcpy(m_data, attr.m_data, attr.m_length); } /** Create TLV RADIUS attribute of a given type, initializing #value# field with 'stringValue.GetLength()' bytes from 'stringValue' string. In case of VSA, #vsaVendorId# is extracted from data contained in the #value# field. */ RadiusAttr( unsigned char attrType, /// Attribute Type (see #enum AttrTypes#) const PString& stringValue /// string to be stored in the attribute Value data ); /** Create TLV RADIUS attribute of a given type, initializing #value# field with an integer value passed with 'intValue' parameter. This constructor should be also used for attributes carrying 32-bit timestamps. */ RadiusAttr( unsigned char attrType, /// Attribute Type (see #enum AttrTypes#) int intValue /// 32 bit integer to be stored in the attribute Value ); /** Create TLV RADIUS attribute of a given type, initializing #value# field with 32 bit IPv4 or 128 bit IPv6 address value passed with 'addressValue' parameter. */ RadiusAttr( unsigned char attrType, /// Attribute Type (see #enum AttrTypes#) const PIPSocket::Address& addressValue /// IP address to be stored in the attribute Value ); /** Create TLV RADIUS vendor-specific (26) attribute of a given vendor specific type, initializing #value# data with 'vendorId' 32 bit identifier, 'vendorType' vendor attribute type and 'attrLength' bytes of data pointer to by 'attrValue'. #vsaVendorId# is set to 'vendorId'. */ RadiusAttr( const void* attrValue, /// buffer with data to be stored in the attribute Value PINDEX valueLength, /// data length (bytes) int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ); /** Create TLV RADIUS vendor-specific attribute of a given type, initializing #value# data with 'vendorId' 32 bit identifier, 'vendorType' vendor attribute type and 'stringValue.GetLength()' bytes (characters) of 'stringValue' parameter. #vsaVendorId# is set to 'vendorId'. */ RadiusAttr( const PString& stringValue, /// string to be stored in the attribute Value int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ); /** Create TLV RADIUS vendor-specific attribute of a given type, initializing #value# data with 'vendorId' 32 bit identifier, 'vendorType' vendor-specific attribute type and 32 bit 'intValue' integer value. #vsaVendorId# is set to 'vendorId'. */ RadiusAttr( int intValue, /// 32 bit integer to be stored in the attribute Value int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ); /** Create TLV RADIUS vendor-specific attribute of a given type, initializing #value# data with 'vendorId' 32 bit identifier, 'vendorType' vendor-specific attribute type and 32 bit IPv6 address specified by 'addressValue' parameter. #vsaVendorId# is set to 'vendorId'. */ RadiusAttr( const PIPSocket::Address& addressValue, /// IP address to be stored in the attribute Value int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ); /** Create TLV RADIUS Cisco VSA attribute of a given type. If #vsaHack# is false, then the attribute name is also embedded into the attribute value (like 'h323-currency=USD'). */ RadiusAttr( unsigned char type, /// Cisco-specific attribute type bool vsaHack, /// true to not prepend attribute name to its value const PString& stringValue /// string to be stored in the attribute Value ); /** Create TLV RADIUS attribute, reading raw data from the buffer specified by 'rawData' parameter. Reading stops after full attribute is reconstructed from the data or 'rawLength' bytes have been read. Make sure to call #IsValid()# to check if the attribute has been reconstructed successfully. Call #GetLength()# to obtain number of bytes that have been actually read. */ RadiusAttr( const void* rawData, /// buffer with the attribute raw data PINDEX rawLength /// length (bytes) of the buffer ); /** @return Type of this attribute (see #enum AttrTypes#). */ unsigned char GetType() const { return m_type; } /** @return Vendor-specific type for this attribute, assuming this attribute is a VSA that conforms to RFC 2865 guidelines for VSAs (has vendorId, vendorType and vendorLength fields). */ unsigned char GetVsaType() const { return (m_length < VsaRfc2865FixedHeaderLength) ? 0 : m_vendorType; } /** @return Total length (bytes) of this attribute. */ PINDEX GetLength() const { return m_length; } /** @return Length of the Value field for this attribute. */ PINDEX GetValueLength() const { const PINDEX len = m_length; const PINDEX headerLen = IsVsa() ? VsaFixedHeaderLength : FixedHeaderLength; return (len <= headerLen) ? 0 : (len - headerLen); } /** @return Length (bytes) of the Value field for this attribute, assuming that it conforms to RFC 2865 guidelines for VSAs (contains vendorId, vendorType and vendorLength fields). */ PINDEX GetVsaValueLength() const; /** Fill the byte array with Value associated with this attribute. The array is resized, if necessary, to contain the value. Call #GetValueLength()# to determine number of bytes written to the array */ bool GetValue( PBYTEArray& val, /// array where data is to be stored PINDEX offset = 0 /// offset into the array, where the data write starts ) const; /** Fill the byte array with Value associated with this attribute, assuming that this attribute is RFC 2865 guidelines conformant VSA. The array is resized, if necessary, to contain the value. Call #GetVsaValueLength()# to determine number of bytes written to the array */ bool GetVsaValue( PBYTEArray& val, /// array where data is to be stored PINDEX offset = 0 /// offset into the array, where the data write starts ) const; /** @return True if this is a vendor-specific attribute (VSA). */ bool IsVsa() const { return (m_type == VendorSpecific); } /** @return 32 bit vendor identifier for VSA. This call is valid only for VSA attributes (see #IsVSA()#). Also ensure that this attribute is valid (#IsValid()). */ int GetVsaVendorId() const; /** Get attribute Value as a string. Be aware that the string may contain embedded 0s. For VSA attributes this call will build the string from data contained after vendorId field. If RFC 2865 guidelines conformant VSA value is to be retrieved use rather #AsVsaString()#. @return PString containing attribute Value. If an error occurs an empty string is returned. */ PString AsString() const; /** Get RFC 2865 guidelines conformant VSA Value as a string. Be aware that the string may contain embedded 0s. This call will build the string from data contained after vendorId, vendorType and vendorLength fields. @return PString containing attribute Value. If an error occurs an empty string is returned. */ PString AsVsaString() const; /** Get Cisco's VSA value as a string, with attribute name removed, if it is prepended to the string. @return PString containing attribute Value. */ PString AsCiscoString() const; /** Get attribute Value as a 32 bit integer. For VSA attributes this call will build the integer from 4 bytes of data contained after vendorId field. If RFC 2865 guidelines conformant VSA value is to be retrieved use rather #AsVsaInteger()#. @return An integer representing attribute Value. If an error occurs 0 is returned. */ int AsInteger() const; /** Get RFC 2865 guidelines conformant VSA Value as a 32 bit integer. This call will build the integer from 4 bytes of data contained after vendorId, vendorType and vendorLength fields. @return An integer representing attribute Value. If an error occurs 0 is returned. */ int AsVsaInteger() const; /** Get attribute Value as a 32 bit timestamp. For VSA attributes this call will build the timestamp from 4 bytes of data contained after vendorId field. If RFC 2865 guidelines conformant VSA value is to be retrieved use rather #AsVsaTimestamp()#. @return PTime representing attribute Value. This timestamp is interpreted as number of seconds passed since */ time_t AsTime() const { return (time_t)AsVsaInteger(); } /** Get RFC 2865 guidelines conformant VSA Value as a 32 bit timestamp. This call will build the timestamp from 4 bytes of data contained after vendorId, vendorType and vendorLength fields. @return PTime representing attribute Value. This timestamp is interpreted as number of seconds passed since */ time_t AsVsaTime() const { return (time_t)AsVsaInteger(); } /** Get attribute Value as IPv4 or IPv6 address. For VSA attributes this call will build the IPv4 or IPv6 address from 4 / 16 bytes of data contained after vendorId field. If RFC 2865 guidelines conformant VSA value is to be retrieved use rather #AsVsaAddress()#. @return IP address representing attribute Value. */ PIPSocket::Address AsAddress() const; /** Get RFC 2865 guidelines conformant VSA Value as IPv4 or IPv6 address. This call will build the IP address from 4 or 16 bytes of data contained after vendorId, vendorType and vendorLength fields. @return IP address representing attribute Value. */ PIPSocket::Address AsVsaAddress() const; /** Write this attribute to the buffer. The buffer is resized, if necessary, to contain this attribute. @return True if successfully written (and 'written' receives number of bytes written to the buffer). */ bool Write( PBYTEArray& buffer, /// buffer the attribute data will be written to PINDEX& written, /// number of bytes written (if successful return) PINDEX offset = 0 /// offset into the buffer, where writting starts ) const; /** Assign contents of the attribute 'attr' to this attribute. @return Reference to this attribute */ RadiusAttr& operator=( const RadiusAttr& attr /// the attribute that contents will be assigned from ) { memcpy(m_data, attr.m_data, attr.m_length); return *this; } /** Check whether this attribute contains valid data. @return Trye if this attribute is "valid". */ bool IsValid() const { return ((PINDEX)m_length) >= ((m_type == VendorSpecific) ? VsaFixedHeaderLength : FixedHeaderLength ); } /** Output PDU to the stream. This is primarily used by the standard #operator<<# function. */ void PrintOn( ostream& strm /// Stream to print the object into. ) const; friend ostream& operator<<(ostream& s, const RadiusAttr& attr) { attr.PrintOn(s); return s; } protected: /** Read attribute data from the raw buffer. @return TRUE if attribute has been sucessfully read. Call #GetLength()# to determine how many bytes have been read. */ bool Read( const void* rawData, /// raw buffer with attribute data PINDEX rawLength /// length of the buffer ); /** Read attribute data from the byte array. @return TRUE if attribute has been sucessfully read. Call #GetLength()# to determine how many bytes have been read. */ bool Read( const PBYTEArray& array, /// byte array with the attribute data PINDEX offset = 0 /// offset into the buffer, where data starts ) { return (array.GetSize() > offset) ? Read(((const BYTE*)array) + offset, array.GetSize() - offset) : false; } protected: union { /** Attribute raw data. The most important fields: data[0] - attribute Type data[1] - attribute Length (bytes) */ unsigned char m_data[MaxLength]; struct { unsigned char m_type; unsigned char m_length; union { unsigned char m_value[MaxLength - FixedHeaderLength]; struct { unsigned char m_vendorId[4]; unsigned char m_vendorType; unsigned char m_vendorLength; unsigned char m_vendorValue[MaxLength - VsaRfc2865FixedHeaderLength]; }; }; }; }; }; /// Encapsulates RADIUS packet (PDU). class RadiusPDU { public: /// Useful constants enum Constants { /// This header is always present FixedHeaderLength = 20, /// Length of Authenticator vector inside Fixed Header AuthenticatorLength = 16, /// Offset of Authenticator field from the beginning of PDU data AuthenticatorOffset = 4 }; /// Standarized RADIUS packet types enum Codes { Invalid = 0, AccessRequest, AccessAccept, AccessReject, AccountingRequest = 4, AccountingResponse, AccountingStatus, PasswordRequest = 7, PasswordAccept = 8, PasswordReject, AccountingMessage, AccessChallenge = 11, StatusServer = 12, StatusClient }; /// Minimum and maximum number of bytes per RADIUS packet. enum PDUBounds { MinPduLength = FixedHeaderLength, MaxPduLength = 4096 }; /** Create RadiusPDU instance initialized with 0 or empty values. This object must be intialized later with meaningful values. */ RadiusPDU(); /// Copy constructor for RADIUS packet. RadiusPDU( const RadiusPDU& ref /// source RADIUS packet to be copied ); /** Create PDU with no attributes, initializing #code# and #id# fields. */ RadiusPDU( unsigned char packetCode, /// code - see #Codes enum# unsigned char packetId = 0/// packet id (sequence number) ); /** Create PDU from the raw data buffer. 'rawLength' defines size of the buffer. Call #IsValid()# to check if PDU was built successfully from the raw data. */ RadiusPDU( const void* rawData, /// raw data buffer PINDEX rawLength /// raw data length ); /** Checks whether this PDU contains valid data. @return TRUE if this PDU is valid. */ bool IsValid() const; /** @return Code for this RADIUS packet (see #enum Codes#) */ unsigned char GetCode() const { return m_code; } /** Set new type (Code filed) for this PDU. */ void SetCode( unsigned char newCode /// new PDU type ) { m_code = newCode; } /** @return Identifier (Id field) of this RADIUS packet */ unsigned char GetId() const { return m_id; } /** Set new identifier for this RADIUS packet. */ void SetId( unsigned char newId /// new packet identifier ) { m_id = newId; } /** @return Length of this RADIUS packet (bytes) */ PINDEX GetLength() const { return (((PINDEX)(m_length[0]) & 0xff) << 8) | ((PINDEX)(m_length[1]) & 0xff); } /** @return A pointer to a memory block that holds 16 bytes authenticator. */ const unsigned char* GetAuthenticator() const { return m_authenticator; } /** Fill the array with 16 bytes #authenticator# vector associated with this RADIUS packet. */ void GetAuthenticator( PBYTEArray& vector, /// buffer where the 16 bytes authenticator will be stored PINDEX offset = 0 /// offset into the buffer where the data write starts ) const; /** Fill 16 bytes #authenticator# vector associated with this RADIUS packet with data passed in 'vector' parameter. @return TRUE if authenticator has been set */ bool SetAuthenticator( const PBYTEArray& vector, /// 16 bytes authenticator PINDEX offset = 0 /// offset into the buffer where the authenticator starts ); /** Fill 16 bytes #authenticator# vector associated with this RADIUS packet with data pointed to by 'data' parameter. @return TRUE if authenticator has been set */ bool SetAuthenticator( const void* data /// 16 bytes authenticator ); /// Fill #authenticator# vector with 16 bytes of random data. void SetAuthenticator( PRandom& random /// random generator to be used ); /** Fill Request Authenticator field in this PDU before sending it to RADIUS server. Default implementation sets RA to random vector for non-accounting PDUs, or to MD5 checksum of the whole packet (replacing RA with 0s) + sharedSecret for accounting requests. */ void SetAuthenticator( const PString& secret, /// secret shared between client and server PMessageDigest5& md5 /// MD5 generator ); /** @return Number of attributes associated with this RADIUS PDU. */ PINDEX GetNumAttributes() const; /** Appends a clone of this attribute to the attribute list tail. @return True if the attribute has been appended. */ bool AppendAttr( const RadiusAttr& attr /// attribute to be appended ); RadiusPDU& operator +=(const RadiusAttr& attr) { AppendAttr(attr); return (*this); } /// Append a generic attribute bool AppendAttr( unsigned char attrType, /// Attribute Type const void* attrValue, /// buffer with attribute Value data PINDEX valueLength /// length of attribute Value data ); /// Append a string attribute bool AppendAttr( unsigned char attrType, /// Attribute Type const PString& stringValue /// string to be stored in the attribute Value data ); /// Append an integer attribute bool AppendAttr( unsigned char attrType, /// Attribute Type int intValue /// 32 bit integer to be stored in the attribute Value ); /// Append an IP address attribute bool AppendAttr( unsigned char attrType, /// Attribute Type const PIPSocket::Address& addressValue /// IP address to be stored in the attribute Value ); /// Append a generic VSA attribute bool AppendVsaAttr( const void* attrValue, /// buffer with data to be stored in the attribute Value PINDEX valueLength, /// data length (bytes) int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ); /// Append a string VSA attribute bool AppendVsaAttr( const PString& stringValue, /// string to be stored in the attribute Value int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ); /// Append an integer VSA attribute bool AppendVsaAttr( int intValue, /// 32 bit integer to be stored in the attribute Value int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ); /// Append an IP address VSA attribute bool AppendVsaAttr( const PIPSocket::Address& addressValue, /// IP address to be stored in the attribute Value int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ); /// Append a string Cisco VSA attribute bool AppendCiscoAttr( unsigned char vendorType, /// vendor-specific attribute type const PString& stringValue, /// string to be stored in the attribute Value bool vsaHack = false /// true to not prepend attribute name to its value ); /// Copy contents of the given Radius packet to this packet RadiusPDU& operator=( const RadiusPDU& pdu ) { CopyContents(pdu); return *this; } /** @return A pointer to a Radius attribute next after #previousAttr# or to the first one in the packet. #previousAttr# can be used to iterate over all attributes present in the packet. NULL is returned if there are no more (or no at all) attributes. NOTE: All xxxAttr functions return a temporary pointer that is valid only during this object instance lifetime. */ const RadiusAttr* GetAttr( const RadiusAttr* previousAttr = NULL ) const; /** Find an attribute of a given type inside the radius packet. The search starts from the list head or after the specified attribute. @return A pointer to the attribute found or NULL if no attribute of this type has been found. */ const RadiusAttr* FindAttr( unsigned char attrType, /// attribute type to be matched const RadiusAttr* prevAttr = NULL /// start element for the search operation ) const; /** Find a VSA attribute of a given VendorId and VendorType fields inside the radius packet. The search starts from the list head or after the specified attribute. @return A pointer to the attribute found or NULL if no attribute of this type has been found. */ const RadiusAttr* FindVsaAttr( int vendorId, /// vendor identifier to be matched unsigned char vendorType, /// vendor attribute type to be matched const RadiusAttr* prevAttr = NULL /// start element for the search operation ) const; /** Write this RADIUS packet data into the buffer. @return TRUE if packet data has been written */ bool Write( PBYTEArray& buffer, /// buffer where the data will be stored PINDEX& written, /// number of bytes written (if successful) PINDEX offset = 0 /// offset into the buffer, where the write operation starts ) const; /** Scan list of attributes associated with this PDU and encrypts passwords. This implementation hides password stored in User-Name attributes using method described in RFC 2865. @return False if passwords could not be encrypted for some reason. */ bool EncryptPasswords( const PString& secret, /// secret shared between client and server PMessageDigest5& md5 /// MD5 generator ); /** Output PDU to the stream. This is primarily used by the standard #operator<<# function. */ void PrintOn( ostream& strm /// Stream to print the object into. ) const; friend ostream& operator<<(ostream& s, const RadiusPDU& pdu) { pdu.PrintOn(s); return s; } protected: /** Build PDU object from the raw data buffer. Data previously associated with this object is lost. @return TRUE if the object has been successfully built (e.g. raw buffer contained valid data) */ bool Read( const void* rawData, /// raw buffer with PDU data PINDEX rawLength /// length (bytes) of the raw buffer ); /** Build PDU object from the raw data buffer. Data previously associated with this object is lost. @return TRUE if the object has been successfully built (e.g. raw buffer contained valid data) */ bool Read( const PBYTEArray& buffer, /// buffer with RADIUS packet data PINDEX offset = 0 /// offset into the buffer, where data starts ); /// Copy content of RADIUS packet 'pdu' into this object. void CopyContents( const RadiusPDU& pdu /// packet to copy data from ); private: /// Set Radius packet length to the given value void SetLength( PINDEX newLen /// new packet length in bytes ) { m_length[0] = (unsigned char)((newLen >> 8) & 0xff); m_length[1] = (unsigned char)(newLen & 0xff); } protected: union { /// raw RADIUS packet data unsigned char m_data[MaxPduLength]; struct { /// RADIUS packet type unsigned char m_code; /// RADIUS packet id (sequence number) unsigned char m_id; /// RADIUS packet length (big endian) unsigned char m_length[2]; /// RADIUS authenticator vector unsigned char m_authenticator[AuthenticatorLength]; unsigned char m_attributes[MaxPduLength - FixedHeaderLength]; }; }; }; /** RADIUS client socket maintaining IDs for pending requests and handling requests from multiple threads. Call #GenerateNewId()# to create new identifier that can be used with next RADIUS packet sent using this socket. If #GenerateNewId()# retruns P_MAX_INDEX it means that all IDs are being used and you can either: 1. wait for oldest Id to return to the pool of free IDs, 2. create new RadiusSocket. */ class RadiusSocket : public PUDPSocket { PCLASSINFO(RadiusSocket, PUDPSocket) public: /** Create new socket at given port. Autoselect local network interface. Call #IsOpen()# to check if the socket has been created. */ RadiusSocket( /// port number to send requests from (0=autoselect) WORD port = 0 ); /** Create new socket at given port, bound to the selected network interface. Call #IsOpen()# to check if the socket has been created. */ RadiusSocket( /// local network interface address const PIPSocket::Address & addr, /// port number to send requests from WORD port = 0 ); virtual ~RadiusSocket(); virtual void PrintOn(ostream& strm) const; /** Process RADIUS request/response sequence. It sends Radius packet to host 'serverAddress:remotePort' and reads the response into 'pdu'. Use #SetReadTimeout()# to set timeout for this operation. @return True if the RADIUS response has been successfully received and stored in a variable referenced by the 'pdu' param. */ virtual bool MakeRequest( const RadiusPDU* request, /// buffer with RADIUS packet to send const Address& serverAddress, /// RADIUS server address WORD remotePort, /// RADIUS server port RadiusPDU*& pdu /// receives RADIUS Response PDU on success ); /** Send RADIUS request and return immediately. It sends the #request# to the host 'serverAddress:remotePort' and does not wait for a response. @return True if the request has been successfully sent. */ virtual bool SendRequest( const RadiusPDU* request, /// buffer with RADIUS packet to send const Address& serverAddress, /// RADIUS server address WORD remotePort /// RADIUS server port ); /** Generate an unique ID suitable for RadiusPDU identifiers. This function automatically calls #RefreshIdCache()#. @return An identifier in range 0-255 or P_MAX_INDEX if no unique ID can be generated at this moment. */ PINDEX GenerateNewId(); /** Free any RADIUS packet identifiers, that can be reused at this moment. */ void RefreshIdCache( const time_t now = time(NULL) /// current time ); /** @return The time interval for generated Identifier to be unique. */ PTimeInterval GetIdCacheTimeout() const { return m_idCacheTimeout; } /** Set new time interval for generated Identifiers to be unique. Each generated Identifier can be reused only if the specified time interval elapses. */ void SetIdCacheTimeout( const PTimeInterval& timeout /// new timeout ) { m_idCacheTimeout = timeout; } /** @return TRUE if the socket is not used by any request any more. Be aware, that the socket (port number) cannot be reused until all its allocated IDs are timed out. */ bool CanDestroy() const { return (m_oldestId == m_nextId); } /** @return Timestamp of the most recent request issued. */ PTime GetRecentRequestTime() const { return m_recentRequestTime; } private: PINDEX AllocReadSyncPoint(); void FreeReadSyncPoint( PINDEX syncPointIndex ); private: struct RadiusRequest { RadiusRequest( const RadiusPDU* request, RadiusPDU*& response, const Address* address, WORD port ) : m_request(request), m_response(response), m_addr(address), m_port(port) {} const RadiusPDU* m_request; RadiusPDU*& m_response; const Address* m_addr; WORD m_port; }; /** Table filled with requests being currently services by this socket. Indexed by request Id. */ RadiusRequest* m_pendingRequests[256]; /** SyncPoint for socket read operations. SyncPoints are allocated/freed on demand. */ PSyncPoint* m_readSyncPoints[256]; /** Index for matching request Id with index into #readSyncPoints# array. */ PINDEX m_readSyncPointIndices[256]; /// Bit map of sync points being used by pending requests /// (256 bits) DWORD m_syncPointMap[8]; /// Number of preallocated SyncPoints (for performance reasons) /// These SyncPoints get freed on socket destruction PINDEX m_permanentSyncPoints; /// mutex for mt synchronization PTimedMutex m_readMutex; /// mutex for atomic WriteTo operation on the socket PMutex m_writeMutex; /// flag signaling that some request thread performs read operation bool m_isReading; /// number of pending requests PINDEX m_nestedCount; /// oldest Id that should not be used (is still valid) unsigned char m_oldestId; /// next free Id unsigned char m_nextId; /// timestamps for generated IDs, used to check when /// Id can be retruned to pool of free IDs time_t m_idTimestamps[256]; /// Timestamp of the most recent request performed on this socket PTime m_recentRequestTime; /// time interval over that generated IDs must be unique PTimeInterval m_idCacheTimeout; /// save listen address for port notifications Address m_addr; WORD m_port; }; class RadiusClient { public: /** Default UDP ports for RADIUS authorization and accounting servers. */ enum RadiusClientPorts { DefaultAuthPort = 1812, DefaultAcctPort = 1813, Rfc2138AuthPort = 1645, /// obsolete Rfc2138AcctPort = 1646 /// obsolete }; /// Some defaults enum DefaultValues { /// timeout for packet IDs returned back to the pool of available IDs DefaultIdCacheTimeout = 9000, /// timeout for single request operation DefaultRequestTimeout = 2000, /// how many times request is send (1==no retransmission) DefaultRetries = 2, /// timeout for unused sockets to be deleted DefaultSocketDeleteTimeout = 60000 }; /** Construct a RADIUS protocol client, building a list of RADIUS servers from the string. Custom port number for each RADIUS server can be specified by appending ":auth_port:acct_port" string to the server name. For example, "radius1.mycompany.com:1812:1813". Sample lists may look as follows: "192.168.1.1" "192.168.1.1:1645:1646" "192.168.1.1:1645:1646;192.168.2.1:1812:1813" "radius1.gnugk.org" "radius1.gnugk.org:1812:1813" "radius1.gnugk.org;radius2.gnugk.org" "radius1.gnugk.org:1812:1813;radius2.gnugk.org:1645:1646" */ RadiusClient( /// primary RADIUS server const PString& servers, /// local address for RADIUS client const PString& address = PString::Empty(), /// default secret shared between the client and the server const PString& sharedSecret = PString::Empty() ); /** Construct a RADIUS protocol client reading its settings from the config. */ RadiusClient( PConfig& config, /// config that contains RADIUS settings const PString& sectionName /// config section with the settings ); /// Destroy this object virtual ~RadiusClient(); /** @return The local IP address this RADIUS client is bound to. */ PIPSocket::Address GetLocalAddress() const { return m_localAddress; } /** Set new time interval for RADIUS packet Identifiers to be unique. Warning: settings this value to short interval can cause packets to be ignored by the server, too long value can produce large number of client UDP sockets opened at once. @return True if the new interval has been set. */ bool SetIdCacheTimeout( const PTimeInterval& timeout /// new time interval ); /** Send requestPDU to RADIUS server and waits for response. If the response is received, it is returned in responsePDU. @return True if request/response sequence completed successfully. */ virtual bool MakeRequest( const RadiusPDU& requestPDU, /// PDU with request packet RadiusPDU*& responsePDU /// filled with PDU received from RADIUS server ); /** Sends a RADIUS request and does not wait for a response. This can be used to send accounting updates, for example. @return True if the RADIUS request has been successfully sent (this does not mean it has arrived at the radius server). */ virtual bool SendRequest( const RadiusPDU& requestPDU /// PDU with request packet ); static WORD GetDefaultAuthPort() { return DefaultAuthPort; } static WORD GetDefaultAcctPort() { return DefaultAcctPort; } protected: /** Determine if the #pdu# should be send to auth port of RADIUS server (FALSE) or acct port (TRUE). @return True to send the PDU to the accounting RADIUS server module, false to send the PDU to authenticating RADIUS server module. */ virtual bool IsAcctPDU( const RadiusPDU& pdu /// PDU to be checked ) const; /** Retrieves reference to RADIUS socket and RADIUS packet identified, that are suitable for a single request. That means that for each request, GetSocket call should be made to obtain the socket and the id. @return: True if socket and if are filled with valid data */ bool GetSocket( /// pointer that will be filled with RadiusSocket pointer /// on success RadiusSocket*& socket, /// identifier that will be initialized to valid Id on success unsigned char& id ); /** Create new instance of RadiusSocket based class. Can be overriden to provide custom RadiusSocket implementations. @return Pointer to the new socket */ virtual RadiusSocket* CreateSocket( const PIPSocket::Address& addr, WORD port = 0 ); /** Create new instance of RadiusSocket based class. Can be overriden to provide custom RadiusSocket implementations. @return Pointer to the new socket */ virtual RadiusSocket* CreateSocket( WORD port = 0 ); /** Verify Response Authenticator vector from received PDU. Provided here mainly for RadiusSocket. @return True if RA is valid, FALSE otherwise */ virtual bool VerifyResponseAuthenticator( const RadiusPDU* request, /// buffer with RADIUS request PDU const RadiusPDU* response, /// buffer with RADIUS response PDU const PString& secret /// shared secret used to create the request PDU ); /// Parse #servers# string and build a list of RADIUS servers from it. void GetServersFromString( const PString& servers ); protected: typedef std::list::iterator socket_iterator; typedef std::list::const_iterator socket_const_iterator; /// An entry describing a signle radius server struct RadiusServer { PString m_serverAddress; /// IP or DNS name PString m_sharedSecret; /// password shared between the client and the server WORD m_authPort; /// port number to send Access Requests to WORD m_acctPort; /// port number to send Accounting Requests to }; /// NOTE: m_radiusServers and m_sharedSecret are expected to be constant /// during the object lifetime /// list of RADIUS servers std::vector m_radiusServers; /// shared password for authorizing this client with RADIUS servers PString m_sharedSecret; /// default port for RADIUS authentication (if not overriden) WORD m_authPort; /// default port for RADIUS accounting (if not overriden) WORD m_acctPort; /// base UDP port for RADIUS client WORD m_portBase; /// upper UDP port limit for RADIUS client WORD m_portMax; /// timeout value for processing RADIUS client requests PTimeInterval m_requestTimeout; /// time interval over which the packet Id has to be unique (for a single client port) PTimeInterval m_idCacheTimeout; /// timeout for unused sockets to be deleted from the pool PTimeInterval m_socketDeleteTimeout; /// number of packet retransmissions to a single RADIUS server unsigned m_numRetries; /// how RADIUS packers are retransmitted /// 0: numRetries to server #1, numRetries to server #2, ... /// 1: 1st packet to #1, 1st packet to #2, ..., 2nd packet to #1, ... bool m_roundRobinServers; /// local address that the client should bind to when making requests PIPSocket::Address m_localAddress; /// array of active RADIUS client sockets std::list m_activeSockets; /// mutex for accessing #activeSockets# and other stuff mutable PMutex m_socketMutex; }; #endif /* __RADPROTO_H */ #endif /* HAS_RADIUS */ gnugk-3.4/PaxHeaders.16356/ldap.cxx0000644000175000001440000000005012210766503015167 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/ldap.cxx0000644000175000001440000002122312210766503014130 0ustar00janusers00000000000000/* * ldapauth.cxx * * LDAP authentication/authorization modules for GNU Gatekeeper * * Copyright (c) 2013, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include #ifdef P_LDAP #include #include "gk_const.h" #include "RasTbl.h" #include "RasPDU.h" #include "gkauth.h" #include "Routing.h" #include "config.h" const char* LDAPServerSect = "GkLDAP::Settings"; const char* LDAPAttributeSect = "GkLDAP::LDAPAttributeNames"; class LDAPBase { public: LDAPBase() : m_serverPort(0), m_authMethod(PLDAPSession::AuthSimple), m_sizelimit(1), m_startTLS(false) { } virtual ~LDAPBase() { } protected: virtual void Init(); virtual PLDAPSession * CreateConnection(); virtual void DestroyConnection(PLDAPSession * session) const; protected: PString m_serverName; unsigned m_serverPort; PLDAPSession::AuthenticationMethod m_authMethod; PTimeInterval m_timeout; unsigned m_sizelimit; PString m_baseDN; PString m_bindUserDN; PString m_bindUserPW; PString m_IDFilter; PString m_E164Filter; PString m_attribute; bool m_startTLS; }; /// Generic LDAP authenticator for H.235 enabled endpoints class LDAPPasswordAuth : public LDAPBase, public SimplePasswordAuth { public: LDAPPasswordAuth(const char* authName); virtual ~LDAPPasswordAuth() { } protected: /** Override from SimplePasswordAuth. @return True if the password has been found for the given alias. */ virtual bool GetPassword(const PString & alias, PString & password); private: LDAPPasswordAuth(); LDAPPasswordAuth(const LDAPPasswordAuth&); LDAPPasswordAuth& operator=(const LDAPPasswordAuth&); }; /// Generic LDAP authenticator for alias/IP based authentication class LDAPAliasAuth : public LDAPBase, public AliasAuth { public: LDAPAliasAuth(const char* authName); virtual ~LDAPAliasAuth() { } protected: /** Get auth condition string for the given alias. This implementation searches the LDAP database for the string. Override from AliasAuth. @return The AliasAuth condition string for the given alias. */ virtual bool GetAuthConditionString(const PString & alias, PString & authCond); private: LDAPAliasAuth(); LDAPAliasAuth(const LDAPAliasAuth&); LDAPAliasAuth& operator=(const LDAPAliasAuth&); }; void LDAPBase::Init() { PConfig* cfg = GkConfig(); m_serverName = cfg->GetString(LDAPServerSect, "ServerName", "localhost"); m_serverPort = cfg->GetInteger(LDAPServerSect, "ServerPort", 389); PCaselessString mode = cfg->GetString(LDAPServerSect, "BindAuthMode", "simple"); m_authMethod = PLDAPSession::AuthSimple; if (mode == "sasl") m_authMethod = PLDAPSession::AuthSASL; else if (mode == "kerberos") m_authMethod = PLDAPSession::AuthKerberos; m_timeout = cfg->GetInteger(LDAPServerSect, "timelimit", 30) * 1000; m_sizelimit = cfg->GetInteger(LDAPServerSect, "sizelimit", 1); m_baseDN = cfg->GetString(LDAPServerSect, "SearchBaseDN", ""); m_bindUserDN = cfg->GetString(LDAPServerSect, "BindUserDN", ""); m_bindUserPW = cfg->GetString(LDAPServerSect, "BindUserPW", ""); m_IDFilter = cfg->GetString(LDAPAttributeSect, "H323ID", "mail") + "="; m_E164Filter = cfg->GetString(LDAPAttributeSect, "TelephonNo", "telephoneNumber") + "="; #ifdef hasLDAPStartTLS m_startTLS = GkConfig()->GetBoolean(LDAPServerSect, "StartTLS", false); #endif } PLDAPSession * LDAPBase::CreateConnection() { PLDAPSession * session = new PLDAPSession(); if (!session->Open(m_serverName, m_serverPort)) { PTRACE(1, "LDAP\tCan't connect to LDAP server " << m_serverName << ":" << m_serverPort); delete session; return NULL; } if (m_startTLS) { #ifdef hasLDAPStartTLS if (!session->StartTLS()) { PTRACE(1,"LDAP\tStartTLS failed"); return NULL; } #else PTRACE(1, "LDAP\tError: LDAP StartTLS not supported in this version"); #endif } if (m_timeout > 0) session->SetTimeout(m_timeout); // TODO: if we set timeout=0, default timeout 30 of LDAPSession class remains, error if we set 0 if (m_sizelimit > 0) session->SetSearchLimit(m_sizelimit); session->SetBaseDN(m_baseDN); if (!m_bindUserDN.IsEmpty()) session->Bind(m_bindUserDN, m_bindUserPW, m_authMethod); return session; } void LDAPBase::DestroyConnection(PLDAPSession * session) const { session->Close(); delete session; } LDAPPasswordAuth::LDAPPasswordAuth(const char* authName) : SimplePasswordAuth(authName) { Init(); m_attribute = GetConfig()->GetString(LDAPAttributeSect, "H235PassWord", "plaintextPassword"); } bool LDAPPasswordAuth::GetPassword(const PString & alias, PString & password) { PLDAPSession * ldapClient = CreateConnection(); if (!ldapClient) return false; PList data = ldapClient->Search(m_IDFilter + alias, m_attribute); if (data.IsEmpty()) { data = ldapClient->Search(m_E164Filter + alias, m_attribute); if (data.IsEmpty()) { PTRACE(2, "LDAP\tCan't find password for " << alias << ": " << ldapClient->GetErrorText()); DestroyConnection(ldapClient); return false; } else { if (data.front().Contains(m_attribute)) password = data.front()[m_attribute]; } } else { if (data.front().Contains(m_attribute)) password = data.front()[m_attribute]; } DestroyConnection(ldapClient); return (!password.IsEmpty()); } LDAPAliasAuth::LDAPAliasAuth(const char* authName) : AliasAuth(authName) { Init(); m_attribute = GetConfig()->GetString(LDAPAttributeSect, "IPAddress", "voIPIpAddress"); } bool LDAPAliasAuth::GetAuthConditionString(const PString & alias, PString & authCond) { PLDAPSession * ldapClient = CreateConnection(); if (!ldapClient) return false; PList data = ldapClient->Search(m_IDFilter + alias, m_attribute); if (data.IsEmpty()) { data = ldapClient->Search(m_E164Filter + alias, m_attribute); if (data.IsEmpty()) { PTRACE(2, "LDAP\tCan't find auth rule for " << alias << ": " << ldapClient->GetErrorText()); DestroyConnection(ldapClient); return false; } } if (data.front().Contains(m_attribute)) { PString ip = data.front()[m_attribute]; if (ip.Find('.') == P_MAX_INDEX) { // add default port if none specified authCond = "sigip:" + ip + ":" + PString(GK_DEF_ENDPOINT_SIGNAL_PORT); } else { authCond = "sigip:" + ip; } } DestroyConnection(ldapClient); return (!authCond.IsEmpty()); } // a routing policy to look up the destination from an LDAP server class LDAPPolicy : public LDAPBase, public Routing::AliasesPolicy { public: LDAPPolicy(); protected: virtual bool FindByAliases(Routing::RoutingRequest &, H225_ArrayOf_AliasAddress &); virtual bool FindByAliases(Routing::LocationRequest &, H225_ArrayOf_AliasAddress &); PString m_attribute; }; LDAPPolicy::LDAPPolicy() { m_name = "LDAP"; Init(); m_attribute = GkConfig()->GetString(LDAPAttributeSect, "CallDestination", "voIPIpAddress"); } bool LDAPPolicy::FindByAliases(Routing::RoutingRequest & request, H225_ArrayOf_AliasAddress & aliases) { bool routed = false; PLDAPSession * ldapClient = CreateConnection(); if (!ldapClient) return false; for (PINDEX i = 0; i < aliases.GetSize(); ++i) { H225_AliasAddress & alias = aliases[i]; PList data; if (alias.GetTag() == H225_AliasAddress::e_dialedDigits) { data = ldapClient->Search(m_E164Filter + AsString(alias, false), m_attribute); } else { data = ldapClient->Search(m_IDFilter + AsString(alias, false), m_attribute); } if (!data.IsEmpty()) { if (data.front().Contains(m_attribute)) { PString destinationIp = data.front()[m_attribute]; if (IsIPAddress(destinationIp)) { PStringArray adr_parts = destinationIp.Tokenise(":", FALSE); PString ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); if (port == 0) port = GK_DEF_ENDPOINT_SIGNAL_PORT; Routing::Route route(m_name, SocketToH225TransportAddr(ip, port)); route.m_destEndpoint = RegistrationTable::Instance()->FindByAliases(aliases); request.AddRoute(route); routed = true; } else { PTRACE(1, "Invalid IP for LDAP routing in " << m_attribute); // TODO: also allow routing to a new alias ? } } } } DestroyConnection(ldapClient); return routed; } bool LDAPPolicy::FindByAliases(Routing::LocationRequest & request, H225_ArrayOf_AliasAddress & aliases) { return FindByAliases((Routing::RoutingRequest&)request, aliases); } namespace { // anonymous namespace // instantiate auth policies GkAuthCreator LDAPPasswordAuthCreator("LDAPPasswordAuth"); GkAuthCreator LDAPAliasAuthCreator("LDAPAliasAuth"); // instatiate routing policy SimpleCreator LDAPPolicyCreator("ldap"); } // end of anonymous namespace #endif gnugk-3.4/PaxHeaders.16356/snmp.h0000644000175000001440000000005011753244754014663 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/snmp.h0000644000175000001440000000231711753244754013627 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // snmp.h for GNU Gatekeeper // // Copyright (c) 2012, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef GNUGKSNMP_H #define GNUGKSNMP_H "@(#) $Id: snmp.h,v 1.11 2012/05/11 17:18:36 willamowius Exp $" #include "config.h" #ifdef HAS_SNMP #include "Toolkit.h" const char * const SNMPSection = "SNMP"; enum SNMPLevel { SNMPError=1, SNMPWarning=2, SNMPInfo=3 }; enum SNMPGroup { General=1, Network=2, Database=3, Accounting=4, Authentication=5, Configuration=6 }; PCaselessString SelectSNMPImplementation(); void StartSNMPAgent(); void StopSNMPAgent(); void DeleteSNMPAgent(); void SendSNMPTrap(unsigned trapNumber, SNMPLevel severity, SNMPGroup group, const PString & msg); #define SNMP_TRAP(NO,LEVEL,GROUP,MSG) if (Toolkit::Instance()->IsSNMPEnabled()) { SendSNMPTrap(NO,LEVEL,GROUP,MSG); } #else // HAS_SNMP #define SNMP_TRAP(NO,LEVEL,GROUP,MSG) #endif // HAS_SNMP #endif // GNUGKSNMP_H gnugk-3.4/PaxHeaders.16356/RasPDU.h0000644000175000001440000000005012177027267015003 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/RasPDU.h0000644000175000001440000002111212177027267013741 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // RasPDU.h // // Define RAS PDU for GNU Gatekeeper // Avoid including large h225.h in RasSrv.h // // Copyright (c) Citron Network Inc. 2001-2003 // Copyright (c) 2006-2011, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef RASPDU_H #define RASPDU_H "@(#) $Id: RasPDU.h,v 1.31 2013/08/02 22:12:07 willamowius Exp $" #include #include "yasocket.h" #include "factory.h" #include "rasinfo.h" class Toolkit; class GkStatus; class RegistrationTable; class CallTable; class RasListener; class MulticastListener; class CallSignalListener; class TLSCallSignalListener; class StatusListener; class MultiplexRTPListener; class RasServer; class CallSignalSocket; const unsigned MaxRasTag = H225_RasMessage::e_serviceControlResponse; class GatekeeperMessage { public: GatekeeperMessage() : m_peerPort(0), m_socket(NULL) #ifdef HAS_H46017 , m_h46017Socket(NULL) #endif { } unsigned GetTag() const { return m_recvRAS.GetTag(); } const char *GetTagName() const; bool Read(RasListener *); #ifdef HAS_H46017 bool Read(const PBYTEArray & buffer); #endif bool Reply() const; PPER_Stream m_rasPDU; H225_RasMessage m_recvRAS; H225_RasMessage m_replyRAS; PIPSocket::Address m_peerAddr; WORD m_peerPort; PIPSocket::Address m_localAddr; RasListener * m_socket; #ifdef HAS_H46017 CallSignalSocket * m_h46017Socket; #endif }; class RasListener : public UDPSocket { public: RasListener(const Address &, WORD); virtual ~RasListener(); GatekeeperMessage *ReadRas(); bool SendRas(const H225_RasMessage &, const Address &, WORD); WORD GetSignalPort() const { return m_signalPort; } void SetSignalPort(WORD pt) { m_signalPort = pt; } #ifdef HAS_TLS WORD GetTLSSignalPort() const { return m_tlsSignalPort; } void SetTLSSignalPort(WORD pt) { m_tlsSignalPort = pt; } #endif Address GetLocalAddr(const Address &) const; Address GetPhysicalAddr(const Address & addr) const; H225_TransportAddress GetRasAddress(const Address &) const; H225_TransportAddress GetCallSignalAddress(const Address &) const; // new virtual function // filter out unwanted message to the listener by returning false virtual bool Filter(GatekeeperMessage *) const; protected: Address m_ip; PMutex m_wmutex; WORD m_signalPort; #ifdef HAS_TLS WORD m_tlsSignalPort; #endif bool m_virtualInterface; }; class RasMsg : public Task { public: virtual ~RasMsg() { delete m_msg; } // new virtual function virtual bool Process() = 0; virtual int GetSeqNum() const = 0; virtual H225_NonStandardParameter *GetNonStandardParam() = 0; // override from class Task virtual void Exec(); bool IsFrom(const PIPSocket::Address &, WORD) const; unsigned GetTag() const { return m_msg->GetTag(); } const char *GetTagName() const { return m_msg->GetTagName(); } void GetRasAddress(H225_TransportAddress &) const; void GetCallSignalAddress(H225_TransportAddress &) const; bool EqualTo(const RasMsg *) const; bool operator==(const RasMsg & other) const { return EqualTo(&other); } bool Reply() const { return m_msg->Reply(); } GatekeeperMessage *operator->() { return m_msg; } const GatekeeperMessage *operator->() const { return m_msg; } static void Initialize(); protected: RasMsg(GatekeeperMessage *m) : m_msg(m) {} RasMsg(const RasMsg &); static bool PrintStatus(const PString &); GatekeeperMessage *m_msg; // just pointers to global singleton objects // cache for faster access static Toolkit *Kit; static RegistrationTable *EndpointTbl; static CallTable *CallTbl; static RasServer *RasSrv; }; template class RasPDU : public RasMsg { public: typedef RAS RasClass; RasPDU(GatekeeperMessage *m) : RasMsg(m), request(m->m_recvRAS) {} virtual ~RasPDU() {} // override from class RasMsg virtual bool Process() { return false; } virtual int GetSeqNum() const { return request.m_requestSeqNum; } virtual H225_NonStandardParameter *GetNonStandardParam(); operator RAS & () { return request; } operator const RAS & () const { return request; } H225_RasMessage & BuildConfirm(); H225_RasMessage & BuildReject(unsigned); typedef Factory::Creator1 RasCreator; struct Creator : public RasCreator { Creator() : RasCreator(RasInfo::tag) {} virtual RasMsg *operator()(GatekeeperMessage *m) const { return new RasPDU(m); } }; protected: RAS & request; }; // abstract factory for listeners class GkInterface { public: typedef PIPSocket::Address Address; GkInterface(const Address &); virtual ~GkInterface(); // we can't call virtual functions in constructor // so initialize here virtual bool CreateListeners(RasServer *); bool IsBoundTo(const Address *addr) const { return m_address == *addr; } bool IsReachable(const Address *) const; RasListener *GetRasListener() const { return m_rasListener; } WORD GetRasPort() const { return m_rasPort; } WORD GetSignalPort() const { return m_signalPort; } Address GetAddress() const { return m_address; } protected: bool ValidateSocket(IPSocket *, WORD &); template bool SetListener(WORD nport, WORD & oport, Listener *& listener, Listener *(GkInterface::*creator)()) { if (!listener || !oport || oport != nport) { oport = nport; if (listener) listener->Close(); listener = (this->*creator)(); if (ValidateSocket(listener, oport)) return true; else listener = NULL; } return false; } Address m_address; RasListener *m_rasListener; MulticastListener *m_multicastListener; CallSignalListener *m_callSignalListener; StatusListener *m_statusListener; WORD m_rasPort, m_multicastPort, m_signalPort, m_statusPort; #ifdef HAS_TLS TLSCallSignalListener *m_tlsCallSignalListener; WORD m_tlsSignalPort; #endif RasServer *m_rasSrv; private: virtual RasListener *CreateRasListener(); virtual MulticastListener *CreateMulticastListener(); virtual CallSignalListener *CreateCallSignalListener(); #ifdef HAS_TLS virtual TLSCallSignalListener *CreateTLSCallSignalListener(); #endif virtual StatusListener *CreateStatusListener(); }; class RasHandler { public: typedef PIPSocket::Address Address; RasHandler(); virtual ~RasHandler() {} // new virtual function // check if the message is the expected one // default behavior: check if the tag is in m_tagArray virtual bool IsExpected(const RasMsg *) const; // process the RasMsg object // the object must be deleted after processed virtual void Process(RasMsg *) = 0; // give the derived class an opportunity to create customized PDU // default behavior: return the original one virtual RasMsg *CreatePDU(RasMsg *ras) { return ras; } // stop the handler virtual void Stop() {} protected: void AddFilter(unsigned); RasServer *m_rasSrv; private: bool m_tagArray[MaxRasTag + 1]; }; // encapsulate a gatekeeper request and reply class RasRequester : public RasHandler { public: RasRequester() : m_request(NULL) { Init(); } // note the H225_RasMessage object must have // longer lifetime than this object RasRequester(H225_RasMessage &); RasRequester(H225_RasMessage &, const Address &); virtual ~RasRequester(); WORD GetSeqNum() const { return m_seqNum; } bool WaitForResponse(int); RasMsg *GetReply(); // override from class RasHandler virtual bool IsExpected(const RasMsg *) const; virtual void Process(RasMsg *); virtual void Stop(); // new virtual function virtual bool SendRequest(const Address &, WORD, int = 2); virtual bool OnTimeout(); protected: void AddReply(RasMsg *); H225_RasMessage *m_request; WORD m_seqNum; Address m_txAddr, m_loAddr; WORD m_txPort; PTime m_sentTime; int m_timeout, m_retry; PSyncPoint m_sync; private: void Init(); PMutex m_qmutex; std::list m_queue; std::list::iterator m_iterator; }; template class Requester : public RasRequester { public: typedef typename RasInfo::Tag Tag; typedef typename RasInfo::ConfirmTag ConfirmTag; typedef typename RasInfo::RejectTag RejectTag; Requester(H225_RasMessage &, const Address &); virtual ~Requester() { this->m_rasSrv->UnregisterHandler(this); } // fix for GCC 3.4.2 }; template Requester::Requester(H225_RasMessage & obj_ras, const Address & ip) : RasRequester(obj_ras, ip) { obj_ras.SetTag(Tag()); RAS & ras = obj_ras; ras.m_requestSeqNum = GetSeqNum(); AddFilter(ConfirmTag()); AddFilter(RejectTag()); this->m_rasSrv->RegisterHandler(this); // fix for GCC 3.4.2 } #endif // RASPDU_H gnugk-3.4/PaxHeaders.16356/readme.txt0000644000175000001440000000005011515325504015520 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/readme.txt0000644000175000001440000000175411515325504014470 0ustar00janusers00000000000000The GNU Gatekeeper ------------------ It is covered by the GNU Public License (GPL) v2; for details see the file COPYING. In addition to that, we explicitely grant the right to link this code to the OpenH323/H323Plus and OpenSSL library. Project homepage: http://www.gnugk.org/ Project coordinator: Jan Willamowius Support: http://www.willamowus.com/gnugk-support.html To ask questions or submit bugs, please use either the users or the developers mailing list. You will find the mailing lists and archives of past messages on the project homepage. There are a number of documents in docs/ subdirectory to get you started working with the gatekeeper. The most important is the users manual. It's in SGML (linuxdoc) format. You can convert it into HTML, plain text or LaTeX format by sgmltools: $ sgml2html manual.sgml $ sgml2txt manual.sgml $ sgml2latex manual.sgml There are a number of useful configuration examples in the etc/ subdirectory. Modify them to suit your needs. gnugk-3.4/PaxHeaders.16356/GkStatus.h0000644000175000001440000000005012176233433015443 xustar000000000000000020 atime=1380868658 20 ctime=1380868609 gnugk-3.4/GkStatus.h0000644000175000001440000001776312176233433014422 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // GkStatus.h thread listening for connections to receive // status updates from the gatekeeper // // Copyright (c) 2000-2011, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef GKSTATUS_H #define GKSTATUS_H "@(#) $Id: GkStatus.h,v 1.44 2013/07/31 16:11:07 willamowius Exp $" #include #include "yasocket.h" #include "singleton.h" /** The idea of status interface output trace levels allows to select the kind of output received by a status interface client. Level 0 - no broadcast messages (except reload notifications and yell) Level 1 - only CDRs and Route Requests Level 2 - everything */ #define MIN_STATUS_TRACE_LEVEL 0 #define MAX_STATUS_TRACE_LEVEL 2 #define STATUS_TRACE_LEVEL_CDR 1 #define STATUS_TRACE_LEVEL_RAS 2 #define STATUS_TRACE_LEVEL_ROUTEREQ 1 class TelnetSocket; class StatusClient; /** Singleton class that listens for the status interface connections and maintains a list of connected status interface clients. */ class GkStatus : public Singleton, public SocketsReader { public: GkStatus(); /** Authenticate new telnet connection. Ask for username/password if required. If the authentication is successful, the client is added to the list of active clients. If it fails, client instance is deleted. */ void AuthenticateClient( /// new status interface client to be authenticated StatusClient* newClient ); /** Broadcast the message to all active client connected to the status interface. The message is sent to each client only if client trace level is greater or equal to the trace level of the message. */ void SignalStatus( /// message string to be broadcasted const PString& msg, /// trace level at which the message should be broadcasted /// if the current output trace level is less than this value, /// the client will not receive this message int level = MIN_STATUS_TRACE_LEVEL ); /** Disconnect the specified status interface client. @return true if the status interface client with the given session ID has been found and disconnected. */ bool DisconnectSession( /// session ID (instance number) for the status client to be disconnected int instanceNo, /// status interface client that requested disconnect StatusClient* requestingClient ); /** Notification form the status client that it has been deleted */ void StatusClientDeleted() { if (m_statusClients > 0) --m_statusClients; } /** Print a list of all connected status interface users to the requesting client. */ void ShowUsers( /// client that requested the list of all active clients StatusClient* requestingClient ) const; /** Print help (list of status interface commands) to the requesting client. */ void PrintHelp( /// client that requested the help message StatusClient* requestingClient ) const; enum StatusInterfaceCommands { e_PrintAllRegistrations, e_PrintAllRegistrationsVerbose,/// extra line per reg starting with '#'. e_PrintAllCached, e_PrintCurrentCalls, e_PrintCurrentCallsVerbose, /// extra line per call starting with '#'. e_PrintCurrentCallsPorts, e_Find, /// find an endpoint e_FindVerbose, e_DisconnectIp, /// disconnect a call by endpoint IP number e_DisconnectAlias, /// disconnect a call by endpoint alias e_DisconnectCall, /// disconnect a call by call number e_DisconnectCallId, /// disconnect a call by call ID e_DisconnectEndpoint, /// disconnect a call by endpoint ID e_DisconnectSession, /// disconnect a user from status port e_ClearCalls, /// disconnect all calls e_UnregisterAllEndpoints, /// force unregisterung of all andpoints e_UnregisterIp, /// force unregisterung of one andpoint by IP number e_UnregisterAlias, /// force unregisterung of one andpoint by alias e_TransferCall, /// transfer call from one endpoint to another e_RerouteCall, /// transfer call with pause and reroute e_MakeCall, /// establish a new call from endpoint A to endpoint B e_Yell, /// write a message to all status clients e_Who, /// list who is logged on at a status port e_GK, /// show my parent gatekeeper e_Help, /// List all commands e_Version, /// GkStatus Protocol Info e_Debug, /// Debugging commands e_Statistics, /// Show Statistics e_Exit, /// Close Connection e_Reload, /// Reload Config File e_Shutdown, /// Shutdown the program e_RouteToAlias, /// Route a call to a specified alias e_RouteToGateway, /// Route a call to a specified alias + destinationCallSignalAddr e_BindAndRouteToGateway, /// Route a call to a specified alias + destinationCallSignalAddr + specify bind IP e_RouteReject, /// Reject to Route a call upon ARQ (send ARJ) e_Trace, /// change trace level for status interface output e_RotateLog, /// Force log file rotation e_SetLogFilename, /// Change log file location e_AddIncludeFilter, /// Add include filter e_RemoveIncludeFilter, /// Remove include filter e_AddExcludeFilter, /// Add exclude filter e_RemoveExcludeFilter, /// Remove exclude filter e_Filter, /// Activate Status Port filtering e_PrintExcludeFilters, /// Print list of all exclude filters e_PrintIncludeFilters, /// Print list of all include filters e_PrintPrefixCapacities, /// Print prefix capacity settings with their current counters e_PrintCapacityControlRules, /// Print CapacityControl rules and their current capacities e_SendProceeding, /// Send a CallProceeding message for this call-id e_GetAuthInfo, /// Get auth module information e_GetAcctInfo, /// Get acct module information e_ResetCallCounters, /// Reset the call counters e_PrintEndpointQoS, /// Print QoS values for all endpoints e_PrintAllConfigSwitches, /// print all known config switches e_numCommands /// Number of different strings }; /** Parse the text message into the status interface command. @return The command code (see #StatusInterfaceCommands enum#) and 'args' filled with command tokens or -1 if no corresponding command has been found. */ int ParseCommand( /// message to be parsed const PString& msg, /// message split into tokens upon successful return PStringArray& args ); protected: // override from class RegularJob virtual void OnStart(); // override from class SocketsReader virtual void ReadSocket(IPSocket *); virtual void CleanUp(); /// map for fast (and easy) 'parsing' the commands from the user std::map m_commands; unsigned m_statusClients; unsigned m_maxStatusClients; }; /** Listen for incoming connections to the status interface port and create StatusClients for each new connection. */ class StatusListener : public TCPListenSocket { #ifndef LARGE_FDSET PCLASSINFO ( StatusListener, TCPListenSocket ) #endif public: /// create the new listener socket StatusListener( /// address the socket is to be bound to const Address& addr, /// port number the socket is to be bound to WORD port ); virtual ~StatusListener(); /** Create a new StatusClient socket that will be used to accept a next incoming connection. Override from class TCPListenSocket. */ virtual ServerSocket *CreateAcceptor() const; protected: Address m_addr; }; #endif // GKSTATUS_H gnugk-3.4/PaxHeaders.16356/gnugk.rc0000644000175000001440000000005012202261625015160 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gnugk.rc0000644000175000001440000000220212202261625014115 0ustar00janusers00000000000000#include #include #include #include "resource.h" // // Icon resources // LANGUAGE 0, SUBLANG_NEUTRAL 1 ICON "gnugk.ico" // // Version Information resources // LANGUAGE 0, SUBLANG_NEUTRAL 1 VERSIONINFO FILEVERSION 3,4,0,0 PRODUCTVERSION 3,4,0,0 FILEOS VOS_UNKNOWN FILETYPE VFT_UNKNOWN FILESUBTYPE VFT2_UNKNOWN FILEFLAGSMASK 0x00000000 FILEFLAGS 0x00000000 { BLOCK "StringFileInfo" { BLOCK "040904e4" { VALUE "CompanyName", "GNU Gatekeeper Project" VALUE "FileDescription", "GNU Gatekeeper" VALUE "FileVersion", "3.4.0" VALUE "InternalName", "GnuGk" VALUE "LegalCopyright", "(c) 2013, GNU General Public License v2" VALUE "LegalTrademarks", " " VALUE "OriginalFilename", "gnugk.exe" VALUE "ProductName", "GnuGk" VALUE "ProductVersion", "3.4.0" } } BLOCK "VarFileInfo" { VALUE "Translation", 0x0409, 0x04E4 } } gnugk-3.4/PaxHeaders.16356/Routing.cxx0000644000175000001440000000005012131442712015671 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/Routing.cxx0000644000175000001440000021135212131442712014636 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // Routing Mechanism for GNU Gatekeeper // // Copyright (c) Citron Network Inc. 2003 // Copyright (c) 2004-2012, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #include #include #include #include "gk_const.h" #include "h323util.h" #include "Toolkit.h" #include "RasTbl.h" #include "RasSrv.h" #include "GkClient.h" #include "GkStatus.h" #include "sigmsg.h" #include "Routing.h" #include "gksql.h" #include "config.h" #ifdef HAS_H46023 #include #endif using std::string; using std::vector; using std::list; using std::stable_sort; using std::binary_function; namespace Routing { const char *SectionName[] = { "RoutingPolicy::OnARQ", "RoutingPolicy::OnLRQ", "RoutingPolicy::OnSetup", "RoutingPolicy::OnFacility", "RoutingPolicy" }; const unsigned DEFAULT_ROUTE_PRIORITY = 1; const long DEFAULT_ROUTE_REQUEST_TIMEOUT = 10; const char* const CTIsection = "CTI::Agents"; Route::Route() : m_proxyMode(CallRec::ProxyDetect), m_flags(0), m_priority(DEFAULT_ROUTE_PRIORITY) { m_destAddr.SetTag(H225_TransportAddress::e_nonStandardAddress); // set to an invalid address Toolkit::Instance()->SetRerouteCauses(m_rerouteCauses); } Route::Route( const PString & policyName, const endptr & destEndpoint, unsigned priority ) : m_destAddr(destEndpoint ? destEndpoint->GetCallSignalAddress() : H225_TransportAddress()), m_destEndpoint(destEndpoint), m_policy(policyName), m_proxyMode(CallRec::ProxyDetect), m_flags(0), m_priority(priority) { Toolkit::Instance()->SetRerouteCauses(m_rerouteCauses); if (!destEndpoint) { PTRACE(1, "Error: Route created with NULL endpoint!"); SNMP_TRAP(7, SNMPWarning, General, "Route created with NULL endpoint"); } } Route::Route( const PString & policyName, const H225_TransportAddress & destAddr, unsigned priority ) : m_destAddr(destAddr), m_policy(policyName), m_proxyMode(CallRec::ProxyDetect), m_flags(0), m_priority(priority) { Toolkit::Instance()->SetRerouteCauses(m_rerouteCauses); } Route::Route( const PString & policyName, const PIPSocket::Address & destIpAddr, WORD destPort, unsigned priority ) : m_destAddr(SocketToH225TransportAddr(destIpAddr, destPort)), m_policy(policyName), m_proxyMode(CallRec::ProxyDetect), m_flags(0), m_priority(priority) { Toolkit::Instance()->SetRerouteCauses(m_rerouteCauses); } PString Route::AsString() const { return AsDotString(m_destAddr) + " (policy: " + m_policy + ", proxy: " + PString(m_proxyMode) + ", flags: " + PString(m_flags) + ", Called-Station-Id: " + m_destNumber + ", Called-Station-Id-Out: " + m_destOutNumber + ", priority: " + PString(m_priority) + ")"; } bool Route::IsFailoverActive( unsigned cause ) const { cause = cause & 0x7f; return m_rerouteCauses[cause >> 3] & (1UL << (cause & 7)); } // class RoutingRequest RoutingRequest::RoutingRequest() : m_reason(-1), m_flags(0) { } RoutingRequest::RoutingRequest( const std::list &failedRoutes ) : m_reason(-1), m_flags(0), m_failedRoutes(failedRoutes) { } RoutingRequest::~RoutingRequest() { } bool RoutingRequest::AddRoute(const Route & route) { PIPSocket::Address addr; WORD port; if (!(route.m_destAddr.IsValid() && GetIPAndPortFromTransportAddr(route.m_destAddr, addr, port) && addr.IsValid() && port != 0)) { PTRACE(1, "ROUTING\tInvalid destination address: " << AsString(route.m_destAddr)); return false; } list::const_iterator i = m_failedRoutes.begin(); while (i != m_failedRoutes.end()) { if (i->m_destAddr == route.m_destAddr && i->m_policy == route.m_policy && i->m_routeId == route.m_routeId) { PTRACE(5, "ROUTING\tSkipping failed route " << route.AsString()); return true; } ++i; } m_routes.push_back(route); m_routes.sort(); // put routes in priority order return true; } bool RoutingRequest::GetFirstRoute(Route & route) { if (m_routes.empty()) return false; route = *m_routes.begin(); return true; } void RoutingRequest::RemoveAllRoutes() { m_routes.clear(); } bool RoutingRequest::GetGatewayDestination(H225_TransportAddress & gw ) const { PIPSocket::Address addr; if (!GetIPFromTransportAddr(m_gwDestination, addr)||!addr.IsValid()) return false; gw = m_gwDestination; return true; } // class AdmissionRequest template<> H225_ArrayOf_AliasAddress *AdmissionRequest::GetAliases() { return (m_request.HasOptionalField(H225_AdmissionRequest::e_destinationInfo) && m_request.m_destinationInfo.GetSize() > 0) ? &m_request.m_destinationInfo : NULL; } template<> void AdmissionRequest::SetAliases(H225_ArrayOf_AliasAddress & aliases) { m_request.IncludeOptionalField(H225_AdmissionRequest::e_destinationInfo); m_request.m_destinationInfo = aliases; } template<> const H225_TransportAddress *AdmissionRequest::GetDestIP() const { return (m_request.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) ? &m_request.m_destCallSignalAddress : NULL; } // class LocationRequest template<> H225_ArrayOf_AliasAddress *LocationRequest::GetAliases() { return (m_request.m_destinationInfo.GetSize() > 0) ? &m_request.m_destinationInfo : NULL; } template<> const H225_TransportAddress *LocationRequest::GetDestIP() const { return NULL; // TODO: check if one alias is transportID or h323ID that matches IP number ? } // class SetupRequest template<> H225_ArrayOf_AliasAddress *SetupRequest::GetAliases() { return (m_request.HasOptionalField(H225_Setup_UUIE::e_destinationAddress) && m_request.m_destinationAddress.GetSize() > 0) ? &m_request.m_destinationAddress : NULL; } template<> const H225_TransportAddress *SetupRequest::GetDestIP() const { return (m_request.HasOptionalField(H225_Setup_UUIE::e_destCallSignalAddress)) ? &m_request.m_destCallSignalAddress : NULL; } // class FacilityRequest template<> H225_ArrayOf_AliasAddress *FacilityRequest::GetAliases() { return (m_request.HasOptionalField(H225_Facility_UUIE::e_alternativeAliasAddress) && m_request.m_alternativeAliasAddress.GetSize() > 0) ? &m_request.m_alternativeAliasAddress : NULL; } template<> const H225_TransportAddress *FacilityRequest::GetDestIP() const { return (m_request.HasOptionalField(H225_Facility_UUIE::e_alternativeAddress)) ? &m_request.m_alternativeAddress : NULL; } bool Policy::Handle(SetupRequest& request) { if( IsActive() ) { const PString tagname = request.GetWrapper()->GetTagName(); const unsigned crv = request.GetWrapper()->GetCallReference(); PTRACE(5, "ROUTING\tChecking policy " << m_name << " for request " << tagname << " CRV=" << crv); if (OnRequest(request)) { PTRACE(5, "ROUTING\tPolicy " << m_name << " applied to the request " << tagname << " CRV=" << crv); return true; } } return m_next && m_next->Handle(request); } bool Policy::Handle(FacilityRequest& request) { if( IsActive() ) { const PString tagname = request.GetWrapper()->GetTagName(); const unsigned crv = request.GetWrapper()->GetCallReference(); PTRACE(5, "ROUTING\tChecking policy " << m_name << " for request " << tagname << " CRV=" << crv ); if (OnRequest(request)) { PTRACE(5, "ROUTING\tPolicy " << m_name << " applied to the request " << tagname << " CRV=" << crv); return true; } } return m_next && m_next->Handle(request); } void Policy::SetInstance(const PString & instance) { PString policyName = PString(m_name); if (!instance.IsEmpty()) { policyName = policyName + "::" + instance; m_name = *(PString *)policyName.Clone(); } m_iniSection = "Routing::" + policyName; LoadConfig(instance); } // class Analyzer Analyzer::Analyzer() : Singleton("Routing::Analyzer") { // OnReload is called by holder } Analyzer::~Analyzer() { WriteLock lock(m_reloadMutex); for (int i = 0; i < 4; ++i) DeleteObjectsInMap(m_rules[i]); } void Analyzer::OnReload() { WriteLock lock(m_reloadMutex); for (int i = 0; i < 4; ++i) { Rules & rules = m_rules[i]; DeleteObjectsInMap(rules); rules.clear(); PStringToString cfgs(GkConfig()->GetAllKeyValues(SectionName[i])); if (cfgs.GetSize() == 0) // no such a section? try default cfgs = GkConfig()->GetAllKeyValues(SectionName[4]); for (PINDEX j = 0; j < cfgs.GetSize(); ++j) { PString prefix = cfgs.GetKeyAt(j); if (prefix *= "default") prefix = "*"; PStringArray prefixes(prefix.Tokenise(",;|", false)); for (PINDEX k = 0; k < prefixes.GetSize(); ++k) rules[prefixes[k]] = Create(cfgs.GetDataAt(j).Trim()); PTRACE(1, SectionName[i] << " add policy " << cfgs.GetDataAt(j) << " for prefix " << prefix); } // default policy for backward compatibility if (rules.empty()) rules["*"] = Create("explicit,internal,parent,neighbor"); } } bool Analyzer::Parse(AdmissionRequest & request) { ReadLock lock(m_reloadMutex); request.SetRejectReason(H225_AdmissionRejectReason::e_calledPartyNotRegistered); Policy *policy = ChoosePolicy(request.GetAliases(), m_rules[0]); bool policyApplied = policy ? policy->HandleRas(request) : false; if (!policyApplied && request.HasRoutes()) { Route fallback; request.GetFirstRoute(fallback); const char * tagname = request.GetWrapper() ? request.GetWrapper()->GetTagName() : "unknown"; const unsigned seqnum = request.GetRequest().m_requestSeqNum.GetValue(); PTRACE(5, "ROUTING\t" << fallback.m_policy << " applied as fallback to the request " << tagname << ' ' << seqnum); } return policyApplied || request.HasRoutes(); } bool Analyzer::Parse(LocationRequest & request) { ReadLock lock(m_reloadMutex); request.SetRejectReason(H225_LocationRejectReason::e_requestDenied); Policy *policy = ChoosePolicy(request.GetAliases(), m_rules[1]); bool policyApplied = policy ? policy->HandleRas(request) : false; if (!policyApplied && request.HasRoutes()) { Route fallback; request.GetFirstRoute(fallback); const char * tagname = request.GetWrapper() ? request.GetWrapper()->GetTagName() : "unknown"; const unsigned seqnum = request.GetRequest().m_requestSeqNum.GetValue(); PTRACE(5, "ROUTING\t" << fallback.m_policy << " applied as fallback to the request " << tagname << ' ' << seqnum); } return policyApplied || request.HasRoutes(); } bool Analyzer::Parse(SetupRequest & request) { ReadLock lock(m_reloadMutex); request.SetRejectReason(H225_ReleaseCompleteReason::e_calledPartyNotRegistered); Policy *policy = ChoosePolicy(request.GetAliases(), m_rules[2]); bool policyApplied = policy ? policy->Handle(request) : false; if (!policyApplied && request.HasRoutes()) { Route fallback; request.GetFirstRoute(fallback); const PString tagname = request.GetWrapper()->GetTagName(); const unsigned crv = request.GetWrapper()->GetCallReference(); PTRACE(5, "ROUTING\t" << fallback.m_policy << " applied as fallback to the request " << tagname << " CRV=" << crv); } return policyApplied || request.HasRoutes(); } bool Analyzer::Parse(FacilityRequest & request) { ReadLock lock(m_reloadMutex); request.SetRejectReason(H225_ReleaseCompleteReason::e_calledPartyNotRegistered); Policy *policy = ChoosePolicy(request.GetAliases(), m_rules[3]); bool policyApplied = policy ? policy->Handle(request) : false; if (!policyApplied && request.HasRoutes()) { Route fallback; request.GetFirstRoute(fallback); const PString tagname = request.GetWrapper()->GetTagName(); const unsigned crv = request.GetWrapper()->GetCallReference(); PTRACE(5, "ROUTING\t" << fallback.m_policy << " applied as fallback to the request " << tagname << " CRV=" << crv); } return policyApplied || request.HasRoutes(); } Policy *Analyzer::Create(const PString & cfg) { return Policy::Create(cfg.ToLower().Tokenise(",;|", false)); } Policy *Analyzer::ChoosePolicy(const H225_ArrayOf_AliasAddress *aliases, Rules & rules) { // safeguard if we don't have any rules (eg. not yet initialized on startup) if (rules.empty()) return NULL; // use rules.begin() as the default policy // since "*" has the minimum key value Rules::iterator iter, biter, eiter; iter = biter = rules.begin(), eiter = rules.end(); if (aliases && aliases->GetSize() > 0) { for (PINDEX i = 0; i < aliases->GetSize(); ++i) { const H225_AliasAddress & alias = (*aliases)[i]; iter = rules.find(alias.GetTagName()); if (iter != eiter) break; PString destination(AsString(alias, false)); while (iter != biter) { --iter; // search in reverse order if (MatchPrefix(destination, iter->first) > 0) return iter->second; } } } return iter->second; } // class AliasesPolicy bool AliasesPolicy::OnRequest(AdmissionRequest & request) { H225_ArrayOf_AliasAddress *aliases = request.GetAliases(); return aliases && FindByAliases(request, *aliases); } bool AliasesPolicy::OnRequest(LocationRequest & request) { H225_ArrayOf_AliasAddress *aliases = request.GetAliases(); return aliases && FindByAliases(request, *aliases); } bool AliasesPolicy::OnRequest(SetupRequest & request) { H225_ArrayOf_AliasAddress *aliases = request.GetAliases(); return aliases && FindByAliases(request, *aliases); } bool AliasesPolicy::OnRequest(FacilityRequest & request) { H225_ArrayOf_AliasAddress *aliases = request.GetAliases(); return aliases && FindByAliases(request, *aliases); } std::map ExplicitPolicy::m_destMap; ExplicitPolicy::ExplicitPolicy() { m_name = "Explicit"; m_iniSection = "Routing::Explicit"; } void ExplicitPolicy::OnReload() { m_destMap.clear(); PStringToString mappings(GkConfig()->GetAllKeyValues("Routing::Explicit")); for (PINDEX i = 0; i < mappings.GetSize(); ++i) { PString src = mappings.GetKeyAt(i); PString dest = mappings.GetDataAt(i); H225_TransportAddress srcAddr; if (GetTransportAddress(src, GK_DEF_ENDPOINT_SIGNAL_PORT, srcAddr)) { if (!dest.IsEmpty()) { if (IsIPAddress(dest)) { // test parse IP, but store again as string H225_TransportAddress destAddr; if (GetTransportAddress(dest, GK_DEF_ENDPOINT_SIGNAL_PORT, destAddr)) { m_destMap[AsDotString(srcAddr, false)] = AsDotString(destAddr, false); } else { PTRACE(1, "Error parsing dest entry in [Routing::Explicit]: " << src << "=" << dest); SNMP_TRAP(7, SNMPError, Configuration, "Invalid [Routing::Explicit] configuration"); } } else { // store anything else as string, will be used as alias m_destMap[AsDotString(srcAddr, false)] = dest; } } else { PTRACE(1, "Error: Empty dest entry in [Routing::Explicit]: " << src << "="); SNMP_TRAP(7, SNMPError, Configuration, "Invalid [Routing::Explicit] configuration"); } } else { PTRACE(1, "Error parsing src entry in [Routing::Explicit]: " << src << "=" << dest); SNMP_TRAP(7, SNMPError, Configuration, "Invalid [Routing::Explicit] configuration"); } } } void ExplicitPolicy::MapDestination(H225_AdmissionRequest & arq) { H225_TransportAddress & addr = arq.m_destCallSignalAddress; PString orig = AsDotString(addr, false); // original IP without port std::map::const_iterator i = m_destMap.find(orig); if (i != m_destMap.end()) { PString newDest = i->second; if (IsIPAddress(newDest)) { // just rewrite the IP H225_TransportAddress destAddr; GetTransportAddress(newDest, GK_DEF_ENDPOINT_SIGNAL_PORT, destAddr); // ignore result, we have parsed these before addr = destAddr; } else { // delete IP and set a new destination alias arq.RemoveOptionalField(H225_AdmissionRequest::e_destCallSignalAddress); arq.IncludeOptionalField(H225_AdmissionRequest::e_destinationInfo); arq.m_destinationInfo.SetSize(1); H323SetAliasAddress(newDest, arq.m_destinationInfo[0]); } PTRACE(4, "[Routing::Explicit]: map destination " << orig << " to " << newDest); } } void ExplicitPolicy::MapDestination(H225_Setup_UUIE & setupBody) { H225_TransportAddress & addr = setupBody.m_destCallSignalAddress; PString orig = AsDotString(addr, false); // original IP without port std::map::const_iterator i = m_destMap.find(orig); if (i != m_destMap.end()) { PString newDest = i->second; if (IsIPAddress(newDest)) { // just rewrite the IP H225_TransportAddress destAddr; GetTransportAddress(newDest, GK_DEF_ENDPOINT_SIGNAL_PORT, destAddr); // ignore result, we have parsed these before addr = destAddr; } else { // delete IP and set a new destination alias setupBody.RemoveOptionalField(H225_Setup_UUIE::e_destCallSignalAddress); setupBody.IncludeOptionalField(H225_Setup_UUIE::e_destinationAddress); setupBody.m_destinationAddress.SetSize(1); H323SetAliasAddress(newDest, setupBody.m_destinationAddress[0]); } PTRACE(4, "[Routing::Explicit]: map destination " << orig << " to " << newDest); } } void ExplicitPolicy::MapDestination(H225_Facility_UUIE & facilityBody) { H225_TransportAddress & addr = facilityBody.m_alternativeAddress; PString orig = AsDotString(addr, false); // original IP without port std::map::const_iterator i = m_destMap.find(orig); if (i != m_destMap.end()) { PString newDest = i->second; if (IsIPAddress(newDest)) { // just rewrite the IP H225_TransportAddress destAddr; GetTransportAddress(newDest, GK_DEF_ENDPOINT_SIGNAL_PORT, destAddr); // ignore result, we have parsed these before addr = destAddr; } else { // delete IP and set a new destination alias facilityBody.RemoveOptionalField(H225_Facility_UUIE::e_alternativeAddress); facilityBody.IncludeOptionalField(H225_Facility_UUIE::e_alternativeAliasAddress); facilityBody.m_alternativeAliasAddress.SetSize(1); H323SetAliasAddress(newDest, facilityBody.m_alternativeAliasAddress[0]); } PTRACE(4, "[Routing::Explicit]: map destination " << orig << " to " << newDest); } } bool ExplicitPolicy::OnRequest(AdmissionRequest & request) { H225_AdmissionRequest & arq = request.GetRequest(); if (arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) { MapDestination(arq); // check if the mapping removed the destination IP if (!arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) return false; // TODO: also check if one of the aliases is a transport-ID ? Route route(m_name, arq.m_destCallSignalAddress); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(route.m_destAddr); #ifdef HAS_H46023 if (!route.m_destEndpoint && arq.HasOptionalField(H225_AdmissionRequest::e_genericData) && H460_FeatureSet(arq.m_genericData).HasFeature(24)) { H225_RasMessage ras; ras.SetTag(H225_RasMessage::e_admissionRequest); H225_AdmissionRequest & req = (H225_AdmissionRequest &)ras; req = arq; route.m_destEndpoint = RegistrationTable::Instance()->InsertRec(ras); } #endif return request.AddRoute(route); } return false; } bool ExplicitPolicy::OnRequest(SetupRequest & request) { H225_Setup_UUIE &setup = request.GetRequest(); if (setup.HasOptionalField(H225_Setup_UUIE::e_destCallSignalAddress)) { // don't map IP here, for Setup already done in OnSetup() Route route(m_name, setup.m_destCallSignalAddress); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(route.m_destAddr); return request.AddRoute(route); } // TODO: also check if one of the aliases is a transport-ID ? return false; } bool ExplicitPolicy::OnRequest(FacilityRequest & request) { H225_Facility_UUIE & facility = request.GetRequest(); if (facility.HasOptionalField(H225_Facility_UUIE::e_alternativeAddress)) { MapDestination(facility); // check if the mapping removed the destination IP if (!facility.HasOptionalField(H225_Facility_UUIE::e_alternativeAddress)) return false; Route route(m_name, facility.m_alternativeAddress); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(route.m_destAddr); return request.AddRoute(route); } // TODO: also check if one of the aliases is a transport-ID ? return false; } InternalPolicy::InternalPolicy() : roundRobin(Toolkit::AsBool(GkConfig()->GetString("RasSrv::ARQFeatures", "RoundRobinGateways", "1"))) { m_name = "Internal"; } bool InternalPolicy::OnRequest(AdmissionRequest & request) { H225_ArrayOf_AliasAddress *aliases = request.GetAliases(); if (aliases == NULL || !FindByAliases(request, *aliases)) return false; return true; } bool InternalPolicy::OnRequest(SetupRequest & request) { H225_ArrayOf_AliasAddress *aliases = request.GetAliases(); if (aliases == NULL || !FindByAliases(request, *aliases)) return false; return true; } bool InternalPolicy::FindByAliases( RoutingRequest &request, H225_ArrayOf_AliasAddress &aliases ) { list routes; RegistrationTable::Instance()->FindEndpoint(aliases, roundRobin, true, routes); list::iterator i = routes.begin(); while (i != routes.end()) { i->m_policy = m_name; request.AddRoute(*i++); } return !routes.empty(); } bool InternalPolicy::FindByAliases( LocationRequest& request, H225_ArrayOf_AliasAddress & aliases ) { // do not apply round robin selection for Location ReQuests list routes; if (RegistrationTable::Instance()->FindEndpoint(aliases, false, true, routes)) request.SetRejectReason(H225_LocationRejectReason::e_resourceUnavailable); list::iterator i = routes.begin(); while (i != routes.end()) { i->m_policy = m_name; request.AddRoute(*i++); } return !routes.empty(); } bool InternalPolicy::FindByAliases( SetupRequest& request, H225_ArrayOf_AliasAddress & aliases ) { list routes; if (RegistrationTable::Instance()->FindEndpoint(aliases, roundRobin, true, routes)) request.SetRejectReason(H225_ReleaseCompleteReason::e_gatewayResources); list::iterator i = routes.begin(); while (i != routes.end()) { i->m_policy = m_name; request.AddRoute(*i++); } return !routes.empty(); } bool InternalPolicy::FindByAliases( AdmissionRequest& request, H225_ArrayOf_AliasAddress & aliases ) { list routes; if (RegistrationTable::Instance()->FindEndpoint(aliases, roundRobin, true, routes)) request.SetRejectReason(H225_AdmissionRejectReason::e_resourceUnavailable); list::iterator i = routes.begin(); while (i != routes.end()) { i->m_policy = m_name; request.AddRoute(*i++); } return !routes.empty(); } ParentPolicy::ParentPolicy() { m_gkClient = RasServer::Instance()->GetGkClient(); m_name = "Parent"; } bool ParentPolicy::IsActive() const { return m_gkClient ? m_gkClient->IsRegistered() : false; } bool ParentPolicy::OnRequest(AdmissionRequest & arq_obj) { return m_gkClient->SendARQ(arq_obj); } bool ParentPolicy::OnRequest(LocationRequest & lrq_obj) { return m_gkClient->SendLRQ(lrq_obj); } bool ParentPolicy::OnRequest(SetupRequest & setup_obj) { return !(setup_obj.GetFlags() & RoutingRequest::e_fromParent) && m_gkClient->SendARQ(setup_obj); } bool ParentPolicy::OnRequest(FacilityRequest & facility_obj) { return !(facility_obj.GetFlags() & RoutingRequest::e_fromParent) && m_gkClient->SendARQ(facility_obj); } DNSPolicy::DNSPolicy() { m_name = "DNS"; m_iniSection = "Routing::DNS"; } void DNSPolicy::LoadConfig(const PString & instance) { m_resolveNonLocalLRQs = Toolkit::AsBool(GkConfig()->GetString(m_iniSection, "ResolveNonLocalLRQ", "1")); } bool DNSPolicy::DNSLookup(const PString & hostname, PIPSocket::Address & addr) const { struct addrinfo hints; struct addrinfo * result = NULL; memset(&hints, 0, sizeof(hints)); if (Toolkit::Instance()->IsIPv6Enabled()) { hints.ai_family = AF_UNSPEC; } else { hints.ai_family = AF_INET; } if (getaddrinfo(hostname, NULL, &hints, &result) != 0) return false; addr = PIPSocket::Address(result->ai_family, result->ai_addrlen, result->ai_addr); freeaddrinfo(result); return true; } bool DNSPolicy::FindByAliases(RoutingRequest & request, H225_ArrayOf_AliasAddress & aliases) { for (PINDEX i = 0; i < aliases.GetSize(); ++i) { // don't apply DNS to dialedDigits if (aliases[i].GetTag() == H225_AliasAddress::e_dialedDigits) continue; PString alias(AsString(aliases[i], FALSE)); PINDEX at = alias.Find('@'); PString domain = (at != P_MAX_INDEX) ? alias.Mid(at + 1) : alias; if (domain.Find("ip$") == 0) domain.Replace("ip$", "", false); PStringArray parts = SplitIPAndPort(domain, GK_DEF_ENDPOINT_SIGNAL_PORT); domain = parts[0]; WORD port = (WORD)parts[1].AsUnsigned(); PIPSocket::Address addr; if (DNSLookup(domain, addr) && addr.IsValid()) { H225_TransportAddress dest; if (!request.GetGatewayDestination(dest)) { dest = SocketToH225TransportAddr(addr, port); H323SetAliasAddress(alias.Left(at), aliases[i]); PTRACE(4, "ROUTING\tDNS policy resolves to " << alias.Left(at) << " @ " << AsDotString(dest)); } if (Toolkit::Instance()->IsGKHome(addr)) { // check if the domain is my IP, if so route to local endpoint if available H225_ArrayOf_AliasAddress find_aliases; find_aliases.SetSize(1); H323SetAliasAddress(alias.Left(at), find_aliases[0]); endptr ep = RegistrationTable::Instance()->FindByAliases(find_aliases); if (ep) { dest = ep->GetCallSignalAddress(); } else { continue; // can't route this alias locally, try next alias } } request.SetFlag(RoutingRequest::e_aliasesChanged); Route route(m_name, dest); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(dest); request.AddRoute(route); return true; } } return false; } bool DNSPolicy::FindByAliases(LocationRequest & request, H225_ArrayOf_AliasAddress & aliases) { for (PINDEX i = 0; i < aliases.GetSize(); ++i) { // don't apply DNS to dialedDigits if (aliases[i].GetTag() == H225_AliasAddress::e_dialedDigits) continue; PString alias(AsString(aliases[i], FALSE)); PINDEX at = alias.Find('@'); PString domain = (at != P_MAX_INDEX) ? alias.Mid(at + 1) : alias; if (domain.Find("ip$") == 0) domain.Replace("ip$", "", false); PStringArray parts = SplitIPAndPort(domain, GK_DEF_ENDPOINT_SIGNAL_PORT); domain = parts[0]; WORD port = (WORD)parts[1].AsUnsigned(); PIPSocket::Address addr; if (DNSLookup(domain, addr) && addr.IsValid()) { H225_TransportAddress dest = SocketToH225TransportAddr(addr, port); if (Toolkit::Instance()->IsGKHome(addr)) { // only apply DNS policy to LRQs that resolve locally H225_ArrayOf_AliasAddress find_aliases; find_aliases.SetSize(1); H323SetAliasAddress(alias.Left(at), find_aliases[0]); endptr ep = RegistrationTable::Instance()->FindByAliases(find_aliases); if (ep) { if (!(RasServer::Instance()->IsGKRouted())) { // in direct mode, send call directly to EP dest = ep->GetCallSignalAddress(); } Route route(m_name, dest); route.m_destEndpoint = ep; request.AddRoute(route); request.SetFlag(RoutingRequest::e_aliasesChanged); // remove the domain name part H323SetAliasAddress(alias.Left(at), aliases[i]); PTRACE(4, "ROUTING\tDNS policy resolves to " << alias.Left(at)); return true; } } else if (m_resolveNonLocalLRQs) { Route route(m_name, dest); request.AddRoute(route); PTRACE(4, "ROUTING\tDNS policy resolves to " << domain); return true; } } } if (!m_resolveNonLocalLRQs) { PTRACE(4, "ROUTING\tPolicy DNS configured to only route LRQs that resolve locally"); } return false; } VirtualQueue::VirtualQueue() : m_active(false), m_requestTimeout(DEFAULT_ROUTE_REQUEST_TIMEOUT*1000) { } VirtualQueue::~VirtualQueue() { m_listMutex.Wait(); int numrequests = m_pendingRequests.size(); if( numrequests ) { PTRACE(1, "VQueue\tDestroying virtual queue with " << numrequests << " pending requests"); } RouteRequests::iterator i = m_pendingRequests.begin(); while (i != m_pendingRequests.end()) { RouteRequest *r = *i++; r->m_sync.Signal(); } m_listMutex.Signal(); // wait a moment to give a chance to pending requests to cleanup if( numrequests ) { PThread::Sleep(500); } } void VirtualQueue::OnReload() { PWaitAndSignal lock(m_listMutex); m_requestTimeout = GkConfig()->GetInteger( CTIsection, GkConfig()->HasKey(CTIsection, "RequestTimeout") ? "RequestTimeout" : "CTI_Timeout", DEFAULT_ROUTE_REQUEST_TIMEOUT ) * 1000; m_requestTimeout = PMAX((long)100,m_requestTimeout); // min wait: 100 msec m_active = false; m_virtualQueueAliases.RemoveAll(); PString vqueues = GkConfig()->GetString(CTIsection, "VirtualQueueAliases", ""); if( vqueues.IsEmpty() ) // backward compatibility vqueues = GkConfig()->GetString(CTIsection, "VirtualQueue", ""); if( !vqueues.IsEmpty() ) { m_virtualQueueAliases = vqueues.Tokenise(" ,;\t", false); if( m_virtualQueueAliases.GetSize() > 0 ) { PTRACE(2, "VQueue\t(CTI) Virtual queues enabled (aliases:" << vqueues << "), request timeout: " << m_requestTimeout/1000 << " s"); m_active = true; } } m_virtualQueuePrefixes.RemoveAll(); vqueues = GkConfig()->GetString(CTIsection, "VirtualQueuePrefixes", ""); if( !vqueues.IsEmpty() ) { m_virtualQueuePrefixes = vqueues.Tokenise(" ,;\t", false); if( m_virtualQueuePrefixes.GetSize() > 0 ) { PTRACE(2, "VQueue\t(CTI) Virtual queues enabled (prefixes:" << vqueues << "), request timeout: " << m_requestTimeout/1000 << " s"); m_active = true; } } m_virtualQueueRegex = GkConfig()->GetString(CTIsection, "VirtualQueueRegex", ""); if( !m_virtualQueueRegex.IsEmpty() ) { // check if regex is valid PRegularExpression regex(m_virtualQueueRegex, PRegularExpression::Extended); if(regex.GetErrorCode() != PRegularExpression::NoError) { PTRACE(2, "Error '"<< regex.GetErrorText() <<"' compiling regex: " << m_virtualQueueRegex); SNMP_TRAP(7, SNMPError, Configuration, "Invalid " + PString(CTIsection) + " configuration: compiling RegEx failed"); } else { PTRACE(2, "VQueue\t(CTI) Virtual queues enabled (regex:" << m_virtualQueueRegex << "), request timeout: " << m_requestTimeout/1000 << " s"); m_active = true; } } if( !m_active ) { PTRACE(2, "VQueue\t(CTI) Virtual queues disabled - no virtual queues configured"); } } bool VirtualQueue::SendRouteRequest( /// source IP of the request (endpoint for ARQ, gatekeeper for LRQ) const PString& source, /// calling endpoint const PString& epid, /// CRV (Call Reference Value) of the call associated with this request unsigned crv, /// destination (virtual queue) aliases as specified /// by the calling endpoint (modified by this function on successful return) H225_ArrayOf_AliasAddress* destinationInfo, /// destination (virtual queue) aliases as specified /// by the calling endpoint (modified by this function on successful return) PString* callSigAdr, /// bind IP for BindAndRouteToGateway PString* bindIP, /// caller ID PString* callerID, /// should the call be rejected modified by this function on return) bool & reject, /// actual virtual queue name (should be present in destinationInfo too) const PString& vqueue, /// a sequence of aliases for the calling endpoint /// (in the "alias:type[=alias:type]..." format) const PString& sourceInfo, /// the callID as string const PString& callID, /// the called IP for unregistered calls const PString& calledip, /// vendor string of caller const PString& vendorString ) { bool result = false; bool duprequest = false; if (RouteRequest *r = InsertRequest(epid, crv, callID, destinationInfo, callSigAdr, bindIP, callerID, duprequest)) { PString cid = callID; cid.Replace(" ", "-", true); PString msg = "RouteRequest|" + source + "|" + epid + "|" + PString(crv) + "|" + vqueue + "|" + sourceInfo + "|" + cid + "|" + calledip + "|" + vendorString + ";"; // signal RouteRequest to the status line only once if( duprequest ) { PTRACE(4, "VQueue\tDuplicate request: "<SignalStatus(msg + "\r\n", STATUS_TRACE_LEVEL_ROUTEREQ); } // wait for an answer from the status line (routetoalias,routetogateway,routereject) result = r->m_sync.Wait(m_requestTimeout); reject = r->m_reject; // set reject status m_listMutex.Wait(); m_pendingRequests.remove(r); m_listMutex.Signal(); if( !result ) { PTRACE(5, "VQueue\tRoute request (EPID: " << r->m_callingEpId << ", CRV=" << r->m_crv << ") timed out"); } delete r; } return result; } bool VirtualQueue::IsDestinationVirtualQueue( const PString& destinationAlias /// alias to be matched ) const { PWaitAndSignal lock(m_listMutex); PINDEX i; for (i = 0; i < m_virtualQueueAliases.GetSize(); ++i) if (m_virtualQueueAliases[i] == destinationAlias) return true; for (i = 0; i < m_virtualQueuePrefixes.GetSize(); ++i) if (destinationAlias.Find(m_virtualQueuePrefixes[i]) == 0) return true; return (!m_virtualQueueRegex.IsEmpty()) && Toolkit::MatchRegex(destinationAlias,m_virtualQueueRegex); } bool VirtualQueue::RouteToAlias( /// aliases for the routing target (an agent that the call will be routed to) /// that will replace the original destination info const H225_ArrayOf_AliasAddress & agent, /// ip that will replace the destinationCallSignalAddress (RouteToGateway) /// used only if set (!= NULL) const PString & destinationip, /// identifier of the endpoint associated with the route request const PString & callingEpId, /// CRV of the call associated with the route request unsigned crv, /// callID of the call associated with the route request const PString & callID, // outgoing IP or empty const PString & bindIP, // caller ID or empty const PString & callerID, /// should this call be rejected bool reject ) { PWaitAndSignal lock(m_listMutex); // signal the command to each pending request bool foundrequest = false; RouteRequests::iterator i = m_pendingRequests.begin(); while (!foundrequest && (i != m_pendingRequests.end())) { RouteRequest *r = *i; bool match = ((r->m_callingEpId == callingEpId) && (r->m_crv == crv)); if (!r->m_callID.IsEmpty() && !callID.IsEmpty()) { // backward compatibility: only check if set match = (r->m_callID == callID); } if (match) { if (!reject) { // replace virtual queue aliases info with agent aliases // TODO: add alias field in ARQ, LRQ if not present, but call is routed to alias ? // TODO: remove alias field if new agent field is size 0 ?? if (r->m_agent) *(r->m_agent) = agent; if (!destinationip.IsEmpty()) // RouteToGateway *(r->m_callsignaladdr) = destinationip; *(r->m_sourceIP) = bindIP; // BindAndRouteToGateway *(r->m_callerID) = callerID; } r->m_reject = reject; r->m_sync.Signal(); if (!foundrequest) { foundrequest = true; if (!reject) { PTRACE(2, "VQueue\tRoute request (EPID:" << callingEpId << ", CRV=" << crv << ") accepted by agent " << AsString(agent)); } else { PTRACE(2, "VQueue\tRoute request (EPID:" << callingEpId << ", CRV=" << crv << ") rejected"); } } } ++i; } if( !foundrequest ) { PTRACE(4, "VQueue\tPending route request (EPID:" << callingEpId << ", CRV=" << crv << ") not found - ignoring RouteToAlias / RouteToGateway / BindAndRouteToGateway command"); } return foundrequest; } bool VirtualQueue::RouteToAlias( /// alias for the routing target that /// will replace the original destination info const PString& targetAlias, /// will replace the original destinationCallSignallAddress const PString& destinationIp, /// identifier of the endpoint associated with the route request const PString& callingEpId, /// CRV for the call associated with the route request unsigned crv, /// callID of the call associated with the route request const PString& callID, // outgoing IP or empty const PString& bindIP, // callerID or empty const PString& callerID, /// should this call be rejected bool reject ) { H225_ArrayOf_AliasAddress alias; if (targetAlias != "" && targetAlias != "-") { alias.SetSize(1); H323SetAliasAddress(targetAlias, alias[0]); } return RouteToAlias(alias, destinationIp, callingEpId, crv, callID, bindIP, callerID, reject); } bool VirtualQueue::RouteReject( /// identifier of the endpoint associated with the route request const PString& callingEpId, /// CRV of the call associated with the route request unsigned crv, /// callID of the call associated with the route request const PString& callID ) { H225_ArrayOf_AliasAddress nullAgent; return RouteToAlias(nullAgent, "", callingEpId, crv, callID, "", "", true); } VirtualQueue::RouteRequest* VirtualQueue::InsertRequest( /// identifier for the endpoint associated with this request const PString& callingEpId, /// CRV for the call associated with this request unsigned crv, /// callID for the call associated with this request const PString& callID, /// a pointer to an array to be filled with agent aliases /// when the routing decision has been made H225_ArrayOf_AliasAddress* agent, /// a pointer to a string to be filled with a destinationCallSignalAddress /// when the routing decision has been made (optional) PString* callSigAdr, /// bind IP for BindAndRouteToGateway PString* bindIP, /// caller ID PString* callerID, /// set by the function to true if another route request for the same /// call is pending bool& duplicate ) { duplicate = false; PWaitAndSignal lock(m_listMutex); // check if another route requests for the same EPID,CRV are pending int duprequests = 0; RouteRequests::iterator i = m_pendingRequests.begin(); while (i != m_pendingRequests.end()) { RouteRequest *r = *i; if (r->m_callingEpId == callingEpId && r->m_crv == crv && r->m_callID == callID) duprequests++; ++i; } if( duprequests ) { duplicate = true; PTRACE(5, "VQueue\tRoute request (EPID: " << callingEpId << ", CRV=" << crv << ") is already active - duplicate requests" " waiting: " << duprequests); } // insert the new pending route request RouteRequest* r = new RouteRequest(callingEpId, crv, callID, agent, callSigAdr, bindIP, callerID); m_pendingRequests.push_back(r); return r; } VirtualQueuePolicy::VirtualQueuePolicy() { m_vqueue = RasServer::Instance()->GetVirtualQueue(); m_name = "VirtualQueue"; } bool VirtualQueuePolicy::IsActive() const { return m_vqueue ? m_vqueue->IsActive() : false; } bool VirtualQueuePolicy::OnRequest(AdmissionRequest & request) { bool reject = false; H225_ArrayOf_AliasAddress *aliases = NULL; PString vq = ""; if ((aliases = request.GetAliases())) vq = AsString((*aliases)[0], FALSE); if (m_vqueue->IsDestinationVirtualQueue(vq)) { H225_AdmissionRequest & arq = request.GetRequest(); PTRACE(5, "Routing\tPolicy " << m_name << " destination matched " "a virtual queue " << vq << " (ARQ " << arq.m_requestSeqNum.GetValue() << ')'); endptr ep = RegistrationTable::Instance()->FindByEndpointId(arq.m_endpointIdentifier); // should not be null if (ep) { PString source = AsDotString(ep->GetCallSignalAddress()); PString epid = ep->GetEndpointIdentifier().GetValue(); PString * callSigAdr = new PString(); PString * bindIP = new PString(); PString * callerID = new PString(); PString calledIP = "unknown"; PString vendorInfo; if (arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) { calledIP = AsDotString(arq.m_destCallSignalAddress, false); } if (ep->GetEndpointType().HasOptionalField(H225_EndpointType::e_vendor)) { if (ep->GetEndpointType().m_vendor.HasOptionalField(H225_VendorIdentifier::e_productId)) { vendorInfo += ep->GetEndpointType().m_vendor.m_productId.AsString(); } if (ep->GetEndpointType().m_vendor.HasOptionalField(H225_VendorIdentifier::e_versionId)) { vendorInfo += ep->GetEndpointType().m_vendor.m_versionId.AsString(); } } if (m_vqueue->SendRouteRequest(source, epid, unsigned(arq.m_callReferenceValue), aliases, callSigAdr, bindIP, callerID, reject, vq, AsString(arq.m_srcInfo), AsString(arq.m_callIdentifier.m_guid), calledIP, vendorInfo)) request.SetFlag(RoutingRequest::e_aliasesChanged); if (reject) { request.SetFlag(RoutingRequest::e_Reject); } request.SetSourceIP(*bindIP); request.SetCallerID(*callerID); if (!callSigAdr->IsEmpty()) { if (!arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) { arq.IncludeOptionalField(H225_AdmissionRequest::e_destCallSignalAddress); } PStringArray adr_parts = SplitIPAndPort(*callSigAdr, GK_DEF_ENDPOINT_SIGNAL_PORT); PString ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); arq.m_destCallSignalAddress = SocketToH225TransportAddr(ip, port); } delete callSigAdr; delete bindIP; delete callerID; } // the trick: if empty, the request is rejected // so we return true to terminate the routing // decision process, otherwise the aliases is // rewritten, we return false to let subsequent // policies determine the request if (m_next == NULL || reject) return true; } return false; } bool VirtualQueuePolicy::OnRequest(LocationRequest & request) { bool reject = false; if (H225_ArrayOf_AliasAddress *aliases = request.GetAliases()) { const PString vq(AsString((*aliases)[0], false)); if (m_vqueue->IsDestinationVirtualQueue(vq)) { H225_LocationRequest & lrq = request.GetRequest(); PTRACE(5, "Routing\tPolicy " << m_name << " destination matched " "a virtual queue " << vq << " (LRQ " << lrq.m_requestSeqNum.GetValue() << ')'); // only use vqueue if sender is able to handle changed destination if (!lrq.HasOptionalField(H225_LocationRequest::e_canMapAlias) || !lrq.m_canMapAlias) { PTRACE(5, "Sender can't map destination alias, skipping virtual queue"); return false; } PString source = AsDotString(lrq.m_replyAddress); PString epid = lrq.m_endpointIdentifier.GetValue(); if (epid.IsEmpty()) { epid = lrq.m_gatekeeperIdentifier.GetValue(); if (lrq.HasOptionalField(H225_LocationRequest::e_sourceInfo) && (lrq.m_sourceInfo.GetSize() > 0)) epid += "_" + AsString(lrq.m_sourceInfo, false); } if (epid.IsEmpty()) { epid = "unknown"; // make sure its not empty } PString * callSigAdr = new PString(); /* unused for LRQs */ PString * bindIP = new PString(); PString * callerID = new PString(); PString sourceInfo = ""; if (lrq.HasOptionalField(H225_LocationRequest::e_sourceInfo) && (lrq.m_sourceInfo.GetSize() > 0)) sourceInfo = AsString(lrq.m_sourceInfo); PString callID = "-"; /* not available for LRQs */ if (m_vqueue->SendRouteRequest(source, epid, unsigned(lrq.m_requestSeqNum), aliases, callSigAdr, bindIP, callerID, reject, vq, sourceInfo, callID)) request.SetFlag(RoutingRequest::e_aliasesChanged); if (reject) { request.SetFlag(RoutingRequest::e_Reject); } request.SetSourceIP(*bindIP); request.SetCallerID(*callerID); if (!reject && !callSigAdr->IsEmpty()) { // 'explicit' policy can't handle LRQs, so we do it directly PStringArray adr_parts = SplitIPAndPort(*callSigAdr, GK_DEF_ENDPOINT_SIGNAL_PORT); PString ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); Route route("vqueue", SocketToH225TransportAddr(ip, port)); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr( route.m_destAddr ); request.AddRoute(route); delete callSigAdr; delete bindIP; delete callerID; return true; // stop processing } delete callSigAdr; delete bindIP; delete callerID; // the trick: if empty, the request is rejected // so we return true to terminate the routing // decision process, otherwise the aliases is // rewritten, we return false to let subsequent // policies determine the request if (m_next == NULL || reject) return true; } } return false; } bool VirtualQueuePolicy::OnRequest(SetupRequest & request) { bool reject = false; H225_ArrayOf_AliasAddress * aliases = new H225_ArrayOf_AliasAddress; aliases->SetSize(1); PString vq = ""; if (request.GetAliases()) { vq = AsString((*request.GetAliases())[0], false); } if (m_vqueue->IsDestinationVirtualQueue(vq)) { H225_Setup_UUIE & setup = request.GetRequest(); PString callerip = AsDotString(setup.m_sourceCallSignalAddress); PString epid = "unregistered"; const unsigned crv = request.GetWrapper()->GetCallReference(); PString * callSigAdr = new PString(); PString * bindIP = new PString(); PString * callerID = new PString(); PString callid = AsString(setup.m_callIdentifier.m_guid); H225_AliasAddress srcAlias; // convert caller string back to alias to get alias type H323SetAliasAddress(request.GetCallingStationId(), srcAlias); PString src = AsString(srcAlias); PIPSocket::Address localAddr; WORD localPort; request.GetWrapper()->GetLocalAddr(localAddr, localPort); PString calledIP = localAddr; PString vendorInfo; if (setup.m_sourceInfo.HasOptionalField(H225_EndpointType::e_vendor)) { if (setup.m_sourceInfo.m_vendor.HasOptionalField(H225_VendorIdentifier::e_productId)) { vendorInfo += setup.m_sourceInfo.m_vendor.m_productId.AsString(); } if (setup.m_sourceInfo.m_vendor.HasOptionalField(H225_VendorIdentifier::e_versionId)) { vendorInfo += setup.m_sourceInfo.m_vendor.m_versionId.AsString(); } vendorInfo.Replace("|", "", true); } PTRACE(5, "Routing\tPolicy " << m_name << " destination matched " "a virtual queue " << vq << " (Setup " << crv << ')'); if (m_vqueue->SendRouteRequest(callerip, epid, crv, aliases, callSigAdr, bindIP, callerID, reject, vq, src, callid, calledIP, vendorInfo)) request.SetFlag(RoutingRequest::e_aliasesChanged); if (reject) { request.SetFlag(RoutingRequest::e_Reject); } request.SetSourceIP(*bindIP); request.SetCallerID(*callerID); if (!reject && callSigAdr->IsEmpty()) { if (!setup.HasOptionalField(H225_Setup_UUIE::e_destinationAddress)) { setup.IncludeOptionalField(H225_Setup_UUIE::e_destinationAddress); } setup.m_destinationAddress = *aliases; } if (!reject && !callSigAdr->IsEmpty()) { if (!setup.HasOptionalField(H225_Setup_UUIE::e_destinationAddress)) { setup.IncludeOptionalField(H225_Setup_UUIE::e_destinationAddress); } setup.m_destinationAddress = *aliases; if (!setup.HasOptionalField(H225_Setup_UUIE::e_destCallSignalAddress)) { setup.IncludeOptionalField(H225_Setup_UUIE::e_destCallSignalAddress); } PStringArray adr_parts = SplitIPAndPort(*callSigAdr, GK_DEF_ENDPOINT_SIGNAL_PORT); PString ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); setup.m_destCallSignalAddress = SocketToH225TransportAddr(ip, port); } delete callSigAdr; delete bindIP; delete callerID; // the trick: if empty, the request is rejected // so we return true to terminate the routing // decision process, otherwise the aliases is // rewritten, we return false to let subsequent // policies determine the request if (m_next == NULL || reject) { delete aliases; return true; } } delete aliases; return false; } struct PrefixGreater : public binary_function { bool operator()(const NumberAnalysisPolicy::PrefixEntry &e1, const NumberAnalysisPolicy::PrefixEntry &e2) const { if (e1.m_prefix.size() == e2.m_prefix.size()) return e1.m_prefix > e2.m_prefix; else return e1.m_prefix.size() > e2.m_prefix.size(); } }; NumberAnalysisPolicy::NumberAnalysisPolicy() { m_name = "NumberAnalysis"; m_iniSection = "Routing::NumberAnalysis"; } void NumberAnalysisPolicy::LoadConfig(const PString & instance) { PStringToString kv = GkConfig()->GetAllKeyValues(m_iniSection); m_prefixes.resize(kv.GetSize()); for (PINDEX i = 0; i < kv.GetSize(); i++) { const PString &val = kv.GetDataAt(i); m_prefixes[i].m_prefix = string((const char*)(kv.GetKeyAt(i))); const PINDEX sepIndex = val.Find(':'); if (sepIndex == P_MAX_INDEX) { m_prefixes[i].m_minLength = val.AsUnsigned(); m_prefixes[i].m_maxLength = -1; } else { m_prefixes[i].m_minLength = val.Left(sepIndex).AsUnsigned(); m_prefixes[i].m_maxLength = val.Mid(sepIndex + 1).AsUnsigned(); } } stable_sort(m_prefixes.begin(), m_prefixes.end(), PrefixGreater()); PTRACE(5, "ROUTING\t" << m_name << " policy loaded with " << m_prefixes.size() << " prefix entries"); if (PTrace::CanTrace(6)) { ostream &strm = PTrace::Begin(6, __FILE__, __LINE__); strm << "ROUTING\t" << m_name << " policy prefixes:" << endl; for (unsigned i = 0; i < m_prefixes.size(); i++) strm << "\t" << m_prefixes[i].m_prefix.c_str() << " => min len: " << m_prefixes[i].m_minLength << ", max len: " << m_prefixes[i].m_maxLength << endl; PTrace::End(strm); } } bool NumberAnalysisPolicy::OnRequest(AdmissionRequest & request) { H225_ArrayOf_AliasAddress *aliases = request.GetAliases(); if (aliases == NULL) return false; for (PINDEX a = 0; a < aliases->GetSize(); a++) { const H225_AliasAddress &alias = (*aliases)[a]; if (alias.GetTag() == H225_AliasAddress::e_dialedDigits || alias.GetTag() == H225_AliasAddress::e_partyNumber) { const PString s = AsString(alias, FALSE); for (unsigned i = 0; i < m_prefixes.size(); i++) if (MatchPrefix(s, m_prefixes[i].m_prefix.c_str()) != 0) { if (s.GetLength() < m_prefixes[i].m_minLength) { request.RemoveAllRoutes(); request.SetRejectReason(H225_AdmissionRejectReason::e_incompleteAddress); return true; } else if (m_prefixes[i].m_maxLength >= 0 && s.GetLength() > m_prefixes[i].m_maxLength) { request.RemoveAllRoutes(); request.SetRejectReason(H225_AdmissionRejectReason::e_undefinedReason); return true; } return false; } } } return false; } bool NumberAnalysisPolicy::OnRequest(SetupRequest & request) { H225_ArrayOf_AliasAddress *aliases = request.GetAliases(); if (aliases == NULL) return false; for (PINDEX a = 0; a < aliases->GetSize(); ++a) { const H225_AliasAddress &alias = (*aliases)[a]; if (alias.GetTag() == H225_AliasAddress::e_dialedDigits || alias.GetTag() == H225_AliasAddress::e_partyNumber) { const PString s = AsString(alias, FALSE); for (unsigned i = 0; i < m_prefixes.size(); ++i) if (MatchPrefix(s, m_prefixes[i].m_prefix.c_str()) != 0) { if (s.GetLength() < m_prefixes[i].m_minLength) { request.RemoveAllRoutes(); request.SetRejectReason(H225_ReleaseCompleteReason::e_badFormatAddress); return true; } else if (m_prefixes[i].m_maxLength >= 0 && s.GetLength() > m_prefixes[i].m_maxLength) { request.RemoveAllRoutes(); request.SetRejectReason(H225_ReleaseCompleteReason::e_badFormatAddress); return true; } return false; } } } return false; } ENUMPolicy::ENUMPolicy() { m_name = "ENUM"; m_iniSection = "Routing::ENUM"; m_resolveLRQs = Toolkit::AsBool(GkConfig()->GetString(m_iniSection, "ResolveLRQ", "0")); m_enum_schema.SetAt("E2U+h323", ""); } void ENUMPolicy::LoadConfig(const PString & instance) { if (instance.IsEmpty()) return; m_enum_schema.RemoveAll(); m_enum_schema = GkConfig()->GetAllKeyValues(m_iniSection); } bool ENUMPolicy::FindByAliases(RoutingRequest & request, H225_ArrayOf_AliasAddress & aliases) { #if P_DNS for (PINDEX i = 0; i < m_enum_schema.GetSize(); ++i) { PString enum_schema = m_enum_schema.GetKeyAt(i); PString gwDestination = m_enum_schema.GetDataAt(i); PBoolean changed = false; for (PINDEX j = 0; j < aliases.GetSize(); ++j) { if (!FindByAliasesInternal(enum_schema, request, aliases, changed)) continue; if (!gwDestination && changed) { // If we have a gateway destination and changed PStringArray parts = SplitIPAndPort(gwDestination, GK_DEF_ENDPOINT_SIGNAL_PORT); PString dom = parts[0]; PIPSocket::Address addr; if (PIPSocket::GetHostAddress(dom, addr)) dom = addr.AsString(); WORD port = (WORD)parts[1].AsUnsigned(); H225_TransportAddress dest; if (!GetTransportAddress(dom, port, dest)) { PTRACE(4, "ROUTING\tPolicy " << m_name << " " << enum_schema << " Could not resolve " << gwDestination); return false; } PString alias(AsString(aliases[j], FALSE)); int at = alias.Find('@'); PString domain = alias.Mid(at+1); if (IsIPAddress(domain) || (domain.FindRegEx(PRegularExpression(":[0-9]+$", PRegularExpression::Extended)) != P_MAX_INDEX)) { // add a route and stop going any further PTRACE(4, "ROUTING\tPolicy " << m_name << " " << enum_schema << " set destination for " << alias << " to " << AsString(dest)); Route * route = new Route(m_name, dest); route->m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(dest); request.AddRoute(*route); delete route; return true; } PTRACE(4, "ROUTING\tPolicy " << m_name << " " << enum_schema << " store destination for " << alias << " to " << AsString(dest)); request.SetGatewayDestination(dest); } changed = false; return false; } } #endif return false; } bool ENUMPolicy::FindByAliasesInternal(const PString & schema, RoutingRequest & request, H225_ArrayOf_AliasAddress & aliases, PBoolean & changed) { #if P_DNS for (PINDEX i = 0; i < aliases.GetSize(); ++i) { PString alias(AsString(aliases[i], FALSE)); if (alias.Left(2) *= "00") { // Check if not GDS number - SH PTRACE(4, "\t" << m_name << " " << schema << " Ignored " << alias << " Not ENUM format."); continue; } // make sure the number has only digits alias.Replace("+","", true); alias.Replace("*","", true); alias.Replace("#","", true); alias.Replace(",","", true); while (alias.Left(1) *= "0") // ENUM registries expect aliases without leading zeros alias = alias.Mid(1); PINDEX j; for (j = 0; j < alias.GetLength(); ++j) if (!isdigit(static_cast(alias[j]))) break; if (j >= alias.GetLength()) { PString str; if (PDNS::ENUMLookup(alias, schema, str)) { // Remove any + or URI Schema at the front PINDEX at = str.Find('@'); PINDEX sch = str.Find(':'); if (sch > 0 && sch < at) str = str.Mid(sch+1); str.Replace("+","", true); PTRACE(4, "\t" << m_name << " " << schema << " converted remote party " << alias << " to " << str); request.SetFlag(RoutingRequest::e_aliasesChanged); H323SetAliasAddress(str, aliases[i]); changed = true; return true; } } } #else PTRACE(4, "\t" << m_name << " policy unavailable as no DNS support."); #endif return false; } bool ENUMPolicy::FindByAliases(LocationRequest & request, H225_ArrayOf_AliasAddress & aliases) { if (m_resolveLRQs) { return FindByAliases((RoutingRequest&)request, aliases); } else { PTRACE(4, "ROUTING\tPolicy " << m_name << " configured not to resolve LRQs"); return false; } } DestinationRoutes::DestinationRoutes() { m_endChain = false; m_reject = false; m_rejectReason = 0; m_aliasesChanged = false; } void DestinationRoutes::AddRoute(const Route & route, bool endChain) { if (endChain) m_endChain = true; // check if route already exists, only use highest prio route (== lowest value) list::iterator it = m_routes.begin(); while (it != m_routes.end()) { if (it->m_destAddr == route.m_destAddr) { PTRACE(5, "ROUTING\tSkipping existing route route " << route.AsString()); // just update prio if we are lower if (route.GetPriority() < it->GetPriority()) { PTRACE(5, "ROUTING\tOnly update priority"); it->SetPriority(route.GetPriority()); } return; } ++it; } m_routes.push_back(route); } DynamicPolicy::DynamicPolicy() { m_active = false; } bool DynamicPolicy::OnRequest(AdmissionRequest & request) { H225_ArrayOf_AliasAddress *aliases = request.GetAliases(); H225_AdmissionRequest & arq = request.GetRequest(); endptr ep = RegistrationTable::Instance()->FindByEndpointId(arq.m_endpointIdentifier); // should not be null if (ep) { PString source = AsDotString(ep->GetCallSignalAddress()); PString calledAlias = ""; if (aliases && !aliases->GetSize() == 0) calledAlias = AsString((*aliases)[0], FALSE); PString calledIP = ""; /* not available for ARQs */ PString caller = AsString(arq.m_srcInfo, FALSE); PString callingStationId = request.GetCallingStationId(); PString callid = AsString(arq.m_callIdentifier.m_guid); PString messageType = "ARQ"; PString clientauthid = request.GetClientAuthId(); DestinationRoutes destination; RunPolicy( /* in */ source, calledAlias, calledIP, caller, callingStationId, callid, messageType, clientauthid, /* out: */ destination); if (destination.m_routes.empty() && !ResolveRoute(request,destination)) destination.SetRejectCall(true); if (destination.RejectCall()) { destination.SetChangedAliases(false); request.SetFlag(RoutingRequest::e_Reject); request.SetRejectReason(destination.GetRejectReason()); } if (destination.ChangeAliases()) { request.SetFlag(RoutingRequest::e_aliasesChanged); arq.m_destinationInfo = destination.GetNewAliases(); } if (!destination.m_routes.empty() && destination.m_routes.front().GetPriority() <= DEFAULT_ROUTE_PRIORITY) { request.SetFlag(RoutingRequest::e_aliasesChanged); if (!arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) { arq.IncludeOptionalField(H225_AdmissionRequest::e_destCallSignalAddress); } arq.m_destCallSignalAddress = destination.m_routes.front().m_destAddr; } while (!destination.m_routes.empty()) { request.AddRoute(destination.m_routes.front()); destination.m_routes.pop_front(); } if (m_next == NULL || destination.EndPolicyChain()) return true; } return false; } bool DynamicPolicy::OnRequest(LocationRequest & request) { H225_ArrayOf_AliasAddress *aliases = request.GetAliases(); H225_LocationRequest & lrq = request.GetRequest(); if (!lrq.HasOptionalField(H225_LocationRequest::e_canMapAlias) || !lrq.m_canMapAlias) { PTRACE(3, "WARNING: Sender can't map destination alias via SqlPolicy"); } PString source = AsDotString(lrq.m_replyAddress); PString calledAlias = ""; if (aliases && !aliases->GetSize() == 0) calledAlias = AsString((*aliases)[0], FALSE); PString calledIP = ""; /* not available for LRQs */ PString caller = ""; if (lrq.HasOptionalField(H225_LocationRequest::e_sourceInfo) && (lrq.m_sourceInfo.GetSize() > 0)) caller = AsString(lrq.m_sourceInfo[0], FALSE); PString callingStationId = request.GetCallingStationId(); if (callingStationId.IsEmpty()) callingStationId = caller; PString callid = ""; /* not available for LRQs */ PString messageType = "LRQ"; PString clientauthid = ""; /* not available for LRQs */ DestinationRoutes destination; RunPolicy( /* in */ source, calledAlias, calledIP, caller, callingStationId,callid, messageType, clientauthid, /* out: */ destination); if (destination.m_routes.empty() && !ResolveRoute(request,destination)) destination.SetRejectCall(true); if (destination.RejectCall()) { destination.SetChangedAliases(false); request.SetFlag(RoutingRequest::e_Reject); request.SetRejectReason(destination.GetRejectReason()); } if (destination.ChangeAliases()) { request.SetFlag(RoutingRequest::e_aliasesChanged); lrq.m_destinationInfo = destination.GetNewAliases(); } if (!destination.m_routes.empty()) { // 'explicit' policy can't handle LRQs, so we do it directly request.SetFlag(RoutingRequest::e_aliasesChanged); while (!destination.m_routes.empty()) { request.AddRoute(destination.m_routes.front()); destination.m_routes.pop_front(); } } if (m_next == NULL || destination.EndPolicyChain()) return true; return false; } bool DynamicPolicy::OnRequest(SetupRequest & request) { H225_Setup_UUIE & setup = request.GetRequest(); PString source = AsDotString(setup.m_sourceCallSignalAddress); PString calledAlias = ""; if (request.GetAliases() && (!request.GetAliases()->GetSize() == 0)) { calledAlias = AsString((*request.GetAliases())[0], FALSE); } PIPSocket::Address localAddr; WORD localPort; request.GetWrapper()->GetLocalAddr(localAddr, localPort); PString calledIP = localAddr; // TODO: only correct if a gatekeeper IP was called, should we use explicit IP if present ? PString caller = request.GetCallingStationId(); PString callingStationId = request.GetCallingStationId(); PString callid = AsString(setup.m_callIdentifier.m_guid); PString messageType = "Setup"; PString clientauthid = request.GetClientAuthId(); DestinationRoutes destination; RunPolicy( /* in */ source, calledAlias, calledIP, caller, callingStationId, callid, messageType, clientauthid, /* out: */ destination); if (destination.m_routes.empty() && !ResolveRoute(request,destination)) destination.SetRejectCall(true); if (destination.RejectCall()) { destination.SetChangedAliases(false); request.SetFlag(RoutingRequest::e_Reject); request.SetRejectReason(destination.GetRejectReason()); } if (destination.ChangeAliases()) { request.SetFlag(RoutingRequest::e_aliasesChanged); if (!setup.HasOptionalField(H225_Setup_UUIE::e_destinationAddress)) { setup.IncludeOptionalField(H225_Setup_UUIE::e_destinationAddress); } setup.m_destinationAddress = destination.GetNewAliases(); } if (!destination.m_routes.empty() && destination.m_routes.front().GetPriority() <= DEFAULT_ROUTE_PRIORITY) { request.SetFlag(RoutingRequest::e_aliasesChanged); if (!setup.HasOptionalField(H225_Setup_UUIE::e_destCallSignalAddress)) { setup.IncludeOptionalField(H225_Setup_UUIE::e_destCallSignalAddress); } setup.m_destCallSignalAddress = destination.m_routes.front().m_destAddr; } while (!destination.m_routes.empty()) { request.AddRoute(destination.m_routes.front()); destination.m_routes.pop_front(); } if (m_next == NULL || destination.EndPolicyChain()) return true; return false; } SqlPolicy::SqlPolicy() { m_active = false; m_sqlConn = NULL; #if HAS_DATABASE m_name = "Sql"; m_iniSection = "Routing::Sql"; m_timeout = -1; #else PTRACE(1, m_name << " not available - no database driver compiled into GnuGk"); #endif // HAS_DATABASE } void SqlPolicy::LoadConfig(const PString & instance) { #if HAS_DATABASE if (GkConfig()->GetAllKeyValues(m_iniSection).GetSize() <= 0) { PTRACE(0, m_name << "\tConfig section " << m_iniSection << " doesn't exist"); return; } const PString driverName = GkConfig()->GetString(m_iniSection, "Driver", ""); if (driverName.IsEmpty()) { PTRACE(2, m_name << "\tmodule creation failed: " "no SQL driver selected"); SNMP_TRAP(4, SNMPError, Database, PString(m_name) + " creation failed"); return; } m_sqlConn = GkSQLConnection::Create(driverName, m_name); if (m_sqlConn == NULL) { PTRACE(2, m_name << "\tmodule creation failed: " "could not find " << driverName << " database driver"); SNMP_TRAP(4, SNMPError, Database, PString(m_name) + " creation failed"); return; } m_query = GkConfig()->GetString(m_iniSection, "Query", ""); if (m_query.IsEmpty()) { PTRACE(2, m_name << "\tmodule creation failed: " "no query configured"); SNMP_TRAP(4, SNMPError, Database, PString(m_name) + " creation failed"); return; } else PTRACE(4, m_name << "\tQuery: " << m_query); if (!m_sqlConn->Initialize(GkConfig(), m_iniSection)) { PTRACE(2, m_name << "\tmodule creation failed: " "could not connect to the database"); SNMP_TRAP(4, SNMPError, Database, PString(m_name) + " creation failed"); return; } m_active = true; #endif } SqlPolicy::~SqlPolicy() { delete m_sqlConn; } void SqlPolicy::RunPolicy( /* in */ const PString & source, const PString & calledAlias, const PString & calledIP, const PString & caller, const PString & callingStationId, const PString & callid, const PString & messageType, const PString & clientauthid, /* out: */ DestinationRoutes & destination) { #if HAS_DATABASE GkSQLResult::ResultRow resultRow; std::map params; params["s"] = source; params["c"] = calledAlias; params["p"] = calledIP; params["r"] = caller; params["Calling-Station-Id"] = callingStationId; params["i"] = callid; params["m"] = messageType; params["client-auth-id"] = clientauthid; GkSQLResult* result = m_sqlConn->ExecuteQuery(m_query, params, m_timeout); if (result == NULL) { PTRACE(2, m_name << ": query failed - timeout or fatal error"); SNMP_TRAP(4, SNMPError, Database, PString(m_name) + " query failed"); return; } if (!result->IsValid()) { PTRACE(2, m_name << ": query failed (" << result->GetErrorCode() << ") - " << result->GetErrorMessage()); SNMP_TRAP(4, SNMPError, Database, PString(m_name) + " query failed"); delete result; return; } if (result->GetNumRows() < 1) PTRACE(3, m_name << ": query returned no rows"); else if (result->GetNumFields() < 1) PTRACE(2, m_name << ": bad query - " "no columns found in the result set" ); else if (!result->FetchRow(resultRow) || resultRow.empty()) { PTRACE(2, m_name << ": query failed - could not fetch the result row"); SNMP_TRAP(4, SNMPError, Database, PString(m_name) + " query failed"); } else if ((result->GetNumFields() == 1) || ((result->GetNumFields() == 2) && (resultRow[1].first.ToUpper() == "IGNORE")) ) { PString newDestination = resultRow[0].first; PTRACE(5, m_name << "\tQuery result : " << newDestination); if (newDestination.ToUpper() == "REJECT") { destination.SetRejectCall(true); } else if (IsIPAddress(newDestination)) { int row = 0; do { PString destinationIp = resultRow[0].first; PStringArray adr_parts = SplitIPAndPort(destinationIp, GK_DEF_ENDPOINT_SIGNAL_PORT); PString ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); Route route("Sql", SocketToH225TransportAddr(ip, port)); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(route.m_destAddr); destination.AddRoute(route); row++; if (row < result->GetNumRows()) { result->FetchRow(resultRow); // fetch next row PTRACE(5, m_name << "\tResult cont'd: " << resultRow[0].first); } } while (row < result->GetNumRows()); } else { H225_ArrayOf_AliasAddress newAliases; newAliases.SetSize(1); if (GkConfig()->GetBoolean(m_iniSection, "EnableRegexRewrite", 0)) newDestination = RewriteWildcard(calledAlias,newDestination); H323SetAliasAddress(newDestination, newAliases[0]); destination.SetNewAliases(newAliases); } } else if (result->GetNumFields() == 2) { PString newDestinationAlias = resultRow[0].first; PString newDestinationIP = resultRow[1].first; PTRACE(5, m_name << "\tQuery result : " << newDestinationAlias << ", " << newDestinationIP); if (newDestinationAlias.ToUpper() == "REJECT") { destination.SetRejectCall(true); destination.SetRejectReason(newDestinationIP.AsInteger()); } else { H225_ArrayOf_AliasAddress newAliases; newAliases.SetSize(1); H323SetAliasAddress(newDestinationAlias, newAliases[0]); destination.SetNewAliases(newAliases); int row = 0; do { PString destinationAlias = resultRow[0].first; PString destinationIp = resultRow[1].first; PStringArray adr_parts = SplitIPAndPort(destinationIp, GK_DEF_ENDPOINT_SIGNAL_PORT); PString ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); Route route("Sql", SocketToH225TransportAddr(ip, port)); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(route.m_destAddr); route.m_destNumber = destinationAlias; destination.AddRoute(route); row++; if (row < result->GetNumRows()) { result->FetchRow(resultRow); // fetch next row PTRACE(5, m_name << "\tResult cont'd: " << resultRow[0].first << ", " << resultRow[1].first); } } while (row < result->GetNumRows()); } } delete result; #endif // HAS_DATABASE } CatchAllPolicy::CatchAllPolicy() { m_name = "CatchAll"; m_iniSection = "Routing::CatchAll"; } void CatchAllPolicy::LoadConfig(const PString & instance) { m_catchAllAlias = GkConfig()->GetString(m_iniSection, "CatchAllAlias", "catchall"); m_catchAllIP = GkConfig()->GetString(m_iniSection, "CatchAllIP", ""); if (!m_catchAllIP.IsEmpty()) { PStringArray parts = SplitIPAndPort(m_catchAllIP, GK_DEF_ENDPOINT_SIGNAL_PORT); if (IsIPv6Address(parts[0])) { m_catchAllIP = "[" + parts[0] + "]:" + parts[1]; } else { m_catchAllIP = parts[0] + ":" + parts[1]; } } } bool CatchAllPolicy::CatchAllRoute(RoutingRequest & request) const { if (!m_catchAllIP.IsEmpty()) { H225_TransportAddress destAddr; if (GetTransportAddress(m_catchAllIP, GK_DEF_UNICAST_RAS_PORT, destAddr)) { Route route("catchall", destAddr, 999); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(route.m_destAddr); request.AddRoute(route); return (m_next == NULL); } else { PTRACE(1, m_name << "\tInvalid catch-all IP " << m_catchAllIP); } } H225_ArrayOf_AliasAddress find_aliases; find_aliases.SetSize(1); H323SetAliasAddress(m_catchAllAlias, find_aliases[0]); endptr ep = RegistrationTable::Instance()->FindByAliases(find_aliases); if (ep) { if (ep->GetEndpointType().HasOptionalField(H225_EndpointType::e_gateway)) { Route route(m_name,ep->GetCallSignalAddress(), 999); route.m_destEndpoint = ep; request.AddRoute(route); } else { Route route(m_name, ep, 999); request.AddRoute(route); } return (m_next == NULL); } PTRACE(1, m_name << "\tCatch-all endpoint " << m_catchAllAlias << " not found!"); return false; // configured default endpoint not found } namespace { // anonymous namespace SimpleCreator ExplicitPolicyCreator("explicit"); SimpleCreator InternalPolicyCreator("internal"); SimpleCreator ParentPolicyCreator("parent"); SimpleCreator DNSPolicyCreator("dns"); SimpleCreator VirtualQueuePolicyCreator("vqueue"); SimpleCreator NumberAnalysisPolicyCreator("numberanalysis"); SimpleCreator ENUMPolicyCreator("enum"); SimpleCreator SqlPolicyCreator("sql"); SimpleCreator CatchAllPolicyCreator("catchall"); } } // end of namespace Routing gnugk-3.4/PaxHeaders.16356/radacct.cxx0000644000175000001440000000005012075745273015662 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/radacct.cxx0000644000175000001440000002517012075745273014630 0ustar00janusers00000000000000/* * radacct.cxx * * RADIUS protocol accounting logger module for GNU Gatekeeper. * * Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz * Copyright (c) 2005-2012, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include "config.h" #if HAS_RADIUS #include #include #include "gk_const.h" #include "h323util.h" #include "Toolkit.h" #include "radproto.h" #include "radacct.h" using std::vector; RadAcct::RadAcct( const char* moduleName, const char* cfgSecName ) : GkAcctLogger(moduleName, cfgSecName), m_nasIdentifier(Toolkit::Instance()->GKName()), m_radiusClient(NULL), m_attrH323CallOrigin(RadiusAttr::CiscoVSA_h323_call_origin, false, PString("proxy")), m_attrH323CallType(RadiusAttr::CiscoVSA_h323_call_type, false, PString("VoIP")) { // it is very important to set what type of accounting events // are supported for each accounting module, otherwise Log method // will no get called SetSupportedEvents(RadAcctEvents); PConfig* cfg = GetConfig(); const PString& cfgSec = GetConfigSectionName(); m_radiusClient = new RadiusClient(*cfg, cfgSec); m_nasIpAddress = m_radiusClient->GetLocalAddress(); UnmapIPv4Address(m_nasIpAddress); if (m_nasIpAddress == GNUGK_INADDR_ANY) { vector interfaces; Toolkit::Instance()->GetGKHome(interfaces); if (!interfaces.empty()) m_nasIpAddress = interfaces.front(); else PTRACE(1, "RADACCT\t" << GetName() << " cannot determine " " NAS IP address"); } m_appendCiscoAttributes = Toolkit::AsBool(cfg->GetString(cfgSec, "AppendCiscoAttributes", "1")); m_fixedUsername = cfg->GetString(cfgSec, "FixedUsername", ""); m_timestampFormat = cfg->GetString(cfgSec, "TimestampFormat", ""); m_useDialedNumber = Toolkit::AsBool(cfg->GetString(cfgSec, "UseDialedNumber", "0")); m_attrNasIdentifier = RadiusAttr(RadiusAttr::NasIdentifier, m_nasIdentifier); m_attrH323GwId = RadiusAttr(RadiusAttr::CiscoVSA_h323_gw_id, false, m_nasIdentifier); } RadAcct::~RadAcct() { delete m_radiusClient; } GkAcctLogger::Status RadAcct::Log( GkAcctLogger::AcctEvent evt, const callptr& call ) { // a workaround to prevent processing end on "sufficient" module // if it is not interested in this event type if ((evt & GetEnabledEvents() & GetSupportedEvents()) == 0) return Next; if (m_radiusClient == NULL) { PTRACE(1, "RADACCT\t"<AppendAttr(RadiusAttr::AcctStatusType, (evt & AcctStart) ? RadiusAttr::AcctStatus_Start : ((evt & AcctStop) ? RadiusAttr::AcctStatus_Stop : ((evt & AcctUpdate) ? RadiusAttr::AcctStatus_InterimUpdate : ((evt & AcctOn) ? RadiusAttr::AcctStatus_AccountingOn : ((evt & AcctOff) ? RadiusAttr::AcctStatus_AccountingOff : 0) )))); PIPSocket::Address addr; // Gk works as NAS point, so append NAS IP if (m_nasIpAddress.GetVersion() == 6) pdu->AppendAttr(RadiusAttr::NasIpv6Address, m_nasIpAddress); else pdu->AppendAttr(RadiusAttr::NasIpAddress, m_nasIpAddress); pdu->AppendAttr(m_attrNasIdentifier); pdu->AppendAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual); if (evt & (AcctStart | AcctStop | AcctUpdate)) { pdu->AppendAttr(RadiusAttr::ServiceType, RadiusAttr::ST_Login); pdu->AppendAttr(RadiusAttr::AcctSessionId, call->GetAcctSessionId()); PBYTEArray classAttr(call->GetRADIUSClass()); if (classAttr.GetSize() > 0) pdu->AppendAttr(RadiusAttr::AttrTypeClass, (const BYTE*)classAttr, classAttr.GetSize()); endptr callingEP = call->GetCallingParty(); PIPSocket::Address callerIP(0); WORD callerPort = 0; call->GetSrcSignalAddr(callerIP, callerPort); const PString username = GetUsername(call); if (username.IsEmpty() && m_fixedUsername.IsEmpty()) PTRACE(3, "RADACCT\t" << GetName() << " could not determine User-Name" << " for the call no. " << call->GetCallNumber()); else pdu->AppendAttr(RadiusAttr::UserName, m_fixedUsername.IsEmpty() ? username : m_fixedUsername); if (callerIP.IsValid()) pdu->AppendAttr(RadiusAttr::FramedIpAddress, callerIP); if ((evt & AcctStart) == 0) pdu->AppendAttr(RadiusAttr::AcctSessionTime, (long)call->GetDuration()); PString stationId = GetCallingStationId(call); if (!stationId) pdu->AppendAttr(RadiusAttr::CallingStationId, stationId); else PTRACE(3, "RADACCT\t" << GetName() << " could not determine" << " Calling-Station-Id for the call " << call->GetCallNumber()); stationId = m_useDialedNumber ? GetDialedNumber(call) : GetCalledStationId(call); if (!stationId) pdu->AppendAttr(RadiusAttr::CalledStationId, stationId); else PTRACE(3, "RADACCT\t" << GetName() << " could not determine" << " Called-Station-Id for the call no. " << call->GetCallNumber()); if (m_appendCiscoAttributes) { pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_conf_id, GetGUIDString(call->GetConferenceIdentifier()) ); pdu->AppendAttr(m_attrH323GwId); pdu->AppendAttr(m_attrH323CallOrigin); pdu->AppendAttr(m_attrH323CallType); Toolkit * const toolkit = Toolkit::Instance(); time_t tm = call->GetSetupTime(); if (tm != 0) pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_setup_time, toolkit->AsString(PTime(tm), m_timestampFormat)); if (evt & (AcctStop | AcctUpdate)) { tm = call->GetConnectTime(); if (tm != 0) pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_connect_time, toolkit->AsString(PTime(tm), m_timestampFormat)); } if (evt & AcctStop) { tm = call->GetDisconnectTime(); if (tm != 0) pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_disconnect_time, toolkit->AsString(PTime(tm), m_timestampFormat)); pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_disconnect_cause, PString(PString::Unsigned, (long)(call->GetDisconnectCause()), 16) ); pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_release_source,call->GetReleaseSource()); pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_preferred_codec,call->GetCodec()); pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_rewritten_e164_num,call->GetCalledStationId()); // Post Dial Delay Time pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("h323pddtime=")+PString(PString::Unsigned,(long)call->GetPostDialDelay()), true); // Ring Time pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("h323ringtime=")+PString(PString::Unsigned,(long)call->GetRingTime()), true); // Number of Route Attempts pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("h323routeattempts=")+PString(PString::Unsigned,call->GetNoCallAttempts()), true); // Proxy Mode pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("h323_rtp_proxy=")+PString(PString::Unsigned, ((call->GetProxyMode() == CallRec::ProxyEnabled) ? 1 : 0)), true); // RTCP SOURCE REPORT pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("RTP_source_IP=")+call->GetSRC_media_IP(), true); pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("RTP_destination_IP=")+call->GetDST_media_IP(), true); pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("RTCP_source_packet_count=")+PString(PString::Unsigned, call->GetRTCP_SRC_packet_count()), true); pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("RTCP_source_packet_lost=")+PString(PString::Unsigned, call->GetRTCP_SRC_packet_lost()), true); pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("RTCP_source_jitter=")+PString(PString::Unsigned, call->GetRTCP_SRC_jitter_min())+PString("|")+PString(PString::Unsigned,call->GetRTCP_SRC_jitter_avg())+PString("|")+PString(PString::Unsigned, call->GetRTCP_SRC_jitter_max()), true); PINDEX i_sdes = 0; PStringList sdes = call->GetRTCP_SRC_sdes(); while (i_sdes < sdes.GetSize()) { pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("RTCP_source_sdes_")+sdes[i_sdes], true); i_sdes ++; } // RTCP DESTINATION REPORT pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("RTCP_destination_packet_count=")+PString(PString::Unsigned, call->GetRTCP_DST_packet_count()), true); pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("RTCP_destination_packet_lost=")+PString(PString::Unsigned, call->GetRTCP_DST_packet_lost()), true); pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("RTCP_destination_jitter=")+PString(PString::Unsigned,call->GetRTCP_DST_jitter_min())+PString("|")+PString(PString::Unsigned, call->GetRTCP_DST_jitter_avg())+PString("|")+PString(PString::Unsigned, call->GetRTCP_DST_jitter_max()), true); i_sdes = 0; sdes = call->GetRTCP_DST_sdes(); while (i_sdes < sdes.GetSize()) { pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("RTCP_destination_sdes_")+sdes[i_sdes], true); i_sdes ++; } } WORD port; if (call->GetDestSignalAddr(addr, port)) pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_remote_address, addr.AsString()); pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("h323-ivr-out=h323-call-id:") + GetGUIDString(call->GetCallIdentifier().m_guid), true); } pdu->AppendAttr(RadiusAttr::AcctDelayTime, 0); } // send request and wait for response RadiusPDU * response = NULL; bool result = true; // accounting updates must be fast, so we are just sending // the request to the server and are not waiting for a response if (evt & AcctUpdate) { result = m_radiusClient->SendRequest(*pdu); } else { result = m_radiusClient->MakeRequest(*pdu, response) && (response != NULL); } delete pdu; if (!result) { delete response; return Fail; } if (response) { // check if Access-Request has been accepted result = (response->GetCode() == RadiusPDU::AccountingResponse); if (!result) { PTRACE(4, "RADACCT\t" << GetName() << " - received response is not " " an AccountingResponse, event " << evt << ", call no. " << (call ? call->GetCallNumber() : 0)); } delete response; } return result ? Ok : Fail; } namespace { // append RADIUS based accounting logger to the global list of loggers GkAcctLoggerCreator RadAcctCreator("RadAcct"); } #endif /* HAS_RADIUS */ gnugk-3.4/PaxHeaders.16356/sigmsg.h0000644000175000001440000000005011461315737015173 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/sigmsg.h0000644000175000001440000001452511461315737014143 0ustar00janusers00000000000000/* * sigmsg.h * * Structures to hold and process signaling messages * * Copyright (c) 2005, Michal Zygmuntowicz * Copyright (c) 2005-2010, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #ifndef SIGMSG_H #define SIGMSG_H "@(#) $Id: sigmsg.h,v 1.10 2010/10/25 15:01:51 willamowius Exp $" #include #include "q931.h" class H225_H323_UserInformation; class H225_Setup_UUIE; class H225_SetupAck_UUIE; class H225_CallProceeding_UUIE; class H225_Alerting_UUIE; class H225_Connect_UUIE; class H225_Progress_UUIE; class H225_Facility_UUIE; class H225_ReleaseComplete_UUIE; class H225_Information_UUIE; class H225_Notify_UUIE; class H225_Status_UUIE; class H225_StatusInquiry_UUIE; /// Base class to hold generic information associated with a signaling message class SignalingMsg { public: virtual ~SignalingMsg(); /// @return a cloned object of this or a derived class virtual SignalingMsg* Clone(); /// @return signaling message type (#Q931::MsgTypes enum#) unsigned GetTag() const; /// @return signaling message type as a string PString GetTagName() const; /// @return CRV associated with the signaling message unsigned GetCallReference() const; /// @return a reference to the Q.931 message stored Q931& GetQ931() const { return *m_q931; } /// @return a pointer to the User-User IE, NULL if not present H225_H323_UserInformation* GetUUIE() { return m_uuie; } /// Get an address the message has been received on void GetLocalAddr( PIPSocket::Address &addr, WORD &port ) const; void GetLocalAddr( PIPSocket::Address &addr ) const; /// Get an address the message has been received from void GetPeerAddr( PIPSocket::Address &addr, WORD &port ) const; void GetPeerAddr( PIPSocket::Address &addr ) const; /// Set a flag to indicate that the Q.931 message has been modified void SetChanged() { m_changed = true; } /** Set a flag to indicate that the decoded H.225 UserInformation element has been modified and the corresponding Q.931 IE should be recreated. */ void SetUUIEChanged() { m_changed = m_uuieChanged = true; } /// @return true if the Q.931 message has been modified bool IsChanged() const { return m_changed; } /** Encode the Q.931 message back into a binary form. @return True if the message has been encoded successfully. */ bool Encode( PBYTEArray &buffer /// buffer to hold the encoded message ); bool Decode( const PBYTEArray & buffer /// buffer holding the encoded message ); /// factory constructor for signaling messages static SignalingMsg* Create( Q931 *q931pdu, /// this pointer is not cloned and deleted by this class destructor H225_H323_UserInformation *uuie, /// decoded User-User IE const PIPSocket::Address &localAddr, /// an address the message has been received on WORD localPort, /// a port number the message has been received on const PIPSocket::Address &peerAddr, /// an address the message has been received from WORD peerPort /// a port number the message has been received from ); protected: SignalingMsg( Q931 *q931pdu, /// this pointer is not cloned and deleted by this class destructor H225_H323_UserInformation *uuie, /// decoded User-User IE const PIPSocket::Address &localAddr, /// an address the message has been received on WORD localPort, /// a port number the message has been received on const PIPSocket::Address &peerAddr, /// an address the message has been received from WORD peerPort /// a port number the message has been received from ); private: SignalingMsg(); SignalingMsg(const SignalingMsg&); public: SignalingMsg& operator=(const SignalingMsg&); protected: Q931 *m_q931; /// whole Q.931 message H225_H323_UserInformation *m_uuie; /// User-User IE element of the Q.931 msg PIPSocket::Address m_localAddr; /// local IP address the msg arrived to WORD m_localPort; /// local port number the msg arrived to PIPSocket::Address m_peerAddr; /// remote IP address the msg arrived from WORD m_peerPort; /// remote port number the msg arrived from bool m_changed; /// indicate changes to the Q.931 message bool m_uuieChanged; /// indicate changes to the H.225 User Information element }; /// Specialized template for a particular H.225.0 signaling message template class H225SignalingMsg : public SignalingMsg { public: /// Build a new SignalingMsg H225SignalingMsg( Q931 *q931pdu, /// this pointer is not cloned and deleted by this class destructor H225_H323_UserInformation *uuie, /// decoded User-User IE UUIE& /*uuieBody*/, /// decoded UUIE body const PIPSocket::Address &localAddr, /// an address the message has been received on WORD localPort, /// a port number the message has been received on const PIPSocket::Address &peerAddr, /// an address the message has been received from WORD peerPort /// a port number the message has been received from ) : SignalingMsg(q931pdu, uuie, localAddr, localPort, peerAddr, peerPort), m_uuieBody(uuie->m_h323_uu_pdu.m_h323_message_body) {} UUIE& GetUUIEBody() const { return m_uuieBody; } virtual SignalingMsg* Clone() { H225_H323_UserInformation *uuieClone = (H225_H323_UserInformation*)(m_uuie->Clone()); return new H225SignalingMsg(new Q931(*m_q931), uuieClone, (UUIE&)(uuieClone->m_h323_uu_pdu.m_h323_message_body), m_localAddr, m_localPort, m_peerAddr, m_peerPort ); } private: H225SignalingMsg(); H225SignalingMsg(const H225SignalingMsg&); H225SignalingMsg& operator=(const H225SignalingMsg&); protected: UUIE &m_uuieBody; /// H.225.0 UUIE structure associated with the message }; typedef H225SignalingMsg SetupMsg; typedef H225SignalingMsg AlertingMsg; typedef H225SignalingMsg CallProceedingMsg; typedef H225SignalingMsg ConnectMsg; typedef H225SignalingMsg ProgressMsg; typedef H225SignalingMsg ReleaseCompleteMsg; typedef H225SignalingMsg InformationMsg; typedef H225SignalingMsg FacilityMsg; //typedef H225SignalingMsg NotifyMsg; //typedef H225SignalingMsg StatusMsg; //typedef H225SignalingMsg StatusInquiryMsg; #endif // SIGMSG_H gnugk-3.4/PaxHeaders.16356/cisco.asn0000644000175000001440000000005010213141663015321 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/cisco.asn0000644000175000001440000000156610213141663014272 0ustar00janusers00000000000000CISCO-MESSAGES DEFINITIONS AUTOMATIC TAGS ::= BEGIN IMPORTS CallIdentifier, AliasAddress FROM H225-MESSAGES; ARQnonStandardInfo ::= [2] SEQUENCE { sourceAlias SEQUENCE OF AliasAddress, -- of calling proxy sourceExtAlias SEQUENCE OF AliasAddress, -- of calling endpoint ..., redirectIEinfo INTEGER(0..255) OPTIONAL, callingOctet3a INTEGER(0..255) OPTIONAL, displayInformationElement IA5String (SIZE (1..128)) OPTIONAL, interfaceSpecificBillingId IA5String (SIZE (1..128)) OPTIONAL, interfaceDescription IA5String (SIZE (1..128)) OPTIONAL } LRQnonStandardInfo ::= [4] SEQUENCE { ttl INTEGER(1..255), ..., callIdentifier CallIdentifier OPTIONAL, redirectIEinfo INTEGER(0..255) OPTIONAL, callingOctet3a INTEGER(0..255) OPTIONAL, gatewaySrcInfo SEQUENCE OF AliasAddress OPTIONAL, displayInformationElement IA5String (SIZE (1..128)) OPTIONAL } END gnugk-3.4/PaxHeaders.16356/gksql.h0000644000175000001440000000005012074312620015010 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gksql.h0000644000175000001440000003165112074312620013757 0ustar00janusers00000000000000/* * gksql.h * * Generic interface to access SQL databases * * Copyright (c) 2004, Michal Zygmuntowicz * Copyright (c) 2006-2011, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #ifndef GKSQL_H #define GKSQL_H "@(#) $Id: gksql.h,v 1.24 2013/01/12 16:55:44 willamowius Exp $" #include #include #include #include "h323util.h" #include "snmp.h" #include "name.h" #include "factory.h" #include "config.h" /** Abstract base class that encapsulates SQL query result. Backend specific operations are performed by derived classes. */ class GkSQLResult { protected: GkSQLResult( /// true if the query failed and no result is available bool queryError = false ) : m_numRows(0), m_numFields(0), m_queryError(queryError) {} public: /// the first element of the pair is a field value and the second /// is a field name typedef std::vector< std::pair > ResultRow; virtual ~GkSQLResult(); /** @return True if the query succeeded and the result is available. Otherwise only GetErrorMessage and GetErrorCode member functions are meaningful. */ bool IsValid() const { return !m_queryError; } /** @return Number of rows in the result set (to be fetched) for SELECT-like query, or number of rows affected for INSERT, UPDATE or DELETE query. */ long GetNumRows() const { return m_numRows; } /** @return Number of columns in the result set rows for SELECT-like query. */ long GetNumFields() const { return m_numFields; } /** @return Backend specific error message, if the query failed. */ virtual PString GetErrorMessage() = 0; /** @return Backend specific error code, if the query failed. */ virtual long GetErrorCode() = 0; /** Fetch a single row from the result set. After each row is fetched, cursor position is moved to a next row. @return True if the row has been fetched, false if no more rows are available. */ virtual bool FetchRow( /// array to be filled with string representations of the row fields PStringArray& result ) = 0; virtual bool FetchRow( /// array to be filled with string representations of the row fields ResultRow& result ) = 0; private: GkSQLResult(const GkSQLResult&); GkSQLResult& operator=(const GkSQLResult&); protected: /// number of rows in the result set or rows affected by the query long m_numRows; /// number of columns in each row in the result set long m_numFields; /// true if query execution failed bool m_queryError; }; /** Abstract class that provides generic access to SQL backend. Can provide a single SQL connection or maintain a pool of SQL connections. Thread safe. NOTE: Currently it implements only fixed SQL connections pool size, so only minPoolSize parameter is examined. */ class GkSQLConnection : public NamedObject { public: struct Info { bool m_connected; unsigned m_idleConnections; unsigned m_busyConnections; unsigned m_waitingRequests; int m_minPoolSize; int m_maxPoolSize; }; GkSQLConnection( /// name to use in the log const char* name = "SQL" ); virtual ~GkSQLConnection(); /** Create an instance of a GkSQLConnection derived class, that implements access to a specific SQL backend. The class to be created is found by searching a global factory list for #driverName# entry. */ static GkSQLConnection* Create( /// driver name for the connection object const char* driverName, /// name for the connection to use in the log file const char* connectionName ); /** Read SQL settings from the config and connect to the database. Derived classes do not have to override this member function. @return True if settings have been read and connections have been established. */ virtual bool Initialize( /// config to be read PConfig* cfg, /// name of the config section with SQL settings const char* cfgSectionName ); /** Execute the query and return the result set. It uses first idle SQL connection or waits for an idle SQL connection, if all connections are busy with query execution. Pool size defines how many concurrent queries can be executed by this object. The query can be parametrized and the parameters are replaced with strings from the #queryParams# list. Usage: SELECT name, surname FROM people WHERE name = '%1' and age > %2 Use double %% to embed % and %{1} notation to allow strings like %{1}123. @return Query execution result (no matters the query failed or succeeded) or NULL if timed out waiting for an idle SQL connection. */ GkSQLResult* ExecuteQuery( /// query to be executed const char* queryStr, /// query parameters (%1, %2, ... notation), NULL if the query /// does not take any parameters const PStringArray* queryParams = NULL, /// time (ms) to wait for an idle connection, -1 means infinite long timeout = -1 ); /** Execute the query and return the result set. It uses first idle SQL connection or waits for an idle SQL connection, if all connections are busy with query execution. Pool size defines how many concurrent queries can be executed by this object. The query can be parametrized and the parameters are replaced with strings from the #queryParams# list. Usage: SELECT name, surname FROM people WHERE name = '%{Name}' and age > %a The parameter names can be a one letter (%a, for example) or whole strings (%{Name}, for example). @return Query execution result (no matters the query failed or succeeded) or NULL if timed out waiting for an idle SQL connection. */ GkSQLResult* ExecuteQuery( /// query to be executed const char* queryStr, /// query parameters (name => value associations) const std::map& queryParams, /// time (ms) to wait for an idle connection, -1 means infinite long timeout = -1 ); /// Get information about SQL connection state void GetInfo( Info &info /// filled with SQL connection state information upon return ); protected: /** Generic SQL database connection object - should be extended by derived classes to include backed specific connection data. */ class SQLConnWrapper { public: SQLConnWrapper( /// unique identifier for this connection int id, /// host:port this connection is made to const PString& host ) : m_id(id), m_host(host) {} virtual ~SQLConnWrapper(); private: SQLConnWrapper(); SQLConnWrapper(const SQLConnWrapper&); SQLConnWrapper& operator=(const SQLConnWrapper&); public: /// unique identifier for this connection int m_id; /// host:port this connection is made to PString m_host; }; typedef SQLConnWrapper* SQLConnPtr; protected: /** Create a new SQL connection using parameters stored in this object. When the connection is to be closed, the object is simply deleted using delete operator. @return NULL if database connection could not be established or an object derived from SQLConnWrapper class. */ virtual SQLConnPtr CreateNewConnection( /// unique identifier for this connection int id ) = 0; /** Get the first idle connection from the pool and set connptr variable to point to this connection. IMPORTANT: After connection is successfully acquired, ReleaseSQLConnection has to be called to return back the connection to the pool. @return True if the connection has been acquired, false if it is not available (timeout, network connection lost, ...). */ bool AcquireSQLConnection( SQLConnPtr& connptr, /// variable to hold connection pointer (handle) long timeout /// timeout (ms) to wait for an idle connection ); /** Return previously acquired connection back to the pool. It is important that the reference references the same variable as when calling AcquireSQLConnection. */ void ReleaseSQLConnection( SQLConnPtr& connptr, /// connection to release (mark as idle) bool deleteFromPool = false /// true to delete the connection (a broken connection) ); /** Execute the query using specified SQL connection. @return Query execution result. */ virtual GkSQLResult* ExecuteQuery( /// SQL connection to use for query execution SQLConnPtr conn, /// query string const char* queryStr, /// maximum time (ms) for the query execution, -1 means infinite long timeout = -1 ) = 0; /** Replace query parameters placeholders (%1, %2, ...) with actual values and escape parameter strings. Derived classes do not need to override this function, unless want to perform some custom parameter processing. @return New query string with all parameters replaced. */ virtual PString ReplaceQueryParams( /// SQL connection to get escape parameters from SQLConnPtr conn, /// parametrized query string const char* queryStr, /// parameter values const PStringArray& queryParams ); /** Replace query parameters placeholders (%a, %{Name}, ...) with actual values and escape parameter strings. Derived classes do not need to override this function, unless want to perform some custom parameter processing. @return New query string with all parameters replaced. */ virtual PString ReplaceQueryParams( /// SQL connection to get escape parameters from SQLConnPtr conn, /// parametrized query string const char* queryStr, /// parameter name => value associations const std::map& queryParams ); /** Escape any special characters in the string, so it can be used in a SQL query. @return Escaped string. */ virtual PString EscapeString( /// SQL connection to get escaping parameters from SQLConnPtr conn, /// string to be escaped const char* str ) = 0; /// Retrieve hostname (IP or DNS) and optional port number (separated by ':') from the string void GetHostAndPort( /// string to be examined const PString& str, /// set to the host name PString& host, /// set to the port number or 0, if no port is given WORD& port ) const; private: GkSQLConnection(const GkSQLConnection&); GkSQLConnection& operator=(const GkSQLConnection&); /** Creates m_minPoolSize initial database connections. Called from Initialize. @return True if at least one database connection has been established. */ bool Connect(); protected: /** Disconnect connection pool from DB on connection error */ void Disconnect(); /// filled with the actual host from m_hosts the database connection is made to PString m_host; /// database port to connect to WORD m_port; /// database name PString m_database; /// database username to connect as PString m_username; /// password associated with the username (if any) PString m_password; /// name of shared library PString m_library; private: /// iterator typedefs for convenience typedef std::list::iterator iterator; typedef std::list::const_iterator const_iterator; typedef std::list::iterator witerator; typedef std::list::const_iterator const_witerator; /// minimum number of SQL connections active int m_minPoolSize; /// maximum number of SQL connections active int m_maxPoolSize; /// list of idle SQL connections std::list m_idleConnections; /// list of connections busy with query execution std::list m_busyConnections; /// FIFO queue of queries waiting to be executed when there is no idle connections std::list m_waitingRequests; /// mutual access to the lists PTimedMutex m_connectionsMutex; /// signalled when a connections moves from the busy to the idle list PSyncPoint m_connectionAvailable; /// set to true when destructor is being invoked bool m_destroying; /// remain false while connection to the database not yet established /// reset to false on disconnect or error during operation -> reconnect bool m_connected; }; typedef Factory::Creator1 SQLCreator1; template struct GkSQLCreator : public SQLCreator1 { GkSQLCreator( const char* name ) : SQLCreator1(name) {} virtual GkSQLConnection* operator()( const char* connectionName ) const { return new SQLDriver(connectionName); } }; inline void GkSQLConnection::GetHostAndPort(const PString & str, PString & host, WORD & port) const { if (IsIPv4Address(str) || (str.Left(1) != "[")) { // IPv4 or IPv6 without bracket and port or DNS name const PINDEX i = str.Find(':'); if (i == P_MAX_INDEX) { host = str; port = 0; } else { host = str.Left(i); port = (WORD)(str.Mid(i+1).AsUnsigned()); } } else { const PINDEX j = str.Find("]:"); if (j != P_MAX_INDEX) { // IPv6 address with port host = str.Left(j+1); port = (WORD)(str.Mid(j+2).AsUnsigned()); } else { host = str; port = 0; } if (host.Left(1) == "[") host = host.Mid(1); if (host.Right(1) == "]") host = host.Left(host.GetLength() - 1); } } #endif /* GKSQL_H */ gnugk-3.4/PaxHeaders.16356/gkconfig.cxx0000644000175000001440000000005011635475005016042 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gkconfig.cxx0000644000175000001440000001065411635475005015011 0ustar00janusers00000000000000/* * gkconfig.cxx * * Copyright (c) 2006-2010, Jan Willamowius * */ #include #include "gkconfig.h" GatekeeperConfig::GatekeeperConfig( const PFilePath& filename, /// Explicit name of the configuration file. const PString& section, /// Default section to search for variables. PConfig* chainedConfig /// a next config in the chain ) : PConfig(filename, section), m_chainedConfig(chainedConfig) { } GatekeeperConfig::~GatekeeperConfig() { delete m_chainedConfig; } #ifdef hasPConfigArray PStringArray GatekeeperConfig::GetSections() const { PStringArray list = PConfig::GetSections(); #else PStringList GatekeeperConfig::GetSections() const { PStringList list = PConfig::GetSections(); #endif if (m_chainedConfig != NULL) { PStringList chainedList = m_chainedConfig->GetSections(); for (PINDEX i = 0; i < chainedList.GetSize(); i++) if (list.GetValuesIndex(chainedList[i]) == P_MAX_INDEX) list.AppendString(chainedList[i]); } return list; } #ifdef hasPConfigArray PStringArray GatekeeperConfig::GetKeys(const PString & theSection) const { PStringArray list = PConfig::GetKeys(theSection); #else PStringList GatekeeperConfig::GetKeys(const PString & theSection) const { PStringList list = PConfig::GetKeys(theSection); #endif if (m_chainedConfig != NULL) { PStringList chainedList = m_chainedConfig->GetKeys(theSection); for (PINDEX i = 0; i < chainedList.GetSize(); i++) if (list.GetValuesIndex(chainedList[i]) == P_MAX_INDEX) list.AppendString(chainedList[i]); } return list; } void GatekeeperConfig::DeleteSection(const PString & theSection) { PConfig::DeleteSection(theSection); if (m_chainedConfig != NULL) m_chainedConfig->DeleteSection(theSection); } void GatekeeperConfig::DeleteKey(const PString & theSection, const PString & theKey) { PConfig::DeleteKey(theSection, theKey); if (m_chainedConfig != NULL) m_chainedConfig->DeleteKey(theSection, theKey); } PBoolean GatekeeperConfig::HasKey(const PString & theSection, const PString & theKey) const { return PConfig::HasKey(theSection, theKey) || (m_chainedConfig != NULL && m_chainedConfig->HasKey(theSection, theKey)); } PStringToString GatekeeperConfig::GetAllKeyValues(const PString& section) const { PStringToString dict = PConfig::GetAllKeyValues(section); if (m_chainedConfig != NULL) { const PStringList keys = m_chainedConfig->GetKeys(section); for (PINDEX i = 0; i < keys.GetSize(); i++) if (!dict.Contains(keys[i])) dict.SetAt(keys[i], m_chainedConfig->GetString(section, keys[i], "")); } return dict; } PString GatekeeperConfig::GetString(const PString & theSection, const PString & theKey, const PString & dflt ) const { if (m_chainedConfig == NULL || PConfig::HasKey(theSection, theKey)) return PConfig::GetString(theSection, theKey, dflt); else return m_chainedConfig->GetString(theSection, theKey, dflt); } PBoolean GatekeeperConfig::GetBoolean(const PString & section, const PString & key, PBoolean dflt) const { if (m_chainedConfig == NULL || PConfig::HasKey(section, key)) return PConfig::GetBoolean(section, key, dflt); else return m_chainedConfig->GetBoolean(section, key, dflt); } long GatekeeperConfig::GetInteger(const PString & section, const PString & key, long dflt) const { if (m_chainedConfig == NULL || PConfig::HasKey(section, key)) return PConfig::GetInteger(section, key, dflt); else return m_chainedConfig->GetInteger(section, key, dflt); } PInt64 GatekeeperConfig::GetInt64(const PString & section, const PString & key, PInt64 dflt) const { if (m_chainedConfig == NULL || PConfig::HasKey(section, key)) return PConfig::GetInt64(section, key, dflt); else return m_chainedConfig->GetInt64(section, key, dflt); } double GatekeeperConfig::GetReal(const PString & section, const PString & key, double dflt) const { if (m_chainedConfig == NULL || PConfig::HasKey(section, key)) return PConfig::GetReal(section, key, dflt); else return m_chainedConfig->GetReal(section, key, dflt); } PTime GatekeeperConfig::GetTime(const PString & section, const PString & key) const { if (m_chainedConfig == NULL || PConfig::HasKey(section, key)) return PConfig::GetTime(section, key); else return m_chainedConfig->GetTime(section, key); } PTime GatekeeperConfig::GetTime(const PString & section, const PString & key, const PTime & dflt) const { if (m_chainedConfig == NULL || PConfig::HasKey(section, key)) return PConfig::GetTime(section, key, dflt); else return m_chainedConfig->GetTime(section, key, dflt); } gnugk-3.4/PaxHeaders.16356/forwarding.cxx0000644000175000001440000000005012131503147016404 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/forwarding.cxx0000644000175000001440000002776312131503147015364 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // Call Forwarding Poliy for GNU Gatekeeper // // Copyright (c) 2012, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #include #include #include "Routing.h" #include "Toolkit.h" #include "gksql.h" #include "gk_const.h" #include "h323util.h" // forwards are handled in ascending order of their type enum ForwardingTypes { FORWARD_UNCONDITIONAL=1, FORWARD_BUSY=2, FORWARD_NOANSWER=3, FORWARD_ERROR=4 }; const unsigned MAX_RECURSION_DEPTH = 25; const unsigned DEFAULT_PRIORITY = 1; const unsigned NO_ANSWER_PRIORITY = 900; namespace Routing { // a policy to route calls via an SQL database class ForwardingPolicy : public DynamicPolicy { public: ForwardingPolicy(); virtual ~ForwardingPolicy(); protected: virtual void LoadConfig(const PString & instance); virtual void RunPolicy( /*in */ const PString & source, const PString & calledAlias, const PString & calledIP, const PString & caller, const PString & callingStationId, const PString & callid, const PString & messageType, const PString & clientauthid, /* out: */ DestinationRoutes & destination); // called recursively virtual bool FindEPForwardingRules( /* in */ std::map params, unsigned recursionDepth, unsigned priority, /* out: */ DestinationRoutes & destination); protected: // connection to the SQL database GkSQLConnection* m_sqlConn; // parametrized query string for the routing query PString m_query; // query timeout long m_timeout; }; ForwardingPolicy::ForwardingPolicy() { m_active = false; m_sqlConn = NULL; #if HAS_DATABASE m_name = "Forwarding"; m_iniSection = "Routing::Forwarding"; m_timeout = -1; #else PTRACE(1, m_name << " not available - no database driver compiled into GnuGk"); #endif // HAS_DATABASE } ForwardingPolicy::~ForwardingPolicy() { delete m_sqlConn; } void ForwardingPolicy::LoadConfig(const PString & instance) { #if HAS_DATABASE const PString driverName = GkConfig()->GetString(m_iniSection, "Driver", ""); if (driverName.IsEmpty()) { PTRACE(2, m_name << "\tmodule creation failed: no SQL driver selected"); SNMP_TRAP(4, SNMPError, Database, PString(m_name) + " creation failed"); return; } m_sqlConn = GkSQLConnection::Create(driverName, m_name); if (m_sqlConn == NULL) { PTRACE(2, m_name << "\tmodule creation failed: " "could not find " << driverName << " database driver"); SNMP_TRAP(4, SNMPError, Database, PString(m_name) + " creation failed"); return; } m_query = GkConfig()->GetString(m_iniSection, "Query", ""); if (m_query.IsEmpty()) { PTRACE(2, m_name << "\tmodule creation failed: " "no query configured"); SNMP_TRAP(4, SNMPError, Database, PString(m_name) + " creation failed"); return; } else PTRACE(4, m_name << "\tQuery: " << m_query); if (!m_sqlConn->Initialize(GkConfig(), m_iniSection)) { PTRACE(2, m_name << "\tmodule creation failed: " "could not connect to the database"); SNMP_TRAP(4, SNMPError, Database, PString(m_name) + " creation failed"); return; } m_active = true; #endif } void ForwardingPolicy::RunPolicy( /* in */ const PString & source, const PString & calledAlias, const PString & calledIP, const PString & caller, const PString & callingStationId, const PString & callid, const PString & messageType, const PString & clientauthid, /* out: */ DestinationRoutes & destination) { #if HAS_DATABASE std::map params; params["s"] = source; params["c"] = calledAlias; params["p"] = calledIP; params["r"] = caller; params["Calling-Station-Id"] = callingStationId; params["i"] = callid; params["m"] = messageType; params["client-auth-id"] = clientauthid; // if IP called, check if its an internal EP and change calledAlias/calledIP if (calledAlias.IsEmpty() && IsIPAddress(calledIP)) { PStringArray adr_parts = calledIP.Tokenise(":", FALSE); PString ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); if (port == 0) port = GK_DEF_ENDPOINT_SIGNAL_PORT; endptr ep = RegistrationTable::Instance()->FindBySignalAdr(SocketToH225TransportAddr(ip, port)); if (ep) { // call goes to an internal endpoint, use the first alias instead H225_ArrayOf_AliasAddress aliases = ep->GetAliases(); if (aliases.GetSize() > 0) { params["c"] = AsString(aliases[0], false); params["p"] = ""; } } } FindEPForwardingRules(params, 0, DEFAULT_PRIORITY, destination); PTRACE(5, "Fwd\t" << destination.m_routes.size() << " routes added"); for(std::list::iterator it = destination.m_routes.begin(); it !=destination.m_routes.end(); ++it) { PTRACE(5, "Fwd\tRoute=" << it->AsString()); } if (destination.ChangeAliases()) { PTRACE(5, "Fwd\tAliases changed to " << destination.GetNewAliases()); } #endif // HAS_DATABASE } bool ForwardingPolicy::FindEPForwardingRules( /* in */ std::map params, // pass copies so they can be modified in recursion unsigned recursionDepth, unsigned priority, /* out: */ DestinationRoutes & destination) { bool skipOrginalForward = false; // make sure we don't produce infinite loops if (recursionDepth > MAX_RECURSION_DEPTH) return false; #if HAS_DATABASE GkSQLResult* result = m_sqlConn->ExecuteQuery(m_query, params, m_timeout); if (result == NULL) { PTRACE(2, m_name << ": query failed - timeout or fatal error"); SNMP_TRAP(5, SNMPError, Database, PString(m_name) + " query failed"); return false; } if (!result->IsValid()) { PTRACE(2, m_name << ": query failed (" << result->GetErrorCode() << ") - " << result->GetErrorMessage()); SNMP_TRAP(5, SNMPError, Database, PString(m_name) + " query failed"); delete result; return false; } if (result->GetNumRows() < 1) { PTRACE(5, m_name << ": query returned no rows"); return false; } else if (result->GetNumFields() != 2) { PTRACE(2, m_name << ": bad query - didn't return 2 fields"); SNMP_TRAP(5, SNMPError, Database, PString(m_name) + " query failed"); return false; } else { // fetch all rows now, recursive checks will invalidate result set std::vector rows(result->GetNumRows()); for (long i = 0; i < result->GetNumRows(); ++i) { if (!result->FetchRow(rows[i]) || rows[i].empty()) { PTRACE(2, m_name << ": query failed - could not fetch the result row"); SNMP_TRAP(5, SNMPError, Database, PString(m_name) + " query failed"); break; } } // look at all rules (ordered by forwarding type) for (unsigned i = 0; i < rows.size(); ++i) { unsigned forwardType = rows[i][0].first.AsInteger(); PString forwardDestination = rows[i][1].first; PTRACE(4, "Fwd\tForward type=" << forwardType << " for call to " << params["c"] << ": new dest=" << forwardDestination); if (forwardDestination.IsEmpty()) { // skip rule, if forwardDestination is empty continue; } if ( (forwardType == FORWARD_UNCONDITIONAL) || (forwardType == FORWARD_BUSY) ) { if (IsIPAddress(forwardDestination)) { // set a route if forward to IP PString destinationIp = forwardDestination; PStringArray adr_parts = destinationIp.Tokenise(":", FALSE); PString ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); if (port == 0) port = GK_DEF_ENDPOINT_SIGNAL_PORT; //PTRACE(0, "JW forward destination to " << forwardDestination << " prio=" << priority + recursionDepth); Route route("ForwardUnconditionalOrBusy", SocketToH225TransportAddr(ip, port), priority + recursionDepth); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(route.m_destAddr); if ((forwardType == FORWARD_UNCONDITIONAL) || (route.m_destEndpoint && CallTable::Instance()->FindCallRec(route.m_destEndpoint))) { route.m_destNumber = forwardDestination; route.m_destOutNumber = forwardDestination; destination.AddRoute(route, false); skipOrginalForward = true; } } else { // check if we have an EPRec for the new destination (to check for current call or forwards) H225_ArrayOf_AliasAddress called; called.SetSize(1); H323SetAliasAddress(params["c"], called[0]); endptr ep = RegistrationTable::Instance()->FindByAliases(called); //PTRACE(0, "JW call to " << AsString(called, false) << " ep=" << ep << " call=" << CallTable::Instance()->FindCallRec(ep)); if ((forwardType == FORWARD_UNCONDITIONAL) || (ep && CallTable::Instance()->FindCallRec(ep))) { // check if new destination is also forwarded params["c"] = forwardDestination; params["p"] = ""; if (FindEPForwardingRules(params, recursionDepth+1, DEFAULT_PRIORITY, destination)) { PTRACE(5, "Fwd\tSkipping forward to " << forwardDestination << " (also redirected uncond/busy)"); //PTRACE(0, "JW destination aliases=" << destination.GetNewAliases()); } else { if (priority <= 1) { // just rewrite the destination if forward to alias //PTRACE(0, "JW rewriting destination to " << forwardDestination); H225_ArrayOf_AliasAddress newAliases; newAliases.SetSize(1); H323SetAliasAddress(forwardDestination, newAliases[0]); destination.SetNewAliases(newAliases); } else { //PTRACE(0, "JW forward destination to " << forwardDestination << " prio=" << priority); // replace an NoAnswer or Error forward if (ep) { Route route("ForwardNoAnswerOrError", ep, priority + recursionDepth); route.m_destNumber = forwardDestination; route.m_destOutNumber = forwardDestination; destination.AddRoute(route, false); } else { PTRACE(3, "Fwd\tUnconditional Forward or on Busy to non-local or currently not registered alias " << forwardDestination); } } } skipOrginalForward = true; } } } else if ((forwardType == FORWARD_NOANSWER) || (forwardType == FORWARD_ERROR)) { if (IsIPAddress(forwardDestination)) { // set a route if forward to IP PString destinationIp = forwardDestination; PStringArray adr_parts = destinationIp.Tokenise(":", FALSE); PString ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); if (port == 0) port = GK_DEF_ENDPOINT_SIGNAL_PORT; Route route("ForwardNoAnswerOrError", SocketToH225TransportAddr(ip, port), NO_ANSWER_PRIORITY + recursionDepth); route.m_destNumber = forwardDestination; route.m_destOutNumber = forwardDestination; destination.AddRoute(route); } else { H225_ArrayOf_AliasAddress forwardAliases; forwardAliases.SetSize(1); H323SetAliasAddress(forwardDestination, forwardAliases[0]); endptr ep = RegistrationTable::Instance()->FindByAliases(forwardAliases); //PTRACE(0, "JW forwarding NoAnswer to " << forwardDestination << " ep=" << ep); // check if destination also has uncond/busy forwarding rules params["c"] = forwardDestination; params["p"] = ""; if (FindEPForwardingRules(params, recursionDepth+1, NO_ANSWER_PRIORITY, destination)) { PTRACE(5, "Fwd\tSkipping forward to " << forwardDestination << " (also redirected uncond/busy)"); } else { if (ep) { // add a NoAnswer route, lower recursion depth is given more priority Route route("ForwardNoAnswerOrError", ep, NO_ANSWER_PRIORITY + recursionDepth); route.m_destNumber = forwardDestination; route.m_destOutNumber = forwardDestination; destination.AddRoute(route, false); } else { PTRACE(3, "Fwd\tForward on NoAnswer or Error to non-local or currently not registered alias " << forwardDestination); } } } } else { PTRACE(1, "Fwd\tUnsupported forward type " << forwardType); } } } delete result; #endif // HAS_DATABASE return skipOrginalForward; } namespace { // anonymous namespace SimpleCreator ForwardingPolicyCreator("forwarding"); } } // end of namespace Routing gnugk-3.4/PaxHeaders.16356/RasSrv.cxx0000644000175000001440000000005012207613561015470 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/RasSrv.cxx0000644000175000001440000043100412207613561014433 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // RAS Server for GNU Gatekeeper // // Copyright (c) Citron Network Inc. 2001-2003 // Copyright (c) 2000-2013, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #include #include #include #include #include #include "gk.h" #include "gk_const.h" #include "stl_supp.h" #include "RasPDU.h" #include "RasTbl.h" #include "SoftPBX.h" #include "Routing.h" #include "GkClient.h" #include "GkStatus.h" #include "Neighbor.h" #include "ProxyChannel.h" #include "h323util.h" #include "gkauth.h" #include "gkacct.h" #include "gktimer.h" #include "RasSrv.h" #include "config.h" #ifdef HAS_H460 #include #ifdef HAS_H46018 #include #endif #endif const char *LRQFeaturesSection = "RasSrv::LRQFeatures"; const char *RRQFeatureSection = "RasSrv::RRQFeatures"; using namespace std; using Routing::Route; class RegistrationRequestPDU : public RasPDU { public: RegistrationRequestPDU(GatekeeperMessage *m) : RasPDU(m) {} // override from class RasPDU virtual bool Process(); struct Creator : public RasPDU::Creator { virtual RasMsg *operator()(GatekeeperMessage *m) const { return new RegistrationRequestPDU(m); } }; private: bool BuildRCF(const endptr &, bool = false); bool BuildRRJ(unsigned, bool = false); }; class AdmissionRequestPDU : public RasPDU { public: AdmissionRequestPDU(GatekeeperMessage *m) : RasPDU(m) {} // override from class RasPDU virtual bool Process(); struct Creator : public RasPDU::Creator { virtual RasMsg *operator()(GatekeeperMessage *m) const { return new AdmissionRequestPDU(m); } }; private: enum { e_acf = -1, e_routeRequest = -2 }; bool BuildReply(int, bool h460 = false, CallRec * rec = NULL); /** @return A string that can be used to identify a calling number. */ PString GetCallingStationId( /// additional data, like call record and requesting endpoint ARQAuthData& authData ) const; /** @return A string that can be used to identify a calling number. */ PString GetCalledStationId( /// additional data, like call record and requesting endpoint ARQAuthData& authData ) const; /** @return A string that can be used to identify the billing number. */ PString GetCallLinkage( /// additional data, like call record and requesting endpoint ARQAuthData& authData ) const; endptr RequestingEP, CalledEP; PString destinationString; }; template<> H225_NonStandardParameter *RasPDU::GetNonStandardParam() { return NULL; } // RAS message abbreviations const char *RasName[] = { "GRQ", // Gatekeeper Request "GCF", // Gatekeeper Confirm "GRJ", // Gatekeeper Reject "RRQ", // Registration Request "RCF", // Registration Confirm "RRJ", // Registration Reject "URQ", // Unregistration Request "UCF", // Unregistration Confirm "URJ", // Unregistration Reject "ARQ", // Admission Request "ACF", // Admission Confirm "ARJ", // Admission Reject "BRQ", // Bandwidth Request "BCF", // Bandwidth Confirm "BRJ", // Bandwidth Reject "DRQ", // Disengage Request "DCF", // Disengage Confirm "DRJ", // Disengage Reject "LRQ", // Location Request "LCF", // Location Confirm "LRJ", // Location Reject "IRQ", // Infomation Request "IRR", // Infomation Request Response "NonStandardMessage", "UnknownMessageResponse", "RIP", // Request In Progress "RAI", // Resources Available Indicate "RAC", // Resources Available Confirm "IACK", // Infomation Request Acknowledgement "INAK", // Infomation Request Negative Acknowledgement "SCI", // Service Control Indication "SCR", // Service Control Response "NotRecognized" // for new messages not recognized by the supported // H.323 version }; // struct GatekeeperMessage const char *GatekeeperMessage::GetTagName() const { return (GetTag() <= MaxRasTag) ? RasName[GetTag()] : RasName[MaxRasTag+1]; } bool GatekeeperMessage::Read(RasListener *socket) { m_socket = socket; const int buffersize = 4096; BYTE buffer[buffersize]; if (!socket->Read(buffer, buffersize)) { PTRACE(1, "RAS\tRead error " << socket->GetErrorCode(PSocket::LastReadError) << '/' << socket->GetErrorNumber(PSocket::LastReadError) << ": " << socket->GetErrorText(PSocket::LastReadError)); SNMP_TRAP(10, SNMPError, Network, "RAS read error : " + socket->GetErrorText(PSocket::LastReadError)); return false; } socket->GetLastReceiveAddress(m_peerAddr, m_peerPort); UnmapIPv4Address(m_peerAddr); PTRACE(2, "RAS\tRead from " << AsString(m_peerAddr, m_peerPort)); m_rasPDU = PPER_Stream(buffer, socket->GetLastReadCount()); bool result = m_recvRAS.Decode(m_rasPDU); PTRACE_IF(1, !result, "RAS\tCould not decode message from " << AsString(m_peerAddr, m_peerPort)); return result; } #ifdef HAS_H46017 bool GatekeeperMessage::Read(const PBYTEArray & buffer) { m_rasPDU = PPER_Stream(buffer); bool result = m_recvRAS.Decode(m_rasPDU); PTRACE_IF(1, !result, "RAS\tCould not decode H.460.17 message"); return result; } #endif bool GatekeeperMessage::Reply() const { #ifdef HAS_H46017 if (m_h46017Socket) { return m_h46017Socket->SendH46017Message(m_replyRAS); } #endif if (m_socket) { return m_socket->SendRas(m_replyRAS, m_peerAddr, m_peerPort); } else { return false; } } // class RasListener RasListener::RasListener(const Address & addr, WORD pt) : UDPSocket(0, addr.GetVersion() == 6 ? AF_INET6 : AF_INET), m_ip(addr) { if (!Listen(addr, 0, pt, PSocket::CanReuseAddress)) { PTRACE(1, "RAS\tCould not open listening socket at " << AsString(addr, pt) << " - error " << GetErrorCode(PSocket::LastGeneralError) << '/' << GetErrorNumber(PSocket::LastGeneralError) << ": " << GetErrorText(PSocket::LastGeneralError) ); Close(); } SetWriteTimeout(1000); SetName(AsString(addr, pt) + "(U)"); if (Toolkit::Instance()->IsPortNotificationActive()) Toolkit::Instance()->PortNotification(RASPort, PortOpen, "udp", addr, pt); m_signalPort = 0; #ifdef HAS_TLS m_tlsSignalPort = 0; #endif // note: this won't be affected by reloading m_virtualInterface = (!GkConfig()->GetString("NetworkInterfaces", "").IsEmpty()); // Check if we have external IP setting if (!m_virtualInterface) { m_virtualInterface = (!GkConfig()->GetString("ExternalIP", "").IsEmpty()); } } RasListener::~RasListener() { if (Toolkit::Instance()->IsPortNotificationActive()) Toolkit::Instance()->PortNotification(RASPort, PortClose, "udp", m_ip, GetPort()); PTRACE(1, "RAS\tDelete listener " << GetName()); } GatekeeperMessage *RasListener::ReadRas() { PTRACE(4, "RAS\tReceiving on " << GetName()); GatekeeperMessage *msg = new GatekeeperMessage(); if (!(msg->Read(this) && Filter(msg))) { delete msg; return NULL; } if ((msg->GetTag() != H225_RasMessage::e_serviceControlIndication && msg->GetTag() != H225_RasMessage::e_serviceControlResponse) || PTrace::CanTrace(5)) { if (PTrace::CanTrace(3)) PTRACE(3, "RAS\n" << setprecision(2) << msg->m_recvRAS); else PTRACE(2, "RAS\tReceived " << msg->GetTagName() << " from " << AsString(msg->m_peerAddr, msg->m_peerPort)); } msg->m_localAddr = GetLocalAddr(msg->m_peerAddr); return msg; } bool RasListener::SendRas(const H225_RasMessage & rasobj, const Address & addr, WORD pt) { if ( ((rasobj.GetTag() != H225_RasMessage::e_serviceControlIndication && rasobj.GetTag() != H225_RasMessage::e_serviceControlResponse) && PTrace::CanTrace(3)) || PTrace::CanTrace(5)) PTRACE(3, "RAS\tSend to " << AsString(addr, pt) << '\n' << setprecision(2) << rasobj); else PTRACE(2, "RAS\tSend " << RasName[rasobj.GetTag()] << " to " << AsString(addr, pt)); PPER_Stream wtstrm; rasobj.Encode(wtstrm); wtstrm.CompleteEncoding(); m_wmutex.Wait(); bool result = WriteTo(wtstrm.GetPointer(), wtstrm.GetSize(), addr, pt); m_wmutex.Signal(); if (result) PTRACE(5, "RAS\tSent Successful"); else { PTRACE(1, "RAS\tWrite error " << GetErrorCode(PSocket::LastWriteError) << '/' << GetErrorNumber(PSocket::LastWriteError) << ": " << GetErrorText(PSocket::LastWriteError) ); SNMP_TRAP(10, SNMPError, Network, "RAS write error: " + GetErrorText(PSocket::LastWriteError)); } return result; } PIPSocket::Address RasListener::GetPhysicalAddr(const Address & /* addr */) const { // Return the physical address. This is used when setting sockets return m_ip; } PIPSocket::Address RasListener::GetLocalAddr(const Address & addr) const { return m_virtualInterface ? Toolkit::Instance()->GetRouteTable()->GetLocalAddress(addr) : m_ip; } H225_TransportAddress RasListener::GetRasAddress(const Address & addr) const { return SocketToH225TransportAddr(GetLocalAddr(addr), GetPort()); } H225_TransportAddress RasListener::GetCallSignalAddress(const Address & addr) const { return SocketToH225TransportAddr(GetLocalAddr(addr), m_signalPort); } bool RasListener::Filter(GatekeeperMessage *msg) const { unsigned tag = msg->GetTag(); if (tag <= MaxRasTag) return true; PTRACE(1, "RAS\tInvalid RAS message tag " << tag); return false; } // class BroadcastListener class BroadcastListener : public RasListener { public: BroadcastListener(WORD); // override from class RasListener virtual bool Filter(GatekeeperMessage *) const; }; BroadcastListener::BroadcastListener(WORD pt) : RasListener(INADDR_ANY, pt) { SetName(AsString(INADDR_ANY, pt) + "(Bcast)"); m_virtualInterface = true; } bool BroadcastListener::Filter(GatekeeperMessage *msg) const { const unsigned tag = msg->GetTag(); if (tag == H225_RasMessage::e_gatekeeperRequest || tag == H225_RasMessage::e_locationRequest) return true; PTRACE(1, "RAS\tIgnoring broadcasted RAS message " << msg->GetTagName()); return false; } // class MulticastListener class MulticastListener : public RasListener { public: MulticastListener(const Address &, WORD); // override from class RasListener virtual bool Filter(GatekeeperMessage *) const; }; // we must listen to INADDR_ANY to get multicast packets, but we need to // call setsockopt() for each IP so all interfaces join the multicast group (tested on Linux 2.6.x) MulticastListener::MulticastListener(const Address & addr, WORD pt) : RasListener(INADDR_ANY, pt) { SetName(AsString(addr, pt) + "(Mcast)"); Address multiaddr(GkConfig()->GetString("MulticastGroup", GK_DEF_MULTICAST_GROUP)); struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = multiaddr; mreq.imr_interface.s_addr = addr; if (::setsockopt(GetHandle(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0) { PTRACE(1, "RAS\tCan't join multicast group " << multiaddr); Close(); } port = pt; } bool MulticastListener::Filter(GatekeeperMessage * msg) const { unsigned tag = msg->GetTag(); if (tag == H225_RasMessage::e_gatekeeperRequest || tag == H225_RasMessage::e_locationRequest) return true; PTRACE(1, "RAS\tInvalid multicasted RAS message tag " << msg->GetTagName()); return false; } // class RasMsg void RasMsg::Exec() { PTRACE(1, "RAS\t" << m_msg->GetTagName() << " Received from " << AsString(m_msg->m_peerAddr, m_msg->m_peerPort)); if (Process()) Reply(); } bool RasMsg::IsFrom(const PIPSocket::Address & addr, WORD pt) const { return (addr == m_msg->m_peerAddr) && (pt == 0 || pt == m_msg->m_peerPort); } void RasMsg::GetRasAddress(H225_TransportAddress & result) const { result = SocketToH225TransportAddr(m_msg->m_localAddr, m_msg->m_socket ? m_msg->m_socket->GetPort() : 0); } void RasMsg::GetCallSignalAddress(H225_TransportAddress & result) const { #ifdef HAS_H46017 if (m_msg->m_h46017Socket) result = SocketToH225TransportAddr(m_msg->m_localAddr, m_msg->m_h46017Socket->GetPort()); else #endif result = SocketToH225TransportAddr(m_msg->m_localAddr, m_msg->m_socket ? m_msg->m_socket->GetSignalPort() : 0); } bool RasMsg::EqualTo(const RasMsg *other) const { if (GetTag() != other->GetTag()) return false; if (GetSeqNum() != other->GetSeqNum()) return false; if ((m_msg->m_peerPort != other->m_msg->m_peerPort) || (m_msg->m_peerAddr != other->m_msg->m_peerAddr)) return false; // should we check the whole PDU? return true; } bool RasMsg::PrintStatus(const PString & log) { PTRACE(2, log); GkStatus::Instance()->SignalStatus(log + "\r\n", STATUS_TRACE_LEVEL_RAS); return true; // reply after logged } template inline void CopyNonStandardData(const ORAS & omsg, DRAS & dmsg) { if (omsg.HasOptionalField(ORAS::e_nonStandardData)) { dmsg.IncludeOptionalField(DRAS::e_nonStandardData); dmsg.m_nonStandardData = omsg.m_nonStandardData; } } // template class RasPDU template H225_NonStandardParameter *RasPDU::GetNonStandardParam() { return request.HasOptionalField(RAS::e_nonStandardData) ? &request.m_nonStandardData : 0; } template inline H225_RasMessage & RasPDU::BuildConfirm() { typedef typename RasInfo::ConfirmTag ConfirmTag; typedef typename RasInfo::ConfirmType ConfirmType; unsigned tag = ConfirmTag(); if (m_msg->m_replyRAS.GetTag() != tag) m_msg->m_replyRAS.SetTag(tag); ConfirmType & confirm = m_msg->m_replyRAS; confirm.m_requestSeqNum = request.m_requestSeqNum; // CopyNonStandardData(request, confirm); return m_msg->m_replyRAS; } template inline H225_RasMessage & RasPDU::BuildReject(unsigned reason) { typedef typename RasInfo::RejectTag RejectTag; typedef typename RasInfo::RejectType RejectType; unsigned tag = RejectTag(); if (m_msg->m_replyRAS.GetTag() != tag) m_msg->m_replyRAS.SetTag(tag); RejectType & reject = m_msg->m_replyRAS; reject.m_requestSeqNum = request.m_requestSeqNum; reject.m_rejectReason.SetTag(reason); // CopyNonStandardData(request, reject); return m_msg->m_replyRAS; } // class GkInterface GkInterface::GkInterface(const PIPSocket::Address & addr) : m_address(addr) { m_rasPort = m_multicastPort = m_signalPort = m_statusPort = 0; m_rasListener = NULL; m_multicastListener = NULL; m_callSignalListener = NULL; m_statusListener = NULL; m_rasSrv = NULL; #ifdef HAS_TLS m_tlsSignalPort = 0; m_tlsCallSignalListener = NULL; #endif } GkInterface::~GkInterface() { if (m_rasListener) m_rasListener->Close(); if (m_multicastListener) m_multicastListener->Close(); if (m_callSignalListener) m_rasSrv->CloseListener(m_callSignalListener); if (m_statusListener) m_rasSrv->CloseListener(m_statusListener); } bool GkInterface::CreateListeners(RasServer *RasSrv) { m_rasSrv = RasSrv; WORD rasPort = (WORD)GkConfig()->GetInteger("UnicastRasPort", GK_DEF_UNICAST_RAS_PORT); WORD multicastPort = (WORD)(Toolkit::AsBool(GkConfig()->GetString("UseMulticastListener", "1")) ? GkConfig()->GetInteger("MulticastPort", GK_DEF_MULTICAST_PORT) : 0); WORD signalPort = (WORD)GkConfig()->GetInteger(RoutedSec, "CallSignalPort", GK_DEF_CALL_SIGNAL_PORT); #ifdef HAS_TLS WORD tlsSignalPort = (WORD)GkConfig()->GetInteger(RoutedSec, "TLSCallSignalPort", GK_DEF_TLS_CALL_SIGNAL_PORT); #endif WORD statusPort = (WORD)GkConfig()->GetInteger("StatusPort", GK_DEF_STATUS_PORT); if (SetListener(rasPort, m_rasPort, m_rasListener, &GkInterface::CreateRasListener)) m_rasSrv->AddListener(m_rasListener); if (SetListener(multicastPort, m_multicastPort, m_multicastListener, &GkInterface::CreateMulticastListener)) m_rasSrv->AddListener(m_multicastListener); if (SetListener(signalPort, m_signalPort, m_callSignalListener, &GkInterface::CreateCallSignalListener)) m_rasSrv->AddListener(m_callSignalListener); #ifdef HAS_TLS if (Toolkit::Instance()->IsTLSEnabled()) { if (SetListener(tlsSignalPort, m_tlsSignalPort, m_tlsCallSignalListener, &GkInterface::CreateTLSCallSignalListener)) { m_rasSrv->AddListener(m_tlsCallSignalListener); } } #endif if (SetListener(statusPort, m_statusPort, m_statusListener, &GkInterface::CreateStatusListener)) m_rasSrv->AddListener(m_statusListener); if (m_rasListener && m_callSignalListener) { if (RasSrv->IsGKRouted()) { m_rasListener->SetSignalPort(m_signalPort); #ifdef HAS_TLS if (m_tlsCallSignalListener) m_rasListener->SetTLSSignalPort(m_tlsSignalPort); #endif if (m_multicastListener) { m_multicastListener->SetSignalPort(m_signalPort); } } else { RasSrv->CloseListener(m_callSignalListener); m_callSignalListener = NULL; #ifdef HAS_TLS RasSrv->CloseListener(m_tlsCallSignalListener); m_tlsCallSignalListener = NULL; #endif } } if (RasSrv->IsGKRouted()) { return (m_rasListener != NULL) && (m_callSignalListener != NULL); } else { return m_rasListener != NULL; } } bool GkInterface::IsReachable(const Address *addr) const { return Toolkit::Instance()->GetRouteTable(false)->GetLocalAddress(*addr) == m_address; } bool GkInterface::ValidateSocket(IPSocket *socket, WORD & port) { if (socket) { if (socket->IsOpen()) { PTRACE(1, "Listening to " << socket->GetName()); // get the real bound port port = socket->GetPort(); return true; } else { PTRACE(1, "Can't listen to " << socket->GetName()); delete socket; socket = NULL; } } return false; } RasListener *GkInterface::CreateRasListener() { return new RasListener(m_address, m_rasPort); } MulticastListener *GkInterface::CreateMulticastListener() { return (m_multicastPort && !IsLoopback(m_address) && (m_address.GetVersion() != 6)) ? new MulticastListener(m_address, m_multicastPort) : 0; } CallSignalListener *GkInterface::CreateCallSignalListener() { return m_rasSrv->IsGKRouted() ? new CallSignalListener(m_address, m_signalPort) : NULL; } #ifdef HAS_TLS TLSCallSignalListener *GkInterface::CreateTLSCallSignalListener() { return m_rasSrv->IsGKRouted() ? new TLSCallSignalListener(m_address, m_tlsSignalPort) : NULL; } #endif StatusListener *GkInterface::CreateStatusListener() { return new StatusListener(m_address, m_statusPort); } // class RasHandler RasHandler::RasHandler() { m_rasSrv = RasServer::Instance(); fill(m_tagArray, m_tagArray + MaxRasTag + 1, false); // can't register now since virtual function table is not ready //m_rasSrv->RegisterHandler(this); } bool RasHandler::IsExpected(const RasMsg *ras) const { return m_tagArray[ras->GetTag()]; } void RasHandler::AddFilter(unsigned tag) { m_tagArray[tag] = true; } // class RasRequester RasRequester::RasRequester(H225_RasMessage & req) : m_request(&req), m_loAddr(GNUGK_INADDR_ANY) { Init(); } RasRequester::RasRequester(H225_RasMessage & req, const Address & addr) : m_request(&req), m_loAddr(addr) { Init(); } void RasRequester::Init() { m_txPort = 0; m_seqNum = m_rasSrv->GetRequestSeqNum(); m_timeout = 0; m_retry = 0; m_iterator = m_queue.end(); AddFilter(H225_RasMessage::e_requestInProgress); } RasRequester::~RasRequester() { m_sync.Signal(); DeleteObjectsInContainer(m_queue); } bool RasRequester::WaitForResponse(int timeout) { m_timeout = timeout; while (m_iterator == m_queue.end()) { int passed = (int)((PTime() - m_sentTime).GetMilliSeconds()); if (m_timeout > passed && m_sync.Wait(m_timeout - passed)) { if (m_timeout > 0) { continue; } else { break; } } if (!OnTimeout()) { break; } } return m_iterator != m_queue.end(); } RasMsg *RasRequester::GetReply() { PWaitAndSignal lock(m_qmutex); return m_iterator != m_queue.end() ? *m_iterator++ : 0; } bool RasRequester::IsExpected(const RasMsg *ras) const { return RasHandler::IsExpected(ras) && (ras->GetSeqNum() == m_seqNum) && ras->IsFrom(m_txAddr, m_txPort); } void RasRequester::Process(RasMsg *ras) { if (ras->GetTag() == H225_RasMessage::e_requestInProgress) { H225_RequestInProgress & rip = (*ras)->m_recvRAS; m_timeout = rip.m_delay; m_sentTime = PTime(); delete ras; ras = NULL; } else { AddReply(ras); m_timeout = 0; } m_sync.Signal(); } void RasRequester::Stop() { m_timeout = 0; m_iterator = m_queue.end(); m_sync.Signal(); } bool RasRequester::SendRequest(const PIPSocket::Address & addr, WORD pt, int r) { m_txAddr = addr, m_txPort = pt, m_retry = r; m_sentTime = PTime(); return m_rasSrv->SendRas(*m_request, m_txAddr, m_txPort, m_loAddr); } bool RasRequester::OnTimeout() { return m_retry > 0 ? SendRequest(m_txAddr, m_txPort, --m_retry) : false; } void RasRequester::AddReply(RasMsg *ras) { PWaitAndSignal lock(m_qmutex); m_queue.push_back(ras); if (m_iterator == m_queue.end()) --m_iterator; } // class RasServer RasServer::RasServer() : Singleton("RasSrv") { SetName("RasSrv"); requestSeqNum = 0; listeners = NULL; broadcastListener = NULL; sigHandler = NULL; authList = NULL; acctList = NULL; gkClient = NULL; neighbors = NULL; vqueue = NULL; GKRoutedSignaling = false; GKRoutedH245 = false; bRemoveCallOnDRQ = true; altGKsSize = 0; epLimit = callLimit = P_MAX_INDEX; redirectGK = e_noRedirect; } RasServer::~RasServer() { delete authList; delete acctList; delete neighbors; delete gkClient; PWaitAndSignal lock(requests_mutex); DeleteObjectsInContainer(requests); } void RasServer::Stop() { #ifndef hasPTLibTraceOnShutdownBug PTRACE(1, "GK\tStopping RasServer..."); #endif SNMP_TRAP(2, SNMPInfo, General, "GnuGk stopping"); PWaitAndSignal lock(m_deletionPreventer); ForEachInContainer(handlers, mem_vfun(&RasHandler::Stop)); delete vqueue; // delete virtual queues before Jobs, otherwise the jobs will wait for the queues vqueue = NULL; RegularJob::Stop(); } // set the signaling mode according to the config file // don't change it if not specified in the config void RasServer::SetRoutedMode() { if (GkConfig()->HasKey(RoutedSec, "GKRouted")) GKRoutedSignaling = Toolkit::AsBool(GkConfig()->GetString(RoutedSec, "GKRouted", "0")); if (GkConfig()->HasKey(RoutedSec, "H245Routed")) GKRoutedH245 = Toolkit::AsBool(GkConfig()->GetString(RoutedSec, "H245Routed", "0")); SetRoutedMode(GKRoutedSignaling, GKRoutedH245); } void RasServer::SetRoutedMode(bool routedSignaling, bool routedH245) { GKRoutedSignaling = routedSignaling; if (GKRoutedSignaling) { if (sigHandler) sigHandler->LoadConfig(); else sigHandler = new HandlerList(); } else { // warning: dangerous delete sigHandler; sigHandler = NULL; } GKRoutedH245 = GKRoutedSignaling ? routedH245 : false; const char *modemsg = GKRoutedSignaling ? "Routed" : "Direct"; const char *h245msg = GKRoutedH245 ? "Enabled" : "Disabled"; PTRACE(2, "GK\tUsing " << modemsg << " Signalling"); PTRACE(2, "GK\tH.245 Routed " << h245msg); const char * h245tunnelingmsg = Toolkit::AsBool(GkConfig()->GetString("RoutedMode", "H245TunnelingTranslation", "0")) ? "Enabled" : "Disabled"; PTRACE(2, "GK\tH.245 tunneling translation " << h245tunnelingmsg); #ifdef HAS_H46017 const char * h46017msg = Toolkit::AsBool(GkConfig()->GetString("RoutedMode", "EnableH46017", "0")) ? "Enabled" : "Disabled"; PTRACE(2, "GK\tH.460.17 Registrations " << h46017msg); #endif #ifdef HAS_H46018 const char * h46018msg = Toolkit::AsBool(GkConfig()->GetString("RoutedMode", "EnableH46018", "0")) ? "Enabled" : "Disabled"; PTRACE(2, "GK\tH.460.18 Registrations " << h46018msg); #endif } void RasServer::SetENUMServers() { #if hasSETENUMSERVERS PString servers = GkConfig()->GetString(RoutedSec, "ENUMservers", ""); PStringArray serverlist(servers.Tokenise(",", false)); if (serverlist.GetSize() > 0) { PDNS::SetENUMServers(serverlist); PTRACE(2, "GK\tLoaded ENUM servers " << serverlist); } else { PTRACE(2, "GK\tNo ENUMservers set, using defaults"); } #else #if P_DNS PTRACE(2, "GK\tSetENUMServers not available, using defaults"); #else PTRACE(2, "GK\tNo ENUM Routing policy available."); #endif #endif } void RasServer::SetRDSServers() { #if hasRDS PString servers = GkConfig()->GetString(RoutedSec, "RDSservers", ""); PStringArray serverlist(servers.Tokenise(",", false)); if (serverlist.GetSize() > 0) { PDNS::SetRDSServers(serverlist); PTRACE(2, "GK\tLoaded RDS servers " << serverlist); } else { PTRACE(2, "GK\tNo RDSservers set, using defaults"); } #else PTRACE(2, "GK\tNo RDS Routing policy available."); #endif } bool RasServer::AcceptUnregisteredCalls(const PIPSocket::Address & addr) const { if (Toolkit::AsBool(GkConfig()->GetString(RoutedSec, "AcceptUnregisteredCalls", "0"))) return true; return Toolkit::AsBool(GkConfig()->GetString(RoutedSec, "AcceptNeighborsCalls", "1")) ? neighbors->CheckIP(addr) : false; } bool RasServer::AcceptPregrantedCalls(const H225_Setup_UUIE & setupBody, const PIPSocket::Address & addr) const { if (!Toolkit::AsBool(GkConfig()->GetString(RoutedSec, "PregrantARQ", "0"))) return false; if (!setupBody.HasOptionalField(H225_Setup_UUIE::e_endpointIdentifier)) return false; endptr ep = RegistrationTable::Instance()->FindByEndpointId(setupBody.m_endpointIdentifier); if (ep) { PIPSocket::Address epIP; WORD epPort = 0; if (GetIPAndPortFromTransportAddr(ep->GetCallSignalAddress(), epIP, epPort) && epIP == addr) { PTRACE(3, "Q931\tAccepting pre-granted Call"); return true; } } return false; } bool RasServer::IsCallFromTraversalClient(const PIPSocket::Address & addr) const { return neighbors->IsTraversalClient(addr); } bool RasServer::IsCallFromTraversalServer(const PIPSocket::Address & addr) const { return neighbors->IsTraversalServer(addr); } bool RasServer::RegisterHandler(RasHandler *handler) { PWaitAndSignal lock(handlers_mutex); handlers.push_front(handler); return true; } bool RasServer::UnregisterHandler(RasHandler *handler) { PWaitAndSignal lock(handlers_mutex); handlers.remove(handler); return true; } void RasServer::LoadConfig() { GetAlternateGK(); vector
GKHome; PString Home(Toolkit::Instance()->GetGKHome(GKHome)); PTRACE(2, "GK\tHome = " << Home); bool bUseBroadcastListener = Toolkit::AsBool(GkConfig()->GetString("UseBroadcastListener", "1")); int hsize = GKHome.size(); if (hsize == 0) { PTRACE(1, "Error: No interface for RAS?"); SNMP_TRAP(10, SNMPError, Configuration, "No RAS interface"); return; } SocketsReader::CleanUp(); ifiterator it = interfaces.begin(); while (it != interfaces.end()) { int i = -1; while (++i < hsize) if ((*it)->IsBoundTo(&GKHome[i])) break; if (i == hsize) { // close unused listeners GkInterface * r = *it; it = interfaces.erase(it); delete r; } else ++it; } if (broadcastListener && !bUseBroadcastListener) { broadcastListener->Close(); broadcastListener = NULL; } RemoveClosed(false); // delete the closed sockets next time for (int i = 0; i < hsize; ++i) { Address addr(GKHome[i]); ifiterator iter = find_if(interfaces.begin(), interfaces.end(), bind2nd(mem_fun(&GkInterface::IsBoundTo), &addr)); if (iter == interfaces.end()) { GkInterface *gkif = CreateInterface(addr); if (gkif->CreateListeners(this)) { interfaces.push_back(gkif); } else { delete gkif; gkif = NULL; } } else { GkInterface *gkif = *iter; // re-create if changed if (!gkif->CreateListeners(this)) { interfaces.erase(iter); delete gkif; gkif = NULL; } } } if ((m_socksize == 0) || (interfaces.empty())) { PTRACE(1, "Error: No valid RAS socket!"); SNMP_TRAP(10, SNMPError, Configuration, "No RAS socket"); return; } #if NEED_BROADCASTLISTENER if (bUseBroadcastListener && !broadcastListener) { WORD rasPort = (WORD)GkConfig()->GetInteger("UnicastRasPort", GK_DEF_UNICAST_RAS_PORT); broadcastListener = new BroadcastListener(rasPort); if (broadcastListener->IsOpen()) { PTRACE(1, "RAS\tBroadcast listener listening at " << broadcastListener->GetName()); AddSocket(broadcastListener); } else { PTRACE(1, "RAS\tCannot start broadcast listener at " << broadcastListener->GetName()); delete broadcastListener; broadcastListener = NULL; } } #endif #ifdef HAS_H46018 // create mutiplex RTP listeners if (Toolkit::AsBool(GkConfig()->GetString(ProxySection, "RTPMultiplexing", "0"))) { if (Toolkit::Instance()->IsH46018Enabled()) { MultiplexedRTPHandler::Instance()->OnReload(); } else { PTRACE(1, "Warning: Must enable H.460.19 for RTP multiplexing"); } } else { // if we had a multiplex listener configured before the reload, but not anymore, then delete it if (MultiplexedRTPHandler::InstanceExists()) delete MultiplexedRTPHandler::Instance(); } #endif #ifdef HAS_H46026 if (Toolkit::Instance()->IsH46026Enabled()) { H46026RTPHandler::Instance()->OnReload(); } #endif if (listeners) listeners->LoadConfig(); if (gkClient) gkClient->OnReload(); if (neighbors) neighbors->OnReload(); if (authList) authList->OnReload(); if (acctList) acctList->OnReload(); if (vqueue) vqueue->OnReload(); Routing::Analyzer::Instance()->OnReload(); Routing::ExplicitPolicy::OnReload(); bRemoveCallOnDRQ = Toolkit::AsBool(GkConfig()->GetString(RoutedSec, "RemoveCallOnDRQ", "1")); // read [ReplyToRasAddress] section m_replyras.clear(); PStringToString ras_rules(GkConfig()->GetAllKeyValues("ReplyToRasAddress")); for (PINDEX i = 0; i < ras_rules.GetSize(); ++i) { PString network = ras_rules.GetKeyAt(i); bool setting = Toolkit::AsBool(ras_rules.GetDataAt(i)); if (!network.IsEmpty()) { NetworkAddress addr = NetworkAddress(network); m_replyras[addr] = setting; } } } bool RasServer::ReplyToRasAddress(const NetworkAddress & ip) const { NetworkAddress bestmatch; bool result = false; std::map::const_iterator iter = m_replyras.begin(); while (iter != m_replyras.end()) { if ((ip << iter->first) && (iter->first.GetNetmaskLen() >= bestmatch.GetNetmaskLen())) { bestmatch = iter->first; result = iter->second; } ++iter; } return result; } void RasServer::AddListener(RasListener *socket) { AddSocket(socket); } void RasServer::AddListener(TCPListenSocket *socket) { if (socket->IsOpen()) listeners->AddListener(socket); else { delete socket; socket = NULL; } } bool RasServer::CloseListener(TCPListenSocket *socket) { return listeners->CloseListener(socket); } WORD RasServer::GetRequestSeqNum() { PWaitAndSignal lock(seqNumMutex); return ++requestSeqNum; } GkInterface *RasServer::SelectDefaultInterface(unsigned version) { if (interfaces.empty()) return NULL; PIPSocket::Address defIP = Toolkit::Instance()->GetRouteTable(false)->GetLocalAddress(version); ifiterator iter = interfaces.begin(); while (iter != interfaces.end()) { GkInterface * intface = *iter++; if (intface->GetRasListener()->GetPhysicalAddr(defIP) == defIP) return intface; } PTRACE(1, "RasSrv\tWARNING: No route detected using first interface"); return interfaces.front(); } GkInterface *RasServer::SelectInterface(const Address & addr) { if (interfaces.empty()) return NULL; ifiterator iter = find_if(interfaces.begin(), interfaces.end(), bind2nd(mem_fun(&GkInterface::IsReachable), &addr)); if (iter != interfaces.end()) return *iter; else return SelectDefaultInterface(addr.GetVersion()); } const GkInterface *RasServer::SelectInterface(const Address & addr) const { return const_cast(this)->SelectInterface(addr); // cast away const } RasListener * RasServer::GetRasListener(const Address & addr) const { const GkInterface * interf = SelectInterface(addr); return interf ? interf->GetRasListener() : NULL; } PIPSocket::Address RasServer::GetLocalAddress(const Address & addr) const { RasListener * listener = GetRasListener(addr); return listener ? listener->GetPhysicalAddr(addr) : PIPSocket::Address(0); } PIPSocket::Address RasServer::GetMasqAddress(const Address & addr) const { RasListener * listener = GetRasListener(addr); return listener ? listener->GetLocalAddr(addr) : PIPSocket::Address(0); } H225_TransportAddress RasServer::GetRasAddress(const Address & addr) const { RasListener * listener = GetRasListener(addr); return listener ? listener->GetRasAddress(addr) : H225_TransportAddress(0); } H225_TransportAddress RasServer::GetCallSignalAddress(const Address & addr) const { RasListener * listener = GetRasListener(addr); return listener ? listener->GetCallSignalAddress(addr) : H225_TransportAddress(0); } bool RasServer::SendRas(const H225_RasMessage & rasobj, const Address & addr, WORD pt, RasListener *socket) { if (socket == NULL) { GkInterface * inter = SelectInterface(addr); if (inter == NULL) return false; else socket = inter->GetRasListener(); } return socket->SendRas(rasobj, addr, pt); } bool RasServer::SendRas(const H225_RasMessage & rasobj, const H225_TransportAddress & dest, RasListener *socket) { PIPSocket::Address addr; WORD pt; if (GetIPAndPortFromTransportAddr(dest, addr, pt)) return SendRas(rasobj, addr, pt, socket); PTRACE(1, "RAS\tInvalid address when trying to send " << rasobj.GetTagName()); return false; } bool RasServer::SendRas(const H225_RasMessage & rasobj, const Address & addr, WORD pt, const Address & local) { GkInterface * inter = SelectInterface(local); if (inter == NULL) return false; RasListener * listener = inter->GetRasListener(); if (listener == NULL) return false; return listener->SendRas(rasobj, addr, pt); } bool RasServer::SendRIP(H225_RequestSeqNum seqNum, unsigned ripDelay, const Address & addr, WORD port) { H225_RasMessage ras_msg; ras_msg.SetTag(H225_RasMessage::e_requestInProgress); H225_RequestInProgress & rip = ras_msg; rip.m_requestSeqNum.SetValue(seqNum); rip.m_delay = ripDelay; return SendRas(ras_msg, addr, port); } bool RasServer::IsRedirected(unsigned tag) const { if (redirectGK != e_noRedirect) return true; if (tag == H225_RasMessage::e_registrationRequest) return RegistrationTable::Instance()->Size() >= epLimit; if (tag == H225_RasMessage::e_admissionRequest) return CallTable::Instance()->Size() >= callLimit; return false; } bool RasServer::IsForwardedMessage(const H225_NonStandardParameter *nonStandardParam, const Address & rxAddr) const { // mechanism 1: forwarding detection per "flag" if (nonStandardParam && nonStandardParam->m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard) { const H225_H221NonStandard & nonStandard = nonStandardParam->m_nonStandardIdentifier; if (Toolkit::Instance()->GetInternalExtensionCode(nonStandard) == Toolkit::iecFailoverRAS) { PTRACE(5, "RAS\tForwarded RAS detected!"); return true; } // mechanism 2: forwarding detection per "from" } if (find(skipAddr.begin(), skipAddr.end(), rxAddr) != skipAddr.end()) { PTRACE(5, "RAS\tSkip forwarded RAS"); return true; } return false; } void RasServer::ForwardRasMsg(H225_RasMessage & msg) { if (altGKsSize <= 0) return; H225_NonStandardParameter oldParam, *nonStandardParam; bool hasStandardParam; PASN_Sequence *sobj; unsigned tag; H225_RequestSeqNum oldReqNum, *reqNum; // ATS 2004-01-16 Forward messages to alternates using our own sequence numbers // instead of using those supplied by the originator of the message, this will // result in clashes in RasMSG::EqualTo() by the receiver of this message switch (msg.GetTag()) { case H225_RasMessage::e_gatekeeperRequest: { tag = H225_GatekeeperRequest::e_nonStandardData; H225_GatekeeperRequest & o = msg; nonStandardParam = &o.m_nonStandardData; sobj = &o; // Get a pointer to the current sequence number reqNum = &o.m_requestSeqNum; // Make a copy of the old sequence number oldReqNum = *reqNum; // Set a new value in the sequence number o.m_requestSeqNum = GetRequestSeqNum(); break; } case H225_RasMessage::e_registrationRequest: { tag = H225_RegistrationRequest::e_nonStandardData; H225_RegistrationRequest & o = msg; nonStandardParam = &o.m_nonStandardData; sobj = &o; if (o.HasOptionalField(H225_RegistrationRequest::e_endpointIdentifier)) nonStandardParam->m_data = o.m_endpointIdentifier; reqNum = &o.m_requestSeqNum; oldReqNum = *reqNum; o.m_requestSeqNum = GetRequestSeqNum(); break; } case H225_RasMessage::e_unregistrationRequest: { tag = H225_UnregistrationRequest::e_nonStandardData; H225_UnregistrationRequest & o = msg; nonStandardParam = &o.m_nonStandardData; sobj = &o; reqNum = &o.m_requestSeqNum; oldReqNum = *reqNum; o.m_requestSeqNum = GetRequestSeqNum(); break; } default: PTRACE(2, "Warning: unsupported RAS message type for forwarding: " << msg.GetTagName()); return; } hasStandardParam = sobj->HasOptionalField(tag); if (hasStandardParam) oldParam = *nonStandardParam; else sobj->IncludeOptionalField(tag); // include the "this is a forwared message" tag (could be a static variable to increase performance) H225_NonStandardIdentifier & id = nonStandardParam->m_nonStandardIdentifier; id.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard & h221 = id; h221.m_t35CountryCode = Toolkit::t35cOpenOrg; h221.m_t35Extension = Toolkit::t35eFailoverRAS; h221.m_manufacturerCode = Toolkit::t35mOpenOrg; for (int i = 0; i < altGKsSize; ++i) { PTRACE(4, "Forwarding RAS to " << AsString(altGKsAddr[i], altGKsPort[i])); SendRas(msg, altGKsAddr[i], altGKsPort[i]); } // restore the old nonstandard field if (hasStandardParam) *nonStandardParam = oldParam; else sobj->RemoveOptionalField(tag); // restore the old sequence number // using the pointer and the old value. *reqNum = oldReqNum; } PString RasServer::GetParent() const { return gkClient->GetParent(); } bool RasServer::IsPassThroughRegistrant() { return (gkClient && gkClient->UsesAdditiveRegistration()); } bool RasServer::RemoveAdditiveRegistration(const H225_ArrayOf_AliasAddress & aliases) { return (gkClient && gkClient->AdditiveUnRegister(aliases)); } ProxyHandler * RasServer::GetSigProxyHandler() { return sigHandler ? sigHandler->GetSigHandler() : NULL; } ProxyHandler * RasServer::GetRtpProxyHandler() { return sigHandler ? sigHandler->GetRtpHandler() : NULL; } void RasServer::SelectH235Capability(const H225_GatekeeperRequest & grq, H225_GatekeeperConfirm & gcf) const { authList->SelectH235Capability(grq, gcf); } bool RasServer::ValidateAdditivePDU(RasPDU& ras, RRQAuthData& authData) { H225_RegistrationRequest & rrq = (ras)->m_recvRAS; H225_ArrayOf_ClearToken * tokens = rrq.HasOptionalField(H225_RegistrationRequest::e_tokens) ? &rrq.m_tokens : NULL; H225_ArrayOf_CryptoH323Token * cryptotokens = rrq.HasOptionalField(H225_RegistrationRequest::e_cryptoTokens) ? &rrq.m_cryptoTokens : NULL; return (gkClient && gkClient->AdditiveRegister(rrq.m_terminalAlias, authData.m_rejectReason, tokens, cryptotokens)); } bool RasServer::LogAcctEvent(int evt, callptr & call, time_t now) { return acctList->LogAcctEvent((GkAcctLogger::AcctEvent)evt, call, now); } bool RasServer::LogAcctEvent(int evt, const endptr & ep) { return acctList->LogAcctEvent((GkAcctLogger::AcctEvent)evt, ep); } PString RasServer::GetAuthInfo(const PString & moduleName) { return authList->GetInfo(moduleName); } PString RasServer::GetAcctInfo(const PString & moduleName) { return acctList->GetInfo(moduleName); } void RasServer::Run() { RasMsg::Initialize(); RasPDU::Creator GRQCreator; RasPDU::Creator GCFCreator; RasPDU::Creator GRJCreator; RegistrationRequestPDU::Creator RRQCreator; RasPDU::Creator RCFCreator; RasPDU::Creator RRJCreator; RasPDU::Creator URQCreator; RasPDU::Creator UCFCreator; RasPDU::Creator URJCreator; AdmissionRequestPDU::Creator ARQCreator; RasPDU::Creator ACFCreator; RasPDU::Creator ARJCreator; RasPDU::Creator BRQCreator; RasPDU::Creator BCFCreator; RasPDU::Creator BRJCreator; RasPDU::Creator DRQCreator; RasPDU::Creator DCFCreator; RasPDU::Creator DRJCreator; RasPDU::Creator LRQCreator; RasPDU::Creator LCFCreator; RasPDU::Creator LRJCreator; RasPDU::Creator IRQCreator; RasPDU::Creator IRRCreator; RasPDU::Creator UMRCreator; RasPDU::Creator RIPCreator; RasPDU::Creator RAICreator; RasPDU::Creator SCICreator; RasPDU::Creator SCRCreator; listeners = new TCPServer(); gkClient = new GkClient(); neighbors = new NeighborList(); authList = new GkAuthenticatorList(); acctList = new GkAcctLoggerList(); vqueue = new VirtualQueue(); LoadConfig(); if ((m_socksize > 0) && (!interfaces.empty())) { callptr nullcall; acctList->LogAcctEvent(GkAcctLogger::AcctOn,nullcall); CreateJob(this, &RasServer::HouseKeeping, "HouseKeeping"); #ifdef HAS_SNMP if (Toolkit::Instance()->IsSNMPEnabled()) { StartSNMPAgent(); } #endif RegularJob::Run(); acctList->LogAcctEvent(GkAcctLogger::AcctOff,nullcall); } else { SNMP_TRAP(10, SNMPError, Network, "No valid interfaces to listen! Shutdown!"); cerr << "FATAL: No valid interfaces to listen! Shutdown!" << endl; PTRACE(0, "FATAL: No valid interfaces to listen! Shutdown!"); } } void RasServer::OnStop() { if (gkClient->IsRegistered()) gkClient->SendURQ(); // clear all calls and unregister all endpoints SoftPBX::UnregisterAllEndpoints(); // close all listeners immediately if (broadcastListener) broadcastListener->Close(); DeleteObjectsInContainer(interfaces); interfaces.clear(); listeners->Stop(); delete sigHandler; sigHandler = NULL; delete Routing::Analyzer::Instance(); #ifdef HAS_SNMP StopSNMPAgent(); #endif PTRACE(1, "GK\tRasServer stopped"); } // load configuration for alternate gatekeepers void RasServer::GetAlternateGK() { ClearAltGKsTable(); PString redirect(GkConfig()->GetString("RedirectGK", "")); if (redirect *= "temporary") redirectGK = e_temporaryRedirect; else if (redirect *= "permanent") redirectGK = e_permanentRedirect; else { PStringArray limits(redirect.Tokenise("|,;&", FALSE)); for (PINDEX i = 0; i < limits.GetSize(); ++i) { PINDEX gr = limits[i].Find('>'); if (gr != P_MAX_INDEX) { PCaselessString tkn(limits[i].Left(gr)); if (tkn.Find("endpoints") != P_MAX_INDEX) { epLimit = limits[i].Mid(gr + 1).AsInteger(); PTRACE(2, "GK\tSet registration limit to " << epLimit); } else if (tkn.Find("calls") != P_MAX_INDEX) { callLimit = limits[i].Mid(gr + 1).AsInteger(); PTRACE(2, "GK\tSet call limit to " << callLimit); } } } } PString skips(GkConfig()->GetString("SkipForwards", "")); PStringArray skipips(skips.Tokenise(" ,;\t", FALSE)); PINDEX skipSize = skipips.GetSize(); if (skipSize > 0) for (PINDEX i = 0; i < skipSize; ++i) skipAddr.push_back(Address(skipips[i])); // read [RasSrv::AlternateGatekeeper] section m_altGkRules.clear(); PStringToString altgk_rules(GkConfig()->GetAllKeyValues("RasSrv::AlternateGatekeeper")); for (PINDEX i = 0; i < altgk_rules.GetSize(); ++i) { PString network = altgk_rules.GetKeyAt(i); PString setting = altgk_rules.GetDataAt(i); if (!network.IsEmpty()) { NetworkAddress addr = NetworkAddress(network); m_altGkRules[addr] = ParseAltGKConfig(setting); } } // parse global alt gk config PString altGkSetting = GkConfig()->GetString("AlternateGKs", ""); if (altGkSetting.IsEmpty()) return; altGKs = ParseAltGKConfig(altGkSetting); PString sendto(GkConfig()->GetString("SendTo", "")); PStringArray svrs(sendto.Tokenise(" ,;\t", FALSE)); if ((altGKsSize = svrs.GetSize()) > 0) for (PINDEX i = 0; i < altGKsSize; ++i) { PStringArray tokens = SplitIPAndPort(svrs[i], GK_DEF_UNICAST_RAS_PORT); altGKsAddr.push_back(Address(tokens[0])); altGKsPort.push_back(WORD(tokens[1].AsUnsigned())); } } H225_ArrayOf_AlternateGK RasServer::ParseAltGKConfig(const PString & altGkSetting) const { PStringArray altgks(altGkSetting.Tokenise(",", FALSE)); H225_ArrayOf_AlternateGK alternateGKs; alternateGKs.SetSize(altgks.GetSize()); for (PINDEX idx = 0; idx < altgks.GetSize(); ++idx) { const PStringArray tokens = altgks[idx].Tokenise(";", FALSE); if (tokens.GetSize() < 4) { PTRACE(1, "GK\tFormat error in AlternateGKs"); SNMP_TRAP(7, SNMPError, Configuration, "Invalid AlternateGK config"); continue; } H225_AlternateGK & alt = alternateGKs[idx]; alt.m_rasAddress = SocketToH225TransportAddr(Address(tokens[0]), (WORD)tokens[1].AsUnsigned()); alt.m_needToRegister = Toolkit::AsBool(tokens[2]); alt.m_priority = tokens[3].AsInteger(); if (tokens.GetSize() > 4) { alt.IncludeOptionalField(H225_AlternateGK::e_gatekeeperIdentifier); alt.m_gatekeeperIdentifier = tokens[4]; } } return alternateGKs; } H225_ArrayOf_AlternateGK RasServer::GetAltGKForIP(const NetworkAddress & ip) const { // find alternate gatekeeper rule by IP address H225_ArrayOf_AlternateGK result; NetworkAddress bestmatch; std::map::const_iterator iter = m_altGkRules.begin(); while (iter != m_altGkRules.end()) { if ((ip << iter->first) && (iter->first.GetNetmaskLen() >= bestmatch.GetNetmaskLen())) { bestmatch = iter->first; result = iter->second; } ++iter; } return result; } void RasServer::ClearAltGKsTable() { redirectGK = e_noRedirect; altGKs.SetSize(0); altGKsAddr.clear(); skipAddr.clear(); altGKsPort.clear(); altGKsSize = 0; epLimit = callLimit = P_MAX_INDEX; } void RasServer::HouseKeeping() { for (unsigned count = 0; IsRunning(); ++count) if (!Wait(1000)) { if( !IsRunning() ) break; ReadLock lock(ConfigReloadMutex); if (!(count % 60)) // one minute RegistrationTable::Instance()->CheckEndpoints(); CallTable::Instance()->CheckCalls(this); gkClient->CheckRegistration(); Toolkit::Instance()->GetTimerManager()->CheckTimers(); } } void RasServer::ReadSocket(IPSocket *socket) { RasListener *listener = static_cast(socket); if (GatekeeperMessage *msg = listener->ReadRas()) { CreateRasJob(msg); } } #ifdef HAS_H46017 void RasServer::ReadH46017Message(const PBYTEArray & ras, const PIPSocket::Address & fromIP, WORD fromPort, const PIPSocket::Address & localAddr, CallSignalSocket * s) { GatekeeperMessage * msg = new GatekeeperMessage(); if (msg->Read(ras)) { msg->m_peerAddr = fromIP; msg->m_peerPort = fromPort; msg->m_localAddr = localAddr; msg->m_h46017Socket = s; PTRACE(3, "RAS\tH460.17 RAS\n" << setprecision(2) << msg->m_recvRAS); // execute job syncronously, so we don't return to signalling thread before processing is done CreateRasJob(msg, true); } } #endif void RasServer::CreateRasJob(GatekeeperMessage * msg, bool syncronous) { typedef Factory RasFactory; unsigned tag = msg->GetTag(); PWaitAndSignal rlock(requests_mutex); PWaitAndSignal hlock(handlers_mutex); if (RasMsg *ras = RasFactory::Create(tag, msg)) { std::list::iterator iter = find_if(handlers.begin(), handlers.end(), bind2nd(mem_fun(&RasHandler::IsExpected), ras)); if (iter == handlers.end()) { std::list::iterator i = find_if(requests.begin(), requests.end(), bind2nd(mem_fun(&RasMsg::EqualTo), ras)); if (i != requests.end() && !(*i)->IsDone()) { PTRACE(2, "RAS\tDuplicate " << msg->GetTagName() << ", deleted"); delete ras; ras = NULL; } else { if (syncronous) { ras->Exec(); delete ras; ras = NULL; } else { requests.push_back(ras); Job *job = new Jobs(ras); job->SetName(msg->GetTagName()); job->Execute(); } } } else { PTRACE(2, "RAS\tTrapped " << msg->GetTagName()); // re-create RasMsg object by the handler ras = (*iter)->CreatePDU(ras); (*iter)->Process(ras); } } else { PTRACE(1, "RAS\tUnknown RAS message " << msg->GetTagName()); delete msg; } } void RasServer::CleanUp() { PWaitAndSignal lock(requests_mutex); if (!requests.empty()) { std::list::iterator iter = partition(requests.begin(), requests.end(), mem_fun(&RasMsg::IsDone)); DeleteObjects(requests.begin(), iter); requests.erase(requests.begin(), iter); } } GkInterface *RasServer::CreateInterface(const Address & addr) { return new GkInterface(addr); } Toolkit *RasMsg::Kit; RegistrationTable *RasMsg::EndpointTbl; CallTable *RasMsg::CallTbl; RasServer *RasMsg::RasSrv; void RasMsg::Initialize() { Kit = Toolkit::Instance(); EndpointTbl = RegistrationTable::Instance(); CallTbl = CallTable::Instance(); RasSrv = RasServer::Instance(); } template<> bool RasPDU::Process() { // OnGRQ // reply only if GK-ID matches if (request.HasOptionalField(H225_GatekeeperRequest::e_gatekeeperIdentifier)) if (request.m_gatekeeperIdentifier.GetValue() != Toolkit::GKName()) { PTRACE(2, "RAS\tGRQ is not meant for this gatekeeper"); return false; } bool bSendReply = !RasSrv->IsForwardedRas(request, m_msg->m_peerAddr); if (RasSrv->ReplyToRasAddress(m_msg->m_peerAddr)) { PIPSocket::Address rasIP; WORD rasPort; if (GetIPAndPortFromTransportAddr(request.m_rasAddress, rasIP, rasPort)) { PTRACE(3, "Reply to rasAddress from request:" << AsString(rasIP, rasPort)); m_msg->m_peerAddr = rasIP; m_msg->m_peerPort = rasPort; } else { PTRACE(1, "Unable to parse rasAddress " << request.m_rasAddress); } } PString log; PString alias((request.HasOptionalField(H225_GatekeeperRequest::e_endpointAlias) && request.m_endpointAlias.GetSize() > 0) ? AsString(request.m_endpointAlias[0], false) : PString(" ")); unsigned rsn = H225_GatekeeperRejectReason::e_securityDenial; bool bReject = !RasSrv->ValidatePDU(*this, rsn); if (!bReject && RasSrv->IsRedirected()) { bReject = true; rsn = H225_GatekeeperRejectReason::e_resourceUnavailable; } if (bReject) { H225_GatekeeperReject & grj = BuildReject(rsn); grj.m_protocolIdentifier = request.m_protocolIdentifier; grj.IncludeOptionalField(H225_GatekeeperReject::e_gatekeeperIdentifier); grj.m_gatekeeperIdentifier = Toolkit::GKName(); if (rsn == H225_GatekeeperRejectReason::e_resourceUnavailable) RasSrv->SetAltGKInfo(grj, m_msg->m_peerAddr); log = "GRJ|" + m_msg->m_peerAddr.AsString() + "|" + alias + "|" + AsString(request.m_endpointType) + "|" + grj.m_rejectReason.GetTagName() + ";"; } else { H225_GatekeeperConfirm & gcf = BuildConfirm(); gcf.m_protocolIdentifier = request.m_protocolIdentifier; GetRasAddress(gcf.m_rasAddress); if (gcf.m_rasAddress.GetTag() == H225_TransportAddress::e_ipAddress) { // make sure we respond with the unicast RAS IP and port, even if the GRQ came in through multicast WORD unicastRasPort = (WORD)GkConfig()->GetInteger("UnicastRasPort", GK_DEF_UNICAST_RAS_PORT); H225_TransportAddress_ipAddress & rasip = gcf.m_rasAddress; if (rasip.m_ip[0] == 0 && rasip.m_ip[1] == 0 && rasip.m_ip[2] == 0 && rasip.m_ip[3] == 0) { gcf.m_rasAddress = SocketToH225TransportAddr(Toolkit::Instance()->GetRouteTable()->GetLocalAddress(m_msg->m_peerAddr), unicastRasPort); } rasip.m_port = unicastRasPort; } gcf.IncludeOptionalField(H225_GatekeeperConfirm::e_gatekeeperIdentifier); gcf.m_gatekeeperIdentifier = Toolkit::GKName(); #ifdef HAS_H46018 if (Toolkit::Instance()->IsH46018Enabled()) { // check if client supports H.460.18 if (request.HasOptionalField(H225_GatekeeperRequest::e_featureSet)) { H460_FeatureSet fs = H460_FeatureSet(request.m_featureSet); if (fs.HasFeature(18)) { PIPSocket::Address remoteRAS; const PIPSocket::Address & rx_addr = m_msg->m_peerAddr; if (GetIPFromTransportAddr(request.m_rasAddress, remoteRAS)) { bool h46018nat = ((rx_addr != remoteRAS) && !IsLoopback(rx_addr)); if (h46018nat || Toolkit::AsBool(Kit->Config()->GetString(RoutedSec, "H46018NoNAT", "1"))) { // include H.460.18 in supported features gcf.IncludeOptionalField(H225_GatekeeperConfirm::e_featureSet); H460_FeatureStd H46018 = H460_FeatureStd(18); gcf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = gcf.m_featureSet.m_supportedFeatures; desc.SetSize(1); desc[0] = H46018; } } } } } #endif // HAS_H46018 #ifdef HAS_H46023 if (Toolkit::Instance()->IsH46023Enabled()) { // check if client supports H.460.23 if (request.HasOptionalField(H225_GatekeeperRequest::e_featureSet)) { H460_FeatureSet fs = H460_FeatureSet(request.m_featureSet); if (fs.HasFeature(23)) { // include H.460.23 in supported features gcf.IncludeOptionalField(H225_GatekeeperConfirm::e_featureSet); H460_FeatureStd h46023 = H460_FeatureStd(23); gcf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = gcf.m_featureSet.m_supportedFeatures; PINDEX lPos = desc.GetSize(); desc.SetSize(lPos+1); desc[lPos] = h46023; } } } #endif // HAS_H46023 #ifdef HAS_H460P if (Toolkit::Instance()->IsH460PEnabled()) { // check if client supports presence if (request.HasOptionalField(H225_GatekeeperRequest::e_featureSet)) { H460_FeatureSet fs = H460_FeatureSet(request.m_featureSet); if (fs.HasFeature(OpalOID(OID3))) { gcf.IncludeOptionalField(H225_GatekeeperConfirm::e_featureSet); H460_FeatureOID oid = H460_FeatureOID(OID3); gcf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = gcf.m_featureSet.m_supportedFeatures; PINDEX lPos = desc.GetSize(); desc.SetSize(lPos+1); desc[lPos] = oid; } } } #endif // HAS_H460P #ifdef HAS_H460PRE // check if client supports preemption if (request.HasOptionalField(H225_GatekeeperRequest::e_featureSet)) { H460_FeatureSet fs = H460_FeatureSet(request.m_featureSet); if (fs.HasFeature(OpalOID(OID6))) { gcf.IncludeOptionalField(H225_GatekeeperConfirm::e_featureSet); H460_FeatureOID oid = H460_FeatureOID(OID6); gcf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = gcf.m_featureSet.m_supportedFeatures; PINDEX lPos = desc.GetSize(); desc.SetSize(lPos+1); desc[lPos] = oid; } } #endif // HAS_H460 #ifdef h323v6 if (request.HasOptionalField(H225_GatekeeperRequest::e_supportsAssignedGK) && RasSrv->HasAssignedGK(alias,m_msg->m_peerAddr,gcf)) PTRACE(2, "GCF\t" << alias << " redirected to assigned Gatekeeper"); else #endif { if (request.HasOptionalField(H225_GatekeeperRequest::e_supportsAltGK)) RasSrv->SetAlternateGK(gcf, m_msg->m_peerAddr); RasSrv->SelectH235Capability(request, gcf); } log = "GCF|" + m_msg->m_peerAddr.AsString() + "|" + alias + "|" + AsString(request.m_endpointType) + ";"; } PrintStatus(log); return bSendReply; } bool RegistrationRequestPDU::Process() { // OnRRQ H225_TransportAddress SignalAddr; const PIPSocket::Address & rx_addr = m_msg->m_peerAddr; const WORD rx_port = m_msg->m_peerPort; bool bSendReply, bForwardRequest; bSendReply = bForwardRequest = !RasSrv->IsForwardedRas(request, rx_addr); #ifdef HAS_H46017 PBoolean usesH46017 = (m_msg->m_h46017Socket != NULL); if (usesH46017) { PTRACE(3, "RAS\tEndpoint uses H.460.17"); // add RAS and signal IP eg. for status port display, but they will never be used request.m_rasAddress.SetSize(1); request.m_rasAddress[0] = SocketToH225TransportAddr(rx_addr, rx_port); request.m_callSignalAddress.SetSize(1); request.m_callSignalAddress[0] = SocketToH225TransportAddr(rx_addr, rx_port); } #endif PINDEX i; /// remove invalid/unsupported entries from RAS and signaling addresses for (i = 0; i < request.m_callSignalAddress.GetSize(); i++) { PIPSocket::Address addr; WORD port = 0; if (!GetIPAndPortFromTransportAddr(request.m_callSignalAddress[i], addr, port) || !addr.IsValid() || port == 0) { PTRACE(5, "RAS\tRemoving signaling address " << AsString(request.m_callSignalAddress[i]) << " from RRQ"); request.m_callSignalAddress.RemoveAt(i--); } } for (i = 0; i < request.m_rasAddress.GetSize(); i++) { PIPSocket::Address addr; WORD port = 0; if (!GetIPAndPortFromTransportAddr(request.m_rasAddress[i], addr, port) || !addr.IsValid() || port == 0) { PTRACE(5, "RAS\tRemoving RAS address " << AsString(request.m_rasAddress[i]) << " from RRQ" ); request.m_rasAddress.RemoveAt(i--); } } /////////////////////////////////////////////////////////////////////////////////////////// // H.460 NAT support code PBoolean h46018nat = false; PBoolean supportH46024 = false; PBoolean supportH46024A = false; PBoolean supportH46024B = false; unsigned ntype = 100; // UnAllocated NAT Type #ifdef HAS_H46018 PBoolean supportH46018 = false; #endif #ifdef HAS_H46023 PBoolean supportH46023 = false; #endif #if (HAS_H46018 || HAS_H46023) H225_TransportAddress originalCallSigAddress; // original call signal address (to restore if H.460.18 is disabled) #endif // H.460 Registration pre-emption int RegPrior = 0; bool preemptsupport = false; PBoolean preempt = false; #ifdef HAS_H460 bool EPSupportsQoSReporting = false; #ifdef HAS_H460P // Presence Support PBoolean presenceSupport = false; OpalOID rPreFS = OpalOID(OID3); PBoolean presencePDU = false; PASN_OctetString preFeature; #endif // HAS_H460P #ifdef HAS_H460PRE // Registration Priority and Pre-emption // This allows the unregistration of duplicate aliases with lower priority OpalOID rPriFS = OpalOID(OID6); #endif // HAS_H460PRE if (request.HasOptionalField(H225_RegistrationRequest::e_featureSet)) { H460_FeatureSet fs = H460_FeatureSet(request.m_featureSet); #ifdef HAS_H46018 // H.460.18 if (Toolkit::Instance()->IsH46018Enabled()) { if (fs.HasFeature(18)) { PIPSocket::Address remoteRAS; if (request.m_rasAddress.GetSize() > 0) GetIPFromTransportAddr(request.m_rasAddress[0], remoteRAS); // ignore possible errors, will be overwritten anyway h46018nat = ((rx_addr != remoteRAS) && !IsLoopback(rx_addr)); if (h46018nat || Toolkit::AsBool(Kit->Config()->GetString(RoutedSec, "H46018NoNAT", "1"))) { supportH46018 = true; // ignore rasAddr and use apparent address request.m_rasAddress.SetSize(1); request.m_rasAddress[0] = SocketToH225TransportAddr(rx_addr, rx_port); if (request.m_callSignalAddress.GetSize() > 0) originalCallSigAddress = request.m_callSignalAddress[0]; request.m_callSignalAddress.SetSize(1); request.m_callSignalAddress[0] = SocketToH225TransportAddr(rx_addr, rx_port); } } } #endif // HAS_H46018 #ifdef HAS_H46023 if (Toolkit::Instance()->IsH46023Enabled()) { supportH46023 = fs.HasFeature(23); if (supportH46023) { H460_FeatureStd * natfeat = (H460_FeatureStd *)fs.GetFeature(23); // Check whether the endpoint supports Remote Nat directly (NATOffoad) if (natfeat->Contains(Std23_RemoteNAT)) supportH46024 = natfeat->Value(Std23_RemoteNAT); // Check whether the endpoint supports SameNAT H.460.24AnnexA if (natfeat->Contains(Std23_AnnexA)) supportH46024A = natfeat->Value(Std23_AnnexA); // Check whether the endpoint supports offload H.460.24AnnexB if (natfeat->Contains(Std23_AnnexB)) supportH46024B = natfeat->Value(Std23_AnnexB); // Check if the endpoint is notifying the Gk the type of NAT detected if (natfeat->Contains(Std23_NATdet)) ntype = natfeat->Value(Std23_NATdet); } } #endif #ifdef HAS_H460P if (Toolkit::Instance()->IsH460PEnabled()) { presenceSupport = fs.HasFeature(rPreFS); if (presenceSupport) { H460_FeatureOID * feat = (H460_FeatureOID *)fs.GetFeature(rPreFS); presencePDU = feat->Contains(OID3_PDU); if (presencePDU) { PASN_OctetString & prePDU = feat->Value(OID3_PDU); preFeature = prePDU; } } } #endif // HAS_H460P #ifdef HAS_H460PRE if (fs.HasFeature(rPriFS)) { H460_FeatureOID * feat = (H460_FeatureOID *)fs.GetFeature(rPriFS); if (feat->Contains(OID6_Priority)) { unsigned prior = feat->Value(PString(OID6_Priority)); RegPrior = (int)prior; } if (feat->Contains(OID6_Preempt)) { preemptsupport = true; preempt = feat->Value(PString(OID6_Preempt)); } } #endif // HAS_H460PRE // H.460.9 if (fs.HasFeature(9)) { EPSupportsQoSReporting = true; } } #endif // HAS_H460 /////////////////////////////////////////////////////////////////////////////////////////////// // If calling NAT support disabled. // Use this to block errant gateways that don't support NAT mechanism properly. bool supportcallingNAT = Toolkit::AsBool(GkConfig()->GetString(RoutedSec, "SupportCallingNATedEndpoints", "1")); if (RasSrv->ReplyToRasAddress(m_msg->m_peerAddr) && request.m_rasAddress.GetSize() > 0) { PIPSocket::Address rasIP; WORD rasPort; if (GetIPAndPortFromTransportAddr(request.m_rasAddress[0], rasIP, rasPort)) { PTRACE(3, "Reply to rasAddress from request:" << AsString(rasIP, rasPort)); m_msg->m_peerAddr = rasIP; m_msg->m_peerPort = rasPort; } else { PTRACE(1, "Unable to parse rasAddress " << request.m_rasAddress[0]); } } // lightweight registration update if (request.HasOptionalField(H225_RegistrationRequest::e_keepAlive) && request.m_keepAlive) { endptr ep = request.HasOptionalField(H225_RegistrationRequest::e_endpointIdentifier) ? EndpointTbl->FindByEndpointId(request.m_endpointIdentifier) : (request.m_callSignalAddress.GetSize() >= 1) ? EndpointTbl->FindBySignalAdr(request.m_callSignalAddress[0], rx_addr) : endptr(0); bool bReject = !ep; if (bReject) { PString epid = request.HasOptionalField(H225_RegistrationRequest::e_endpointIdentifier) ? request.m_endpointIdentifier.GetValue() : ""; PTRACE(3, "RAS\tLightweight registration rejected, because endpoint isn't found (EPID=" << epid << ", IP=" << rx_addr << ")"); } // check if the RRQ was sent from the registered endpoint if (ep && bSendReply) { // not forwarded RRQ if (ep->IsNATed() || ep->IsTraversalClient() || ep->UsesH46017()) { // for nated endpoint, only check rx_addr bReject = (ep->GetNATIP() != rx_addr); if (bReject) { PTRACE(3, "RAS\tLightweight registration rejected, because IP doesn't match"); } } else { PIPSocket::Address oaddr, raddr; WORD oport = 0, rport = 0; if (request.m_callSignalAddress.GetSize() >= 1) { GetIPAndPortFromTransportAddr(ep->GetCallSignalAddress(), oaddr, oport); for (int s = 0; s < request.m_callSignalAddress.GetSize(); ++s) { GetIPAndPortFromTransportAddr(request.m_callSignalAddress[s], raddr, rport); if (oaddr == raddr && oport == rport) break; } } else if (request.m_rasAddress.GetSize() >= 1) { GetIPAndPortFromTransportAddr(ep->GetRasAddress(), oaddr, oport), GetIPAndPortFromTransportAddr(request.m_rasAddress[0], raddr, rport); } else { GetIPAndPortFromTransportAddr(ep->GetCallSignalAddress(), oaddr, oport), raddr = oaddr, rport = oport; } bReject = (oaddr != raddr) || (oport != rport) || (IsLoopback(rx_addr) ? false : (raddr != rx_addr)); if (bReject) { PTRACE(3, "RAS\tLightweight registration rejected, because IP or ports don't match: old addr=" << AsString(oaddr, oport) << " receive addr=" << AsString(raddr, rport) << " rx_addr=" << rx_addr); } } } if (bReject) { if (ep && bSendReply) { PTRACE(1, "RAS\tWarning: Possibly endpointId collide, security attack or IP change"); if (Toolkit::AsBool(Kit->Config()->GetString(RRQFeatureSection, "SupportDynamicIP", "0"))) { PTRACE(1, "RAS\tDynamic IP? Removing existing Endpoint record and force reregistration."); while (callptr call = CallTbl->FindCallRec(ep)) { call->Disconnect(); CallTbl->RemoveCall(call); } EndpointTbl->RemoveByEndptr(ep); } } // endpoint was NOT registered and force Full Registration return BuildRRJ(H225_RegistrationRejectReason::e_fullRegistrationRequired); } else { if (ntype < 8) { #ifdef HAS_H46023 if (ntype > 1) { PTRACE(4, "Std23\tEndpoint reports itself as being behind a NAT/FW!"); PTRACE(4, "Std23\tNAT/FW reported as being " << ep->GetEPNATTypeString((EndpointRec::EPNatTypes)ntype)); ep->SetNAT(true); ep->SetNATAddress(rx_addr, rx_port); } else { if (ntype == 0) { PTRACE(4, "Std23\tEndpoint instructs H.460.23/.24 to be disabled (BAD NAT)"); ep->SetH46024(false); ep->SetUsesH46023(false); } else { PTRACE(4, "Std23\tEndpoint reports itself as not behind a NAT/FW!"); ep->SetCallSignalAddress(originalCallSigAddress); ep->SetNAT(false); } } #endif ep->SetEPNATType(ntype); } #ifdef HAS_H460P // If we have some presence information ep->SetUsesH460P(presenceSupport); if (presencePDU) ep->ParsePresencePDU(preFeature); #endif // forward lightweights, too if (bForwardRequest) RasSrv->ForwardRasMsg(m_msg->m_recvRAS); // Additive Registration lightweightRRQ if (request.HasOptionalField(H225_RegistrationRequest::e_additiveRegistration) && request.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) { // Authenticate the new registration RRQAuthData authData; authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial; if (!RasSrv->ValidatePDU(*this, authData)) return BuildRRJ(authData.m_rejectReason); // Check for existing aliases const endptr lep = EndpointTbl->FindByAliases(request.m_terminalAlias); if (lep && (lep->GetCallSignalAddress() != ep->GetCallSignalAddress())) return BuildRRJ(H225_RegistrationRejectReason::e_invalidTerminalAliases); } // endpoint was already registered ep->Update(m_msg->m_recvRAS); if (bSendReply) { BuildRCF(ep); #ifdef HAS_H46018 // H.460.18 if (Toolkit::Instance()->IsH46018Enabled()) { if (ep->IsTraversalClient()) { H225_RegistrationConfirm & rcf = m_msg->m_replyRAS; rcf.IncludeOptionalField(H225_RegistrationConfirm::e_featureSet); H460_FeatureStd H46018 = H460_FeatureStd(18); rcf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = rcf.m_featureSet.m_supportedFeatures; desc.SetSize(1); desc[0] = H46018; } } #endif #ifdef HAS_H46023 // H.460.23 if (Toolkit::Instance()->IsH46023Enabled()) { if (ep->UsesH46023()) { H225_RegistrationConfirm & rcf = m_msg->m_replyRAS; rcf.IncludeOptionalField(H225_RegistrationConfirm::e_featureSet); H460_FeatureStd H46023 = H460_FeatureStd(23); rcf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = rcf.m_featureSet.m_supportedFeatures; PINDEX sz = desc.GetSize(); desc.SetSize(sz+1); desc[sz] = H46023; } } #endif #ifdef HAS_H460P // H.460P if (presenceSupport) { H225_RegistrationConfirm & rcf = m_msg->m_replyRAS; H460_FeatureOID presence = H460_FeatureOID(rPreFS); #ifndef HAS_H460P_VER_3 PASN_OctetString preData; if (ep->BuildPresencePDU(rcf.GetTag(),preData)) presence.Add(OID3_PDU,H460_FeatureContent(preData)); #endif rcf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = rcf.m_featureSet.m_supportedFeatures; PINDEX sz = desc.GetSize(); desc.SetSize(sz+1); desc[sz] = presence; } #endif #ifdef HAS_H460 #ifdef HAS_H460PRE // H.460 PreEmption if (ep->SupportPreemption()) { H225_RegistrationConfirm & rcf = m_msg->m_replyRAS; rcf.IncludeOptionalField(H225_RegistrationConfirm::e_featureSet); rcf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = rcf.m_featureSet.m_supportedFeatures; PINDEX sz = desc.GetSize(); desc.SetSize(sz+1); desc[sz] = H460_FeatureOID(rPriFS); } #endif // HAS_H460PRE // H.460.9 if (EPSupportsQoSReporting && Toolkit::AsBool(GkConfig()->GetString("GkQoSMonitor", "Enable", "0"))) { H225_RegistrationConfirm & rcf = m_msg->m_replyRAS; rcf.IncludeOptionalField(H225_RegistrationConfirm::e_featureSet); rcf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_desiredFeatures); H225_ArrayOf_FeatureDescriptor & desc = rcf.m_featureSet.m_desiredFeatures; PINDEX sz = desc.GetSize(); desc.SetSize(sz+1); desc[sz] = H460_FeatureStd(9); } #endif } return bSendReply; } } if (request.m_rasAddress.GetSize() == 0) return BuildRRJ(H225_RegistrationRejectReason::e_invalidRASAddress); bool nated = false, validaddress = false; if (request.m_callSignalAddress.GetSize() >= 1) { PIPSocket::Address ipaddr; for (int s = 0; s < request.m_callSignalAddress.GetSize(); ++s) { SignalAddr = request.m_callSignalAddress[s]; if (GetIPFromTransportAddr(SignalAddr, ipaddr)) { validaddress = ((rx_addr == ipaddr) || IsLoopback(rx_addr)); if (validaddress) { break; } } } //validaddress = PIPSocket::IsLocalHost(rx_addr.AsString()); if (!bSendReply) { // don't check forwarded RRQ validaddress = true; } else if (!validaddress && !IsLoopback(ipaddr)) { // do not allow nated from loopback nated = true; PString featureRequired = Kit->Config()->GetString(RoutedSec, "NATStdMin", ""); if (!featureRequired && ( 0 #ifdef HAS_H46018 || (featureRequired == "18" && !supportH46018) #endif #ifdef HAS_H46023 || (featureRequired == "23" && !supportH46023) #endif )) { return BuildRRJ(H225_RegistrationRejectReason::e_neededFeatureNotSupported, nated); } else validaddress = Toolkit::AsBool(Kit->Config()->GetString(RoutedSec, "SupportNATedEndpoints", "0")); } } if (!validaddress) return BuildRRJ(H225_RegistrationRejectReason::e_invalidCallSignalAddress, nated); // Check if the endpoint has specified the EndpointIdentifier. // The GK will accept the EndpointIdentifier if // the EndpointIdentifier doesn't exist in the RegistrationTable, // or the request is sent from the original endpoint that has // this EndpointIdentifier. Otherwise the request will be rejected. if (request.HasOptionalField(H225_RegistrationRequest::e_endpointIdentifier)) { // Alternate Gatekeepers based on rules // if (ResolveAlternateGatekeeper(request.m_endpointIdentifier,rx_addr)) // return BuildRRJ(H225_RegistrationRejectReason::e_invalidRASAddress); endptr ep = EndpointTbl->FindByEndpointId(request.m_endpointIdentifier); if (ep && ep->GetCallSignalAddress() != SignalAddr) // no reason named invalidEndpointIdentifier? :( return BuildRRJ(H225_RegistrationRejectReason::e_securityDenial); } RRQAuthData authData; authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial; if (!RasSrv->IsPassThroughRegistrant() && !RasSrv->ValidatePDU(*this, authData)) return BuildRRJ(authData.m_rejectReason); bool bNewEP = true; if (request.HasOptionalField(H225_RegistrationRequest::e_terminalAlias) && (request.m_terminalAlias.GetSize() >= 1)) { H225_ArrayOf_AliasAddress Alias, & Aliases = request.m_terminalAlias; if (Toolkit::AsBool(Kit->Config()->GetString("RasSrv::RRQFeatures", "AuthenticatedAliasesOnly", "0")) && authData.m_authAliases.GetSize() > 0) { PString recvAlias; bool found = false; for (int a = 0; a < Aliases.GetSize(); ++a) { found = false; recvAlias = AsString(Aliases[a],false); for (int j = 0; j < authData.m_authAliases.GetSize(); ++j) { if (recvAlias == authData.m_authAliases[j]) { found = true; break; } } if (!found) { PTRACE(4, "RAS\tRemoving UnAuthenticated Alias " << recvAlias); Aliases.RemoveAt(a--); } } } Alias.SetSize(1); for (int a = 0; a < Aliases.GetSize(); ++a) { Alias[0] = Aliases[a]; bool skip = false; for (int j = 0; j < a; ++j) { skip = (Alias[0] == Aliases[j]); if (skip) break; } if (skip) { // remove duplicate alias Aliases.RemoveAt(a--); continue; } const endptr ep = EndpointTbl->FindByAliases(Alias); if (ep) { bNewEP = (ep->GetCallSignalAddress() != SignalAddr); if (bNewEP) { if ((RegPrior > ep->Priority()) || (preempt) || (Toolkit::AsBool(Kit->Config()->GetString("RasSrv::RRQFeatures", "OverwriteEPOnSameAddress", "0")))) { // If the operators policy allows this case: // 1a) terminate all calls on active ep and // 1b) unregister the active ep - sends URQ and // 2) remove the ep from the EndpointTable, then // 3) allow the new ep to register - see below while (callptr call = CallTbl->FindCallRec(ep)) { call->Disconnect(); CallTbl->RemoveCall(call); } if (RegPrior > ep->Priority()) ep->Unregisterpreempt(1); // Unregistered by high priority else if (preempt) ep->Unregisterpreempt(2); // Unregistered by preempt notification else ep->Unregister(); EndpointTbl->RemoveByEndptr(ep); } else { BuildRRJ(H225_RegistrationRejectReason::e_duplicateAlias); H225_RegistrationReject & rrj = m_msg->m_replyRAS; #ifdef HAS_H460PRE // notify that EP can pre-empt previous registration if ((preemptsupport) && (RegPrior == ep->Priority())) { rrj.IncludeOptionalField(H225_RegistrationReject::e_genericData); H460_FeatureOID pre = H460_FeatureOID(rPriFS); pre.Add(PString(OID6_PreNot),H460_FeatureContent(TRUE)); H225_ArrayOf_GenericData & data = rrj.m_genericData; PINDEX lastPos = data.GetSize(); data.SetSize(lastPos+1); data[lastPos] = pre; } #endif // HAS_H460PRE H225_ArrayOf_AliasAddress & duplicateAlias = rrj.m_rejectReason; duplicateAlias = Alias; return true; } } } PString s = AsString(Alias[0], FALSE); // reject the empty string //if (s.GetLength() < 1 || !(isalnum(s[0]) || s[0]=='#') ) if (s.GetLength() < 1) return BuildRRJ(H225_RegistrationRejectReason::e_invalidAlias); if (!nated && !s) nated = Kit->Config()->HasKey("NATedEndpoints", s); } } else { // reject gw without alias switch (request.m_terminalType.GetTag()) { case H225_EndpointType::e_gatekeeper: case H225_EndpointType::e_gateway: case H225_EndpointType::e_mcu: return BuildRRJ(H225_RegistrationRejectReason::e_invalidAlias); /* only while debugging default: return BuildRRJ(H225_RegistrationRejectReason::e_invalidAlias); */ } } if (bNewEP && RasSrv->IsRedirected(H225_RasMessage::e_registrationRequest)) { PTRACE(1, "RAS\tWarning: Exceed registration limit!!"); return BuildRRJ(H225_RegistrationRejectReason::e_resourceUnavailable); } if (!nated && request.HasOptionalField(H225_RegistrationRequest::e_nonStandardData)) { int iec = Toolkit::iecUnknown; if (request.m_nonStandardData.m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard) { iec = Toolkit::Instance()->GetInternalExtensionCode(request.m_nonStandardData.m_nonStandardIdentifier); } else if (request.m_nonStandardData.m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_object) { PASN_ObjectId &oid = request.m_nonStandardData.m_nonStandardIdentifier; if (oid.GetDataLength() == 0) iec = Toolkit::iecNATTraversal; } if (iec == Toolkit::iecNATTraversal) { PString ipdata = request.m_nonStandardData.m_data.AsString(); if (strncmp(ipdata, "IP=", 3) == 0) { PStringArray ips(ipdata.Mid(3).Tokenise(",;", false)); PINDEX k; for (k = 0; k < ips.GetSize(); ++k) if (PIPSocket::Address(ips[k]) == rx_addr) break; nated = (k >= ips.GetSize()); request.RemoveOptionalField(H225_RegistrationRequest::e_nonStandardData); } } } request.m_callSignalAddress.SetSize(1); request.m_callSignalAddress[0] = SignalAddr; if (RasSrv->IsPassThroughRegistrant() && !RasSrv->ValidateAdditivePDU(*this, authData)) return BuildRRJ(authData.m_rejectReason); endptr ep = EndpointTbl->InsertRec(m_msg->m_recvRAS, nated ? rx_addr : PIPSocket::Address(GNUGK_INADDR_ANY)); if (!ep) { PTRACE(3, "RAS\tRRQ rejected by unknown reason from " << rx_addr); return BuildRRJ(H225_RegistrationRejectReason::e_undefinedReason); } #ifdef HAS_H46017 if (usesH46017) { if (ep->IsH46017Disabled()) { EndpointTbl->RemoveByEndptr(ep); return BuildRRJ(H225_RegistrationRejectReason::e_securityDenial); } ep->SetUsesH46017(usesH46017); ep->SetNATAddress(rx_addr, rx_port); } #endif #ifdef HAS_H46018 if (supportH46018 && !ep->IsH46018Disabled()) { // check endpoint specific switch, too PTRACE(3, "H46018\tEP on " << rx_addr << " supports H.460.18"); ep->SetTraversalRole(TraversalClient); ep->SetNATAddress(rx_addr, rx_port); } if (supportH46018 && ep->IsH46018Disabled()) { // if the endpoint wanted H.460.18, we have overwritten its callSignalAddr above // we have to restore it here if we disable H.460.18 for this endpoint ep->SetCallSignalAddress(originalCallSigAddress); } #endif // HAS_H46018 #ifdef HAS_H460P // If we have some presence information ep->SetUsesH460P(presenceSupport); if (presencePDU) ep->ParsePresencePDU(preFeature); #endif // HAS_H460P if (nated || (ep->IsTraversalClient() && !validaddress)) { ep->SetNATAddress(rx_addr, rx_port); } else { ep->SetNAT(h46018nat); if (h46018nat && supportH46024) { PTRACE(4, "RAS\tH46024 NAT detected set default PortRestricted: Wait for result of NAT Test"); ep->SetEPNATType(EndpointRec::NatPortRestricted); } ep->SetH46024(supportH46024); ep->SetH46024A(supportH46024A); ep->SetH46024B(supportH46024B); } ep->SetPriority(RegPrior); ep->SetPreemption(preemptsupport); if (bSendReply) { // // OK, now send RCF // BuildRCF(ep); H225_RegistrationConfirm & rcf = m_msg->m_replyRAS; if (Toolkit::AsBool(Kit->Config()->GetString(RoutedSec, "PregrantARQ", "0"))) { rcf.IncludeOptionalField(H225_RegistrationConfirm::e_preGrantedARQ); rcf.m_preGrantedARQ.m_makeCall = true; rcf.m_preGrantedARQ.m_answerCall = true; if (RasSrv->IsGKRouted()) { // in routed-mode we require all calls to be placed through the gatekeeper rcf.m_preGrantedARQ.m_useGKCallSignalAddressToMakeCall = true; rcf.m_preGrantedARQ.m_useGKCallSignalAddressToAnswer = true; } } if (supportcallingNAT #ifdef HAS_H46017 && !usesH46017 #endif && !ep->IsTraversalClient()) { // tell the endpoint its translated address rcf.IncludeOptionalField(H225_RegistrationConfirm::e_nonStandardData); rcf.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard &t35 = rcf.m_nonStandardData.m_nonStandardIdentifier; t35.m_t35CountryCode = Toolkit::t35cPoland; t35.m_manufacturerCode = Toolkit::t35mGnuGk; t35.m_t35Extension = Toolkit::t35eNATTraversal; // if the client is NAT or you are forcing ALL registrations to use a keepAlive TCP socket if ((nated) || Toolkit::AsBool(Kit->Config()->GetString(RoutedSec, "ForceNATKeepAlive", "0"))) rcf.m_nonStandardData.m_data = "NAT=" + rx_addr.AsString(); else // Be careful as some public IP's may block TCP but not UDP resulting in an incorrect NAT test result. rcf.m_nonStandardData.m_data = "NoNAT"; } #ifdef HAS_H460 H225_ArrayOf_FeatureDescriptor & gd = rcf.m_featureSet.m_supportedFeatures; #ifdef HAS_H46017 if (m_msg->m_h46017Socket) ep->SetNATSocket(m_msg->m_h46017Socket); if (ep->UsesH46017()) { rcf.m_callSignalAddress.SetSize(0); rcf.IncludeOptionalField(H225_RegistrationConfirm::e_maintainConnection); rcf.m_maintainConnection = true; } #endif #ifdef HAS_H46018 // H.460.18 if (Toolkit::Instance()->IsH46018Enabled()) { if (ep->IsTraversalClient()) { H460_FeatureStd H46018 = H460_FeatureStd(18); gd.SetSize(1); gd[0] = H46018; } } #endif // HAS_H46018 #ifdef HAS_H46023 if (supportH46023 && Toolkit::Instance()->IsH46023Enabled()) { // if we support NAT notify the client they are behind a NAT or to test for NAT // send off a request to test the client NAT type with a STUN Server // if behind Nat see if STUN server is available for the interface // if not then disable Std23 for this endpoint H323TransportAddress stunaddr; bool ok23 = Toolkit::Instance()->GetH46023STUN(rx_addr, stunaddr); // Build the message if (ok23) { ep->SetUsesH46023(true); bool h46023nat = nated || h46018nat || Toolkit::AsBool(Kit->Config()->GetString(RoutedSec, "H46018NoNAT", "1")); H460_FeatureStd natfs = H460_FeatureStd(23); natfs.Add(Std23_IsNAT,H460_FeatureContent(h46023nat)); if (h46023nat) { natfs.Add(Std23_STUNAddr,H460_FeatureContent(stunaddr)); } else { // If not NAT then provide the RAS address to the client to determine // whether there is an ALG (or someother device) making things appear as they are not natfs.Add(Std23_DetRASAddr, H460_FeatureContent(H323TransportAddress(request.m_rasAddress[0]))); } PINDEX lPos = gd.GetSize(); gd.SetSize(lPos+1); gd[lPos] = natfs; } } #endif // HAS_H46023 #ifdef HAS_H460P if (presenceSupport) { H460_FeatureOID presence = H460_FeatureOID(rPreFS); #ifndef HAS_H460P_VER_3 PASN_OctetString preData; if (ep->BuildPresencePDU(rcf.GetTag(),preData)) presence.Add(OID3_PDU,H460_FeatureContent(preData)); #endif PINDEX lPos = gd.GetSize(); gd.SetSize(lPos+1); gd[lPos] = presence; } #endif // HAS_H460P #ifdef HAS_H460PRE // if the client supports Registration PreEmption then notify the client that we do too if ((preemptsupport) && (request.HasOptionalField(H225_RegistrationRequest::e_keepAlive) && (!request.m_keepAlive))) { H460_FeatureOID pre = H460_FeatureOID(rPriFS); PINDEX lPos = gd.GetSize(); gd.SetSize(lPos+1); gd[lPos] = pre; } #endif // HAS_H460PRE // H.460.9 if (EPSupportsQoSReporting && Toolkit::AsBool(GkConfig()->GetString("GkQoSMonitor", "Enable", "0"))) { rcf.IncludeOptionalField(H225_RegistrationConfirm::e_featureSet); rcf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_desiredFeatures); H225_ArrayOf_FeatureDescriptor & desc = rcf.m_featureSet.m_desiredFeatures; PINDEX sz = desc.GetSize(); desc.SetSize(sz+1); desc[sz] = H460_FeatureStd(9); } if (gd.GetSize() > 0) { rcf.IncludeOptionalField(H225_RegistrationConfirm::e_featureSet); rcf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); } #endif // HAS_H460 // Gatekeeper assigned Aliases if the client supplied aliases if (request.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) { if (!ep->IsGateway() || Toolkit::AsBool(Kit->Config()->GetString(RRQFeatureSection, "GatewayAssignAliases", "1"))) ep->SetAssignedAliases(rcf.m_terminalAlias); rcf.IncludeOptionalField(H225_RegistrationConfirm::e_terminalAlias); } #ifdef h323v6 // Assigned GKs if (request.HasOptionalField(H225_RegistrationRequest::e_assignedGatekeeper)) ep->SetAssignedGatekeeper(rcf.m_assignedGatekeeper); #endif // Alternate GKs if (request.HasOptionalField(H225_RegistrationRequest::e_supportsAltGK)) RasSrv->SetAlternateGK(rcf, m_msg->m_peerAddr); // Call credit display if (ep->AddCallCreditServiceControl(rcf.m_serviceControl, authData.m_amountString, authData.m_billingMode, -1)) rcf.IncludeOptionalField(H225_RegistrationConfirm::e_serviceControl); // URL link if (ep->AddHTTPServiceControl(rcf.m_serviceControl) && !rcf.HasOptionalField(H225_RegistrationConfirm::e_serviceControl)) rcf.IncludeOptionalField(H225_RegistrationConfirm::e_serviceControl); #ifdef H323_H350 // H.350 link if (ep->AddH350ServiceControl(rcf.m_serviceControl) && !rcf.HasOptionalField(H225_RegistrationConfirm::e_serviceControl)) rcf.IncludeOptionalField(H225_RegistrationConfirm::e_serviceControl); #endif } else { PIPSocket::Address rasip, sigip; if (GetIPFromTransportAddr(request.m_rasAddress[0], rasip) && GetIPFromTransportAddr(SignalAddr, sigip) && rasip != sigip) // this is an nated endpoint ep->SetNATAddress(rasip); } // forward heavyweight if (bForwardRequest) { request.IncludeOptionalField(H225_RegistrationRequest::e_endpointIdentifier); request.m_endpointIdentifier = ep->GetEndpointIdentifier(); if (nated) { request.m_rasAddress.SetSize(1); request.m_rasAddress[0] = ep->GetRasAddress(); // translated address } RasSrv->ForwardRasMsg(m_msg->m_recvRAS); } // Note that the terminalAlias is not optional here as we pass the auto generated alias if not were provided from // the endpoint itself if ( bNewEP || !Toolkit::AsBool(GkConfig()->GetString("GkStatus::Filtering", "NewRCFOnly", "0"))) { PString log = "RCF|" + ep->PrintOn(false) + ";"; PrintStatus(log); } return bSendReply; } bool RegistrationRequestPDU::BuildRCF(const endptr & ep, bool additiveRegistration) { H225_RegistrationConfirm & rcf = BuildConfirm(); rcf.m_protocolIdentifier = request.m_protocolIdentifier; if (RasSrv->IsGKRouted()) { rcf.m_callSignalAddress.SetSize(1); GetCallSignalAddress(rcf.m_callSignalAddress[0]); } else { rcf.m_callSignalAddress.SetSize(0); // we don't have a call signal address in direct mode } if (!additiveRegistration) { rcf.IncludeOptionalField(H225_RegistrationConfirm::e_terminalAlias); rcf.m_terminalAlias = ep->GetAliases(); } rcf.m_endpointIdentifier = ep->GetEndpointIdentifier(); rcf.IncludeOptionalField(H225_RegistrationConfirm::e_gatekeeperIdentifier); rcf.m_gatekeeperIdentifier = Toolkit::GKName(); if (ep->GetTimeToLive() > 0) { rcf.IncludeOptionalField(H225_RegistrationConfirm::e_timeToLive); rcf.m_timeToLive = ep->GetTimeToLive(); } rcf.IncludeOptionalField(H225_RegistrationConfirm::e_supportsAdditiveRegistration); return true; } bool RegistrationRequestPDU::BuildRRJ(unsigned reason, bool alt) { H225_RegistrationReject & rrj = BuildReject(reason); rrj.m_protocolIdentifier = request.m_protocolIdentifier; rrj.IncludeOptionalField(H225_RegistrationReject::e_gatekeeperIdentifier); rrj.m_gatekeeperIdentifier = Toolkit::GKName(); if (alt) RasSrv->SetAltGKInfo(rrj, m_msg->m_peerAddr); if (request.HasOptionalField(H225_RegistrationRequest::e_nonStandardData) && request.m_nonStandardData.m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard) { const H225_H221NonStandard& nonStandard = request.m_nonStandardData.m_nonStandardIdentifier; if (Toolkit::Instance()->GetInternalExtensionCode(nonStandard) == Toolkit::iecFailoverRAS) CopyNonStandardData(request, rrj); } PString alias(request.HasOptionalField(H225_RegistrationRequest::e_terminalAlias) ? AsString(request.m_terminalAlias) : PString(" ")); PString log = "RRJ|" + m_msg->m_peerAddr.AsString() + "|" + alias + "|" + AsString(request.m_terminalType) + "|" + rrj.m_rejectReason.GetTagName() + ";"; return PrintStatus(log); } template<> bool RasPDU::Process() { // OnURQ PString log; bool bSendReply, bForwardRequest; bSendReply = bForwardRequest = !RasSrv->IsForwardedRas(request, m_msg->m_peerAddr); PString endpointId(request.HasOptionalField(H225_UnregistrationRequest::e_endpointIdentifier) ? request.m_endpointIdentifier.GetValue() : PString(" ")); endptr ep = request.HasOptionalField(H225_UnregistrationRequest::e_endpointIdentifier) ? EndpointTbl->FindByEndpointId(request.m_endpointIdentifier) : request.m_callSignalAddress.GetSize() ? EndpointTbl->FindBySignalAdr(request.m_callSignalAddress[0], m_msg->m_peerAddr) : endptr(0); if (ep) { if (RasSrv->ReplyToRasAddress(m_msg->m_peerAddr)) { if (GetIPAndPortFromTransportAddr(ep->GetRasAddress(), m_msg->m_peerAddr, m_msg->m_peerPort)) { PTRACE(3, "Reply to saved rasAddress:" << AsString(m_msg->m_peerAddr, m_msg->m_peerPort)); } else { PTRACE(1, "Unable to parse saved rasAddress " << ep->GetRasAddress()); } } if (ep->IsAdditiveRegistrant() && request.HasOptionalField(H225_UnregistrationRequest::e_endpointAlias) && !ep->RemoveAliases(request.m_endpointAlias)) { EndpointRec logRec(m_msg->m_recvRAS); RasServer::Instance()->LogAcctEvent(GkAcctLogger::AcctUnregister, endptr(&logRec)); log = "UCF|" + m_msg->m_peerAddr.AsString() + "|" + endpointId + "|" + AsString(request.m_endpointAlias); PrintStatus(log); BuildConfirm(); return bSendReply; } // Disconnect all calls of the endpoint SoftPBX::DisconnectEndpoint(ep); // Remove from the table EndpointTbl->RemoveByEndptr(ep); // Return UCF BuildConfirm(); log = "UCF|" + m_msg->m_peerAddr.AsString() + "|" + endpointId + ";"; } else { // Return URJ H225_UnregistrationReject & urj = BuildReject(H225_UnregRejectReason::e_notCurrentlyRegistered); log = "URJ|" + m_msg->m_peerAddr.AsString() + "|" + endpointId + "|" + urj.m_rejectReason.GetTagName() + ";"; } if (bForwardRequest) RasSrv->ForwardRasMsg(m_msg->m_recvRAS); PrintStatus(log); return bSendReply; } PString AdmissionRequestPDU::GetCallingStationId( /// additional data ARQAuthData& authData ) const { if (!authData.m_callingStationId) return authData.m_callingStationId; const H225_AdmissionRequest& arq = request; const bool hasCall = authData.m_call.operator->() != NULL; PString id; // Calling-Station-Id if (!arq.m_answerCall) // srcInfo is meaningful only in an originating ARQ id = GetBestAliasAddressString(arq.m_srcInfo, false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); else if (hasCall) id = authData.m_call->GetCallingStationId(); if (!id) return id; if (id.IsEmpty() && hasCall) id = GetBestAliasAddressString(authData.m_call->GetSourceAddress(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty() && authData.m_requestingEP && !arq.m_answerCall) id = GetBestAliasAddressString( authData.m_requestingEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty() && arq.m_answerCall && hasCall) { const endptr callingEP = authData.m_call->GetCallingParty(); if (callingEP) id = GetBestAliasAddressString( callingEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); } if (id.IsEmpty() && hasCall) { PIPSocket::Address addr(0); WORD port = 0; if (authData.m_call->GetSrcSignalAddr(addr, port) && addr.IsValid()) id = AsString(addr, port); } return id; } PString AdmissionRequestPDU::GetCalledStationId( /// additional data ARQAuthData& authData ) const { if (!authData.m_calledStationId) return authData.m_calledStationId; const H225_AdmissionRequest& arq = request; const bool hasCall = authData.m_call.operator->() != NULL; PString id; if (!arq.m_answerCall) { if (arq.HasOptionalField(H225_AdmissionRequest::e_destinationInfo)) id = GetBestAliasAddressString(arq.m_destinationInfo, false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); } else if (hasCall) id = authData.m_call->GetCalledStationId(); if (!id) return id; if (id.IsEmpty() && hasCall) id = GetBestAliasAddressString( authData.m_call->GetDestinationAddress(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty() && arq.m_answerCall) { if (arq.HasOptionalField(H225_AdmissionRequest::e_destinationInfo)) id = GetBestAliasAddressString(arq.m_destinationInfo, false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty() && authData.m_requestingEP) id = GetBestAliasAddressString( authData.m_requestingEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); PIPSocket::Address addr; if (id.IsEmpty() && authData.m_requestingEP && GetIPFromTransportAddr(authData.m_requestingEP->GetCallSignalAddress(), addr) && addr.IsValid()) id = addr.AsString(); } // this does not work well in routed mode, when destCallSignalAddress // is usually the gatekeeper address if (id.IsEmpty() && arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) { const H225_TransportAddress& tsap = arq.m_destCallSignalAddress; id = AsDotString(tsap); } return id; } PString AdmissionRequestPDU::GetCallLinkage( /// additional data ARQAuthData& authData ) const { if (!authData.m_callLinkage) return authData.m_callLinkage; const H225_AdmissionRequest& arq = request; const bool hasCall = authData.m_call.operator->() != NULL; PString id; if (!arq.m_answerCall) { if (arq.HasOptionalField(H225_AdmissionRequest::e_callLinkage)) { const H225_CallLinkage & cl = arq.m_callLinkage; if (cl.HasOptionalField(H225_CallLinkage::e_globalCallId)) id = cl.m_globalCallId.AsString(); } } else if (hasCall) id = authData.m_call->GetCallLinkage(); if (!id) return id; else return PString::Empty(); // No Call Linkage detected. } bool AdmissionRequestPDU::Process() { // OnARQ bool bReject = false; bool answer = request.m_answerCall; PString in_rewrite_source, out_rewrite_source; // find the caller RequestingEP = EndpointTbl->FindByEndpointId(request.m_endpointIdentifier); if (!RequestingEP) return BuildReply(H225_AdmissionRejectReason::e_callerNotRegistered); if (RasSrv->ReplyToRasAddress(m_msg->m_peerAddr)) { if (GetIPAndPortFromTransportAddr(RequestingEP->GetRasAddress(), m_msg->m_peerAddr, m_msg->m_peerPort)) { PTRACE(3, "Reply to saved rasAddress:" << AsString(m_msg->m_peerAddr, m_msg->m_peerPort)); } else { PTRACE(1, "Unable to parse saved rasAddress " << RequestingEP->GetRasAddress()); } } if (RasSrv->IsRedirected(H225_RasMessage::e_admissionRequest) && !answer) { PTRACE(1, "RAS\tWarning: Exceed call limit!!"); return BuildReply(H225_AdmissionRejectReason::e_resourceUnavailable); } // send RIP message before doing any real work if configured to do so unsigned ripDelay = GkConfig()->GetInteger("RasSrv::ARQFeatures", "SendRIP", 0); if (ripDelay > 0) { RasSrv->SendRIP(request.m_requestSeqNum, ripDelay, m_msg->m_peerAddr, m_msg->m_peerPort); } bool aliasesChanged = false; bool hasDestInfo = request.HasOptionalField(H225_AdmissionRequest::e_destinationInfo) && request.m_destinationInfo.GetSize() > 0; // CallRecs should be looked for using callIdentifier instead of callReferenceValue // callIdentifier is globally unique, callReferenceValue is just unique per-endpoint. callptr pExistingCallRec = request.HasOptionalField(H225_AdmissionRequest::e_callIdentifier) ? CallTbl->FindCallRec(request.m_callIdentifier) : // since callIdentifier is optional, we might have to look for the callReferenceValue as well CallTbl->FindCallRec(request.m_callReferenceValue); ARQAuthData authData(RequestingEP, pExistingCallRec); if (answer && pExistingCallRec) authData.m_dialedNumber = pExistingCallRec->GetDialedNumber(); if (authData.m_dialedNumber.IsEmpty()) { if (!answer && hasDestInfo) { authData.m_dialedNumber = GetBestAliasAddressString( request.m_destinationInfo, false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber)); } } if (hasDestInfo) { // apply rewriting rules in_rewrite_source = GetBestAliasAddressString( RequestingEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (in_rewrite_source.IsEmpty() && request.m_srcInfo.GetSize() > 0) { in_rewrite_source = GetBestAliasAddressString(request.m_srcInfo, false, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber)); } if (!in_rewrite_source.IsEmpty()) { if (Kit->GWRewriteE164(in_rewrite_source, GW_REWRITE_IN, request.m_destinationInfo[0], pExistingCallRec) && !RasSrv->IsGKRouted()) { aliasesChanged = true; } } // Normal rewriting if (Kit->RewriteE164(request.m_destinationInfo[0]) && !RasSrv->IsGKRouted()) { aliasesChanged = true; } } destinationString = hasDestInfo ? AsString(request.m_destinationInfo) : request.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress) ? AsDotString(request.m_destCallSignalAddress) : PString("unknown"); authData.m_callingStationId = GetCallingStationId(authData); authData.m_calledStationId = GetCalledStationId(authData); authData.m_callLinkage = GetCallLinkage(authData); if (!RasSrv->ValidatePDU(*this, authData)) { if (authData.m_rejectReason < 0) { authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial; } return BuildReply(authData.m_rejectReason); } if (authData.m_routeToAlias.GetSize() > 0) { request.IncludeOptionalField(H225_AdmissionRequest::e_destinationInfo); request.m_destinationInfo = authData.m_routeToAlias; authData.m_calledStationId = AsString(authData.m_routeToAlias, FALSE); PTRACE(2, "RAS\tARQ destination set to " << authData.m_calledStationId); hasDestInfo = true; destinationString = AsString(request.m_destinationInfo); if (!RasSrv->IsGKRouted()) { aliasesChanged = true; } } if (RasSrv->IsGKRouted() && answer && !pExistingCallRec) { if (Toolkit::AsBool(Kit->Config()->GetString("RasSrv::ARQFeatures", "ArjReasonRouteCallToGatekeeper", "1"))) { bReject = true; if (request.HasOptionalField(H225_AdmissionRequest::e_srcCallSignalAddress)) { PIPSocket::Address ipaddress; if (GetIPFromTransportAddr(request.m_srcCallSignalAddress, ipaddress)) { bReject = !RasSrv->IsForwardedMessage(0, ipaddress); } } if (bReject) { return BuildReply(H225_AdmissionRejectReason::e_routeCallToGatekeeper); } } } bool signalOffload = false; #ifdef HAS_H460 bool EPSupportsQoSReporting = false; bool vendorInfo = false; PString vendor, version; #if defined(HAS_H46026) || defined(HAS_H46023) bool EPRequiresH46026 = false; #endif #ifdef HAS_H46023 bool natsupport = false; CallRec::NatStrategy natoffloadsupport = CallRec::e_natUnknown; #endif /// H.460.9 QoS Reporting if (request.HasOptionalField(H225_AdmissionRequest::e_featureSet)) { H460_FeatureSet fs = H460_FeatureSet(request.m_featureSet); if (fs.HasFeature(9)) { EPSupportsQoSReporting = true; } #ifdef HAS_H46026 if (fs.HasFeature(26) && Toolkit::Instance()->IsH46026Enabled()) EPRequiresH46026 = true; RequestingEP->SetUsesH46026(EPRequiresH46026); #endif } if (request.HasOptionalField(H225_AdmissionRequest::e_genericData)) { H225_ArrayOf_GenericData & data = request.m_genericData; for (PINDEX i = 0; i < data.GetSize(); i++) { H460_Feature & feat = (H460_Feature &)data[i]; if (feat.GetFeatureID() == H460_FeatureID(OpalOID(OID9))) vendorInfo = true; #ifdef HAS_H46023 if (Toolkit::Instance()->IsH46023Enabled() && !EPRequiresH46026) { if (feat.GetFeatureID() == H460_FeatureID(24)) { natsupport = true; H460_FeatureStd & std24 = (H460_FeatureStd &)feat; if (std24.Contains(Std24_NATInstruct)) { unsigned natstat = std24.Value(Std24_NATInstruct); natoffloadsupport = (CallRec::NatStrategy)natstat; } } } #endif } } #endif if (request.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) { H225_TransportAddress tmp; GetCallSignalAddress(tmp); if (tmp == request.m_destCallSignalAddress) request.RemoveOptionalField(H225_AdmissionRequest::e_destCallSignalAddress); } // routing decision bool toParent = false; H225_TransportAddress CalledAddress; Routing::AdmissionRequest arq(request, this, authData.m_callingStationId, authData.m_clientAuthId); if (!answer) { if (!authData.m_destinationRoutes.empty()) { list::const_iterator i = authData.m_destinationRoutes.begin(); while (i != authData.m_destinationRoutes.end()) arq.AddRoute(*i++); } else arq.Process(); if (arq.GetRoutes().empty()) return BuildReply(arq.GetRejectReason()); list::iterator r = arq.GetRoutes().begin(); while (r != arq.GetRoutes().end()) { // PTRACE(1, "route = " << r->AsString() ); if (authData.m_proxyMode != CallRec::ProxyDetect) r->m_proxyMode = authData.m_proxyMode; ++r; } Route route; arq.GetFirstRoute(route); if ((arq.GetRoutes().size() == 1 || !Toolkit::AsBool(GkConfig()->GetString(RoutedSec, "ActivateFailover", "0"))) && route.m_destEndpoint && !route.m_destEndpoint->HasAvailableCapacity(request.m_destinationInfo)) return BuildReply(H225_AdmissionRejectReason::e_resourceUnavailable); CalledEP = route.m_destEndpoint; CalledAddress = route.m_destAddr; toParent = route.m_flags & Route::e_toParent; aliasesChanged = aliasesChanged || (arq.GetFlags() & Routing::AdmissionRequest::e_aliasesChanged); // record neighbor used for rewriting purposes if (route.m_flags & Route::e_toNeighbor) out_rewrite_source = route.m_routeId; authData.m_proxyMode = route.m_proxyMode; } // bandwidth check long BWRequest = request.m_bandWidth.GetValue(); // this is the bidirectional bandwidth // force a minimum bandwidth for endpoints that don't properly report usage (eg. Netmeeting) if ((CallTbl->GetMinimumBandwidthPerCall() > 0) && (BWRequest < CallTbl->GetMinimumBandwidthPerCall())) BWRequest = CallTbl->GetMinimumBandwidthPerCall(); // enforce max bandwidth per call if ((CallTbl->GetMaximumBandwidthPerCall() > 0) && (BWRequest > CallTbl->GetMaximumBandwidthPerCall())) BWRequest = CallTbl->GetMaximumBandwidthPerCall(); if (BWRequest > 0) { // check if it is the first arrived ARQ if (pExistingCallRec) { // 2nd ARQ: request more bandwidth if needed BWRequest = CallTbl->CheckEPBandwidth(pExistingCallRec->GetCalledParty(), BWRequest); long AdditionalBW = 0; long callBW = pExistingCallRec->GetBandwidth(); if (BWRequest > callBW) { AdditionalBW = CallTbl->CheckTotalBandwidth(BWRequest - callBW); AdditionalBW = CallTbl->CheckEPBandwidth(pExistingCallRec->GetCallingParty(), AdditionalBW); BWRequest = callBW + AdditionalBW; callBW = BWRequest; } if (BWRequest <= 0) { bReject = true; } else { bReject = false; CallTbl->UpdateEPBandwidth(pExistingCallRec->GetCallingParty(), AdditionalBW); CallTbl->UpdateEPBandwidth(pExistingCallRec->GetCalledParty(), callBW); CallTbl->UpdateTotalBandwidth(AdditionalBW); pExistingCallRec->SetBandwidth(callBW); } } else { // 1st ARQ BWRequest = CallTbl->CheckEPBandwidth(RequestingEP, BWRequest); BWRequest = CallTbl->CheckEPBandwidth(CalledEP, BWRequest); BWRequest = CallTbl->CheckTotalBandwidth(BWRequest); if (BWRequest <= 0) { bReject = true; PTRACE(3, "GK\tARJ - no bandwidth"); } else { CallTbl->UpdateEPBandwidth(RequestingEP, BWRequest); CallTbl->UpdateTotalBandwidth(BWRequest); } } } if (bReject) return BuildReply(H225_AdmissionRejectReason::e_requestDenied); // what the spec says PTRACE(3, "GK\tACF will grant bandwidth of " << BWRequest); // new connection admitted H225_AdmissionConfirm & acf = BuildConfirm(); acf.m_bandWidth = (int)BWRequest; // Per GW outbound rewrite if (hasDestInfo && CalledEP && (RequestingEP != CalledEP)) { if(CalledEP->GetAliases().GetSize() > 0) { out_rewrite_source = GetBestAliasAddressString( CalledEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); } if (!out_rewrite_source.IsEmpty()) if (Kit->GWRewriteE164(out_rewrite_source, GW_REWRITE_OUT, request.m_destinationInfo[0], pExistingCallRec) && !RasSrv->IsGKRouted()) aliasesChanged = true; } if (hasDestInfo && CalledEP && RequestingEP != CalledEP && !arq.GetRoutes().empty() && !arq.GetRoutes().front().m_destOutNumber.IsEmpty()) { for (PINDEX i = 0; i < request.m_destinationInfo.GetSize(); ++i) if (request.m_destinationInfo[i].GetTag() == H225_AliasAddress::e_dialedDigits) { H323SetAliasAddress(arq.GetRoutes().front().m_destOutNumber, request.m_destinationInfo[i], request.m_destinationInfo[i].GetTag()); aliasesChanged = aliasesChanged || RasSrv->IsGKRouted(); } else if (request.m_destinationInfo[i].GetTag() == H225_AliasAddress::e_partyNumber) { H225_PartyNumber &partyNumber = request.m_destinationInfo[i]; if (partyNumber.GetTag() == H225_PartyNumber::e_e164Number) { H225_PublicPartyNumber &number = partyNumber; number.m_publicNumberDigits = arq.GetRoutes().front().m_destOutNumber; aliasesChanged = aliasesChanged || RasSrv->IsGKRouted(); } else if (partyNumber.GetTag() == H225_PartyNumber::e_privateNumber) { H225_PrivatePartyNumber &number = partyNumber; number.m_privateNumberDigits = arq.GetRoutes().front().m_destOutNumber; aliasesChanged = aliasesChanged || RasSrv->IsGKRouted(); } } } CallRec *pCallRec =NULL; if (pExistingCallRec) { // duplicate or answer ARQ PTRACE(3, "GK\tACF: found existing call no " << pExistingCallRec->GetCallNumber()); if (authData.m_callDurationLimit > 0) pExistingCallRec->SetDurationLimit(authData.m_callDurationLimit); if (!authData.m_callingStationId) pExistingCallRec->SetCallingStationId(authData.m_callingStationId); if (!authData.m_calledStationId) pExistingCallRec->SetCalledStationId(authData.m_calledStationId); if (authData.m_clientAuthId > 0) pExistingCallRec->SetClientAuthId(authData.m_clientAuthId); pExistingCallRec->SetBindHint(arq.GetSourceIP()); } else { // the call is not in the table pCallRec = new CallRec(*this, BWRequest, destinationString, authData.m_proxyMode); pCallRec->SetBindHint(arq.GetSourceIP()); pCallRec->SetCallerID(arq.GetCallerID()); if (CalledEP) { pCallRec->SetCalled(CalledEP); } else { if (answer) { pCallRec->SetCalled(RequestingEP); } pCallRec->SetDestSignalAddr(CalledAddress); } if (!answer) { pCallRec->SetCalling(RequestingEP); } if (toParent) { pCallRec->SetToParent(true); } #ifdef HAS_H46023 if (natoffloadsupport == CallRec::e_natNoassist) { // If no assistance then No NAT type PTRACE(4, "RAS\tNAT Type reset to none"); pCallRec->SetNATType(0); } #endif pCallRec->SetNewRoutes(arq.GetRoutes()); if (!authData.m_disabledcodecs.IsEmpty()) pCallRec->SetDisabledCodecs(authData.m_disabledcodecs); if (authData.m_callDurationLimit > 0) pCallRec->SetDurationLimit(authData.m_callDurationLimit); if (!authData.m_callingStationId) pCallRec->SetCallingStationId(authData.m_callingStationId); if (!authData.m_calledStationId) pCallRec->SetCalledStationId(authData.m_calledStationId); if (!authData.m_dialedNumber) pCallRec->SetDialedNumber(authData.m_dialedNumber); if (authData.m_routeToAlias.GetSize() > 0) pCallRec->SetRouteToAlias(authData.m_routeToAlias); if (authData.m_clientAuthId > 0) pCallRec->SetClientAuthId(authData.m_clientAuthId); if (!RasSrv->IsGKRouted()) pCallRec->SetConnected(); else if (acf.HasOptionalField(H225_AdmissionConfirm::e_cryptoTokens)) pCallRec->SetAccessTokens(acf.m_cryptoTokens); CallTbl->Insert(pCallRec); #ifdef HAS_H46023 if (Toolkit::Instance()->IsH46023Enabled() && !EPRequiresH46026) { // Std24 proxy offload. See if the media can go direct. if (natoffloadsupport == CallRec::e_natUnknown) { if (!pCallRec->NATOffLoad(answer,natoffloadsupport)) { if (natoffloadsupport == CallRec::e_natFailure) { PTRACE(2, "RAS\tWarning: NAT Media Failure detected " << (unsigned)request.m_callReferenceValue); return BuildReply(H225_AdmissionRejectReason::e_noRouteToDestination, true); } } } // If not required disable the proxy support function for this call if (pCallRec->GetProxyMode() != CallRec::ProxyDisabled && (natoffloadsupport == CallRec::e_natLocalMaster || natoffloadsupport == CallRec::e_natRemoteMaster || natoffloadsupport == CallRec::e_natNoassist || (!pCallRec->SingleGatekeeper() && (natoffloadsupport == CallRec::e_natRemoteProxy || natoffloadsupport == CallRec::e_natAnnexB)))) { PTRACE(4, "RAS\tNAT Proxy disabled due to offload support"); pCallRec->SetProxyMode(CallRec::ProxyDisabled); } if (!pCallRec->SingleGatekeeper() && pCallRec->GetProxyMode() == CallRec::ProxyDisabled && (natoffloadsupport == CallRec::e_natRemoteMaster || natoffloadsupport == CallRec::e_natRemoteProxy || natoffloadsupport == CallRec::e_natAnnexB)) { // Where the remote will handle the NAT Traversal // the local gatekeeper may not receive any signalling so // set the call as connected. if (!RasSrv->IsGKRouted()) pCallRec->SetConnected(); } if (!RasSrv->IsGKRouted() && natoffloadsupport == CallRec::e_natUnknown) pCallRec->SetConnected(); pCallRec->SetNATStrategy(natoffloadsupport); PTRACE(4, "RAS\tNAT strategy for Call No: " << pCallRec->GetCallNumber() << " set to " << pCallRec->GetNATOffloadString(natoffloadsupport)); if (natoffloadsupport == CallRec::e_natNoassist) { // If no assistance then No NAT type PTRACE(4, "RAS\tNAT Type reset to none"); pCallRec->SetNATType(0); } signalOffload = pCallRec->NATSignallingOffload(answer); if (signalOffload) { PTRACE(4, "RAS\tNAT H46023 Signal Offload set by policy"); pCallRec->ResetTimeOut(); } pCallRec->GetRemoteInfo(vendor, version); } #endif // Put rewriting information into call record pCallRec->SetInboundRewriteId(in_rewrite_source); pCallRec->SetOutboundRewriteId(out_rewrite_source); } if (RasSrv->IsGKRouted() && !signalOffload) { acf.m_callModel.SetTag(H225_CallModel::e_gatekeeperRouted); GetCallSignalAddress(acf.m_destCallSignalAddress); #ifdef HAS_TLS if (Toolkit::Instance()->IsTLSEnabled() && RequestingEP->UseTLS()) { // tell endpoint to use the TLS port WORD tlsSignalPort = (WORD)GkConfig()->GetInteger(RoutedSec, "TLSCallSignalPort", GK_DEF_TLS_CALL_SIGNAL_PORT); if (acf.m_destCallSignalAddress.GetTag() == H225_TransportAddress::e_ip6Address) { H225_TransportAddress_ip6Address & addr = acf.m_destCallSignalAddress; addr.m_port = tlsSignalPort; } else if (acf.m_destCallSignalAddress.GetTag() == H225_TransportAddress::e_ipAddress) { H225_TransportAddress_ipAddress & addr = acf.m_destCallSignalAddress; addr.m_port = tlsSignalPort; } } #endif } else { acf.m_callModel.SetTag(H225_CallModel::e_direct); acf.m_destCallSignalAddress = CalledAddress; } long irrFrq = GkConfig()->GetInteger("CallTable", "IRRFrequency", 120); if (irrFrq > 0) { acf.IncludeOptionalField ( H225_AdmissionConfirm::e_irrFrequency ); acf.m_irrFrequency.SetValue(irrFrq); } if( !answer && aliasesChanged && request.HasOptionalField(H225_AdmissionRequest::e_canMapAlias) && request.m_canMapAlias && request.HasOptionalField(H225_AdmissionRequest::e_destinationInfo) && request.m_destinationInfo.GetSize() > 0) { acf.IncludeOptionalField(H225_AdmissionConfirm::e_destinationInfo); acf.m_destinationInfo = request.m_destinationInfo; } if (RequestingEP->AddCallCreditServiceControl(acf.m_serviceControl, authData.m_amountString, authData.m_billingMode, authData.m_callDurationLimit)) { acf.IncludeOptionalField(H225_AdmissionConfirm::e_serviceControl); } #ifdef HAS_H460 if (!answer) { PINDEX lastPos = 0; H225_ArrayOf_GenericData & data = acf.m_genericData; #ifdef HAS_H46023 // If we have a call record and the remote party needs NAT support // and the requesting EP can provide it then notify the EP to lend assistance. if (natsupport && RequestingEP->UsesH46023()) { H460_FeatureStd fs = H460_FeatureStd(24); fs.Add(Std24_NATInstruct,H460_FeatureContent((int)natoffloadsupport,8)); lastPos++; data.SetSize(lastPos); data[lastPos-1] = fs; } #endif #ifdef HAS_H460VEN /// OID9 Vendor Information if (vendorInfo) { H460_FeatureOID fs = H460_FeatureOID(OID9); if (!vendor.IsEmpty()) { fs.Add(PString(VendorProdOID), H460_FeatureContent(vendor)); fs.Add(PString(VendorVerOID), H460_FeatureContent(version)); } lastPos++; data.SetSize(lastPos); data[lastPos-1] = fs; } #endif if (lastPos > 0) acf.IncludeOptionalField(H225_AdmissionConfirm::e_genericData); } /// H.460.9 QoS Reporting if (EPSupportsQoSReporting && Toolkit::AsBool(GkConfig()->GetString("GkQoSMonitor", "Enable", "0"))) { H460_FeatureStd feat = H460_FeatureStd(9); if (Toolkit::AsBool(GkConfig()->GetString("GkQoSMonitor", "CallEndOnly", "1"))) { H460_FeatureID finalonly = H460_FeatureID(0); // TODO: standard says 0, Wireshark says 1, PVX ignores both feat.AddParameter(&finalonly); } acf.IncludeOptionalField(H225_AdmissionConfirm::e_featureSet); acf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_desiredFeatures); H225_ArrayOf_FeatureDescriptor & desc = acf.m_featureSet.m_desiredFeatures; desc.SetSize(1); desc[0] = feat; } #ifdef HAS_H46026 /// H.460.26 media tunneling if (EPRequiresH46026) { H460_FeatureStd feat = H460_FeatureStd(26); acf.IncludeOptionalField(H225_AdmissionConfirm::e_featureSet); acf.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_neededFeatures); H225_ArrayOf_FeatureDescriptor & desc = acf.m_featureSet.m_neededFeatures; PINDEX len = desc.GetSize(); desc.SetSize(len + 1); desc[len] = feat; } #endif // HAS_H46026 #endif // HAS_H460 return BuildReply(e_acf, false, pCallRec); } bool AdmissionRequestPDU::BuildReply(int reason, bool h460, CallRec * rec) { PString source = RequestingEP ? AsDotString(RequestingEP->GetCallSignalAddress()) : PString(" "); PString srcInfo = AsString(request.m_srcInfo); const char *answerCall = request.m_answerCall ? "true" : "false"; PString log; if (reason == e_routeRequest) { log = "RouteRequest|" + source + "|" + RequestingEP->GetEndpointIdentifier().GetValue() + "|" + PString(request.m_callReferenceValue) + "|" + destinationString + "|" + srcInfo; PString callid = AsString(request.m_callIdentifier.m_guid); callid.Replace(" ", "-", true); log += PString("|") + callid; log += PString("|") + (rec ? rec->GetMediaRouting() : " "); log += PString(";"); } else if (reason < 0) { log = "ACF|" + source + "|" + RequestingEP->GetEndpointIdentifier().GetValue() + "|" + PString(request.m_callReferenceValue) + "|" + destinationString + "|" + srcInfo + "|" + answerCall; PString callid = AsString(request.m_callIdentifier.m_guid); callid.Replace(" ", "-", true); log += PString("|") + callid; log += PString("|") + (rec ? rec->GetMediaRouting() : " "); log += PString(";"); } else { H225_AdmissionReject & arj = BuildReject(reason); if (reason == H225_AdmissionRejectReason::e_resourceUnavailable) RasSrv->SetAltGKInfo(arj, m_msg->m_peerAddr); #ifdef HAS_H46023 if (h460) { arj.IncludeOptionalField(H225_AdmissionReject::e_genericData); H460_FeatureStd h46024 = H460_FeatureStd(24); arj.m_genericData.SetSize(1); arj.m_genericData[0] = h46024; } #endif log = "ARJ|" + source + "|" + destinationString + "|" + srcInfo + "|" + answerCall + "|" + arj.m_rejectReason.GetTagName(); PString callid = AsString(request.m_callIdentifier.m_guid); callid.Replace(" ", "-", true); log += PString("|") + callid; log += PString(";"); } return PrintStatus(log); } template<> bool RasPDU::Process() { // OnBRQ endptr RequestingEP = EndpointTbl->FindByEndpointId(request.m_endpointIdentifier); if (RequestingEP && RasSrv->ReplyToRasAddress(m_msg->m_peerAddr)) { if (GetIPAndPortFromTransportAddr(RequestingEP->GetRasAddress(), m_msg->m_peerAddr, m_msg->m_peerPort)) { PTRACE(3, "Reply to saved rasAddress:" << AsString(m_msg->m_peerAddr, m_msg->m_peerPort)); } else { PTRACE(1, "Unable to parse saved rasAddress " << RequestingEP->GetRasAddress()); } } long bandwidth = request.m_bandWidth.GetValue(); // enforce minimum bandwidth per call if ((CallTbl->GetMinimumBandwidthPerCall() > 0) && (bandwidth < CallTbl->GetMinimumBandwidthPerCall())) bandwidth = CallTbl->GetMinimumBandwidthPerCall(); // enforce max bandwidth per call if ((CallTbl->GetMaximumBandwidthPerCall() > 0) && (bandwidth > CallTbl->GetMaximumBandwidthPerCall())) bandwidth = CallTbl->GetMaximumBandwidthPerCall(); callptr pCall = request.HasOptionalField(H225_BandwidthRequest::e_callIdentifier) ? CallTbl->FindCallRec(request.m_callIdentifier) : CallTbl->FindCallRec(request.m_callReferenceValue); PString log; unsigned rsn = H225_BandRejectReason::e_securityDenial; bool bReject = !RasSrv->ValidatePDU(*this, rsn); if (!bReject && !pCall) { bReject = true; rsn = H225_BandRejectReason::e_invalidConferenceID; } else if (!bReject) { long AdditionalBW = bandwidth - pCall->GetBandwidth(); // only update settings when more BW is requested, if its less, agree and leave as is if (AdditionalBW > 0) { AdditionalBW = CallTbl->CheckEPBandwidth(pCall->GetCallingParty(), AdditionalBW); AdditionalBW = CallTbl->CheckEPBandwidth(pCall->GetCalledParty(), AdditionalBW); AdditionalBW = CallTbl->CheckTotalBandwidth(AdditionalBW); if (AdditionalBW > 0) { CallTbl->UpdateEPBandwidth(pCall->GetCallingParty(), AdditionalBW); CallTbl->UpdateEPBandwidth(pCall->GetCalledParty(), AdditionalBW); CallTbl->UpdateTotalBandwidth(AdditionalBW); bandwidth = pCall->GetBandwidth() + AdditionalBW; pCall->SetBandwidth(bandwidth); } else { bReject = true; rsn = H225_BandRejectReason::e_insufficientResources; } } else { // the endpoint has requested to lower the bandwidth // fow now we just agree, we could also reduce update the current total, per call and per endpoint usage } } if (bReject) { H225_BandwidthReject & brj = BuildReject(rsn); if (rsn == H225_BandRejectReason::e_insufficientResources) { brj.m_allowedBandWidth = (int)CallTbl->GetAvailableBW(); // ask the endpoint to try alternate gatekeepers RasSrv->SetAltGKInfo(brj, m_msg->m_peerAddr); } log = PString(PString::Printf, "BRJ|%s|%s|%u|%s;", (const char *)m_msg->m_peerAddr.AsString(), (const unsigned char *) request.m_endpointIdentifier.GetValue(), bandwidth, (const unsigned char *) brj.m_rejectReason.GetTagName() ); } else { H225_BandwidthConfirm & bcf = BuildConfirm(); bcf.m_bandWidth = (int)bandwidth; log = PString(PString::Printf, "BCF|%s|%s|%u;", (const char *)m_msg->m_peerAddr.AsString(), (const unsigned char *) request.m_endpointIdentifier.GetValue(), bandwidth ); } return PrintStatus(log); } template<> bool RasPDU::Process() { // OnDRQ bool bReject = false; endptr ep; unsigned rsn = H225_DisengageRejectReason::e_securityDenial; if ((ep = EndpointTbl->FindByEndpointId(request.m_endpointIdentifier))) { PTRACE(4, "GK\tDRQ: closed conference"); bReject = !RasSrv->ValidatePDU(*this, rsn); } else { bReject = true; rsn = H225_DisengageRejectReason::e_notRegistered; } if (ep && RasSrv->ReplyToRasAddress(m_msg->m_peerAddr)) { if (GetIPAndPortFromTransportAddr(ep->GetRasAddress(), m_msg->m_peerAddr, m_msg->m_peerPort)) { PTRACE(3, "Reply to saved rasAddress:" << AsString(m_msg->m_peerAddr, m_msg->m_peerPort)); } else { PTRACE(1, "Unable to parse saved rasAddress " << ep->GetRasAddress()); } } #ifdef HAS_H460 if (request.HasOptionalField(H225_DisengageRequest::e_genericData)) { H225_ArrayOf_GenericData & data = request.m_genericData; for (PINDEX i = 0; i < data.GetSize(); i++) { H460_Feature & feat = (H460_Feature &)data[i]; /// H.460.9 QoS Feature if (feat.GetFeatureID() == H460_FeatureID(9)) { H460_FeatureStd & qosfeat = (H460_FeatureStd &)feat; if (qosfeat.Contains(1)) { CallTbl->QoSReport(*this, ep, qosfeat.Value(1)); } } } } #endif PString log; if (bReject) { H225_DisengageReject & drj = BuildReject(rsn); log = PString(PString::Printf, "DRJ|%s|%s|%u|%s", (const char *)m_msg->m_peerAddr.AsString(), (const unsigned char *) request.m_endpointIdentifier.GetValue(), (unsigned) request.m_callReferenceValue, (const unsigned char *) drj.m_rejectReason.GetTagName() ); PString callid = AsString(request.m_callIdentifier.m_guid); callid.Replace(" ", "-", true); log += PString("|") + callid; log += PString(";"); } else { BuildConfirm(); // always signal DCF log = PString(PString::Printf, "DCF|%s|%s|%u|%s", (const char *)m_msg->m_peerAddr.AsString(), (const unsigned char *) request.m_endpointIdentifier.GetValue(), (unsigned) request.m_callReferenceValue, (const unsigned char *) request.m_disengageReason.GetTagName() ); PString callid = AsString(request.m_callIdentifier.m_guid); callid.Replace(" ", "-", true); log += PString("|") + callid; log += PString(";"); if (!RasSrv->IsGKRouted() || RasSrv->RemoveCallOnDRQ()) CallTbl->RemoveCall(request, ep); } return PrintStatus(log); } template<> bool RasPDU::Process() { // OnLRQ PString log; bool fromTraversalClient = false; if (request.m_destinationInfo.GetSize() > 0) { // Do a check and make sure this is not a ping PString pingAlias = GkConfig()->GetString(LRQFeaturesSection, "PingAlias", ""); if (!pingAlias && pingAlias == AsString(request.m_destinationInfo[0],false)) { BuildReject(H225_LocationRejectReason::e_undefinedReason); PTRACE(5,"LRQ PING caught from " << AsDotString(request.m_replyAddress)); return true; } // do GWRewriteE164 for neighbor before processing PString neighbor_id = RasSrv->GetNeighbors()->GetNeighborIdBySigAdr(request.m_replyAddress); PString neighbor_gkid = RasSrv->GetNeighbors()->GetNeighborGkIdBySigAdr(request.m_replyAddress); // if we didn't find the neighbor by SigIP, check if is a traversal client if (neighbor_id.IsEmpty()) { fromTraversalClient = RasSrv->GetNeighbors()->IsTraversalClient(m_msg->m_peerAddr); if (fromTraversalClient) neighbor_id = RasSrv->GetNeighbors()->GetNeighborGkIdBySigAdr(m_msg->m_peerAddr); } // do the rewrites if (!neighbor_id.IsEmpty()) { Kit->GWRewriteE164(neighbor_id, GW_REWRITE_IN, request.m_destinationInfo[0]); } if (!neighbor_gkid.IsEmpty() && (neighbor_gkid != neighbor_id)) { Kit->GWRewriteE164(neighbor_gkid, GW_REWRITE_IN, request.m_destinationInfo[0]); } // Normal rewrite Kit->RewriteE164(request.m_destinationInfo[0]); } unsigned reason = H225_LocationRejectReason::e_securityDenial; PIPSocket::Address ipaddr; WORD port; bool fromRegEndpoint = false; bool replyAddrMatch = GetIPAndPortFromTransportAddr(request.m_replyAddress, ipaddr, port) && (ipaddr == m_msg->m_peerAddr && port == m_msg->m_peerPort); if (request.HasOptionalField(H225_LocationRequest::e_endpointIdentifier)) if (endptr ep = EndpointTbl->FindByEndpointId(request.m_endpointIdentifier)) fromRegEndpoint = replyAddrMatch ? true : ep->IsNATed(); // send RIP message before doing any real work if configured to do so unsigned ripDelay = GkConfig()->GetInteger("RasSrv::LRQFeatures", "SendRIP", 0); if (ripDelay > 0) { RasSrv->SendRIP(request.m_requestSeqNum, ripDelay, m_msg->m_peerAddr, m_msg->m_peerPort); } // Neighbors do not need Validation bool bReject = !(fromRegEndpoint || RasSrv->GetNeighbors()->CheckLRQ(this)); // If not Neighbor and support non neighbor LRQ's then validate the PDU if (bReject && (Toolkit::AsBool(GkConfig()->GetString(LRQFeaturesSection, "AcceptNonNeighborLRQ", "0")))) bReject = !RasSrv->ValidatePDU(*this, reason); PString sourceInfoString(fromRegEndpoint ? request.m_endpointIdentifier.GetValue() : request.HasOptionalField(H225_LocationRequest::e_gatekeeperIdentifier) ? request.m_gatekeeperIdentifier.GetValue() : m_msg->m_peerAddr.AsString()); // reply to the replyAddress if ReplyToRasAddress is configured if (RasSrv->ReplyToRasAddress(m_msg->m_peerAddr)) { if (GetIPAndPortFromTransportAddr(request.m_replyAddress, m_msg->m_peerAddr, m_msg->m_peerPort)) { PTRACE(3, "Reply to saved rasAddress:" << AsString(m_msg->m_peerAddr, m_msg->m_peerPort)); } else { PTRACE(1, "Unable to parse saved rasAddress " << request.m_replyAddress); } } if (!bReject) { endptr WantedEndPoint; Route route; Routing::LocationRequest lrq(request, this); lrq.Process(); if (lrq.GetFirstRoute(route)) { WantedEndPoint = route.m_destEndpoint; if ((lrq.GetRoutes().size() == 1 || !Toolkit::AsBool(GkConfig()->GetString(RoutedSec, "ActivateFailover", "0"))) && WantedEndPoint && !WantedEndPoint->HasAvailableCapacity(request.m_destinationInfo)) { bReject = true; reason = H225_LocationRejectReason::e_resourceUnavailable; } else { H225_LocationConfirm & lcf = BuildConfirm(); GetRasAddress(lcf.m_rasAddress); if (RasSrv->IsGKRouted()) { GetCallSignalAddress(lcf.m_callSignalAddress); #ifdef HAS_TLS if (Toolkit::Instance()->IsTLSEnabled()) { bool useTLS = RasSrv->GetNeighbors()->GetNeighborTLSBySigAdr(request.m_replyAddress); if (useTLS) { // tell endpoint to use the TLS port WORD tlsSignalPort = (WORD)GkConfig()->GetInteger(RoutedSec, "TLSCallSignalPort", GK_DEF_TLS_CALL_SIGNAL_PORT); if (lcf.m_callSignalAddress.GetTag() == H225_TransportAddress::e_ip6Address) { H225_TransportAddress_ip6Address & addr = lcf.m_callSignalAddress; addr.m_port = tlsSignalPort; } else if (lcf.m_callSignalAddress.GetTag() == H225_TransportAddress::e_ipAddress) { H225_TransportAddress_ipAddress & addr = lcf.m_callSignalAddress; addr.m_port = tlsSignalPort; } } } #endif /* The access token should be standarized somehow and use a correct object identifier. As it does not (currently), we disable it to remove interop problems. PINDEX s = 0; if (lcf.HasOptionalField(H225_LocationConfirm::e_cryptoTokens)) s = lcf.m_cryptoTokens.GetSize(); else lcf.IncludeOptionalField(H225_LocationConfirm::e_cryptoTokens); lcf.m_cryptoTokens.SetSize(s + 1); lcf.m_cryptoTokens[s] = Neighbors::BuildAccessToken(*dest, ipaddr); */ } else { lcf.m_callSignalAddress = route.m_destAddr; } // canMapAlias: include destinationInfo if it has been changed if (lrq.GetFlags() & Routing::LocationRequest::e_aliasesChanged && request.HasOptionalField(H225_LocationRequest::e_canMapAlias) && request.m_canMapAlias) { if (!lcf.HasOptionalField(H225_LocationConfirm::e_destinationInfo)) { lcf.IncludeOptionalField(H225_LocationConfirm::e_destinationInfo); lcf.m_destinationInfo.SetSize(1); } lcf.m_destinationInfo = request.m_destinationInfo; } #ifdef HAS_H460 H225_ArrayOf_GenericData & data = lcf.m_genericData; PINDEX lastPos = 0; if ((WantedEndPoint) && (request.HasOptionalField(H225_LocationRequest::e_genericData))) { H225_ArrayOf_GenericData & locdata = request.m_genericData; for (PINDEX i = 0; i < locdata.GetSize(); i++) { H460_Feature & feat = (H460_Feature &)locdata[i]; /// Std24 NAT Traversal #ifdef HAS_H46023 if (Toolkit::Instance()->IsH46023Enabled()) { if (feat.GetFeatureID() == H460_FeatureID(24)) { H460_FeatureStd std24 = H460_FeatureStd(24); PIPSocket::Address remoteIP; GetIPFromTransportAddr(request.m_replyAddress,remoteIP); bool mustproxy = !Toolkit::Instance()->H46023SameNetwork(remoteIP, WantedEndPoint->IsNATed() ? WantedEndPoint->GetNATIP() :WantedEndPoint->GetIP()); std24.Add(Std24_RemoteNAT,H460_FeatureContent(WantedEndPoint->SupportH46024())); if (mustproxy) { std24.Add(Std24_MustProxy,H460_FeatureContent(mustproxy)); } else { std24.Add(Std24_IsNAT,H460_FeatureContent(true)); std24.Add(Std24_NATdet,H460_FeatureContent(WantedEndPoint->GetEPNATType(), 8)); std24.Add(Std24_ProxyNAT,H460_FeatureContent(WantedEndPoint->HasNATProxy())); std24.Add(Std24_SourceAddr,H460_FeatureContent(H323TransportAddress(WantedEndPoint->GetNATIP(), 0))); std24.Add(Std24_AnnexA,H460_FeatureContent(WantedEndPoint->SupportH46024A())); std24.Add(Std24_AnnexB,H460_FeatureContent(WantedEndPoint->SupportH46024B())); } lastPos++; data.SetSize(lastPos); data[lastPos-1] = std24; } } #endif #ifdef HAS_H460VEN /// OID9 Vendor Interoperability if (feat.GetFeatureID() == H460_FeatureID(OpalOID(OID9))) { PString vendor, version; if (WantedEndPoint->GetEndpointInfo(vendor, version)) { H460_FeatureOID foid9 = H460_FeatureOID(OID9); foid9.Add(PString(VendorProdOID),H460_FeatureContent(vendor)); foid9.Add(PString(VendorVerOID),H460_FeatureContent(version)); lastPos++; data.SetSize(lastPos); data[lastPos-1] = foid9; } } #endif // HAS_H460VEN } } if (lastPos > 0) lcf.IncludeOptionalField(H225_LocationConfirm::e_genericData); PString featureRequired = Kit->Config()->GetString(RoutedSec, "NATStdMin", ""); PBoolean assumePublicH46024 = Toolkit::AsBool(GkConfig()->GetString(RoutedSec, "H46023PublicIP", 0)); if (!featureRequired && featureRequired == "23" && WantedEndPoint && (!WantedEndPoint->SupportH46024() && !assumePublicH46024)) { bReject = true; reason = H225_LocationRejectReason::e_genericDataReason; } else #endif { log = "LCF|" + m_msg->m_peerAddr.AsString() + "|" + (WantedEndPoint ? WantedEndPoint->GetEndpointIdentifier().GetValue() : AsDotString(route.m_destAddr)) + "|" + AsString(request.m_destinationInfo), + "|" + sourceInfoString + ";"; } } } else { if (m_msg->m_replyRAS.GetTag() == H225_RasMessage::e_requestInProgress) { // LRQ is forwarded H225_RequestInProgress & rip = m_msg->m_replyRAS; PString ripData; if (rip.HasOptionalField(H225_RequestInProgress::e_nonStandardData)) { int iec = Toolkit::iecUnknown; if (rip.m_nonStandardData.m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard) { iec = Toolkit::Instance()->GetInternalExtensionCode(rip.m_nonStandardData.m_nonStandardIdentifier); } else if (rip.m_nonStandardData.m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_object) { PASN_ObjectId &oid = rip.m_nonStandardData.m_nonStandardIdentifier; if (oid.GetDataLength() == 0) iec = Toolkit::iecNeighborId; } if (iec == Toolkit::iecNeighborId) ripData = rip.m_nonStandardData.m_data.AsString(); } log = "RIP|" + m_msg->m_peerAddr.AsString() + "|" + ripData + "|" + AsString(request.m_destinationInfo) + "|" + sourceInfoString + ";"; } else { bReject = true; reason = lrq.GetRejectReason(); } } } if (bReject) { // Alias not found H225_LocationReject & lrj = BuildReject(reason); log = "LRJ|" + m_msg->m_peerAddr.AsString() + "|" + AsString(request.m_destinationInfo), + "|" + sourceInfoString, + "|" + lrj.m_rejectReason.GetTagName() + ";"; } // for a registered endpoint, reply to the sent address if (!(fromRegEndpoint || replyAddrMatch || fromTraversalClient)) m_msg->m_peerAddr = ipaddr, m_msg->m_peerPort = port; PrintStatus(log); return true; } template<> bool RasPDU::Process() { // OnIRR if (endptr ep = EndpointTbl->FindByEndpointId(request.m_endpointIdentifier)) { ep->Update(m_msg->m_recvRAS); callptr call; if (request.HasOptionalField(H225_InfoRequestResponse::e_perCallInfo) && request.m_perCallInfo.GetSize() > 0) call = CallTbl->FindCallRec(request.m_perCallInfo[0].m_callIdentifier); else if (request.m_callSignalAddress.GetSize() > 0) call = CallTbl->FindBySignalAdr(request.m_callSignalAddress[0]); if (call) call->Update(request); #ifdef HAS_H460 if (call && request.HasOptionalField(H225_InfoRequestResponse::e_genericData)) { H225_ArrayOf_GenericData & data = request.m_genericData; for (PINDEX i = 0; i < data.GetSize(); i++) { H460_Feature & feat = (H460_Feature &)data[i]; /// H.460.9 QoS Feature if (feat.GetFeatureID() == H460_FeatureID(9)) { H460_FeatureStd & qosfeat = (H460_FeatureStd &)feat; if (qosfeat.Contains(1)) { CallTbl->QoSReport(*this, call, ep , qosfeat.Value(1)); } } } } #endif if (request.HasOptionalField(H225_InfoRequestResponse::e_needResponse) && request.m_needResponse) { if (RasSrv->ReplyToRasAddress(m_msg->m_peerAddr)) { PIPSocket::Address rasIP; WORD rasPort; if (GetIPAndPortFromTransportAddr(request.m_rasAddress, rasIP, rasPort)) { PTRACE(3, "Reply to rasAddress from request:" << AsString(rasIP, rasPort)); m_msg->m_peerAddr = rasIP; m_msg->m_peerPort = rasPort; } else { PTRACE(1, "Unable to parse rasAddress " << request.m_rasAddress); } } BuildConfirm(); PrintStatus(PString(PString::Printf, "IACK|%s;", (const char *)m_msg->m_peerAddr.AsString())); return true; } } // otherwise don't respond return false; } template<> bool RasPDU::Process() { // OnRAI endptr ep = EndpointTbl->FindByEndpointId(request.m_endpointIdentifier); if (ep && RasSrv->ReplyToRasAddress(m_msg->m_peerAddr)) { if (GetIPAndPortFromTransportAddr(ep->GetRasAddress(), m_msg->m_peerAddr, m_msg->m_peerPort)) { PTRACE(3, "Reply to saved rasAddress:" << AsString(m_msg->m_peerAddr, m_msg->m_peerPort)); } else { PTRACE(1, "Unable to parse saved rasAddress " << ep->GetRasAddress()); } } // accept all RAIs H225_ResourcesAvailableConfirm & rac = BuildConfirm(); rac.m_protocolIdentifier = request.m_protocolIdentifier; PrintStatus(PString(PString::Printf, "RAC|%s;", (const char *)m_msg->m_peerAddr.AsString())); return true; } template<> bool RasPDU::Process() { // OnSCI endptr ep = EndpointTbl->FindByEndpointId(request.m_endpointIdentifier); if (ep && RasSrv->ReplyToRasAddress(m_msg->m_peerAddr)) { if (GetIPAndPortFromTransportAddr(ep->GetRasAddress(), m_msg->m_peerAddr, m_msg->m_peerPort)) { PTRACE(3, "Reply to saved rasAddress:" << m_msg->m_peerAddr << ":" << m_msg->m_peerPort); } else { PTRACE(1, "Unable to parse saved rasAddress " << ep->GetRasAddress()); } } H225_ServiceControlResponse & scr = BuildConfirm(); scr.m_requestSeqNum = request.m_requestSeqNum; // redundant, just to avoid compiler warning when H.460.18 is disabled #ifdef HAS_H46018 bool incomingCall = false; // check if its from parent GkClient * gkClient = RasServer::Instance()->GetGkClient(); bool fromParent = gkClient && gkClient->IsRegistered() && gkClient->UsesH46018() && gkClient->CheckFrom(m_msg->m_peerAddr); // find the neighbor this comes from NeighborList::List & neighbors = *RasServer::Instance()->GetNeighbors(); NeighborList::List::iterator iter = find_if(neighbors.begin(), neighbors.end(), bind2nd(mem_fun(&Neighbors::Neighbor::IsFrom), &m_msg->m_peerAddr)); Neighbors::Neighbor * from_neighbor = NULL; bool neighbor_authenticated = true; if (iter != neighbors.end()) from_neighbor = (*iter); // check the password, if set if (from_neighbor) neighbor_authenticated = from_neighbor->Authenticate(this); // accept incomingIndication from neighbor without an entry in supportedFeatures if (request.HasOptionalField(H225_ServiceControlIndication::e_genericData)) { for (PINDEX i=0; i < request.m_genericData.GetSize(); i++) { H460_FeatureStd & feat = (H460_FeatureStd &)request.m_genericData[i]; if (feat.Contains(H460_FeatureID(1))) { if (fromParent || (from_neighbor && neighbor_authenticated)) { // incoming call from parent or neighor PASN_OctetString rawIncomingIndication = feat.Value(H460_FeatureID(1)); H46018_IncomingCallIndication incomingIndication; PPER_Stream raw(rawIncomingIndication); if (incomingIndication.Decode(raw)) { incomingCall = true; PTRACE(2, "Incomming H.460.18 call from neighbor/parent sigAdr=" << AsDotString(incomingIndication.m_callSignallingAddress) << " callID=" << AsString(incomingIndication.m_callID.m_guid)); CallSignalSocket * outgoingSocket = new CallSignalSocket(); // TODO: handle TLS outgoingSocket->OnSCICall(incomingIndication.m_callID, incomingIndication.m_callSignallingAddress); } else { PTRACE(1, "Error decoding IncomingIndication"); SNMP_TRAP(9, SNMPError, Network, "Error decoding IncomingIndication"); } } } else { // not from neighbor or unauthenticated, ignore call } } } // check if its a keepAlive from neighbor if (!incomingCall && request.HasOptionalField(H225_ServiceControlIndication::e_featureSet)) { H460_FeatureSet fs = H460_FeatureSet(request.m_featureSet); if (fs.HasFeature(18) && Toolkit::Instance()->IsH46018Enabled()) { if (!from_neighbor && request.HasOptionalField(H225_ServiceControlIndication::e_cryptoTokens)) { // check if this is from a traversal client, so we can update the IP if ((request.m_cryptoTokens.GetSize() > 0) && (request.m_cryptoTokens[0].GetTag() == H225_CryptoH323Token::e_cryptoGKPwdHash)) { H225_CryptoH323Token_cryptoGKPwdHash & token = request.m_cryptoTokens[0]; PString gkid = token.m_gatekeeperId; NeighborList::List::iterator iter = find_if(neighbors.begin(), neighbors.end(), bind2nd(mem_fun(&Neighbors::Neighbor::IsTraversalUser), &gkid)); if (iter != neighbors.end()) { from_neighbor = (*iter); neighbor_authenticated = from_neighbor->Authenticate(this); if (neighbor_authenticated) from_neighbor->SetApparentIP(m_msg->m_peerAddr); } } } // set interval if (from_neighbor && neighbor_authenticated) { from_neighbor->SetH46018Server(true); // remember to use H.460.18 with this neighbor // keepAlive from a neigbor, send out a SCR with a keepAliveInterval H460_FeatureStd feat = H460_FeatureStd(18); scr.IncludeOptionalField(H225_ServiceControlResponse::e_featureSet); scr.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); scr.m_featureSet.m_supportedFeatures.SetSize(1); scr.m_featureSet.m_supportedFeatures[0] = feat; H46018_LRQKeepAliveData lrqKeepAlive; lrqKeepAlive.m_lrqKeepAliveInterval = 29; PASN_OctetString rawKeepAlive; rawKeepAlive.EncodeSubType(lrqKeepAlive); feat.Add(2, H460_FeatureContent(rawKeepAlive)); scr.IncludeOptionalField(H225_ServiceControlResponse::e_genericData); H225_ArrayOf_GenericData & gd = scr.m_genericData; gd.SetSize(1); gd[0] = feat; // signal success scr.IncludeOptionalField(H225_ServiceControlResponse::e_result); scr.m_result = H225_ServiceControlResponse_result::e_started; } else { // signal failure scr.IncludeOptionalField(H225_ServiceControlResponse::e_result); scr.m_result = H225_ServiceControlResponse_result::e_failed; } } } #endif #ifdef HAS_H460P if (request.HasOptionalField(H225_ServiceControlIndication::e_genericData)) { H460_FeatureSet fs = H460_FeatureSet(request.m_genericData); OpalOID oid3(OID3); if (fs.HasFeature(oid3) && Toolkit::Instance()->IsH460PEnabled()) { H460_FeatureOID * feat = (H460_FeatureOID *)fs.GetFeature(oid3); if (feat->Contains(OID3_PDU)) { PASN_OctetString & prePDU = feat->Value(OID3_PDU); GkPresence & handler = Toolkit::Instance()->GetPresenceHandler(); handler.ProcessPresenceElement(prePDU); } } } #endif PrintStatus(PString(PString::Printf, "SCR|%s;", (const char *)m_msg->m_peerAddr.AsString())); return true; } template<> bool RasPDU::Process() { // OnSCR #ifdef HAS_H46018 // check if it belongs to a neighbor SCI and use the keepAliveInterval if (request.HasOptionalField(H225_ServiceControlResponse::e_featureSet)) { H460_FeatureSet fs = H460_FeatureSet(request.m_featureSet); if (fs.HasFeature(18) && Toolkit::Instance()->IsH46018Enabled()) { for (PINDEX i=0; i < request.m_genericData.GetSize(); i++) { H460_FeatureStd & feat = (H460_FeatureStd &)request.m_genericData[i]; if (feat.Contains(H460_FeatureID(2))) { PASN_OctetString rawKeepAlive = feat.Value(H460_FeatureID(2)); H46018_LRQKeepAliveData lrqKeepAlive; PPER_Stream raw(rawKeepAlive); if (lrqKeepAlive.Decode(raw)) { // find matching neighbor and set interval NeighborList::List & neighbors = *RasServer::Instance()->GetNeighbors(); NeighborList::List::iterator iter = find_if(neighbors.begin(), neighbors.end(), bind2nd(mem_fun(&Neighbors::Neighbor::IsFrom), &m_msg->m_peerAddr)); if (iter != neighbors.end()) { (*iter)->SetH46018GkKeepAliveInterval(lrqKeepAlive.m_lrqKeepAliveInterval); } } else { PTRACE(1, "Error decoding LRQKeepAlive"); SNMP_TRAP(9, SNMPError, Network, "Error decoding LRQKeepAlive"); } } } } } #endif // do nothing return false; } template<> bool RasPDU::Process() { // OnRRJ if (request.HasOptionalField(H225_RegistrationReject::e_nonStandardData) && request.m_nonStandardData.m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard && Toolkit::Instance()->GetInternalExtensionCode((const H225_H221NonStandard&)request.m_nonStandardData.m_nonStandardIdentifier) == Toolkit::iecFailoverRAS) { // RRJ from alternateGKs H225_EndpointIdentifier id; id = request.m_nonStandardData.m_data.AsString(); if (endptr ep = EndpointTbl->FindByEndpointId(id)) { m_msg->m_replyRAS = ep->GetCompleteRegistrationRequest(); if (m_msg->m_replyRAS.GetTag() == H225_RasMessage::e_registrationRequest) { CopyNonStandardData(request, (H225_RegistrationRequest &)m_msg->m_replyRAS); PTRACE(3, "RAS\tSending full RRQ to " << m_msg->m_peerAddr); return true; } } } return false; } gnugk-3.4/PaxHeaders.16356/h323util.h0000644000175000001440000000005012214316316015246 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/h323util.h0000644000175000001440000002226712214316316014220 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // H.323 utility functions // // Copyright (c) 2000-2013, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef H323UTIL_H #define H323UTIL_H "@(#) $Id: h323util.h,v 1.54 2013/09/12 10:51:58 willamowius Exp $" #include #include #include #include #include "config.h" class H225_CallIdentifier; class H225_GloballyUniqueID; class H225_TransportAddress; class H225_TransportAddress_ipAddress; class H225_TransportAddress_ip6Address; class H225_EndpointType; class H225_AliasAddress; class H225_ArrayOf_AliasAddress; class H245_TransportAddress; class H245_UnicastAddress; class PASN_OctetString; class H460_Feature; PString AsString(const PIPSocket::Address &); PString AsString(const PIPSocket::Address &, WORD); PString AsString(const H245_UnicastAddress &); PString AsString(const H245_UnicastAddress_iPAddress &); PString AsString(const H245_UnicastAddress_iP6Address &); PString AsString(const H225_TransportAddress & ta); PString AsDotString(const H225_TransportAddress & ip, bool showPort=true); PString AsString(const H323TransportAddress & ta); PString AsString(const H225_EndpointType & terminalType); PString AsString(const H225_AliasAddress & terminalAlias, bool includeAliasTypeName = true); PString AsString(const PBYTEArray & array); PString AsString(const H225_ArrayOf_AliasAddress & terminalAlias, bool includeAliasTypeName = true); PString AsString(const PASN_OctetString & Octets); PString StripAliasType(const PString & alias); // check if an alias is a valid US 10 digit or 11 digit number (no formatting allowed) bool Is10Dor11Dnumber(const H225_AliasAddress & alias); H245_TransportAddress IPToH245TransportAddr(const PIPSocket::Address & ip, WORD Port); // convert a string (dot notation without port) into an H245 transport address //H245_TransportAddress StringToH245TransportAddr(const PString & Addr, WORD Port); // convert a socket IP address into an H225 transport address H225_TransportAddress SocketToH225TransportAddr(const PIPSocket::Address & Addr, WORD Port); // convert a H.245 unicast address into an H.323 transport address H323TransportAddress H245UnicastToH323TransportAddr(const H245_UnicastAddress & h245unicast); bool GetTransportAddress(const PString & addr, WORD def_port, PIPSocket::Address & ip, WORD & port); bool GetTransportAddress(const PString & addr, WORD def_port, H225_TransportAddress & Result); bool GetIPFromTransportAddr(const H225_TransportAddress & addr, PIPSocket::Address & ip); bool GetIPAndPortFromTransportAddr(const H225_TransportAddress & addr, PIPSocket::Address & ip, WORD & port); PStringArray SplitIPAndPort(const PString & str, WORD default_port); WORD GetH245Port(const H245_UnicastAddress & addr); void SetH245Port(H245_UnicastAddress & addr, WORD port); void SetSockaddr(sockaddr_in & sin, const PIPSocket::Address & ip, WORD port); void SetSockaddr(sockaddr_in & sin, const H323TransportAddress & addr); void SetSockaddr(sockaddr_in & sin, const H245_UnicastAddress & addr); #ifdef hasIPV6 void SetSockaddr(sockaddr_in6 & sin6, const PIPSocket::Address & ip, WORD port); void SetSockaddr(sockaddr_in6 & sin6, const H323TransportAddress & addr); void SetSockaddr(sockaddr_in6 & sin6, const H245_UnicastAddress & addr); #endif bool IsIPAddress(const PString & addr); bool IsIPv4Address(const PString & addr); bool IsIPv6Address(PString addr); unsigned GetVersion(const H225_TransportAddress & ta); // convert an IPv4-mapped-IPv6 address into an IPv4 address, otherwise leave unchanged void UnmapIPv4Address(PIPSocket::Address & addr); // convert an IPv4 address into an IPv4-mapped-IPv6 address, // leave unchanged if IPv6 disabled or is already an IPv6 address void MapIPv4Address(PIPSocket::Address & addr); bool IsLoopback(const PIPSocket::Address &); bool IsSet(const H323TransportAddress & addr); // check if s only contains characters that are valid for E.164s (1234567890*#+,) bool IsValidE164(const PString & s); /** Find an alias which tag is of type specified by #primaryTags#. If no such aliases are found, #secondaryTags# are examined. If still no match is found and #exactMatch# is false, the first alias on the list is returned. @return An index of the alias found or P_MAX_INDEX if there was no match. */ PINDEX GetBestAliasAddressIndex( const H225_ArrayOf_AliasAddress& aliases, /// aliases to be searched bool exactMatch, /// search only specified tags or find any alias unsigned primaryTags, /// ORed tag flags (AliasAddressTagMask) unsigned secondaryTags = 0 /// ORed tag flags (AliasAddressTagMask) ); /** @return An alias tag converted to a bit flag suitable to use with primaryTags or secondaryTags parameter to #GetBestAliasAddressIndex#. */ inline unsigned AliasAddressTagMask(unsigned tag) { return 1U << tag; } /** Find an alias which tag is of type specified by #primaryTags#. If no such aliases are found, #secondaryTags# are examined. If still no match is found and #exactMatch# is false, the first alias on the list is returned. @return A string with the alias found or an empty string if there was no match. */ PString GetBestAliasAddressString( const H225_ArrayOf_AliasAddress& aliases, /// aliases to be searched bool exactMatch, /// search only specified tags or find any alias unsigned primaryTags, /// ORed tag flags (BestAliasTagMask) unsigned secondaryTags = 0 /// ORed tag flags (BestAliasTagMask) ); /** Return 128-bit globally unique identifier as a string composed of four 32-bit hex numbers, with leading zeros skipped or not. This format is compatible with Cisco equipment. @return A string with properly formatted identifier. */ PString GetGUIDString( const H225_GloballyUniqueID& id, /// 128-bit identifier to convert bool fixedLength = false /// skip leading zeros (false) or not (true) ); /** convert a string into a call-id */ H225_CallIdentifier StringToCallId(PString CallId); /** Check if the given #alias# is present on the list of #aliases#. @return An index of the alias on the list or P_MAX_INDEX if the alias is not found. */ PINDEX FindAlias( const H225_ArrayOf_AliasAddress& aliases, /// the list of aliases to check const PString& alias /// alias to find on the list ); /** Check if the given alias matches the prefix. The prefix can be preceeded with '!' to force negative match and contain dots ('.') or percent signs ('%') to match any character. @return 0 if no match is found, a positive integer if normal match is found (the integer gives the match length) or a negative integer if '!' match is found (absolute value gives the match length). */ int MatchPrefix( const char* alias, const char* prefix ); /** Rewrite the string #s# replacing #prefix# with #value#. The #prefix# and #value# strings can contain dots ('.') to copy source characters to the destination string. The #prefix# string can also contain percent signs ('%') to match any character and skip copying, as it is done in case of dots. Examples (prefix=value): 49=111149 - this will change numbers like 497654321 into 1111497654321 49..1=111149..1 - this will change numbers like 49771777 into 111149771777 %%%%49=49 - this will change numbers like 777749123456 into 49123456 %%%%.=. - tricky, but should work as a prefix stripper There are some restrictions: 1.2.3=4.5.6 - this will work (112233 will be changed to 415263) 1..2=1.2.3 - this will work (1122 will be changed to 11223) 1.2.3=1..3 - this won't work The main idea is that "dot strips" on the right hand side of the rule have to match "dot strips" of the same length on the left hand side of the rule. @return Rewritten string. */ PString RewriteString( const PString& s, /// original string to rewrite const char *prefix, /// prefix string that matched const char *value, /// new string that replaces the prefix string PString & postdialdigts, bool postdialmatch = false ); /** Rewrite Wildcard match Lightweight Regex match rewrite. Only basic number matching supported at the moment Example 12345678 {\1}@mydomain.com = 12345678@mydomain.com 12345678 {^\d(4)}@mydomain.com = 1234@mydomain.com 12345678 {\d(4)$}@mydomain.com = 5678@mydomain.com @return Rewritten string. */ PString RewriteWildcard( const PString & s, /// original string to rewrite const PString & expression /// prefix string that matched ); /** ReplaceParameters General parameter rewrite procedure. @return Rewritten string. */ PString ReplaceParameters( const PString & queryStr, /// parametrized query string const std::map & queryParams /// parameter values ); bool FindH460Descriptor(unsigned feat, H225_ArrayOf_FeatureDescriptor & features, unsigned & location); void RemoveH460Descriptor(unsigned feat, H225_ArrayOf_FeatureDescriptor & features); #ifdef HAS_H460 void AddH460Feature(H225_ArrayOf_FeatureDescriptor & desc, const H460_Feature & newFeat); #endif #endif // H323UTIL_H gnugk-3.4/PaxHeaders.16356/addpasswd_2008.vcproj0000644000175000001440000000005011451722072017372 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/addpasswd_2008.vcproj0000644000175000001440000001236011451722072016335 0ustar00janusers00000000000000 gnugk-3.4/PaxHeaders.16356/stl_supp.h0000644000175000001440000000005011461315737015553 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/stl_supp.h0000644000175000001440000001504511461315737014521 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // stl_supp.h // // Supplementary of STL // // Copyright (c) 2001-2010, Jan Willamowius // // Part of this code is adapted from the SGI implementation // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef STL_SUPP_H #define STL_SUPP_H "@(#) $Id: stl_supp.h,v 1.24 2010/10/25 15:01:51 willamowius Exp $" #include #include #include #include // Composition adaptor is not part of C++ standard #if !defined(__GNUC__) || (defined(__GNUC__) && __GNUC__ >= 3) template class unary_compose : public std::unary_function { protected: _Operation1 __op1; _Operation2 __op2; public: unary_compose(const _Operation1& __x, const _Operation2& __y) : __op1(__x), __op2(__y) {} typename _Operation1::result_type operator()(const typename _Operation2::argument_type& __x) const { return __op1(__op2(__x)); } }; template inline unary_compose<_Operation1,_Operation2> compose1(const _Operation1& __op1, const _Operation2& __op2) { return unary_compose<_Operation1,_Operation2>(__op1, __op2); } #else using std::compose1; #endif // since VC6 didn't support partial specialization, use different names template class mem_vfun_t : public std::unary_function<_Tp*,void> { public: explicit mem_vfun_t(void (_Tp::*__pf)()) : _M_f(__pf) {} void operator()(_Tp* __p) const { (__p->*_M_f)(); } private: void (_Tp::*_M_f)(); }; template class const_mem_vfun_t : public std::unary_function { public: explicit const_mem_vfun_t(void (_Tp::*__pf)() const) : _M_f(__pf) {} void operator()(const _Tp* __p) const { (__p->*_M_f)(); } private: void (_Tp::*_M_f)() const; }; template class mem_vfun_ref_t : public std::unary_function<_Tp,void> { public: explicit mem_vfun_ref_t(void (_Tp::*__pf)()) : _M_f(__pf) {} void operator()(_Tp& __r) const { (__r.*_M_f)(); } private: void (_Tp::*_M_f)(); }; template class const_mem_vfun_ref_t : public std::unary_function<_Tp,void> { public: explicit const_mem_vfun_ref_t(void (_Tp::*__pf)() const) : _M_f(__pf) {} void operator()(const _Tp& __r) const { (__r.*_M_f)(); } private: void (_Tp::*_M_f)() const; }; template class mem_vfun1_t : public std::binary_function<_Tp*,_Arg,void> { public: explicit mem_vfun1_t(void (_Tp::*__pf)(_Arg)) : _M_f(__pf) {} void operator()(_Tp* __p, _Arg __x) const { (__p->*_M_f)(__x); } private: void (_Tp::*_M_f)(_Arg); }; template class const_mem_vfun1_t : public std::binary_function { public: explicit const_mem_vfun1_t(void (_Tp::*__pf)(_Arg) const) : _M_f(__pf) {} void operator()(const _Tp* __p, _Arg __x) const { (__p->*_M_f)(__x); } private: void (_Tp::*_M_f)(_Arg) const; }; template class mem_vfun1_ref_t : public std::binary_function<_Tp,_Arg,void> { public: explicit mem_vfun1_ref_t(void (_Tp::*__pf)(_Arg)) : _M_f(__pf) {} void operator()(_Tp& __r, _Arg __x) const { (__r.*_M_f)(__x); } private: void (_Tp::*_M_f)(_Arg); }; template class const_mem_vfun1_ref_t : public std::binary_function<_Tp,_Arg,void> { public: explicit const_mem_vfun1_ref_t(void (_Tp::*__pf)(_Arg) const) : _M_f(__pf) {} void operator()(const _Tp& __r, _Arg __x) const { (__r.*_M_f)(__x); } private: void (_Tp::*_M_f)(_Arg) const; }; template inline mem_vfun_t<_Tp> mem_vfun(void (_Tp::*__f)()) { return mem_vfun_t<_Tp>(__f); } template inline const_mem_vfun_t<_Tp> mem_vfun(void (_Tp::*__f)() const) { return const_mem_vfun_t<_Tp>(__f); } template inline mem_vfun1_t<_Tp,_Arg> mem_vfun(void (_Tp::*__f)(_Arg)) { return mem_vfun1_t<_Tp,_Arg>(__f); } template inline const_mem_vfun1_t<_Tp,_Arg> mem_vfun(void (_Tp::*__f)(_Arg) const) { return const_mem_vfun1_t<_Tp,_Arg>(__f); } template inline mem_vfun1_ref_t<_Tp,_Arg> mem_vfun_ref(void (_Tp::*__f)(_Arg)) { return mem_vfun1_ref_t<_Tp,_Arg>(__f); } template inline const_mem_vfun_ref_t<_Tp> mem_vfun_ref(void (_Tp::*__f)() const) { return const_mem_vfun_ref_t<_Tp>(__f); } template inline const_mem_vfun1_ref_t<_Tp,_Arg> mem_vfun_ref(void (_Tp::*__f)(_Arg) const) { return const_mem_vfun1_ref_t<_Tp,_Arg>(__f); } // end of partial specialization struct str_prefix_greater : public std::binary_function { bool operator()(const std::string& s1, const std::string& s2) const { if (s1.size() == s2.size()) return s1 > s2; else return s1.size() > s2.size(); } }; struct str_prefix_lesser : public std::binary_function { bool operator()(const std::string& s1, const std::string& s2) const { if (s1.size() == s2.size()) return s1 < s2; else return s1.size() < s2.size(); } }; struct pstr_prefix_lesser : public std::binary_function { bool operator()(const PString& s1, const PString& s2) const { if (s1.GetLength() == s2.GetLength()) return s1 < s2; else return s1.GetLength() < s2.GetLength(); } }; template class deleteobj { // PT is a pointer type public: void operator()(PT pt) { delete pt; } }; template class deletepair { // PAIR::second_type is a pointer type public: void operator()(const PAIR & p) { delete p.second; } }; template inline void ForEachInContainer(const C & c, const F & f) { std::for_each(c.begin(), c.end(), f); } template inline void DeleteObjectsInContainer(const C & c) { typedef typename C::value_type PT; std::for_each(c.begin(), c.end(), deleteobj()); } template inline void DeleteObjectsInMap(const M & m) { typedef typename M::value_type PAIR; std::for_each(m.begin(), m.end(), deletepair()); } template inline void DeleteObjectsInArray(PT *begin, PT *end) { std::for_each(begin, end, deleteobj()); } template inline void DeleteObjects(Iterator begin, Iterator end) { typedef typename Iterator::value_type PT; std::for_each(begin, end, deleteobj()); } #endif // STL_SUPP_H gnugk-3.4/PaxHeaders.16356/gk.rc0000644000175000001440000000005012202261625014446 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gk.rc0000644000175000001440000000560112202261625013411 0ustar00janusers00000000000000//Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 #ifndef _MAC ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION 3,4,0,0 PRODUCTVERSION 3,4,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x2bL #else FILEFLAGS 0x28L #endif FILEOS 0x40004L FILETYPE 0x1L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "Comments", "The OpenSource H.323 Gatekeeper\0" VALUE "CompanyName", "GnuGk.org\0" VALUE "FileDescription", "gnugk\0" VALUE "FileVersion", "3.4.0\0" VALUE "InternalName", "gnugk\0" VALUE "LegalCopyright", "Copyright (c) 1999-2013, Published under the terms of the GNU General Public License version 2.\0" VALUE "LegalTrademarks", "GNU Gatekeeper\0" VALUE "OriginalFilename", "gnugk.exe\0" VALUE "PrivateBuild", "\0" VALUE "ProductName", "GNU Gatekeeper\0" VALUE "ProductVersion", "3.4.0\0" VALUE "SpecialBuild", "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END #endif // !_MAC #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_ICON_GNUGK ICON DISCARDABLE "gk.ico" #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED gnugk-3.4/PaxHeaders.16356/Neighbor.cxx0000644000175000001440000000005012214316314015777 xustar000000000000000020 atime=1380868658 20 ctime=1380868609 gnugk-3.4/Neighbor.cxx0000644000175000001440000022773012214316314014753 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // Neighboring System for GNU Gatekeeper // // Copyright (c) Citron Network Inc. 2002-2003 // Copyright (c) 2004-2012, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #include #include #include #include #include #include "gk_const.h" #include "stl_supp.h" #include "GkClient.h" #include "RasPDU.h" #include "RasSrv.h" #include "RasTbl.h" #include "sigmsg.h" #include "cisco.h" #include "h323util.h" #include "Neighbor.h" #include "config.h" #ifdef HAS_H460 #include #endif using std::multimap; using std::make_pair; using std::find_if; using std::bind2nd; using std::equal_to; using std::mem_fun; using Routing::Route; namespace Neighbors { const char *NeighborSection = "RasSrv::Neighbors"; const char *LRQFeaturesSection = "RasSrv::LRQFeatures"; static const char OID_MD5[] = "1.2.840.113549.2.5"; void SetCryptoGkTokens(H225_ArrayOf_CryptoH323Token & cryptoTokens, const PString & id, const PString & password) { cryptoTokens.SetSize(0); H235AuthSimpleMD5 auth; // avoid copying for thread-safety auth.SetLocalId((const char *)id); auth.SetPassword((const char *)password); H225_ArrayOf_ClearToken dumbTokens; H225_ArrayOf_CryptoH323Token newCryptoTokens; auth.PrepareTokens(dumbTokens, newCryptoTokens); H225_CryptoH323Token_cryptoEPPwdHash & cryptoEPPwdHash = newCryptoTokens[0]; // Create the H.225 GK crypto token with the hash calculated for the EP token H225_CryptoH323Token * finalCryptoToken = new H225_CryptoH323Token; finalCryptoToken->SetTag(H225_CryptoH323Token::e_cryptoGKPwdHash); H225_CryptoH323Token_cryptoGKPwdHash & cryptoGKPwdHash = *finalCryptoToken; // Set the token data that actually goes over the wire cryptoGKPwdHash.m_gatekeeperId = id; cryptoGKPwdHash.m_timeStamp = cryptoEPPwdHash.m_timeStamp; cryptoGKPwdHash.m_token.m_algorithmOID = OID_MD5; cryptoGKPwdHash.m_token.m_hash = cryptoEPPwdHash.m_token.m_hash; cryptoTokens.Append(finalCryptoToken); } class OldGK : public Neighbor { // override from class Neighbor virtual bool SetProfile(const PString &, const PString &); }; class GnuGK : public Neighbor { // override from class Neighbor virtual bool OnSendingLRQ(H225_LocationRequest &, const AdmissionRequest &); virtual bool OnSendingLRQ(H225_LocationRequest &, const SetupRequest &); virtual bool OnSendingLRQ(H225_LocationRequest &, const FacilityRequest &); virtual bool IsAcceptable(RasMsg *ras) const; }; class CiscoGK : public Neighbor { public: // override from class Neighbor virtual bool OnSendingLRQ(H225_LocationRequest &, const AdmissionRequest &); virtual bool OnSendingLRQ(H225_LocationRequest &, const LocationRequest &); virtual bool OnSendingLRQ(H225_LocationRequest &, const SetupRequest &); virtual bool CheckReply(RasMsg *msg) const; }; // stupid Clarent gatekeeper class ClarentGK : public Neighbor { // override from class Neighbor virtual bool OnSendingLRQ(H225_LocationRequest &); }; // a gatekeeper by Korea vendor class GlonetGK : public Neighbor { // override from class Neighbor virtual bool OnSendingLRQ(H225_LocationRequest &, const AdmissionRequest &); virtual bool OnSendingLRQ(H225_LocationRequest &, const LocationRequest &); virtual bool OnSendingLRQ(H225_LocationRequest &, const SetupRequest &); virtual bool OnSendingLRQ(H225_LocationRequest &, const FacilityRequest &); bool BuildLRQ(H225_LocationRequest &, WORD); }; namespace { // anonymous namespace SimpleCreator OldGKCreator("OldGK"); SimpleCreator GnuGKCreator("GnuGK"); SimpleCreator CiscoGKCreator("CiscoGK"); SimpleCreator ClarentGKCreator("ClarentGK"); SimpleCreator GlonetGKCreator("GlonetGK"); int challenge; const char * const OID_T = "0.0.8.235.0.2.5"; } // if we put nomatch into anonymous namespace, // stupid VC can't find it, why?? static const PrefixInfo nomatch(-1, 0); // template class LRQSender typedef Functor2 LRQFunctor; template class LRQSender : public LRQFunctor { public: LRQSender(const R & r) : m_r(r) {} virtual PrefixInfo operator()(Neighbor *, WORD seqnum) const; private: const R & m_r; }; template PrefixInfo LRQSender::operator()(Neighbor *nb, WORD seqnum) const { // select neighbor based on dialed alias if (const H225_ArrayOf_AliasAddress *dest = m_r.GetAliases()) { H225_ArrayOf_AliasAddress aliases; if (PrefixInfo info = nb->GetPrefixInfo(*dest, aliases)) { H225_RasMessage lrq_ras; H225_LocationRequest & lrq = nb->BuildLRQ(lrq_ras, seqnum, aliases); if (nb->OnSendingLRQ(lrq, m_r) && nb->SendLRQ(lrq_ras)) return info; } } // select neighbor based on dialed IP if (const H225_TransportAddress *dest = m_r.GetDestIP()) { H225_ArrayOf_AliasAddress aliases; if (nb->GetIPInfo(*dest, aliases)) { H225_RasMessage lrq_ras; H225_LocationRequest & lrq = nb->BuildLRQ(lrq_ras, seqnum, aliases); if (nb->OnSendingLRQ(lrq, m_r) && nb->SendLRQ(lrq_ras)) return PrefixInfo(100, 1); } } return nomatch; } class LRQForwarder : public LRQFunctor { public: LRQForwarder(const LocationRequest & l) : m_lrq(l) {} virtual PrefixInfo operator()(Neighbor *, WORD) const; private: const LocationRequest & m_lrq; }; PrefixInfo LRQForwarder::operator()(Neighbor *nb, WORD /*seqnum*/) const { H225_ArrayOf_AliasAddress aliases; if (PrefixInfo info = nb->GetPrefixInfo(m_lrq.GetRequest().m_destinationInfo, aliases)) { H225_RasMessage lrq_ras; lrq_ras.SetTag(H225_RasMessage::e_locationRequest); H225_LocationRequest & lrq = lrq_ras; // copy and forward lrq = m_lrq.GetRequest(); lrq.m_destinationInfo = aliases; // include hopCount if configured and not already included if (nb->GetDefaultHopCount() >= 1 && !lrq.HasOptionalField(H225_LocationRequest::e_hopCount)) { lrq.IncludeOptionalField(H225_LocationRequest::e_hopCount); lrq.m_hopCount = nb->GetDefaultHopCount(); } if (nb->OnSendingLRQ(lrq, m_lrq) && nb->SendLRQ(lrq_ras)) return info; } return nomatch; } // class Neighbor Neighbor::Neighbor() { m_rasSrv = RasServer::Instance(); m_port = 0; m_forwardHopCount = 0; m_dynamic = false; m_acceptForwarded = true; m_forwardResponse = false; m_forwardto = 0; m_externalGK = false; m_keepAliveTimer = GkTimerManager::INVALID_HANDLE; m_keepAliveTimerInterval = 0; m_H46018Server = false; m_H46018Client = false; m_useTLS = false; } Neighbor::~Neighbor() { if (m_keepAliveTimer != GkTimerManager::INVALID_HANDLE) Toolkit::Instance()->GetTimerManager()->UnregisterTimer(m_keepAliveTimer); PTRACE(1, "NB\tDelete neighbor " << m_id); } bool Neighbor::SendLRQ(H225_RasMessage & lrq_ras) { if (!m_sendPassword.IsEmpty()) { H225_LocationRequest & lrq = lrq_ras; lrq.IncludeOptionalField(H225_LocationRequest::e_cryptoTokens); lrq.m_cryptoTokens.SetSize(1); SetCryptoGkTokens(lrq.m_cryptoTokens, m_sendAuthUser, m_sendPassword); } return m_rasSrv->SendRas(lrq_ras, GetIP(), m_port); } PIPSocket::Address Neighbor::GetIP() const { if (m_dynamic) { PIPSocket::ClearNameCache(); // Retrieve the ip address at this time if (!GetTransportAddress(m_name, GK_DEF_UNICAST_RAS_PORT, m_ip, m_port)) { PTRACE(1, "NB\tCan't get neighbor ip for " << m_name); } } return m_ip; } WORD Neighbor::GetPort() const { if (m_dynamic) { PIPSocket::ClearNameCache(); // Retrieve the ip address at this time if (!GetTransportAddress(m_name, GK_DEF_UNICAST_RAS_PORT, m_ip, m_port)) { PTRACE(1, "NB\tCan't get neighbor port for " << m_name); } } return m_port; } H225_LocationRequest & Neighbor::BuildLRQ(H225_RasMessage & lrq_ras, WORD seqnum, const H225_ArrayOf_AliasAddress & dest) { lrq_ras.SetTag(H225_RasMessage::e_locationRequest); H225_LocationRequest & lrq = lrq_ras; lrq.m_requestSeqNum = seqnum; lrq.m_destinationInfo = dest; // perform outbound per GK rewrite on the destination of the LRQ Toolkit::Instance()->GWRewriteE164(m_id, GW_REWRITE_OUT, lrq.m_destinationInfo[0]); if (m_gkid != m_id) Toolkit::Instance()->GWRewriteE164(m_gkid, GW_REWRITE_OUT, lrq.m_destinationInfo[0]); lrq.m_replyAddress = m_rasSrv->GetRasAddress(GetIP()); // lrq.IncludeOptionalField(H225_LocationRequest::e_gatekeeperIdentifier); // lrq.m_gatekeeperIdentifier = Toolkit::GKName(); // lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData); // lrq.m_nonStandardData.m_data.SetValue(m_id); lrq.IncludeOptionalField(H225_LocationRequest::e_sourceInfo); lrq.m_sourceInfo.SetSize(1); H323SetAliasAddress(Toolkit::GKName(), lrq.m_sourceInfo[0], H225_AliasAddress::e_h323_ID); // TODO: is this right ? if (m_externalGK) { m_rasSrv->GetGkClient()->SetNBPassword(lrq, Toolkit::GKName()); } if (m_forwardHopCount >= 1) { // what if set hopCount = 1? lrq.IncludeOptionalField(H225_LocationRequest::e_hopCount); lrq.m_hopCount = m_forwardHopCount; } return lrq; } bool Neighbor::SetProfile(const PString & id, const PString & type) { PConfig *config = GkConfig(); PString section("Neighbor::" + (m_id = id)); m_gkid = config->GetString(section, "GatekeeperIdentifier", id); m_name = config->GetString(section, "Host", ""); m_dynamic = Toolkit::AsBool(config->GetString(section, "Dynamic", "0")); m_externalGK = false; m_authUser = config->GetString(section, "AuthUser", m_gkid); // defaults to GatekeeperIdentifier m_password = Toolkit::Instance()->ReadPassword(section, "Password"); // checking incomming password in LRQ (not implemented, yet) m_sendAuthUser = config->GetString(section, "SendAuthUser", Toolkit::GKName()); // defaults to own GatekeeperId m_sendPassword = Toolkit::Instance()->ReadPassword(section, "SendPassword"); // password to send to neighbor if (!m_dynamic && !GetTransportAddress(m_name, GK_DEF_UNICAST_RAS_PORT, m_ip, m_port)) return false; m_sendPrefixes.clear(); PString sprefix(config->GetString(section, "SendPrefixes", "")); PStringArray sprefixes(sprefix.Tokenise(",", false)); for (PINDEX i = 0; i < sprefixes.GetSize(); ++i) { PStringArray p(sprefixes[i].Tokenise(":=", false)); m_sendPrefixes[p[0]] = (p.GetSize() > 1) ? p[1].AsInteger() : 1; } m_sendIPs = config->GetString(section, "SendIPs", ""); PString salias(config->GetString(section, "SendAliases", "")); PStringArray defs(salias.Tokenise(",", FALSE)); m_sendAliases.SetSize(0); for (PINDEX i = 0; i < defs.GetSize(); i++) { if (defs[i].Find("-") != P_MAX_INDEX) { // range PStringArray bounds(defs[i].Tokenise("-", FALSE)); unsigned lower = bounds[0].AsUnsigned(); unsigned upper = 0; if (bounds.GetSize() == 2) { upper = bounds[1].AsUnsigned(); } else { PTRACE(1, "SendAliases: Invalid range definition: " << defs[i]); continue; } if (upper <= lower) { PTRACE(1, "SendAliases: Invalid range bounds: " << defs[i]); continue; } unsigned num = upper - lower; for (unsigned j = 0; j <= num; j++) { PString number(lower + j); PTRACE(4, "Adding alias " << number << " to neighbor " << m_id << " (from range)"); m_sendAliases.AppendString(number); } } else { // single alias PTRACE(4, "Adding alias " << defs[i] << " to neighbor " << m_id); m_sendAliases.AppendString(defs[i]); } } PString aprefix(config->GetString(section, "AcceptPrefixes", "*")); m_acceptPrefixes = PStringArray(aprefix.Tokenise(",", false)); if (m_keepAliveTimer != GkTimerManager::INVALID_HANDLE) Toolkit::Instance()->GetTimerManager()->UnregisterTimer(m_keepAliveTimer); #ifdef HAS_H46018 if (Toolkit::AsBool(config->GetString(section, "H46018Client", "0"))) { m_H46018Client = true; SetH46018GkKeepAliveInterval(29); // start with every 29 seconds } m_H46018Server = Toolkit::AsBool(config->GetString(section, "H46018Server", "0")); #endif #ifdef HAS_TLS m_useTLS = Toolkit::AsBool(config->GetString(section, "UseTLS", "0")); #endif SetForwardedInfo(section); PString info = " of type " + type; if (!sprefix) info = " send=" + sprefix; if (!aprefix) info += " accept=" + aprefix; PTRACE(1, "Set neighbor " << id << '(' << (m_dynamic ? m_name : AsString(m_ip, m_port)) << ')' << info); return true; } void Neighbor::SendH46018GkKeepAlive(GkTimer* timer) { #ifdef HAS_H46018 // send SCI to open the pinhole to neighbor GK H225_RasMessage sci_ras; sci_ras.SetTag(H225_RasMessage::e_serviceControlIndication); H225_ServiceControlIndication & sci = sci_ras; sci.m_requestSeqNum = m_rasSrv->GetRequestSeqNum(); // Tandberg GK adds open here, the standard doesn't mention this H225_ServiceControlSession controlOpen; controlOpen.m_sessionId = 0; controlOpen.m_reason = H225_ServiceControlSession_reason::e_open; sci.m_serviceControl.SetSize(1); sci.m_serviceControl[0] = controlOpen; H460_FeatureStd feat = H460_FeatureStd(18); sci.IncludeOptionalField(H225_ServiceControlIndication::e_featureSet); sci.m_featureSet.IncludeOptionalField(H225_FeatureSet::e_supportedFeatures); H225_ArrayOf_FeatureDescriptor & desc = sci.m_featureSet.m_supportedFeatures; desc.SetSize(1); desc[0] = feat; if (!m_sendPassword.IsEmpty()) { sci.IncludeOptionalField(H225_ServiceControlIndication::e_cryptoTokens); sci.m_cryptoTokens.SetSize(1); SetCryptoGkTokens(sci.m_cryptoTokens, m_sendAuthUser, m_sendPassword); } m_rasSrv->SendRas(sci_ras, GetIP(), m_port); #endif } void Neighbor::SetH46018GkKeepAliveInterval(int interval) { if (m_keepAliveTimerInterval != interval) { m_keepAliveTimerInterval = interval; if (m_keepAliveTimer != GkTimerManager::INVALID_HANDLE) Toolkit::Instance()->GetTimerManager()->UnregisterTimer(m_keepAliveTimer); if (m_keepAliveTimerInterval > 0) { PTime now; m_keepAliveTimer = Toolkit::Instance()->GetTimerManager()->RegisterTimer( this, &Neighbor::SendH46018GkKeepAlive, now, m_keepAliveTimerInterval); // do it now and every n seconds } } } // initialize neighbor object created by SRV policy bool Neighbor::SetProfile(const PString & name, const H323TransportAddress & addr) { addr.GetIpAndPort(m_ip,m_port); m_id = "SRVrec"; m_name = name; m_dynamic = false; m_externalGK = true; m_sendPrefixes.clear(); m_sendPrefixes["*"] = 1; SetForwardedInfo(LRQFeaturesSection); return true; } PrefixInfo Neighbor::GetPrefixInfo(const H225_ArrayOf_AliasAddress & aliases, H225_ArrayOf_AliasAddress & dest) { Prefixes::iterator iter, biter = m_sendPrefixes.begin(), eiter = m_sendPrefixes.end(); for (PINDEX i = 0; i < aliases.GetSize(); ++i) { H225_AliasAddress & alias = aliases[i]; // send by alias type iter = m_sendPrefixes.find(alias.GetTagName()); if (iter != eiter) { dest.SetSize(1); dest[0] = alias; return PrefixInfo(100, (short)iter->second); } PString destination(AsString(alias, false)); // send by exact alias match for (PINDEX j = 0; j < m_sendAliases.GetSize(); j++) { if (destination == m_sendAliases[j]) { dest.SetSize(1); dest[0] = alias; return PrefixInfo(100, 1); } } // send by prefix while (iter != biter) { --iter; // search in reverse order const int len = MatchPrefix(destination, iter->first); if (len < 0) { return nomatch; } else if (len > 0) { dest.SetSize(1); dest[0] = alias; return PrefixInfo((short)len, (short)iter->second); } } } // send allways ? (handled last, treated as shortest match) iter = m_sendPrefixes.find("*"); if (iter == eiter) return nomatch; dest = aliases; return PrefixInfo(0, (short)iter->second); } PrefixInfo Neighbor::GetIPInfo(const H225_TransportAddress & ip, H225_ArrayOf_AliasAddress & dest) const { PStringArray sendNet(m_sendIPs.Tokenise(",")); for (PINDEX i=0; i < sendNet.GetSize(); ++i) { bool noMatch = false; if (sendNet[i].Left(1) == "!") { noMatch = true; sendNet[i] = sendNet[i].Mid(1); // cut away first char ("!") } NetworkAddress network = NetworkAddress(sendNet[i]); PIPSocket::Address addr; bool validNetwork = GetIPFromTransportAddr(ip, addr); if ((sendNet[i] == "*") || ((sendNet[i] == "public") && !addr.IsRFC1918()) || ((sendNet[i] == "private") && addr.IsRFC1918()) || (validNetwork && !noMatch && (NetworkAddress(addr) << network)) || (validNetwork && noMatch && !(NetworkAddress(addr) << network)) ) { dest.SetSize(1); H323SetAliasAddress(AsDotString(ip), dest[0], H225_AliasAddress::e_transportID); return PrefixInfo(100, 1); } } return nomatch; // don't send to this neighbor } bool Neighbor::OnSendingLRQ(H225_LocationRequest & lrq) { return true; } bool Neighbor::OnSendingLRQ(H225_LocationRequest & lrq, const AdmissionRequest &) { return OnSendingLRQ(lrq); } bool Neighbor::OnSendingLRQ(H225_LocationRequest & lrq, const LocationRequest & orig_lrq) { // adjust hopCount to be lesser or equal to the original value if (orig_lrq.GetRequest().HasOptionalField(H225_LocationRequest::e_hopCount)) { if (lrq.HasOptionalField(H225_LocationRequest::e_hopCount)) { if (lrq.m_hopCount > orig_lrq.GetRequest().m_hopCount) lrq.m_hopCount = orig_lrq.GetRequest().m_hopCount; } else { lrq.IncludeOptionalField(H225_LocationRequest::e_hopCount); lrq.m_hopCount = orig_lrq.GetRequest().m_hopCount; } } // copy over canMapAlias if (orig_lrq.GetRequest().HasOptionalField(H225_LocationRequest::e_canMapAlias)) { lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias); lrq.m_canMapAlias = orig_lrq.GetRequest().m_canMapAlias; } return OnSendingLRQ(lrq); } bool Neighbor::OnSendingLRQ(H225_LocationRequest & lrq, const SetupRequest &) { return OnSendingLRQ(lrq); } bool Neighbor::OnSendingLRQ(H225_LocationRequest & lrq, const FacilityRequest &) { return OnSendingLRQ(lrq); } bool Neighbor::CheckReply(RasMsg *ras) const { if( ras->IsFrom(GetIP(), 0) ) return true; const H225_NonStandardParameter *param = ras->GetNonStandardParam(); if (param == NULL) return false; int iec = Toolkit::iecUnknown; if (param->m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard) { iec = Toolkit::Instance()->GetInternalExtensionCode((const H225_H221NonStandard&)param->m_nonStandardIdentifier); } else if (param->m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_object) { const PASN_ObjectId &oid = param->m_nonStandardIdentifier; if (oid.GetDataLength() == 0) iec = Toolkit::iecNeighborId; } return iec == Toolkit::iecNeighborId ? strncmp(m_id, param->m_data.AsString(), m_id.GetLength()) == 0 : false; } bool Neighbor::Authenticate(RasMsg *ras) const { if (m_password.IsEmpty()) { return true; // no password, no check needed } else { // check tokens H225_ArrayOf_CryptoH323Token tokens; // check LRQs and SCIs switch (ras->GetTag()) { case H225_RasMessage::e_locationRequest: { H225_LocationRequest & lrq = (*ras)->m_recvRAS; if (lrq.HasOptionalField(H225_LocationRequest::e_cryptoTokens)) tokens = lrq.m_cryptoTokens; } break; case H225_RasMessage::e_serviceControlIndication: { H225_ServiceControlIndication & sci = (*ras)->m_recvRAS; if (sci.HasOptionalField(H225_ServiceControlIndication::e_cryptoTokens)) tokens = sci.m_cryptoTokens; } break; default: break; } H235AuthSimpleMD5 authMD5; PBYTEArray dummy; authMD5.SetLocalId(m_authUser); authMD5.SetPassword(m_password); for (PINDEX i = 0 ; i < tokens.GetSize(); i++) { H225_CryptoH323Token cryptoToken; if (tokens[i].GetTag() == H225_CryptoH323Token::e_cryptoGKPwdHash) { // convert the GKPwdHash into an EPPwdHash that H323Plus is able to check H225_CryptoH323Token_cryptoGKPwdHash & cryptoGKPwdHash = tokens[i]; cryptoToken.SetTag(H225_CryptoH323Token::e_cryptoEPPwdHash); H225_CryptoH323Token_cryptoEPPwdHash & cryptoEPPwdHash = cryptoToken; H323SetAliasAddress(cryptoGKPwdHash.m_gatekeeperId, cryptoEPPwdHash.m_alias); cryptoEPPwdHash.m_timeStamp = cryptoGKPwdHash.m_timeStamp; cryptoEPPwdHash.m_token.m_algorithmOID = OID_MD5; cryptoEPPwdHash.m_token.m_hash = cryptoGKPwdHash.m_token.m_hash; } else { cryptoToken = tokens[i]; // use other tokens as they are } if (authMD5.ValidateCryptoToken(cryptoToken, dummy) == H235Authenticator::e_OK) { PTRACE(5, "Neighbor\tMD5 password match"); return true; } } PTRACE(1, "Neighbor\tPassword required, but no match"); return false; // no token found that allows access } } bool Neighbor::IsAcceptable(RasMsg *ras) const { if (ras->IsFrom(GetIP(), 0)) { // ras must be an LRQ H225_LocationRequest & lrq = (*ras)->m_recvRAS; PINDEX i, j, sz = m_acceptPrefixes.GetSize(); H225_ArrayOf_AliasAddress & aliases = lrq.m_destinationInfo; for (j = 0; j < aliases.GetSize(); ++j) { H225_AliasAddress & alias = aliases[j]; for (i = 0; i < sz; ++i) { if (m_acceptPrefixes[i] == alias.GetTagName()) { return true; } } PString destination(AsString(alias, false)); int maxlen = 0; for (i = 0; i < sz; ++i) { const PString & prefix = m_acceptPrefixes[i]; const int len = MatchPrefix(destination, prefix); if (len < 0) return false; else if (len > maxlen) maxlen = len; } if (maxlen > 0) return true; } for (i = 0; i < sz; ++i) { if (m_acceptPrefixes[i] == "*") { return true; } } } return false; } void Neighbor::SetForwardedInfo(const PString & section) { PConfig *config = GkConfig(); m_forwardHopCount = (WORD)config->GetInteger(section, "ForwardHopCount", 0); m_acceptForwarded = Toolkit::AsBool(config->GetString(section, "AcceptForwardedLRQ", "1")); m_forwardResponse = Toolkit::AsBool(config->GetString(section, "ForwardResponse", "0")); PString forwardto(config->GetString(section, "ForwardLRQ", "0")); if (forwardto *= "never") m_forwardto = -1; else if (forwardto *= "always") m_forwardto = 1; else m_forwardto = 0; } // class OldGK bool OldGK::SetProfile(const PString & id, const PString & args) { m_authUser = m_id = m_gkid = id; PStringArray cfg(args.Tokenise(";", true)); m_name = cfg[0].Trim(); m_sendPrefixes.clear(); if (cfg.GetSize() > 1) { PStringArray p = cfg[1].Tokenise(",", false); for (PINDEX i = 0; i < p.GetSize(); ++i) m_sendPrefixes[p[i]] = 1; } else m_sendPrefixes["*"] = 1; m_acceptPrefixes.SetSize(1); m_acceptPrefixes[0] = "*"; if (cfg.GetSize() > 2) m_password = cfg[2]; m_dynamic = (cfg.GetSize() > 3) ? Toolkit::AsBool(cfg[3]) : false; if (!m_dynamic && !GetTransportAddress(m_name, GK_DEF_UNICAST_RAS_PORT, m_ip, m_port)) return false; SetForwardedInfo(LRQFeaturesSection); if (Toolkit::AsBool(GkConfig()->GetString(LRQFeaturesSection, "AlwaysForwardLRQ", "0"))) m_forwardto = 1; PTRACE(1, "Set neighbor " << m_gkid << '(' << (m_dynamic ? m_name : AsString(m_ip, m_port)) << ')' << (cfg.GetSize() > 1 ? (" for prefix " + cfg[1]) : PString::Empty())); return true; } // class GnuGK bool GnuGK::OnSendingLRQ(H225_LocationRequest & lrq, const AdmissionRequest & request) { lrq.IncludeOptionalField(H225_LocationRequest::e_gatekeeperIdentifier); lrq.m_gatekeeperIdentifier = Toolkit::GKName(); lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData); lrq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard &t35 = lrq.m_nonStandardData.m_nonStandardIdentifier; t35.m_t35CountryCode = Toolkit::t35cPoland; t35.m_manufacturerCode = Toolkit::t35mGnuGk; t35.m_t35Extension = Toolkit::t35eNeighborId; lrq.m_nonStandardData.m_data.SetValue(m_id); const H225_AdmissionRequest & arq = request.GetRequest(); lrq.IncludeOptionalField(H225_LocationRequest::e_sourceInfo); lrq.m_sourceInfo = arq.m_srcInfo; if (arq.HasOptionalField(H225_AdmissionRequest::e_canMapAlias)) { lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias); lrq.m_canMapAlias = arq.m_canMapAlias; } // must include callID to traversal servers and clients, no harm to always include it if (arq.HasOptionalField(H225_AdmissionRequest::e_callIdentifier)) { lrq.IncludeOptionalField(H225_LocationRequest::e_callIdentifier); lrq.m_callIdentifier = arq.m_callIdentifier; } #ifdef HAS_H46023 /// STD24 NAT Support if (Toolkit::Instance()->IsH46023Enabled()) { H460_FeatureStd std24 = H460_FeatureStd(24); int sz = lrq.m_genericData.GetSize(); lrq.m_genericData.SetSize(sz+1); lrq.m_genericData[sz] = std24; } #endif #ifdef HAS_H460VEN /// OID9 'Remote endpoint vendor info THIS IS "1.3.6.1.4.1.17090.0.9" NOT H.460.9 - SH H460_FeatureOID foid9 = H460_FeatureOID(OID9); int sz = lrq.m_genericData.GetSize(); lrq.m_genericData.SetSize(sz+1); lrq.m_genericData[sz] = foid9; #endif if (lrq.m_genericData.GetSize() > 0) lrq.IncludeOptionalField(H225_LocationRequest::e_genericData); return true; } bool GnuGK::OnSendingLRQ(H225_LocationRequest & lrq, const SetupRequest & request) { lrq.IncludeOptionalField(H225_LocationRequest::e_gatekeeperIdentifier); lrq.m_gatekeeperIdentifier = Toolkit::GKName(); lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData); lrq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard &t35 = lrq.m_nonStandardData.m_nonStandardIdentifier; t35.m_t35CountryCode = Toolkit::t35cPoland; t35.m_manufacturerCode = Toolkit::t35mGnuGk; t35.m_t35Extension = Toolkit::t35eNeighborId; lrq.m_nonStandardData.m_data.SetValue(m_id); const H225_Setup_UUIE & setup = request.GetRequest(); if (setup.HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) { lrq.IncludeOptionalField(H225_LocationRequest::e_sourceInfo); lrq.m_sourceInfo = setup.m_sourceAddress; } lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias); lrq.m_canMapAlias = TRUE; // must include callID to traversal servers and clients, no harm to always include it if (setup.HasOptionalField(H225_Setup_UUIE::e_callIdentifier)) { lrq.IncludeOptionalField(H225_LocationRequest::e_callIdentifier); lrq.m_callIdentifier = setup.m_callIdentifier; } return true; } bool GnuGK::OnSendingLRQ(H225_LocationRequest & lrq, const FacilityRequest & /*request*/) { lrq.IncludeOptionalField(H225_LocationRequest::e_gatekeeperIdentifier); lrq.m_gatekeeperIdentifier = Toolkit::GKName(); lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData); lrq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard &t35 = lrq.m_nonStandardData.m_nonStandardIdentifier; t35.m_t35CountryCode = Toolkit::t35cPoland; t35.m_manufacturerCode = Toolkit::t35mGnuGk; t35.m_t35Extension = Toolkit::t35eNeighborId; lrq.m_nonStandardData.m_data.SetValue(m_id); lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias); lrq.m_canMapAlias = TRUE; return true; } bool GnuGK::IsAcceptable(RasMsg *ras) const { if (Neighbor::IsAcceptable(ras)) { if (!m_acceptForwarded) { H225_LocationRequest & lrq = (*ras)->m_recvRAS; return lrq.HasOptionalField(H225_LocationRequest::e_gatekeeperIdentifier) && lrq.m_gatekeeperIdentifier.GetValue() == m_gkid; } return true; } return false; } // class CiscoGK bool CiscoGK::OnSendingLRQ(H225_LocationRequest &lrq, const AdmissionRequest &req) { const H225_AdmissionRequest &arq = req.GetRequest(); Cisco_LRQnonStandardInfo nonStandardData; nonStandardData.m_ttl = m_forwardHopCount >= 1 ? m_forwardHopCount : 5; nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_gatewaySrcInfo); nonStandardData.m_gatewaySrcInfo.SetSize(arq.m_srcInfo.GetSize()); for (PINDEX i = 0; i < arq.m_srcInfo.GetSize(); i++) nonStandardData.m_gatewaySrcInfo[i] = arq.m_srcInfo[i]; if (arq.HasOptionalField(H225_AdmissionRequest::e_callIdentifier)) { nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_callIdentifier); nonStandardData.m_callIdentifier = arq.m_callIdentifier; } // Cisco GK needs these lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData); lrq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard & h221 = lrq.m_nonStandardData.m_nonStandardIdentifier; h221.m_manufacturerCode = Toolkit::t35mCisco; h221.m_t35CountryCode = Toolkit::t35cUSA; h221.m_t35Extension = 0; PPER_Stream buff; nonStandardData.Encode(buff); buff.CompleteEncoding(); lrq.m_nonStandardData.m_data = buff; lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias); lrq.m_canMapAlias = TRUE; return true; } // class CiscoGK bool CiscoGK::OnSendingLRQ(H225_LocationRequest &lrq, const LocationRequest & /*req*/) { if (lrq.HasOptionalField(H225_LocationRequest::e_nonStandardData) && lrq.m_nonStandardData.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard) { const H225_H221NonStandard &h221 = lrq.m_nonStandardData.m_nonStandardIdentifier; if (h221.m_manufacturerCode == Toolkit::t35mCisco && h221.m_t35CountryCode == Toolkit::t35cUSA) return true; lrq.RemoveOptionalField(H225_LocationRequest::e_nonStandardData); } Cisco_LRQnonStandardInfo nonStandardData; nonStandardData.m_ttl = m_forwardHopCount >= 1 ? m_forwardHopCount : 5; if (lrq.HasOptionalField(H225_LocationRequest::e_sourceInfo)) { nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_gatewaySrcInfo); nonStandardData.m_gatewaySrcInfo.SetSize(lrq.m_sourceInfo.GetSize()); for (PINDEX i = 0; i < lrq.m_sourceInfo.GetSize(); i++) nonStandardData.m_gatewaySrcInfo[i] = lrq.m_sourceInfo[i]; } lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData); lrq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard & h221 = lrq.m_nonStandardData.m_nonStandardIdentifier; h221.m_manufacturerCode = Toolkit::t35mCisco; h221.m_t35CountryCode = Toolkit::t35cUSA; h221.m_t35Extension = 0; PPER_Stream buff; nonStandardData.Encode(buff); buff.CompleteEncoding(); lrq.m_nonStandardData.m_data = buff; lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias); lrq.m_canMapAlias = TRUE; return true; } // class CiscoGK bool CiscoGK::OnSendingLRQ(H225_LocationRequest &lrq, const SetupRequest &req) { const Q931 &setup = req.GetWrapper()->GetQ931(); const H225_Setup_UUIE &setupBody = req.GetRequest(); Cisco_LRQnonStandardInfo nonStandardData; nonStandardData.m_ttl = m_forwardHopCount >= 1 ? m_forwardHopCount : 5; if (setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) { nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_gatewaySrcInfo); nonStandardData.m_gatewaySrcInfo.SetSize(setupBody.m_sourceAddress.GetSize()); for (PINDEX i = 0; i < setupBody.m_sourceAddress.GetSize(); i++) nonStandardData.m_gatewaySrcInfo[i] = setupBody.m_sourceAddress[i]; } if (setupBody.HasOptionalField(H225_Setup_UUIE::e_callIdentifier)) { nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_callIdentifier); nonStandardData.m_callIdentifier = setupBody.m_callIdentifier; } if (setup.HasIE(Q931::CallingPartyNumberIE)) { PBYTEArray data = setup.GetIE(Q931::CallingPartyNumberIE); if ((data[0] & 0x80) == 0x80 && data.GetSize() >= 2) { nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_callingOctet3a); nonStandardData.m_callingOctet3a = data[1]; } } // Cisco GK needs these lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData); lrq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard & h221 = lrq.m_nonStandardData.m_nonStandardIdentifier; h221.m_manufacturerCode = Toolkit::t35mCisco; h221.m_t35CountryCode = Toolkit::t35cUSA; h221.m_t35Extension = 0; PPER_Stream buff; nonStandardData.Encode(buff); buff.CompleteEncoding(); lrq.m_nonStandardData.m_data = buff; lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias); lrq.m_canMapAlias = TRUE; return true; } bool CiscoGK::CheckReply(RasMsg *msg) const { if (msg->IsFrom(GetIP(), 0)) return true; if (msg->GetTag() != H225_RasMessage::e_locationConfirm && msg->GetTag() != H225_RasMessage::e_locationReject) return false; H225_NonStandardParameter *nonStandardData = msg->GetNonStandardParam(); if (nonStandardData == NULL) return false; if (nonStandardData->m_nonStandardIdentifier.GetTag() != H225_NonStandardIdentifier::e_h221NonStandard) return false; H225_H221NonStandard & h221 = nonStandardData->m_nonStandardIdentifier; if (h221.m_manufacturerCode != Toolkit::t35mCisco || h221.m_t35CountryCode != Toolkit::t35cUSA) return false; PPER_Stream strm(nonStandardData->m_data); Cisco_LRQnonStandardInfo ciscoNonStandardData; if (ciscoNonStandardData.Decode(strm)) { // here should go additional checks to match callIdentifier, for example } else { PTRACE(5, "NB\tFailed to decode Cisco nonStandardInfo field"); } return true; } // class ClarentGK bool ClarentGK::OnSendingLRQ(H225_LocationRequest & lrq) { // Clarent gatekeeper can't decode nonStandardData, stupid! lrq.RemoveOptionalField(H225_LocationRequest::e_nonStandardData); return true; } // class GlonetGK bool GlonetGK::OnSendingLRQ(H225_LocationRequest & lrq, const AdmissionRequest & request) { return BuildLRQ(lrq, (WORD)request.GetRequest().m_callReferenceValue); } bool GlonetGK::OnSendingLRQ(H225_LocationRequest &, const LocationRequest &) { // not supported, since LRQ doesn't have call reference value return false; } bool GlonetGK::OnSendingLRQ(H225_LocationRequest & lrq, const SetupRequest & request) { return BuildLRQ(lrq, (WORD)request.GetWrapper()->GetCallReference()); } bool GlonetGK::OnSendingLRQ(H225_LocationRequest & lrq, const FacilityRequest & request) { return BuildLRQ(lrq, (WORD)request.GetWrapper()->GetCallReference()); } bool GlonetGK::BuildLRQ(H225_LocationRequest & lrq, WORD crv) { lrq.IncludeOptionalField(H225_LocationRequest::e_sourceInfo); lrq.m_sourceInfo.SetSize(2); H323SetAliasAddress(Toolkit::GKName(), lrq.m_sourceInfo[0], H225_AliasAddress::e_h323_ID); H323SetAliasAddress(PString(crv), lrq.m_sourceInfo[1]); return true; } // class LRQRequester class LRQRequester : public RasRequester { public: LRQRequester(const LRQFunctor &); virtual ~LRQRequester(); bool Send(NeighborList::List &, Neighbor * = 0); bool Send(Neighbor * nb); int GetReqNumber() const { return m_requests.size(); } H225_LocationConfirm *WaitForDestination(int); PString GetNeighborUsed() const { return m_neighbor_used; } bool IsTraversalClient() const { return m_h46018_client; } bool IsTraversalServer() const { return m_h46018_server; } bool IsTraversalZone() const { return m_h46018_client || m_h46018_server; } bool IsH46024Supported() const; bool UseTLS() const { return m_useTLS; } // override from class RasRequester virtual bool IsExpected(const RasMsg *) const; virtual void Process(RasMsg *); virtual bool OnTimeout(); private: struct Request { Request(Neighbor *n) : m_neighbor(n), m_reply(0), m_count(1) {} Neighbor *m_neighbor; RasMsg *m_reply; int m_count; }; typedef multimap Queue; Queue m_requests; PMutex m_rmutex; const LRQFunctor & m_sendto; RasMsg *m_result; PString m_neighbor_used; bool m_h46018_client, m_h46018_server; bool m_useTLS; }; LRQRequester::LRQRequester(const LRQFunctor & fun) : m_sendto(fun), m_result(0), m_h46018_client(false), m_h46018_server(false), m_useTLS(false) { AddFilter(H225_RasMessage::e_locationConfirm); AddFilter(H225_RasMessage::e_locationReject); m_rasSrv->RegisterHandler(this); } LRQRequester::~LRQRequester() { m_rasSrv->UnregisterHandler(this); } bool LRQRequester::Send(NeighborList::List & neighbors, Neighbor *requester) { PWaitAndSignal lock(m_rmutex); NeighborList::List::iterator iter = neighbors.begin(); while (iter != neighbors.end()) { Neighbor *nb = *iter++; if (nb != requester) { if (PrefixInfo info = m_sendto(nb, m_seqNum)) { m_requests.insert(make_pair(info, nb)); } } } if (m_requests.empty()) return false; m_retry = GkConfig()->GetInteger(LRQFeaturesSection, "SendRetries", 2); PTRACE(2, "NB\t" << m_requests.size() << " LRQ(s) sent"); return true; } bool LRQRequester::Send(Neighbor * nb) { PWaitAndSignal lock(m_rmutex); if (PrefixInfo info = m_sendto(nb, m_seqNum)) m_requests.insert(make_pair(info, nb)); if (m_requests.empty()) { PTRACE(2, "SRV\tError sending LRQ to " << nb->GetIP()); SNMP_TRAP(11, SNMPError, Network, "Error sending LRQ to " + AsString(nb->GetIP())); return false; } m_retry = GkConfig()->GetInteger(LRQFeaturesSection, "SendRetries", 2); PTRACE(2, "SRV\tLRQ sent to " << nb->GetIP()); return true; } H225_LocationConfirm * LRQRequester::WaitForDestination(int timeout) { while (WaitForResponse(timeout)) { if (m_result) { break; } else { GetReply(); // ignore and increase iterator } } return m_result ? &(H225_LocationConfirm &)(*m_result)->m_recvRAS : NULL; } bool LRQRequester::IsH46024Supported() const { if (!m_result) return false; const H225_LocationConfirm *lcf = &(H225_LocationConfirm &)(*m_result)->m_recvRAS; if (lcf && lcf->HasOptionalField(H225_LocationConfirm::e_genericData)) { H460_FeatureSet fs = H460_FeatureSet(lcf->m_genericData); if (fs.HasFeature(24)) return true; } return false; } bool LRQRequester::IsExpected(const RasMsg * ras) const { return RasHandler::IsExpected(ras) && (ras->GetSeqNum() == m_seqNum); } void LRQRequester::Process(RasMsg * ras) { PWaitAndSignal lock(m_rmutex); for (Queue::iterator iter = m_requests.begin(); iter != m_requests.end(); ++iter) { Request & req = iter->second; if (req.m_neighbor->CheckReply(ras) || Toolkit::AsBool(GkConfig()->GetString(LRQFeaturesSection, "AcceptNonNeighborLCF", "0"))) { PTRACE(5, "NB\tReceived " << ras->GetTagName() << " message matched" << " pending LRQ for neighbor " << req.m_neighbor->GetId() << ':' << req.m_neighbor->GetIP() ); unsigned tag = ras->GetTag(); if (tag == H225_RasMessage::e_requestInProgress) { // TODO: honor the delay specified in the RIP ? if (H225_NonStandardParameter *param = ras->GetNonStandardParam()) { int iec = Toolkit::iecUnknown; if (param->m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard) { iec = Toolkit::Instance()->GetInternalExtensionCode((const H225_H221NonStandard&)param->m_nonStandardIdentifier); } else if (param->m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_object) { PASN_ObjectId &oid = param->m_nonStandardIdentifier; if (oid.GetDataLength() == 0) iec = Toolkit::iecNeighborId; } if (iec == Toolkit::iecNeighborId) { PStringArray ttl(param->m_data.AsString().Tokenise(":", false)); if (ttl.GetSize() > 1) req.m_count += ttl[1].AsInteger(); } } RasRequester::Process(ras); } else if (tag == H225_RasMessage::e_locationConfirm) { --req.m_count; // Note: to avoid race condition, the order is important if (iter == m_requests.begin()) // the highest priority m_result = ras; AddReply(req.m_reply = ras); m_neighbor_used = req.m_neighbor->GetId(); // record neighbor used #ifdef HAS_TLS m_useTLS = req.m_neighbor->UseTLS(); #endif m_h46018_client = req.m_neighbor->IsH46018Client(); m_h46018_server = req.m_neighbor->IsH46018Server(); if (m_h46018_server) { // if we are traversal server we must use the apparent RAS IP of the client H225_LocationConfirm & lcf = (*ras)->m_recvRAS; lcf.m_rasAddress = SocketToH225TransportAddr(req.m_neighbor->GetIP(), req.m_neighbor->GetPort()); } if (m_result) m_sync.Signal(); } else { // should be H225_RasMessage::e_locationReject --req.m_count; delete ras; ras = NULL; if (req.m_count <= 0 && req.m_reply == 0) { PTRACE(5, "NB\tLRQ rejected for neighbor " << req.m_neighbor->GetId() << ':' << req.m_neighbor->GetIP() ); m_requests.erase(iter); if (m_requests.empty()) RasRequester::Stop(); else if (RasMsg *reply = m_requests.begin()->second.m_reply) m_result = reply, RasRequester::Stop(); } } return; } } PTRACE(1, "RAS\tUnknown reply " << ras->GetTagName()); delete ras; } bool LRQRequester::OnTimeout() { PWaitAndSignal lock(m_rmutex); if (m_requests.empty()) return false; Queue::iterator iter, biter = m_requests.begin(), eiter = m_requests.end(); for (iter = biter; iter != eiter; ++iter) { m_result = iter->second.m_reply; if (m_result) return false; } if (m_retry-- == 0) return false; // re-send LRQs for (iter = biter; iter != eiter; ++iter) { m_sendto(iter->second.m_neighbor, m_seqNum); iter->second.m_count = 1; // reset count } m_sentTime = PTime(); PTRACE(2, "NB\t" << m_requests.size() << " LRQ(s) re-sent"); return true; } // class NeighborList NeighborList::NeighborList() { Factory::SetDefaultCreator(&OldGKCreator); // OnReload is called by holder } NeighborList::~NeighborList() { DeleteObjectsInContainer(m_neighbors); } void NeighborList::OnReload() { challenge = rand(); PStringToString cfgs(GkConfig()->GetAllKeyValues(NeighborSection)); PINDEX i, sz = cfgs.GetSize(); List::iterator iter = m_neighbors.begin(); while (iter != m_neighbors.end()) { for (i = 0; i < sz; ++i) if ((*iter)->GetId() == cfgs.GetKeyAt(i)) break; if (i == sz) { Neighbor * r = *iter; iter = m_neighbors.erase(iter); delete r; r = NULL; } else ++iter; } for (i = 0; i < sz; ++i) { const PString & nbid = cfgs.GetKeyAt(i); PString type = cfgs.GetDataAt(i); // make neighbor type caseless if (PCaselessString(type) == "Generic") type = "GnuGK"; if (PCaselessString(type) == "GnuGK") type = "GnuGK"; if (PCaselessString(type) == "CiscoGK") type = "CiscoGK"; if (PCaselessString(type) == "ClarentGK") type = "ClarentGK"; if (PCaselessString(type) == "GlonetGK") type = "GlonetGK"; iter = find_if(m_neighbors.begin(), m_neighbors.end(), compose1(bind2nd(equal_to(), nbid), mem_fun(&Neighbor::GetId))); bool newnb = (iter == m_neighbors.end()); Neighbor *nb = newnb ? Factory::Create(type) : *iter; if (nb && nb->SetProfile(nbid, type)) { if (newnb) m_neighbors.push_back(nb); } else { PTRACE(1, "NB\tCan't get profile for neighbor " << nbid); delete nb; nb = NULL; if (!newnb) m_neighbors.erase(iter); } } } bool NeighborList::CheckLRQ(RasMsg *ras) const { List::const_iterator iter = m_neighbors.begin(); while (iter != m_neighbors.end()) { if ((*iter)->IsAcceptable(ras) && (*iter)->Authenticate(ras)) return true; ++iter; } return false; } bool NeighborList::CheckIP(const PIPSocket::Address & addr) const { return find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsFrom), &addr)) != m_neighbors.end(); } bool NeighborList::IsTraversalClient(const PIPSocket::Address & addr) const { return find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsTraversalClient), &addr)) != m_neighbors.end(); } bool NeighborList::IsTraversalServer(const PIPSocket::Address & addr) const { return find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsTraversalServer), &addr)) != m_neighbors.end(); } PString NeighborList::GetNeighborIdBySigAdr(const H225_TransportAddress & sigAd) { PIPSocket::Address ipaddr; // Get the Neigbor IP address from the transport address if (!GetIPFromTransportAddr(sigAd, ipaddr)) { return PString::Empty(); } return GetNeighborIdBySigAdr(ipaddr); } PString NeighborList::GetNeighborIdBySigAdr(const PIPSocket::Address & sigAd) { // Attempt to find the neigbor in the list List::iterator findNeighbor = find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsFrom), &sigAd)); if (findNeighbor == m_neighbors.end()) { return PString::Empty(); } return (*findNeighbor)->GetId(); } PString NeighborList::GetNeighborGkIdBySigAdr(const PIPSocket::Address & sigAd) { // Attempt to find the neigbor in the list List::iterator findNeighbor = find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsFrom), &sigAd)); if (findNeighbor == m_neighbors.end()) { return PString::Empty(); } return (*findNeighbor)->GetGkId(); } bool NeighborList::GetNeighborTLSBySigAdr(const PIPSocket::Address & sigAd) { // Attempt to find the neigbor in the list List::iterator findNeighbor = find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsFrom), &sigAd)); if (findNeighbor == m_neighbors.end()) { return false; } return (*findNeighbor)->UseTLS(); } PString NeighborList::GetNeighborGkIdBySigAdr(const H225_TransportAddress & sigAd) { PIPSocket::Address ipaddr; // Get the Neigbor IP address from the transport address if (!GetIPFromTransportAddr(sigAd, ipaddr)) { return PString::Empty(); } return GetNeighborGkIdBySigAdr(ipaddr); } bool NeighborList::GetNeighborTLSBySigAdr(const H225_TransportAddress & sigAd) { PIPSocket::Address ipaddr; // Get the Neigbor IP address from the transport address if (!GetIPFromTransportAddr(sigAd, ipaddr)) { return false; } return GetNeighborTLSBySigAdr(ipaddr); } /* Not used currently H225_CryptoH323Token BuildAccessToken(const H225_TransportAddress & dest, const PIPSocket::Address & addr) { H225_CryptoH323Token token; token.SetTag(H225_CryptoH323Token::e_nestedcryptoToken); H235_CryptoToken & nestedCryptoToken = token; nestedCryptoToken.SetTag(H235_CryptoToken::e_cryptoHashedToken); H235_CryptoToken_cryptoHashedToken & cryptoHashedToken = nestedCryptoToken; // "T" indicates that the hashed token is used for authentication and integrity cryptoHashedToken.m_tokenOID = OID_T; H235_ClearToken & clearToken = cryptoHashedToken.m_hashedVals; clearToken.IncludeOptionalField(H235_ClearToken::e_timeStamp); time_t timeStamp = time(0); clearToken.m_timeStamp = timeStamp; DWORD key = (DWORD)(addr ^ timeStamp ^ challenge); PTEACypher::Key cryptokey; memset(&cryptokey, challenge, sizeof(PTEACypher::Key)); memcpy(&cryptokey, &key, sizeof(DWORD)); PTEACypher cypher(cryptokey); PPER_Stream strm; dest.Encode(strm); PString hashed(cypher.Encode(strm)); cryptoHashedToken.m_token.m_hash.SetData(hashed.GetLength() * 8, hashed); return token; } */ bool DecodeAccessToken(const H225_CryptoH323Token & token, const PIPSocket::Address & addr, H225_TransportAddress & dest) { if (token.GetTag() != H225_CryptoH323Token::e_nestedcryptoToken) return false; const H235_CryptoToken & nestedCryptoToken = token; if (nestedCryptoToken.GetTag() != H235_CryptoToken::e_cryptoHashedToken) return false; const H235_CryptoToken_cryptoHashedToken & cryptoHashedToken = nestedCryptoToken; if (cryptoHashedToken.m_tokenOID.AsString() != OID_T) return false; const H235_ClearToken & clearToken = cryptoHashedToken.m_hashedVals; if (!clearToken.HasOptionalField(H235_ClearToken::e_timeStamp)) return false; time_t now = time(0), timeStamp = clearToken.m_timeStamp.GetValue(); if (timeStamp > now || (now - timeStamp) > 30) return false; const PASN_BitString & bitstring = cryptoHashedToken.m_token.m_hash; PString hashed((const char *)bitstring.GetDataPointer(), bitstring.GetSize() / 8); DWORD key = (DWORD)(addr ^ timeStamp ^ challenge); PTEACypher::Key cryptokey; memset(&cryptokey, challenge, sizeof(PTEACypher::Key)); memcpy(&cryptokey, &key, sizeof(DWORD)); PTEACypher cypher(cryptokey); PPER_Stream strm; return cypher.Decode(hashed, strm) && dest.Decode(strm); } } // end of namespace Neighbors namespace Routing { using namespace Neighbors; class NeighborPolicy : public Policy { public: NeighborPolicy(); virtual ~NeighborPolicy() {} private: // override from class Policy virtual bool IsActive() const; virtual bool OnRequest(AdmissionRequest &); virtual bool OnRequest(LocationRequest &); virtual bool OnRequest(SetupRequest &); virtual bool OnRequest(FacilityRequest &); typedef NeighborList::List List; List & m_neighbors; int m_neighborTimeout; }; NeighborPolicy::NeighborPolicy() : m_neighbors(*RasServer::Instance()->GetNeighbors()) { m_neighborTimeout = GkConfig()->GetInteger(LRQFeaturesSection, "NeighborTimeout", 5) * 1000; m_name = "Neighbor"; } bool NeighborPolicy::IsActive() const { return !m_neighbors.empty(); } template inline void CopyCryptoTokens(const H225_LocationConfirm *lcf, H2250 & msg) { // copy access tokens if (lcf->HasOptionalField(H225_LocationConfirm::e_cryptoTokens)) { msg.IncludeOptionalField(H2250::e_cryptoTokens); msg.m_cryptoTokens = lcf->m_cryptoTokens; } } bool NeighborPolicy::OnRequest(AdmissionRequest & arq_obj) { LRQSender functor(arq_obj); LRQRequester request(functor); if (request.Send(m_neighbors)) { if (H225_LocationConfirm *lcf = request.WaitForDestination(m_neighborTimeout)) { Route route(m_name, lcf->m_callSignalAddress); #if defined(HAS_H460) || defined (HAS_TLS) if ((request.UseTLS() || request.IsTraversalZone() || request.IsH46024Supported()) && RasServer::Instance()->IsGKRouted()) { // overwrite callSignalAddress and replace with ours, we must proxy this call endptr ep = RegistrationTable::Instance()->FindByEndpointId(arq_obj.GetRequest().m_endpointIdentifier); // should not be null if (ep) { PIPSocket::Address epip; if (GetIPFromTransportAddr(ep->GetCallSignalAddress(), epip)) route.m_destAddr = RasServer::Instance()->GetCallSignalAddress(epip); } // create an EPRec to remember the NAT settings for H.460.18 (traversal zone) or H.460.23/.24 (genericData) H225_RasMessage ras; ras.SetTag(H225_RasMessage::e_locationConfirm); H225_LocationConfirm & con = (H225_LocationConfirm &)ras; con = *lcf; route.m_destEndpoint = RegistrationTable::Instance()->InsertRec(ras); // set flag to use TLS if (request.UseTLS()) route.m_destEndpoint->SetUseTLS(true); // set flag to use H.460.18 if neighbor is traversal server if (request.IsTraversalClient()) { // if we are the client, then the call goes to a traversal server route.m_destEndpoint->SetTraversalRole(TraversalServer); } if (request.IsTraversalServer()) { route.m_destEndpoint->SetTraversalRole(TraversalClient); } } #endif route.m_routeId = request.GetNeighborUsed(); route.m_flags |= Route::e_toNeighbor; // check if we have to update the dialed alias (canMapAlias) if ((lcf->HasOptionalField(H225_LocationConfirm::e_destinationInfo)) && (lcf->m_destinationInfo.GetSize() > 0)) { // new alias from neighbor arq_obj.SetAliases(lcf->m_destinationInfo); arq_obj.SetFlag(Routing::AdmissionRequest::e_aliasesChanged); } else { if (arq_obj.GetAliases() == NULL && arq_obj.GetDestIP() != NULL) { // call dialed by IP H225_ArrayOf_AliasAddress newAliases; newAliases.SetSize(1); H323SetAliasAddress(AsDotString(*arq_obj.GetDestIP()), newAliases[0], H225_AliasAddress::e_transportID); arq_obj.SetAliases(newAliases); arq_obj.SetFlag(Routing::AdmissionRequest::e_aliasesChanged); } } arq_obj.AddRoute(route); RasMsg *ras = arq_obj.GetWrapper(); (*ras)->m_replyRAS.SetTag(H225_RasMessage::e_admissionConfirm); H225_AdmissionConfirm & acf = (*ras)->m_replyRAS; CopyCryptoTokens(lcf, acf); return true; } } return false; } bool NeighborPolicy::OnRequest(LocationRequest & lrq_obj) { RasMsg * ras = lrq_obj.GetWrapper(); List::iterator iter = find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsAcceptable), ras)); Neighbor * requester = (iter != m_neighbors.end()) ? *iter : NULL; int hopCount = 0; if (requester) { if (requester->ForwardLRQ() < 0) { return false; } else if (requester->ForwardLRQ() > 0) { hopCount = 1; } } H225_LocationRequest & lrq = (*ras)->m_recvRAS; if (lrq.HasOptionalField(H225_LocationRequest::e_hopCount)) { hopCount = lrq.m_hopCount - 1; if (hopCount) lrq.m_hopCount = hopCount; } if (!hopCount) return false; if (requester && !requester->ForwardResponse()) { LRQForwarder functor(lrq_obj); LRQRequester request(functor); if (request.Send(m_neighbors, requester)) { (*ras)->m_replyRAS.SetTag(H225_RasMessage::e_requestInProgress); H225_RequestInProgress & rip = (*ras)->m_replyRAS; rip.m_requestSeqNum = ras->GetSeqNum(); rip.m_delay = m_neighborTimeout; if (H225_NonStandardParameter *param = ras->GetNonStandardParam()) { int iec = Toolkit::iecUnknown; if (param->m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard) { iec = Toolkit::Instance()->GetInternalExtensionCode((const H225_H221NonStandard&)param->m_nonStandardIdentifier); } else if (param->m_nonStandardIdentifier.GetTag() == H225_NonStandardIdentifier::e_object) { PASN_ObjectId &oid = param->m_nonStandardIdentifier; if (oid.GetDataLength() == 0) iec = Toolkit::iecNeighborId; } if (iec == Toolkit::iecNeighborId) { PString data = param->m_data.AsString() + ":" + PString(request.GetReqNumber()); rip.IncludeOptionalField(H225_RequestInProgress::e_nonStandardData); rip.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); H225_H221NonStandard &t35 = rip.m_nonStandardData.m_nonStandardIdentifier; t35.m_t35CountryCode = Toolkit::t35cPoland; t35.m_manufacturerCode = Toolkit::t35mGnuGk; t35.m_t35Extension = Toolkit::t35eNeighborId; rip.m_nonStandardData.m_data.SetValue(data); } } return true; } } else { LRQSender functor(lrq_obj); LRQRequester request(functor); if (request.Send(m_neighbors, requester)) { if (H225_LocationConfirm *lcf = request.WaitForDestination(m_neighborTimeout)) { Route route(m_name, lcf->m_callSignalAddress); #if defined(HAS_H460) || defined (HAS_TLS) if (lcf->HasOptionalField(H225_LocationConfirm::e_genericData) || request.IsTraversalZone() || request.UseTLS()) { // create an EPRec to remember the NAT settings for H.460.18 (traversal zone) or H.460.23/.24 (genericData) H225_RasMessage ras; ras.SetTag(H225_RasMessage::e_locationConfirm); H225_LocationConfirm & con = (H225_LocationConfirm &)ras; con = *lcf; route.m_destEndpoint = RegistrationTable::Instance()->InsertRec(ras); // set flag to use TLS if (request.UseTLS()) route.m_destEndpoint->SetUseTLS(true); // set flag to use H.460.18 if neighbor is traversal server if (request.IsTraversalClient()) { // if we are the client, then the call goes to a traversal server route.m_destEndpoint->SetTraversalRole(TraversalServer); } if (request.IsTraversalServer()) { route.m_destEndpoint->SetTraversalRole(TraversalClient); } } #endif route.m_routeId = request.GetNeighborUsed(); route.m_flags |= Route::e_toNeighbor; lrq_obj.AddRoute(route); (*ras)->m_replyRAS.SetTag(H225_RasMessage::e_locationConfirm); // canMapAlias: copy new destination if changed if (lrq_obj.GetRequest().HasOptionalField(H225_LocationRequest::e_canMapAlias) && lrq_obj.GetRequest().m_canMapAlias && lcf->HasOptionalField(H225_LocationConfirm::e_destinationInfo) && (lcf->m_destinationInfo.GetSize() > 0) && (lrq_obj.GetRequest().m_destinationInfo != lcf->m_destinationInfo)) { lrq_obj.GetRequest().m_destinationInfo = lcf->m_destinationInfo; lrq_obj.SetFlag(RoutingRequest::e_aliasesChanged); } H225_LocationConfirm & nlcf = (*ras)->m_replyRAS; CopyCryptoTokens(lcf, nlcf); return true; } } } return false; } bool NeighborPolicy::OnRequest(SetupRequest & setup_obj) { LRQSender functor(setup_obj); LRQRequester request(functor); if (request.Send(m_neighbors)) { if (H225_LocationConfirm *lcf = request.WaitForDestination(m_neighborTimeout)) { Route route(m_name, lcf->m_callSignalAddress); #if defined(HAS_H460) || defined (HAS_TLS) if (request.UseTLS() || request.IsTraversalZone() || request.IsH46024Supported()) { // create an EPRec to remember the NAT settings for H.460.18 (traversal zone) or H.460.23/.24 (genericData) H225_RasMessage ras; ras.SetTag(H225_RasMessage::e_locationConfirm); H225_LocationConfirm & con = (H225_LocationConfirm &)ras; con = *lcf; route.m_destEndpoint = RegistrationTable::Instance()->InsertRec(ras); // set flag to use TLS if (request.UseTLS()) route.m_destEndpoint->SetUseTLS(true); // set flag to use H.460.18 if neighbor is traversal server if (request.IsTraversalClient()) { // if we are the client, then the call goes to a traversal server route.m_destEndpoint->SetTraversalRole(TraversalServer); } if (request.IsTraversalServer()) { route.m_destEndpoint->SetTraversalRole(TraversalClient); } } #endif route.m_routeId = request.GetNeighborUsed(); route.m_flags |= Route::e_toNeighbor; setup_obj.AddRoute(route); CopyCryptoTokens(lcf, setup_obj.GetRequest()); // canMapAlias: adjust new destination if (lcf->HasOptionalField(H225_LocationConfirm::e_destinationInfo) && (lcf->m_destinationInfo.GetSize() > 0) && (setup_obj.GetRequest().m_destinationAddress != lcf->m_destinationInfo)) { setup_obj.GetRequest().m_destinationAddress = lcf->m_destinationInfo; setup_obj.SetFlag(RoutingRequest::e_aliasesChanged); } return true; } } return false; } bool NeighborPolicy::OnRequest(FacilityRequest & facility_obj) { LRQSender functor(facility_obj); LRQRequester request(functor); if (request.Send(m_neighbors)) { if (H225_LocationConfirm *lcf = request.WaitForDestination(m_neighborTimeout)) { Route route(m_name, lcf->m_callSignalAddress); #if defined(HAS_H460) || defined (HAS_TLS) if (request.UseTLS() || request.IsTraversalZone() || request.IsH46024Supported()) { // create an EPRec to remember the NAT settings for H.460.18 (traversal zone) or H.460.23/.24 (genericData) H225_RasMessage ras; ras.SetTag(H225_RasMessage::e_locationConfirm); H225_LocationConfirm & con = (H225_LocationConfirm &)ras; con = *lcf; route.m_destEndpoint = RegistrationTable::Instance()->InsertRec(ras); // set flag to use TLS if (request.UseTLS()) route.m_destEndpoint->SetUseTLS(true); // set flag to use H.460.18 if neighbor is traversal server if (request.IsTraversalClient()) { // if we are the client, then the call goes to a traversal server route.m_destEndpoint->SetTraversalRole(TraversalServer); } if (request.IsTraversalServer()) { route.m_destEndpoint->SetTraversalRole(TraversalClient); } } #endif route.m_routeId = request.GetNeighborUsed(); route.m_flags |= Route::e_toNeighbor; facility_obj.AddRoute(route); CopyCryptoTokens(lcf, facility_obj.GetRequest()); return true; } } return false; } #ifdef hasSRV class SRVPolicy : public AliasesPolicy { public: SRVPolicy(); protected: virtual bool OnRequest(FacilityRequest &) { return false; } virtual bool FindByAliases(RoutingRequest &, H225_ArrayOf_AliasAddress &); virtual bool FindByAliases(LocationRequest &, H225_ArrayOf_AliasAddress &); virtual Route * CSLookup(H225_ArrayOf_AliasAddress & aliases, bool localonly, const PString & schema, WORD schemaPort, const PString & gateway, PBoolean & changed); virtual Route * LSLookup(RoutingRequest & request, H225_ArrayOf_AliasAddress & aliases, bool localonly, const PString & schema); virtual void LoadConfig(const PString & instance); bool m_resolveNonLocalLRQs; PStringToString m_ls_schema; PStringToString m_cs_schema; }; SRVPolicy::SRVPolicy() { m_name = "SRV"; m_iniSection = "Routing::SRV"; m_resolveNonLocalLRQs = Toolkit::AsBool(GkConfig()->GetString(m_iniSection, "ResolveNonLocalLRQ", "0")); m_ls_schema.SetAt("_h323ls._udp.", ""); m_cs_schema.SetAt("_h323cs._tcp.", ""); } void SRVPolicy::LoadConfig(const PString & instance) { if (instance.IsEmpty()) return; m_ls_schema.RemoveAll(); m_cs_schema.RemoveAll(); m_cs_schema = GkConfig()->GetAllKeyValues(m_iniSection); } Route * SRVPolicy::LSLookup(RoutingRequest & request, H225_ArrayOf_AliasAddress & aliases, bool localonly, const PString & schema) { for (PINDEX i = 0; i < aliases.GetSize(); ++i) { // only apply to urlID and h323ID if ((aliases[i].GetTag() != H225_AliasAddress::e_url_ID) && (aliases[i].GetTag() != H225_AliasAddress::e_h323_ID)) continue; PString alias(AsString(aliases[i], FALSE)); PINDEX at = alias.Find('@'); // skip empty aliases or those without at-sign if ((alias.GetLength() == 0) || (at == P_MAX_INDEX)) continue; // DNS SRV Record lookup PString number = "h323:" + alias; PString domain = alias.Mid(at+1); PString localalias = alias.Left(at); if (IsIPAddress(domain) || (domain.FindRegEx(PRegularExpression(":[0-9]+$", PRegularExpression::Extended)) != P_MAX_INDEX)) continue; // don't use SRV record if domain part is IP or has port (Annex O, O.9), let dns policy handle them // LS Record lookup PStringList ls; if (PDNS::LookupSRV(number, schema, ls)) { for (PINDEX i=0; i < ls.GetSize(); i++) { PINDEX at = ls[i].Find('@'); PString ipaddr = ls[i].Mid(at + 1); if (ipaddr.Left(7) == "0.0.0.0") { PTRACE(1, "ROUTING\tERROR in LS SRV lookup (" << ls[i] << ")"); SNMP_TRAP(11, SNMPError, Network, "SRV LS lookup failed: " + ls[i]); continue; } PTRACE(4, "ROUTING\tSRV LS located domain " << domain << " at " << ipaddr); H323TransportAddress addr = H323TransportAddress(ipaddr); PIPSocket::Address socketip; WORD port; if (!GetTransportAddress(ipaddr, GK_DEF_UNICAST_RAS_PORT, socketip, port) && socketip.IsValid()) { PTRACE(1, "ROUTING\tERROR in SRV LS IP " << ipaddr); SNMP_TRAP(11, SNMPError, Network, "SRV LS lookup failed: " + ipaddr); continue; } if (Toolkit::Instance()->IsGKHome(socketip)) { // this is my domain, no need to send LRQs, just look into the endpoint table PINDEX numberat = number.Find('@'); // always has an @ H225_ArrayOf_AliasAddress find_aliases; find_aliases.SetSize(1); PString local_alias = number.Mid(5,numberat-5); H323SetAliasAddress(local_alias, find_aliases[0]); endptr ep = RegistrationTable::Instance()->FindByAliases(find_aliases); if (ep) { // endpoint found locally Route * route = new Route("srv", ep); return route; } else { return NULL; } } else if (!localonly) { // Create a SRV gatekeeper object GnuGK * nb = new GnuGK(); if (!nb->SetProfile(domain, addr)) { PTRACE(2, "ROUTING\tERROR setting SRV neighbor profile " << domain << " at " << addr); SNMP_TRAP(11, SNMPError, Configuration, "Error setting SRV neighbor profile " + domain + " at " + addr); delete nb; return NULL; } int m_neighborTimeout = GkConfig()->GetInteger(LRQFeaturesSection, "NeighborTimeout", 5) * 100; // Send LRQ to retreive callers signaling address // Caution: we may only use the functor object of the right type and never touch the others! LRQSender ArqFunctor((AdmissionRequest &)request); LRQSender SetupFunctor((SetupRequest &)request); LRQSender FacilityFunctor((FacilityRequest &)request); LRQSender LrqFunctor((LocationRequest &)request); LRQRequester * pRequest = NULL; if (dynamic_cast(&request)) { pRequest = new LRQRequester(ArqFunctor); } else if (dynamic_cast(&request)) { pRequest = new LRQRequester(SetupFunctor); } else if (dynamic_cast(&request)) { pRequest = new LRQRequester(FacilityFunctor); } else if (dynamic_cast(&request)) { pRequest = new LRQRequester(LrqFunctor); } if (pRequest && pRequest->Send(nb)) { if (H225_LocationConfirm * lcf = pRequest->WaitForDestination(m_neighborTimeout)) { Route * route = new Route(m_name, lcf->m_callSignalAddress); #ifdef HAS_H460 if (pRequest->IsH46024Supported()) { H225_RasMessage ras; ras.SetTag(H225_RasMessage::e_locationConfirm); H225_LocationConfirm & conf = (H225_LocationConfirm &)ras; conf = *lcf; route->m_destEndpoint = RegistrationTable::Instance()->InsertRec(ras); } #endif delete pRequest; delete nb; return route; } } delete pRequest; delete nb; PTRACE(4, "ROUTING\tDNS SRV LRQ Error for " << domain << " at " << ipaddr); // we found the directory for this domain, but it didn't have a destination, so we fail the call Route * route = new Route(); route->m_flags |= Route::e_Reject; return route; } else { PTRACE(4, "ROUTING\tDNS SRV no applied to LRQ"); } } } } return NULL; } Route * SRVPolicy::CSLookup(H225_ArrayOf_AliasAddress & aliases, bool localonly, const PString & schema, WORD schemaPort, const PString & gateway, PBoolean & changed) { for (PINDEX i = 0; i < aliases.GetSize(); ++i) { // only apply to urlID and h323ID if ((aliases[i].GetTag() != H225_AliasAddress::e_url_ID) && (aliases[i].GetTag() != H225_AliasAddress::e_h323_ID)) continue; PString alias(AsString(aliases[i], FALSE)); PINDEX at = alias.Find('@'); // skip empty aliases or those without at-sign if ((alias.GetLength() == 0) || (at == P_MAX_INDEX)) continue; // DNS SRV Record lookup PString number = "h323:" + alias; PString domain = alias.Mid(at+1); if (IsIPAddress(domain) || (domain.FindRegEx(PRegularExpression(":[0-9]+$", PRegularExpression::Extended)) != P_MAX_INDEX)) continue; // don't use SRV record if domain part is IP or has port (Annex O, O.9), let dns policy handle them // CS SRV Lookup PStringList cs; if (PDNS::LookupSRV(number, schema, cs)) { for (PINDEX j = 0; j < cs.GetSize(); j++) { H225_TransportAddress dest; PINDEX in = cs[j].Find('@'); PString dom = cs[j].Mid(in+1); if (dom.Left(7) == "0.0.0.0") { PTRACE(1, "ROUTING\tERROR in CS SRV lookup (" << cs[j] << ")"); SNMP_TRAP(11, SNMPError, Network, "SRV CS lookup failed: " + cs[i]); continue; } // If we have a gateway destination replace destination with our destination. PStringArray parts; if (!gateway) { parts = SplitIPAndPort(gateway, GK_DEF_ENDPOINT_SIGNAL_PORT); PIPSocket::Address addr; if (PIPSocket::GetHostAddress(parts[0], addr)) parts[0] = addr.AsString(); } else parts = SplitIPAndPort(dom, GK_DEF_ENDPOINT_SIGNAL_PORT); dom = parts[0]; WORD port = (WORD)parts[1].AsUnsigned(); PTRACE(4, "ROUTING\tSRV CS converted remote party " << alias << " to " << cs[j]); if (GetTransportAddress(dom, port, dest)) { PIPSocket::Address addr; if (!(GetIPFromTransportAddr(dest, addr) && addr.IsValid())) continue; if (!gateway) { // If we have a gateway destination send full URI PStringArray parts = SplitIPAndPort(cs[j].Mid(in+1), schemaPort); PString finalDest = parts[0] + ":" + parts[1]; if (in > 0) finalDest = cs[j].Left(in) + "@" + finalDest; H323SetAliasAddress(finalDest, aliases[i]); changed = true; } else H323SetAliasAddress(cs[j].Left(in), aliases[i]); Route * route = NULL; if (Toolkit::Instance()->IsGKHome(addr)) { H225_ArrayOf_AliasAddress find_aliases; find_aliases.SetSize(1); H323SetAliasAddress(alias.Left(at), find_aliases[0]); endptr ep = RegistrationTable::Instance()->FindByAliases(find_aliases); if (ep) { dest = ep->GetCallSignalAddress(); route = new Route(m_name, dest); route->m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(dest); return route; } else { return NULL; // endpoint not found } } if (!localonly) { route = new Route(m_name, dest); route->m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(dest); return route; } } } } } return NULL; } // used for ARQs and Setups bool SRVPolicy::FindByAliases(RoutingRequest & request, H225_ArrayOf_AliasAddress & aliases) { bool localonly = dynamic_cast(&request) && !m_resolveNonLocalLRQs; Route * route = NULL; // Always do the h323ls query first (if present) for (PINDEX i = 0; i < m_ls_schema.GetSize(); ++i) { PString ls_schema = m_ls_schema.GetKeyAt(i); route = LSLookup(request, aliases, localonly, ls_schema); if (route) { if (route->m_flags & Route::e_Reject) { request.SetFlag(RoutingRequest::e_Reject); } else { request.AddRoute(*route); } delete route; return true; } } for (PINDEX i = 0; i < m_cs_schema.GetSize(); ++i) { PString cs_schema = m_cs_schema.GetKeyAt(i); PStringArray dest = m_cs_schema.GetDataAt(i).Tokenise(",;"); PString gwDestination = dest[0]; WORD defSchemaPort = GK_DEF_ENDPOINT_SIGNAL_PORT; if (dest.GetSize() > 1) defSchemaPort = (WORD)dest[1].AsInteger(); PBoolean changed = false; route = CSLookup(aliases, localonly, cs_schema, defSchemaPort, gwDestination, changed); if (route) { if (route->m_flags & Route::e_Reject) { request.SetFlag(RoutingRequest::e_Reject); } else { if (changed) request.SetFlag(RoutingRequest::e_aliasesChanged); request.AddRoute(*route); } delete route; return true; } } return false; } // used for LRQs bool SRVPolicy::FindByAliases(LocationRequest & request, H225_ArrayOf_AliasAddress & aliases) { return SRVPolicy::FindByAliases((RoutingRequest&)request, aliases); } #endif /////////////////////////////////////////////////////////////////////////////////////////// // RDS policy #ifdef hasRDS class RDSPolicy : public AliasesPolicy { public: RDSPolicy(); protected: virtual bool FindByAliases(RoutingRequest &, H225_ArrayOf_AliasAddress &); virtual bool FindByAliases(LocationRequest &, H225_ArrayOf_AliasAddress &); virtual void LoadConfig(const PString & instance); bool m_resolveLRQs; }; RDSPolicy::RDSPolicy() { m_name = "RDS"; m_iniSection = "Routing::RDS"; m_resolveLRQs = Toolkit::AsBool(GkConfig()->GetString("Routing::RDS", "ResolveLRQ", "0")); } void RDSPolicy::LoadConfig(const PString & instance) { m_resolveLRQs = Toolkit::AsBool(GkConfig()->GetString(m_iniSection, "ResolveLRQ", m_resolveLRQs)); } bool RDSPolicy::FindByAliases(RoutingRequest & request, H225_ArrayOf_AliasAddress & aliases) { for (PINDEX a = 0; a < aliases.GetSize(); ++a) { PString alias(AsString(aliases[a], FALSE)); if (alias.GetLength() == 0) continue; // DNS RDS Record lookup PString number; PString domain; PINDEX at = alias.Find('@'); if (at == P_MAX_INDEX) { number = "h323:t@" + alias; domain = alias; } else { number = "h323:" + alias; domain = alias.Mid(at+1); } // LS Record lookup PStringList ls; if (PDNS::RDSLookup(number, "H323+D2U", ls)) { for (PINDEX i=0; iSetProfile(domain,addr)) { PTRACE(2, "ROUTING\tERROR setting RDS neighbor profile " << domain << " at " << addr); SNMP_TRAP(11, SNMPError, Configuration, "Error setting RDS neighbor profile " + domain + " at " + addr); delete nb; return false; } int m_neighborTimeout = GkConfig()->GetInteger(LRQFeaturesSection, "NeighborTimeout", 5) * 100; // Send LRQ to retreive callers signaling address // Caution: we may only use the functor object of the right type and never touch the others! LRQSender ArqFunctor((AdmissionRequest &)request); LRQSender SetupFunctor((SetupRequest &)request); LRQSender FacilityFunctor((FacilityRequest &)request); LRQRequester * pRequest = NULL; if (dynamic_cast(&request)) { pRequest = new LRQRequester(ArqFunctor); } else if (dynamic_cast(&request)) { pRequest = new LRQRequester(SetupFunctor); } else if (dynamic_cast(&request)) { pRequest = new LRQRequester(FacilityFunctor); } if (pRequest && pRequest->Send(nb)) { if (H225_LocationConfirm *lcf = pRequest->WaitForDestination(m_neighborTimeout)) { Route route(m_name, lcf->m_callSignalAddress); #ifdef HAS_H460 if (pRequest->IsH46024Supported()) { H225_RasMessage ras; ras.SetTag(H225_RasMessage::e_locationConfirm); H225_LocationConfirm & con = (H225_LocationConfirm &)ras; con = *lcf; route.m_destEndpoint = RegistrationTable::Instance()->InsertRec(ras); } #endif request.AddRoute(route); request.SetFlag(RoutingRequest::e_aliasesChanged); delete pRequest; delete nb; return true; } } PTRACE(4, "ROUTING\tDNS RDS LRQ Error for " << domain << " at " << ipaddr); delete pRequest; delete nb; } } } return false; } bool RDSPolicy::FindByAliases(LocationRequest & request, H225_ArrayOf_AliasAddress & aliases) { if (m_resolveLRQs) { return RDSPolicy::FindByAliases((RoutingRequest&)request, aliases); } else { PTRACE(4, "ROUTING\tPolicy RDS configured not to resolve LRQs"); return false; } } #endif class NeighborSqlPolicy : public SqlPolicy { public: NeighborSqlPolicy(); protected: virtual bool ResolveRoute(RoutingRequest & request, DestinationRoutes & destination); }; NeighborSqlPolicy::NeighborSqlPolicy() { m_active = false; m_sqlConn = NULL; #if HAS_DATABASE m_name = "NeighborSql"; m_iniSection = "Routing::NeighborSql"; m_timeout = -1; #else PTRACE(1, m_name << " not available - no database driver compiled into GnuGk"); #endif // HAS_DATABASE } bool NeighborSqlPolicy::ResolveRoute(RoutingRequest & request, DestinationRoutes & destination) { if (!destination.ChangeAliases()) { PTRACE(2, m_name << "\tNothing to resolve."); return true; } if (destination.RejectCall()) { destination.SetRejectReason(H225_AdmissionRejectReason::e_undefinedReason); return false; } PString destinationIp = AsString(destination.GetNewAliases()[0],0); PStringArray adr_parts = SplitIPAndPort(destinationIp,GK_DEF_UNICAST_RAS_PORT); PIPSocket::Address ip; if (!IsIPAddress(adr_parts[0])) PIPSocket::GetHostAddress(adr_parts[0],ip); else ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); H323TransportAddress addr(ip,port); if (addr == H323TransportAddress(RasServer::Instance()->GetRasAddress(ip))) { PTRACE(2, m_name << "\tERROR LRQ loop detected."); return false; } // Create a gatekeeper object GnuGK * nb = new GnuGK(); if (!nb->SetProfile(m_name, addr)) { PTRACE(2, "ROUTING\tERROR setting " << m_name << " profile at " << addr); SNMP_TRAP(11, SNMPError, Configuration, "Error setting " + PString(m_name) + " profile at " + AsString(addr)); delete nb; destination.SetRejectReason(H225_AdmissionRejectReason::e_undefinedReason); return false; } int m_neighborTimeout = GkConfig()->GetInteger(LRQFeaturesSection, "NeighborTimeout", 5) * 100; // Send LRQ to retreive callers signaling address // Caution: we may only use the functor object of the right type and never touch the others! LRQSender ArqFunctor((AdmissionRequest &)request); LRQSender SetupFunctor((SetupRequest &)request); LRQSender FacilityFunctor((FacilityRequest &)request); LRQSender LrqFunctor((LocationRequest &)request); LRQRequester * pRequest = NULL; if (dynamic_cast(&request)) { pRequest = new LRQRequester(ArqFunctor); } else if (dynamic_cast(&request)) { pRequest = new LRQRequester(SetupFunctor); } else if (dynamic_cast(&request)) { pRequest = new LRQRequester(FacilityFunctor); } else if (dynamic_cast(&request)) { pRequest = new LRQRequester(LrqFunctor); } if (pRequest && pRequest->Send(nb)) { if (H225_LocationConfirm * lcf = pRequest->WaitForDestination(m_neighborTimeout)) { Route route(m_name, lcf->m_callSignalAddress); #ifdef HAS_H460 if (pRequest->IsH46024Supported()) { H225_RasMessage ras; ras.SetTag(H225_RasMessage::e_locationConfirm); H225_LocationConfirm & conf = (H225_LocationConfirm &)ras; conf = *lcf; route.m_destEndpoint = RegistrationTable::Instance()->InsertRec(ras); } #endif destination.AddRoute(route); destination.SetChangedAliases(false); delete pRequest; delete nb; return true; } } delete pRequest; delete nb; return false; } namespace { SimpleCreator NeighborPolicyCreator("neighbor"); SimpleCreator NeighborSqlPolicyCreator("neighborsql"); #ifdef hasSRV SimpleCreator SRVPolicyCreator("srv"); #endif #ifdef hasRDS SimpleCreator RDSPolicyCreator("rds"); #endif } } // end of namespace Routing gnugk-3.4/PaxHeaders.16356/H46023_license.txt0000644000175000001440000000005011240400721016541 xustar000000000000000020 atime=1380868658 20 ctime=1380868609 gnugk-3.4/H46023_license.txt0000644000175000001440000000370211240400721015504 0ustar00janusers00000000000000P2Pnat Technology license (H.460.23/24) In relation to any work derived from the Point to Point through NAT Specification ("P2Pnat Technology"), International Secure Virtual offices (Asia) Pte Ltd "ISVO" (together with his successors and assignees) will grant a royalty-free, non-exclusive license with reciprocity to Qualified Parties to use the P2Pnat Technology solely to the extent necessary to implement and practice such as in compliance with the P2Pnat Specification. As used herein, Qualified Parties means a party who has not, does not and will not assert, in litigation or otherwise, including in licensing discussions, any patent or other intellectual property right against ISVO of any nature. Any license to a Qualified Party shall terminate at once if such party: (a) asserts a patent or other intellectual property right against ISVO as set forth above; or (b) if applicable, fails to properly implement the disclosure flag described in the P2Pnat Specification in a truthful manner. This license also extends to cover users and furthur development of the licensee's implementation only as far as the use does not violate the licensee's own licensing terms and conditions, where apon a user is in breach of the licensee's license then they shall be deemed to be breach of this license. ISVO will grant non-exclusive licenses to Non-Qualified Parties on reasonable and reciprocal terms and conditions. The GnuGk project (www.gnugk.org) is hereby granted non-exclusive royalty-free license of Point to Point through NAT ("P2Pnat Technology") to be used with the GnuGk project. Users and developers of GnuGk are hereby also granted non-exclusive royalty-free license of P2Pnat Technology as long as the use of GnuGk and/or any derived work containing this technology is used and/or issued under the same terms and conditions as the GnuGk project. Failure to comply with the GnuGk license shall automatically be deemed a violation of this license. gnugk-3.4/PaxHeaders.16356/h460presence.h0000644000175000001440000000005012146036662016106 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/h460presence.h0000644000175000001440000001372212146036662015054 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // Presence in H.323 gatekeeper // // Copyright (c) 2009-2010, Simon Horne // Copyright (c) 2009-2010, Jan Willamowius // // This work is published under the GNU Public License (GPL) // see file COPYING for details. // We also explicitly grant the right to link this code // with the H323plus library. // ////////////////////////////////////////////////////////////////// #include "config.h" #ifdef HAS_H460P #ifndef HAS_H460_H #define HAS_H460_H #include class PresWorker; #ifdef HAS_DATABASE class GkSQLConnection; #endif class GkPresence : public H323PresenceHandler { public: GkPresence(); ~GkPresence(); bool IsEnabled() const; void Stop(); void LoadConfig(PConfig * cfg); bool RegisterEndpoint(const H225_EndpointIdentifier & ep, const H225_ArrayOf_AliasAddress & addr); void UnRegisterEndpoint(const H225_ArrayOf_AliasAddress & addr); #ifdef HAS_H460P_VER_3 bool BuildPresenceElement(unsigned msgtag, const H225_EndpointIdentifier & ep, list & pdu); bool BuildPresenceElement(unsigned msgtag, const H225_TransportAddress & ip, list & pdu); #else bool BuildPresenceElement(unsigned msgtag, const H225_EndpointIdentifier & ep, PASN_OctetString & pdu); bool BuildPresenceElement(unsigned msgtag, const H225_TransportAddress & ip, PASN_OctetString & pdu); #endif void ProcessPresenceElement(const PASN_OctetString & pdu); bool GetPendingIdentifiers(list & epid); bool GetPendingAddresses(list & gkip); bool GetSubscriptionIdentifier(const H225_AliasAddress & local, const H225_AliasAddress & remote, H460P_PresenceIdentifier & id); bool GetSubscription(const H460P_PresenceIdentifier & id, H323PresenceID & local); bool GetLocalSubscriptions(const H225_AliasAddress & local, list & id); void DatabaseIncrementalUpdate(); protected: // Processing Functions bool EnQueuePresence(const H225_AliasAddress & addr, const H460P_PresencePDU & msg); bool EnQueueFullNotification(const H225_AliasAddress & local, const H225_AliasAddress & remote); // Inherited Events Endpoints virtual void OnNotification(MsgType tag, const H460P_PresenceNotification & notify, const H225_AliasAddress & addr ); virtual void OnSubscription(MsgType tag, const H460P_PresenceSubscription & subscription, const H225_AliasAddress & addr ); virtual void OnInstructions(MsgType tag, const H460P_ArrayOf_PresenceInstruction & instruction, const H225_AliasAddress & addr ); // Inherited Events Gatekeepers virtual void OnNotification(MsgType tag, const H460P_PresenceNotification & notify, const H225_TransportAddress & ip ); virtual void OnSubscription(MsgType tag, const H460P_PresenceSubscription & subscription, const H225_TransportAddress & ip ); virtual void OnIdentifiers(MsgType tag, const H460P_PresenceIdentifier & identifier, const H225_TransportAddress & ip ); // Build callback - Endpoint virtual PBoolean BuildSubscription(const H225_EndpointIdentifier & ep, H323PresenceStore & subscription ); virtual PBoolean BuildNotification(const H225_EndpointIdentifier & ep, H323PresenceStore & notify ); virtual PBoolean BuildInstructions(const H225_EndpointIdentifier & ep, H323PresenceStore & instruction ); // Build Callback - Gatekeepers virtual PBoolean BuildSubscription(bool request, const H225_TransportAddress & ip, H323PresenceGkStore & subscription ); virtual PBoolean BuildNotification( const H225_TransportAddress & ip, H323PresenceGkStore & notify ); virtual PBoolean BuildIdentifiers(bool alive, const H225_TransportAddress & ip, H323PresenceGkStore & identifiers ); // Handling Functions bool HandleNewAlias(const H225_AliasAddress & addr); bool HandleStatusUpdates(const H460P_PresenceIdentifier & identifier, const H225_AliasAddress & local, unsigned type, const H225_AliasAddress & remote, const H323PresenceID * id = NULL); bool HandleForwardPresence(const H460P_PresenceIdentifier & identifier, const H460P_PresencePDU & msg); bool HandleNewInstruction(unsigned tag, const H225_AliasAddress & addr, const H460P_PresenceInstruction & instruction, H323PresenceInstructions & instructions); H460P_PresenceSubscription & HandleSubscription(bool isNew, const H460P_PresenceIdentifier & pid, const H323PresenceID & id); bool HandleSubscriptionLocal(const H460P_PresenceSubscription & subscription, bool & approved); bool RemoveSubscription(unsigned type, const H460P_PresenceIdentifier & pid); // Database Functions bool DatabaseLoad(PBoolean incremental); bool DatabaseAdd(const PString & identifier, const H323PresenceID & id); bool DatabaseDelete(const PString & identifier); bool DatabaseUpdate(unsigned tag, const PString & identifier); private: H323PresenceStore localStore; // Subscription/Block list for Local Registered endpoints H323PresenceAlias aliasList; // list of presence Aliases registered locally H323PresenceLocal pendingStore; // Local Message handling store H323PresenceExternal remoteList; // remote aliases and their transport address H323PresenceRemote remoteStore; // Messages to/from remote gatekeepers H323PresenceIds remoteIds; H323PresenceIdMap remoteIdmap; H323PresenceLRQRelay remoteRelay; PMutex m_AliasMutex; bool m_enabled; bool m_sqlactive; PresWorker* m_worker; #if HAS_DATABASE // connection to the SQL database GkSQLConnection* m_sqlConn; PString m_queryList; PString m_queryAdd; PString m_queryDelete; PString m_queryUpdate; // query timeout long m_timeout; PInt64 m_lastTimeStamp; PBoolean m_incrementalUpdate; #endif }; #endif // HAS_H460_H #endif // HAS_H460P gnugk-3.4/PaxHeaders.16356/radauth.cxx0000644000175000001440000000005012015545467015706 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/radauth.cxx0000644000175000001440000012645112015545467014660 0ustar00janusers00000000000000/* * radauth.cxx * * RADIUS protocol authenticator module for GNU Gatekeeper. * Please see docs/radauth.txt for more details. * * Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz * Copyright (c) 2005-2012, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include "config.h" #if HAS_RADIUS #include #include #include #include #include #include #include "gk_const.h" #include "h323util.h" #include "stl_supp.h" #include "Toolkit.h" #include "RasTbl.h" #include "RasPDU.h" #include "Routing.h" #include "sigmsg.h" #include "radproto.h" #include "radauth.h" using std::vector; using Routing::Route; namespace { // Settings for H.235 based module will be stored inside [RadAuth] config section const char* const RadAuthConfigSectionName = "RadAuth"; // Settings for alias based module will be stored inside [RadAliasAuth] config section const char* const RadAliasAuthConfigSectionName = "RadAliasAuth"; } // OID for CAT (Cisco Access Token) algorithm PString RadAuth::OID_CAT("1.2.840.113548.10.1.2.1"); RadAuthBase::RadAuthBase( const char* authName, const char* configSectionName, unsigned supportedRasChecks, unsigned supportedMiscChecks ) : GkAuthenticator(authName, supportedRasChecks, supportedMiscChecks), m_radiusClient(NULL), m_attrH323CallType(RadiusAttr::CiscoVSA_h323_call_type, false, PString("VoIP")), m_attrH323CallOriginOriginate(RadiusAttr::CiscoVSA_h323_call_origin, false, PString("originate")), m_attrH323CallOriginAnswer(RadiusAttr::CiscoVSA_h323_call_origin, false, PString("answer")) { // read settings from the config m_appendCiscoAttributes = Toolkit::AsBool(GetConfig()->GetString( configSectionName,"AppendCiscoAttributes", "1" )); m_includeTerminalAliases = Toolkit::AsBool(GetConfig()->GetString( configSectionName, "IncludeTerminalAliases", "1" )); m_nasIdentifier = Toolkit::Instance()->GKName(); /// build RADIUS client m_radiusClient = new RadiusClient(*GetConfig(), configSectionName); m_nasIpAddress = m_radiusClient->GetLocalAddress(); UnmapIPv4Address(m_nasIpAddress); if (m_nasIpAddress == GNUGK_INADDR_ANY) { vector interfaces; Toolkit::Instance()->GetGKHome(interfaces); if (!interfaces.empty()) m_nasIpAddress = interfaces.front(); else PTRACE(1, "RADAUTH\t" << GetName() << " cannot determine " " NAS IP address" ); } m_useDialedNumber = Toolkit::AsBool(GetConfig()->GetString( configSectionName, "UseDialedNumber", "0" )); m_attrH323GwId = RadiusAttr(RadiusAttr::CiscoVSA_h323_gw_id, false, m_nasIdentifier); m_attrNasIdentifier = RadiusAttr(RadiusAttr::NasIdentifier, m_nasIdentifier); } RadAuthBase::~RadAuthBase() { delete m_radiusClient; } int RadAuthBase::Check( /// RRQ RAS message to be authenticated RasPDU & rrqPdu, /// authorization data (reject reason, ...) RRQAuthData & authData ) { H225_RegistrationRequest& rrq = (H225_RegistrationRequest&)rrqPdu; // build RADIUS Access-Request RadiusPDU* const pdu = new RadiusPDU(RadiusPDU::AccessRequest); // Append User-Name and a password related attributes // (User-Password or Chap-Password and Chap-Timestamp) const int status = AppendUsernameAndPassword(*pdu, rrqPdu, authData); if (status != e_ok) { delete pdu; return status; } // Gk works as NAS point, so append NAS IP if (m_nasIpAddress.GetVersion() == 6) pdu->AppendAttr(RadiusAttr::NasIpv6Address, m_nasIpAddress); else pdu->AppendAttr(RadiusAttr::NasIpAddress, m_nasIpAddress); // NAS-Identifier as Gk name pdu->AppendAttr(m_attrNasIdentifier); // Gk does not have a concept of physical ports, // so define port type as NAS-Port-Virtual pdu->AppendAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual); // RRQ service type is Login-User pdu->AppendAttr(RadiusAttr::ServiceType, RadiusAttr::ST_Login); // append Framed-IP-Address PIPSocket::Address addr; const PIPSocket::Address & rx_addr = rrqPdu->m_peerAddr; bool ipFound = false; if (rrq.m_callSignalAddress.GetSize() > 0) { if (GetIPFromTransportAddr(rrq.m_callSignalAddress[0], addr) && addr.IsValid()) ipFound = true; } else if (rrq.m_rasAddress.GetSize() > 0) { if (GetIPFromTransportAddr(rrq.m_rasAddress[0], addr) && addr.IsValid()) ipFound = true; } if (!ipFound) { PTRACE(2, "RADAUTH\t" << GetName() << " RRQ auth failed: " "could not determine Framed-IP-Address"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " RRQ check failed"); authData.m_rejectReason = H225_RegistrationRejectReason::e_invalidCallSignalAddress; delete pdu; return e_fail; } else pdu->AppendAttr(RadiusAttr::FramedIpAddress, (rx_addr != addr)? rx_addr : addr); if (m_appendCiscoAttributes && m_includeTerminalAliases && rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) { PString aliasList("terminal-alias:"); for (PINDEX i = 0; i < rrq.m_terminalAlias.GetSize(); i++) { if(i > 0) aliasList += ","; aliasList += AsString(rrq.m_terminalAlias[i], FALSE); } // Cisco-AV-Pair pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair, PString("h323-ivr-out=") + aliasList + ";", true ); } // send request and wait for response RadiusPDU* response = NULL; bool result = m_radiusClient->MakeRequest(*pdu, response) && response; delete pdu; if (!result) { PTRACE(2, "RADAUTH\t" << GetName() << " RRQ auth failed: " " could not receive or decode response from RADIUS"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " RRQ check failed"); delete response; response = NULL; authData.m_rejectReason = H225_RegistrationRejectReason::e_undefinedReason; return GetDefaultStatus(); } result = (response->GetCode() == RadiusPDU::AccessAccept); PString value; const RadiusAttr* attr; // test for h323-return-code attribute (has to be 0 if accept) if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_return_code ); if (attr != NULL) { value = attr->AsCiscoString(); if (value.GetLength() > 0 && strspn((const char*)value, "0123456789") == (size_t)value.GetLength()) { const unsigned retcode = value.AsUnsigned(); if (retcode != 0) { PTRACE(3, "RADAUTH\t" << GetName() << " RRQ check failed: return code " << retcode); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " RRQ check failed"); result = false; } } else { PTRACE(2, "RADAUTH\t" << GetName() << " RRQ check failed: " "invalid h323-return-code attribute '" << value << '\''); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " RRQ check failed"); result = false; } } } // check for h323-billing-model if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_billing_model ); if (attr != NULL) { value = attr->AsCiscoString(); if (value.GetLength() > 0 && strspn((const char*)value,"0123456789") == (size_t)value.GetLength()) { const int intVal = value.AsInteger(); if (intVal == 0) authData.m_billingMode = H225_CallCreditServiceControl_billingMode::e_credit; else if (intVal == 1 || intVal == 2) authData.m_billingMode = H225_CallCreditServiceControl_billingMode::e_debit; } else { PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-billing-model " "attribute '" << value << '\'' ); } } } // check for h323-credit-amount if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_credit_amount ); if (attr != NULL) { value = attr->AsCiscoString(); if (value.GetLength() > 0 && strspn((const char*)value,"0123456789.") == (size_t)value.GetLength()) { if (value.Find('.') == P_MAX_INDEX) { PTRACE(3, "RADAUTH\t" << GetName() << " h323-credit-amount " "without a decimal dot is ambiguous '" << value << '\'' ); authData.m_amountString = psprintf(PString("%d.%d"), value.AsInteger() / 100, value.AsInteger() % 100 ); } else authData.m_amountString = value; attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_currency ); if (attr != NULL) authData.m_amountString += attr->AsCiscoString(); } else { PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-credit-amount " "attribute '" << value << '\'' ); } } } // process h323-ivr-in=terminal-alias attribute if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_AV_Pair ); while (attr != NULL) { PINDEX index; value = attr->AsCiscoString(); if (value.Find("h323-ivr-in=") == 0 && ((index = value.Find("terminal-alias:")) != P_MAX_INDEX)) { index += strlen("terminal-alias:"); const PINDEX semicolonpos = value.Find(';', index); value = value.Mid(index, semicolonpos == P_MAX_INDEX ? P_MAX_INDEX : (semicolonpos-index) ); PStringArray aliases = value.Tokenise(","); if (aliases.GetSize() > 0 && rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) { PINDEX i = 0; while (i < rrq.m_terminalAlias.GetSize()) { PINDEX j = aliases.GetStringsIndex(AsString(rrq.m_terminalAlias[i], FALSE)); if( j == P_MAX_INDEX ) rrq.m_terminalAlias.RemoveAt(i); else { i++; aliases.RemoveAt(j); } } } for (PINDEX i = 0; i < aliases.GetSize(); i++) { if (rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) rrq.m_terminalAlias.SetSize(rrq.m_terminalAlias.GetSize()+1); else { rrq.IncludeOptionalField(H225_RegistrationRequest::e_terminalAlias); rrq.m_terminalAlias.SetSize(1); } H323SetAliasAddress(aliases[i], rrq.m_terminalAlias[rrq.m_terminalAlias.GetSize()-1]); } break; } attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_AV_Pair, attr ); } } if (!result) authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial; delete response; response = NULL; return result ? e_ok : e_fail; } int RadAuthBase::Check( /// ARQ nessage to be authenticated RasPDU & arqPdu, /// authorization data (call duration limit, reject reason, ...) ARQAuthData& authData ) { H225_AdmissionRequest& arq = (H225_AdmissionRequest&)arqPdu; // build RADIUS Access-Request packet RadiusPDU* const pdu = new RadiusPDU(RadiusPDU::AccessRequest); const bool hasCall = authData.m_call.operator->() != NULL; PIPSocket::Address addr; endptr callingEP, calledEP; // try to extract calling/called endpoints from RegistrationTable // (unregistered endpoints will not be present there) if (arq.m_answerCall) { calledEP = authData.m_requestingEP; if (hasCall) callingEP = authData.m_call->GetCallingParty(); } else { callingEP = authData.m_requestingEP; if (hasCall) calledEP = authData.m_call->GetCalledParty(); if (!calledEP && arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) calledEP = RegistrationTable::Instance()->FindBySignalAdr(arq.m_destCallSignalAddress); } // at least requesting endpoint (the one that is sending ARQ) // has to be present in the RegistrationTable if (arq.m_answerCall ? !calledEP : !callingEP) { delete pdu; PTRACE(3, "RADAUTH\t" << GetName() << " ARQ auth failed: " "requesting endpoint " << arq.m_endpointIdentifier << " not registered"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " ARQ check failed"); authData.m_rejectReason = arq.m_answerCall ? H225_AdmissionRejectReason::e_calledPartyNotRegistered : H225_AdmissionRejectReason::e_callerNotRegistered; return e_fail; } // Append User-Name and a password related attributes // (User-Password or Chap-Password and Chap-Timestamp) PString username; const int status = AppendUsernameAndPassword(*pdu, arqPdu, authData, &username); if (status != e_ok) { delete pdu; return status; } // Gk acts as NAS, so include NAS IP if (m_nasIpAddress.GetVersion() == 6) pdu->AppendAttr(RadiusAttr::NasIpv6Address, m_nasIpAddress); else pdu->AppendAttr(RadiusAttr::NasIpAddress, m_nasIpAddress); // NAS-Identifier as Gk name pdu->AppendAttr(m_attrNasIdentifier); // NAS-Port-Type as Virtual, since Gk does // not care about physical ports concept pdu->AppendAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual); // Service-Type is Login-User if originating the call // and Call Check if answering the call pdu->AppendAttr(RadiusAttr::ServiceType, arq.m_answerCall ? RadiusAttr::ST_CallCheck : RadiusAttr::ST_Login ); // append Frame-IP-Address const PIPSocket::Address & rx_addr = arqPdu->m_peerAddr; bool ipFound = false; if (arq.m_answerCall) { if (calledEP && GetIPFromTransportAddr(calledEP->GetCallSignalAddress(), addr) && addr.IsValid()) ipFound = true; else if (arq.HasOptionalField(arq.e_destCallSignalAddress) && GetIPFromTransportAddr(arq.m_destCallSignalAddress, addr) && addr.IsValid()) ipFound = true; } else { if (callingEP && GetIPFromTransportAddr(callingEP->GetCallSignalAddress(), addr) && addr.IsValid()) ipFound = true; else if(arq.HasOptionalField(arq.e_srcCallSignalAddress) && GetIPFromTransportAddr(arq.m_srcCallSignalAddress, addr) && addr.IsValid()) ipFound = true; } if (!ipFound) { PTRACE(2, "RADAUTH\t" << GetName() << " ARQ auth failed: " "could not setup Framed-IP-Address"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " ARQ check failed"); authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial; delete pdu; return e_fail; } else pdu->AppendAttr(RadiusAttr::FramedIpAddress, (rx_addr != addr)? rx_addr : addr); // fill Calling-Station-Id and Called-Station-Id fields PString stationId = GetCallingStationId(arqPdu, authData); if (!stationId) { pdu->AppendAttr(RadiusAttr::CallingStationId, stationId); } const PString dialedNumber = GetDialedNumber(arqPdu, authData); const PString calledStationId = GetCalledStationId(arqPdu, authData); stationId = m_useDialedNumber ? dialedNumber : calledStationId; if (stationId.IsEmpty()) { delete pdu; PTRACE(2, "RADAUTH\t" << GetName() << " ARQ auth failed: " "no suitable alias for Calling-Station-Id has been found"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " ARQ check failed"); authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial; return e_fail; } else pdu->AppendAttr(RadiusAttr::CalledStationId, stationId); if (m_appendCiscoAttributes) { pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_conf_id, GetGUIDString(arq.m_conferenceID) ); if (arq.m_answerCall) pdu->AppendAttr(m_attrH323CallOriginAnswer); else pdu->AppendAttr(m_attrH323CallOriginOriginate); pdu->AppendAttr(m_attrH323CallType); pdu->AppendAttr(m_attrH323GwId); } // send the request and wait for a response RadiusPDU* response = NULL; bool result = m_radiusClient->MakeRequest(*pdu, response) && response; delete pdu; if (!result) { PTRACE(2, "RADAUTH\t" << GetName() << " ARQ auth failed: " " could not receive or decode response from RADIUS"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " ARQ check failed"); delete response; response = NULL; authData.m_rejectReason = H225_AdmissionRejectReason::e_undefinedReason; return GetDefaultStatus(); } // authenticated? result = (response->GetCode() == RadiusPDU::AccessAccept); PString value; const RadiusAttr* attr; // check for Class attribute if (result) { attr = response->FindAttr(RadiusAttr::AttrTypeClass); if (attr != NULL) { PBYTEArray classData; if (attr->GetValue(classData)) authData.m_radiusClass = classData; } } // test for h323-return-code attribute (has to be 0 if accept) if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_return_code ); if (attr != NULL) { value = attr->AsCiscoString(); if (value.GetLength() > 0 && strspn((const char*)value, "0123456789") == (size_t)value.GetLength()) { const unsigned retcode = value.AsUnsigned(); if (retcode != 0) { PTRACE(3, "RADAUTH\t" << GetName() << " ARQ check failed: " "return code " << retcode); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " ARQ check failed"); result = false; } } else { PTRACE(2, "RADAUTH\t" << GetName() << " ARQ check failed: " "invalid h323-return-code attribute '" << value << '\''); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " ARQ check failed"); result = false; } } } // process h323-ivr-in=codec-disable attribute if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_AV_Pair); while (attr != NULL) { PINDEX index; value = attr->AsCiscoString(); if (value.Find("h323-ivr-in=") == 0 && ((index = value.Find("codec-disable:")) != P_MAX_INDEX)) { index += strlen("codec-disable:"); const PINDEX semicolonpos = value.FindLast(';', index); value = value.Mid(index, semicolonpos == P_MAX_INDEX ? P_MAX_INDEX : (semicolonpos-index)); PTRACE(4, "RADAUTH\t" << GetName() << " Setup check set codec-disable: " << value); authData.m_disabledcodecs = value; break; } attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_AV_Pair, attr); } } // check for h323-credit-time attribute (call duration limit) if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_credit_time ); if (attr != NULL) { value = attr->AsCiscoString(); if (value.GetLength() > 0 && strspn((const char*)value,"0123456789") == (size_t)value.GetLength()) { authData.m_callDurationLimit = value.AsInteger(); PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check set duration " "limit: " << authData.m_callDurationLimit ); if (authData.m_callDurationLimit == 0) result = false; } else { PTRACE(2, "RADAUTH\t" << GetName() << " ARQ check failed: " "invalid h323-credit-time attribute '" << value << '\''); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " ARQ check failed"); result = false; } } } // check for Session-Timeout attribute (alternate call duration limit) if (result) { const RadiusAttr* const tattr = response->FindAttr(RadiusAttr::SessionTimeout); if (tattr != NULL) { const long sessionTimeout = tattr->AsInteger(); if (authData.m_callDurationLimit < 0 || authData.m_callDurationLimit > sessionTimeout) { authData.m_callDurationLimit = sessionTimeout; PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check set " "duration limit set " << authData.m_callDurationLimit ); } if (authData.m_callDurationLimit == 0) result = false; } } // check for h323-billing-model if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_billing_model ); if (attr != NULL) { value = attr->AsCiscoString(); if (value.GetLength() > 0 && strspn((const char*)value,"0123456789") == (size_t)value.GetLength()) { const int intVal = value.AsInteger(); if (intVal == 0) authData.m_billingMode = H225_CallCreditServiceControl_billingMode::e_credit; else if (intVal == 1 || intVal == 2) authData.m_billingMode = H225_CallCreditServiceControl_billingMode::e_debit; } else { PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-billing-model " "attribute '" << value << '\'' ); } } } // check for h323-credit-amount if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_credit_amount ); if (attr != NULL) { value = attr->AsCiscoString(); if (value.GetLength() > 0 && strspn((const char*)value,"0123456789.") == (size_t)value.GetLength()) { if (value.Find('.') == P_MAX_INDEX) { PTRACE(3, "RADAUTH\t" << GetName() << " h323-credit-amount " "without a decimal dot is ambiguous '" << value << '\'' ); authData.m_amountString = psprintf(PString("%d.%d"), value.AsInteger() / 100, value.AsInteger() % 100 ); } else authData.m_amountString = value; attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_currency ); if (attr != NULL) authData.m_amountString += attr->AsCiscoString(); } else { PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-credit-amount " "attribute '" << value << '\'' ); } } } PStringArray numbersToDial; // check for h323-redirect-number if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_redirect_number ); if (attr != NULL) { value = attr->AsCiscoString(); if (!value) { numbersToDial = value.Tokenise("; \t", FALSE); if (numbersToDial.GetSize() > 0) { authData.SetRouteToAlias(numbersToDial[0]); PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check redirect " "to the number " << value ); } else { PTRACE(1, "RADAUTH\t" << GetName() << " invalid ARQ check redirect numbers list: " << value ); } } } } // check for h323-redirect-ip-address if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_redirect_ip_address ); if (attr != NULL) { value = attr->AsCiscoString(); if (!value) { PStringArray tokens(value.Tokenise("; \t", FALSE)); for (PINDEX i = 0; i < tokens.GetSize(); ++i) { PIPSocket::Address raddr; WORD rport = 0; if (GetTransportAddress(tokens[i], GK_DEF_ENDPOINT_SIGNAL_PORT, raddr, rport) && raddr.IsValid() && rport != 0) { Route route("RADIUS", raddr, rport); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr( SocketToH225TransportAddr(raddr, rport) ); if (numbersToDial.GetSize() > 0) { route.m_destNumber = (i < numbersToDial.GetSize()) ? numbersToDial[i] : numbersToDial[numbersToDial.GetSize() - 1]; PINDEX pos = route.m_destNumber.Find('='); if (pos != P_MAX_INDEX) { route.m_destOutNumber = route.m_destNumber.Mid(pos + 1); route.m_destNumber = route.m_destNumber.Left(pos); } } authData.m_destinationRoutes.push_back(route); PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check redirect " "to the address " << route.AsString() ); } } } } } // process h323-ivr-in=proxy attribute if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_AV_Pair ); while (attr != NULL) { PINDEX index; value = attr->AsCiscoString(); if (value.Find("h323-ivr-in=") == 0 && ((index = value.Find("proxy:")) != P_MAX_INDEX)) { index += strlen("proxy:"); const PINDEX semicolonpos = value.Find(';', index); value = value.Mid(index, semicolonpos == P_MAX_INDEX ? P_MAX_INDEX : (semicolonpos-index) ); if (!value) { authData.m_proxyMode = Toolkit::AsBool(value) ? CallRec::ProxyEnabled : CallRec::ProxyDisabled; PTRACE(5, "RADAUTH\t" << GetName() << " - proxy mode " << (authData.m_proxyMode == CallRec::ProxyEnabled ? "enabled" : "disabled") ); } } attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_AV_Pair, attr ); } } if (!result) authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial; delete response; response = NULL; return result ? e_ok : e_fail; } int RadAuthBase::Check( SetupMsg &setup, SetupAuthData &authData ) { // build RADIUS Access-Request packet RadiusPDU* const pdu = new RadiusPDU(RadiusPDU::AccessRequest); H225_Setup_UUIE& setupBody = setup.GetUUIEBody(); const bool hasCall = authData.m_call.operator->() != NULL; PIPSocket::Address addr; endptr callingEP; if (hasCall) callingEP = authData.m_call->GetCallingParty(); if (!callingEP && setupBody.HasOptionalField(H225_Setup_UUIE::e_endpointIdentifier)) callingEP = RegistrationTable::Instance()->FindByEndpointId( setupBody.m_endpointIdentifier ); // Append User-Name and a password related attributes // (User-Password or Chap-Password and Chap-Timestamp) PString username; const int status = AppendUsernameAndPassword(*pdu, setup, callingEP, authData, &username ); if (status != e_ok) { delete pdu; return status; } // Gk acts as NAS, so include NAS IP if (m_nasIpAddress.GetVersion() == 6) pdu->AppendAttr(RadiusAttr::NasIpv6Address, m_nasIpAddress); else pdu->AppendAttr(RadiusAttr::NasIpAddress, m_nasIpAddress); // NAS-Identifier as Gk name pdu->AppendAttr(m_attrNasIdentifier); // NAS-Port-Type as Virtual, since Gk does // not care about physical ports concept pdu->AppendAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual); // Service-Type is Login-User if originating the call // and Call Check if answering the call pdu->AppendAttr(RadiusAttr::ServiceType, RadiusAttr::ST_Login); // append Frame-IP-Address bool ipFound = false; WORD dummyPort; if (hasCall && authData.m_call->GetSrcSignalAddr(addr, dummyPort) && addr.IsValid()) ipFound = true; else if (callingEP && GetIPFromTransportAddr(callingEP->GetCallSignalAddress(), addr) && addr.IsValid()) ipFound = true; else if (setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceCallSignalAddress) && GetIPFromTransportAddr(setupBody.m_sourceCallSignalAddress, addr) && addr.IsValid()) ipFound = true; else { setup.GetPeerAddr(addr); ipFound = addr.IsValid(); } if (!ipFound) { PTRACE(2, "RADAUTH\t" << GetName() << " Setup auth failed: " "could not setup Framed-IP-Address"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " Setup check failed"); delete pdu; authData.m_rejectCause = Q931::CallRejected; return e_fail; } else pdu->AppendAttr(RadiusAttr::FramedIpAddress, addr); // fill Calling-Station-Id and Called-Station-Id fields PString stationId = GetCallingStationId(setup, authData); if (!stationId) { pdu->AppendAttr(RadiusAttr::CallingStationId, stationId); } const PString calledStationId = GetCalledStationId(setup, authData); const PString dialedNumber = GetDialedNumber(setup, authData); stationId = m_useDialedNumber ? dialedNumber : calledStationId; if (stationId.IsEmpty()) { delete pdu; PTRACE(2, "RADAUTH\t" << GetName() << " Setup check failed: " "no called station id found"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " Setup check failed"); authData.m_rejectReason = H225_ReleaseCompleteReason::e_badFormatAddress; return e_fail; } else pdu->AppendAttr(RadiusAttr::CalledStationId, stationId); if (m_appendCiscoAttributes) { pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_conf_id, GetGUIDString(setupBody.m_conferenceID) ); pdu->AppendAttr(m_attrH323CallOriginOriginate); pdu->AppendAttr(m_attrH323CallType); pdu->AppendAttr(m_attrH323GwId); } // send the request and wait for a response RadiusPDU* response = NULL; bool result = m_radiusClient->MakeRequest(*pdu, response) && response; delete pdu; if (!result) { PTRACE(2, "RADAUTH\t" << GetName() << " Setup auth failed: " " could not receive or decode response from RADIUS"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " Setup check failed"); delete response; response = NULL; authData.m_rejectCause = Q931::TemporaryFailure; return GetDefaultStatus(); } // authenticated? result = (response->GetCode() == RadiusPDU::AccessAccept); PString value; const RadiusAttr* attr; // check for Class attribute if (result) { attr = response->FindAttr(RadiusAttr::AttrTypeClass); if (attr != NULL) { PBYTEArray classData; if (attr->GetValue(classData)) authData.m_radiusClass = classData; } } // test for h323-return-code attribute (has to be 0 if accept) if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_return_code ); if (attr != NULL) { value = attr->AsCiscoString(); if (value.GetLength() > 0 && strspn((const char*)value, "0123456789") == (size_t)value.GetLength()) { const unsigned retcode = value.AsUnsigned(); if (retcode != 0) { PTRACE(5, "RADAUTH\t" << GetName() << " Setup check failed: " "return code " << retcode); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " Setup check failed"); result = false; } } else { PTRACE(2, "RADAUTH\t" << GetName() << " Setup check failed: " "invalid h323-return-code attribute '" << value << '\''); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " Setup check failed"); result = false; } } } // process h323-ivr-in=codec-disable attribute if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_AV_Pair); while (attr != NULL) { PINDEX index; value = attr->AsCiscoString(); if (value.Find("h323-ivr-in=") == 0 && ((index = value.Find("codec-disable:")) != P_MAX_INDEX)) { index += strlen("codec-disable:"); const PINDEX semicolonpos = value.FindLast(';', index); value = value.Mid(index, semicolonpos == P_MAX_INDEX ? P_MAX_INDEX : (semicolonpos-index)); PTRACE(4, "RADAUTH\t" << GetName() << " Setup check set codec-disable: " << value); authData.m_disabledcodecs = value; break; } attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_AV_Pair, attr); } } // check for h323-credit-time attribute (call duration limit) if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_credit_time ); if (attr != NULL) { value = attr->AsCiscoString(); if (value.GetLength() > 0 && strspn((const char*)value,"0123456789") == (size_t)value.GetLength() ) { authData.m_callDurationLimit = value.AsInteger(); PTRACE(5, "RADAUTH\t" << GetName() << " Setup check set duration " "limit: " << authData.m_callDurationLimit ); if (authData.m_callDurationLimit == 0) result = false; } else { PTRACE(2, "RADAUTH\t" << GetName() << " Setup check failed: " "invalid h323-credit-time attribute '" << value << '\''); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " Setup check failed"); result = false; } } } // check for Session-Timeout attribute (alternate call duration limit) if (result) { const RadiusAttr* const tattr = response->FindAttr(RadiusAttr::SessionTimeout); if (tattr != NULL) { const long sessionTimeout = tattr->AsInteger(); if (authData.m_callDurationLimit < 0 || authData.m_callDurationLimit > sessionTimeout) { authData.m_callDurationLimit = sessionTimeout; PTRACE(5, "RADAUTH\t" << GetName() << " Setup check " "set duration limit: " << authData.m_callDurationLimit ); } if (authData.m_callDurationLimit == 0) result = false; } } PStringArray numbersToDial; // check for h323-redirect-number if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_redirect_number ); if (attr != NULL) { value = attr->AsCiscoString(); if (!value) { numbersToDial = value.Tokenise("; \t", FALSE); if (numbersToDial.GetSize() > 0) { authData.SetRouteToAlias(numbersToDial[0]); PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check redirect " "to the number " << value ); } else { PTRACE(1, "RADAUTH\t" << GetName() << " invalid ARQ check redirect numbers list: " << value ); } } } } // check for h323-redirect-ip-address if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_h323_redirect_ip_address ); if (attr != NULL) { value = attr->AsCiscoString(); if (!value) { PStringArray tokens(value.Tokenise("; \t", FALSE)); for (PINDEX i = 0; i < tokens.GetSize(); ++i) { PIPSocket::Address raddr; WORD rport = 0; if (GetTransportAddress(tokens[i], GK_DEF_ENDPOINT_SIGNAL_PORT, raddr, rport) && raddr.IsValid() && rport != 0) { Route route("RADIUS", raddr, rport); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr( SocketToH225TransportAddr(raddr, rport) ); if (numbersToDial.GetSize() > 0) { route.m_destNumber = (i < numbersToDial.GetSize()) ? numbersToDial[i] : numbersToDial[numbersToDial.GetSize() - 1]; PINDEX pos = route.m_destNumber.Find('='); if (pos != P_MAX_INDEX) { route.m_destOutNumber = route.m_destNumber.Mid(pos + 1); route.m_destNumber = route.m_destNumber.Left(pos); } } authData.m_destinationRoutes.push_back(route); PTRACE(5, "RADAUTH\t" << GetName() << " Setup check redirect " "to the address " << route.AsString() ); } } } } } // process h323-ivr-in=proxy attribute if (result) { attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_AV_Pair ); while (attr != NULL) { PINDEX index; value = attr->AsCiscoString(); if (value.Find("h323-ivr-in=") == 0 && ((index = value.Find("proxy:")) != P_MAX_INDEX)) { index += strlen("proxy:"); const PINDEX semicolonpos = value.Find(';', index); value = value.Mid(index, semicolonpos == P_MAX_INDEX ? P_MAX_INDEX : (semicolonpos-index) ); if (!value) { authData.m_proxyMode = Toolkit::AsBool(value) ? CallRec::ProxyEnabled : CallRec::ProxyDisabled; PTRACE(5, "RADAUTH\t" << GetName() << " - proxy mode " << (authData.m_proxyMode == CallRec::ProxyEnabled ? "enabled" : "disabled") ); } } attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, RadiusAttr::CiscoVSA_AV_Pair, attr ); } } if (!result) authData.m_rejectCause = Q931::CallRejected; delete response; response = NULL; return result ? e_ok : e_fail; } int RadAuthBase::AppendUsernameAndPassword( RadiusPDU& /*pdu*/, RasPDU& /*rrqPdu*/, RRQAuthData& /*authData*/, PString* /*username*/ ) const { return GetDefaultStatus(); } int RadAuthBase::AppendUsernameAndPassword( RadiusPDU& /*pdu*/, RasPDU& /*arqPdu*/, ARQAuthData& /*authData*/, PString* /*username*/ ) const { return GetDefaultStatus(); } int RadAuthBase::AppendUsernameAndPassword( RadiusPDU &/*pdu*/, SetupMsg &/*setup*/, endptr &/*callingEP*/, SetupAuthData &/*authData*/, PString * /*username*/ ) const { return GetDefaultStatus(); } RadAuth::RadAuth( const char* authName ) : RadAuthBase(authName, RadAuthConfigSectionName) { // setup H.235 algorithm and method types used // by this authenticator - this will make sure // GCF H.235 alogirthm selection will not skip // information required by this authenticator H235AuthCAT* authenticator = new H235AuthCAT(); authenticator->SetLocalId("dummy"); authenticator->SetRemoteId("dummy"); authenticator->SetPassword("dummy"); AppendH235Authenticator(authenticator); } RadAuth::~RadAuth() { } int RadAuth::CheckTokens( RadiusPDU& pdu, const H225_ArrayOf_ClearToken& tokens, const H225_ArrayOf_AliasAddress* aliases, PString* username ) const { // scan ClearTokens and find CATs for (PINDEX i = 0; i < tokens.GetSize(); i++) { const H235_ClearToken& token = tokens[i]; // is this CAT? if (token.m_tokenOID != OID_CAT) continue; // these field are required for CAT if (!(token.HasOptionalField(H235_ClearToken::e_generalID) && token.HasOptionalField(H235_ClearToken::e_random) && token.HasOptionalField(H235_ClearToken::e_timeStamp) && token.HasOptionalField(H235_ClearToken::e_challenge))) { PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: " "CAT without all required fields"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " failed"); return e_fail; } // generalID should be present in the list of terminal aliases const PString id = token.m_generalID; if (aliases && FindAlias(*aliases, id) == P_MAX_INDEX) { PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: " "CAT m_generalID is not a valid alias"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " failed"); return e_fail; } // CAT pseudo-random has to be one byte only const int randomInt = token.m_random; if (randomInt < -127 || randomInt > 255) { PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: " "CAT m_random out of range"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " failed"); return e_fail; } // CAT challenge has to be 16 bytes if (token.m_challenge.GetValue().GetSize() < 16) { PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: " "m_challenge less than 16 bytes"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " failed"); return e_fail; } // append User-Name pdu.AppendAttr(RadiusAttr::UserName, id); if (username != NULL) *username = (const char*)id; // build CHAP-Password unsigned char password[17] = { (BYTE)randomInt }; memcpy(password + 1, (const BYTE*)(token.m_challenge), 16); pdu.AppendAttr(RadiusAttr::ChapPassword, password, sizeof(password)); pdu.AppendAttr(RadiusAttr::ChapChallenge, (int)(DWORD)token.m_timeStamp); return e_ok; } PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: no CAT token found"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " failed: no token"); return GetDefaultStatus(); } int RadAuth::AppendUsernameAndPassword( RadiusPDU& pdu, RasPDU& rrqPdu, RRQAuthData& authData, PString* username ) const { H225_RegistrationRequest& rrq = (H225_RegistrationRequest&)rrqPdu; // RRQ has to carry at least one terminalAlias if (!rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) { PTRACE(3, "RADAUTH\t" << GetName() << " RRQ auth failed: " "no m_terminalAlias field"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + "RRQ check failed"); authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial; return GetDefaultStatus(); } // check for ClearTokens (CAT uses ClearTokens) if (!rrq.HasOptionalField(H225_RegistrationRequest::e_tokens)) { PTRACE(3, "RADAUTH\t" << GetName() << " RRQ auth failed: " "tokens not found"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + "RRQ check failed"); authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial; return GetDefaultStatus(); } const int result = CheckTokens(pdu, rrq.m_tokens, &rrq.m_terminalAlias, username); if (result != e_ok) authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial; return result; } int RadAuth::AppendUsernameAndPassword( RadiusPDU & pdu, RasPDU & arqPdu, ARQAuthData & authData, PString * username ) const { H225_AdmissionRequest& arq = (H225_AdmissionRequest&)arqPdu; // check for ClearTokens if (!arq.HasOptionalField(H225_AdmissionRequest::e_tokens)) { PTRACE(3, "RADAUTH\t" << GetName() << " ARQ auth failed: " "tokens not found"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " ARQ check failed"); authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial; return GetDefaultStatus(); } const int result = CheckTokens(pdu, arq.m_tokens, NULL, username); if (result != e_ok) authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial; return result; } int RadAuth::AppendUsernameAndPassword( RadiusPDU & pdu, SetupMsg & setup, endptr & /*callingEP*/, SetupAuthData & authData, PString * username ) const { H225_Setup_UUIE &setupBody = setup.GetUUIEBody(); // check for ClearTokens (CAT uses ClearTokens) if (!setupBody.HasOptionalField(H225_Setup_UUIE::e_tokens)) { PTRACE(3, "RADAUTH\t" << GetName() << " Setup auth failed: no tokens"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " Setup check failed"); authData.m_rejectReason = H225_ReleaseCompleteReason::e_securityDenied; return GetDefaultStatus(); } const int result = CheckTokens(pdu, setupBody.m_tokens, NULL, username); if (result != e_ok) authData.m_rejectCause = Q931::CallRejected; return result; } RadAliasAuth::RadAliasAuth( const char * authName) : RadAuthBase(authName, RadAliasAuthConfigSectionName) { m_fixedUsername = GetConfig()->GetString( RadAliasAuthConfigSectionName, "FixedUsername", ""); m_fixedPassword = Toolkit::Instance()->ReadPassword( RadAliasAuthConfigSectionName, "FixedPassword"); m_emptyUsername = GetConfig()->GetString( RadAliasAuthConfigSectionName, "EmptyUsername", ""); } RadAliasAuth::~RadAliasAuth() { } int RadAliasAuth::AppendUsernameAndPassword( RadiusPDU & pdu, RasPDU & rrqPdu, RRQAuthData & authData, PString * username ) const { const PString id = GetUsername(rrqPdu); if (id.IsEmpty() && m_fixedUsername.IsEmpty()) { PTRACE(3, "RADAUTH\t" << GetName() << " RRQ check failed: " "neither FixedUsername nor alias inside RRQ were found"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " RRQ check failed"); authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial; return GetDefaultStatus(); } // append User-Name pdu.AppendAttr(RadiusAttr::UserName, m_fixedUsername.IsEmpty() ? id : m_fixedUsername); if (username != NULL) *username = (const char*)id; // append User-Password if (!m_fixedPassword) pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedPassword); else pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedUsername.IsEmpty() ? id : m_fixedUsername); return e_ok; } int RadAliasAuth::AppendUsernameAndPassword( RadiusPDU & pdu, RasPDU & arqPdu, ARQAuthData & authData, PString * username ) const { const PString id = GetUsername(arqPdu, authData); if (id.IsEmpty() && m_fixedUsername.IsEmpty()) { PTRACE(3, "RADAUTH\t" << GetName() << " ARQ check failed: " "neither FixedUsername nor alias inside ARQ were found"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " ARQ check failed"); authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial; return GetDefaultStatus(); } // append User-Name pdu.AppendAttr(RadiusAttr::UserName, m_fixedUsername.IsEmpty() ? id : m_fixedUsername); if (username != NULL) *username = (const char*)id; if (!m_fixedPassword) pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedPassword); else pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedUsername.IsEmpty() ? id : m_fixedUsername); return e_ok; } int RadAliasAuth::AppendUsernameAndPassword( RadiusPDU & pdu, SetupMsg & setup, endptr& /* callingEP*/, SetupAuthData & authData, PString * username ) const { const PString id = GetUsername(setup, authData); if (id.IsEmpty() && m_fixedUsername.IsEmpty() && m_emptyUsername.IsEmpty()) { PTRACE(3, "RADAUTH\t" << GetName() << " Setup check failed: " "neither EmptyUsername nor FixedUsername nor alias inside Setup were found"); SNMP_TRAP(8, SNMPError, Authentication, GetName() + " Setup check failed"); authData.m_rejectReason = H225_ReleaseCompleteReason::e_badFormatAddress; return GetDefaultStatus(); } // append EmptyUsername if (id.IsEmpty() && !m_emptyUsername.IsEmpty()) { pdu.AppendAttr(RadiusAttr::UserName, m_emptyUsername); } // append User-Name pdu.AppendAttr(RadiusAttr::UserName, m_fixedUsername.IsEmpty() ? id : m_fixedUsername); if (username != NULL) *username = (const char*)id; if (!m_fixedPassword) pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedPassword); else pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedUsername.IsEmpty() ? id : m_fixedUsername); return e_ok; } namespace { GkAuthCreator RadAuthCreator("RadAuth"); GkAuthCreator RadAliasAuthCreator("RadAliasAuth"); } #endif /* HAS_RADIUS */ gnugk-3.4/PaxHeaders.16356/gkacct.cxx0000644000175000001440000000005012176205413015502 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gkacct.cxx0000644000175000001440000007421412176205413014453 0ustar00janusers00000000000000/* * gkacct.cxx * * Accounting modules for GNU Gatekeeper. Provides generic * support for accounting to the gatekeeper. * * Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz * Copyright (c) 2005-2013, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include #include #include "gk_const.h" #include "h323util.h" #include "stl_supp.h" #include "Toolkit.h" #include "gktimer.h" #include "snmp.h" #include "gkacct.h" using std::find; using std::vector; /// Name of the config file section for accounting configuration namespace { const char* GkAcctSectionName = "Gatekeeper::Acct"; } extern const char* CallTableSection; GkAcctLogger::GkAcctLogger( const char* moduleName, const char* cfgSecName ) : NamedObject(moduleName), m_controlFlag(Required), m_defaultStatus(Fail), m_enabledEvents(AcctAll), m_supportedEvents(AcctNone), m_config(GkConfig()), m_configSectionName(cfgSecName) { if (m_configSectionName.IsEmpty()) m_configSectionName = moduleName; const PStringArray control( m_config->GetString(GkAcctSectionName, moduleName, "").Tokenise(";,") ); if (control.GetSize() < 1) PTRACE(1, "GKACCT\tEmpty config entry for module " << moduleName); else if (strcasecmp(moduleName, "default") == 0) { m_controlFlag = Required; m_defaultStatus = Toolkit::AsBool(control[0]) ? Ok : Fail; m_supportedEvents = AcctAll; } else if (control[0] *= "optional") m_controlFlag = Optional; else if (control[0] *= "sufficient") m_controlFlag = Sufficient; else if (control[0] *= "alternative") m_controlFlag = Alternative; if (control.GetSize() > 1) m_enabledEvents = GetEvents(control); PTRACE(1, "GKACCT\tCreated module " << moduleName << " with event mask " << PString(PString::Unsigned, (long)m_enabledEvents, 16) ); } GkAcctLogger::~GkAcctLogger() { PTRACE(1, "GKACCT\tDestroyed module "< value) associations std::map& params, /// call (if any) associated with an accounting event being logged const callptr& call, /// timestamp formatting string const PString& timestampFormat ) const { PIPSocket::Address addr; WORD port = 0; time_t t; vector interfaces; Toolkit* const toolkit = Toolkit::Instance(); toolkit->GetGKHome(interfaces); params["g"] = toolkit->GKName(); params["n"] = PString(call->GetCallNumber()); params["u"] = GetUsername(call); params["d"] = call->GetDuration(); params["c"] = call->GetDisconnectCause(); params["cause-translated"] = call->GetDisconnectCauseTranslated(); params["s"] = call->GetAcctSessionId(); params["p"] = call->GetPostDialDelay(); params["r"] = call->GetReleaseSource(); params["t"] = call->GetTotalCallDuration(); if (interfaces.empty()) params["gkip"] = ""; else params["gkip"] = interfaces.front().AsString(); params["CallId"] = ::AsString(call->GetCallIdentifier().m_guid); params["ConfId"] = ::AsString(call->GetConferenceIdentifier()); params["CallLink"] = call->GetCallLinkage(); t = call->GetSetupTime(); if (t) params["setup-time"] = toolkit->AsString(PTime(t), timestampFormat); t = call->GetAlertingTime(); if (t) params["alerting-time"] = toolkit->AsString(PTime(t), timestampFormat); t = call->GetConnectTime(); if (t) params["connect-time"] = toolkit->AsString(PTime(t), timestampFormat); t = call->GetDisconnectTime(); if (t) params["disconnect-time"] = toolkit->AsString(PTime(t), timestampFormat); params["ring-time"] = call->GetRingTime(); if (call->GetSrcSignalAddr(addr, port)) { params["caller-ip"] = addr.AsString(); params["caller-port"] = port; } params["src-info"] = call->GetSrcInfo(); params["Calling-Station-Id"] = GetCallingStationId(call); addr = (DWORD)0; port = 0; if (call->GetDestSignalAddr(addr, port)) { params["callee-ip"] = addr.AsString(); params["callee-port"] = port; } params["dest-info"] = call->GetDestInfo(); params["Called-Station-Id"] = GetCalledStationId(call); params["Dialed-Number"] = GetDialedNumber(call); endptr caller; if ((caller = call->GetCallingParty())) { params["caller-epid"] = caller->GetEndpointIdentifier().GetValue(); } endptr callee; if ((callee = call->GetCalledParty())) { params["callee-epid"] = callee->GetEndpointIdentifier().GetValue(); } params["call-attempts"] = PString(call->GetNoCallAttempts()); params["last-cdr"] = call->GetNoRemainingRoutes() > 0 ? "0" : "1"; if ((call->GetMediaOriginatingIp(addr))) params["media-oip"] = addr.AsString(); params["codec"] = call->GetCodec(); params["bandwidth"] = call->GetBandwidth(); params["client-auth-id"] = call->GetClientAuthId(); PString vendor, version; call->GetCallingVendor(vendor, version); params["caller-vendor"] = vendor + " " + version; call->GetCalledVendor(vendor, version); params["callee-vendor"] = vendor + " " + version; } void GkAcctLogger::SetupAcctEndpointParams( /// parameter (name => value) associations std::map& params, /// endpoint associated with an accounting event being logged const endptr& ep ) const { PIPSocket::Address addr; WORD port = 0; H225_TransportAddress sigip = ep->GetCallSignalAddress(); if (GetIPAndPortFromTransportAddr(sigip, addr, port)) { params["endpoint-ip"] = addr.AsString(); params["endpoint-port"] = port; } PString aliasString = AsString(ep->GetAliases(), false); aliasString.Replace(PString("="), PString(","), true); // make list comma separated params["aliases"] = aliasString; // The username is always the last in the Alias List PStringArray aliasList = aliasString.Tokenise(","); if (aliasList.GetSize() > 0) params["u"] = aliasList[aliasList.GetSize()-1]; params["epid"] = ep->GetEndpointIdentifier().GetValue(); params["g"] = Toolkit::GKName(); } // avoid warning in PTLib object.h #if (!_WIN32) && (GCC_VERSION >= 40400) #pragma GCC diagnostic ignored "-Wstrict-overflow" #endif PString GkAcctLogger::ReplaceAcctParams( /// parametrized CDR string const PString& cdrStr, /// parameter values const std::map& params ) const { PString finalCDR((const char*)cdrStr); PINDEX len = finalCDR.GetLength(); PINDEX pos = 0; while (pos != P_MAX_INDEX && pos < len) { pos = finalCDR.Find('%', pos); if (pos++ == P_MAX_INDEX) break; if (pos >= len) // strings ending with '%' - special case break; const char c = finalCDR[pos]; // char next after '%' if (c == '%') { // replace %% with % finalCDR.Delete(pos, 1); len--; } else if (c == '{') { // escaped syntax (%{Name}) const PINDEX closingBrace = finalCDR.Find('}', ++pos); if (closingBrace != P_MAX_INDEX) { const PINDEX paramLen = closingBrace - pos; std::map::const_iterator i = params.find( finalCDR.Mid(pos, paramLen) ); if (i != params.end()) { const PINDEX escapedLen = EscapeAcctParam(i->second).GetLength(); finalCDR.Splice(EscapeAcctParam(i->second), pos - 2, paramLen + 3); len = len + escapedLen - paramLen - 3; pos = pos - 2 + escapedLen; } else { // replace out of range parameter with an empty string finalCDR.Delete(pos - 2, paramLen + 3); len -= paramLen + 3; pos -= 2; } } } else { // simple syntax (%c) std::map::const_iterator i = params.find(c); if (i != params.end()) { const PINDEX escapedLen = EscapeAcctParam(i->second).GetLength(); finalCDR.Splice(EscapeAcctParam(i->second), pos - 1, 2); len = len + escapedLen - 2; pos = pos - 1 + escapedLen; } else { // replace out of range parameter with an empty string finalCDR.Delete(pos - 1, 2); len -= 2; pos--; } } } return finalCDR; } PString GkAcctLogger::EscapeAcctParam(const PString& param) const { return param; // default implementation: don't escape anything } PString GkAcctLogger::GetUsername( /// call (if any) associated with the RAS message const callptr& call ) const { if (!call) return PString::Empty(); const endptr callingEP = call->GetCallingParty(); PString username; username = GetBestAliasAddressString(call->GetSourceAddress(), true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID) ); if (callingEP && (username.IsEmpty() || FindAlias(callingEP->GetAliases(), username) == P_MAX_INDEX)) username = GetBestAliasAddressString(callingEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID) ); if (username.IsEmpty()) username = GetBestAliasAddressString(call->GetSourceAddress(), false, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID) ); if (username.IsEmpty()) username = call->GetCallingStationId(); if (username.IsEmpty()) { PIPSocket::Address callingSigAddr; WORD callingSigPort; if (call->GetSrcSignalAddr(callingSigAddr, callingSigPort) && callingSigAddr.IsValid()) username = callingSigAddr.AsString(); } return username; } PString GkAcctLogger::GetCallingStationId( /// call associated with the accounting event const callptr& call ) const { if (!call) return PString::Empty(); PString id = call->GetCallingStationId(); if (!id) return id; if (id.IsEmpty()) id = GetBestAliasAddressString(call->GetSourceAddress(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty()) { const endptr callingEP = call->GetCallingParty(); if (callingEP) id = GetBestAliasAddressString(callingEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); } if (id.IsEmpty()) { PIPSocket::Address callingSigAddr; WORD callingSigPort = 0; if (call->GetSrcSignalAddr(callingSigAddr, callingSigPort) && callingSigAddr.IsValid()) id = ::AsString(callingSigAddr, callingSigPort); } return id; } PString GkAcctLogger::GetCalledStationId( /// call associated with the accounting event const callptr& call ) const { if (!call) return PString::Empty(); PString id = call->GetCalledStationId(); if (!id) return id; if (id.IsEmpty()) id = GetBestAliasAddressString(call->GetDestinationAddress(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty()) { const endptr calledEP = call->GetCalledParty(); if (calledEP) id = GetBestAliasAddressString(calledEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); } if (id.IsEmpty()) { PIPSocket::Address calledSigAddr; WORD calledSigPort = 0; if (call->GetDestSignalAddr(calledSigAddr, calledSigPort) && calledSigAddr.IsValid()) id = ::AsString(calledSigAddr, calledSigPort); } return id; } PString GkAcctLogger::GetDialedNumber( /// call associated with the accounting event const callptr& call ) const { if (!call) return PString::Empty(); PString id = call->GetDialedNumber(); if (!id) return id; if (id.IsEmpty()) id = GetBestAliasAddressString(call->GetDestinationAddress(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty()) { const endptr calledEP = call->GetCalledParty(); if (calledEP) id = GetBestAliasAddressString(calledEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); } if (id.IsEmpty()) { PIPSocket::Address calledSigAddr; WORD calledSigPort = 0; if (call->GetDestSignalAddr(calledSigAddr, calledSigPort) && calledSigAddr.IsValid()) id = ::AsString(calledSigAddr, calledSigPort); } return id; } PString GkAcctLogger::GetInfo() { return "No information available\r\n"; } const char* const FileAcct::m_intervalNames[] = { "Hourly", "Daily", "Weekly", "Monthly" }; FileAcct::FileAcct( const char* moduleName, const char* cfgSecName ) : GkAcctLogger(moduleName, cfgSecName), m_cdrFile(NULL), m_rotateLines(-1), m_rotateSize(-1), m_rotateInterval(-1), m_rotateMinute(-1), m_rotateHour(-1), m_rotateDay(-1), m_rotateTimer(GkTimerManager::INVALID_HANDLE), m_cdrLines(0), m_standardCDRFormat(true) { SetSupportedEvents(FileAcctEvents); m_cdrString = GetConfig()->GetString(GetConfigSectionName(), "CDRString", ""); m_standardCDRFormat = Toolkit::AsBool( GetConfig()->GetString(GetConfigSectionName(), "StandardCDRFormat", m_cdrString.IsEmpty() ? "1" : "0" )); m_timestampFormat = GetConfig()->GetString(GetConfigSectionName(), "TimestampFormat", "" ); // determine rotation type (by lines, by size, by time) const PString rotateCondition = GetConfig()->GetString( GetConfigSectionName(), "Rotate", "" ).Trim(); if (!rotateCondition) { const char suffix = rotateCondition[rotateCondition.GetLength()-1]; if (rotateCondition[0] == 'L' || rotateCondition[0] == 'l') { // rotate per number of lines m_rotateLines = rotateCondition.Mid(1).AsInteger(); if (suffix == 'k' || suffix == 'K') m_rotateLines *= 1000; else if (suffix == 'm' || suffix == 'M') m_rotateLines *= 1000*1000; } else if (rotateCondition[0] == 'S' || rotateCondition[0] == 's') { // rotate per CDR file size m_rotateSize = rotateCondition.Mid(1).AsInteger(); if (suffix == 'k' || suffix == 'K') m_rotateSize *= 1024; else if (suffix == 'm' || suffix == 'M') m_rotateSize *= 1024*1024; } else { for (int i = 0; i < RotationIntervalMax; i++) if (strcasecmp(rotateCondition, m_intervalNames[i]) == 0) m_rotateInterval = i; if (m_rotateInterval < 0 || m_rotateInterval >= RotationIntervalMax) PTRACE(1, "GKACCT\t" << GetName() << " unsupported rotation " "method: " << rotateCondition << " - rotation disabled" ); else { // time based rotation GetRotateInterval(*GetConfig(), GetConfigSectionName()); } } } m_cdrFilename = GetConfig()->GetString(GetConfigSectionName(), "DetailFile", ""); m_cdrFile = OpenCDRFile(m_cdrFilename); if (m_cdrFile && m_cdrFile->IsOpen()) { PTRACE(2, "GKACCT\t" << GetName() << " CDR file: " << m_cdrFile->GetFilePath() ); // count an initial number of CDR lines if (m_rotateLines > 0) { PString s; m_cdrFile->SetPosition(0); while (m_cdrFile->ReadLine(s)) m_cdrLines++; m_cdrFile->SetPosition(m_cdrFile->GetLength()); } } // setup rotation timer in case of time based rotation PTime now, rotateTime; switch (m_rotateInterval) { case Hourly: rotateTime = PTime(0, m_rotateMinute, now.GetHour(), now.GetDay(), now.GetMonth(), now.GetYear(), now.GetTimeZone() ); if (rotateTime <= now) rotateTime += PTimeInterval(0, 0, 0, 1); // 1 hour m_rotateTimer = Toolkit::Instance()->GetTimerManager()->RegisterTimer( this, &FileAcct::RotateOnTimer, rotateTime, 60*60 ); PTRACE(5, "GKACCT\t" << GetName() << " hourly rotation enabled (first " "rotation scheduled at " << rotateTime ); break; case Daily: rotateTime = PTime(0, m_rotateMinute, m_rotateHour, now.GetDay(), now.GetMonth(), now.GetYear(), now.GetTimeZone() ); if (rotateTime <= now) rotateTime += PTimeInterval(0, 0, 0, 0, 1); // 1 day m_rotateTimer = Toolkit::Instance()->GetTimerManager()->RegisterTimer( this, &FileAcct::RotateOnTimer, rotateTime, 60*60*24 ); PTRACE(5, "GKACCT\t" << GetName() << " daily rotation enabled (first " "rotation scheduled at " << rotateTime ); break; case Weekly: rotateTime = PTime(0, m_rotateMinute, m_rotateHour, now.GetDay(), now.GetMonth(), now.GetYear(), now.GetTimeZone() ); if (rotateTime.GetDayOfWeek() < m_rotateDay) rotateTime += PTimeInterval(0, 0, 0, 0, m_rotateDay - rotateTime.GetDayOfWeek() ); else if (rotateTime.GetDayOfWeek() > m_rotateDay) rotateTime -= PTimeInterval(0, 0, 0, 0, rotateTime.GetDayOfWeek() - m_rotateDay ); if (rotateTime <= now) rotateTime += PTimeInterval(0, 0, 0, 0, 7); // 1 week m_rotateTimer = Toolkit::Instance()->GetTimerManager()->RegisterTimer( this, &FileAcct::RotateOnTimer, rotateTime, 60*60*24*7 ); PTRACE(5, "GKACCT\t" << GetName() << " weekly rotation enabled (first " "rotation scheduled at " << rotateTime ); break; case Monthly: rotateTime = PTime(0, m_rotateMinute, m_rotateHour, 1, now.GetMonth(), now.GetYear(), now.GetTimeZone() ); rotateTime += PTimeInterval(0, 0, 0, 0, m_rotateDay - 1); while (rotateTime.GetMonth() != now.GetMonth()) rotateTime -= PTimeInterval(0, 0, 0, 0, 1); // 1 day if (rotateTime <= now) { rotateTime = PTime(0, m_rotateMinute, m_rotateHour, 1, now.GetMonth() + (now.GetMonth() == 12 ? -11 : 1), now.GetYear() + (now.GetMonth() == 12 ? 1 : 0), now.GetTimeZone() ); const int month = rotateTime.GetMonth(); rotateTime += PTimeInterval(0, 0, 0, 0, m_rotateDay - 1); while (rotateTime.GetMonth() != month) rotateTime -= PTimeInterval(0, 0, 0, 0, 1); // 1 day } m_rotateTimer = Toolkit::Instance()->GetTimerManager()->RegisterTimer( this, &FileAcct::RotateOnTimer, rotateTime ); PTRACE(5, "GKACCT\t" << GetName() << " monthly rotation enabled (first " "rotation scheduled at " << rotateTime ); break; } } FileAcct::~FileAcct() { if (m_rotateTimer != GkTimerManager::INVALID_HANDLE) Toolkit::Instance()->GetTimerManager()->UnregisterTimer(m_rotateTimer); PWaitAndSignal lock(m_cdrFileMutex); if (m_cdrFile) { m_cdrFile->Close(); delete m_cdrFile; } } void FileAcct::GetRotateInterval( PConfig& cfg, const PString& section ) { PString s; if (m_rotateInterval == Hourly) m_rotateMinute = cfg.GetInteger(section, "RotateTime", 59); else { s = cfg.GetString(section, "RotateTime", "00:59"); m_rotateHour = s.AsInteger(); m_rotateMinute = 0; if (s.Find(':') != P_MAX_INDEX) m_rotateMinute = s.Mid(s.Find(':') + 1).AsInteger(); if (m_rotateHour < 0 || m_rotateHour > 23 || m_rotateMinute < 0 || m_rotateMinute > 59) { PTRACE(1, "GKACCT\t" << GetName() << " invalid " "RotateTime specified: " << s ); m_rotateMinute = 59; m_rotateHour = 0; } } if (m_rotateInterval == Weekly) { s = cfg.GetString(section, "RotateDay", "Sun"); if (strspn(s, "0123456") == (size_t)s.GetLength()) { m_rotateDay = s.AsInteger(); } else { std::map dayNames; dayNames["sun"] = 0; dayNames["sunday"] = 0; dayNames["mon"] = 1; dayNames["monday"] = 1; dayNames["tue"] = 2; dayNames["tuesday"] = 2; dayNames["wed"] = 3; dayNames["wednesday"] = 3; dayNames["thu"] = 4; dayNames["thursday"] = 4; dayNames["fri"] = 5; dayNames["friday"] = 5; dayNames["sat"] = 6; dayNames["saturday"] = 6; std::map::const_iterator i = dayNames.find(s); m_rotateDay = (i != dayNames.end()) ? i->second : -1; } if (m_rotateDay < 0 || m_rotateDay > 6) { PTRACE(1, "GKACCT\t" << GetName() << " invalid " "RotateDay specified: " << s ); m_rotateDay = 0; } } else if (m_rotateInterval == Monthly) { m_rotateDay = cfg.GetInteger(section, "RotateDay", 1); if (m_rotateDay < 1 || m_rotateDay > 31) { PTRACE(1, "GKACCT\t" << GetName() << " invalid " "RotateDay specified: " << cfg.GetString(section, "RotateDay", "") ); m_rotateDay = 1; } } } GkAcctLogger::Status FileAcct::Log( GkAcctLogger::AcctEvent evt, const callptr& call ) { if ((evt & GetEnabledEvents() & GetSupportedEvents()) == 0) return Next; if (!call) { PTRACE(1, "GKACCT\t" << GetName() << " - missing call info for event " << evt); return Fail; } PString cdrString; if (!GetCDRText(cdrString, evt, call)) { PTRACE(2, "GKACCT\t" << GetName() << " - unable to get CDR text for " "event " << evt << ", call no. " << call->GetCallNumber() ); return Fail; } PWaitAndSignal lock(m_cdrFileMutex); if (m_cdrFile && m_cdrFile->IsOpen()) { if (m_cdrFile->WriteLine(PString(cdrString))) { PTRACE(5, "GKACCT\t" << GetName() << " - CDR string for event " << evt << ", call no. " << call->GetCallNumber() << ": " << cdrString ); m_cdrLines++; if (IsRotationNeeded()) Rotate(); return Ok; } else PTRACE(1, "GKACCT\t" << GetName() << " - write CDR text for event " << evt << ", call no. " << call->GetCallNumber() << " failed: " << m_cdrFile->GetErrorText()); } else PTRACE(1, "GKACCT\t" << GetName() << " - write CDR text for event " << evt << ", for call no. " << call->GetCallNumber() << " failed: CDR file is closed"); SNMP_TRAP(6, SNMPError, Accounting, GetName() + " failed"); return Fail; } bool FileAcct::GetCDRText( PString& cdrString, AcctEvent evt, const callptr& call ) { if ((evt & AcctStop) != AcctStop || !call) return false; if (m_standardCDRFormat) cdrString = call->GenerateCDR(m_timestampFormat); else { std::map params; SetupAcctParams(params, call, m_timestampFormat); cdrString = ReplaceAcctParams(m_cdrString, params); } return !cdrString; } bool FileAcct::IsRotationNeeded() { if (m_rotateLines > 0 && m_cdrLines >= m_rotateLines) return true; if (m_rotateSize > 0 && m_cdrFile && m_cdrFile->GetLength() >= m_rotateSize) return true; return false; } void FileAcct::RotateOnTimer( GkTimer* timer ) { if (m_rotateInterval == Monthly) { // setup next time for one-shot timer const PTime& rotateTime = timer->GetExpirationTime(); PTime newRotateTime(rotateTime.GetSecond(), rotateTime.GetMinute(), rotateTime.GetHour(), 1, rotateTime.GetMonth() < 12 ? rotateTime.GetMonth() + 1 : 1, rotateTime.GetMonth() < 12 ? rotateTime.GetYear() : rotateTime.GetYear() + 1, rotateTime.GetTimeZone() ); const int month = newRotateTime.GetMonth(); newRotateTime += PTimeInterval(0, 0, 0, 0, m_rotateDay - 1); while (newRotateTime.GetMonth() != month) newRotateTime -= PTimeInterval(0, 0, 0, 0, 1); timer->SetExpirationTime(newRotateTime); timer->SetFired(false); } PWaitAndSignal lock(m_cdrFileMutex); Rotate(); } void FileAcct::Rotate() { if (m_cdrFile) { if (m_cdrFile->IsOpen()) m_cdrFile->Close(); delete m_cdrFile; m_cdrFile = NULL; } const PFilePath fn = m_cdrFilename; if (PFile::Exists(fn)) { if (!PFile::Rename(fn, fn.GetFileName() + PTime().AsString(".yyyyMMdd-hhmmss"))) { PTRACE(1, "GKACCT\t" << GetName() << " rotate failed - could not " "rename the log file"); SNMP_TRAP(6, SNMPError, Accounting, GetName() + " failed"); } } m_cdrFile = OpenCDRFile(fn); m_cdrLines = 0; } PTextFile* FileAcct::OpenCDRFile( const PFilePath& fn ) { PTextFile* cdrFile = new PTextFile(fn, PFile::ReadWrite, PFile::Create | PFile::DenySharedWrite ); if (!cdrFile->IsOpen()) { PTRACE(1, "GKACCT\t" << GetName() << " could not open file" " required for plain text accounting \"" << fn << "\" :" << cdrFile->GetErrorText() ); delete cdrFile; return NULL; } cdrFile->SetPermissions(PFileInfo::UserRead | PFileInfo::UserWrite); cdrFile->SetPosition(cdrFile->GetLength()); return cdrFile; } GkAcctLoggerList::GkAcctLoggerList() : m_acctUpdateInterval( GkConfig()->GetInteger(CallTableSection, "AcctUpdateInterval", 0) ) { // should not be less than 10 seconds if (m_acctUpdateInterval) m_acctUpdateInterval = PMAX((long)10, m_acctUpdateInterval); } GkAcctLoggerList::~GkAcctLoggerList() { DeleteObjectsInContainer(m_loggers); m_loggers.clear(); } void GkAcctLoggerList::OnReload() { m_acctUpdateInterval = GkConfig()->GetInteger(CallTableSection, "AcctUpdateInterval", 0 ); // should not be less than 10 seconds if (m_acctUpdateInterval) m_acctUpdateInterval = PMAX((long)10, m_acctUpdateInterval); DeleteObjectsInContainer(m_loggers); m_loggers.clear(); const PStringArray modules = GkConfig()->GetKeys(GkAcctSectionName); for (PINDEX i = 0; i < modules.GetSize(); i++) { GkAcctLogger* logger = Factory::Create(modules[i]); if (logger) m_loggers.push_back(logger); } } bool GkAcctLoggerList::LogAcctEvent( GkAcctLogger::AcctEvent evt, /// the accounting event to be logged const callptr& call, /// a call associated with the event (if any) time_t now /// "now" timestamp for accounting update events ) { // if this is an accounting update, check the interval if (evt & GkAcctLogger::AcctUpdate) { if ((!call) || m_acctUpdateInterval == 0 || (now - call->GetLastAcctUpdateTime()) < m_acctUpdateInterval) { return true; } else { call->SetLastAcctUpdateTime(now); } } bool finalResult = true; GkAcctLogger::Status status = GkAcctLogger::Ok; std::list::const_iterator iter = m_loggers.begin(); while (iter != m_loggers.end()) { GkAcctLogger* logger = *iter++; if ((evt & logger->GetEnabledEvents() & logger->GetSupportedEvents()) == 0) continue; status = logger->Log(evt, call); switch (status) { case GkAcctLogger::Ok: if (PTrace::CanTrace(3)) { ostream& strm = PTrace::Begin(3,__FILE__,__LINE__); strm << "GKACCT\t" << logger->GetName() << " logged event " << evt; if (call) strm << " for call no. " << call->GetCallNumber(); PTrace::End(strm); } break; default: if (PTrace::CanTrace(3)) { ostream& strm = PTrace::Begin(3, __FILE__, __LINE__); strm << "GKACCT\t" << logger->GetName() << " failed to log event " << evt; SNMP_TRAP(7, SNMPError, Accounting, logger->GetName() + " failed"); if (call) strm << " for call no. " << call->GetCallNumber(); PTrace::End(strm); } // required and sufficient rules always determine // status of the request if (logger->GetControlFlag() == GkAcctLogger::Required || logger->GetControlFlag() == GkAcctLogger::Sufficient) finalResult = false; } // sufficient and alternative are terminal rules (on log success) if (status == GkAcctLogger::Ok && (logger->GetControlFlag() == GkAcctLogger::Sufficient || logger->GetControlFlag() == GkAcctLogger::Alternative)) break; } // a last rule determine status of the the request if (finalResult && status != GkAcctLogger::Ok) finalResult = false; if (PTrace::CanTrace(2)) { ostream& strm = PTrace::Begin(2, __FILE__, __LINE__); strm << "GKACCT\t" << (finalResult ? "Successfully logged event " : "Failed to log event ") << evt; if (call) strm << " for call no. " << call->GetCallNumber(); PTrace::End(strm); #ifdef HAS_SNMP if (!finalResult) SNMP_TRAP(7, SNMPError, Accounting, "Failed to log event " + PString(PString::Unsigned, evt)); #endif } return finalResult; } bool GkAcctLoggerList::LogAcctEvent( GkAcctLogger::AcctEvent evt, /// the accounting event to be logged const endptr& ep /// endpoint associated with the event ) { bool finalResult = true; GkAcctLogger::Status status = GkAcctLogger::Ok; std::list::const_iterator iter = m_loggers.begin(); while (iter != m_loggers.end()) { GkAcctLogger* logger = *iter++; if ((evt & logger->GetEnabledEvents() & logger->GetSupportedEvents()) == 0) continue; status = logger->Log(evt, ep); switch (status) { case GkAcctLogger::Ok: if (PTrace::CanTrace(3)) { ostream& strm = PTrace::Begin(3,__FILE__,__LINE__); strm << "GKACCT\t" << logger->GetName() << " logged event " << evt; if (ep) strm << " for endpoint " << ep->GetEndpointIdentifier().GetValue(); PTrace::End(strm); } break; default: if (PTrace::CanTrace(3)) { ostream& strm = PTrace::Begin(3, __FILE__, __LINE__); strm << "GKACCT\t" << logger->GetName() << " failed to log event " << evt; if (ep) strm << " for endpoint " << ep->GetEndpointIdentifier().GetValue(); PTrace::End(strm); SNMP_TRAP(7, SNMPError, Accounting, logger->GetName() + " failed to log event " + PString(evt)); } // required and sufficient rules always determine // status of the request if (logger->GetControlFlag() == GkAcctLogger::Required || logger->GetControlFlag() == GkAcctLogger::Sufficient) finalResult = false; } // sufficient and alternative are terminal rules (on log success) if (status == GkAcctLogger::Ok && (logger->GetControlFlag() == GkAcctLogger::Sufficient || logger->GetControlFlag() == GkAcctLogger::Alternative)) break; } // a last rule determine status of the the request if (finalResult && status != GkAcctLogger::Ok) finalResult = false; if (PTrace::CanTrace(2)) { ostream& strm = PTrace::Begin(2, __FILE__, __LINE__); strm << "GKACCT\t" << (finalResult ? "Successfully logged event " : "Failed to log event ") << evt; if (ep) strm << " for endpoint " << ep->GetEndpointIdentifier().GetValue(); PTrace::End(strm); #ifdef HAS_SNMP if (!finalResult) SNMP_TRAP(7, SNMPError, Accounting, "Failed to log event " + PString(PString::Unsigned, evt)); #endif } return finalResult; } namespace { GkAcctLoggerCreator DefaultAcctLoggerCreator("default"); GkAcctLoggerCreator FileAcctLoggerCreator("FileAcct"); } gnugk-3.4/PaxHeaders.16356/gksql.cxx0000644000175000001440000000005012075745273015402 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gksql.cxx0000644000175000001440000003346012075745273014351 0ustar00janusers00000000000000/* * gksql.cxx * * Generic interface to access SQL databases * * Copyright (c) 2004, Michal Zygmuntowicz * Copyright (c) 2006-2012, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include #include #include "stl_supp.h" #include "Toolkit.h" #include "gksql.h" using std::max; using std::min; namespace { const int GKSQL_DEFAULT_MIN_POOL_SIZE = 1; const int GKSQL_DEFAULT_MAX_POOL_SIZE = 1; const long GKSQL_CLEANUP_TIMEOUT = 5000; } GkSQLResult::~GkSQLResult() { } GkSQLConnection::GkSQLConnection( /// name to use in the log const char * name) : NamedObject(name), m_port(0), m_minPoolSize(GKSQL_DEFAULT_MIN_POOL_SIZE), m_maxPoolSize(GKSQL_DEFAULT_MAX_POOL_SIZE), m_destroying(false), m_connected(false) { } GkSQLConnection* GkSQLConnection::Create( const char * driverName, const char * connectionName) { return Factory::Create(driverName, connectionName); } bool GkSQLConnection::Initialize( /// config to be read PConfig * cfg, /// name of the config section with SQL settings const char * cfgSectionName) { PWaitAndSignal lock(m_connectionsMutex); if (!(cfg && cfgSectionName)) { PTRACE(1, GetName() << "\tInitialize failed: NULL config or config section not specified!"); SNMP_TRAP(4, SNMPError, Database, GetName() + " creation failed"); return false; } m_library = cfg->GetString(cfgSectionName, "Library", ""); GetHostAndPort(cfg->GetString(cfgSectionName, "Host", "localhost"), m_host, m_port); m_database = cfg->GetString(cfgSectionName, "Database", ""); m_username = cfg->GetString(cfgSectionName, "Username", ""); m_password = Toolkit::Instance()->ReadPassword(cfgSectionName, "Password"); m_minPoolSize = cfg->GetInteger(cfgSectionName, "MinPoolSize", GKSQL_DEFAULT_MIN_POOL_SIZE); m_minPoolSize = max(m_minPoolSize, 0); m_maxPoolSize = cfg->GetInteger(cfgSectionName, "MaxPoolSize", m_minPoolSize); if (m_maxPoolSize >= 0) m_maxPoolSize = max(m_minPoolSize, m_maxPoolSize); if (m_host.IsEmpty() || m_database.IsEmpty()) { PTRACE(1, GetName() << "\tInitialize failed: database name or host not specified!"); SNMP_TRAP(4, SNMPError, Database, GetName() + " creation failed"); return false; } return Connect(); } bool GkSQLConnection::Connect() { PWaitAndSignal lock(m_connectionsMutex); for (PINDEX i = m_idleConnections.size(); i < m_minPoolSize; ++i) { SQLConnPtr connptr = CreateNewConnection(i); if (connptr != NULL) m_idleConnections.push_back(connptr); } if (m_idleConnections.empty() && m_minPoolSize) { PTRACE(1, GetName() << "\tDatabase connection failed: " << m_username << '@' << m_host << '[' << m_database << ']'); SNMP_TRAP(4, SNMPError, Database, GetName() + " connection failed"); return false; } else { PTRACE(3, GetName() << "\tDatabase connection pool created: " << m_username << '@' << m_host << '[' << m_database << ']'); PTRACE(5, GetName() << "\tConnection pool: " << m_idleConnections.size() << " SQL connections created, " << (m_minPoolSize - m_idleConnections.size()) << " failed"); m_connected = true; return true; } } void GkSQLConnection::Disconnect() { PWaitAndSignal lock(m_connectionsMutex); // disconnect/delete all connections PTRACE(3, GetName() << "\tDisconnecting all SQL connections in pool"); for(iterator Iter = m_idleConnections.begin(); Iter != m_idleConnections.end(); ++Iter) { delete *Iter; } m_idleConnections.clear(); m_connected = false; } GkSQLConnection::~GkSQLConnection() { const PTime timeStart; m_destroying = true; // wakeup any waiting threads m_connectionAvailable.Signal(); // wait for still active connections (should not happen, but...) do { { PWaitAndSignal lock(m_connectionsMutex); m_waitingRequests.clear(); if (m_busyConnections.empty()) break; else PTRACE(2, GetName() << "\tActive connections (" << m_busyConnections.size() << ") during cleanup - sleeping 250ms"); } PThread::Sleep(250); } while ((PTime()-timeStart).GetMilliSeconds() < GKSQL_CLEANUP_TIMEOUT); // close connections from the idle list and leave any on the busy list // busy list should be empty at this moment PWaitAndSignal lock(m_connectionsMutex); m_waitingRequests.clear(); iterator iter = m_idleConnections.begin(); iterator end = m_idleConnections.end(); while (iter != end) { PTRACE(5, GetName() << "\tDatabase connection (id " << (*iter)->m_id << ") closed"); delete *iter++; } m_idleConnections.clear(); PTRACE(5, GetName() << "\tConnection pool cleanup finished"); if (!m_busyConnections.empty()) { PTRACE(1, GetName() << "\tConnection cleanup finished with " << m_busyConnections.size() << " active connections"); } } bool GkSQLConnection::AcquireSQLConnection( SQLConnPtr& connptr, long timeout ) { if (m_destroying) return false; if (!m_connected) { PTRACE(2, GetName() << "\tAttempting to reconnect to the database"); Disconnect(); if (!Connect()) { PTRACE(2, GetName() << "\tFailed to reconnect to the database"); SNMP_TRAP(5, SNMPError, Database, GetName() + " connection failed"); return false; } } const PTime timeStart; connptr = NULL; bool waiting = false; // wait for an idle connection or timeout do { if (!waiting) { PWaitAndSignal lock(m_connectionsMutex); if (m_destroying) break; // grab an idle connection if available or add itself // to the list of waiting requests if (!m_idleConnections.empty()) { connptr = m_idleConnections.front(); m_idleConnections.pop_front(); m_busyConnections.push_front(connptr); } else { m_waitingRequests.push_back(&connptr); waiting = true; } } if (connptr == NULL && timeout != 0 && !m_destroying) m_connectionAvailable.Wait(min(250L, timeout)); if (connptr == NULL && timeout >= 0) if ((PTime()-timeStart).GetMilliSeconds() >= timeout) break; } while (connptr == NULL && !m_destroying); if (connptr == NULL || m_destroying) { PWaitAndSignal lock(m_connectionsMutex); m_waitingRequests.remove(&connptr); if (connptr) { m_idleConnections.push_back(connptr); m_busyConnections.remove(connptr); } PTRACE(2, GetName() << "\tQuery timed out waiting for idle connection"); return false; } return connptr != NULL; } void GkSQLConnection::ReleaseSQLConnection( SQLConnPtr& connptr, bool deleteFromPool ) { if (connptr == NULL) return; // mark the connection as idle or give it to the first waiting request { PWaitAndSignal lock(m_connectionsMutex); // remove itself from the list of waiting requests m_waitingRequests.remove(&connptr); witerator iter = m_waitingRequests.begin(); witerator end = m_waitingRequests.end(); // find a waiting requests that has not been given a connection yet while (iter != end) { // check if SQLConnPtr* is not NULL and if SQLConnPtr is empty (NULL) if (*iter && *(*iter) == NULL) break; iter++; } if (iter != end && !m_destroying && !deleteFromPool) { // do not remove itself from the list of busy connections // just move the connection to the waiting request *(*iter) = connptr; } else { // move the connection to the list of idle connections m_busyConnections.remove(connptr); if (deleteFromPool) delete connptr; else m_idleConnections.push_back(connptr); } connptr = NULL; } // wake up any threads waiting for an idle connection if (!deleteFromPool) m_connectionAvailable.Signal(); } GkSQLResult* GkSQLConnection::ExecuteQuery( const char* queryStr, const PStringArray* queryParams, long timeout ) { SQLConnPtr connptr; if (AcquireSQLConnection(connptr, timeout)) { GkSQLResult* result = NULL; if (queryParams) { const PString finalQueryStr = ReplaceQueryParams(connptr, queryStr, *queryParams); PTRACE(5, GetName() << "\tExecuting query: " << finalQueryStr); result = ExecuteQuery(connptr, finalQueryStr, timeout); } else { PTRACE(5, GetName() << "\tExecuting query: " << queryStr); result = ExecuteQuery(connptr, queryStr, timeout); } ReleaseSQLConnection(connptr, !m_connected); return result; } else { PTRACE(2, GetName() << "\tQuery failed - no idle connection in the pool"); SNMP_TRAP(5, SNMPError, Database, GetName() + " query failed"); return NULL; } } GkSQLResult* GkSQLConnection::ExecuteQuery( const char* queryStr, const std::map& queryParams, long timeout ) { SQLConnPtr connptr; if (AcquireSQLConnection(connptr, timeout)) { GkSQLResult* result = NULL; if (queryParams.empty()) { PTRACE(5, GetName() << "\tExecuting query: " << queryStr); result = ExecuteQuery(connptr, queryStr, timeout); } else { const PString finalQueryStr = ReplaceQueryParams(connptr, queryStr, queryParams); PTRACE(5, GetName() << "\tExecuting query: " << finalQueryStr); result = ExecuteQuery(connptr, finalQueryStr, timeout); } ReleaseSQLConnection(connptr, !m_connected); return result; } else { PTRACE(2, GetName() << "\tQuery failed - no idle connection in the pool"); SNMP_TRAP(5, SNMPError, Database, GetName() + " query failed"); return NULL; } } PString GkSQLConnection::ReplaceQueryParams( /// SQL connection to get escape parameters from GkSQLConnection::SQLConnPtr conn, /// parametrized query string const char* queryStr, /// parameter values const PStringArray& queryParams ) { const PINDEX numParams = queryParams.GetSize(); PString finalQuery(queryStr); PINDEX queryLen = finalQuery.GetLength(); PINDEX pos = 0; char* endChar; while (pos != P_MAX_INDEX && pos < queryLen) { pos = finalQuery.Find('%', pos); if (pos++ == P_MAX_INDEX) break; if (pos >= queryLen) // strings ending with '%' - special case break; const char c = finalQuery[pos]; // char next after '%' if (c == '%') { // replace %% with % finalQuery.Delete(pos, 1); queryLen--; } else if (c >= '0' && c <= '9') { // simple syntax (%1) const long paramNo = strtol((const char*)finalQuery + pos, &endChar, 10); const long paramLen = endChar - (const char*)finalQuery - pos; if (paramNo >= 1 && paramNo <= numParams) { const PString escapedStr = EscapeString(conn, queryParams[paramNo-1]); const PINDEX escapedLen = escapedStr.GetLength(); finalQuery.Splice(escapedStr, pos - 1, paramLen + 1); queryLen = queryLen + escapedLen - paramLen - 1; pos = pos - 1 + escapedLen; } else if (paramNo && paramLen) { // replace out of range parameter with an empty string finalQuery.Delete(pos - 1, paramLen + 1); queryLen -= paramLen + 1; pos--; } } else if (c == '{') { // escaped syntax (%{1}) const PINDEX closingBrace = finalQuery.Find('}', ++pos); if (closingBrace != P_MAX_INDEX) { const long paramNo = strtol((const char*)finalQuery + pos, &endChar, 10); const long paramLen = endChar - (const char*)finalQuery - pos; if (*endChar == '}' && paramNo >= 1 && paramNo <= numParams) { const PString escapedStr = EscapeString(conn, queryParams[paramNo-1]); const PINDEX escapedLen = escapedStr.GetLength(); finalQuery.Splice(escapedStr, pos - 2, paramLen + 3); queryLen = queryLen + escapedLen - paramLen - 3; pos = pos - 2 + escapedLen; } else if (paramNo && paramLen) { // replace out of range parameter with an empty string finalQuery.Delete(pos - 2, paramLen + 3); queryLen -= paramLen + 3; pos -= 2; } } } } return finalQuery; } PString GkSQLConnection::ReplaceQueryParams( /// SQL connection to get escape parameters from GkSQLConnection::SQLConnPtr conn, /// parametrized query string const char* queryStr, /// parameter values const std::map& queryParams ) { PString finalQuery(queryStr); PINDEX queryLen = finalQuery.GetLength(); PINDEX pos = 0; while (pos != P_MAX_INDEX && pos < queryLen) { pos = finalQuery.Find('%', pos); if (pos++ == P_MAX_INDEX) break; if (pos >= queryLen) // strings ending with '%' - special case break; const char c = finalQuery[pos]; // char next after '%' if (c == '%') { // replace %% with % finalQuery.Delete(pos, 1); queryLen--; } else if (c == '{') { // escaped syntax (%{Name}) const PINDEX closingBrace = finalQuery.Find('}', ++pos); if (closingBrace != P_MAX_INDEX) { const PINDEX paramLen = closingBrace - pos; std::map::const_iterator i = queryParams.find( finalQuery.Mid(pos, paramLen) ); if (i != queryParams.end()) { const PString escapedStr = EscapeString(conn, i->second); const PINDEX escapedLen = escapedStr.GetLength(); finalQuery.Splice(escapedStr, pos - 2, paramLen + 3); queryLen = queryLen + escapedLen - paramLen - 3; pos = pos - 2 + escapedLen; } else { // replace out of range parameter with an empty string finalQuery.Delete(pos - 2, paramLen + 3); queryLen -= paramLen + 3; pos -= 2; } } } else { // simple syntax (%1) std::map::const_iterator i = queryParams.find(c); if (i != queryParams.end()) { const PString escapedStr = EscapeString(conn, i->second); const PINDEX escapedLen = escapedStr.GetLength(); finalQuery.Splice(escapedStr, pos - 1, 2); queryLen = queryLen + escapedLen - 2; pos = pos - 1 + escapedLen; } else { // replace out of range parameter with an empty string finalQuery.Delete(pos - 1, 2); queryLen -= 2; pos--; } } } return finalQuery; } void GkSQLConnection::GetInfo( Info &info /// filled with SQL connection state information upon return ) { PWaitAndSignal lock(m_connectionsMutex); info.m_connected = m_connected; info.m_minPoolSize = m_minPoolSize; info.m_maxPoolSize = m_maxPoolSize; info.m_idleConnections = m_idleConnections.size(); info.m_busyConnections = m_busyConnections.size(); info.m_waitingRequests = m_waitingRequests.size(); } GkSQLConnection::SQLConnWrapper::~SQLConnWrapper() { } gnugk-3.4/PaxHeaders.16356/resource.h0000644000175000001440000000005011411167202015514 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/resource.h0000644000175000001440000000070211411167202014454 0ustar00janusers00000000000000//{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by gk.rc // #define IDI_ICON_GNUGK 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif gnugk-3.4/PaxHeaders.16356/SoftPBX.h0000644000175000001440000000005011647351203015161 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/SoftPBX.h0000644000175000001440000000435311647351203014127 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // SoftPBX.h // // Copyright (c) 2000-2011, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef SOFTPBX_H #define SOFTPBX_H "@(#) $Id: SoftPBX.h,v 1.28 2011/10/18 19:24:51 willamowius Exp $" // nothing to include :) class PTime; class PString; class USocket; class EndpointRec; class CallRec; template class SmartPtr; typedef SmartPtr endptr; namespace SoftPBX { void PrintEndpoint(const PString & EpStr, USocket *client, bool verbose); void PrintAllRegistrations(USocket *client, bool verbose=false); void PrintAllCached(USocket *client, bool verbose=false); void PrintRemoved(USocket *client, bool verbose=false); void PrintCurrentCalls(USocket *client, bool verbose=false); void PrintCurrentCallsPorts(USocket *client); void PrintStatistics(USocket *client, bool verbose=false); void ResetCallCounters(USocket *client); void UnregisterAllEndpoints(); void UnregisterAlias(const PString & Alias); void UnregisterIp(const PString & Ip); void DisconnectAll(); void DisconnectCall(unsigned CallNumber); void DisconnectCallId(const PString & CallId); void DisconnectIp(const PString & Ip); void DisconnectAlias(const PString & Alias); void DisconnectEndpoint(const PString & Id); void DisconnectEndpoint(const endptr &); void SendProceeding(const PString & CallId); void TransferCall(const PString & SourceAlias, const PString & DestinationAlias); void TransferCall(const PString & CallId, const PCaselessString & which, const PString & Destination, const PString & method); void MakeCall(const PString & SourceAlias, const PString & DestinationAlias); void RerouteCall(const PString & CallId, const PCaselessString & whichLeg, const PString & destination); void PrintPrefixCapacities(USocket *client, const PString & alias); void PrintCapacityControlRules(USocket *client); void PrintEndpointQoS(USocket *client); PString Uptime(); extern int TimeToLive; extern PTime StartUp; } #endif // SOFTPBX_H gnugk-3.4/PaxHeaders.16356/gkauth.cxx0000644000175000001440000000005012211166636015534 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gkauth.cxx0000644000175000001440000016534012211166636014506 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // gkauth.cxx // // Copyright (c) 2001-2013, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #include #include #include #include #include "gk_const.h" #include "h323util.h" #include "stl_supp.h" #include "Toolkit.h" #include "RasTbl.h" #include "RasPDU.h" #include "sigmsg.h" #include "Routing.h" #include "gkauth.h" #include "config.h" #if H323_H350 extern const char *H350Section; #include #include "h350/h350.h" #endif namespace { const char* const GkAuthSectionName = "Gatekeeper::Auth"; const char OID_CAT[] = "1.2.840.113548.10.1.2.1"; } using std::stable_sort; using std::for_each; using std::find_if; using std::greater; ARQAuthData::ARQAuthData( /// an endpoint requesting admission const endptr & ep, /// call record matching this ARQ (if any) const callptr & call ) : m_rejectReason(-1), m_callDurationLimit(-1), m_requestingEP(ep), m_call(call), m_billingMode(-1), m_proxyMode(CallRec::ProxyDetect), m_clientAuthId(0) { } ARQAuthData::ARQAuthData(const ARQAuthData & obj) : m_rejectReason(obj.m_rejectReason), m_callDurationLimit(obj.m_callDurationLimit), m_requestingEP(obj.m_requestingEP), m_call(obj.m_call), m_billingMode(obj.m_billingMode), m_routeToAlias(obj.m_routeToAlias), m_destinationRoutes(obj.m_destinationRoutes), m_proxyMode(obj.m_proxyMode), m_clientAuthId(0) { } ARQAuthData& ARQAuthData::operator=(const ARQAuthData & obj) { if (this != &obj) { m_rejectReason = obj.m_rejectReason; m_callDurationLimit = obj.m_callDurationLimit; m_requestingEP = obj.m_requestingEP; m_call = obj.m_call; m_billingMode = obj.m_billingMode; m_proxyMode = obj.m_proxyMode; m_clientAuthId = obj.m_clientAuthId; m_routeToAlias = obj.m_routeToAlias; m_destinationRoutes = obj.m_destinationRoutes; } return *this; } void ARQAuthData::SetRouteToAlias(const H225_ArrayOf_AliasAddress & alias) { m_routeToAlias = alias; } void ARQAuthData::SetRouteToAlias(const PString& alias, int tag) { m_routeToAlias.SetSize(1); H323SetAliasAddress(alias, m_routeToAlias[0], tag); } SetupAuthData::SetupAuthData( /// call associated with the message (if any) const callptr& call, /// is the Setup message from a registered endpoint bool fromRegistered, /// did the Setup come in over TLS bool overTLS ) : m_rejectReason(-1), m_rejectCause(-1), m_callDurationLimit(-1), m_call(call), m_fromRegistered(fromRegistered), m_proxyMode(CallRec::ProxyDetect), m_clientAuthId(0), m_overTLS(overTLS) { } SetupAuthData::SetupAuthData(const SetupAuthData & obj) : m_rejectReason(obj.m_rejectReason), m_rejectCause(obj.m_rejectCause), m_callDurationLimit(obj.m_callDurationLimit), m_call(obj.m_call), m_fromRegistered(obj.m_fromRegistered), m_routeToAlias(obj.m_routeToAlias), m_destinationRoutes(obj.m_destinationRoutes), m_proxyMode(obj.m_proxyMode), m_clientAuthId(0), m_overTLS(false) { } SetupAuthData& SetupAuthData::operator=(const SetupAuthData& obj) { if (this != &obj) { m_rejectReason = obj.m_rejectReason; m_rejectCause = obj.m_rejectCause; m_callDurationLimit = obj.m_callDurationLimit; m_call = obj.m_call; m_fromRegistered = obj.m_fromRegistered; m_proxyMode = obj.m_proxyMode; m_clientAuthId = obj.m_clientAuthId; m_routeToAlias = obj.m_routeToAlias; m_destinationRoutes = obj.m_destinationRoutes; m_overTLS = obj.m_overTLS; } return *this; } SetupAuthData::~SetupAuthData() { } void SetupAuthData::SetRouteToAlias(const H225_ArrayOf_AliasAddress & alias) { m_routeToAlias = alias; } void SetupAuthData::SetRouteToAlias(const PString& alias, int tag) { m_routeToAlias.SetSize(1); H323SetAliasAddress(alias, m_routeToAlias[0], tag); } // class GkAuthenticator GkAuthenticator::GkAuthenticator( const char* name, /// a name for the module (to be used in the config file) unsigned supportedRasChecks, /// RAS checks supported by this module unsigned supportedMiscChecks /// non-RAS checks supported by this module ) : NamedObject(name), m_defaultStatus(e_fail), m_controlFlag(e_Required), m_enabledRasChecks(~0U), m_supportedRasChecks(supportedRasChecks), m_enabledMiscChecks(~0U), m_supportedMiscChecks(supportedMiscChecks), m_config(GkConfig()) { const PStringArray control( m_config->GetString(GkAuthSectionName, name, "").Tokenise(";,") ); if (control.GetSize() > 0) { const PString controlStr = control[0].Trim(); if (strcasecmp(name, "default") == 0) m_controlFlag = e_Sufficient, m_defaultStatus = Toolkit::AsBool(controlStr) ? e_ok : e_fail; else if (controlStr *= "optional") m_controlFlag = e_Optional, m_defaultStatus = e_next; else if (controlStr *= "required") m_controlFlag = e_Required, m_defaultStatus = e_fail; else if (controlStr *= "sufficient") m_controlFlag = e_Sufficient, m_defaultStatus = e_fail; else if (controlStr *= "alternative") m_controlFlag = e_Alternative, m_defaultStatus = e_next; else PTRACE(1, "GKAUTH\tInvalid control flag '" << controlStr << "' specified in the config for " << GetName()); } else PTRACE(1, "GKAUTH\tNo control flag specified in the config for module '" << GetName() << '\''); std::map rasmap; rasmap["GRQ"] = RasInfo::flag, rasmap["RRQ"] = RasInfo::flag, rasmap["URQ"] = RasInfo::flag, rasmap["ARQ"] = RasInfo::flag, rasmap["BRQ"] = RasInfo::flag, rasmap["DRQ"] = RasInfo::flag, rasmap["LRQ"] = RasInfo::flag, rasmap["IRQ"] = RasInfo::flag; std::map miscmap; miscmap["SETUP"] = e_Setup; miscmap["SETUPUNREG"] = e_SetupUnreg; if (control.GetSize() > 1) { m_enabledRasChecks = 0; m_enabledMiscChecks = 0; for (PINDEX i = 1; i < control.GetSize(); ++i) { const PString checkStr = control[i].Trim().ToUpper(); if (rasmap.find(checkStr) != rasmap.end()) { m_enabledRasChecks |= rasmap[checkStr]; if ((m_supportedRasChecks & rasmap[checkStr]) != rasmap[checkStr]) { PTRACE(1, "GKAUTH\t" << GetName() << " does not support '" << control[i] << "' check"); } } else if(miscmap.find(checkStr) != miscmap.end()) { m_enabledMiscChecks |= miscmap[checkStr]; if ((m_supportedMiscChecks & miscmap[checkStr]) != miscmap[checkStr]) { PTRACE(1, "GKAUTH\t" << GetName() << " does not support '" << control[i] << "' check"); } } else { PTRACE(1, "GKAUTH\tInvalid check flag '" << control[i] << "' specified in the config for " << GetName()); } } if ((m_enabledRasChecks & m_supportedRasChecks) == 0 && (m_enabledMiscChecks & m_supportedMiscChecks) == 0) { PTRACE(1, "GKAUTH\tNo check flags have been specified " "in the config for " << GetName() << " - it will be disabled"); } } // convert bit flags to human readable names PString rasFlagsStr, miscFlagsStr; std::map::const_iterator iter = rasmap.begin(); while (iter != rasmap.end()) { if (m_enabledRasChecks & iter->second) { if (!rasFlagsStr) rasFlagsStr += ' '; rasFlagsStr += iter->first; } iter++; } iter = miscmap.begin(); while (iter != miscmap.end()) { if (m_enabledMiscChecks & iter->second) { if (!miscFlagsStr) miscFlagsStr += ' '; miscFlagsStr += iter->first; } iter++; } if (rasFlagsStr.IsEmpty()) rasFlagsStr = "NONE"; if (miscFlagsStr.IsEmpty()) miscFlagsStr = "NONE"; PTRACE(1, "GKAUTH\t" << GetName() << " rule added to check RAS: " << rasFlagsStr << ", OTHER: " << miscFlagsStr); m_h235Authenticators = NULL; } GkAuthenticator::~GkAuthenticator() { delete m_h235Authenticators; PTRACE(1, "GKAUTH\t" << GetName() << " rule removed"); } int GkAuthenticator::Check(RasPDU &, unsigned &) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check( /// a request to be authenticated RasPDU & /*request*/, /// authorization data (reject reason, ...) RRQAuthData & /*authData*/) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check(RasPDU &, unsigned &) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check( /// a request to be authenticated RasPDU & /*req*/, /// authorization data (call duration limit, reject reason, ...) ARQAuthData & /*authData*/) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check(RasPDU &, unsigned &) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check(RasPDU &, unsigned &) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check(RasPDU &, unsigned &) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check(RasPDU &, unsigned &) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check( SetupMsg & /*setup*/, /// authorization data (call duration limit, reject reason, ...) SetupAuthData & /*authData*/) { return (IsMiscCheckEnabled(e_Setup) || IsMiscCheckEnabled(e_SetupUnreg)) ? m_defaultStatus : e_next; } // TODO: unused ? remove ? bool GkAuthenticator::GetH235Capability( /// append supported authentication mechanism to this array H225_ArrayOf_AuthenticationMechanism & mechanisms, /// append supported algorithm OIDs for the given authentication /// mechanism H225_ArrayOf_PASN_ObjectId & algorithmOIDs ) const { if (m_h235Authenticators && m_h235Authenticators->GetSize() > 0) { for (PINDEX i = 0; i < m_h235Authenticators->GetSize(); i++) (*m_h235Authenticators)[i].SetCapability(mechanisms, algorithmOIDs); return true; } else return false; } bool GkAuthenticator::IsH235Capability( /// authentication mechanism const H235_AuthenticationMechanism & mechanism, /// algorithm OID for the given authentication mechanism const PASN_ObjectId & algorithmOID ) const { if (m_h235Authenticators) { for (PINDEX i = 0; i < m_h235Authenticators->GetSize(); i++) { if ((*m_h235Authenticators)[i].IsCapability(mechanism, algorithmOID)) { return true; } } } return false; } bool GkAuthenticator::IsH235Capable() const { return m_h235Authenticators && m_h235Authenticators->GetSize() > 0; } void GkAuthenticator::AppendH235Authenticator( H235Authenticator* h235Auth /// H.235 authenticator to append ) { if (h235Auth) { if (m_h235Authenticators == NULL) m_h235Authenticators = new H235Authenticators(); m_h235Authenticators->Append(h235Auth); } } PString GkAuthenticator::GetUsername( /// RRQ message with additional data const RasPDU& request ) const { const H225_RegistrationRequest& rrq = request; PString username; if (rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) username = GetBestAliasAddressString(rrq.m_terminalAlias, false, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID)); if (username.IsEmpty()) { PIPSocket::Address addr; if (rrq.m_callSignalAddress.GetSize() > 0 && GetIPFromTransportAddr(rrq.m_callSignalAddress[0], addr) && addr.IsValid()) username = addr.AsString(); else if (rrq.m_rasAddress.GetSize() > 0 && GetIPFromTransportAddr(rrq.m_rasAddress[0], addr) && addr.IsValid()) username = addr.AsString(); } return username; } PString GkAuthenticator::GetUsername( /// ARQ message with additional data const RasPDU & request, /// additional data ARQAuthData & authData ) const { const H225_AdmissionRequest& arq = request; const bool hasCall = authData.m_call.operator->() != NULL; PString username; /// try to find h323_ID, email_ID or url_ID to use for User-Name if (!arq.m_answerCall) username = GetBestAliasAddressString(arq.m_srcInfo, true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID) ); else if (hasCall) username = GetBestAliasAddressString(authData.m_call->GetSourceAddress(), true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID)); if (authData.m_requestingEP && (username.IsEmpty() || FindAlias(authData.m_requestingEP->GetAliases(), username) == P_MAX_INDEX)) username = GetBestAliasAddressString(authData.m_requestingEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID)); /// if no h323_ID, email_ID or url_ID has been found, try to find any alias if (username.IsEmpty()) { if (!arq.m_answerCall) { username = GetBestAliasAddressString(arq.m_srcInfo, false, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID)); } else if (hasCall) { username = GetBestAliasAddressString( authData.m_call->GetSourceAddress(), true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID)); } } if (username.IsEmpty()) { PIPSocket::Address addr; if (arq.HasOptionalField(H225_AdmissionRequest::e_srcCallSignalAddress) && GetIPFromTransportAddr(arq.m_srcCallSignalAddress, addr) && addr.IsValid()) username = addr.AsString(); else if (authData.m_requestingEP && GetIPFromTransportAddr(authData.m_requestingEP->GetCallSignalAddress(), addr) && addr.IsValid()) username = addr.AsString(); } return username; } PString GkAuthenticator::GetUsername( const SetupMsg & setup, /// additional data SetupAuthData & authData ) const { const bool hasCall = authData.m_call.operator->() != NULL; PString username; endptr callingEP; Q931& q931pdu = setup.GetQ931(); H225_Setup_UUIE &setupBody = setup.GetUUIEBody(); if (hasCall) callingEP = authData.m_call->GetCallingParty(); if (setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) { username = GetBestAliasAddressString(setupBody.m_sourceAddress, true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID) ); if (!username && callingEP && FindAlias(callingEP->GetAliases(), username) == P_MAX_INDEX) username = PString::Empty(); } if (username.IsEmpty() && hasCall) { username = GetBestAliasAddressString( authData.m_call->GetSourceAddress(), true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID)); if (!username && callingEP && FindAlias(callingEP->GetAliases(), username) == P_MAX_INDEX) username = PString::Empty(); } if (username.IsEmpty() && callingEP) username = GetBestAliasAddressString(callingEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID)); if (username.IsEmpty() && hasCall) username = GetBestAliasAddressString( authData.m_call->GetSourceAddress(), false, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID)); if (username.IsEmpty() && setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) username = GetBestAliasAddressString(setupBody.m_sourceAddress, false, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID)); if (username.IsEmpty()) q931pdu.GetCallingPartyNumber(username); if (username.IsEmpty()) { PIPSocket::Address addr(0); WORD port = 0; bool addrValid = false; if (hasCall) addrValid = authData.m_call->GetSrcSignalAddr(addr, port) && addr.IsValid(); if (!addrValid && setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceCallSignalAddress)) addrValid = GetIPFromTransportAddr(setupBody.m_sourceCallSignalAddress, addr) && addr.IsValid(); if (!addrValid && callingEP) addrValid = GetIPFromTransportAddr(callingEP->GetCallSignalAddress(), addr) && addr.IsValid(); if (addrValid) username = addr.AsString(); } return username; } PString GkAuthenticator::GetCallingStationId( /// ARQ message with additional data const RasPDU & /*request*/, /// additional data ARQAuthData & authData ) const { return authData.m_callingStationId; } PString GkAuthenticator::GetCallingStationId( const SetupMsg & /*setup*/, /// additional data SetupAuthData & authData ) const { return authData.m_callingStationId; } PString GkAuthenticator::GetCalledStationId( /// ARQ message with additional data const RasPDU & /*request*/, /// additional data ARQAuthData & authData ) const { return authData.m_calledStationId; } PString GkAuthenticator::GetCalledStationId( const SetupMsg & /*setup*/, /// additional data SetupAuthData & authData ) const { return authData.m_calledStationId; } PString GkAuthenticator::GetDialedNumber( /// ARQ message with additional data const RasPDU & /*request*/, /// additional data ARQAuthData & authData ) const { return authData.m_dialedNumber; } PString GkAuthenticator::GetDialedNumber( const SetupMsg & /*setup*/, /// additional data SetupAuthData & authData ) const { return authData.m_dialedNumber; } PString GkAuthenticator::GetInfo() { return "No information available\r\n"; } // class GkAuthenticatorList GkAuthenticatorList::GkAuthenticatorList() { // TODO: should we move this into OnReload() so it can be dynamically changed ? PFactory::KeyList_T keyList = PFactory::GetKeyList(); PFactory::KeyList_T::const_iterator r; // if a global list of autenticators is configured, use it in the priority order supplied PStringList authlist = Toolkit::Instance()->GetAuthenticatorList(); if (authlist.GetSize() > 0) { for (PINDEX i = 0; i < authlist.GetSize(); ++i) { for (r = keyList.begin(); r != keyList.end(); ++r) { H235Authenticator * Auth = PFactory::CreateInstance(*r); if (PString(Auth->GetName()) == authlist[i]) { m_h235authenticators.Append(Auth); } else { delete Auth; } } } } else { for (r = keyList.begin(); r != keyList.end(); ++r) { H235Authenticator * Auth = PFactory::CreateInstance(*r); if ((Auth->GetApplication() == H235Authenticator::EPAuthentication) ||(Auth->GetApplication() == H235Authenticator::GKAdmission) ||(Auth->GetApplication() == H235Authenticator::AnyApplication) ) { m_h235authenticators.Append(Auth); } else { delete Auth; } } } } GkAuthenticatorList::~GkAuthenticatorList() { WriteLock lock(m_reloadMutex); DeleteObjectsInContainer(m_authenticators); m_authenticators.clear(); } void GkAuthenticatorList::OnReload() { // lock here to prevent too early authenticator destruction // from another thread WriteLock lock(m_reloadMutex); // first destroy old authenticators DeleteObjectsInContainer(m_authenticators); m_authenticators.clear(); std::list authenticators; GkAuthenticator *auth; const PStringArray authRules = GkConfig()->GetKeys(GkAuthSectionName); for (PINDEX r = 0; r < authRules.GetSize(); r++) { auth = Factory::Create(authRules[r]); if (auth) { authenticators.push_back(auth); } } m_authenticators = authenticators; } void GkAuthenticatorList::SelectH235Capability( const H225_GatekeeperRequest & grq, H225_GatekeeperConfirm & gcf) { ReadLock lock(m_reloadMutex); if (m_authenticators.empty()) return; // if GRQ does not contain a list of authentication mechanisms simply return if (!(grq.HasOptionalField(H225_GatekeeperRequest::e_authenticationCapability) && grq.HasOptionalField(H225_GatekeeperRequest::e_algorithmOIDs) && grq.m_authenticationCapability.GetSize() > 0 && grq.m_algorithmOIDs.GetSize() > 0)) return; // TODO: add TLS to negotiation either here or in H323Plus ? for (PINDEX auth = 0; auth < m_h235authenticators.GetSize(); auth++) { for (PINDEX cap = 0; cap < grq.m_authenticationCapability.GetSize(); cap++) { for (PINDEX alg = 0; alg < grq.m_algorithmOIDs.GetSize(); alg++) { if (m_h235authenticators[auth].IsCapability(grq.m_authenticationCapability[cap], grq.m_algorithmOIDs[alg])) { std::list::const_iterator iter = m_authenticators.begin(); while (iter != m_authenticators.end()) { GkAuthenticator* gkauth = *iter++; if (gkauth->IsH235Capable() && gkauth->IsH235Capability(grq.m_authenticationCapability[cap], grq.m_algorithmOIDs[alg])) { PTRACE(4, "GKAUTH\tGRQ accepted on " << H323TransportAddress(gcf.m_rasAddress) << " using authenticator " << m_h235authenticators[auth]); gcf.IncludeOptionalField(H225_GatekeeperConfirm::e_authenticationMode); gcf.m_authenticationMode = grq.m_authenticationCapability[cap]; gcf.IncludeOptionalField(H225_GatekeeperConfirm::e_algorithmOID); gcf.m_algorithmOID = grq.m_algorithmOIDs[alg]; if (gcf.m_authenticationMode.GetTag() == H235_AuthenticationMechanism::e_pwdSymEnc) { // add the challenge token gcf.IncludeOptionalField(H225_GatekeeperConfirm::e_tokens); gcf.m_tokens.SetSize(1); gcf.m_tokens[0].m_tokenOID = "0.0"; gcf.m_tokens[0].IncludeOptionalField(H235_ClearToken::e_timeStamp); gcf.m_tokens[0].m_timeStamp = (int)time(NULL); gcf.m_tokens[0].IncludeOptionalField(H235_ClearToken::e_random); gcf.m_tokens[0].m_random = rand(); gcf.m_tokens[0].IncludeOptionalField(H235_ClearToken::e_generalID); gcf.m_tokens[0].m_generalID = Toolkit::GKName(); } return; } } } } } } } bool GkAuthenticatorList::Validate( /// RRQ to be validated by authenticators RasPDU& request, /// authorization data (reject reason, ...) RRQAuthData& authData ) { ReadLock lock(m_reloadMutex); std::list::const_iterator i = m_authenticators.begin(); while (i != m_authenticators.end()) { GkAuthenticator* auth = *i++; if (auth->IsRasCheckEnabled(RasInfo::flag)) { const int result = auth->Check(request, authData); if (result == GkAuthenticator::e_ok) { PTRACE(3, "GKAUTH\t" << auth->GetName() << " RRQ check ok"); if (auth->GetControlFlag() == GkAuthenticator::e_Sufficient || auth->GetControlFlag() == GkAuthenticator::e_Alternative) return true; } else if (result == GkAuthenticator::e_fail) { PTRACE(3, "GKAUTH\t" << auth->GetName() << " RRQ check failed"); SNMP_TRAP(8, SNMPError, Authentication, auth->GetName() + " RRQ check failed"); return false; } } } return true; } bool GkAuthenticatorList::Validate( /// ARQ to be validated by authenticators RasPDU & request, /// authorization data (call duration limit, reject reason, ...) ARQAuthData & authData) { ReadLock lock(m_reloadMutex); std::list::const_iterator i = m_authenticators.begin(); while (i != m_authenticators.end()) { GkAuthenticator* auth = *i++; if (auth->IsRasCheckEnabled(RasInfo::flag)) { const long oldDurationLimit = authData.m_callDurationLimit; const int result = auth->Check(request, authData); if (authData.m_callDurationLimit == 0) { PTRACE(3, "GKAUTH\t" << auth->GetName() << " ARQ check failed: " "call duration 0"); SNMP_TRAP(8, SNMPError, Authentication, auth->GetName() + " ARQ check failed"); return false; } if (authData.m_callDurationLimit >= 0 && oldDurationLimit >= 0) authData.m_callDurationLimit = PMIN( authData.m_callDurationLimit, oldDurationLimit ); else authData.m_callDurationLimit = PMAX( authData.m_callDurationLimit, oldDurationLimit ); if (result == GkAuthenticator::e_ok) { PTRACE(3, "GKAUTH\t" << auth->GetName() << " ARQ check ok"); if (auth->GetControlFlag() == GkAuthenticator::e_Sufficient || auth->GetControlFlag() == GkAuthenticator::e_Alternative) return true; } else if (result == GkAuthenticator::e_fail) { PTRACE(3, "GKAUTH\t" << auth->GetName() << " ARQ check failed"); SNMP_TRAP(8, SNMPError, Authentication, auth->GetName() + " ARQ check failed"); return false; } } } return true; } bool GkAuthenticatorList::Validate( SetupMsg & setup, /// authorization data (call duration limit, reject reason, ...) SetupAuthData & authData) { ReadLock lock(m_reloadMutex); std::list::const_iterator i = m_authenticators.begin(); while (i != m_authenticators.end()) { GkAuthenticator* auth = *i++; if (auth->IsMiscCheckEnabled(GkAuthenticator::e_Setup) || (!authData.m_fromRegistered && auth->IsMiscCheckEnabled(GkAuthenticator::e_SetupUnreg))) { const long oldDurationLimit = authData.m_callDurationLimit; const int result = auth->Check(setup, authData); if (authData.m_callDurationLimit == 0) { PTRACE(3, "GKAUTH\t" << auth->GetName() << " Setup check failed: " "call duration limit 0"); SNMP_TRAP(8, SNMPError, Authentication, auth->GetName() + " Setup check failed"); return false; } if (authData.m_callDurationLimit >= 0 && oldDurationLimit >= 0) { authData.m_callDurationLimit = PMIN(authData.m_callDurationLimit, oldDurationLimit); } else { authData.m_callDurationLimit = PMAX(authData.m_callDurationLimit, oldDurationLimit); } if (result == GkAuthenticator::e_ok) { PTRACE(3, "GKAUTH\t" << auth->GetName() << " Setup check ok"); if (auth->GetControlFlag() == GkAuthenticator::e_Sufficient || auth->GetControlFlag() == GkAuthenticator::e_Alternative) return true; } else if (result == GkAuthenticator::e_fail) { PTRACE(3, "GKAUTH\t" << auth->GetName() << " Setup check failed"); SNMP_TRAP(8, SNMPError, Authentication, auth->GetName() + " Setup check failed"); return false; } } } return true; } // class CacheManager bool CacheManager::Retrieve( const PString & key, /// the key to look for PString& value /// filled with the value on return ) const { // quick check if (m_ttl == 0) return false; ReadLock lock(m_rwmutex); std::map::const_iterator iter = m_cache.find(key); if (iter == m_cache.end()) return false; if (m_ttl >= 0) { std::map::const_iterator i = m_ctime.find(key); if (i == m_ctime.end() || (time(NULL) - i->second) >= m_ttl) return false; // cache expired } value = (const char *)(iter->second); return true; } void CacheManager::Save( const PString & key, /// a key to be stored const PString & value /// a value to be associated with the key ) { if (m_ttl != 0) { WriteLock lock(m_rwmutex); m_cache[key] = (const char*)value; m_ctime[key] = time(NULL); } } // class SimplePasswordAuth SimplePasswordAuth::SimplePasswordAuth( const char * name, unsigned supportedRasChecks, unsigned supportedMiscChecks) : GkAuthenticator(name, supportedRasChecks, supportedMiscChecks), m_cache(NULL) { if (!GetConfig()->HasKey(name, "KeyFilled")) { PTRACE(1, "GKAUTH\t" << GetName() << " KeyFilled config variable is missing"); } m_encryptionKey = GetConfig()->GetInteger(name, "KeyFilled", 0); m_checkID = Toolkit::AsBool(GetConfig()->GetString(name, "CheckID", "0")); m_cache = new CacheManager(GetConfig()->GetInteger(name, "PasswordTimeout", -1)); m_disabledAlgorithms = GetConfig()->GetString(name, "DisableAlgorithm", "").Tokenise(",;", FALSE); PFactory::KeyList_T keyList = PFactory::GetKeyList(); PFactory::KeyList_T::const_iterator r; PStringList authlist = Toolkit::Instance()->GetAuthenticatorList(); // if a global list of autenticators is configured, use it in the priority order supplied if (authlist.GetSize() > 0) { for (PINDEX i = 0; i < authlist.GetSize(); ++i) { for (r = keyList.begin(); r != keyList.end(); ++r) { H235Authenticator * Auth = PFactory::CreateInstance(*r); // only use, if it's not disabled for this GnuGk authentication method if ((PString(Auth->GetName()) == authlist[i]) && (m_disabledAlgorithms.GetStringsIndex(Auth->GetName()) == P_MAX_INDEX)) { if ((Auth->GetApplication() == H235Authenticator::EPAuthentication) ||(Auth->GetApplication() == H235Authenticator::GKAdmission) ||(Auth->GetApplication() == H235Authenticator::AnyApplication) ) { AppendH235Authenticator(Auth); } else { delete Auth; } } } } } else { for (r = keyList.begin(); r != keyList.end(); ++r) { H235Authenticator * Auth = PFactory::CreateInstance(*r); // only use, if it's not disabled for this GnuGk authentication method if (m_disabledAlgorithms.GetStringsIndex(Auth->GetName()) == P_MAX_INDEX) { if ((Auth->GetApplication() == H235Authenticator::EPAuthentication) ||(Auth->GetApplication() == H235Authenticator::GKAdmission) ||(Auth->GetApplication() == H235Authenticator::AnyApplication) ) { AppendH235Authenticator(Auth); } else { delete Auth; } } } } } SimplePasswordAuth::~SimplePasswordAuth() { delete m_cache; } int SimplePasswordAuth::Check( RasPDU & request, RRQAuthData& authData ) { H225_RegistrationRequest& rrq = request; return doCheck(request, rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias) ? &rrq.m_terminalAlias : NULL, &authData ); } int SimplePasswordAuth::Check(RasPDU & request, unsigned &) { return doCheck(request); } int SimplePasswordAuth::Check(RasPDU & request, unsigned &) { return doCheck(request); } int SimplePasswordAuth::Check(RasPDU & request, unsigned &) { return doCheck(request); } int SimplePasswordAuth::Check(RasPDU & request, unsigned &) { return doCheck(request); } int SimplePasswordAuth::Check(RasPDU & request, unsigned &) { return doCheck(request); } int SimplePasswordAuth::Check( /// ARQ to be authenticated/authorized RasPDU & request, /// authorization data (call duration limit, reject reason, ...) ARQAuthData & /*authData*/) { H225_AdmissionRequest& arq = request; return doCheck(request, &arq.m_srcInfo); } bool SimplePasswordAuth::GetPassword( const PString & id, /// get the password for this id PString & passwd /// filled with the password on return ) { if (id.IsEmpty()) return false; if (!GetConfig()->HasKey(GetName(), id)) return false; if (strcasecmp(id, "KeyFilled") == 0 || strcasecmp(id, "CheckID") == 0 || strcasecmp(id, "PasswordTimeout") == 0) { PTRACE(2, "GKAUTH\t" << GetName() << " trying to get password for " " the forbidden alias '" << id << '\''); return false; } passwd = Toolkit::Instance()->ReadPassword(GetName(), id, true); return true; } bool SimplePasswordAuth::InternalGetPassword( const PString & id, /// get the password for this id PString & passwd /// filled with the password on return ) { if (m_cache->Retrieve(id, passwd)) { PTRACE(5, "GKAUTH\t" << GetName() << " cached password found for '" << id << '\''); return true; } if (GetPassword(id, passwd)) { m_cache->Save(id, passwd); return true; } else return false; } int SimplePasswordAuth::CheckTokens( /// an array of tokens to be checked const H225_ArrayOf_ClearToken & tokens, /// aliases for the endpoint that generated the tokens const H225_ArrayOf_AliasAddress * aliases ) { for (PINDEX i = 0; i < tokens.GetSize(); i++) { H235_ClearToken& token = tokens[i]; // check for Cisco Access Token if (token.m_tokenOID == OID_CAT) { if (!token.HasOptionalField(H235_ClearToken::e_generalID)) { PTRACE(3, "GKAUTH\t" << GetName() << " generalID field " "not found inside CAT token"); return e_fail; } const PString id = token.m_generalID; if (m_checkID && (aliases == NULL || FindAlias(*aliases, id) == P_MAX_INDEX)) { PTRACE(3, "GKAUTH\t" << GetName() << " generalID '" << id << "' of CAT token does not match any alias for the endpoint"); return e_fail; } PString passwd; if (!InternalGetPassword(id, passwd)) { PTRACE(3, "GKAUTH\t" << GetName() << " password not found for '" << id << '\''); return e_fail; } H235AuthCAT authCAT; authCAT.SetLocalId(id); authCAT.SetPassword(passwd); if (authCAT.ValidateClearToken(token) == H235Authenticator::e_OK) { PTRACE(5, "GKAUTH\t" << GetName() << " CAT password match for '" << id << '\''); return e_ok; } else return e_fail; } if (token.HasOptionalField(H235_ClearToken::e_password)) { if (!token.HasOptionalField(H235_ClearToken::e_generalID)) { PTRACE(3, "GKAUTH\t"<< GetName() << " generalID field not found" <<" inside the clear text token"); return e_fail; } const PString id = token.m_generalID; if (m_checkID && (aliases == NULL || FindAlias(*aliases, id) == P_MAX_INDEX)) { PTRACE(3, "GKAUTH\t" << GetName() << " generalID '" << "' does not match any alias for the endpoint"); return e_fail; } PString passwd; if (!InternalGetPassword(id, passwd)) { PTRACE(3, "GKAUTH\t" << GetName() << " password not found for '" << id << '\''); return e_fail; } const PString tokenpasswd = token.m_password; if (passwd == tokenpasswd) { PTRACE(5, "GKAUTH\t" << GetName() << " clear text password " "match for '" << id << '\''); return e_ok; } else { return e_fail; } } } return e_next; } // TODO: unused ? remove ? int SimplePasswordAuth::CheckCryptoTokens( /// an array of cryptoTokens to be checked const H225_ArrayOf_CryptoH323Token & tokens, /// aliases for the endpoint that generated the tokens const H225_ArrayOf_AliasAddress * aliases, /// raw data for RAS PDU - required to validate some tokens /// like H.235 Auth Procedure I const PBYTEArray & rawPDU) { for (PINDEX i = 0; i < tokens.GetSize(); i++) { if (tokens[i].GetTag() == H225_CryptoH323Token::e_cryptoEPPwdHash) { H225_CryptoH323Token_cryptoEPPwdHash& pwdhash = tokens[i]; const PString id = AsString(pwdhash.m_alias, false); if (m_checkID && (aliases == NULL || FindAlias(*aliases, id) == P_MAX_INDEX)) { PTRACE(3, "GKAUTH\t" << GetName() << " alias '" << id << "' of the cryptoEPPwdHash token does not match " "any alias for the endpoint"); return e_fail; } PString passwd; if (!InternalGetPassword(id, passwd)) { PTRACE(3, "GKAUTH\t" << GetName() << " password not found for '" << id << '\''); return e_fail; } H235AuthSimpleMD5 authMD5; authMD5.SetLocalId(id); authMD5.SetPassword(passwd); if (authMD5.ValidateCryptoToken(tokens[i], rawPDU) == H235Authenticator::e_OK) { PTRACE(5, "GKAUTH\t" << GetName() << " MD5 password match for '" << id << '\''); return e_ok; } else return e_fail; #if P_SSL } else if (tokens[i].GetTag() == H225_CryptoH323Token::e_nestedcryptoToken) { const H235_CryptoToken& nestedCryptoToken = tokens[i]; if (nestedCryptoToken.GetTag() != H235_CryptoToken::e_cryptoHashedToken) continue; const H235_CryptoToken_cryptoHashedToken& cryptoHashedToken = nestedCryptoToken; const H235_ClearToken& clearToken = cryptoHashedToken.m_hashedVals; if (!clearToken.HasOptionalField(H235_ClearToken::e_sendersID)) { PTRACE(5, "GKAUTH\t" << GetName() << " hashedVals of nested " " cryptoHashedToken do not contain sendersID"); continue; } PString id = clearToken.m_sendersID; if (m_checkID && (aliases == NULL || FindAlias(*aliases, id) == P_MAX_INDEX)) { PTRACE(3, "GKAUTH\t" << GetName() << " sendersID '" << id << "' of the cryptoHashedToken hasgedVals does not match " "any alias for the endpoint"); return e_fail; } PString passwd; bool passwordFound = InternalGetPassword(id, passwd); //if a password is not found: senderID == endpointIdentifier? if (!passwordFound) { H225_EndpointIdentifier epId; epId = id; endptr ep = RegistrationTable::Instance()->FindByEndpointId(epId); if (!ep) { PTRACE(3, "GKAUTH\t" << GetName() << " sendersID '" << id << "' of the cryptoHashedToken hashedVals does not match " "any endpoint identifier"); return e_fail; } // check all endpoint aliases for a password const H225_ArrayOf_AliasAddress pwaliases = ep->GetAliases(); for (PINDEX p = 0; p < pwaliases.GetSize(); p++) { id = AsString(pwaliases[p], FALSE); passwordFound = InternalGetPassword(id, passwd); if (passwordFound) break; } } if (!passwordFound) { PTRACE(3, "GKAUTH\t" << GetName() << " password not found for '" << id << '\''); return e_fail; } H235AuthProcedure1 authProcedure1; authProcedure1.SetLocalId(Toolkit::GKName()); authProcedure1.SetPassword(passwd); const int result = authProcedure1.ValidateCryptoToken(tokens[i], rawPDU); if (result == H235Authenticator::e_OK) { PTRACE(5, "GKAUTH\t" << GetName() << " SHA-1 password match for '" << id << '\''); return e_ok; } else if (result == H235Authenticator::e_Absent) continue; else return e_fail; #endif } } return e_next; } bool SimplePasswordAuth::ResolveUserName(const H235_ClearToken & token, PString & username) { // CAT if (token.HasOptionalField(H235_ClearToken::e_generalID)) { username = token.m_generalID; return true; } return false; } bool SimplePasswordAuth::ResolveUserName(const H225_CryptoH323Token & cryptotoken, const H225_ArrayOf_AliasAddress * aliases, PString & username) { // MD5 if (cryptotoken.GetTag() == H225_CryptoH323Token::e_cryptoEPPwdHash) { const H225_CryptoH323Token_cryptoEPPwdHash & pwdhash = cryptotoken; username = AsString(pwdhash.m_alias, false); return true; } else if (cryptotoken.GetTag() == H225_CryptoH323Token::e_cryptoEPPwdEncr) { // use alias as username, token contans just the encrypted challenge if (aliases && (aliases->GetSize() > 0)) { username = AsString(aliases[0], false); return true; } else { return false; } } else if (cryptotoken.GetTag() == H225_CryptoH323Token::e_nestedcryptoToken) { H235_ClearToken clearToken; bool found = false; const H235_CryptoToken & nestedCryptoToken = cryptotoken; // H235.1 if (nestedCryptoToken.GetTag() == H235_CryptoToken::e_cryptoHashedToken) { const H235_CryptoToken_cryptoHashedToken& cryptoHashedToken = nestedCryptoToken; clearToken = cryptoHashedToken.m_hashedVals; found = true; } // H235.2 if (nestedCryptoToken.GetTag() == H235_CryptoToken::e_cryptoSignedToken) { const H235_CryptoToken_cryptoSignedToken & cryptoSignedToken = nestedCryptoToken; H235_SIGNED m_Signed = cryptoSignedToken.m_token; if (m_Signed.m_toBeSigned.DecodeSubType(clearToken)) found = true; } if (found && (clearToken.HasOptionalField(H235_ClearToken::e_sendersID))) { username = clearToken.m_sendersID; return true; } } return false; } #ifdef H323_H350 H350PasswordAuth::H350PasswordAuth(const char* authName) : SimplePasswordAuth(authName) { } H350PasswordAuth::~H350PasswordAuth() { } bool H350PasswordAuth::GetPassword(const PString & alias, PString & password) { // search the directory PString search = GkConfig()->GetString(H350Section, "SearchBaseDN", ""); H225_AliasAddress aliasaddress; H323SetAliasAddress(alias, aliasaddress); PString filter = "h235IdentityEndpointID=" + alias; H350_Session session; if (!Toolkit::Instance()->CreateH350Session(&session)) { PTRACE(1, "H350\tH235Auth: Could not connect to server"); return false; } H350_Session::LDAP_RecordList rec; int count = session.Search(search, filter, rec); if (count <= 0) { PTRACE(4, "H350\tH235Auth: No record found"); session.Close(); return false; } // locate the record for (H350_Session::LDAP_RecordList::const_iterator x = rec.begin(); x != rec.end(); ++x) { H350_Session::LDAP_Record entry = x->second; if (session.GetAttribute(entry, "h235IdentityPassword", password)) { password = password.Trim(); // server may send newline at end etc. PTRACE(4, "H350\tH235Auth: Password located"); session.Close(); return true; } } PTRACE(4, "H350\tH235Auth: No password found"); session.Close(); return false; } #endif // class AliasAuth AliasAuth::AliasAuth( const char * name, unsigned supportedRasChecks, unsigned supportedMiscChecks) : GkAuthenticator(name, supportedRasChecks, supportedMiscChecks), m_cache(NULL) { m_cache = new CacheManager(GetConfig()->GetInteger(name, "CacheTimeout", -1)); } AliasAuth::~AliasAuth() { delete m_cache; } int AliasAuth::Check( RasPDU & request, RRQAuthData & /*authData*/) { H225_RegistrationRequest& rrq = request; if (!rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) { PTRACE(3, "GKAUTH\t" << GetName() << " - terminalAlias field not found in RRQ message"); return GetDefaultStatus(); } const H225_ArrayOf_AliasAddress& aliases = rrq.m_terminalAlias; for (PINDEX i = 0; i <= aliases.GetSize(); i++) { const PString alias = (i < aliases.GetSize()) ? AsString(aliases[i], false) : PString("default"); PString authcond; if (InternalGetAuthConditionString(alias, authcond)) { if (doCheck(rrq.m_callSignalAddress, authcond)) { PTRACE(5, "GKAUTH\t" << GetName() << " auth condition '" << authcond <<"' accepted RRQ from '" << alias << '\''); return e_ok; } else { PTRACE(3, "GKAUTH\t" << GetName() << " auth condition '" << authcond <<"' rejected RRQ from '" << alias << '\''); return e_fail; } } else PTRACE(4, "GKAUTH\t" << GetName() << " auth condition not found " << "for alias '" << alias << '\''); } return GetDefaultStatus(); } bool AliasAuth::GetAuthConditionString( /// an alias the condition string is to be retrieved for const PString & alias, /// filled with auth condition string that has been found PString & authCond) { if (alias.IsEmpty()) return false; if (!GetConfig()->HasKey("RasSrv::RRQAuth", alias)) return false; if (strcasecmp(alias, "CacheTimeout") == 0) { PTRACE(2, "GKAUTH\t" << GetName() << " trying to get auth condition " " string for the forbidden alias '" << alias << '\''); return false; } authCond = GetConfig()->GetString("RasSrv::RRQAuth", alias, ""); return true; } bool AliasAuth::InternalGetAuthConditionString( const PString & id, /// get the password for this id PString & authCond /// filled with the auth condition string on return ) { if (m_cache->Retrieve(id, authCond)) { PTRACE(5, "GKAUTH\t" << GetName() << " cached auth condition string " "found for '" << id << '\''); return true; } if (GetAuthConditionString(id, authCond)) { m_cache->Save(id, authCond); return true; } else return false; } bool AliasAuth::doCheck( /// an array of source signaling addresses for an endpoint that sent the request const H225_ArrayOf_TransportAddress & sigaddr, /// auth condition string as returned by GetAuthConditionString const PString & condition) { const PStringArray authrules(condition.Tokenise("&|", FALSE)); if (authrules.GetSize() < 1) { PTRACE(2, "GKAUTH\t" << GetName() << " contains an empty auth condition"); return false; } for (PINDEX i = 0; i < authrules.GetSize(); ++i) for (PINDEX j = 0; j < sigaddr.GetSize(); ++j) if (CheckAuthRule(sigaddr[j], authrules[i])) { PTRACE(5, "GKAUTH\t" << GetName() << " auth rule '" << authrules[i] << "' applied successfully to RRQ " " from " << AsDotString(sigaddr[j])); return true; } return false; } bool AliasAuth::CheckAuthRule( /// a signaling address for the endpoint that sent the request const H225_TransportAddress & sigaddr, /// the auth rule to be used for checking const PString & authrule) { const PStringArray rule = authrule.Tokenise(":", false); if (rule.GetSize() < 1) { PTRACE(1, "GKAUTH\t" << GetName() << " found invalid empty auth rule '" << authrule << '\''); return false; } // authrule = rName[:params...] const PString rName = rule[0].Trim(); if (strcasecmp(rName, "confirm") == 0 || strcasecmp(rName, "allow") == 0) return true; else if (strcasecmp(rName, "reject") == 0 || strcasecmp(rName, "deny") == 0 || strcasecmp(rName, "forbid") == 0) return false; else if (strcasecmp(rName, "sigaddr") == 0) { // condition 'sigaddr' example: // sigaddr:.*ipAddress .* ip = .* c3 47 e2 a2 .*port = 1720.* if (rule.GetSize() < 2) { PTRACE(1, "GKAUTH\t" << GetName() << " found invalid empty sigaddr " "auth rule '" << authrule << '\''); return false; } return Toolkit::MatchRegex(AsString(sigaddr), rule[1].Trim()) != 0; } else if (strcasecmp(rName, "sigip") == 0) { // condition 'sigip' example: // sigip:195.71.129.69:1720 if (rule.GetSize() < 2) { PTRACE(1, "GKAUTH\t" << GetName() << " found invalid empty sigip " "auth rule '" << authrule << '\''); return false; } PString allowed_ip = authrule.Mid(authrule.Find("sigip:")+6).Trim(); PStringArray ip_parts = SplitIPAndPort(allowed_ip, GK_DEF_ENDPOINT_SIGNAL_PORT); PIPSocket::Address ip; PIPSocket::GetHostAddress(ip_parts[0], ip); const WORD port = (WORD)(ip_parts[1].AsUnsigned()); return (sigaddr == SocketToH225TransportAddr(ip, port)); } else { PTRACE(1, "GKAUTH\t" << GetName() << " found unknown auth rule '" << rName << '\''); return false; } } // class PrefixAuth class AuthRule; class AuthObj; class PrefixAuth : public GkAuthenticator { public: typedef std::map< PString, AuthRule *, greater > Rules; enum SupportedRasChecks { PrefixAuthRasChecks = RasInfo::flag | RasInfo::flag }; PrefixAuth( const char* name, unsigned supportedRasChecks = PrefixAuthRasChecks, unsigned supportedMiscChecks = 0); virtual ~PrefixAuth(); // override from class GkAuthenticator virtual int Check(RasPDU& request, unsigned& rejectReason); /** Authenticate/Authorize ARQ message. Override from GkAuthenticator. @return e_fail - authentication failed e_ok - authenticated with this authenticator e_next - authentication could not be determined */ virtual int Check( /// ARQ to be authenticated/authorized RasPDU& request, /// authorization data (call duration limit, reject reason, ...) ARQAuthData& authData); protected: virtual int doCheck(const AuthObj& aobj); private: PrefixAuth(); PrefixAuth(const PrefixAuth &); PrefixAuth& operator=(const PrefixAuth &); private: Rules m_prefrules; int m_defaultRule; }; // Help classes for PrefixAuth class AuthObj // abstract class { public: virtual ~AuthObj() { } virtual bool IsValid() const { return true; } virtual PStringArray GetPrefixes() const = 0; virtual PIPSocket::Address GetIP() const = 0; virtual PString GetAliases() const = 0; }; class ARQAuthObj : public AuthObj { public: ARQAuthObj(const H225_AdmissionRequest & arq); virtual bool IsValid() const { return m_ep; } virtual PStringArray GetPrefixes() const; virtual PIPSocket::Address GetIP() const; virtual PString GetAliases() const; private: ARQAuthObj(); ARQAuthObj(const ARQAuthObj &); ARQAuthObj & operator=(const ARQAuthObj &); private: const H225_AdmissionRequest& m_arq; endptr m_ep; }; class LRQAuthObj : public AuthObj { public: LRQAuthObj(const H225_LocationRequest & lrq); virtual PStringArray GetPrefixes() const; virtual PIPSocket::Address GetIP() const; virtual PString GetAliases() const; private: LRQAuthObj(); LRQAuthObj(const LRQAuthObj &); LRQAuthObj & operator=(const LRQAuthObj &); private: const H225_LocationRequest & m_lrq; PIPSocket::Address m_ipAddress; }; class AuthRule : public NamedObject { public: enum Result { e_nomatch, e_allow, e_deny }; AuthRule( Result fate, bool inverted ) : m_priority(1000), m_fate(fate), m_inverted(inverted), m_next(NULL) { } virtual ~AuthRule() { delete m_next; } virtual bool Match(const AuthObj & aobj) = 0; int Check(const AuthObj & aobj); bool operator<(const AuthRule & obj) const { return m_priority < obj.m_priority; } void SetNext(AuthRule * next) { m_next = next; } private: AuthRule(); AuthRule(const AuthRule& ); AuthRule & operator=(const AuthRule &); protected: /// the lesser the value, the higher the priority int m_priority; private: Result m_fate; bool m_inverted; AuthRule * m_next; }; class NullRule : public AuthRule { public: NullRule() : AuthRule(e_nomatch, false) { SetName("NULL"); } virtual bool Match(const AuthObj & /*aobj*/) { return false; } private: NullRule(const NullRule &); NullRule & operator=(const NullRule &); }; class IPAuthRule : public AuthRule { public: IPAuthRule(Result fate, const PString & ipStr, bool inverted); virtual bool Match(const AuthObj & aobj); private: IPAuthRule(); IPAuthRule(const IPAuthRule &); IPAuthRule & operator=(const IPAuthRule &); private: PIPSocket::Address m_network, m_netmask; }; class AliasAuthRule : public AuthRule { public: AliasAuthRule( Result fate, const PString & aliasStr, bool inverted ) : AuthRule(fate, inverted), m_pattern(aliasStr) { m_priority = -1; SetName(PString((fate == e_allow) ? "allow alias" : "deny alias") + (inverted ? ":!" : ":") + aliasStr); } virtual bool Match(const AuthObj & aobj); private: AliasAuthRule(); AliasAuthRule(const AliasAuthRule &); AliasAuthRule & operator=(const AliasAuthRule &); private: PString m_pattern; }; ARQAuthObj::ARQAuthObj(const H225_AdmissionRequest & arq) : m_arq(arq), m_ep(RegistrationTable::Instance()->FindByEndpointId(arq.m_endpointIdentifier)) { } PStringArray ARQAuthObj::GetPrefixes() const { PStringArray array; if (m_arq.HasOptionalField(H225_AdmissionRequest::e_destinationInfo)) { const PINDEX ss = m_arq.m_destinationInfo.GetSize(); if (ss > 0) { array.SetSize(ss); for (PINDEX i = 0; i < ss; ++i) array[i] = AsString(m_arq.m_destinationInfo[i], false); } } if (array.GetSize() == 0) array.AppendString(PString::Empty()); return array; } PIPSocket::Address ARQAuthObj::GetIP() const { PIPSocket::Address result; const H225_TransportAddress& addr = m_arq.HasOptionalField(H225_AdmissionRequest::e_srcCallSignalAddress) ? m_arq.m_srcCallSignalAddress : m_ep->GetCallSignalAddress(); GetIPFromTransportAddr(addr, result); return result; } PString ARQAuthObj::GetAliases() const { return AsString(m_ep->GetAliases()); } LRQAuthObj::LRQAuthObj(const H225_LocationRequest & lrq) : m_lrq(lrq) { GetIPFromTransportAddr(m_lrq.m_replyAddress, m_ipAddress); } PStringArray LRQAuthObj::GetPrefixes() const { PStringArray array; const PINDEX ss = m_lrq.m_destinationInfo.GetSize(); if (ss > 0) { array.SetSize(ss); for (PINDEX i = 0; i < ss; ++i) array[i] = AsString(m_lrq.m_destinationInfo[i], false); } return array; } PIPSocket::Address LRQAuthObj::GetIP() const { return m_ipAddress; } PString LRQAuthObj::GetAliases() const { return m_lrq.HasOptionalField(H225_LocationRequest::e_sourceInfo) ? AsString(m_lrq.m_sourceInfo) : PString::Empty(); } int AuthRule::Check(const AuthObj & aobj) { if (Match(aobj) ^ m_inverted) { PTRACE(5, "GKAUTH\tPrefix auth rule '" << GetName() << "' matched"); return m_fate; } else return m_next ? m_next->Check(aobj) : e_nomatch; } inline void delete_rule(PrefixAuth::Rules::value_type r) { delete r.second; r.second = NULL; } IPAuthRule::IPAuthRule(Result fate, const PString & ipStr, bool inverted) : AuthRule(fate, inverted) { Toolkit::GetNetworkFromString(ipStr, m_network, m_netmask); DWORD n = ~PIPSocket::Net2Host(DWORD(m_netmask)); for (m_priority = 0; n; n >>= 1) ++m_priority; SetName(PString((fate == e_allow) ? "allow ip(" : "deny ip(") + PString(m_priority) + (inverted ? "):!" : "):") + ipStr); } bool IPAuthRule::Match(const AuthObj & aobj) { return ((aobj.GetIP().GetVersion() == m_network.GetVersion()) && ((aobj.GetIP() & m_netmask) == m_network)); } bool AliasAuthRule::Match(const AuthObj& aobj) { return aobj.GetAliases().FindRegEx(m_pattern) != P_MAX_INDEX; } inline bool is_inverted(const PString & cfg, PINDEX p) { return (p > 1) ? cfg[p-1] == '!' : false; } inline bool comp_authrule_priority(AuthRule *a1, AuthRule *a2) { return *a1 < *a2; } namespace { const char* const allowflag = "allow"; const char* const denyflag = "deny"; const char* const ipflag = "ip:"; const char* const ipv4flag = "ipv4:"; const char* const ipv6flag = "ipv6:"; const char* const aliasflag = "alias:"; } // class PrefixAuth PrefixAuth::PrefixAuth( const char * name, unsigned supportedRasChecks, unsigned supportedMiscChecks) : GkAuthenticator(name, supportedRasChecks, supportedMiscChecks) { m_defaultRule = GetDefaultStatus(); const int ipfl = (int)strlen(ipflag); const int ipv4fl = (int)strlen(ipv4flag); const int ipv6fl = (int)strlen(ipv6flag); const int aliasfl = (int)strlen(aliasflag); const PStringToString cfgs = GetConfig()->GetAllKeyValues(name); for (PINDEX i = 0; i < cfgs.GetSize(); ++i) { PString key = cfgs.GetKeyAt(i); if (key *= "default") { m_defaultRule = Toolkit::AsBool(cfgs.GetDataAt(i)) ? e_ok : e_fail; continue; } else if (key *= "ALL") { // use space (0x20) as the key so it will be the last resort key = " "; } if (m_prefrules.find(key) != m_prefrules.end()) { PTRACE(1, "GKAUTH\t" << GetName() << " duplicate entry for " "destination '" << key << '\''); continue; //rule already exists? ignore } const PStringArray rules = cfgs.GetDataAt(i).Tokenise("|", false); const PINDEX sz = rules.GetSize(); if (sz < 1) { PTRACE(1, "GKAUTH\t" << GetName() << " no rules found for " "destination '" << key << '\''); continue; } //AuthRule *rls[sz]; AuthRule **rls = new AuthRule *[sz]; for (PINDEX j = 0; j < sz; ++j) { // if not allowed, assume denial const AuthRule::Result fate = (rules[j].Find(allowflag) != P_MAX_INDEX) ? AuthRule::e_allow : AuthRule::e_deny; PINDEX pp; if ((pp = rules[j].Find(ipflag)) != P_MAX_INDEX) rls[j] = new IPAuthRule(fate, rules[j].Mid(pp + ipfl).Trim(), is_inverted(rules[j], pp) ); else if ((pp = rules[j].Find(ipv4flag)) != P_MAX_INDEX) rls[j] = new IPAuthRule(fate, rules[j].Mid(pp + ipv4fl).Trim(), is_inverted(rules[j], pp) ); else if ((pp = rules[j].Find(ipv6flag)) != P_MAX_INDEX) rls[j] = new IPAuthRule(fate, rules[j].Mid(pp + ipv6fl).Trim(), is_inverted(rules[j], pp) ); else if ((pp = rules[j].Find(aliasflag)) != P_MAX_INDEX) rls[j] = new AliasAuthRule(fate, rules[j].Mid(pp+aliasfl).Trim(), is_inverted(rules[j], pp) ); else { rls[j] = new NullRule(); } } // sort the rules by priority stable_sort(rls, rls + sz, comp_authrule_priority); for (PINDEX k = 1; k < sz; ++k) rls[k-1]->SetNext(rls[k]); m_prefrules[key] = rls[0]; delete [] rls; rls = NULL; } if (m_prefrules.empty()) { PTRACE(1, "GKAUTH\t" << GetName() << " contains no rules - check the config"); } } PrefixAuth::~PrefixAuth() { for_each(m_prefrules.begin(), m_prefrules.end(), delete_rule); } int PrefixAuth::Check(RasPDU & request, unsigned &) { LRQAuthObj tmpObj((const H225_LocationRequest&)request); // fix for GCC 3.4.2 return doCheck(tmpObj); } int PrefixAuth::Check( /// ARQ to be authenticated/authorized RasPDU & request, /// authorization data (call duration limit, reject reason, ...) ARQAuthData & /*authData*/) { H225_AdmissionRequest& arq = request; if (arq.m_answerCall && arq.HasOptionalField(H225_AdmissionRequest::e_callIdentifier) && CallTable::Instance()->FindCallRec(arq.m_callIdentifier)) { PTRACE(5, "GKAUTH\t" << GetName() << " ARQ check skipped - call " "already admitted and present in the call table"); return e_ok; } ARQAuthObj tmpObj(arq); // fix for GCC 3.4.2 return doCheck(tmpObj); } struct comp_pref { // function object comp_pref(const PString & s) : value(s) {} bool operator()(const PrefixAuth::Rules::value_type & v) const; const PString & value; }; inline bool comp_pref::operator()(const PrefixAuth::Rules::value_type & v) const { return (value.Find(v.first) == 0) || (v.first *= " "); } int PrefixAuth::doCheck(const AuthObj & aobj) { if (!aobj.IsValid()) return e_fail; const PStringArray destinationInfo(aobj.GetPrefixes()); for (PINDEX i = 0; i < destinationInfo.GetSize(); ++i) { // find the first match rule // since prefrules is descendently sorted // it must be the most specific prefix for (Rules::iterator j = m_prefrules.begin(); j != m_prefrules.end(); ++j) { Rules::iterator iter = find_if(j, m_prefrules.end(), comp_pref(destinationInfo[i])); if (iter == m_prefrules.end()) break; switch (iter->second->Check(aobj)) { case AuthRule::e_allow: PTRACE(4, "GKAUTH\t" << GetName() << " rule matched and " "accepted destination prefix '" << ((iter->first == " ") ? PString("ALL") : iter->first) << "' for alias '" << destinationInfo[i] << '\''); return e_ok; case AuthRule::e_deny: PTRACE(4, "GKAUTH\t" << GetName() << " rule matched and " "rejected destination prefix '" << ((iter->first == " ") ? PString("ALL") : iter->first) << "' for alias '" << destinationInfo[i] << '\''); return e_fail; default: // try next prefix... j = iter; PTRACE(4, "GKAUTH\t" << GetName() << " rule matched and " "could not reject or accept destination prefix '" << ((iter->first == " ") ? PString("ALL") : iter->first) << "' for alias '" << destinationInfo[i] << '\''); } } } if (m_defaultRule == e_ok) PTRACE(4, "GKAUTH\t" << GetName() << " default rule accepted the request"); else if (m_defaultRule == e_fail) PTRACE(4, "GKAUTH\t" << GetName() << " default rule rejected the request"); else PTRACE(4, "GKAUTH\t" << GetName() << " could not reject or accept the request"); return m_defaultRule; } namespace { // anonymous namespace GkAuthCreator DefaultAuthenticatorCreator("default"); GkAuthCreator SimplePasswordAuthCreator("SimplePasswordAuth"); #if H323_H350 GkAuthCreator SQLPasswordAuthCreator("H350PasswordAuth"); #endif GkAuthCreator AliasAuthCreator("AliasAuth"); GkAuthCreator PrefixAuthCreator("PrefixAuth"); } // end of anonymous namespace gnugk-3.4/PaxHeaders.16356/statusacct.h0000644000175000001440000000005011461315737016060 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/statusacct.h0000644000175000001440000000467411461315737015034 0ustar00janusers00000000000000/* * statusacct.h * * accounting module for GNU Gatekeeper that send it's output to the status port. * * Copyright (c) 2005-2010, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #ifndef __STATUSACCT_H #define __STATUSACCT_H "@(#) $Id: statusacct.h,v 1.6 2010/10/25 15:01:51 willamowius Exp $" #include "gkacct.h" /** Accounting module for the status port. It sends accounting call start/stop/update/connect events to the status port. */ class StatusAcct : public GkAcctLogger { public: enum Constants { /// events recognized by this module StatusAcctEvents = AcctStart | AcctStop | AcctUpdate | AcctConnect | AcctAlert | AcctRegister | AcctUnregister }; StatusAcct( /// name from Gatekeeper::Acct section const char* moduleName, /// config section name to be used with an instance of this module, /// pass NULL to use a default section (named "moduleName") const char* cfgSecName = NULL ); /// Destroy the accounting logger virtual ~StatusAcct(); /// overriden from GkAcctLogger virtual Status Log( AcctEvent evt, const callptr& call ); /// overriden from GkAcctLogger virtual Status Log( AcctEvent evt, const endptr& ep ); /// overriden from GkAcctLogger virtual PString EscapeAcctParam(const PString& param) const; /// overriden from GkAcctLogger PString ReplaceAcctParams( /// parametrized accounting string const PString& cdrStr, /// parameter values const std::map& params ) const; private: StatusAcct(); /* No copy constructor allowed */ StatusAcct(const StatusAcct&); /* No operator= allowed */ StatusAcct& operator=(const StatusAcct&); private: /// parametrized string for the call start event PString m_startEvent; /// parametrized string for the call stop (disconnect) event PString m_stopEvent; /// parametrized string for the call update event PString m_updateEvent; /// parametrized string for the call connect event PString m_connectEvent; /// parametrized string for the call alerting event PString m_alertEvent; /// parametrized string for the endpoint register event PString m_registerEvent; /// parametrized string for the endpoint un-register event PString m_unregisterEvent; /// timestamp formatting string PString m_timestampFormat; }; #endif /* __STATUSACCT_H */ gnugk-3.4/PaxHeaders.16356/copying0000644000175000001440000000005007101044567015120 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/copying0000644000175000001440000004362107101044567014067 0ustar00janusers00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. gnugk-3.4/PaxHeaders.16356/sqlacct.h0000644000175000001440000000005011715772427015341 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/sqlacct.h0000644000175000001440000000561411715772427014310 0ustar00janusers00000000000000/* * sqlacct.h * * SQL accounting module for GNU Gatekeeper * * Copyright (c) 2004, Michal Zygmuntowicz * Copyright (c) 2005-2010, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #ifndef SQLACCT_H #define SQLACCT_H "@(#) $Id: sqlacct.h,v 1.12 2012/02/12 17:21:27 shorne Exp $" #include "gkacct.h" /** This accounting module stores call information directly to an SQL database. It uses generic SQL interface, so different SQL backends are supported. Queries to store accounting information are parametrized using named parameters. */ class GkSQLConnection; class SQLAcct : public GkAcctLogger { public: enum Constants { /// events recognized by this module SQLAcctEvents = AcctStart | AcctUpdate | AcctStop | AcctConnect | AcctAlert | AcctRegister | AcctUnregister }; /// Create a logger that sends accounting to an SQL database SQLAcct( /// name from Gatekeeper::Acct section const char* moduleName, /// name for a config section with logger settings /// pass NULL to use the moduleName as the section name const char* cfgSecName = NULL ); /// Destroy the accounting logger virtual ~SQLAcct(); /** Log call accounting event. @return Status of this logging operation (see #Status enum#) */ virtual Status Log( AcctEvent evt, /// accounting event to log const callptr& call /// additional data for the event ); /** Log endpoint accounting event. @return Status of this logging operation (see #Status enum#) */ virtual Status Log( AcctEvent evt, /// accounting event to log const endptr& ep /// additional data for the event ); virtual PString GetInfo(); private: /* No copy constructor allowed */ SQLAcct(const SQLAcct&); /* No operator= allowed */ SQLAcct& operator=(const SQLAcct&); private: /// connection to the SQL database GkSQLConnection* m_sqlConn; /// parametrized query string for the call start event PString m_startQuery; /// parametrized alternative query string for the call start event PString m_startQueryAlt; /// parametrized query string for the call update event PString m_updateQuery; /// parametrized query string for the call stop event PString m_stopQuery; /// parametrized alternative query string for the call stop event PString m_stopQueryAlt; /// parametrized query string for call alerting PString m_alertQuery; /// parametrized query string for endpoint registration PString m_registerQuery; /// parametrized query string for endpoint un-registration PString m_unregisterQuery; /// parametrized query string for gatekeeper coming online PString m_onQuery; /// parametrized query string for gatekeeper going offline PString m_offQuery; /// timestamp formatting string PString m_timestampFormat; }; #endif /* SQLACCT_H */ gnugk-3.4/PaxHeaders.16356/version.cxx0000644000175000001440000000005012200603310015715 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/version.cxx0000644000175000001440000000463412200603310014665 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // PURPOSE OF THIS FILE: Give version info // // Copyright (C) 2003 Nils.Bokermann@mediaways.net // Copyright (c) 2006-2012, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #include #include #include "versionts.h" #include "version.h" #include "Toolkit.h" #include "config.h" const PString Toolkit::GKVersion() { return PString(PString::Printf, "Gatekeeper(%s) Version(%s) Ext(pthreads=%d,radius=%d,mysql=%d,pgsql=%d,firebird=%d,odbc=%d,sqlite=%d,large_fdset=%d,crypto/ssl=%d,h46018=%d,h46023=%d,ldap=%d,ssh=%d,ipv6=%d,h235media=%d,lua=%d,h46017=%d,snmp=%d,h46026=%d)" " H323Plus(%d.%d.%d) PTLib(%d.%d.%d) Build(%s, %s) Sys(%s %s %s)\r\n", (const unsigned char*)(PProcess::Current().GetManufacturer()), (const unsigned char*)(PProcess::Current().GetVersion(true)), #ifdef P_PTHREADS (int)1, #else (int)0, #endif #if HAS_RADIUS (int)1, #else (int)0, #endif #if HAS_MYSQL (int)1, #else (int)0, #endif #if HAS_PGSQL (int)1, #else (int)0, #endif #if HAS_FIREBIRD (int)1, #else (int)0, #endif #if HAS_ODBC (int)1, #else (int)0, #endif #if HAS_SQLITE (int)1, #else (int)0, #endif #ifdef LARGE_FDSET (int)LARGE_FDSET, #else (int)0, #endif #if P_SSL (int)1, #else (int)0, #endif #if HAS_H46018 (int)1, #else (int)0, #endif #if HAS_H46023 (int)1, #else (int)0, #endif #if P_LDAP (int)1, #else (int)0, #endif #if HAS_LIBSSH (int)1, #else (int)0, #endif #ifdef hasIPV6 (int)1, #else (int)0, #endif #ifdef HAS_H235_MEDIA (int)1, #else (int)0, #endif #ifdef HAS_LUA (int)1, #else (int)0, #endif #if HAS_H46017 (int)1, #else (int)0, #endif #if HAS_SNMP (int)1, #else (int)0, #endif #if HAS_H46026 (int)1, #else (int)0, #endif OPENH323_MAJOR, OPENH323_MINOR, OPENH323_BUILD, PTLIB_MAJOR, PTLIB_MINOR, PTLIB_BUILD, __DATE__, __TIME__, (const unsigned char*)(PProcess::GetOSName()), (const unsigned char*)(PProcess::GetOSHardware()), (const unsigned char*)(PProcess::GetOSVersion()) ); } gnugk-3.4/PaxHeaders.16356/radproto.cxx0000644000175000001440000000005012055670474016111 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/radproto.cxx0000644000175000001440000020640612055670474015062 0ustar00janusers00000000000000/* * radproto.cxx * * RADIUS protocol classes. * * Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz * Copyright (c) 2003-2012, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include "config.h" #if HAS_RADIUS #include #include #include #include "Toolkit.h" #include "h323util.h" #include "snmp.h" #include "radproto.h" // ignore overflow warnings #if (!_WIN32) && (GCC_VERSION >= 40400) #pragma GCC diagnostic ignored "-Wstrict-overflow" #endif namespace { /// Human-readable attribute names const char* const radiusAttributeNames[] = { /* 0*/ "Invalid", "User-Name", "User-Password", "CHAP-Password", /* 4*/ "NAS-IP-Address", "NAS-Port", "Service-Type", "Framed-Protocol", /* 8*/ "Framed-IP-Address", "Framed-IP-Netmask", "Framed-Routing", "Filter-Id", /* 12*/ "Framed-MTU", "Framed-Compression", "Login-IP-Host", "Login-Service", /* 16*/ "Login-TCP-Port", "Unknown", "Reply-Message", "Callback-Number", /* 20*/ "Callback-Id", "Unknown", "Framed-Route", "Framed-IPX-Network", /* 24*/ "State", "Class", "Vendor-Specific", "Session-Timeout", /* 28*/ "Idle-Timeout", "Termination-Action", "Called-Station-Id", "Calling-Station-Id", /* 32*/ "NAS-Identifier", "Proxy-State", "Login-LAT-Service", "Login-LAT-Node", /* 36*/ "Login-LAT-Group", "Framed-AppleTalk-Link", "Framed-AppleTalk-Network", "Framed-AppleTalk-Zone", /* 40*/ "Acct-Status-Type", "Acct-Delay-Time", "Acct-Input-Octets", "Acct-Output-Octets", /* 44*/ "Acct-Session-Id", "Acct-Authentic", "Acct-Session-Time", "Acct-Input-Packets", /* 48*/ "Acct-Output-Packets", "Acct-Terminate-Cause", "Acct-Multi-Session-Id", "Acct-Link-Count", /* 52*/ "Acct-Input-Gigawords", "Acct-Output-Gigawords", "Unknown", "Unknown", /* 56*/ "Unknown", "Unknown", "Unknown", "Unknown", /* 60*/ "CHAP-Challenge", "NAS-Port-Type", "Port-Limit", "Login-LAT-Port", /* 64*/ "Unknown", "Unknown", "Unknown", "Unknown", /* 68*/ "Unknown", "Unknown", "ARAP-Password", "ARAP-Features", /* 72*/ "ARAP-Zone-Access", "ARAP-Security", "ARAP-Security-Data", "Password-Retry", /* 76*/ "Prompt", "Connect-Info", "Configuration-Token", "EAP-Message", /* 80*/ "Message-Authenticator", "Unknown", "Unknown", "Unknown", /* 84*/ "ARAP-Challenge-Response", "Acct-Interim-Interval", "Unknown", "NAS-Port-Id", /* 88*/ "Framed-Pool", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* RFC 3162 */ /* 95*/ "NAS-IPv6-Address", "Framed-Interface-Id", "Framed-IPv6-Prefix", /* 98*/ "Login-IPv6-Host", "Framed-IPv6-Route", "Framed-IPv6-Pool", /*101*/ "Unknown", }; /// Human readable RADIUS packet code names const char* const radiusPacketCodeNames[] = { /* 0*/ "Invalid", "Access-Request", "Access-Accept", "Access-Reject", /* 4*/ "Accounting-Request", "Accounting-Response", "Unknown", "Unknown", /* 8*/ "Unknown", "Unknown", "Access-Challenge", "Status-Server", /*12*/ "Status-Client", "Unknown" }; /** Macro that returns name associated with the given attribute type. Returns "Unknown" if the name is not defined. */ inline const char* const PMAP_ATTR_TYPE_TO_NAME(unsigned type) { return type >= sizeof(radiusAttributeNames)/sizeof(radiusAttributeNames[0]) ? radiusAttributeNames[sizeof(radiusAttributeNames)/sizeof(radiusAttributeNames[0])-1] : radiusAttributeNames[type]; } /** Macro that returns name associated with the given RADIUS packet code. Returns "Unknown" if the name is not defined. */ inline const char* const PMAP_CODE_TO_NAME(unsigned code) { return code >= sizeof(radiusPacketCodeNames)/sizeof(radiusPacketCodeNames[0]) ? radiusPacketCodeNames[sizeof(radiusPacketCodeNames)/sizeof(radiusPacketCodeNames[0])-1] : radiusPacketCodeNames[code]; } // Cisco VSA attributes together with names and name lengths for fast lookup #define CISCO_ATTR_NAME(namestr) namestr, strlen(namestr) struct CiscoAttrName { CiscoAttrName(const char* n, size_t l, unsigned char t) : m_name(n), m_nameLen(l), m_type(t) {} // workaround for VC6 const char* const m_name; size_t m_nameLen; unsigned char m_type; } CiscoAttrNames[] = { CiscoAttrName(CISCO_ATTR_NAME("h323-remote-address"), RadiusAttr::CiscoVSA_h323_remote_address), CiscoAttrName(CISCO_ATTR_NAME("h323-conf-id"), RadiusAttr::CiscoVSA_h323_conf_id), CiscoAttrName(CISCO_ATTR_NAME("h323-setup-time"), RadiusAttr::CiscoVSA_h323_setup_time), CiscoAttrName(CISCO_ATTR_NAME("h323-connect-time"), RadiusAttr::CiscoVSA_h323_connect_time), CiscoAttrName(CISCO_ATTR_NAME("h323-disconnect-time"), RadiusAttr::CiscoVSA_h323_disconnect_time), CiscoAttrName(CISCO_ATTR_NAME("h323-disconnect-cause"), RadiusAttr::CiscoVSA_h323_disconnect_cause), CiscoAttrName(CISCO_ATTR_NAME("h323-credit-amount"), RadiusAttr::CiscoVSA_h323_credit_amount), CiscoAttrName(CISCO_ATTR_NAME("h323-credit-time"), RadiusAttr::CiscoVSA_h323_credit_time), CiscoAttrName(CISCO_ATTR_NAME("h323-return-code"), RadiusAttr::CiscoVSA_h323_return_code), CiscoAttrName(CISCO_ATTR_NAME("h323-billing-model"), RadiusAttr::CiscoVSA_h323_billing_model), CiscoAttrName(CISCO_ATTR_NAME("h323-currency"), RadiusAttr::CiscoVSA_h323_currency), CiscoAttrName(CISCO_ATTR_NAME("h323-redirect-number"), RadiusAttr::CiscoVSA_h323_redirect_number), CiscoAttrName(CISCO_ATTR_NAME("h323-redirect-ip-address"), RadiusAttr::CiscoVSA_h323_redirect_ip_address), CiscoAttrName(CISCO_ATTR_NAME("h323-gw-id"), RadiusAttr::CiscoVSA_h323_gw_id), CiscoAttrName(CISCO_ATTR_NAME("h323-call-origin"), RadiusAttr::CiscoVSA_h323_call_origin), CiscoAttrName(CISCO_ATTR_NAME("h323-call-type"), RadiusAttr::CiscoVSA_h323_call_type), CiscoAttrName(CISCO_ATTR_NAME("h323-voice-quality"), RadiusAttr::CiscoVSA_h323_voice_quality), CiscoAttrName(CISCO_ATTR_NAME("h323-incoming-conf-id"), RadiusAttr::CiscoVSA_h323_incoming_conf_id), CiscoAttrName(CISCO_ATTR_NAME("h323-preferred-lang"), RadiusAttr::CiscoVSA_h323_preferred_lang), CiscoAttrName(CISCO_ATTR_NAME("release-source"),RadiusAttr::CiscoVSA_release_source), CiscoAttrName(CISCO_ATTR_NAME("preferred-codec"),RadiusAttr::CiscoVSA_preferred_codec), CiscoAttrName(CISCO_ATTR_NAME("rewritten-e164-num"),RadiusAttr::CiscoVSA_rewritten_e164_num), CiscoAttrName(NULL, 0, 0) }; /// macro to put an integer into the buffer using big endian byte order inline void SetRadiusInteger( unsigned char* intBuffer, unsigned intValue ) { intBuffer[0] = (BYTE)((intValue >> 24) & 0xff); intBuffer[1] = (BYTE)((intValue >> 16) & 0xff); intBuffer[2] = (BYTE)((intValue >> 8) & 0xff); intBuffer[3] = (BYTE)(intValue & 0xff); } /// macro to get an integer from the buffer stored in big endian byte order inline unsigned GetRadiusInteger( const unsigned char* intBuffer ) { return (((unsigned)intBuffer[0]) << 24) | (((unsigned)intBuffer[1]) << 16) | (((unsigned)intBuffer[2]) << 8) | ((unsigned)intBuffer[3]); } } // anonymous namespace RadiusAttr::RadiusAttr() : m_type(0), m_length(0) { } RadiusAttr::RadiusAttr( unsigned char attrType, /// type of the attribute const void* attrValue, /// actual attribute value data PINDEX valueLength /// length for the attribute value ) : m_type(attrType), m_length(FixedHeaderLength) { if (valueLength > 0) PAssertNULL(attrValue); #ifdef _DEBUG PAssert(valueLength <= MaxValueLength, PInvalidParameter); #endif if (valueLength > MaxValueLength) valueLength = MaxValueLength; if (valueLength > 0) { m_length = m_length + (unsigned char)valueLength; if (attrValue != NULL) memcpy(m_value, attrValue, valueLength); } } RadiusAttr::RadiusAttr( const void* attrValue, /// buffer with data to be stored in the attribute Value PINDEX valueLength, /// data length (bytes) int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ) : m_type(VendorSpecific), m_length(VsaRfc2865FixedHeaderLength), m_vendorType(vendorType), m_vendorLength(2) { SetRadiusInteger(m_vendorId, vendorId); if (valueLength > 0) PAssertNULL(attrValue); #ifdef _DEBUG PAssert(valueLength <= VsaMaxRfc2865ValueLength, PInvalidParameter); #endif if (valueLength > VsaMaxRfc2865ValueLength) valueLength = VsaMaxRfc2865ValueLength; if (valueLength > 0) { m_length = m_length + (unsigned char)valueLength; m_vendorLength = m_vendorLength + (unsigned char)valueLength; if (attrValue != NULL) memcpy(m_vendorValue, attrValue, valueLength); } } RadiusAttr::RadiusAttr( unsigned char attrType, /// Attribute Type (see #enum AttrTypes#) const PString& stringValue /// string to be stored in the attribute Value data ) : m_type(attrType), m_length(FixedHeaderLength) { if (attrType == VendorSpecific) PAssertAlways(PInvalidParameter); PINDEX attrLength = stringValue.GetLength(); if (attrLength > MaxValueLength) attrLength = MaxValueLength; if (attrLength > 0) { m_length = m_length + (unsigned char)attrLength; memcpy(m_value, (const char*)stringValue, attrLength); } } RadiusAttr::RadiusAttr( unsigned char attrType, /// Attribute Type (see #enum AttrTypes#) int intValue /// 32 bit integer to be stored in the attribute Value ) : m_type(attrType), m_length(FixedHeaderLength + 4) { if (attrType == VendorSpecific) PAssertAlways(PInvalidParameter); SetRadiusInteger(m_value, intValue); } static unsigned _addrLen(const PIPSocket::Address & addr) { return (addr.GetVersion() == 6) ? 16 : 4; } RadiusAttr::RadiusAttr( unsigned char attrType, /// Attribute Type (see #enum AttrTypes#) const PIPSocket::Address & addressValue /// IP address to be stored in the attribute Value ) : m_type(attrType), m_length(FixedHeaderLength + _addrLen(addressValue)) { if (attrType == VendorSpecific) PAssertAlways(PInvalidParameter); if (addressValue.GetVersion() == 6) { for (unsigned i=0; i < 15; i++) m_value[i] = addressValue[i]; } else { const DWORD addr = (DWORD)addressValue; m_value[0] = ((const BYTE*)&addr)[0]; m_value[1] = ((const BYTE*)&addr)[1]; m_value[2] = ((const BYTE*)&addr)[2]; m_value[3] = ((const BYTE*)&addr)[3]; } } RadiusAttr::RadiusAttr( const PString& stringValue, /// string to be stored in the attribute Value int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ) : m_type(VendorSpecific), m_length(VsaRfc2865FixedHeaderLength), m_vendorType(vendorType), m_vendorLength(2) { SetRadiusInteger(m_vendorId, vendorId); PINDEX vsaLength = stringValue.GetLength(); #ifdef _DEBUG PAssert(vsaLength <= VsaMaxRfc2865ValueLength, PInvalidParameter); #endif if (vsaLength > VsaMaxRfc2865ValueLength) vsaLength = VsaMaxRfc2865ValueLength; if (vsaLength > 0) { m_length = m_length + (unsigned char)vsaLength; m_vendorLength = m_vendorLength + (unsigned char)vsaLength; memcpy(m_vendorValue, (const char*)stringValue, vsaLength); } } RadiusAttr::RadiusAttr( int intValue, /// 32 bit integer to be stored in the attribute Value int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ) : m_type(VendorSpecific), m_length(VsaRfc2865FixedHeaderLength + 4), m_vendorType(vendorType), m_vendorLength(2 + 4) { SetRadiusInteger(m_vendorId, vendorId); SetRadiusInteger(m_vendorValue, intValue); } RadiusAttr::RadiusAttr( const PIPSocket::Address& addressValue, /// IP address to be stored in the attribute Value int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ) : m_type(VendorSpecific), m_length(VsaRfc2865FixedHeaderLength + _addrLen(addressValue)), m_vendorType(vendorType), m_vendorLength(2 + _addrLen(addressValue)) { SetRadiusInteger(m_vendorId, vendorId); if (addressValue.GetVersion() == 6) { for (unsigned i=0; i < 15; i++) m_vendorValue[i] = addressValue[i]; } else { const DWORD addr = (DWORD)addressValue; m_vendorValue[0] = ((BYTE*)&addr)[0]; m_vendorValue[1] = ((BYTE*)&addr)[1]; m_vendorValue[2] = ((BYTE*)&addr)[2]; m_vendorValue[3] = ((BYTE*)&addr)[3]; } } RadiusAttr::RadiusAttr( unsigned char type, /// Cisco-specific attribute type bool vsaHack, /// true to not prepend attribute name to its value const PString& stringValue /// string to be stored in the attribute Value ) : m_type(VendorSpecific), m_length(VsaRfc2865FixedHeaderLength), m_vendorType(type), m_vendorLength(2) { SetRadiusInteger(m_vendorId, CiscoVendorId); if (!vsaHack) { int i = 0; while (CiscoAttrNames[i].m_name != NULL) if (CiscoAttrNames[i].m_type == type) { memcpy(m_vendorValue, CiscoAttrNames[i].m_name, CiscoAttrNames[i].m_nameLen); m_length = m_length + (unsigned char)CiscoAttrNames[i].m_nameLen; m_vendorLength = m_vendorLength + (unsigned char)CiscoAttrNames[i].m_nameLen; m_data[m_length++] = '='; m_vendorLength++; break; } else i++; } const PINDEX len = stringValue.GetLength(); if (((PINDEX)m_length + len) > MaxLength) return; memcpy(m_data + (PINDEX)m_length, (const char*)stringValue, len); m_length = m_length + (unsigned char)len; m_vendorLength = m_vendorLength + (unsigned char)len; } RadiusAttr::RadiusAttr( const void* rawData, /// buffer with the attribute raw data PINDEX rawLength /// length (bytes) of the buffer ) : m_type(0), m_length(0) { Read(rawData, rawLength); } int RadiusAttr::GetVsaVendorId() const { return GetRadiusInteger(m_vendorId); } bool RadiusAttr::Write( PBYTEArray& buffer, /// buffer the attribute data will be written to PINDEX& written, /// number of bytes written (if successful return) PINDEX offset /// offset into the buffer, where writting starts ) const { if (!IsValid()) return false; if (offset == P_MAX_INDEX) offset = buffer.GetSize(); const PINDEX len = m_length; memcpy(buffer.GetPointer(offset + len) + offset, m_data, len); written = len; return true; } bool RadiusAttr::Read(const void* rawData, PINDEX rawLength) { m_type = m_length = 0; #ifdef _DEBUG PAssertNULL(rawData); PAssert(rawLength >= FixedHeaderLength, PInvalidParameter); #endif if (rawData == NULL || rawLength < FixedHeaderLength) return false; const PINDEX len = ((const unsigned char*)rawData)[1]; if (len < FixedHeaderLength || len > rawLength || (((const unsigned char*)rawData)[0] == VendorSpecific && len < VsaFixedHeaderLength)) return false; memcpy(m_data, rawData, len); return true; } void RadiusAttr::PrintOn( ostream &strm /// Stream to print the object into. ) const { const std::streamsize indent = strm.precision() + 2; if (!IsValid()) { strm << "(Invalid) {\n"; if (m_length > 0) { const ios::fmtflags flags = strm.flags(); const PBYTEArray value((const BYTE*)m_data, m_length, FALSE); strm << hex << setfill('0') << resetiosflags(ios::floatfield) << setprecision(indent) << setw(16); if (value.GetSize() <= 32 || (flags&ios::floatfield) != ios::fixed) strm << value << '\n'; else { const PBYTEArray truncatedArray((const BYTE*)value, 32, FALSE); strm << truncatedArray << '\n' << setfill(' ') << setw(indent+4) << "...\n"; } strm << dec << setfill(' '); strm.flags(flags); } strm << setw(indent) << "}\n" << setprecision(indent-2); return; } strm << "{\n"; strm << setw(indent+7) << "type = " << (unsigned)m_type << " (" << PMAP_ATTR_TYPE_TO_NAME(m_type) << ")\n"; const PINDEX totalLen = m_length; strm << setw(indent+9) << "length = " << totalLen << " octets\n"; if (!IsVsa()) { const ios::fmtflags flags = strm.flags(); const PINDEX valueLen = (totalLen <= FixedHeaderLength) ? 0 : (totalLen - FixedHeaderLength); const PBYTEArray value((const BYTE*)m_value, valueLen, FALSE); strm << setw(indent+8) << "value = " << value.GetSize() << " octets {\n"; strm << hex << setfill('0') << resetiosflags(ios::floatfield) << setprecision(indent+2) << setw(16); if (value.GetSize() > 0) { if (value.GetSize() <= 32 || (flags&ios::floatfield) != ios::fixed) strm << value << '\n'; else { const PBYTEArray truncatedArray((const BYTE*)value, 32, FALSE); strm << truncatedArray << '\n' << setfill(' ') << setw(indent+6) << "...\n"; } } strm << dec << setfill(' ') << setprecision(indent); strm.flags(flags); strm << setw(indent+2) << "}\n"; } else { strm << setw(indent+11) << "vendorId = " << GetVsaVendorId() << '\n'; const ios::fmtflags flags = strm.flags(); PINDEX valueLen = (totalLen <= VsaFixedHeaderLength) ? 0 : (totalLen - VsaFixedHeaderLength); PINDEX headerLen = VsaFixedHeaderLength; if (valueLen > 2) { valueLen -= 2; headerLen += 2; strm << setw(indent+13) << "vendorType = " << (unsigned)m_vendorType << '\n'; strm << setw(indent+15) << "vendorLength = " << (unsigned)m_vendorLength << '\n'; } const PBYTEArray value((const BYTE*)(m_data + headerLen), valueLen, FALSE); strm << setw(indent+14) << "vendorValue = " << value.GetSize() << " octets {\n"; strm << hex << setfill('0') << resetiosflags(ios::floatfield) << setprecision(indent+2) << setw(16); if (value.GetSize() > 0) { if (value.GetSize() <= 32 || (flags&ios::floatfield) != ios::fixed) strm << value << '\n'; else { const PBYTEArray truncatedArray((const BYTE*)value, 32, FALSE); strm << truncatedArray << '\n' << setfill(' ') << setw(indent+6) << "...\n"; } } strm << dec << setfill(' ') << setprecision(indent); strm.flags(flags); strm << setw(indent+2) << "}\n"; } strm << setw(indent) << "}\n" << setprecision(indent-2); } PINDEX RadiusAttr::GetVsaValueLength() const { PINDEX len = m_length; len = (len <= VsaRfc2865FixedHeaderLength) ? 0 : (len - VsaRfc2865FixedHeaderLength); PINDEX len2 = 0; if (len > 0) { len2 = m_vendorLength; len2 = (len2 <= 2) ? 0 : (len2 - 2); } if (len2 < len) len = len2; return len; } bool RadiusAttr::GetValue(PBYTEArray& buffer, PINDEX offset) const { if (!IsValid()) return false; const PINDEX len = GetValueLength(); if (offset == P_MAX_INDEX) offset = buffer.GetSize(); if (len > 0) memcpy(buffer.GetPointer(offset + len) + offset, m_data + (IsVsa() ? VsaFixedHeaderLength : FixedHeaderLength), len ); return true; } bool RadiusAttr::GetVsaValue(PBYTEArray& buffer, PINDEX offset) const { if (!(IsValid() && IsVsa())) return false; const PINDEX len = GetVsaValueLength(); if (offset == P_MAX_INDEX) offset = buffer.GetSize(); if (len > 0) memcpy(buffer.GetPointer(len + offset) + offset, m_vendorValue, len); return true; } PString RadiusAttr::AsString() const { if (!IsValid()) return PString::Empty(); const PINDEX len = m_length; const PINDEX headerLen = (m_type == VendorSpecific) ? VsaFixedHeaderLength : FixedHeaderLength; if (len <= headerLen) return PString::Empty(); else return PString((const char*)(m_data + headerLen), len - headerLen); } int RadiusAttr::AsInteger() const { if (m_length < (FixedHeaderLength+4) || m_type == VendorSpecific) return 0; return GetRadiusInteger(m_value); } PIPSocket::Address RadiusAttr::AsAddress() const { if (m_length < (FixedHeaderLength+4) || m_type == VendorSpecific) return 0; if (m_length == (FixedHeaderLength+16)) { return PIPSocket::Address(16, (const BYTE*)m_value); } else { DWORD addr = 0; ((BYTE*)&addr)[0] = m_value[0]; ((BYTE*)&addr)[1] = m_value[1]; ((BYTE*)&addr)[2] = m_value[2]; ((BYTE*)&addr)[3] = m_value[3]; return addr; } } PString RadiusAttr::AsVsaString() const { if (!IsValid() || m_type != VendorSpecific) return PString::Empty(); const PINDEX len = m_length; if (len <= VsaRfc2865FixedHeaderLength) return PString::Empty(); else return PString((const char*)m_vendorValue, len - VsaRfc2865FixedHeaderLength); } PString RadiusAttr::AsCiscoString() const { if (!IsValid() || m_type != VendorSpecific || GetRadiusInteger(m_vendorId) != CiscoVendorId) return PString::Empty(); const PINDEX len = m_length; PINDEX offset = VsaRfc2865FixedHeaderLength; int i = 0; while (CiscoAttrNames[i].m_name != NULL) if (CiscoAttrNames[i].m_type == m_vendorType) { if (CiscoAttrNames[i].m_nameLen < (size_t)(len - offset)) if (memcmp(m_data + offset, CiscoAttrNames[i].m_name, CiscoAttrNames[i].m_nameLen) == 0 && m_data[offset + CiscoAttrNames[i].m_nameLen] == '=') offset += CiscoAttrNames[i].m_nameLen + 1; break; } else i++; if (offset >= len) return PString::Empty(); else return PString((const char*)m_data + offset, len - offset); } int RadiusAttr::AsVsaInteger() const { if (m_length < (VsaRfc2865FixedHeaderLength+4) || m_type != VendorSpecific) return 0; return GetRadiusInteger(m_vendorValue); } PIPSocket::Address RadiusAttr::AsVsaAddress() const { if (m_length < (VsaRfc2865FixedHeaderLength+4) || m_type != VendorSpecific) return 0; if (m_length == (VsaRfc2865FixedHeaderLength + 16)) { return PIPSocket::Address(16, (const BYTE*)m_vendorValue); } else { DWORD addr = 0; ((BYTE*)&addr)[0] = m_vendorValue[0]; ((BYTE*)&addr)[1] = m_vendorValue[1]; ((BYTE*)&addr)[2] = m_vendorValue[2]; ((BYTE*)&addr)[3] = m_vendorValue[3]; return addr; } } RadiusPDU::RadiusPDU() : m_code(Invalid), m_id(0) { SetLength(FixedHeaderLength); } RadiusPDU::RadiusPDU(const RadiusPDU & pdu) { CopyContents(pdu); } RadiusPDU::RadiusPDU( unsigned char packetCode, /// code - see #Codes enum# unsigned char packetId /// packet id (sequence number) ) : m_code(packetCode), m_id(packetId) { SetLength(FixedHeaderLength); } RadiusPDU::RadiusPDU( const void * rawData, /// raw data buffer PINDEX rawLength /// raw data length ) { if (!Read(rawData, rawLength)) { m_code = m_id = Invalid; SetLength(FixedHeaderLength); } } void RadiusPDU::PrintOn( ostream& strm /// Stream to print the object into. ) const { const std::streamsize indent = strm.precision() + 2; strm << ((!IsValid()) ? "(Invalid) {\n" : "{\n"); strm << setw(indent+7) << "code = " << (unsigned)m_code << " (" << PMAP_CODE_TO_NAME(m_code) << ")\n"; strm << setw(indent+5) << "id = " << (unsigned)m_id << '\n'; strm << setw(indent+9) << "length = " << GetLength() << " octets\n"; const ios::fmtflags flags = strm.flags(); const PBYTEArray value((const BYTE*)m_authenticator, AuthenticatorLength, FALSE); strm << setw(indent+28) << "authenticator = 16 octets {\n"; strm << hex << setfill('0') << resetiosflags(ios::floatfield) << setprecision(indent+2) << setw(16); strm << value << '\n'; strm << dec << setfill(' ') << setprecision(indent); strm.flags(flags); strm << setw(indent+2) << "}\n"; const PINDEX numAttributes = GetNumAttributes(); if (numAttributes == 0) strm << setw(indent+22) << "attributes = <>\n"; else { strm << setw(indent+13) << "attributes = " << numAttributes << " elements {\n"; const std::streamsize aindent = indent + 2; const RadiusAttr* attr = GetAttr(); PINDEX i = 0; while (attr != NULL) { strm << setw(aindent + 1) << "[" << i << "]= " << setprecision(aindent) << *attr << setprecision(indent); attr = GetAttr(attr); i++; } strm << setw(aindent) << "}\n"; } strm << setw(indent-1) << "}\n" << setprecision(indent-2); } bool RadiusPDU::IsValid() const { if (m_code == Invalid) return false; const PINDEX len = GetLength(); if (len < MinPduLength || len > MaxPduLength) return false; PINDEX currLen = FixedHeaderLength; while (currLen < len) { const RadiusAttr* const attr = reinterpret_cast(m_data + currLen); const PINDEX remainingLen = len - currLen; if (remainingLen < RadiusAttr::FixedHeaderLength || remainingLen < attr->GetLength() || !attr->IsValid()) break; currLen += attr->GetLength(); } return currLen == len; } void RadiusPDU::GetAuthenticator(PBYTEArray & vector, PINDEX offset) const { if (offset == P_MAX_INDEX) offset = vector.GetSize(); memcpy(vector.GetPointer(offset + AuthenticatorLength) + offset, m_authenticator, AuthenticatorLength ); } bool RadiusPDU::SetAuthenticator(const PBYTEArray & vector, PINDEX offset) { PINDEX len = vector.GetSize(); if (offset >= len) return false; len -= offset; if (len > 0) memcpy(m_authenticator, ((const BYTE*)vector)+offset, (len < AuthenticatorLength) ? len : AuthenticatorLength ); return true; } bool RadiusPDU::SetAuthenticator(const void * data) { #ifdef _DEBUG PAssertNULL(data); #endif if (data == NULL) return false; memcpy(m_authenticator, data, AuthenticatorLength); return true; } void RadiusPDU::SetAuthenticator(PRandom & random) { DWORD r = (DWORD)random; m_authenticator[0] = ((const BYTE*)&r)[0]; m_authenticator[1] = ((const BYTE*)&r)[1]; m_authenticator[2] = ((const BYTE*)&r)[2]; m_authenticator[3] = ((const BYTE*)&r)[3]; r = (DWORD)random; m_authenticator[4] = ((const BYTE*)&r)[0]; m_authenticator[5] = ((const BYTE*)&r)[1]; m_authenticator[6] = ((const BYTE*)&r)[2]; m_authenticator[7] = ((const BYTE*)&r)[3]; r = (DWORD)random; m_authenticator[8] = ((const BYTE*)&r)[0]; m_authenticator[9] = ((const BYTE*)&r)[1]; m_authenticator[10] = ((const BYTE*)&r)[2]; m_authenticator[11] = ((const BYTE*)&r)[3]; r = (DWORD)random; m_authenticator[12] = ((const BYTE*)&r)[0]; m_authenticator[13] = ((const BYTE*)&r)[1]; m_authenticator[14] = ((const BYTE*)&r)[2]; m_authenticator[15] = ((const BYTE*)&r)[3]; } void RadiusPDU::SetAuthenticator( const PString & secret, PMessageDigest5 & md5 ) { if (m_code == AccountingRequest) { const PINDEX pduLength = GetLength(); const PINDEX secretLength = secret.GetLength(); memset(m_authenticator, 0, AuthenticatorLength); md5.Start(); md5.Process(m_data, pduLength); if (secretLength > 0) md5.Process((const char*)secret, secretLength); PMessageDigest::Result digest; md5.CompleteDigest(digest); memcpy(m_authenticator, digest.GetPointer(), AuthenticatorLength); } else { PRandom random; SetAuthenticator(random); } } bool RadiusPDU::AppendAttr( const RadiusAttr & attr /// attribute to be appended ) { const PINDEX len = GetLength(); const PINDEX attrLen = attr.GetLength(); if (!attr.IsValid() || (len + attrLen) > MaxPduLength) return false; *reinterpret_cast(m_data + len) = attr; SetLength(len + attrLen); return true; } bool RadiusPDU::AppendAttr( unsigned char attrType, /// Attribute Type const void* attrValue, /// buffer with attribute Value data PINDEX valueLength /// length of attribute Value data ) { const PINDEX len = GetLength(); const PINDEX attrLen = RadiusAttr::FixedHeaderLength + valueLength; if (attrLen > RadiusAttr::MaxLength || (len + attrLen) > MaxPduLength) return false; RadiusAttr* const attr = reinterpret_cast(m_data + len); attr->m_type = attrType; attr->m_length = attrLen; memcpy(attr->m_value, attrValue, valueLength); SetLength(len + attrLen); return true; } bool RadiusPDU::AppendAttr( unsigned char attrType, /// Attribute Type const PString& stringValue /// string to be stored in the attribute Value data ) { const PINDEX len = GetLength(); const PINDEX attrLen = RadiusAttr::FixedHeaderLength + stringValue.GetLength(); if (attrLen > RadiusAttr::MaxLength || (len + attrLen) > MaxPduLength) return false; RadiusAttr* const attr = reinterpret_cast(m_data + len); attr->m_type = attrType; attr->m_length = attrLen; memcpy(attr->m_value, (const char*)stringValue, stringValue.GetLength()); SetLength(len + attrLen); return true; } bool RadiusPDU::AppendAttr( unsigned char attrType, /// Attribute Type int intValue /// 32 bit integer to be stored in the attribute Value ) { const PINDEX len = GetLength(); const PINDEX attrLen = RadiusAttr::FixedHeaderLength + 4; if (attrLen > RadiusAttr::MaxLength || (len + attrLen) > MaxPduLength) return false; RadiusAttr* const attr = reinterpret_cast(m_data + len); attr->m_type = attrType; attr->m_length = attrLen; SetRadiusInteger(attr->m_value, intValue); SetLength(len + attrLen); return true; } bool RadiusPDU::AppendAttr( unsigned char attrType, /// Attribute Type const PIPSocket::Address & addressValue /// IP address to be stored in the attribute Value ) { const PINDEX len = GetLength(); const PINDEX attrLen = RadiusAttr::FixedHeaderLength + _addrLen(addressValue); if (attrLen > RadiusAttr::MaxLength || (len + attrLen) > MaxPduLength) return false; RadiusAttr* const attr = reinterpret_cast(m_data + len); attr->m_type = attrType; attr->m_length = attrLen; if (addressValue.GetVersion() == 6) { for (unsigned i=0; i < 15; i++) attr->m_value[i] = addressValue[i]; } else { const DWORD addr = (DWORD)addressValue; attr->m_value[0] = ((const BYTE*)&addr)[0]; attr->m_value[1] = ((const BYTE*)&addr)[1]; attr->m_value[2] = ((const BYTE*)&addr)[2]; attr->m_value[3] = ((const BYTE*)&addr)[3]; } SetLength(len + attrLen); return true; } bool RadiusPDU::AppendVsaAttr( const void* attrValue, /// buffer with data to be stored in the attribute Value PINDEX valueLength, /// data length (bytes) int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ) { const PINDEX len = GetLength(); const PINDEX attrLen = RadiusAttr::VsaRfc2865FixedHeaderLength + valueLength; if (attrLen > RadiusAttr::MaxLength || (len + attrLen) > MaxPduLength) return false; RadiusAttr* const attr = reinterpret_cast(m_data + len); attr->m_type = RadiusAttr::VendorSpecific; attr->m_length = attrLen; SetRadiusInteger(attr->m_vendorId, vendorId); attr->m_vendorType = vendorType; attr->m_vendorLength = valueLength + 2; memcpy(attr->m_vendorValue, attrValue, valueLength); SetLength(len + attrLen); return true; } bool RadiusPDU::AppendVsaAttr( const PString& stringValue, /// string to be stored in the attribute Value int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ) { const PINDEX len = GetLength(); const PINDEX valueLen = stringValue.GetLength(); const PINDEX attrLen = RadiusAttr::VsaRfc2865FixedHeaderLength + valueLen; if (attrLen > RadiusAttr::MaxLength || (len + attrLen) > MaxPduLength) return false; RadiusAttr* const attr = reinterpret_cast(m_data + len); attr->m_type = RadiusAttr::VendorSpecific; attr->m_length = attrLen; SetRadiusInteger(attr->m_vendorId, vendorId); attr->m_vendorType = vendorType; attr->m_vendorLength = valueLen + 2; memcpy(attr->m_vendorValue, (const char*)stringValue, valueLen); SetLength(len + attrLen); return true; } bool RadiusPDU::AppendVsaAttr( int intValue, /// 32 bit integer to be stored in the attribute Value int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ) { const PINDEX len = GetLength(); const PINDEX attrLen = RadiusAttr::VsaRfc2865FixedHeaderLength + 4; if (attrLen > RadiusAttr::MaxLength || (len + attrLen) > MaxPduLength) return false; RadiusAttr* const attr = reinterpret_cast(m_data + len); attr->m_type = RadiusAttr::VendorSpecific; attr->m_length = attrLen; SetRadiusInteger(attr->m_vendorId, vendorId); attr->m_vendorType = vendorType; attr->m_vendorLength = 4 + 2; SetRadiusInteger(attr->m_vendorValue, intValue); SetLength(len + attrLen); return true; } bool RadiusPDU::AppendVsaAttr( const PIPSocket::Address& addressValue, /// IP address to be stored in the attribute Value int vendorId, /// 32 bit vendor identifier unsigned char vendorType /// vendor-specific attribute type ) { const PINDEX len = GetLength(); const PINDEX attrLen = RadiusAttr::VsaRfc2865FixedHeaderLength + _addrLen(addressValue); if (attrLen > RadiusAttr::MaxLength || (len + attrLen) > MaxPduLength) return false; RadiusAttr* const attr = reinterpret_cast(m_data + len); attr->m_type = RadiusAttr::VendorSpecific; attr->m_length = attrLen; SetRadiusInteger(attr->m_vendorId, vendorId); attr->m_vendorType = vendorType; attr->m_vendorLength = _addrLen(addressValue) + 2; if (addressValue.GetVersion() == 6) { for (unsigned i=0; i < 15; i++) attr->m_vendorValue[i] = addressValue[i]; } else { const DWORD addr = (DWORD)addressValue; attr->m_vendorValue[0] = ((const BYTE*)&addr)[0]; attr->m_vendorValue[1] = ((const BYTE*)&addr)[1]; attr->m_vendorValue[2] = ((const BYTE*)&addr)[2]; attr->m_vendorValue[3] = ((const BYTE*)&addr)[3]; } SetLength(len + attrLen); return true; } /// Append a string Cisco VSA attribute bool RadiusPDU::AppendCiscoAttr( unsigned char vendorType, /// vendor-specific attribute type const PString& stringValue, /// string to be stored in the attribute Value bool vsaHack /// true to not prepend attribute name to its value ) { const PINDEX len = GetLength(); PINDEX attrLen = RadiusAttr::VsaRfc2865FixedHeaderLength; if ((len + attrLen) > MaxPduLength) return false; RadiusAttr* const attr = reinterpret_cast(m_data + len); attr->m_type = RadiusAttr::VendorSpecific; attr->m_length = attrLen; SetRadiusInteger(attr->m_vendorId, RadiusAttr::CiscoVendorId); attr->m_vendorType = vendorType; attr->m_vendorLength = 2; if (!vsaHack) { int i = 0; while (CiscoAttrNames[i].m_name != NULL) if (CiscoAttrNames[i].m_type == vendorType) { attrLen += CiscoAttrNames[i].m_nameLen + 1; if ((len + attrLen) > MaxPduLength) return false; memcpy(attr->m_vendorValue, CiscoAttrNames[i].m_name, CiscoAttrNames[i].m_nameLen); attr->m_length = attr->m_length + (unsigned char)CiscoAttrNames[i].m_nameLen; attr->m_vendorLength = attr->m_vendorLength + (unsigned char)CiscoAttrNames[i].m_nameLen; attr->m_data[attr->m_length++] = '='; attr->m_vendorLength++; break; } else i++; } const PINDEX strLen = stringValue.GetLength(); attrLen += strLen; if (((PINDEX)attr->m_length + strLen) > RadiusAttr::MaxLength || (len + attrLen) > MaxPduLength) return false; memcpy(attr->m_data + (PINDEX)attr->m_length, (const char*)stringValue, strLen); attr->m_length = attr->m_length + (unsigned char)strLen; attr->m_vendorLength = attr->m_vendorLength + (unsigned char)strLen; SetLength(len + attrLen); return true; } PINDEX RadiusPDU::GetNumAttributes() const { PINDEX count = 0; const RadiusAttr* attr = GetAttr(); while (attr != NULL) { attr = GetAttr(attr); count++; } return count; } const RadiusAttr* RadiusPDU::GetAttr(const RadiusAttr * prevAttr) const { const PINDEX len = GetLength(); PINDEX offset = FixedHeaderLength; if (prevAttr != NULL) { const unsigned long ptr = reinterpret_cast(prevAttr); #ifdef _DEBUG PAssert(ptr >= reinterpret_cast(m_data + FixedHeaderLength) && ptr < reinterpret_cast(m_data + len), PInvalidParameter ); #endif offset = ptr - reinterpret_cast(m_data) + prevAttr->GetLength(); } return (offset >= len) ? NULL : reinterpret_cast(m_data + offset); } const RadiusAttr* RadiusPDU::FindAttr( unsigned char attrType, /// attribute type to be matched const RadiusAttr* prevAttr /// start element for the search operation ) const { const RadiusAttr* attr = GetAttr(prevAttr); while (attr != NULL && attr->GetType() != attrType) attr = GetAttr(attr); return attr; } const RadiusAttr* RadiusPDU::FindVsaAttr( int vendorId, /// vendor identifier to be matched unsigned char vendorType, /// vendor attribute type to be matched const RadiusAttr* prevAttr /// start element for the search operation ) const { const RadiusAttr* attr = GetAttr(prevAttr); while (attr != NULL && (!attr->IsVsa() || attr->GetVsaVendorId() != vendorId || attr->GetVsaType() != vendorType)) attr = GetAttr(attr); return attr; } bool RadiusPDU::Write(PBYTEArray& buffer, PINDEX& written, PINDEX offset) const { if (!IsValid()) return false; if (offset == P_MAX_INDEX) offset = buffer.GetSize(); const PINDEX len = GetLength(); BYTE* const buffptr = buffer.GetPointer(len + offset) + offset; memcpy(buffptr, m_data, len); written = len; return true; } bool RadiusPDU::Read(const void* rawData, PINDEX rawLength) { #ifdef _DEBUG PAssertNULL(rawData); PAssert(rawLength >= MinPduLength, PInvalidParameter); #endif m_code = m_id = Invalid; SetLength(FixedHeaderLength); if (rawData == NULL || rawLength < MinPduLength) return false; const BYTE* buffptr = (const BYTE*)rawData; memcpy(m_data, buffptr, FixedHeaderLength); buffptr += FixedHeaderLength; const PINDEX length = GetLength(); if (length > rawLength || length < MinPduLength || length > MaxPduLength) { m_code = m_id = Invalid; SetLength(FixedHeaderLength); return false; } if (length > FixedHeaderLength) { memcpy(m_attributes, buffptr, length - FixedHeaderLength); } return true; } bool RadiusPDU::Read( const PBYTEArray& buffer, /// buffer with RADIUS packet data PINDEX offset /// offset into the buffer, where data starts ) { const PINDEX len = buffer.GetSize(); if (len <= offset) return false; return Read(((const BYTE*)buffer) + offset, len - offset); } void RadiusPDU::CopyContents(const RadiusPDU& pdu) { memcpy(m_data, pdu.m_data, FixedHeaderLength); const PINDEX len = GetLength(); if (len < MinPduLength || len > MaxPduLength) { m_code = m_id = Invalid; SetLength(FixedHeaderLength); return; } if (len > FixedHeaderLength) memcpy(m_attributes, pdu.m_attributes, len - FixedHeaderLength); } bool RadiusPDU::EncryptPasswords( const PString & secret, PMessageDigest5 & md5 ) { RadiusAttr* const pwdAttr = const_cast(FindAttr(RadiusAttr::UserPassword)); if (pwdAttr == NULL) return true; /// generate 128-bit digest from shared secret and authenticator PMessageDigest::Result digest; const PINDEX secretLength = secret.GetLength(); md5.Start(); if (secretLength > 0) md5.Process((const char*)secret, secretLength); md5.Process(m_authenticator, AuthenticatorLength); md5.CompleteDigest(digest); // calculate length of the new and the old User-Password value const PINDEX origPwdLength = pwdAttr->GetValueLength(); PINDEX encPwdLength = (origPwdLength == 0) ? 16 : ((origPwdLength + 15) & (~((PINDEX)0xf))); const PINDEX len = GetLength(); if ((len + encPwdLength + RadiusAttr::FixedHeaderLength) > MaxPduLength) return false; // the encrypted password attribute will be appended as the last attribute RadiusAttr* const encPwdAttr = reinterpret_cast(m_data + len); encPwdAttr->m_type = RadiusAttr::UserPassword; encPwdAttr->m_length = encPwdLength + RadiusAttr::FixedHeaderLength; memset(encPwdAttr->m_value, 0, encPwdLength); if (origPwdLength > 0) memcpy(encPwdAttr->m_value, pwdAttr->m_value, origPwdLength); // encrypt first 16 bytes of the password DWORD* buf1ptr = reinterpret_cast(encPwdAttr->m_value); const DWORD* buf2ptr = reinterpret_cast(digest.GetPointer()); // XOR either byte-wise or dword-wise (if the memory block is aligned properly) if ((reinterpret_cast(buf2ptr) & 3) || (reinterpret_cast(buf1ptr) & 3)) { for (int _i = 0; _i < 16; _i++) ((BYTE*)buf1ptr)[_i] = ((BYTE*)buf1ptr)[_i] ^ ((const BYTE*)buf2ptr)[_i]; buf1ptr += 4; buf2ptr += 4; } else { // dword aligned data *buf1ptr = *buf1ptr ^ *buf2ptr++; ++buf1ptr; *buf1ptr = *buf1ptr ^ *buf2ptr++; ++buf1ptr; *buf1ptr = *buf1ptr ^ *buf2ptr++; ++buf1ptr; *buf1ptr = *buf1ptr ^ *buf2ptr++; ++buf1ptr; } // encrypt remaining 16 byte blocks of the password while (encPwdLength > 16) { encPwdLength -= 16; // get a new 128-bit digest for encryption md5.Start(); if (secretLength > 0) md5.Process((const char*)secret, secretLength); md5.Process(buf1ptr - 4, 16); md5.CompleteDigest(digest); buf2ptr = reinterpret_cast(digest.GetPointer()); if ((reinterpret_cast(buf2ptr) & 3) || (reinterpret_cast(buf1ptr) & 3)) { for (int _i = 0; _i < 16; _i++) ((BYTE*)buf1ptr)[_i] = ((BYTE*)buf1ptr)[_i] ^ ((const BYTE*)buf2ptr)[_i]; buf1ptr += 4; buf2ptr += 4; } else { // dword aligned data *buf1ptr = *buf1ptr ^ *buf2ptr++; ++buf1ptr; *buf1ptr = *buf1ptr ^ *buf2ptr++; ++buf1ptr; *buf1ptr = *buf1ptr ^ *buf2ptr++; ++buf1ptr; *buf1ptr = *buf1ptr ^ *buf2ptr++; ++buf1ptr; } } // delete the old (clear text) User-Password attribute and append the new // one (encrypted) at the end // this is done by overwritting the old User-Password with attributes // present after it (memory block holding remaining attributes is moved) SetLength(len + encPwdAttr->GetLength() - pwdAttr->GetLength()); memcpy(pwdAttr, reinterpret_cast(pwdAttr) + pwdAttr->GetLength(), reinterpret_cast(encPwdAttr) - reinterpret_cast(pwdAttr) + encPwdAttr->GetLength() - pwdAttr->GetLength() ); // !!! At this point pwdAttr and encPwdAttr are no longer valid! return true; } #ifndef DEFAULT_PERMANENT_SYNCPOINTS #define DEFAULT_PERMANENT_SYNCPOINTS 8 #endif RadiusSocket::RadiusSocket( WORD _port ) : m_permanentSyncPoints(DEFAULT_PERMANENT_SYNCPOINTS), m_isReading(false), m_nestedCount(0), m_idCacheTimeout(RadiusClient::DefaultIdCacheTimeout) { if (!Listen(GNUGK_INADDR_ANY, 0, _port)) { PTRACE(1, "RADIUS\tCould not bind socket to the port " << _port << " - error " << GetErrorCode(PSocket::LastGeneralError) << '/' << GetErrorNumber(PSocket::LastGeneralError) << ": " << GetErrorText(PSocket::LastGeneralError) ); Close(); } m_addr = GNUGK_INADDR_ANY; m_port = _port; if (Toolkit::Instance()->IsPortNotificationActive()) Toolkit::Instance()->PortNotification(RadiusPort, PortOpen, "udp", GNUGK_INADDR_ANY, _port); PRandom random; const unsigned _id_ = random; m_oldestId = m_nextId = (BYTE)(_id_^(_id_>>8)^(_id_>>16)^(_id_>>24)); memset(m_readSyncPoints, 0, sizeof(m_readSyncPoints)); if (IsOpen()) { memset(m_pendingRequests, 0, sizeof(m_pendingRequests)); memset(m_syncPointMap, 0, sizeof(m_syncPointMap)); memset(m_idTimestamps, 0, sizeof(m_idTimestamps)); for (int i = 0; i < 256; i++) m_readSyncPointIndices[i] = P_MAX_INDEX; for (int i = 0; i < m_permanentSyncPoints; i++) m_readSyncPoints[i] = new PSyncPoint(); } } RadiusSocket::RadiusSocket( const PIPSocket::Address & addr, WORD _port ) : m_permanentSyncPoints(DEFAULT_PERMANENT_SYNCPOINTS), m_isReading(false), m_nestedCount(0), m_idCacheTimeout(RadiusClient::DefaultIdCacheTimeout) { if (!Listen(addr, 0, _port)) { PTRACE(1, "RADIUS\tCould not bind socket to " << AsString(addr, _port) << " - error " << GetErrorCode(PSocket::LastGeneralError) << '/' << GetErrorNumber(PSocket::LastGeneralError) << ": " << GetErrorText(PSocket::LastGeneralError) ); Close(); } m_addr = addr; m_port = _port; if (Toolkit::Instance()->IsPortNotificationActive()) Toolkit::Instance()->PortNotification(RadiusPort, PortOpen, "udp", addr, _port); PRandom random; const unsigned _id_ = random; m_oldestId = m_nextId = (BYTE)(_id_^(_id_>>8)^(_id_>>16)^(_id_>>24)); memset(m_readSyncPoints, 0, sizeof(m_readSyncPoints)); if (IsOpen()) { memset(m_pendingRequests, 0, sizeof(m_pendingRequests)); memset(m_syncPointMap, 0, sizeof(m_syncPointMap)); memset(m_idTimestamps, 0, sizeof(m_idTimestamps)); int i; for (i = 0; i < 256; i++) m_readSyncPointIndices[i] = P_MAX_INDEX; for (i = 0; i < m_permanentSyncPoints; i++) m_readSyncPoints[i] = new PSyncPoint(); } } RadiusSocket::~RadiusSocket() { if (Toolkit::Instance()->IsPortNotificationActive()) Toolkit::Instance()->PortNotification(RadiusPort, PortClose, "udp", m_addr, m_port); PWaitAndSignal lock(m_readMutex); for (int i = 0; i < 256; i++) delete m_readSyncPoints[i]; } void RadiusSocket::PrintOn(ostream& strm) const { strm << "port:" << GetPort() << "[active requests: " << m_nestedCount << ", ID space: " << (PINDEX)m_oldestId << '-' << (PINDEX)m_nextId << ']'; } PINDEX RadiusSocket::AllocReadSyncPoint() { PINDEX idx = 0; for (PINDEX k = 0; k < 8; k++) if (m_syncPointMap[k] != 0xffffffff) { for (PINDEX i = 0, j = 1; i < 32; i++, j <<= 1, idx++) if ((m_syncPointMap[k] & ((DWORD)j)) == 0) { m_syncPointMap[k] |= (DWORD)j; if (m_readSyncPoints[idx] == NULL) m_readSyncPoints[idx] = new PSyncPoint(); return idx; } } else idx += 32; return P_MAX_INDEX; } void RadiusSocket::FreeReadSyncPoint(PINDEX syncPointIndex) { if (syncPointIndex < 256 && syncPointIndex >= 0) { m_syncPointMap[(syncPointIndex >> 5) & 7] &= ~(DWORD)(((DWORD)1)<<(syncPointIndex & 31)); if (syncPointIndex >= m_permanentSyncPoints) { delete m_readSyncPoints[syncPointIndex]; m_readSyncPoints[syncPointIndex] = NULL; } } } bool RadiusSocket::MakeRequest( const RadiusPDU* request, const Address& serverAddress, WORD serverPort, RadiusPDU*& pdu ) { if (!IsOpen() || request == NULL || !request->IsValid()) return false; const PINDEX length = request->GetLength(); const unsigned char id = request->GetId(); const PTimeInterval timeout = GetReadTimeout(); const PTime startTime; bool shouldRead = false; PSyncPoint* syncPoint = NULL; RadiusRequest* requestInfo = NULL; { if (!m_readMutex.Wait(timeout)) { PTRACE(4, "RADIUS\tMutex timed out for the request (id:" << (PINDEX)id << ')'); return false; } PWaitAndSignal lock(m_readMutex, FALSE); if (m_pendingRequests[id] != NULL) { PTRACE(1, "RADIUS\tDuplicate RADIUS socket request (id:" << (PINDEX)id << ')'); return false; } if (!m_isReading) m_isReading = shouldRead = true; else { const PINDEX index = AllocReadSyncPoint(); if (index == P_MAX_INDEX) { PTRACE(1, "RADIUS\tFailed to allocate a new mutex for the request (id:" << (PINDEX)id << ')'); SNMP_TRAP(8, SNMPError, Network, "Radius failed"); return false; } syncPoint = m_readSyncPoints[index]; if (syncPoint == NULL) { PTRACE(1, "RADIUS\tFailed to allocate a new mutex for the request (id:" << (PINDEX)id << ')'); SNMP_TRAP(8, SNMPError, Network, "Radius failed"); FreeReadSyncPoint(index); return false; } m_readSyncPointIndices[id] = index; m_nestedCount++; } requestInfo = m_pendingRequests[id] = new RadiusRequest(request, pdu, &serverAddress, serverPort); } m_writeMutex.Wait(); bool result = WriteTo(request, length, serverAddress, serverPort); if (!result) { PTRACE(5, "RADIUS\tError sending UDP packet (" << GetErrorCode(LastWriteError) << '/' << GetErrorNumber(LastWriteError) << ": " << GetErrorText(LastWriteError) << " (id:" << (PINDEX)id << ')'); SNMP_TRAP(10, SNMPError, Network, "Sending Radius message failed: " + GetErrorText(LastWriteError)); } m_writeMutex.Signal(); if (result) do { result = FALSE; if (shouldRead) { PIPSocket::Address remoteAddress; WORD remotePort; RadiusPDU* response = new RadiusPDU(); result = ReadFrom(response, sizeof(RadiusPDU), remoteAddress, remotePort); if (!result) { if (GetErrorCode(LastReadError) == Timeout) PTRACE(6, "RADIUS\tTimed out reading socket " << *this); else PTRACE(5, "RADIUS\tError reading socket " << *this << " (" << GetErrorCode(LastReadError) << '/' << GetErrorNumber(LastReadError) << ": " << GetErrorText(LastReadError) << ')'); SNMP_TRAP(10, SNMPError, Network, "Radius read error: " + GetErrorText(LastReadError)); delete response; response = NULL; break; } result = FALSE; PINDEX bytesRead = GetLastReadCount(); if (bytesRead < RadiusPDU::MinPduLength) { PTRACE(5, "RADIUS\tReceived packet is too small ("<< bytesRead << ')'); delete response; response = NULL; continue; } if (!response->IsValid()) { PTRACE(5, "RADIUS\tReceived packet is not a valid Radius PDU"); delete response; response = NULL; continue; } const BYTE newId = response->GetId(); if (!m_readMutex.Wait(timeout)) { PTRACE(5, "RADIUS\tTimed out (mutex) - dropping PDU (id:" << (PINDEX)newId); delete response; response = NULL; continue; } PWaitAndSignal lock(m_readMutex, FALSE); if (m_pendingRequests[newId] == NULL) { PTRACE(5, "RADIUS\tUnmatched PDU received (code:" << (PINDEX)response->GetCode() << ",id:" << (PINDEX)newId << ')' ); delete response; response = NULL; continue; } if (remoteAddress != *(m_pendingRequests[newId]->m_addr) || remotePort != m_pendingRequests[newId]->m_port) { PTRACE(5, "RADIUS\tReceived PDU from unknown address: " << AsString(remoteAddress, remotePort)); delete response; response = NULL; continue; } m_pendingRequests[newId]->m_response = response; m_pendingRequests[newId] = NULL; response = NULL; if (newId == id) { m_isReading = false; if (m_nestedCount) for (PINDEX i = 0, j = m_oldestId; i < 256; i++, j = (j + 1) & 0xff) if (m_readSyncPointIndices[j] != P_MAX_INDEX && m_readSyncPoints[m_readSyncPointIndices[j] & 0xff] != NULL) { m_readSyncPoints[m_readSyncPointIndices[j] & 0xff]->Signal(); break; } delete requestInfo; requestInfo = NULL; return true; } else if(m_readSyncPointIndices[newId] != P_MAX_INDEX && m_readSyncPoints[m_readSyncPointIndices[newId]] != NULL) { m_readSyncPoints[m_readSyncPointIndices[newId]]->Signal(); continue; } } else { result = (syncPoint != NULL && syncPoint->Wait(timeout)); if (!result) break; result = FALSE; PWaitAndSignal lock(m_readMutex); if (m_pendingRequests[id] == NULL) { FreeReadSyncPoint(m_readSyncPointIndices[id]); m_readSyncPointIndices[id] = P_MAX_INDEX; if (m_nestedCount) m_nestedCount--; delete requestInfo; requestInfo = NULL; return true; } if (!m_isReading) { m_isReading = shouldRead = true; FreeReadSyncPoint(m_readSyncPointIndices[id]); m_readSyncPointIndices[id] = P_MAX_INDEX; syncPoint = NULL; if (m_nestedCount) m_nestedCount--; continue; } continue; } if (!result) break; } while (PTime() < (startTime + timeout)); { PWaitAndSignal lock(m_readMutex); m_pendingRequests[id] = NULL; if (m_readSyncPointIndices[id] != P_MAX_INDEX) { FreeReadSyncPoint(m_readSyncPointIndices[id]); m_readSyncPointIndices[id] = P_MAX_INDEX; if (m_nestedCount) m_nestedCount--; } if (m_isReading && shouldRead) { m_isReading = false; if (m_nestedCount) for (PINDEX i = m_oldestId, j = 0; j < 256; j++, i = (i + 1) & 0xff) if (m_readSyncPointIndices[i] != P_MAX_INDEX && m_readSyncPoints[m_readSyncPointIndices[i] & 0xff] != NULL) { m_readSyncPoints[m_readSyncPointIndices[i] & 0xff]->Signal(); break; } } } delete requestInfo; requestInfo = NULL; return result ? true : false; } bool RadiusSocket::SendRequest( const RadiusPDU* request, const Address& serverAddress, WORD serverPort ) { if (!IsOpen() || request == NULL || !request->IsValid()) return false; PWaitAndSignal lock(m_writeMutex); if (WriteTo(request, request->GetLength(), serverAddress, serverPort)) return true; PTRACE(5, "RADIUS\tError sending UDP packet (" << GetErrorCode(LastWriteError) << '/' << GetErrorNumber(LastWriteError) << ": " << GetErrorText(LastWriteError) << " (id:" << (PINDEX)request->GetId() << ')'); SNMP_TRAP(10, SNMPError, Network, "Sending Radius message failed: " + GetErrorText(LastWriteError)); return false; } void RadiusSocket::RefreshIdCache(const time_t now) { const PINDEX lastId = ((m_nextId >= m_oldestId) ? m_nextId : ((PINDEX)m_nextId + 256)); const long timeout = m_idCacheTimeout.GetSeconds(); PINDEX i = m_oldestId; while (i++ < lastId && (m_idTimestamps[m_oldestId] + timeout) < now) ++m_oldestId; } PINDEX RadiusSocket::GenerateNewId() { const PTime now; const time_t nowInSeconds = now.GetTimeInSeconds(); RefreshIdCache(nowInSeconds); if (((m_nextId + 1) & 0xff) == m_oldestId) return P_MAX_INDEX; else { m_recentRequestTime = now; m_idTimestamps[m_nextId] = nowInSeconds; return m_nextId++; } } RadiusClient::RadiusClient( /// primary RADIUS server const PString& servers, /// local address for RADIUS client const PString& address, /// default secret shared between the client and the server const PString& sharedSecret ) : m_sharedSecret((const char*)sharedSecret), m_authPort(RadiusClient::GetDefaultAuthPort()), m_acctPort(RadiusClient::GetDefaultAcctPort()), m_portBase(1024), m_portMax(65535), m_requestTimeout(DefaultRequestTimeout), m_idCacheTimeout(DefaultIdCacheTimeout), m_socketDeleteTimeout(DefaultSocketDeleteTimeout), m_numRetries(DefaultRetries), m_roundRobinServers(false), m_localAddress(GNUGK_INADDR_ANY) { GetServersFromString(servers); if (!address) { if (!PIPSocket::IsLocalHost(address)) { PTRACE(1, "RADIUS\tSpecified local client address " << address << " is not bound to any local interface"); } else { PIPSocket::GetHostAddress(address, m_localAddress); } } if (PTrace::CanTrace(4)) { ostream& s = PTrace::Begin(4, __FILE__, __LINE__); const std::streamsize indent = s.precision() + 2; s << "RADIUS\tCreated instance of RADIUS client (local if: " << m_localAddress << ", default ports: " << m_authPort << ',' << m_acctPort << ") for RADIUS servers group:"; for (unsigned i = 0; i < m_radiusServers.size(); i++) s << '\n' << setw(indent + m_radiusServers[i]->m_serverAddress.GetLength()) << m_radiusServers[i]->m_serverAddress << " (auth port: " << (m_radiusServers[i]->m_authPort == 0 ? m_authPort : m_radiusServers[i]->m_authPort) << ", acct port: " << (m_radiusServers[i]->m_acctPort == 0 ? m_acctPort : m_radiusServers[i]->m_acctPort) << ')'; PTrace::End(s); } } RadiusClient::RadiusClient( PConfig & config, /// config that contains RADIUS settings const PString& sectionName /// config section with the settings ) : m_sharedSecret(Toolkit::Instance()->ReadPassword(sectionName, "SharedSecret")), m_authPort((WORD)config.GetInteger(sectionName, "DefaultAuthPort", RadiusClient::GetDefaultAuthPort())), m_acctPort((WORD)config.GetInteger(sectionName, "DefaultAcctPort", RadiusClient::GetDefaultAcctPort())), m_portBase(1024), m_portMax(65535), m_requestTimeout(config.GetInteger(sectionName, "RequestTimeout", DefaultRequestTimeout)), m_idCacheTimeout(config.GetInteger(sectionName, "IdCacheTimeout", DefaultIdCacheTimeout)), m_socketDeleteTimeout(config.GetInteger(sectionName, "SocketDeleteTimeout", DefaultSocketDeleteTimeout)), m_numRetries(config.GetInteger(sectionName, "RequestRetransmissions", DefaultRetries)), m_roundRobinServers(config.GetBoolean( sectionName, "RoundRobinServers", TRUE)), m_localAddress(GNUGK_INADDR_ANY) { GetServersFromString(config.GetString(sectionName, "Servers", "")); const PString addr = config.GetString(sectionName, "LocalInterface", ""); if (!addr) { if (!PIPSocket::IsLocalHost(addr)) { PTRACE(2, "RADIUS\tSpecified local client address '" << addr << "' is not bound to any local interface"); } else { PIPSocket::GetHostAddress(addr, m_localAddress); } } // parse port range (if it does exist) const PStringArray s = config.GetString(sectionName, "RadiusPortRange", "").Tokenise("-"); if (s.GetSize() >= 2) { unsigned p1 = s[0].AsUnsigned(); unsigned p2 = s[1].AsUnsigned(); // swap if base is greater than max if (p2 < p1) { const unsigned temp = p1; p1 = p2; p2 = temp; } if (p1 > 65535) p1 = 65535; if (p2 > 65535) p2 = 65535; if (p1 > 0 && p2 > 0) { m_portBase = (WORD)p1; m_portMax = (WORD)p2; } } if (PTrace::CanTrace(4)) { ostream& os = PTrace::Begin(4, __FILE__, __LINE__); const std::streamsize indent = os.precision() + 2; os << "RADIUS\tCreated instance of RADIUS client (local if: " << m_localAddress << ", default ports: " << m_authPort << ',' << m_acctPort << ") for RADIUS servers group:"; for (unsigned i = 0; i < m_radiusServers.size(); i++) os << '\n' << setw(indent + m_radiusServers[i]->m_serverAddress.GetLength()) << m_radiusServers[i]->m_serverAddress << " (auth port: " << (m_radiusServers[i]->m_authPort == 0 ? m_authPort : m_radiusServers[i]->m_authPort) << ", acct port: " << (m_radiusServers[i]->m_acctPort == 0 ? m_acctPort : m_radiusServers[i]->m_acctPort) << ')'; PTrace::End(os); } } RadiusClient::~RadiusClient() { socket_iterator iter = m_activeSockets.begin(); while (iter != m_activeSockets.end()) { RadiusSocket *s = *iter; iter = m_activeSockets.erase(iter); delete s; } for (unsigned i = 0; i < m_radiusServers.size(); i++) delete m_radiusServers[i]; m_radiusServers.clear(); } void RadiusClient::GetServersFromString(const PString & servers) { const PStringArray tokens = servers.Tokenise(" ;,", FALSE); for (PINDEX i = 0; i < tokens.GetSize(); i++) { PStringArray serverTokens; if (tokens[i].Left(1) == "[") { // IPv6 address PINDEX ip_end = tokens[i].Find(']'); serverTokens.SetSize(1); serverTokens[0] = tokens[i].Mid(1, ip_end-1); const PStringArray detailsTokens = tokens[i].Mid(ip_end+1).Tokenise(":", FALSE); for (PINDEX j=0; j < detailsTokens.GetSize(); ++j) { serverTokens.SetSize(serverTokens.GetSize()+1); serverTokens[j+1] = detailsTokens[j]; } } else { // IPv4 or DNS names serverTokens = tokens[i].Tokenise(":", FALSE); } if (serverTokens.GetSize() > 0) { const PString serverAddress = serverTokens[0].Trim(); if (!serverAddress) { RadiusServer* const server = new RadiusServer(); server->m_serverAddress = serverAddress; server->m_authPort = 0; server->m_acctPort = 0; if (serverTokens.GetSize() >= 2) server->m_authPort = (WORD)(serverTokens[1].AsInteger()); if (serverTokens.GetSize() >= 3) server->m_acctPort = (WORD)(serverTokens[2].AsInteger()); if (serverTokens.GetSize() >= 4) server->m_sharedSecret = serverTokens[3].Trim(); m_radiusServers.push_back(server); } } } } bool RadiusClient::SetIdCacheTimeout( const PTimeInterval & timeout /// new time interval ) { PWaitAndSignal lock(m_socketMutex); if (timeout < PTimeInterval(1000)) return false; m_idCacheTimeout = timeout; socket_const_iterator i = m_activeSockets.begin(); while (i != m_activeSockets.end()) { (*i)->SetIdCacheTimeout(timeout); ++i; } return true; } bool RadiusClient::MakeRequest( const RadiusPDU & requestPDU, /// PDU with request packet RadiusPDU * & responsePDU /// filled with PDU received from RADIUS server ) { if (!requestPDU.IsValid()) return false; bool retransmission = false; RadiusSocket* socket = NULL; unsigned char id; const unsigned numServers = m_radiusServers.size(); const PString * secret = NULL; for (unsigned i = 0; i < (m_roundRobinServers ? m_numRetries * numServers : numServers); i++) { const unsigned serverIndex = i % numServers; const PTime now; const RadiusServer* const server = m_radiusServers[serverIndex]; const WORD authPort = server->m_authPort == 0 ? m_authPort : server->m_authPort; const WORD acctPort = server->m_acctPort == 0 ? m_acctPort : server->m_acctPort; const WORD serverPort = IsAcctPDU(requestPDU) ? acctPort : authPort; const PString* const oldSecret = secret; secret = server->m_sharedSecret.IsEmpty() ? &m_sharedSecret : &server->m_sharedSecret; bool secretChanged = secret != oldSecret && oldSecret != NULL && secret->Compare(*oldSecret) != PString::EqualTo; PIPSocket::Address serverAddress; if (!PIPSocket::GetHostAddress(server->m_serverAddress, serverAddress) || !serverAddress.IsValid()) { PTRACE(3, "RADIUS\tCould not get IP address for RADIUS server " "host: " << server->m_serverAddress ); continue; } for (unsigned j = 0; j < (m_roundRobinServers ? 1 : m_numRetries); j++) { RadiusPDU* const clonedRequestPDU = new RadiusPDU(requestPDU); bool requireNewId = false; if (secretChanged || requireNewId || !retransmission) if (!GetSocket(socket, id)) { PTRACE(3, "RADIUS\tSocket allocation failed"); SNMP_TRAP(8, SNMPError, Network, "Radius failed"); delete clonedRequestPDU; return false; } secretChanged = false; clonedRequestPDU->SetId(id); PMessageDigest5 md5; clonedRequestPDU->SetAuthenticator(*secret, md5); if (!clonedRequestPDU->EncryptPasswords(*secret, md5)) { PTRACE(3, "RADIUS\tCould not encrypt passwords " "(id:" << (PINDEX)(clonedRequestPDU->GetId()) << ')' ); delete clonedRequestPDU; return false; } if( PTrace::CanTrace(3) ) { ostream& strm = PTrace::Begin(3, __FILE__, __LINE__); strm << "RADIUS\tSending PDU to RADIUS server " << server->m_serverAddress << " (" << AsString(serverAddress, serverPort) << ')' << " from " << (*socket) << ", PDU: "; if( PTrace::CanTrace(5) ) strm << *clonedRequestPDU; else strm << PMAP_CODE_TO_NAME(clonedRequestPDU->GetCode()) << ", id " << (PINDEX)(clonedRequestPDU->GetId()); PTrace::End(strm); } RadiusPDU* response = NULL; retransmission = true; if (!socket->MakeRequest(clonedRequestPDU, serverAddress, serverPort, response)) { PTRACE(3, "RADIUS\tReceive response from RADIUS server failed " "(id:" << (PINDEX)(clonedRequestPDU->GetId()) << ')'); SNMP_TRAP(8, SNMPError, Network, "Radius server failed"); delete clonedRequestPDU; continue; } if (!VerifyResponseAuthenticator( clonedRequestPDU, response, *secret)) { PTRACE(5, "RADIUS\tReceived PDU (id: " << (PINDEX)clonedRequestPDU->GetId() << ") has an invalid response authenticator" ); delete clonedRequestPDU; continue; } delete clonedRequestPDU; if (PTrace::CanTrace(3)) { ostream& strm = PTrace::Begin(3, __FILE__, __LINE__); strm << "RADIUS\tReceived PDU from RADIUS server " << server->m_serverAddress << " (" << AsString(serverAddress, serverPort) << ')' << " by socket " << (*socket) << ", PDU: "; if (PTrace::CanTrace(5)) strm << (*response); else strm << PMAP_CODE_TO_NAME(response->GetCode()) << ", id " << (PINDEX)(response->GetId()); PTrace::End(strm); } responsePDU = response; return true; } } return false; } bool RadiusClient::SendRequest( const RadiusPDU & requestPDU /// PDU with request packet ) { if (!requestPDU.IsValid()) return false; RadiusSocket* socket = NULL; unsigned char id; RadiusPDU* clonedRequestPDU = NULL; if (m_radiusServers.empty()) { PTRACE(1, "RADIUS\tNo RADIUS servers configured"); return false; } const RadiusServer* const server = m_radiusServers.front(); const WORD authPort = server->m_authPort == 0 ? m_authPort : server->m_authPort; const WORD acctPort = server->m_acctPort == 0 ? m_acctPort : server->m_acctPort; const WORD serverPort = IsAcctPDU(requestPDU) ? acctPort : authPort; const PString& secret = server->m_sharedSecret.IsEmpty() ? m_sharedSecret : server->m_sharedSecret; PIPSocket::Address serverAddress; if (!PIPSocket::GetHostAddress(server->m_serverAddress, serverAddress) || !serverAddress.IsValid()) { PTRACE(3, "RADIUS\tCould not get IP address for RADIUS server host: " << server->m_serverAddress ); return false; } clonedRequestPDU = new RadiusPDU(requestPDU); if (!GetSocket(socket, id)) { PTRACE(3, "RADIUS\tSocket allocation failed"); SNMP_TRAP(8, SNMPError, Network, "Radius failed"); delete clonedRequestPDU; return false; } clonedRequestPDU->SetId(id); PMessageDigest5 md5; clonedRequestPDU->SetAuthenticator(secret, md5); if (!clonedRequestPDU->EncryptPasswords(secret, md5)) { PTRACE(3, "RADIUS\tCould not encrypt passwords " "(id:" << (PINDEX)(clonedRequestPDU->GetId()) << ')' ); delete clonedRequestPDU; return false; } if (PTrace::CanTrace(3)) { ostream& strm = PTrace::Begin(3, __FILE__, __LINE__); strm << "RADIUS\tSending PDU to RADIUS server " << server->m_serverAddress << " (" << AsString(serverAddress, serverPort) << ')' << " from " << (*socket) << ", PDU: "; if (PTrace::CanTrace(5)) strm << *clonedRequestPDU; else strm << PMAP_CODE_TO_NAME(clonedRequestPDU->GetCode()) << ", id " << (PINDEX)(clonedRequestPDU->GetId()); PTrace::End(strm); } if (!socket->SendRequest(clonedRequestPDU, serverAddress, serverPort)) { PTRACE(3, "RADIUS\tError sending RADIUS request (id:" << (PINDEX)id << ')'); SNMP_TRAP(10, SNMPError, Network, "Sending Radius message failed"); delete clonedRequestPDU; return false; } delete clonedRequestPDU; return true; } bool RadiusClient::VerifyResponseAuthenticator( const RadiusPDU* request, const RadiusPDU* response, const PString& secret ) { PMessageDigest5 md5; PMessageDigest::Result digest; const PINDEX len = response->GetLength(); md5.Process(response, RadiusPDU::AuthenticatorOffset); md5.Process(request->GetAuthenticator(), RadiusPDU::AuthenticatorLength); if (len > RadiusPDU::FixedHeaderLength) md5.Process( reinterpret_cast(response) + RadiusPDU::FixedHeaderLength, len - RadiusPDU::FixedHeaderLength ); const PINDEX secretLength = secret.GetLength(); if (secretLength > 0) md5.Process((const char*)secret, secretLength); md5.CompleteDigest(digest); return digest.GetSize() == RadiusPDU::AuthenticatorLength && memcmp(digest.GetPointer(), response->GetAuthenticator(), RadiusPDU::AuthenticatorLength) == 0; } bool RadiusClient::IsAcctPDU(const RadiusPDU & pdu) const { const unsigned char c = pdu.GetCode(); return (c == RadiusPDU::AccountingRequest) || (c == RadiusPDU::AccountingResponse) || (c == RadiusPDU::AccountingStatus) || (c == RadiusPDU::AccountingMessage); } bool RadiusClient::GetSocket(RadiusSocket*& socket, unsigned char& id) { PWaitAndSignal lock(m_socketMutex); const socket_iterator endIter = m_activeSockets.end(); socket_iterator si = m_activeSockets.begin(); // find a first socket that is not busy (has at least one ID that can // be used for a request) while (si != endIter) { const PINDEX newId = (*si)->GenerateNewId(); if (newId != P_MAX_INDEX) { id = (unsigned char)newId; break; } else ++si; } // refresh state of remaining sockets (reclaim unused request IDs) // and delete sockets that have not been used for a long time const PTime now; socket_iterator i = m_activeSockets.begin(); while (i != endIter) { if (i == si) { ++i; continue; } (*i)->RefreshIdCache(now.GetTimeInSeconds()); if ((*i)->CanDestroy() && ((*i)->GetRecentRequestTime() + m_socketDeleteTimeout) < now) { RadiusSocket *s = *i; i = m_activeSockets.erase(i); delete s; } else ++i; } if (si != endIter) { socket = *si; return true; } // all sockets are busy, create a new one PRandom random; PINDEX randCount = (unsigned)(m_portMax-m_portBase+1) / 3; RadiusSocket* newSocket = NULL; if (randCount > 0) do { PINDEX portIndex = random % (unsigned)(m_portMax-m_portBase+1); delete newSocket; newSocket = NULL; if (m_localAddress == GNUGK_INADDR_ANY) newSocket = CreateSocket((WORD)(m_portBase + portIndex)); else newSocket = CreateSocket(m_localAddress, (WORD)(m_portBase + portIndex)); } while ((newSocket == NULL || !newSocket->IsOpen()) && --randCount); if (newSocket == NULL || !newSocket->IsOpen()) for (WORD p = m_portBase; p < m_portMax; p++) { delete newSocket; newSocket = NULL; if (m_localAddress == GNUGK_INADDR_ANY) newSocket = CreateSocket(p); else newSocket = CreateSocket(m_localAddress, p); if (newSocket && newSocket->IsOpen()) break; } if (newSocket == NULL || !newSocket->IsOpen()) { delete newSocket; return false; } newSocket->SetReadTimeout(m_requestTimeout); newSocket->SetWriteTimeout(m_requestTimeout); newSocket->SetIdCacheTimeout(m_idCacheTimeout); const PINDEX newId = newSocket->GenerateNewId(); if (newId == P_MAX_INDEX) { delete newSocket; return false; } m_activeSockets.push_back(newSocket); PTRACE(5, "RADIUS\tCreated new RADIUS client socket: " << (*newSocket)); socket = newSocket; id = (unsigned char)newId; return true; } RadiusSocket* RadiusClient::CreateSocket(const PIPSocket::Address & addr, WORD port) { return new RadiusSocket(addr, port); } RadiusSocket* RadiusClient::CreateSocket(WORD port) { return new RadiusSocket(port); } #endif /* HAS_RADIUS */ gnugk-3.4/PaxHeaders.16356/gnugk.mib0000644000175000001440000000005011754166064015337 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gnugk.mib0000644000175000001440000002055411754166064014306 0ustar00janusers00000000000000-- ***************************************************************** -- A MIB for the GNU Gatekeeper (GnuGk) -- -- May 2012, Jan Willamowius -- -- Copyright (c) 2008-2012 by Jan Willamowius, http://www.willamowius.com -- All rights reserved. -- ***************************************************************** -- GNU-GATEKEEPER-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, OBJECT-IDENTITY, Unsigned32, Counter32, enterprises FROM SNMPv2-SMI DisplayString FROM SNMPv2-TC OBJECT-GROUP, NOTIFICATION-GROUP, MODULE-COMPLIANCE FROM SNMPv2-CONF; gnugkMIB MODULE-IDENTITY LAST-UPDATED "201205070000Z" ORGANIZATION "GNU Gatekeeper Project" CONTACT-INFO " Jan Willamowius Web: http://www.gnugk.org Email: jan@willamowius.de" DESCRIPTION "SNMP MIB for the GNU Gatekeeper" REVISION "201205070000Z" DESCRIPTION "Draft" ::= { enterprises 27938 } gnugkMIBObjects OBJECT IDENTIFIER ::= { gnugkMIB 11 } gnugkTraps OBJECT-IDENTITY STATUS current DESCRIPTION "Traps for GnuGk MIB" ::= { gnugkMIBObjects 0 } gnugkStatusObjects OBJECT-IDENTITY STATUS current DESCRIPTION "Status objects" ::= { gnugkMIBObjects 1 } gnugkTrapObjects OBJECT-IDENTITY STATUS current DESCRIPTION "Data objects for traps" ::= { gnugkMIBObjects 2 } gnugkVersion OBJECT-TYPE SYNTAX OCTET STRING (SIZE(0..10)) MAX-ACCESS read-only STATUS current DESCRIPTION "GnuGk version" ::= { gnugkStatusObjects 1 } gnugkFeatures OBJECT-TYPE SYNTAX OCTET STRING (SIZE(0..255)) MAX-ACCESS read-only STATUS current DESCRIPTION "GnuGk feature string" ::= { gnugkStatusObjects 2 } gnugkRegistrations OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of endpoint registrations" ::= { gnugkStatusObjects 3 } gnugkOngoingCalls OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of ongoing calls" ::= { gnugkStatusObjects 4 } gnugkTracelevel OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "Current GnuGk trace level" ::= { gnugkStatusObjects 5 } gnugkCatchAllDestination OBJECT-TYPE SYNTAX OCTET STRING (SIZE(0..255)) MAX-ACCESS read-only STATUS current DESCRIPTION "Destination for the CatchAll routing policy" ::= { gnugkStatusObjects 6 } gnugkTotalCalls OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Total calls since startup" ::= { gnugkStatusObjects 7 } gnugkSuccessfulCalls OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Successful calls since startup" ::= { gnugkStatusObjects 8 } -- data objects for traps / notifications gnugkTrapSeverity OBJECT-TYPE SYNTAX INTEGER { error(1), warning(2), info(3) } MAX-ACCESS accessible-for-notify STATUS current DESCRIPTION "Trap severity" ::= { gnugkTrapObjects 1 } gnugkTrapGroup OBJECT-TYPE SYNTAX INTEGER { general(1), network(2), database(3), accounting(4), authentication(5), configuration(6) } MAX-ACCESS accessible-for-notify STATUS current DESCRIPTION "Trap group" ::= { gnugkTrapObjects 2 } gnugkTrapDisplayString OBJECT-TYPE SYNTAX DisplayString (SIZE(0..255)) MAX-ACCESS accessible-for-notify STATUS current DESCRIPTION "Trap description for display" ::= { gnugkTrapObjects 3 } -- traps / notifications gnugkStarted NOTIFICATION-TYPE OBJECTS { gnugkTrapSeverity, gnugkTrapGroup, gnugkTrapDisplayString } STATUS current DESCRIPTION "GnuGk was started" ::= { gnugkTraps 1 } gnugkStopped NOTIFICATION-TYPE OBJECTS { gnugkTrapSeverity, gnugkTrapGroup, gnugkTrapDisplayString } STATUS current DESCRIPTION "GnuGk was stopped" ::= { gnugkTraps 2 } gnugkConfigReload NOTIFICATION-TYPE OBJECTS { gnugkTrapSeverity, gnugkTrapGroup, gnugkTrapDisplayString } STATUS current DESCRIPTION "Configuration was reloaded" ::= { gnugkTraps 3 } gnugkModuleFailed NOTIFICATION-TYPE OBJECTS { gnugkTrapSeverity, gnugkTrapGroup, gnugkTrapDisplayString } STATUS current DESCRIPTION "The creation of an internal module failed" ::= { gnugkTraps 4 } gnugkDatabaseError NOTIFICATION-TYPE OBJECTS { gnugkTrapSeverity, gnugkTrapGroup, gnugkTrapDisplayString } STATUS current DESCRIPTION "Database operation failure" ::= { gnugkTraps 5 } gnugkIOError NOTIFICATION-TYPE OBJECTS { gnugkTrapSeverity, gnugkTrapGroup, gnugkTrapDisplayString } STATUS current DESCRIPTION "IO operation failure" ::= { gnugkTraps 6 } gnugkGeneralError NOTIFICATION-TYPE OBJECTS { gnugkTrapSeverity, gnugkTrapGroup, gnugkTrapDisplayString } STATUS current DESCRIPTION "General error" ::= { gnugkTraps 7 } gnugkAuthenticationError NOTIFICATION-TYPE OBJECTS { gnugkTrapSeverity, gnugkTrapGroup, gnugkTrapDisplayString } STATUS current DESCRIPTION "Authentication error" ::= { gnugkTraps 8 } gnugkEncodingError NOTIFICATION-TYPE OBJECTS { gnugkTrapSeverity, gnugkTrapGroup, gnugkTrapDisplayString } STATUS current DESCRIPTION "Message encoding/decoding error" ::= { gnugkTraps 9 } gnugkNetworkError NOTIFICATION-TYPE OBJECTS { gnugkTrapSeverity, gnugkTrapGroup, gnugkTrapDisplayString } STATUS current DESCRIPTION "Network error" ::= { gnugkTraps 10 } gnugkNeighborError NOTIFICATION-TYPE OBJECTS { gnugkTrapSeverity, gnugkTrapGroup, gnugkTrapDisplayString } STATUS current DESCRIPTION "Neighbor error" ::= { gnugkTraps 11 } -- conformance gnugkStatusConf OBJECT-GROUP OBJECTS { gnugkVersion, gnugkFeatures, gnugkRegistrations, gnugkOngoingCalls, gnugkTracelevel, gnugkCatchAllDestination, gnugkTotalCalls, gnugkSuccessfulCalls } STATUS current DESCRIPTION "Conformance group for GnuGk MIB" ::= { gnugkMIBObjects 999 } gnugkTrapObjectsConf OBJECT-GROUP OBJECTS { gnugkTrapSeverity, gnugkTrapGroup, gnugkTrapDisplayString } STATUS current DESCRIPTION "Conformance group for GnuGk MIB" ::= { gnugkMIBObjects 998 } gnugkTrapsConf NOTIFICATION-GROUP NOTIFICATIONS { gnugkStarted, gnugkStopped, gnugkConfigReload, gnugkModuleFailed, gnugkDatabaseError, gnugkIOError, gnugkGeneralError, gnugkAuthenticationError, gnugkEncodingError, gnugkNetworkError, gnugkNeighborError } STATUS current DESCRIPTION "Conformance group for GnuGk MIB" ::= { gnugkMIBObjects 997 } gnugkMIBConformance OBJECT IDENTIFIER ::= {gnugkMIB 5} gnugkMIBCompliance MODULE-COMPLIANCE STATUS current DESCRIPTION "Compliance statement" MODULE MANDATORY-GROUPS { gnugkStatusConf, gnugkTrapObjectsConf, gnugkTrapsConf } ::= {gnugkMIBConformance 1} END gnugk-3.4/PaxHeaders.16356/radacct.h0000644000175000001440000000005011667765730015315 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/radacct.h0000644000175000001440000000454411667765730014265 0ustar00janusers00000000000000/* * radacct.h * * RADIUS protocol accounting module for GNU Gatekeeper. * * Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz * Copyright (c) 2005-2011, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #if HAS_RADIUS #ifndef __RADACCT_H #define __RADACCT_H "@(#) $Id: radacct.h,v 1.17 2011/12/07 22:42:32 willamowius Exp $" #include "RasTbl.h" #include "gkacct.h" class RadiusClient; class RadiusPDU; /** Accounting logger for RADIUS protocol. It sends accounting call start/stop/update and NAS on/off events to a remote RADIUS server. */ class RadAcct : public GkAcctLogger { public: enum Constants { /// events recognized by this module RadAcctEvents = AcctStart | AcctStop | AcctUpdate | AcctOn | AcctOff, CiscoVendorId = 9 }; /** Create GkAcctLogger for RADIUS protocol */ RadAcct( /// name from Gatekeeper::Acct section const char* moduleName, /// config section name to be used with an instance of this module, /// pass NULL to use a default section (named "moduleName") const char* cfgSecName = NULL ); /// Destroy the accounting logger virtual ~RadAcct(); /// overriden from GkAcctLogger virtual Status Log( AcctEvent evt, const callptr& call ); private: RadAcct(); /* No copy constructor allowed */ RadAcct(const RadAcct&); /* No operator= allowed */ RadAcct& operator=(const RadAcct&); private: /// if true Cisco VSAs are appended to the RADIUS packets bool m_appendCiscoAttributes; /// NAS (GK) identifier PString m_nasIdentifier; /// NAS IP address (local interface for RADIUS client) PIPSocket::Address m_nasIpAddress; /// Fixed value for User-Name attribute in outgoing requests PString m_fixedUsername; /// timestamp formatting string PString m_timestampFormat; /// RADIUS protocol client class associated with this authenticator RadiusClient* m_radiusClient; /// false to use rewritten number, true to use the original one for Called-Station-Id bool m_useDialedNumber; /// radius attributes that do not change - x4 performance boost RadiusAttr m_attrNasIdentifier; RadiusAttr m_attrH323GwId; RadiusAttr m_attrH323CallOrigin; RadiusAttr m_attrH323CallType; }; #endif /* __RADACCT_H */ #endif /* HAS_RADIUS */ gnugk-3.4/PaxHeaders.16356/changes.txt0000644000175000001440000000005012213370243015667 xustar000000000000000020 atime=1380868658 20 ctime=1380868610 gnugk-3.4/changes.txt0000644000175000001440000025503712213370243014644 0ustar00janusers00000000000000Changes from 3.3 to 3.4 ======================= - set RTP source IP on OS that support it (Linux, FreeBSD, Win Vista+) - allow "0.0.0.0/0" in ModeSelection and ExplicitRoutes= (auto-converted to 0.0.0.0/1 + 128.0.0.0/1) - new switch [Gatekeeper::Main] MaxSocketQueue= - setting on/off config switches to y/n or a/? is now depricated, use 1/0 - new routing policy 'ldap' - new authentication modules: LDAPAliasAuth and LDAPPasswordAuth - allow non-gateways to use additive registration, fix H.460.x support, now always enabled - BUGFIX(ProxyChannel.cxx) fix DisableH245Tunneling= switch - automatically update H.235.6 media key when too many packets have been encrypted - implement H.460.26 - BUGFIX(ProxyChannel.*) forward ReleaseComplete for H.460.17 endpoints - BUGFIX(ProxyChannel.cxx) don't throw SNMP trap for PTLib error code NoError - implement TLS support to encrypt call signaling channel - access to the SSH status port can be restricted by additional IP rules in [GkStatus::Auth] - implement H.235 media encryption of data channels - BUGFIX(GkStatus.h) fix interger underflow in status port - BUGFIX(ProxyChannel.cxx) fix %{media-oip} with encrypted calls - BUGFIX(ProxyChannel.cxx) fix codec filtering with encrypted calls - new accounting variables %{caller-vendor} and %{callee-vendor} - BUGFIX(Toolkit.cxx) fix crash in 10D rewriting - new feature: send post dial digits after call connects - BUGFIX(snmp.cxx) clean shutdown of Net-SNMP agent - BUGFIX(ProxyChannel.cxx) fix master assigned RTP session IDs and encrypted H.239 channels using RTP multiplexing - BUGFIX(gk.cxx) fix checking of EP::, Neighbor:: and Routing:: sections - updated H.460.23 STUN to support _stun._udp SRV records Changes from 3.2 to 3.3 ======================= - implement H.460.18/.19 for parent/child gatekeepers - updated H.460.23/.24 support to child gatekeeper - support pre-granted ARQs: [RoutedMode] PregrantARQ=1 - SNMP traps with warnings are now disabled by default, re-enable with [SNMP] EnableWarningTraps=1 - new ./configure switches --disable-ptlibsnmp, --disable-winsnmp - new switch [Proxy] RTPDiffServ= to set DiffServ DSCP for proxied RTP, TCLASS is set for IPv6 packets - new [RewriteCLI::SQL] feature - new [Routing::NeighborSQL] policy to read Neighbor targets from database - updated [CallTable] DisabledCodecs to include UserInput Capabilities - update Presence system. Reduce registration load, PDU size and UDP fragmentation - new [GkStatus::Message] to allow reformating of status port messages - new [RasSrv::LRQFeatures] PingAlias= to reduce resource usage processing LRQ pings - new [RewriteSourceAddress] Rules to apply general CallSourceAddress prefix rewrites - new [RewriteSourceAddress] ReplaceChar to substitute characters in source address like '+' - new [RewriteSourceAddress] ForceAliasType for change address to different AliasType - new [RewriteSourceAddress] MatchSourceTypeToDestination for filtering source addresses to match destination address type - new [SQLConfig] NeighborsQuery2= for loading neighbors from database in new style format - new [RasSrv::RRQFeatures] AuthenticatedAliasesOnly to strip out any aliases that are not authenticated - new [RasSvr::RRQFeatures] GatewayAssignAliases switch to disable assigning aliases to gateways on registration - BUGFIX(gksql_*) fix SQL character quoting for SQLite, ODBC and Firebird - BUGFIX(ProxyChannel.cxx) add missing h245Tunneling element in tunneling translation - BUGFIX(gk.cxx) fix --core switch - BUGFIX(Toolkit.cxx) loop detection in AssignedGatekeeper - BUGFIX(RasSrv.h) ensure alternate gatekeepers can be read from database on GRJ and RRJ - BUGFIX(ProxyChannel.cxx) make sure H.460.19 keepalive PT doesn't conflict with encryption PT (VCS bug) - BUGFIX(ProxyChannel.cxx) fix multiplexing to VCS traversal server - BUGFIX(ProxyChannel.cxx) don't loose H.245 port on NATed calls - BUGFIX(GkClient.cxx) send valid vendor information in lightweight RRQs to parent - BUGFIX(Neighbor.cxx) add crypto token to forwarded LRQs - BUGFIX(Routing.cxx) add gateway support to CatchAll routing policy - BUGFIX(Routing.cxx) return route in ENUM policy if destination is an A record with valid TransportAddress - BUGFIX(Toolkit.cxx) ignore routes with source IPs we don't listen to - BUGFIX(RasSrv.cxx) fix mem leak in H.460.17 - BUGFIX(snmp.cxx) don't accidently start SNMP server when stopping gatekeeper - BUGFIX(h323util.cxx) remove "h323:" prefix when comparing URL_IDs - BUGFIX(RasTbl.cxx) disable SO_LINGER before LARGE_FDSET TCP Close() so it doesn't wait Changes from 3.1 to 3.2 ======================= - BUGFIX(Toolkit.cxx) remove duplicates from Home IPs - new [Gatekeeper::Main] TTLExpireDropCall switch to prevent calls being dropped due to registration timeout - added AssignedGatekeeper support to child gatekeeper - BUGFIX(GkClient.cxx) ensure HideGk also applies on GRQ messages - new [Routing::Sql] EnableRegexRewrite switch to support basic regex rewrite in SQL queries - BUGFIX(ProxyChannel.cxx) always close RTP sockets before marking them deletable - BUGFIX(ProxyChannel.cxx) clean up H.245 handler after H.460.17 calls - BUGFIX(ProxyChannel.cxx) fix tracing of RTP sequence numbers - BUGFIX(ProxyChannel.cxx) set call pointer in UDP socket to NULL when closing the call - BUGFIX(RasTbl.cxx) avoid sending a 2nd call onto a H.460.17 connection - BUGFIX(neighbor.cxx) explicitly check the H.460.24 featureID in the LCF - BUGFIX(job.cxx) delete unused worker threads after 2 minutes idle time instead of 10 min - BUGFIX(gk.cxx) work around bug in PTLib 2.10.x where housekeeping thread isn't started causing a handle leak - BUGFIX(Neighbor.cxx) don't try to proxy calls if gatekeeper in direct mode - BUGFIX(Toolkit.cxx) IP addresses in [AlternateGateekeepers::SQL] - new [Endpoint] EnableH46018 and EnableH46023 switches - new Alias SQL variable "Additive-RRQ" - support for additive registration for gateways. new switch [RasSrv::RRQFeatures] EnableAdditiveRegistration - allow multiple instances of routing policies with different settings (currently works for sql, enum, srv, dns, numberanalysis, lua, forwarding) - BUGFIX(Toolkit.cxx) remove h323: prefix if DefaultDomain is set - new switch [RoutedMode] RemoveH245AddressFromSetup= - BUGFIX(ProxyChannel.cxx) fix RTCP port for multiplexed RTP in mixed-mode calls - BUGFIX(ProxyChannel.cxx) process all tunneled H.245 messages before un-tunneling them - improve speed of H.460.19 port detection (avoid reversing direction during detection) - new ACF status item for media routing for calls - BUGFIX(ProxyChannel.cxx) check for creating RTP loops when auto-detecting H.460.19 ports without keepAlive - BUGFIX(RasSrv.cxx) fix H.460.24 3 min bug - RTP_DEBUG messages now always present on trace level 7 - extend [RasSrv::PermanentEndpoints] to support vendor information on gateways - BUGFIX(ProxyChannel.cxx) fix H.460.19 with H.460.17 endpoints - BUGFIX(ProxyChannel.cxx) fix H.460.17 IPs in trace - BUGFIX(RasSrv.cxx) relax check of sender ports for H.460.17 RRQs - BUGFIX(ProxyChannel.cxx) don't create the multiplex RTP listener when not needed - BUGFIX(ProxyChannel.cxx) fix update of H.245 tunneling flag in H.460.18 calls - BUGFIX(ProxyChannel.cxx) request RTP multiplexing even when other call leg doesn't want mutiplexing - BUGFIX(ProxyChannel.cxx) also remove outgoing H.235 tokens for IPs listed in H235RemoveCall= - BUGFIX(GkClient.cxx) forward LRQ hopCount from child to parent Changes from 3.0 to 3.1 ======================= - new switch [RoutedMode] Q931DecodingError= to specify handling of non-decodable messages - new switch [Gatekeeper::Main] MaxStatusClients= to limit number of status port connections (default: 20) - H.235 Media encryption/decryption support for a single leg of a call (EnableH235HalfCallMedia=1) - BUGFIX(GkClient.cxx) include password in RRQ to parent - new switches: [Endpoint] ProductId=, ProductVersion=, HideGk= - new switch syntax [Endpoint] Vendor=,, - support for connections with different H.245 tunneling settings on each call leg ([RoutedMode] H245TunnelingTranslation=1) - new switch: [RoutedMode] DisableH245Tunneling=1 - increase packet buffer size from 1536 to 2048 bytes - BUGFIX(GkClient.cxx) always send a valid vendorID in RRQ to parent - removed command line switch -i, use Home= in config file instead - BUGFIX(GkStatus.cxx) fix DEBUG SET for parameter values with spaces - BUGFIX(ProxyChannel.cxx) fix possible crash accessing empty InputIndications - BUGFIX(ProxyChannel.cxx) fix Answer parameter in GenericIndication to traversal server - new switch [Proxy] ExplicitRoutes= - SNMP support: [SNMP] EnableSNMP=1 - the DisabledCodecs setting doesn't have to have a trailing semikolon anymore - only enable RTP multiplexing if both parties explicitely signal their support for it - extend RemoveH235Call=1 to also remove clear and crypto tokens from Connect or to set a list of network to remove the tokens from - changed default: DisableRTPQueueing now defaults to 1 - BUGFIX(ProxyChannel.cxx) fix crash in RTP processing - new switch [Gatekeeper::Main] EnableTTLRestrictions=0 - BUGFIX(ProxyChannel.cxx) fix port on Facility callForwarded - BUGFIX(RasTbl.cxx) fix failover for calls terminated by gatekeeper - BUGFIX(RasSrv.cxx) make sure H.460.23 is only active on ARQs when it is enabled - BUGFIX(Neighbor.cxx) set travsersal server IP in ACF for calls through traversal zone - BUGFIX(ProxyChannel.cxx) fix H.239 between H.460.19 and regular endpoints - BUGFIX(ProxyChannel.cxx) fix bug sending startH245 to H.460.18 server - new switch [Neighbor::...] SendIPs= - BUGFIX(Neighbor.cxx) fix crash in SRV and RDS lookups for unregistered calls, fix memleak in RDS - new switch RemoveSorensonSourceInfo=1 - new routing policy 'forwarding' configured in [Routing::Forwarding] - [AlternateGatekeepers::SQL] switch to query DB for alternate gatekeepers - new OnQuery and OffQuery support (GK stop/start event) to SQLAcct - H.460.17 support (needs H323Plus 1.24.1) - BUGFIX(ProxyChannel.cxx) fix ForwardOnFacility for calls dialed by alias and by IP - new config section [RewriteSourceAddress] - BUGFIX(ProxyChannel.cxx) fix removal of H.460.23 feature - new experimental routing policy 'lua' (needs PTLib 2.11.x) - new switch [RadAliasAuth] EmptyUsername= - BUGFIX(Routing.cxx) use same caller alias in RouteRequest and SQL routing as for accounting - new switch [GkStatus::Filtering] NewRCFOnly=1 - BUGFIX(Routing.cxx) check if Setup contains sourceAddress before using it - BUGFIX(ProxyChannel.cxx) fix H.239 where only one sides uses H.460.19 - signal H.460.9 as desired feature if QoS monitoring is enabled - changed behavior of H.235 auth modules: allow call if one token matches, don't reject call if first token is unknown - BUGFIX(RasTbl.cxx) fix crash when handling IRRs - BUGFIX(Neighbor.cxx) rewrite destination to alias part if we find a matching SRV CS records Changes from 2.3.5 to 3.0 ========================= - BUGFIX(RasTbl.cxx) fix possibility of stale calls when using failover - when using alias "-" in RouteToGateway, no destination alias will be set - BUGFIX(gk.cxx) fix trace level if the trace file is in the config file and the trace level on the command line - [Routing::Explicit] rules now allow IP destinations to be mapped to any kind of alias destination - BUGFIX(gkauth.h,gkauth.cxx) don't check GRQ in password auth modules - BUGFIX(RasSrv.cxx) fix crash if H.460.18 client sends only invalid RAS addresses in RRQ - BUGFIX(Routing.cxx) the DNS policy didn't properly handle ports (eg. joe@company.com:1720) - rds policy now supports routing unregistered calls - new switch [Routing::ENUM] ResolveLRQ= - new switch [Routing::RDS] ResolveLRQ= - new switch [Routing::SRV] ResolveNonLocalLRQ= - TraceLevel switch in config now re-read on config reload - new switch [GkStatus::Filtering] Enable=1 - new config section [PortNotifications] to run scripts whenever GnuGk opens or closes a port and new status port command PrintCurrentCallsPorts - H.235 Media encryption/decryption support for a single leg of a call (EnableH235HalfCallMedia=1) - doesn't work, yet - stop accepting the old config format for [Gatekeeper::Main] AlternateGKs= (deprecated since 2.3.3) - IPv4-IPv6 proxying - IPv6 support - massive performance improvement when (re-)loading large numbers of GW rewrites - support for RTP multiplexing (with H.460.19) - improved H.235 password authentication with neighbors - support for gatekeeper to gatekeeper H.460.18/.19 (traversal zones) - BUGFIX(ProxyChannel.cxx) fix H.239 where only one sides uses H.460.19 - BUGFIX(RasSrv.cxx) use correct callSignalAddress for H.460.18 enabled endpoint if H.460.18 has been disabled per endpoint - BUGFIX(ProxyChannel.cxx) fix crash when handling a OLCA without mediaControlChannel on a NATed connection - always include destCallSignalAddress in Setup to avoid crash in Polycom m100 - BUGFIX(ProxyChannel.cxx) remove endpointIdentifier from Setup before forwarding - new switch [Routed::H46023SignalGKRouted] to force all signalling to be Gatekeeper routed for H.460.23/.24 - added more interop support for Sorenson equipment Changes from 2.3.4 to 2.3.5 =========================== - new switch [Routing::DNS] ResolveNonLocalLRQ=1 to configure if DNS policy resolves hostnames in LRQs that don't terminate locally - BUGFIX(ProxyChannel.cxx) silently drop GnuGk NAT message if endpoint is not NATed - BUGFIX(ProxyChannel.cxx) make sure ReleaseComplete gets dropped if it doesn't belong to a call - BUGFIX(ProxyChannel.cxx) strip Private: prefix from H.450.2 transfer destinations - new switch [Gatekeeper::Main] MaxASNArraySize= - BUGFIX(ProxyChannel.cxx) rewrite H.460.19 port detection logic - BUGFIX(RasTbl.h) make sure H.460.18 registrations expire, even without global TimeToLive - support GWRewriteE164 for neighbors by neighbor ID or neighbor GK-ID - BUGFIX(Toolkit.cxx) fix PTLib assert when using empty string as regex - actively reject Home=0.0.0.0 - enable stack protection and full RELRO on Linux - new setting [RoutedMode] H46023PublicIP to allow Non-NAT endpoints provide NAT support (default=0). - the config file is now checked for unrecognized entries and a warning is printed - config switch Fortytwo=42 is now obsolete - add encryption support (StartTLS) for H.350 (needs PTLib 2.11.x) - BUGFIX(gnugk.rc) fix tray icon for Windows service - BUGFIX(gkauth.cxx) remove newline from H.235 password from H.350 server - BUGFIX(Toolkit.cxx) fix H.350 AssignedAliases and GatekeeperDiscovery - BUGFIX(gksql_pqsql.cxx) use PQescapeStringConn() instead of PQescapeString() - add the called IP as alternativeAliasAddress to a routeCallToGK Facility if none is provided - BUGFIX(ProxyChannel.cxx) fix rare crash for H.460.18 calls - support for SSH encryption of the status port connection - BUGFIX(*.cxx) treat empty ExternalIP and NetworkInterfaces settings correctly - BUGFIX(Toolkit.cxx) fix setting of the default IP, make sure its include in the Home= setting, unless explicitly specified in Bind= - BUGFIX(Routing.cxx) populate calledIP field in RouteRequests resulting from ARQs - jitter values reported through Radius are now in msec (was codec dependent clock rate) - new switches: [RasSrv::ARQFeatures] SendRIP= and [RasSrv::LRQFeatures] SendRIP= - new status port command: PrintEndpointQoS - use H.460.9 data in the same way as sniffed RTCP stats for status port and Radius reporting - BUGFIX(RasSrv.cxx) fix handling of H.460.9 in ARQ/ACF, signal support in RCF - BUGFIX(ProxyChannel.cxx) remove callDestSignalAddress from incoming Setup if its the ExternalIP to avoid loops - BUGFIX(RasSrv.cxx) don't include a callSignalAddress in RCFs in direct mode - BUGFIX(RasSrv.cxx) fix bandwidth calculation - enable failover to CatchAll endpoint - enable failover for calls released by the gatekeeper (eg. timeouts) Changes from 2.3.3 to 2.3.4 =========================== - convert 2nd CallProceeding even if the first was sent through the status port - BUGFIX(Toolkit.cxx) fix handling of ModeSelection rules (sponsored by Charite, Berlin) - BUGFIX(gksql_mysql.cxx) set MySQL connect timeout to 10 seconds (was 10000 seconds) - BUGFIX(RasTbl.cxx) add NULL pointer checks when searching for endpoints - BUGFIX(RasSrv.cxx) fix handling of BRQs reducing the bandwidth - BUGFIX(ProxyChannel.cxx) fix H.239 from H.460.19 client (sponsored by Nanjing Southern Telecom) - BUGFIX(ProxyChannel.cxx) fix H.245 IP in H.460.18 call - BUGFIX(ProxyChannel.cxx) sourceCallSignalAddr rewrite and RemoveH235Call= were ignored when calling endpoint who uses H.460.18 - BUGFIX(yasocket.cxx) fix crash on Windows service shutdown - new switch: [EP::...]AdditionalDestinationAlias= - BUGFIX rewrite memory mangement for routeToAlias - BUGFIX(RasSrv.cxx) allow calls with zero bandwidth - BUGFIX(ProxyChannel.cxx) fix crash when call being retried is deleted by another thread - new switch: [Proxy]EnableRTCPStats=, must be enabled to send RTCP stats to Radius server - BUGFIX(ProxyChannel.cxx) fix crash in RTCP handling - new switch: [RoutedMode]AlwaysRewriteSourceCallSignalAddress=, defaults to 2.3.2 behavior Changes from 2.3.2 to 2.3.3 =========================== - BUGFIX(ProxyChannel.cxx) avoid bug in H323Plus so H.460.19 feature indicator can be removed - "make" with no target builds optnoshared now - BUGFIX(ProxyChannel.cxx) fix crash on failover with DisableRetryChecks=1 - ignore a 2nd column in SQL routing if it equals "IGNORE" - addpasswd moved to subdir - BUGFIX(routedmode.sgml): TcpKeepAlive= has always defaulted to 0 - BUGFIX(Toolkit.cxx): disregard IPv6 addresses if the machine has any - BUGFIX(SoftPBX.cxx): DisconnectIP now disconnects all calls on the IP as specified - database drivers load their libraries at runtime now, added Library= switch - documented [RoutedMode] EnableH460P= and H460PActThread= switches - BUGFIX(Routing.cxx): apply ENUM policy also to Setup and Facility messages - BUGFIX(gk.cxx): fix crash when setting a non-writable trace file in the config file - improved bandwidth management: calls that exceed the bandwidth limit will be allowed with reduced bandwidth instead of rejected - limit bandwidth per call: [Gatekeeper::Main] MinimumBandwidthPerCall= and MaximumBandwidthPerCall= - limit bandwidth for registered endpoints with [EP::...] MaxBandwidth= - change to the format of [Gatekeeper::Main] AlternateGKs= - new config section [RasSrv::AlternateGatekeeper] for alternate gatekeeper configuration by endpoint IP - new config section [Routing::Explicit] to define mappings for IP dialing - include BMV, a web front-end for SQLBill (in contrib/bmv2/) - new config section [ReplyToRasAddress] - BUGFIX(RasSrv.cxx): fix multicast discovery - document status port command: DisconnectEndpoint - H.450.2 emulator extended for unregistered calls - new switch: [CTI::MakeCall]TransferMethod= - added vendor string in RouteRequest event - extended status port commands RouteToAlias/RouteToGateway/BindAndRouteToGateway with caller-ID parameter - updated status port command: TransferCall by call-id and with transfer method - new status port command: DisconnectCallId - BUGFIX(GkClient.cxx) signal change of destination alias in parent policy - gatekeeper-based TCS=0 call transfer: activated through H.450.2 emulator or new status port command RerouteCall - BUGFIX(yasocket.cxx, RasTbl.cxx) don't accept new calls or retry calls when shutdown is in progress, avoids possible crash - BUGFIX(Routing.cxx) check if routing policies have been configured, before using them - BUGFIX(ProxyChannel.cxx) only rewrite sourceCallSignalAddress if proxying - BUGFIX(ProxyChannel.cxx) fix NAT detection for unregistered callers - BUGFIX(RasTbl.cxx) fire unregister event also when endpoint expired - BUGFIX(Toolkit.cxx) fix order of home IPs - display H323Plus and PTLib version in GnuGk's version string Changes from 2.3.1 to 2.3.2 =========================== - new GKClient supports SRV lookup on registrations - BUGFIX(RasSrv.cxx) set unresolved interface to the default gateway IP as first is loopback on windows - BUGFIX(Toolkit.cxx) Setting ExternalIP when behind a NAT on a private network - BUGFIX(ProxyChannel.cxx) fix memory leak in BuildReceiverReport() - new status port command BindAndRouteToGateway - new setting [Neighbor::...] SendAliases= to specify aliases to send to the neighbor - new setting [EP::...] AddNumbers= to add numeric aliases to an endpoint - new variable for sql routing: %{client-auth-id} - new switch [RoutedMode]RemoveFaxUDPOptionsFromRM=1 to remove t38FaxUdpOptions from t38FaxProfile eg. for Avaya Communication Manager (thanks Denis Kochmashev) - BUGFIX(Toolkit.cxx) consider H245Routed setting for implied [ModeSelection] rules for internal networks (based on patch by Denis Kochmashev) - BUGFIX(Toolkit.cxx) make sure explicit [ModeSelection] rules override implied rules for internal networks - BUGFIX(RasSrv.cxx) don't start GnuGk, if we can't open a signaling port in routed mode - BUGFIX(gksql_mysql.cxx) make sure /etc/my.cnf gets read - changed default for [CTI::MakeCall]UseH450= to 0 (supported by more endpoints) - new switch [RoutedMode]NATStdMin for registering endpoints behind NAT to support NAT standard. - extend statusport commands UnregisterIP and DisconnectIP to find endpoints on non-standard ports - BUGFIX(GkClient.cxx) fix race condition when unregistering child - BUGFIX(GkClient.h) fix memory leak - rewrite srv policy, add handling of LRQs and Setups - BUGFIX(Neighbor.cxx) fix memory leaks in neighbor, srv and rds policy - more presence work added - added H.460.24 Annex B support - add new accounting events: AcctAlert, AcctRegister, AcctUnregister - BUGFIX(ProxyChannel.cxx, gkauth.cxx) always check if DecodeSubType() was successfull before using the result value - BUGFIX(ProxyChannel.cxx) remove supportedFeature H.460.19 from Setup when called party doesn't support it - added support for multiple DefaultDomains Changes from 2.3.0 to 2.3.1 =========================== - Firebird database driver updated for Firebird 2.0.x and 2.1.x - BUGFIX(Toolkit.cxx) don't ignore ExternalIP settings when NetworkInterfaces= is set - BUGFIX(SoftPBX.cxx) make response messages more consistent - the old format (2.0) to specify neighbor gatekeepers is deprecated now and has been removed from the manual; it still works, but may be removed from future versions - BUGFIX(ProxyChannel.cxx) only close endpoints logical channel on CloseLogicalChannel, use [Proxy]SearchBothSidesOnCLC=1 to retain the old behavior - BUGFIX(Toolkit.cxx) Enable RewritePString to rewrite address correctly if unregistered full URI. - BUGFIX(Neighbor.cxx) srv policy now fails calls and doesn't pass them to the next policy if a SRV record exists for a domain, but doesn't have a destination for the call - allow to set proxy mode through 'proxy' variable in Radius h323-ivr-in attribute (thanks to Yuriy Georgiewskiy) - BUGFIX(Routing.cxx) let dns policy handle LRQs that resolve locally, so URI dialing from neighbors works - the manual has been improved (thanks to Robert Kulagowski) - set Radius VSA attribute h323_rtp_proxy in stop event (thanks to Yuriy Georgiewskiy) - BUGFIX(ProxyChannel.cxx) fix memory leak when creation of a fastStart channel fails - status port command DisconnectIP now also works with unregistered endpoints using the std port - BUGFIX(ProxyChannel.cxx) fix handling of H.460.19 RTP keepalive packets - new switch [Proxy]DisableRTPQueueing=1 - BUGFIX(ProxyChannel.cxx) fully initialize UDP proxy IPs and ports - BUGFIX(Routing.cxx) don't apply dns policy to aliases of type dialedDigits - BUGFIX(Toolkit.h/.cxx) use default IP for networks specified in InternalNetwork=, not ExternalIP - enable data execution protection on Windows versions that support it (starting with XP SP3) - BUGFIX(gk.cxx) make sure Windows service writes same trace format as application (including file and line) - BUGFIX(Neighbor.cxx) fix memory leak in srv policy, only send the local part of the alias in the LRQ - new switch [RoutedMode]H46018NoNat to disable H.460.18 if no NAT detected (default enabled) - new routing policy 'catchall' to route all calls to one alias or IP - new Radius attributes on stop: h323pddtime, h323ringtime, h323routeattempts - BUGFIX(Neighbor.cxx) fix srv policy for direct mode, don't send LRQ if SRV points to us - new switch [RoutedMode]TranslateSorensonSourceInfo=1 to convert the Sorenson VP200 caller information - BUGFIX(GkStatus.cxx) don't flush status port socket when access is denied to avoid blocking - new switches in [Gatekeeper::Main]: CompareAliasType and CompareAliasCase to switch off alias type and case matching - BUGFIX(Routing.cxx) fix dns policy for direct mode - BUGFIX(Neighbor.cxx) try next policy if SRV lookup fails - neighbor type (GnuGk|CiscoGk|ClarentGk|GlonetGk) is now case insensitive - features documented: [AssignedAliases::SQL], [AssignedGatekeepers::SQL] and [GkQoSMonitor::SQL] - BUGFIX(RasTbl.cxx) use rewritten number when decrementing prefix capacity counters, so they match the increment - feature to set a ClientAuthId in SqlAuth that is provided as %{client-auth-id} on all accounting events - new status port command ResetCallCounters - changed ProxyForSameNAT default from 0 to 1 (to avoid media failure uncertainty) - BUGFIX(RasSrv.cxx) IsReachable looks at correct route table. (virtual was ignored) - changed [RoutedMode]EnableH46024= to be consistent with H.460.18 switch - BUGFIX(RasSrv.cxx) RCF messages H.460 features now supported Features not GenericData - BUGFIX(ProxyChannel.cxx) H.460.19 working with Fast Connect - BUGFIX(RasSrv.cxx,ProxyChannel.cxx) Tandberg MXP interoperability fixes - BUGFIX(ProxyChannel.cxx) check capacity on routes supplied by authenticators (Radius, SQL etc.) - BUGFIX(RasSrv.cxx) avoid cutting off status messages after 1000 chars (problem in PString::Printf) - BUGFIX(ProxyChannel.cxx) fix for fastStart with H.460.19 - new switch [RoutedMode]EnableH.460.24=, which defaults to OFF - send RTCP statistics to Radius server (thanks to Yuriy Georgiewskiy) - BUGFIX(Toolkit.cxx) fix a crash in RewriteE164() when casting aliases - new config section [ModeSelection] to specify the routing/proxy mode by IP network Changes from 2.2.8 to 2.3.0 =========================== - codec filtering using [CallTable]DisableCodecs= or Radius (based on a patch by Georgiewskiy Yuriy) - H.460.18 and H.460.19 support - BUGFIX(Routing.cxx) fix round-robin that broke in 2.2.8 - new switch [Gatekeeper::Main]DisconnectCallsOnShutdown=0 to avoid disconnecting calls on shutdown - new switch: [Proxy] ProxyAlways=1 - new variable for sql routing: %{Calling-Station-Id} - BUGFIX(RasTbl.cxx) fix pointer check in SingleGatekeeper() - BUGFIX(RasSrv.h) fix memory leak in assignedGK code - BUGFIX(ProxyChannel.cxx) only set CallInProgress on Alerting and Connect so a 3rd and following routes can be tried on failover even when DisableRetryChecks is not set - BUGFIX(ProxyChannel.cxx) make sure the DisableRetryChecks switch also covers the cases where the remote side didn't accept the call (without a ReleaseComplete) - new switch [SimplePasswordAuth]DisableAlgorithm= to disable H.235 algorithms - re-enable H.235.1 (Annex D / Baseline Profile) when compiling against OpenH323 < 1.19.x or H323Plus - BUGFIX(gkauth.cxx) fix CAT authentication when compiling against OpenH323 < 1.19.x or H323Plus Changes from 2.2.7 to 2.2.8 =========================== - new RadAcct attribute: RewriteE164 - enable multiple failover routes with sql routing policy - BUGFIX(RasTbl.*) %{last-cdr} was wrong if last call succeeded - forward the destCallSignalAddress in ARQs to the parent gatekeeper (set [Endpoint]ForwardDestIp=0 to get the old behavior) - BUGFIX(RasTbl.cxx) never overwrite dialed_number after is has been set - BUGFIX(RasSrv.cxx) add check to avoid crash on GRQ - BUGFIX(ProxyChannel.cxx) fix reading of fragmented TPKT packets - new switches Called/CallingPlanOfNumber in [RoutedMode] and [EP::...] to set numbering plan - enable SRV policy for all OpenH323 versions as long as DNS services are available - global switches for TranslateReceivedQ931Cause and TranslateSentQ931Cause in [RoutedMode], similar to those in [EP::...] - BUGFIX(ProxyChannel.cxx) supress 2nd acct start event for 2nd Setup with same callid - allow outbound number rewrite through SQL/RADIUS modules - rewrite also aliases of type partyNumber (public and private), don't change alias type during rewrite - BUGFIX(MakeCall.cxx) MakeCall didn't work on Windows - implemented status port gai/gci commands for SQLAcct, SQLAuth, SQLPasswordAuth and SQLAliasAuth modules - BUGFIX(capctrl.cxx) added a missing lock during config reload for CapacityControl module - BUGFIX InternalPolicy should set a reject reason to something like gatewayResources/resourcesUnavailable instead of calledPartyNotRegistered when terminating gateways were found, but there was no capacity - BUGFIX(gksql.cxx) SQL reconnect thread-safety fixes - new q931cause variable in SQLAuth module - status port connection can now be closed with Ctrl-D (instead of 'exit') - selective reload on the status port: Reload - BUGFIX(radauth.cxx) fixed crash in processing h323-redirect-ip when no h323-redirect-number is present - BUGFIX (RasTbl.cxx) read GWPrefixes even if there is an EP:: section for this endpoint - BUGFIX (GkStatus.cxx) make sure status port threads don't share string memory with other threads - new compile option COMPILE_AS_SERVICE to create a native Windows service - new config options [Logfile]Filename=, [Gatekeeper::Main]TraceLevel= (same as -o and -t on cmd line) - BUGFIX (ProxyChannel.cxx) always check m_call in H.450.2 call transfer emulator - allow mutiple results per query from MySQL (and ignore all after the first) needed for using strored procedures (patch by Matteo Piscitelli) - Added Bind INI setting to set the default interface for multihomed virtual servers. - BUGFIX (Toolkit.cxx) on reload, check if new config is not empty (Fortytwo=42) - BUGFIX (ProxyChannel.cxx) add NULL pointer checks to avoid crashes - CapacityControl H.323 Id rules work now also for SetupUnreg calls - BUGFIX (ProxyChannel.cxx) make sure Q.931 cause is included in generated ReleaseComplete - new ^= and /= RewriteCLI rules for H.323 ID only rewritting - new getauthinfo/getacctinfo status port commands - Changed P2Pnat from H.460 OID to the standard allocated H.460.23/24 - BUGFIX(RasTbl.cxx): dynamically registered prefixes are added with the GatewayPriority - BUGFIX(GkClient.cxx): use [Endpoint] Type= setting for GRQ, not only for RRQ - merged P2Pnat support - added first cut of Presence support - set radius release-source attribute in stop accounting packet, like %r in sqlacct - ./configure support for Windows - new database driver 'SQLite' - new database driver 'ODBC' - auto-reconnect on database errors (for all database drivers) - allow setting a reject reason when rejecting using the sql policy Changes from 2.2.6 to 2.2.7 =========================== - clarified optional rule effect on auth modules, new "alternative" rule added WARNING: semantics of optional rules has changed slightly - BUGFIX(gk.cxx) don't look for the default config file gatekeeper.ini if -c switch is given - BUGFIX(RasTbl.cxx) honor setting of Called/CallingTypeOfNumber in [EP::..] section (patch by Mauricio Nuñez) - BUGFIX(Toolkit.cxx) fix detection of default IP when host has a default route, but no default gateway - SQLBill: implement fixed per-call billing - BUGFIX(contrib/sqlbill/sql/src/upgrade_tables.sql): fix wrong foreign key reference - implement SendProceeding command on status port (experimental) - BUGFIX(Routing.*): calls from registered endpoints to IPs should match vqueue ^.*$ - added cause code translation (see [EP::..] TranslateReceivedQ931Cause and TranslateSentQ931Cause) - Added H.450 Call Transfer emulator [RoutedMode] EnableH450.2=1 - implement MakeCall command on status port - BUGFIX SRV policy correctly detects E.164 number. - BUGFIX Radius module now sent public IP of endpoints behind NAT. - new switches [RoutedMode] GenerateCallProceeding, UseProvisionalRespToH245Tunneling (experimental) - new SQLAUth variable %{CallId} - BUGFIX(RasTbl.*,SoftPBX.cxx) status port commands (find,disconnectcall,transfercall) could influence round-robin order - enable auto-reconnect for MySQL >= 5.0 - BUGFIX(capctrl.cxx) counters were incorrectly updated for H.323ID and CLI rules - Added %{CallLink} SQL parameter for correct billing of H.450 call transfer - BUGFIX (proxychannel.cxx) Detect call signalling is using H.245 Tunneling but says it doesn't - BUGFIX (RasSrv.cxx) Endpoint registered on same machine as GnuGk should not be treated as being NAT. - added H.460.interop support for interworking of NetMeeting with everything else (req. OpenH323 v1.19.5) - added [GkQoSMonitor] H.460.9 realtime QoS monitoring (req. OpenH323 v1.19.5) - BUGFIX (statusacct.*) output callID in same format as other status port events (spaces replaced with dashes) - new status port command: printcc - BUGFIX (RasTbl.cxx) preserve internal call number for each failed call record if we have SingleFailoverCDR option enabled. This allow CapacityControl and other modules to work fine with failover enabled - BUGFIX (RasTbl.cxx) don't send URQ to permanent endpoints on shutdown - BUGFIX (Routing.cxx) fix LRQ canMapAlias detection for vqueue - BUGFIX (RasTbl.cxx) added missing include file for H.350 support - added Prefix support for MCU's - extend virtual queue functionality to unregistered calls (anonymous sponsor) - remove SignalCallId switch, now call ID is always sent with status port events - new status port command: PrintPrefixCapacities/printpc - implement rate limiting, configured by CpsLimit and CpsCheckInterval in the [RoutedMode] section, defaults to OFF - BUGFIX (RasSrv.cxx, GkClient.cxx) deal better with having no RAS interfaces at all - BUGFIX (RasSrv.cxx) ensure compatibility with PWLib < 1.9.3 - new 'sql' routing policy configured via [Routing::Sql], sponsored by CoProSys - BUGFIX (RasTbl.cxx) fix race condition where same call number could be assigned multiple times - BUGFIX (RasTbl.cxx) avoid memory reference in AliasTypeFilter that causes crash on endpoint delete - Added [GkH350::Settings] H.350 LDAP directory support (req. OpenLDAP,OpenH323 v1.19.5) - Added H.350 Authenticator module (req. OpenLDAP,OpenH323 v1.19.5) - new accounting variable %{bandwidth} - allow Radius server to send multiple destinations in "h323-redirect-number" attribute for call failover (patch by Lucas Martinez) - Added Alias Filter via [RasSrv::RRQFeatures] AliasTypeFilter - Added RDS (resolver discovery service) Routing policy - BUGFIX (RasTbl.cxx) check route capacity just before it is used for failover, not only at the beginning of the call (patch by Vladimir Voronin) - Added HTTP Service control via [RasSrv::RRQFeatures] AccHTTPLink - BUGFIX (GkStatus.cxx) add semicolon after status port 'debug cfg' output for consistency - Added [RasSrv::AssignedAlias] to gatekeeper assign aliases - Added [RasSrv::AssignedGKs] to assign gatekeepers based on alias or IP.(req. H.323v6) - Added [Gatekeeper::Main] Authenticators option to select which authenticators to use. (req. openh323 v1.19) - BUGFIX (ProxyChannel.cxx) Finally fixed same nat bug with GnuGk NAT method - [EP::xxx] PrefixCapacities: limit capacity by prefix on an endpoint - BUGFIX (gkauth.cxx) Fixed H.235.1 Authenticator - [RoutedMode] added ENUMservers setting Changes from 2.2.5 to 2.2.6 =========================== - BUGFIX(toolkit.cxx) Fixed DefaultDomain Alias rewrite - BUGFIX(routing.cxx) ENUM policy not to be used with LRQ - BUGFIX(Neighbors.cxx) Fixed SRV policy to try both CS and LR records and not use with LRQ request - remove ArjReasonRouteCallToSCN (was inactive + broken for a long time) - BUGFIX(Toolkit.cxx) enable SQLConfig also if only Firebird is available - BUGFIX(RasTbl.cxx) reject the call if no gateway has capacity, don't just use the first matching gateway - BUGFIX(RasTbl.cxx) the first route was duplicated and a call was sent twice to the same gw. Thanks to Alex Golyshev for pointing this out! - BUGFIX(RasSrv.cxx) fixed crash inside IRR handler - BUGFIX(yasocket.cxx) due to recent changes, it was impossible to send more than 10240 bytes of data when data queuing had to be involved - move status port examples to contrib/statusport/ - fix nonStandardData field usage, make use of assigned T.35 codes - BUGFIX(RasTbl.cxx) ensure that full gateways are eliminated, when failover is active and remaining gateways are sorted due to their priority correctly - BUGFIX(ProxyChannel.cxx) preserve presentation and screening indicators in Calling-Party-Number IEs - BUGFIX(RasTbl.cxx) restore default value for capacity and priority on config reload, if removed from an EP section (or if the whole section has been removed) - BUGFIX(Neighbor.cxx,ProxyChannel.cxx): fix canMapAlias for LRQs with unregistered endpoints - new config section [H225toQ931] to change the H.225 reason to Q.931 cause code mapping - optional priorities for gateway prefixes: gw-alias=prefix[:=priority][,prefix[:=priority],...] (patch by Alex Golyshev and Andrey Pasukov) - change default value for CallSignalHandler to 5 - BUGFIX(Toolkit.cxx): fix for calling between 2 internal networks - Added AcceptNonNeighborLCF to LRQFeatures settings - Added GnuGk OID & T.35 Codes - BUGFIX(Toolkit.cxx): fix to allow URI rewriting on ARQ processing - BUGFIX(ProxyChannel.cxx): fix to show correct Setup CallSignallingAddress when behind NAT - Added RemoveH235Call & RemoveH460Call Settings - BUGFIX(ProxyChannel.cxx): H.245 addresses being incorrectly written in NATHandler for public proxies. Changes from 2.2.4 to 2.2.5 =========================== - RADIUS AAA module now handles a Class attribute correctly - BUGFIX(clirw.cxx) missing iterator check, that could cause an invalid memory access, fixed - thanks to Vladimir Voronin - implement optional call-id parameter for Routing commands - Bugfix(RasSrv.cxx) fix SignalCallID=1 - Bugfix(Proxychannel.cxx) avoid inserting same socket twice in cleanup list - Bugfix(Proxychannel.cxx) make sure all members of CallSignalSocket are initialized on creation - Bugfix(yasocket.cxx) send very large status port messages in 10KB chunks and sleep after each - new feature: StatusPort filtering by Doron Bleiberg - new switch: [RoutedMode]DisableRetryChecks retries all calls - BUGFIX(ProxyChannel.cxx) make sure we preserve the original Setup message for retries and not only copy the buffer address - Bugfix(Neighbor.cxx) copy canMapAlias when forwarding LRQs - implement virtual queues for LRQs sponsored by Associated Engineering Srl. - implement canMapAlias for LCFs sponsored by TelecomUnity Ltd. - copy all unknown IEs in Notify messages when forwarding (fixes some failed transfers) - BUGFIX(*.cxx) set all deleted pointers to NULL - new SyslogAcct accounting module for the Unix syslog - (gkauth.cxx)Reapplied H235.1 (formally known as H235AnnexA) by using OpenH323 Authenticator factory loader - (rassrv.cxx)Registration Priority Added - (Proxychannel.cxx)TreatUnregisteredNat added. Treat unregistered calls unknown NAT status as being NAT - (routing.cxx)DNS SRV support to DNS Routing policy - (toolkit.cxx)RewriteAlias section added - BUGFIX(ProxyChannel.cxx) WaitAndSignal added to OnInformation to avoid NAT support crash. - BUGFIX(ProxyChannel.cxx) Check for NATSupport added. - BUGFIX(RasTbl.cxx) WaitAndSignal added to FindByEndPointid() - BUGFIX(ProxyChannel.cxx) Added SupportCallingNATedEndpoints. - Moved DNS SRV to it's own seperate policy - Registration pre-emption support added - Support for EP's on dynamic IP's - fixed code to compile with the latest pwlib CVS (STL string conversion) - Added support for GnuGk to work behind NAT box - Added support for Third party (non-neighbor) Gatekeepers (AcceptNonNeighborLRQ=1) - Added auto-detection on internal network for proxy mode. - Added DefaultDomain for H323 URI rewriting Changes from 2.2.3 to 2.2.4 =========================== - new Calling/CalledTypeOfNumber and Proxy configuration variables in [RouteMode] section and endpoint configuration. Thanks to Vladimir Voronin - (ipauth.cxx) added support for prefixes. Thanks to Vladimir Voronin - (gksql_firebird.cxx) Firebird/Interbase driver for SQL modules. Sponsored by Roitel Telecommunication S.A. - (Routing.cxx) NumberAnalysis routing policy can handle Setup messages now. Thanks to Vladimir (vl@kamatele.com) - (pwlib_compat.h) adapt to changed mutexes starting with PWLib 1.9.2 - call mute in proxy mode using userinput * (patch by Simon Horne) - NAT support for unregistered callers (patch by Simon Horne) - (gk.cxx) allow correct spelling for FortyTwo=42 entry - BUGFIX(yasocket.h) fix compilation with gcc 4.1 - (clirw.cxx) process CLI ranges with leading zeros correctly - new CapacityControl auth/acct modules to control inbound traffic - BUGFIX(ProxyChannel.cxx) check added for a NULL pointer in NAT traversal code - (RasTbl.cxx) default SignalTimeout value increased from 15 to 30 seconds - BUGFIX(RasSrv.cxx) remove call records correctly in direct signaling mode, when only called party is registered. Thanks to Julius Bajzik - BUGFIX(RasTbl.cxx) fixed race condition when accessing endpoint records - BUGFIX(ProxyChannel.cxx) fixed race condition in H245Socket when H.245 tunneling is disabled - (configure) better detection of PostgreSQL on FreeBSD - BUGFIX(RasSrv.cxx) allow bandwidth=0 in ARQ / ACF - BUGFIX(ProxyChannel.cxx) fix bug when using RADIUS/SQL based routing for Setup messages - BUGFIX(ProxyChannel.cxx) check for poor NAT implementations, that might have crashed GnuGk (by Simon Horne) - new accounting variables: %{media-oip}, %{codec} - new accounting variables: %{call-attemps}, %{last-cdr}, %{caller/callee-epid} - new feature: call failover (see manual) - BUGFIX(clirw.cxx) set presentationIndicator correctly in H.225.0 Setup UUIE - BUGFIX(RasTbl.h) when active, perform IRR checks for local endpoints only, others (parent, neighbor etc.) won't send us IRRs - BUGFIX(Routing.cxx) remove leading zeros for ENUM queries - BUGFIX(RasSrv.cxx) have AcceptNeighborsCalls default to 1 like the documentation always said - BUGFIX(Routing.cxx) fix ENUMPolicy implementation - new StatusAcct accounting module for the status port sponsored by Grupo Isec Changes from 2.2.2 to 2.2.3 =========================== - (ProxyChannel.cxx) use Calling-Party-Number to perform inbound per-gateway number rewrite (GWRewriteE164), if no sourceAddress is present in H.225.0 Setup UUIE - IRR checking for active calls [CallTable] IRRFrequency=n, IRRCheck=TRUE - BUGFIX(RasSrv.cxx) terminate all calls before unregistering endpoint when OverwriteEPOnSameAddress=1 - new RewriteCLI options to control CLIR features precisely - new CLIR/CLIP (Calling Line Identification Restriction/Presentation) features in RewriteCLI module. Ability to hide CLI (enable CLIR/CLIP) per endpoint - Unix man page in the docs folder by Ivan Lopez - do not leave sockets open when proxy handlers are idle. Peform proper cleanup all the time - manual updated with notes on TIME_WAIT and Q931PortRange/H245PortRange/T120PortRange - new SocketCleanupTimeout config variable to control time to wait before a socket is deleted - more reliable port allocation making better call throughput better on a heavily loaded server or when using very small port ranges - more detailed description of network operations - if Home contains a single IP address, bind RTP sockets to this specific address, instead of INADDR_ANY - do not remove addresses specified in Home that are not found in PWLib interface table, just generate a warning - BUGFIX(clirw.cxx) random rewrite did not work for outbound rules and comma separated items - Solaris compilation errors fixed, thanks to Mikko Oilinki - BUGFIX(yasocket.cxx) fixed address reuse for UDP sockets in LARGE_FDSET mode, broadcast RAS socket did not work previously - better error reporting on socket allocation - new ENUM routing policy from Simon Horne - added check for PWLib/OpenH323 minimum version supported - optionally signal call ids in the status port with ACF/ARJ/DCF/DRJ/RouteRequest messages (switch on with SignalCallIds=1 in main section) - NetMeeting compatibility problems fixed - new [RasSrv::RRQFeatures] IRQPollCount config variable, default number of "poll" IRQ messages changed from 2 to 1 - FileIPAuth module moved from the contrib section into the main branch - BUGFIX(ProxyChannel.cxx) tunnelled H.245 messages were not processed correctly Changes from 2.2.1 to 2.2.2 =========================== - allow empty key values in SQLConfig ConfigQuery/RewriteE164Query - restored MSVC6.0 compatibility - BUGFIX(radproto.cxx) GetSocket could use an invalidated iterators under some circumstances. Thanks to kubuqi cn - (ProxyChannel.cxx) ignore incorrect mediaChannel offers with port number 0, internal option [Proxy] RemoveMCInFastStartTransmitOffer to enable removing mediaChannel from fast start transmit channel open offers - creditamout and billingmode fields may appear in a result for SQLAuth CallQuery - exclude all SQL modules from build if no SQL support is enabled - BUGFIX(ProxyChannel.cxx) originate an H.245 TCP connection from the same IP as an associated signalling connection is established - (clirw.cxx) new CLI rewrite types - prefix replacement (*=) and an identity match (~=) - don't lock the call table when logging accounting data on call disconnect, to prevent gatekeeper long lock periods when a backend is not working properly - new TranslateFacility config variable to enable Facility message conversion between H.323v4 and previous versions of the protocol - %t, %p, %{ring-time}, %{alerting-time} accounting variables ported from 2.0 branch. ConnectTimeout config variable replaced with SignalTimeout and AlertingTimeout - new %r accounting variable to get who disconnected a call, thanks to Freddy Parra - new generic SQLAuth module to authenticate and authorize RRQ, ARQ, LRQ and Setup messages - BUGFIX(ProxyChannel.cxx) another attempt to solve race issues in H.245 routed mode that may cause occasional crashes - new --core command line argument to enable core dump generation for Unix - new Vendor config variable for [Endpoint] section to provide vendor specific extensions when registering with a parent gatekeeper - added support for Connect event in SqlAcct module, thanks to Boian Bonev - BUGFIX(Neighbor.cxx) nonStandardData field was not included for CiscoGK neighbors - a new module to rewrite ANI/CLI numbers. Sponsored by Gabriel Georgescu. - new FreeBSD 5.3 startup script - BUGFIX(ProxyChannel.cxx) send proper Release Complete when the remote side does not respond to a Setup message - BUGFIX(ProxyChannel.cxx) correctly remove calls from the call table, when setup processing fails for some reason (auth failed, acct failed, ...) - BUGFIX(ProxyChannel.cxx) fixed config reload deadlock when using H.245 routed mode and listening sockets are holding the config reload mutex - (Routing.cxx) new routing policy NumberAnalysis to support overlapped digit sending with ARQs - WinSvc from Franz J Ehrengruber updated to work with GnuGk 2.2.2 and 2.0.10 - new FileIPAuth module in the contrib/ipauth directory - (RasTbl.cxx) call accounting updates/disconnecting is now more robust and does not lock up the call table and (effectively) the gatekeeper for long time periods - Makefile for docs, make sure generated PDF manual is searchable - do not support mutiple rewrite targets, as this feature does not work well if rewrite is performed more than once - BUGFIX(GkStatus.cxx) the gatekeeper could crash if the connection was closed before the welcome message has been sent - BIGFIX(gkacct.cxx) different Username was reported during Setup auth and acct step, if no sourceAddress has been present for an unregistered call - more missing config reload locks added to allow seamless config reload - BUGFIX(ProxyChannel.cxx) the code could reference an invalid H.245 socket due to recent changes - BUGFIX(h323util.cxx) conference/call-id could get incorrectly converted to a string with some compilers - (Routing.cxx) ability to do per-call proxy mode control from routing handlers - BUGFIX(ProxyChannel.cxx) fixed race condition between signaling/H.245 channel closure - parts of signalling code rewritten - default value for config variable ForwardOnFacility changed to 0 - BUGFIX(ProxyChannel.cxx) inbound rewrite code did not check for presence of sourceCallSignalAddress field in Setup messages. Use real peer IP instead - ability to encrypt all passwords in the config (radius shared secrets, database passwords). A new EncryptAllPasswords config variable and KeyFilled usage extended - SQLConfig ported from 2.0 - BUGFIX(radauth.cxx) Framed-IP-Address could not be determined for unregistered calls with no Setup-UUIE.sourceCallSignalAddress field, causing authentication to fail - (ProxyChannel.cxx) small optimizations in signaling handling - fixed bug with aliases handling in various places, introduced by recent changes - BUGFIX(Toolkit.cxx) correct handling of '!' character in rewrite rules - provide proper handling of aliases of type partyNumber (e164Number or privateNumber) - BUGFIX(ProxyChannel.cxx) fix for RTP/Q931/H245/T120PortRange to correct a bug with port range wraparound if the last port is 65535. This caused a next port to be set to 0 and any subsequent port allocation to fail - BUGFIX(ProxyChannel.cxx) dynamic allocation of RTP ports did not work, use a fixed range 1024-65535 as a default for RTPPortRange config variable - (RasTbl.cxx) LRQs received on a multicast interface triggered LCFs with an invalid signalling port number (0) in signalling routed mode - get rid of redundant config reload mutex inside accounting modules - obsolete auth modules MySQLAliasAuth and MySQLPasswordAuth are now removed - (GkStatus.cxx) fixed a missing lock during config reload - accept LRQs from any port (check only an IP address) - SQL modules accept only one database host now (the failover was not supported properly either, so it has been removed) Changes from 2.2.0 to 2.2.1 =========================== - enchanced prefix matching for routing policies. A dot (.) matches any digit - enchanced prefix matching for neighbors. A dot (.) matches any digit, ! at the beginning disables the prefix - (ProxyChannel.cxx) critical fix - a missing lock during config reload caused the gatekeeper to crash - (ProxyChannel.cxx) more reliable port number selection for Q.931, H.245, T.120 and RTP port ranges (before, a config reload could cause many calls to fail because of unability to get a new socket) - (ProxyChannel.cxx) default setting for RTPPortRange is now to let the OS select a port number - more flexible rewrite rules (both global and per-gw) with '.' and '%' wildcard characters - security fix for socket handle/select oveflow issue - new accounting event 'connect' (only in routed mode) - timestamp microseconds field has a fixed length of 3 digits - enchanced prefix matching for gateways. A dot (.) matches any digit, ! at the beginning disables the prefix - BUGFIX(radproto.cxx) VSA matching routing ignored a vendor identifier causing incorrect attributes to be matched - insert missing Calling-Party-Number-IE/Display-IE if corresponing Screen... options are enabled - shutdown the gatekeeper if there are errors in SQL auth/acct modules configuration, thanks to Mikko Oilinki - (gkslq.cxx) try to reconnect to the database, if the initial attempt failed. Thanks to Mikko Oilinki - another changes to Calling/Called-Station-Id handling, always use Calling/Called-Party-Number IEs when available. Calling/Called-Station-Id is now set outside auth modules, so acct modules receive correct values, independetly of auth modules usage - BUGFIX(Toolkit.cxx) correctly replace %u in timestamps with number of microseconds instead of passing %u to strftime - ability to select Called-Station-Id number type between the original one (dialed number) and the rewritten one. New UseDialedNumber config option for RadAuth/RadAliasAuth/RadAcct modules, new %{Dialed-Number} variable for SQLAcct and FileAcct modules - ability to customize timestamp formats. New TimestampFormat config variables for main, SqlAcct, RadAcct, FileAcct and CallTable sections - (ProxyChannel.cxx) safety check before a signaling socket is actually deleted for being removed from an associated call record - new TcpKeepAlive option to remove problem with network errors and hanging calls (see docs/keepalive.txt for more details) - RadAuth/RadAliasAuth modules can now add/remove endpoint aliases during endpoint registration (using h323-ivr-in=terminal-alias: Cisco AV-Pair) - per-call proxy control - new status port command RouteToGateway Changes from 2.2beta5 to 2.2.0 ============================== - BUGFIX(RasSrv.cxx) invalid/unsupported RAS/signaling addresses are ignored in RRQ messages - GCC 3.4.2 compilation errors fixed - BUGFIX(gkauth.cxx) PrefixAuth module crashed when checking ARQ without a destinationInfo field Changes from 2.2beta4 to 2.2beta5 ================================= - BUGFIX(ProxyChannel.cxx) race condition with NAT sockets fixed, thanks to Daniel Liu - new RoundRobinGateways config option in the RasSrv::ARQFeatures sections to enable/disable round-robin gateway selection - call capacity limits and gateway prioritization (new EP:: config sections) - BUGFIX(ProxyChannel.cxx) errors during H.245 channel establishment caused calls to not disconnect correctly - check VirtualQueueRegex on startup - BUGFIX(gk.cxx) reopen a log file on reload (HUP) signal to allow logrotate to manage logs - BUGFIX(RasTbl.cxx) multithreading issues with an access to endpoint record data fixed. Thanks to kubuqi cn! - BUGFIX(ProxyChannel.cxx) RTP proxy handling moved to a separate RTP proxy threads. New RtpHandlerNumber config option - BUGFIX(yasocket.cxx) enabled REUSE_ADDRESS option on listening sockets in non-LARGE_FDSET mode to allow fast gatekeeper restarting - better disconnect cause reporting in Setup processing - ability to set call destination in auth modules. RADIUS based call routing - BUGFIX(Neighbor.cxx) fixed sourceInfo LRQ field handling - now it contains an H.323 identifier of the gatekeeper - (radproto.cxx) ability to set shared secrets for each radius server separatelly, more Radius optimizations - new, much faster, Radius client implementation. Thanks to Pavel Pavlov for numerous ideas and suggestions! - BUGFIX(ProxyChannel.cxx) Called-Party-Number-IE rewrite occured too late, causing auth/acct modules to receive the original number instead of the rewritten one - (RasSrv.cxx) duplicated RAS messages are now simply discarded - BUGFIX(ProxyChannel.cxx) fixed proxying of RTP packets, so RTP sockets are not closed on temporary errors (like remote socket not yet ready). This bug affected especially NAT traversal and situation, when audio was sent very early, when reverse proxy path has not been yet established - BUGFIX(RasSrv.cxx) fixed handling of RRJ (fullRegistrationRequired) from an alternate GnuGk - BUGFIX(GkStatus.cxx) fixed bug in status client authentication introduced by recent changes, thanks to Razvan Radu! - (sqlbill) examples how to setup SQLAcct, SQLPasswordAuth and SQLAliasAuth with the billing - direct SQL accounting module (SQLAcct) ported from 2.0 branch - BUGFIX(gkauth.cxx) gcc 2.95.x compilation errors fixed - BUGFIX(Neighbor.cxx) handling reply messages (RIP/LCF/LRJ) from neighbors fixed, thanks to kubuqi cn (kubuqi@hotmail.com) - support for CallCreditServiceControl in RCF and ACF messages, which allows reporting call duration limit and user's account balance to endpoints. Currently RadAuth and RadAliasAuth modules support this feature. - BUGFIX(gk.cxx) removed an invalid test for the log filename, preventing from creating log files in a directory other than the current one - BUGFIX(GkClient.cxx) TTL bug fixed (RRQ every second), documentation updates - fixed assertions caused by attempts to get/set config keys with an empty key name - BUGFIX(gk.cxx) fixed log file rotation under Windows, thanks to Cristian Bullokles! - log file rotation, new LogFile config section, new setlog and rotatelog status interface commands - BUGFIX(gkacct.cxx) a fix to rotation interval calculation for monthly CDR file rotation - BUGFIX(gkacct.cxx) FileAcct monthly rotation rotated a CDR file unnecessary on gatekeeper restart - BUGFIX(gkacct.cxx) fixed a critical bug in Username determination for unregistered endpoints, introduced by recent changes - BUGFIX(RasSrv.cxx) do not include an invalid access token (with null object identifier) in LCF to prevent interoperability problems, thanks to Andreas Sikkema! - BUGFIX(ProxyChannel.cxx) better handling of multiple calls over a single signalling channel by setting multipleCalls and maintainConnection H.225.0 fields to FALSE in all messages. Thanks to Ian Campbell! - better Username, Calling-Station-Id and Called-Station-Id determination, consistant across all auth/acct modules, redundant code removed - new Gatekeeper::Auth flag SetupUnreg to toggle Q.931 Setup authentication for unregistered endpoints only - IncludeEndpointIP flag for RadAuth, RadAliasAuth and RadAcct is obsolete, these modules will always send Framed-IP-Address - (sqlbill) reject gracefully Access-Requests without Framed-IP-Address - (sqlbill) improvements for IP only authorization - BUGFIX(sqlbill) FreeRadius accounting_stop_query changed to not check h323id in order to allow IP only authorization - BUGFIX(sqlbill) FreeRadius accounting_update_query changed to not check h323id in order to allow IP only authorization - (radacct.cxx) new RADIUS h323-ivr-out=h323-call-id parameter that contains an H.323 Call Identifier - BUGFIX(radauth.cxx) better Framed-IP-Address handling in RadAliasAuth Setup check - BUGFIX (ProxyChannel.cxx) With a certain combination of endpoints it was possible to have UDPProxySocket trying to forward incoming RTP / RTCP trafic to 127.0.0.1:0. Added a check for this situation and send traffic to non-loopback address and hope it is the correct address. - (sqlbill) added ability to authenticate users only by their IP (ignoring User-Name) - BUGFIX(radauth.cxx) RadAliasAuth will use Calling-Party-Number as User-Name if no sourceAddress inside is present in Setup-UUIE - (sqlbill) new, more flexible tariff/rating engine - BUGFIX(gk.cxx) fixed signal handling to use sigaction instead of signal to prevent accidental gatekeeper crashes because of restored signal handlers - BUGFIX(gkacct.cxx) rotation per number of lines works correctly now Changes from 2.2beta3 to 2.2beta4 ================================= - BUGFIX(radauth.cxx) check if generalID matches one of endpoint's aliases only when authenticating RRQ - (gkacct.cxx) parametrized FileAcct CDR output - BUGFIX(gkauth.cxx) do not declare H.235 Auth Procedure I support as it is not fully implemented - (RasSrv.cxx) do not copy nonStandardData field from RAS requests to RAS replies, as it does not make any sense and cause interoperability problems with some Cisco IOSes - (yasocket.cxx) fixed a bug in TCPListener::IsTimeout() which prevented GNUgk from running > 596 hours - (ProxyChannel.cxx) fixed a critical bug with referencing already deleted socket, when the remote party did not accept signalling tcp connection - (ProxyChannel.cxx) more UDP proxy optimizations - new flexible CDR file rotation for FileAcct module - (gktimer.cxx) new generic support for time-based events - BUGFIX(Routing.cxx) processing LRQ request should not apply round robin selection to gateways with the same prefix - BUGFIXes: make sure we delete list items _after_ we have removed them from the list - BUGFIX(RasTbl.cxx) make sure PrintCurrentCalls prints ACF messages in the same format as all ACFs - improved performance of the socket code (especially when LARGE_FDSET is enabled). Faster select for a single socket and an unnecessary write lock for UDP sockets removed - BUGFIX(radproto.cxx) default auth/acct ports are now set to fixed values 1812 and 1813, due to problems with getservbyname and multithreading - (gksql.cxx) improved SQL query parameters replacement routine - removed descendancy check in radproto to allow compilation with the latest RTTI enabled PWLib - BUGFIX(Neighbor.cxx) fixed missing parentheses, thanks to kubuqi cn - BUGFIX(h323util.cxx) GetIPAndPortFromTransportAddr now checks if the address is valid - BUGFIX(ProxyChannel.cxx) fixed bugs in ForwardCall, thanks to Daniel Liu - header file usage more consistent - SQL authenticators moved to a separate file - added VSNET2003 sln and vcproj files - fixed Windows DSP files to not enable some optimizations for Windows that result in VC6 linker throwing away some gnugk modules (static variables) - improved configure script to detect installed PWLib/OpenH323 - significant API changes to authentication/accounting modules - added logparser application from Saad Faisal to the contrib section - pgsqlauth removed from the contrib section, as it is obsolete now - BUGFIX(Routing.cxx) the absense of a regexp for virtual queues wasn't detected - BUGFIX(ProxyChannel.cxx) multiple calls over a single signalling channel are now gracefully handled using ReleaseComplete with newConnectionNeeded code. - (RasTbl.cxx) permanent endpoints are correctly reloaded now, with removal of permanent endpoints for which config entries have been deleted - (gkauth.cxx) depreciated ExternalPasswordAuth finally removed - SQL engine rewritten completelly to use a concept of SQL driver. New modules SQLPasswordAuth and SQLAliasAuth added. MySQL and PostgreSQL drivers implemented. Funded by Doxum Technologies Inc. Backward compatibility (MySQLAliasAuth and MySQLPasswordAuth) maintained. - BUGFIX(Routing.h) fixed referencing to a temporary PString object after it is destroyed - better LOG_OFF Windows event handling to prevent gatekeeper shutdown when user logs out. The patch from Franz J Ehrengruber - added GNU Gatekeeper Service for Windows from Franz J Ehrengruber - added Windows icon and version info from Franz J Ehrengruber - (manual.sgml) corrected MySQLPasswordAuth, MySQLAliasAuth sections, removed NeighborPasswordAuth documentation (not necessary anymore) - (gkauth.cxx) fixed SimplePasswordAuth module to read settings from [SimplePasswordAuth] section instead of [Password] section. Fixed MySQLPasswordAuth module to accept CacheTimeout parameter. Default CacheTimeout for MySQLPasswordAuth and MySQLAliasAuth set to 0 (do not cache passwords/aliases at all) - applied the concept of output trace levels for the status interface. It allows to select amount of information a status client receives (level 0 - silent mode + reloads; level 1 - CDRs, RouteRequests, reloads; level 2 - everything: the default mode) - (GkStatus.cxx) some cleanup, better tracing, new trace command introduced to support multiple output levels in the future - added missing addpasswd Visual Studio project files (dsp and dsw) - (configure.in) added check for MSG_NOSIGNAL flag support with send/recv calls - (configure.in) better pwlib/openh323 detection patches from Klaus Kaempf - (RasTbl.cxx) treat MCUs as gateways and allow MCUs to register prefixes - (Toolkit.cxx) use in-place constructors and destructors inside RewriteData to prevent Windows exceptions on rewrite rules reload in debug mode - removed duplicated build timestamp from 'version' command output. Added special file versionts.h to ensure version.cxx is rebuilt each time GnuGk is recompiled, in order to get proper build timestamp - (job.cxx) jobs simplified, optimised and checked for thread safety - (version.cxx) modified to list all extensions in the version string - (ProxyChannel.cxx) support for H.323 V4 provisionalRespToH245Tunneling and parallelH245Control H.225.0 elements - log rotation now maintains the original filename and stores rotated log files with precise timestamp (YYYYMMDD_HHMMSS) to prevent overwritting. Thanks to Rafael Costa dos Santos. - added Q.931 Setup authentication to RadAuth module to allow authentication based on crypto tokens carried inside Setup - modified GkAuthenticator and RADIUS authenticators prototypes to allow modifications of messages being processed (NOTE: This may break compatibility with some customer derived classes) - (ProxyChannel.cxx) RTP proxy fixed to not require RTCP channel and work better with T.38 faxes - (ProxyChannel.cxx) added new config parameter ScreenSourceAddress to screen calling party aliases from UUIE Setup element - added sample SQL/RADIUS billing example to the contrib directory - removed Makefile to force users running configure script before compilation - added per gateway E.164 rewriting of dialedDigits in addition to global E.164 rewriting Changes from 2.2beta2 to 2.2beta3 ================================= - addpasswd utility changed to require section name where the encrypted username/password will be stored - BUGFIX(gkauth.cxx) fixed call duration limit handling when multiple authenticator modules are specified - BUGFIX(gkacct.cxx) changed accounting reload handling to first cleanup the old modules and then create the new ones. This allows the FileAcct logger to work fine with the reload command - BUGFIX(h323util.cxx) additional check for H225_TransportAddress to prevent NULL pointer assertions. Thanks to Andrey Pankov! - added Q.931/H.225 Setup authentication (RadAliasAuth module implementation) - BUGFIX(radproto.cxx) fixed invalid signed/unsigned conversion that caused some functions to return unexpected results - BUGFIX(Neighbor.cxx) LCF is considered to come from a given neighbor if either IP:PORT or neighbor identifier inside LCF nonstandarparam field matches (was: neighbor identifier inside LCF nonstandarparam field or, if the field is missing, IP:PORT) - added PostgreSQL authenticator modules to the contrib directory - API for AliasAuth has changed slightly - better checking of tokens/cryptoTokens by SimplePasswordAuth derived authenticators - BUGFIX(RasTbl.cxx) fixed NULL reference in CallRec::SetSocket - caused crash in ForwardCall. Thanks to kubuqui cn! - check ReleaseComplete for H225 reason field, if no Q.931 cause IE is found - set disconnect cause to "normal call clearing" on DRQ received with normalDrop disengageReason and no termination cause set - BUGFIX(ProxyChannel.cxx) fixed h225 termination cause setting for ReleaseComplete - better hop count handling for neighbors - now LRQ m_hopCount field is forwarded/generated correctly, according to an original hop count or a ForwardHopCount config settings - small radius accounting/authentication module optimizations and better trace ouput - changed handling of multiple accounting modules (added new "alternative" control) - added new VirtualQueuePrefixes and VirtualQueueRegex config variables to [CTI::Agents] section. These make possible to call virtual queue not only with the exact alias name, but also with an alias that matches configured prefixes or configured regular expression. Thanks to Max Speransky - renamed config variable VirtualQueue to VirtualQueueAliases (backward compatibility is maintained) Changes from 2.2beta1 to 2.2beta2 ================================= - BUGFIX(RasTbl.cxx) fixed connect/disconnect time handling and call duration calculation. Thanks to Aivis Olsteins - BUGFIX(GkClient.cxx) changed SetPassword to fill LRQs with all recognized tokens/cryptoTokens - better disconnect cause handling - BUGFIX(ProxyChannel.cxx) ReleaseComplete now always contains either CauseIE or H225_ReleaseCompleteReason, because one of these fields is mandatory - added status line improvements (command expansion, repeat last command, backspace handling for windows) from Hu Yuxin - BUGFIX(GkStatus.cxx) removed unimplemented "disconnect" and "unregister" commands - ACF is filled with destinationInfo if aliases have changed during ARQ processing and ARQ.canMapAlias is TRUE - added more tracing and human readable names to routing policies - added modified CTI virtual queues implementation from Chih-Wei Huang - BUGFIX(singleton.cxx): incorrect singleton instance counting with different trace levels - added "run as user" feature - plain text CDR accounting logger module from Tamas Jalsovszky - call duration limit features introduced for ARQ handlers - old LDAP support removed from the source code tree - full Radius - authentication and accounting(start/stop/update) - added configure script to detect system settings and create Makefile Changes from 2.0.x to 2.2beta1 (by cwhuang) =========================================== - new method to detect NATed endpoints - better handling of call timeout - support multiple mysql servers - add authentication method selection for GkClient - the underlying architecture is almost rewritten entirely, includes object factory new thread model universal sockets handling mechanism RAS type traits multi-threaded RAS server new neighboring system configurable routing policies thread-safed gkauth - use MySQL C API directly instead of MySQL++ Changes from 2.0.3 to 2.0.4 (by cwhuang) ======================================== - add multiple prefixes for neighbors from Rodrigo Gonzalez - TransferCall patch from Olivier GRALL - fix H.235 compatibility with lastest CVS of Openh323 - set m_h245Tunneling as h245Tunneling flag in Setup_UUIE - allow h245 reverting work for calltype=1 - allow disable Shutdown command - permanent endpoints can be reloaded now - fix conflict between AcceptGatewayPrefixes option and permanent endpoints - fix bug for multiple SupportedProtocols - add StatusWriteTimeout option - add OverwriteEPOnSameAddress option - add Shutdown command (jan) Changes from 2.0.2 to 2.0.3 (by cwhuang) ======================================== - add VendorIdentifier in RRQ - more elegant way to detect version of Openh323 - fix typo for CheckID :( - fix nattype bug on fullRRQ - reject nated endpoint from private IP 127.0.0.1 - fix problem if receiving DRQ before than ReleaseComplete - fix compile problem with latest CVS of Openh323 - add AcceptEndpointIdentifier option - fix hang bug on GkStatus - allow to reject forwarded LRQ - forward call on Facility - fix bug LRQ with empty destinationInfo - send endpoint alias via sourceInfo of LRQ - allow set nated endpoints forcedly - let virtual queue feature be optional - fix a bug on startH245 - implemented inbound call distribution with virtual queues (based on patch by Dennis Lazreg) - workaround for Setup contains empty destinationAddress - workaround to copy cryptoTokens for the latest CVS of OpenH323 - fix shutdown problem on SMP machine - reject lightweightRRQ if IP has changed - send DRQ to parent if reject a call - fix billing issue for forwarded calls - change 'ifdef PTRACING' to 'if PTRACING' since it is always defined - fix warnings of unused variables in non PTRACING mode - expire endpoints even in active calls - send endSessionCommand before closing h245socket Changes from 2.0.1 to 2.0.2 (by cwhuang) ======================================== - show NAT IP in print verbose command - fix reply of pending ARQs to NATed endpoint - fix bug on handling Facility - allow hide DisplayIE and CallingPartyNumberIE - fix an interoperability bug with old NAT solution - fix a bug that prefixes is not changed on full RRQ - support 'H323-GW' prefixes - fix leaking messages on status port - fix a security bug OnDRQ - rewrite Setup.destinationAddress even if no CalledPartyNumber - fix interoperability problem on CloseLogicalChannel for some bad endpoints - reload route table on RRJ with reason fullRegistrationRequired Changes from 2.0.0 to 2.0.1 (by cwhuang) ======================================== - add statistics for nated ep and calls from parent - avoid looped calls - add NetworkInterfaces option for OS that don't support GetRouteTable() - add H.245 Reverting technology(Patent Pending) - fix H.225.0 procotolID problem - fix a long existing bug according to Packetizer http://www.packetizer.com/in/q14.html - add TOS flag for RTP/RTCP, thanks to Thomas Jalsovsky - add Citron NAT technology(Patent Pending) - allow multiple nated endpoints with the same private IP but different public IP register simultaneously - fix possible race conditions in all STL list::size() - fix a sigfault when name of parent GK is unresolved - add 'gk' command to show parent GK - add AlwaysForwardLRQ option - don't do round-robin for LRQ and find command - fix a bug in proxy mode (when there are several session IDs in fast start) - improve support NATed endpoints - only include alternateGatekeeper field in GCF/RCF if GRQ/RRQ has supportsAltGK - add a workaround so OpenH323 clients won't reject our confirm messages when use H.235 authenticators - add UseBroadcastListener option - fix incorrect bandwidth in answered ARQ as a child GK - add DisconnectSession command, thanks to Rodrigo Gonzalez - change debug level of received messages(Q931, H245) to 4, only show sent messages if modified - change RasSvr to RasSrv for consistent - fix bandwidth problem of the answered ARQ - add password rule for GkStatus, thanks to Rodrigo Gonzalez - large fd_set patch - fix problem of h245socket handling - fix incorrect lightweight RRQ handling Changes from 2.0b8 to 2.0.0 (by cwhuang) ======================================== - allow to find by epid or ip address - improve ProxySocket for thread-safe writing - improve BRQ handling - add alternateGKs support for GkClient - improve alternateGKs support - sort the AuthRules by priority - clean up BroadcastListen.cxx and MulticastGRQ.cxx - fix a serious bug in MulticastGRQ.cxx - let setting options from status port work - print version when connect to status port Changes from 2.0b5 to 2.0b8 (by cwhuang) ======================================== - allow to limit port range - allow call unregistered endpoints - add RemoveTrailingChar feature - show CallerIP for CDR from neighbors - disconnect all calls on shutdown - disconnect calls when unregister an endpoint - accept LRQ from registered endpoints - add AcceptGatewayPrefixes option - always rewrite dialedDigits OnARQ - create symbolic link in /tmp to avoid permission problem - rewrite GkAuthorize to PrefixAuth module - accept broadcasting RRQ (workaround for Cisco Call Manager) Changes from 2.0b4 to 2.0b5 (by cwhuang) ======================================== - add ExternalPasswordAuth module - accept unregistered call if Q.931 contains CalledPartyNumber - add IncludeDestinationInfoInLCF option - allow a second ARQ if srcCallSignalAddress is in SkipForwards - support forwarding LRQs - add call duration in seconds, thanks to Rodrigo Gonzalez - change executable name to gnugk - add DropCallsByReleaseComplete option - support NATed endpoints by NATHandler - add AcceptNeighborsCalls, change the meaning of AcceptUnregisteredCalls Changes from 2.0b3 to 2.0b4 (by cwhuang) ======================================== - add MySQLAliasAuth module - add ExtraCriterion to MySQLAuth, remove CheckField - bandwidth management is disable by default - remove resourceManager, bandwidth management now in CallTable - allow set neighbors without prefix, no LRQs will be sent to such neighbors - send IRQ to an endpoint before expiring it - only accept unregistered call from neighbors - in SimplePasswordAuth module, allow expire password cache - add NeighborPasswordAuth module - check if LRQs is from IP of my neighbors - add dynamic resolving name for neighbors, thanks to Rodrigo Gonzalez - add GkClient module - allow remove a key or a section in the config from status thread - remove isalnum check for aliases, we hope to use unicode. :) - add total call counting for EndpointRec Changes from 2.0b2 to 2.0b3 (by cwhuang) ======================================== - add AcceptUnregisteredCalls feature - fix problem of RouteTable in Linux with alias interfaces - show endtime in CDR even if call not connected - in routed mode, reject answered ARQ if no CallRec found - add detailed explanations of known options - support proxy for fast start logical channels - fix GKHome not work for proxy Changes from 2.0b1 to 2.0b2 (by cwhuang) ======================================== - add proxy for T.120 logical channels - add proxy for RTP logical channels, add Proxy section Changes from 1.3 to now (by mmuehlen) ==================================== - added destination analysis module list (gkDestAnalysis.cxx, gkDestAnalysis.h) with a similar structure as the authentication module list (can be enabled with compiler option WITH_DEST_ANALYSIS_LIST) - added base class 'GkLDAP' for LDAP access (gkldap.h gkldap.cxx) - changed the following section names in ini-file (reason: LDAP is not only used for authentification): LDAPAuth::LDAPAttributeNames -> GkLDAP::LDAPAttributeNames LDAPAuth::Settings -> GkLDAP::Settings Changes from 1.3 to 2.0b1 (by cwhuang) ====================================== - redesign routed model, add RoutedMode section - add H.245 routed support, including early connect - TimeToLive now can be specified in config file - improve multi-homed support - new command unregisterip (jan) - other minor cleanup Changes from 1.2 to 1.3 (by mmuehlen) ==================================== - fix a bug in Toolkit::GKName (13.11.01) - add SrcInfo and GkName to CDR: CDR|CallNo|CallId|Duration|Starttime|Endtime|CallerIP|CallerEndId|CalledIP|CalledEndId|DestInfo| srcInfo|GkName (13.11.01) - add SHA1 support for SimplePasswordAuth (14.12.01) - add LDAPAliasAuth (03.01.02) - add support for new voip.schema (15.01.02) Changes from 1.2 to 1.3 (by cwhuang) ==================================== - fix problem for lightweightRRQ without callSignalAddress & rasAddress - rotate log on SIGHUP signal - write pid to /var/run/gk.pid (can be specified by --pid option) - rewrite dialedDigits OnLRQ Changes from 1.1 to 1.2 (by cwhuang) ==================================== - add MySQLPasswordAuth, need mysql++ to compile it - show left time of calls in PrintCurrentCalls - add 'Statistics' command - Rewrite destinationAddress in H225_Setup_UUIE - fix a bug in Toolkit::RewritePString - send DRQ to endpoints when tear down a call, give up send Release Complete (in some situation it causes deadlock, very strange!!) however, some buggy endpoints would ignore DRQ, sigh... only send Release Complete on 'DisconnectCall' command - fix 'connectionList returned NULL' bug in SignalChannel.cxx - modify SignalConnection::Send to more thread-safe - modify OnARQ to fix routed mode problem - show count of current calls of endpoints in PrintAllRegistrationsVerbose - add 'Find' and 'FindVerbose' commands - check endpoint identifier for fullRegistrationRRQ - fix a (security) bug in lightweightRRQ - add startup and running time to 'Version' command - change EndpointRec::m_terminalType to be a pointer to reduce memory consumed (sizeof H225_EndpointType = 2552 in x86) - send Release Complete to endpoints when tear down a call (routed mode) - disconnect calls of an endpoint on URQ - add DefaultCallTimeout to remove staled calls - fix bug in DisconnectAlias - only use CallId to find CallRec if it presents, this fix a potential CRV conflict in ARQ - re-use endpoint identifier if call signal address found in RemovedList - send URQ & DRQ from RAS port instead of arbitrary port - print dialed number, number of calls in PrintCurrentCalls cmd - print number of endpoints in PrintAllRegistrations cmd - allow generate NB, UC CDR - don't let ttl < 60 - only generate CDR for call that originated from my zone and connected (must work in routed mode) - redesign CallRec & CallTable - extend debug cfg SEC command to retrieve a whole section Changes from 1.0 to 1.1 (by cwhuang) ==================================== - change neighbors structure - add class GkAuthInitializer for authenticators initialization - add authentication modules (gkauth.cxx, gkauth.h) Changes from 1.0pre9 to 1.0 (by cwhuang) ======================================== - add 'debug printrm' command - change EndpointRec::Ptr to template Changes from 1.0pre8 to 1.0pre9 (by cwhuang) ============================================ - Neighbor function based on LRQ/LCF. GK would cache the records in a list. A command "PrintAllCached" is added to GkStatus. - Add more information in LCF reply, including terminal type, aliases and supportedProtocols (if any) so that the neighbors could cache it. - Re-design EndpointRec & RegistrationTable. The output format of "PrintAllRegistrations" is also changed. - Add permanent endpoints support for endpoints without RAS. - Add gateway SupportedPrefix support. Note the gateway aliases no longer be treated as prefixes. If you want an alias to be a prefix, add it to SupportedPrefix or config file. - Replace using directive by using declaration to avoid namespace pollution. Changes from 0.17 to 1.0pre8 ============================ - add destInfo to CallRec and CDR (cwhuang) - rewrite some codes in OnRRQ to be more clean and compact (cwhuang) - change H323RasSrv to be a separate thread, do housekeeping in the main thread (cwhuang) - shutdown handler refine (cwhuang) - allow aliases begin with '#' (cwhuang) - don't search endpoint table for an ARQ to an answerCall (cwhuang) - add registration timeout (cwhuang) - add mutex to RegistrationTable for thread-safe (cwhuang) - almost rewrite SoftPBX.cxx (cwhuang) - add singleton.h, singleton.cxx, change all singleton objects to derive from a template singleton base to avoid code duplication and allow a better cleanup (cwhuang) - remove all usages of for_each_with by STL for_each (cwhuang) - add RegistrationTable::RemovePrefixes(const PString &) (cwhuang) - add more fields to CDR output, add port number to ACF output (cwhuang) - rewrite UnixShutdownhandler for graceful shutdown (cwhuang) - change some members and methods in Toolkit to non-static (cwhuang) - ensure Toolkit::Config be called after SetConfig some order of initialization in gk.cxx changed accordingly (cwhuang) - fix the reload mechanism, set m_RewriteFastmatch when reload (cwhuang) - add 'reload' command to status thread and remove 'debug reload' command 'shutdown' command is also added, but not implemented yet (cwhuang) - show a copyright notice to be compliant to GPL requirement (cwhuang) - more documentation - status thread: changed Disconnect command to DisconnectIp, added DisconnectAlias, DisconnectCall, DisconnectEndpoint, UnregisterAlias - better handling of DCF - better error checking for command arguments via status thread - dummy implementation for TransferCall and MakeCall (doesn't do anything, yet) - slightly different handling of SIGHUP - RewriteE164: multiple comma-separated targets, randomly chosen - simplified conversion between socket and H225 ip addresses - first steps to implement H.245-routing (doesn't do anything, yet) - commands via status thread are case insensitive - changed command line option -h (home interface) to -i, new option -h (help) - updated to latest OpenH323 version (pwlib 1.1.32, openh323 1.5.4) - use callIndentifiers instead of callReferences (the first are globally unique) - generate CallProceeding messages - using all aliases of endpoint, not only the first alias - generate CDRs, signal all ACFs - check H.225 connections with Q.931 StatusEnquiry heartbeat (optional) - changed default for RasSrv::RRQAuth back to confirm (so you can use the gatekeeper without a config file) - compile fixes for Visual C++ 6.0 Changes from 0.16 to 0.17 ========================= - fixed crash in status reporting of ACF in routed mode - fixed bug, where the destCallSignalAdress still pointed to the GK after being forwarded in routed mode (prevented routed call from OhPhone -> Netmeeting) - the config file gatekeeper.ini is no longer mandatory; simple configurations will work without any config file, but you'll need it for more complicated setups Changes from 0.15 to 0.16 ========================= - small fixes to make the gatekeeper compile under Visual C++ 6.0 Changes from 0.14 to 0.15 ========================= - fixed unsafe cast from H323 alias to PASN_BMPString Bacci Emiliano found it Changes from 0.13 to 0.14 ========================= - major bug-fix for ohphone compatibility Christoph Stueckjuergen found it Changes from 0.12 to 0.13 ========================= - updated to compile with OpenH323 beta 3 - small fix in SignalConnection for Windows NT by rama@anwsi.com - multicast requests are answered to the specified port (not the sender port by Denver Trouton - RegistrationTable::FindByPrefix now finds the longest prefix, not the first one by Michael Rubashenkov - fix in H323RasSrv::OnLRQ to allow it to work with Cisco gatekeeper by Michael Rubashenkov - fix to allow Netmeeting to call ohphone (the other always way has been working) by Andreas Hofmeister Changes from 0.11 to 0.12 ========================= - you must have a config file now to set the various new options (eg. gatekeeper.ini in the current directory, or set a path on the command line with -c) - support for failover to other gatekeepers - RAS messages can be forwarded to other gatekeepers - TCP parameters can be configured in the config file - what gateway to use on what E.164 prefix can be specified in the config file - overlapped sending: specify in the config file when to send "incompleteAddress" on ARQ and collect more digits - detailed authentication rules which endpoints may register based on their IP numbers - rule based authentication for usage of the status port - alias rewriting: the gatekeeper has a list of E.164 aliases that are replaced by another E.164 number - many bug fixes... - the timeToLive filed in RCF can be set on the command line with -l - more detailed status messages (see GkStatusMsgFmt.html) many patches from Henrik Joerring Most of the changes in this release are donated by folks at MediaWays (Markus Storm and Torsten Will). They sent more stuff than I can mention here. Changes from 0.10 to 0.11 ========================= - many memory leaks fixed by Damian Slee - LRQ implemented patch by Ashley Unitt - accept LRQ as Multicast, too - when more bandwidth is requested than available we now grant ACF with the bandwidth that's still available input from Robert Jongbloed - more attempts for a clean shutdown handling - status thread accepts commands now (see gkstatus.txt) patch by Ashley Unitt - more detailed status messages (see GkStatusMsgFmt.html) input from Henrik Joerring - general code fixups by nils@shkoo.com Changes from 0.9 to 0.10 ======================== - trace messages now use the Pwlib trace facility (use -t or -tt ... to turn them on and -o to write to a file) patch from Seungoh Jhung - support for lightweight registration updates - on shutdown all registered endpoint receive a UnregistrationRequest - support for gateways: for gateways we only compare the prefix of the E.164 address the gateway registered and assume that the gateway provides access to the whole address space (needs testing!) input from Frank Breitenbach - new command line option -h to specify the ip number the gatekeeper binds to. Now you can use IP aliases on Linux and start as many gatekeepers as you want. input from Markus Storm - status messages with ip numbers input from Seungoh Jhung - bugfixes from many contributors... Changes from 0.8 to 0.9 ======================= - gatekeeper routed signaling implemented (only H.225; no H.245, yet) provided by Sergio Artero Martinez - Bugfix: RRQ from Innovaphone IP400 now ok input by Markus Storm - gk sends status messages to all clients connected to TCP port 7000 (eg. for SNMP agent) - this is experimental and may evolve into a full-fledged interface to the gatekeeper - all mandatory RAS messages implemented gnugk-3.4/PaxHeaders.16356/lua.cxx0000644000175000001440000000005012112437134015024 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/lua.cxx0000644000175000001440000001170212112437134013766 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // lua.cxx // // LUA routing policy for GNU Gatekeeper // // Copyright (c) 2012, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #include "config.h" #ifdef HAS_LUA #include "Routing.h" #include "Toolkit.h" #include "gk_const.h" #include "snmp.h" #include namespace Routing { // a policy to route calls with LUA class LuaPolicy : public DynamicPolicy { public: LuaPolicy(); virtual ~LuaPolicy(); protected: virtual void LoadConfig(const PString & instance); virtual void RunPolicy( /*in */ const PString & source, const PString & calledAlias, const PString & calledIP, const PString & caller, const PString & callingStationId, const PString & callid, const PString & messageType, const PString & clientauthid, /* out: */ DestinationRoutes & destination); void SetValue(const char * name, const char * value); PString GetValue(const char * name); protected: // LUA interpreter lua_State * m_lua; // script to run PString m_script; }; static int gnugk_trace(lua_State * L) { if ((lua_gettop(L) != 2) || !lua_isnumber(L, 1) || !lua_isstring(L, 2)) { lua_pushstring(L, "Incorrect arguments for 'trace(level, 'message')'"); lua_error(L); return 0; } PTRACE(lua_tonumber(L, 1), "LUA\t" << lua_tostring(L, 2)); return 0; // no results } LuaPolicy::LuaPolicy() { m_name = "Lua"; m_iniSection = "Routing::Lua"; m_active = false; m_lua = NULL; } void LuaPolicy::LoadConfig(const PString & instance) { m_script = GkConfig()->GetString(m_iniSection, "Script", ""); if (m_script.IsEmpty()) { PString scriptFile = GkConfig()->GetString(m_iniSection, "ScriptFile", ""); if (!scriptFile.IsEmpty()) { PTextFile f(scriptFile, PFile::ReadOnly); if (!f.IsOpen()) { PTRACE(1, "LUA\tCan't read LUA script " << scriptFile); } else { PString line; while (f.ReadLine(line)) { m_script += (line + "\n"); } } } } if (m_script.IsEmpty()) { PTRACE(2, m_name << "\tmodule creation failed: " << "\tno LUA script"); SNMP_TRAP(4, SNMPError, General, PString(m_name) + " creation failed"); return; } if (!m_lua) { m_lua = luaL_newstate(); luaL_openlibs(m_lua); lua_register(m_lua, "trace", gnugk_trace); } m_active = true; } LuaPolicy::~LuaPolicy() { lua_close(m_lua); } void LuaPolicy::SetValue(const char * name, const char * value) { lua_pushstring(m_lua, value); lua_setglobal(m_lua, name); } PString LuaPolicy::GetValue(const char * name) { lua_getglobal(m_lua, name); PString result = lua_tostring(m_lua, -1); lua_pop(m_lua, 1); return result; } void LuaPolicy::RunPolicy( /* in */ const PString & source, const PString & calledAlias, const PString & calledIP, const PString & caller, const PString & callingStationId, const PString & callid, const PString & messageType, const PString & clientauthid, /* out: */ DestinationRoutes & destination) { SetValue("source", source); SetValue("calledAlias", calledAlias); SetValue("calledIP", calledIP); SetValue("caller", caller); SetValue("callingStationId", callingStationId); SetValue("callid", callid); SetValue("messageType", messageType); SetValue("clientauthid", clientauthid); if (luaL_loadstring(m_lua, m_script) != 0 || lua_pcall(m_lua, 0, 0, 0) != 0) { PTRACE(1, "LUA\tError in LUA script: " << lua_tostring(m_lua, -1)); lua_pop(m_lua, 1); return; } PString action = GetValue("action"); PString rejectCode = GetValue("rejectCode"); PString destAlias = GetValue("destAlias"); PString destIP = GetValue("destIP"); if (action.ToUpper() == "SKIP") { PTRACE(5, m_name << "\tSkipping to next policy"); return; } if (action.ToUpper() == "REJECT") { PTRACE(5, m_name << "\tRejecting call"); destination.SetRejectCall(true); if (!rejectCode.IsEmpty()) { destination.SetRejectReason(rejectCode.AsInteger()); } return; } if (!destAlias.IsEmpty()) { PTRACE(5, m_name << "\tSet new destination alias " << destAlias); H225_ArrayOf_AliasAddress newAliases; newAliases.SetSize(1); H323SetAliasAddress(destAlias, newAliases[0]); destination.SetNewAliases(newAliases); } if (!destIP.IsEmpty()) { PTRACE(5, m_name << "\tSet new destination IP " << destIP); PStringArray adr_parts = SplitIPAndPort(destIP, GK_DEF_ENDPOINT_SIGNAL_PORT); PString ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); Route route("Lua", SocketToH225TransportAddr(ip, port)); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(route.m_destAddr); if (!destAlias.IsEmpty()) route.m_destNumber = destAlias; destination.AddRoute(route); } } namespace { // anonymous namespace SimpleCreator LuaPolicyCreator("lua"); } } // namespace Routing #endif // HAS_LUA gnugk-3.4/PaxHeaders.16356/statusacct.cxx0000644000175000001440000000005012016656506016432 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/statusacct.cxx0000644000175000001440000001064212016656506015376 0ustar00janusers00000000000000/* * statusacct.cxx * * accounting module for GNU Gatekeeper for the status port. * * Copyright (c) 2005-2010, Jan Willamowius * * This work is published under the GNU Public License version 2 (GPLv2) * see file COPYING for details. * We also explicitly grant the right to link this code * with the OpenH323/H323Plus and OpenSSL library. * */ #include #include #include "GkStatus.h" #include "statusacct.h" StatusAcct::StatusAcct( const char* moduleName, const char* cfgSecName ) : GkAcctLogger(moduleName, cfgSecName) { // it is very important to set what type of accounting events // are supported for each accounting module, otherwise the Log method // will no get called SetSupportedEvents(StatusAcctEvents); PConfig* cfg = GetConfig(); const PString& cfgSec = GetConfigSectionName(); m_timestampFormat = cfg->GetString(cfgSec, "TimestampFormat", ""); m_startEvent = cfg->GetString(cfgSec, "StartEvent", "CALL|Start|%{caller-ip}:%{caller-port}|%{callee-ip}:%{callee-port}|%{CallId}"); m_stopEvent = cfg->GetString(cfgSec, "StopEvent", "CALL|Stop|%{caller-ip}:%{caller-port}|%{callee-ip}:%{callee-port}|%{CallId}"); m_updateEvent = cfg->GetString(cfgSec, "UpdateEvent", "CALL|Update|%{caller-ip}:%{caller-port}|%{callee-ip}:%{callee-port}|%{CallId}"); m_connectEvent = cfg->GetString(cfgSec, "ConnectEvent", "CALL|Connect|%{caller-ip}:%{caller-port}|%{callee-ip}:%{callee-port}|%{CallId}"); m_alertEvent = cfg->GetString(cfgSec, "AlertEvent", "CALL|Alert|%{caller-ip}:%{caller-port}|%{callee-ip}:%{callee-port}|%{CallId}"); m_registerEvent = cfg->GetString(cfgSec, "RegisterEvent", "EP|Register|%{endpoint-ip}:%{endpoint-port}|%{aliases}"); m_unregisterEvent = cfg->GetString(cfgSec, "UnregisterEvent", "EP|Unregister|%{endpoint-ip}:%{endpoint-port}|%{aliases}"); } StatusAcct::~StatusAcct() { } GkAcctLogger::Status StatusAcct::Log( GkAcctLogger::AcctEvent evt, const callptr& call ) { // a workaround to prevent processing end on "sufficient" module // if it is not interested in this event type if ((evt & GetEnabledEvents() & GetSupportedEvents()) == 0) return Next; if (!call) { PTRACE(1, "STATUSACCT\t"< params; SetupAcctParams(params, call, m_timestampFormat); PString msg = ReplaceAcctParams(eventTmpl, params); GkStatus::Instance()->SignalStatus(msg + "\r\n", STATUS_TRACE_LEVEL_CDR); } return Ok; } GkAcctLogger::Status StatusAcct::Log( GkAcctLogger::AcctEvent evt, const endptr& ep ) { // a workaround to prevent processing end on "sufficient" module // if it is not interested in this event type if ((evt & GetEnabledEvents() & GetSupportedEvents()) == 0) return Next; if (!ep) { PTRACE(1, "STATUSACCT\t"< params; SetupAcctEndpointParams(params, ep); PString msg = ReplaceAcctParams(eventTmpl, params); GkStatus::Instance()->SignalStatus(msg + "\r\n", STATUS_TRACE_LEVEL_CDR); } return Ok; } PString StatusAcct::EscapeAcctParam(const PString& param) const { return param; // don't quote here, quote in template if needed } // override output format of callid PString StatusAcct::ReplaceAcctParams( /// parametrized accounting string const PString& cdrStr, /// parameter values const std::map& params ) const { std::map new_params = params; std::map::iterator i = new_params.find("CallId"); if (i != new_params.end()) { i->second.Replace(" ", "-", TRUE); } return GkAcctLogger::ReplaceAcctParams(cdrStr, new_params); } namespace { // append status port accounting logger to the global list of loggers GkAcctLoggerCreator StatusAcctCreator("StatusAcct"); } gnugk-3.4/PaxHeaders.16356/gk.ico0000644000175000001440000000005011173624535014625 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gk.ico0000644000175000001440000000427611173624535013577 0ustar00janusers00000000000000 ¨( @y|z¹»ž¼Ÿ¶ ¾¢´„‚µž¿£ ƒ†„±¡À¤¯ž/¸¡$Á¥°Ÿ0­¢.¨¤*ç‰ŒŠ¼¦,Å©Œ½§.ƪ!ŽŸ¦AŽ‘’¦U‘”’‹©Sì6‘ªNЧ[—©R¤f¦_‘¨X¥g£o‘¦hŽ­V°¦c¬aȱ=˜›š™¤|–®]š¥}—¯^ʳ@ȰK›¨wɱMž¡Ÿž¨ˆ «ƒµd £¡Ï¶SзTžµnµ®‚Ÿ¶o̹^¦©§Ã¼d¨°—¨«©Ð½b»¶Ñ¾cÒ¿d§¾vÓÀe©¾€³´ ³¯±ÕÂh®¶žº·—­Ã„²¹¨ÛÆw¶Å‹¶¼«ÖÇ~¸»¹ØÉ€´Æ™¹¾´ÙʻêßЇ¼Ï¡âÑ’ÜÑ—ÊÇÂÄÇÅËÈÃÆÉÇÇÒ©áÕ›ÇÉÎÈÊÏÉÌÊÉËÐãØÐÌÎäÙžäצËÔºËÏÍÍÙ¯ÌÐÎÕÐÓçÛ©ÖÓÎÐÓÑÒÕÓåÞ±ÔÞ½Ô×ÕÕß¾Û×Ùèá´è߻֨Ý×ÛÙÙâÈÚÛáíä¿ÛÞÜîåÀÝàÞäâÜåãÝòéÅìéÈæâåáåãïêÑãêÙêåèêçâåèæôï×õðØïëíöðßêìñêîëòïêëïííðîìòèôóæïðöõôçöõèòõóò÷íòôùøøêùùëó÷ôûøóôøöüùôöù÷÷úøþûöøûùùüúÿý÷úýûÿþøüÿþ³³³³³³³³³³³³³³³³³³³³³³³³³³³³­³²³³³³³³„¨²³³³³³³³³³³³³³­­³­³²³°¤³³³³³³N%2Tt’¯²³³³³³³³³³³³³­¦———¦²³³­²ª9 " '):\w–­³³­­³²³³ªŒzv‹”¥¦³³³±˜( " ' &qkf†š±³­¤€UX_h€”—³³³³e "- 'V£­­¨¨–iuB5=UXam³³³²Q '#"'"K²³³hZ²³—B7>JP_p…¥³³³Ÿ6 " }³³5a 57=UXŒ²³³³²‰&3M ""'?©…5.h¦³³³³³¤f[ªA # gI L”³³³³³³³²n³³³s '"'"$H­³³³³³³³³˜‚­³³³M Gl³³³³³³³³³wœ³±°±""""4‡³“‰³³³³³³³³­lv]pŒ—¡V L¤³~Ÿ³³³³³³³³¯j•    ‹³n³³³³³³³³³’‰³Z" #1ž…o€¨t³³³³³³³³²w˜p "-#""`²­³³Š³³³³³³³³³\.D#""# 3³³²n§³³³³³³³³¤, žS" ""M²­šn³³³³³³³³³„L²”-" --ƒ{?t³³³³³³³³³c³³` #'""+ (–³³³³³³³³³N!z”4²³³ 3" # '"9±³³³³³³³³³¯˜tR@Š¢±bv³³³­{" "" \³³³³³³³³³³­¨–ª«Šwnw’­³ª};-" '&|³³³³³³³³³³˜/™~Ÿ¤œ„ll^&##"#")–³³³³³³³³³³cYOOO, <@MYSQL_DIR@/include/mysql.h>) #endif // PostgreSQL #undef HAS_PGSQL #if defined(_MSC_VER) && HAS_PGSQL #pragma include_alias(, <@PGSQL_DIR@/include/libpq-fe.h>) #endif // Firebird #undef HAS_FIREBIRD #if defined(_MSC_VER) && HAS_FIREBIRD #pragma include_alias(, <@FIREBIRD_DIR@/include/ibase.h>) #endif // SQLite #undef HAS_SQLITE #if defined(_MSC_VER) && HAS_SQLITE #pragma include_alias(, <@SQLITE_DIR@/sqlite3.h>) #endif // ODBC #undef HAS_ODBC #if defined(_MSC_VER) && HAS_ODBC #pragma include_alias(, <@ODBC_DIR@/include/sql.h>) #pragma include_alias(, <@ODBC_DIR@/include/sqlext.h>) #pragma include_alias(, <@ODBC_DIR@/include/sqltypes.h>) #endif #endif gnugk-3.4/PaxHeaders.16356/gkauth.h0000644000175000001440000000005012177353266015171 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/gkauth.h0000644000175000001440000010331212177353266014132 0ustar00janusers00000000000000////////////////////////////////////////////////////////////////// // // gkauth.h // // Copyright (c) 2001-2010, Jan Willamowius // // Gatekeeper authentication modules // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. // We also explicitly grant the right to link this code // with the OpenH323/H323Plus and OpenSSL library. // ////////////////////////////////////////////////////////////////// #ifndef GKAUTH_H #define GKAUTH_H "@(#) $Id: gkauth.h,v 1.67 2013/08/04 04:21:10 willamowius Exp $" #include #include #include "name.h" #include "rwlock.h" #include #include "Toolkit.h" #include "snmp.h" #include "h323util.h" class H225_GatekeeperRequest; class H225_GatekeeperConfirm; class H225_RegistrationRequest; class H225_UnregistrationRequest; class H225_AdmissionRequest; class H225_BandwidthRequest; class H225_DisengageRequest; class H225_LocationRequest; class H225_InfoRequest; class H225_ArrayOf_ClearToken; class H225_ArrayOf_CryptoH323Token; class H225_ArrayOf_AliasAddress; class H225_ArrayOf_AuthenticationMechanism; class H225_ArrayOf_PASN_ObjectId; class H225_TransportAddress; class H225_ArrayOf_TransportAddress; class H235_AuthenticationMechanism; class PASN_ObjectId; class H235Authenticators; class H235Authenticator; class H225_Setup_UUIE; class SignalingMsg; template class H225SignalingMsg; typedef H225SignalingMsg SetupMsg; class EndpointRec; class CallRec; template class SmartPtr; typedef SmartPtr endptr; typedef SmartPtr callptr; template class RasPDU; template struct RasInfo; namespace Routing { class Route; } /// Data read/written during RRQ processing by all configured /// authenticator modules struct RRQAuthData { RRQAuthData() : m_rejectReason(-1), m_billingMode(-1) {} /// -1 if not set, H225_RegistrationRejectReason enum otherwise int m_rejectReason; /// optional user's account balance amount string PString m_amountString; /// H225_CallCreditServiceControl_billingMode or -1, if not defined int m_billingMode; /// Authenticated Aliases PStringArray m_authAliases; }; /// Data read/written during ARQ processing by all configured /// authenticator modules struct ARQAuthData { ARQAuthData( const ARQAuthData& obj ); ARQAuthData( const endptr& ep, const callptr& call ); ARQAuthData& operator=(const ARQAuthData& obj); void SetRouteToAlias(const H225_ArrayOf_AliasAddress & alias); void SetRouteToAlias(const PString& alias, int tag = -1); /// -1 if not set, H225_AdmissionRejectReason enum otherwise int m_rejectReason; /// -1 if not set, max allowe call duration in seconds otherwise long m_callDurationLimit; /// disabled codecs PString m_disabledcodecs; /// endpoint that sent the request endptr m_requestingEP; /// call associated with the request (if any, only for answering ARQ) callptr m_call; /// input/output - set or get Calling-Station-Id PString m_callingStationId; /// call party to be billed for the call (if other then Calling-Station-Id) PString m_callLinkage; /// input/output - set or get Called-Station-Id PString m_calledStationId; /// number dialed by the user (Called-Station-Id before rewrite) PString m_dialedNumber; /// optional user's account balance amount string PString m_amountString; /// H225_CallCreditServiceControl_billingMode or -1, if not defined int m_billingMode; /// if not NULL, route the call to the specified alias H225_ArrayOf_AliasAddress m_routeToAlias; /// if not empty, route the call to the specified destinations std::list m_destinationRoutes; /// override global proxy setting from the config (see #CallRec::ProxyMode enum#) int m_proxyMode; /// RADIUS Class attribute, if found in Access-Accept/Access-Reject PBYTEArray m_radiusClass; /// ID provided by client, to be passed out with accounting events PUInt64 m_clientAuthId; private: ARQAuthData(); }; /// Data read/written during Q.931/H.225.0 Setup processing /// by all authenticators struct SetupAuthData { SetupAuthData(const SetupAuthData & obj); SetupAuthData( /// call associated with the message (if any) const callptr& call, /// is the Setup message from a registered endpoint bool fromRegistered, /// did the Setup come in over TLS bool overTLS = false ); virtual ~SetupAuthData(); SetupAuthData& operator=(const SetupAuthData& obj); void SetRouteToAlias(const H225_ArrayOf_AliasAddress & alias); void SetRouteToAlias(const PString & alias, int tag = -1); /// -1 if not set, H225_ReleaseCompleteReason enum otherwise int m_rejectReason; /// -1 if not set, Q931 cause value otherwise int m_rejectCause; /// -1 if not set, max allowe call duration in seconds otherwise long m_callDurationLimit; /// disabled codecs PString m_disabledcodecs; /// call associated with the message (if any) callptr m_call; /// is the Setup message from a registered endpoint bool m_fromRegistered; /// input/output - set or get Calling-Station-Id PString m_callingStationId; /// input/output - set or get Called-Station-Id PString m_calledStationId; /// number dialed by the user (Called-Station-Id before rewrite) PString m_dialedNumber; /// if not NULL, route the call to the specified alias H225_ArrayOf_AliasAddress m_routeToAlias; /// if not empty, route the call to the specified destinations std::list m_destinationRoutes; /// override global proxy setting from the config (see #CallRec::ProxyMode enum#) int m_proxyMode; /// RADIUS Class attribute, if found in Access-Accept/Access-Reject PBYTEArray m_radiusClass; /// ID provided by client, to be passed out with accounting events PUInt64 m_clientAuthId; /// True if Setup came in over TLS bool m_overTLS; private: SetupAuthData(); }; /** The base class for all authenticator modules. Authenticator modules are used to authenticate/authorized RAS and Q.931 messages sent by endpoints and to check if the endpoints are authorized to use H.323 network resources. The modules are stackable - each request can be checked by multiple modules to get the final authentication result. Derived classes usually override one or more Check virtual methods to implement specific authentication mechanism. */ class GkAuthenticator : public NamedObject { public: /// processing rule for the authenticator enum Control { /// if this module cannot determine authentication success or failure /// (due to some missing info, for example), remaining modules will /// decide about acceptation/rejection of the reqest, /// if the request is accepted it is passed to a next rule e_Optional, /// the request has to be authenticated by this module /// and processing is continued with remaining modules e_Required, /// if the request is authenticated by this module, authentication /// is successful, otherwise the request is rejected /// (no further modules are processed in both cases) e_Sufficient, /// if the request is accepted/rejected by this module, authentication /// processing ends, otherwise the request is passed to a next rule e_Alternative }; /// authentication status returned from Check methods enum Status { e_ok = 1, /// the request is authenticated and accepted e_fail = -1, /// the request is authenticated and rejected e_next = 0 /// the module could not authenticate the request }; /// bit masks for event types other than RAS - see miscCheckFlags variable enum MiscCheckEvents { e_Setup = 0x0001, /// Q.931/H.225 Setup message e_SetupUnreg = 0x0002 /// Q.931/H.225 Setup message only from an unregistered endpoint }; /** Build a new authenticator object with the given name. It is important to pass proper check flags to signal, which checks are supported/implemented by this authenticator. */ GkAuthenticator( const char* name, /// a name for the module (to be used in the config file) unsigned supportedRasChecks = ~0U, /// RAS checks supported by this module unsigned supportedMiscChecks = ~0U /// non-RAS checks supported by this module ); virtual ~GkAuthenticator(); /** @return true if this authenticator provides H.235 compatible security. It simply checks if m_h235Authenticators list is not empty. */ virtual bool IsH235Capable() const; /** If the authenticator supports H.235 security, this call returns H.235 security capabilities associated with it. It scans list pointed by m_h235Authenticators. @return true if H.235 security is supported by this authenticator and capabilities has been set. */ virtual bool GetH235Capability( /// append supported authentication mechanism to this array H225_ArrayOf_AuthenticationMechanism& mechanisms, /// append supported algorithm OIDs for the given authentication /// mechanism H225_ArrayOf_PASN_ObjectId& algorithmOIDs ) const; /** Check if this authenticator supports the given H.235 capability (mechanism+algorithmOID) by scanning the m_h235Authenticators list of H.235 capabilities. @return true if the capability is supported by this module. */ virtual bool IsH235Capability( /// authentication mechanism const H235_AuthenticationMechanism& mechanism, /// algorithm OID for the given authentication mechanism const PASN_ObjectId& algorithmOID ) const; /** @return Control flag determining authenticator behaviour (optional, sufficient, required). */ Control GetControlFlag() const { return m_controlFlag; } /** @return True if the check is supported (implemented) by this authenticator. */ bool IsRasCheckEnabled( unsigned rasCheck ) const { return (m_enabledRasChecks & m_supportedRasChecks & rasCheck) == rasCheck; } /** @return True if the check is supported (implemented) by this authenticator. */ bool IsMiscCheckEnabled( unsigned miscCheck ) const { return (m_enabledMiscChecks & m_supportedMiscChecks & miscCheck) == miscCheck; } /** Virtual methods overriden in derived classes to perform the actual authentication. The first parameter is a request to be checked, the second is a H225_XXXRejectReason that can be set if the authentication rejects the request. @return e_fail - authentication rejected the request e_ok - authentication accepted the request e_next - authentication is not supported for this request or cannot be determined (SQL failure, no cryptoTokens, ...) */ virtual int Check(RasPDU& req, unsigned& rejectReason); virtual int Check(RasPDU& req, unsigned& rejectReason); virtual int Check(RasPDU& req, unsigned& rejectReason); virtual int Check(RasPDU& req, unsigned& rejectReason); virtual int Check(RasPDU& req, unsigned& rejectReason); virtual int Check(RasPDU& req, unsigned& rejectReason); /** Authenticate/Authorize RAS or signaling message. @return e_fail - authentication rejected the request e_ok - authentication accepted the request e_next - authentication is not supported for this request or cannot be determined (SQL failure, no cryptoTokens, ...) */ virtual int Check( /// RRQ to be authenticated/authorized RasPDU& request, /// authorization data (reject reason, ...) RRQAuthData& authData ); virtual int Check( /// ARQ to be authenticated/authorized RasPDU& request, /// authorization data (call duration limit, reject reason, ...) ARQAuthData& authData ); virtual int Check( /// Q.931/H.225 Setup to be authenticated SetupMsg &setup, /// authorization data (call duration limit, reject reason, ...) SetupAuthData& authData ); /** Get human readable information about current module state that can be displayed on the status port interface. @return A string (may contain multiple lines) with module information. Each line (including the last one) has to be ended with \r\n. */ virtual PString GetInfo(); protected: /** @return Default authentication status, if not determined by Check... method. */ int GetDefaultStatus() const { return m_defaultStatus; } /** @return Config that contains settings for this authenticator. */ PConfig* GetConfig() const { return m_config; } /** Should be called only from derived constructor to add supported H.235 capabilities (if any). */ void AppendH235Authenticator( H235Authenticator* h235Auth /// H.235 authenticator to append ); /** @return A string that can be used to identify an account name associated with the call. */ virtual PString GetUsername( /// RRQ message with additional data const RasPDU& request ) const; virtual PString GetUsername( /// ARQ message with additional data const RasPDU& request, /// additional data, like call record and requesting endpoint ARQAuthData& authData ) const; virtual PString GetUsername( /// Q.931/H.225 Setup with additional data const SetupMsg &setup, /// additional data SetupAuthData &authData ) const; /** @return A string that can be used to identify a calling number. */ virtual PString GetCallingStationId( /// ARQ message with additional data const RasPDU& request, /// additional data, like call record and requesting endpoint ARQAuthData& authData ) const; virtual PString GetCallingStationId( /// Q.931/H.225 Setup to be authenticated const SetupMsg &setup, /// additional data SetupAuthData& authData ) const; /** @return A string that can be used to identify a calling number. */ virtual PString GetCalledStationId( /// ARQ message with additional data const RasPDU& request, /// additional data, like call record and requesting endpoint ARQAuthData& authData ) const; virtual PString GetCalledStationId( /// Q.931/H.225 Setup to be authenticated const SetupMsg &setup, /// additional data SetupAuthData& authData ) const; /// @return Number actually dialed by the user (before rewrite) PString GetDialedNumber( /// ARQ message with additional data const RasPDU& request, /// additional data ARQAuthData& authData ) const; /// @return Number actually dialed by the user (before rewrite) virtual PString GetDialedNumber( /// Q.931/H.225 Setup to be authenticated const SetupMsg &setup, /// additional data SetupAuthData& authData ) const; /// a list of H.235 capabilities supported by this module (if any) H235Authenticators* m_h235Authenticators; private: GkAuthenticator(); GkAuthenticator(const GkAuthenticator&); GkAuthenticator & operator=(const GkAuthenticator&); private: /// default status to be returned, if not determined otherwise Status m_defaultStatus; /// processing rule for this authenticator Control m_controlFlag; /// bit flags for RAS messages to be authenticated (this enforces the limit /// of first 32 RAS messages being supported) unsigned m_enabledRasChecks; /// bit flags with RAS checks supported by a given authenticator unsigned m_supportedRasChecks; /// bit flags for other event types to be authenticated (like Q.931 Setup) unsigned m_enabledMiscChecks; /// bit flags with non-RAS checks supported by a given authenticator unsigned m_supportedMiscChecks; /// authenticator config PConfig* m_config; }; /** Cache used by some authenticators to remember key-value associations, like username-password. It increases performance, as backend does not need to be queried each time. */ class CacheManager { public: CacheManager( long timeout = -1 /// cache timeout - expiry period (seconds) ) : m_ttl(timeout) { } /** Get a value associated with the key. @return true if association has been found and the value is valid, false if the key-value pair is not cached or the cache expired */ bool Retrieve( const PString & key, /// the key to look for PString & value /// filled with the value on return ) const; /// Store a key-value association in the cache void Save( const PString & key, /// a key to be stored const PString & value /// a value to be associated with the key ); void SetTimeout( long newTimeout /// new cache expiration timeout ) { m_ttl = newTimeout; } private: CacheManager(const CacheManager &); CacheManager & operator=(const CacheManager &); private: /// cache timeout (seconds), 0 = do not cache, -1 = never expires long m_ttl; /// cached key-value pairs std::map m_cache; /// timestamps for key-value pair expiration calculation std::map m_ctime; /// mutex for multiple read/mutual write access to the cache mutable PReadWriteMutex m_rwmutex; }; /** A base class for all authenticators that only checks if username-password pairs match. This authenticator checks H.235 tokens/cryptoTokens carried inside RAS requests. Currently, only simple MD5 password hash and Cisco CAT authentication token types are supported. Derived authenticators usually override only GetPassword virtual method. */ class SimplePasswordAuth : public GkAuthenticator { public: enum SupportedRasChecks { /// bitmask of RAS checks implemented by this module SimplePasswordAuthRasChecks = RasInfo::flag | RasInfo::flag | RasInfo::flag | RasInfo::flag | RasInfo::flag | RasInfo::flag | RasInfo::flag }; SimplePasswordAuth( const char* name, /// a name for this module (a config section name) unsigned supportedRasChecks = SimplePasswordAuthRasChecks, unsigned supportedMiscChecks = 0 /// none supported ); virtual ~SimplePasswordAuth(); // overriden from class GkAuthenticator virtual int Check(RasPDU& req, unsigned& rejectReason); virtual int Check(RasPDU& req, unsigned& rejectReason); virtual int Check(RasPDU& req, unsigned& rejectReason); virtual int Check(RasPDU& req, unsigned& rejectReason); virtual int Check(RasPDU& req, unsigned& rejectReason); /** Authenticate/Authorize RAS or signaling message. An override from GkAuthenticator. @return e_fail - authentication rejected the request e_ok - authentication accepted the request e_next - authentication is not supported for this request or cannot be determined (SQL failure, no cryptoTokens, ...) */ virtual int Check( /// RRQ to be authenticated/authorized RasPDU& request, /// authorization data (reject reason, ...) RRQAuthData& authData ); virtual int Check( /// ARQ to be authenticated/authorized RasPDU& request, /// authorization data (call duration limit, reject reason, ...) ARQAuthData& authData ); protected: /** Get a password associated with the identifier. @return true if the password is returned, false if the password could not be found. */ virtual bool GetPassword( const PString& id, /// get the password for this id PString& passwd /// filled with the password on return ); /** Validate username/password carried inside the tokens. This method supports only CAT and clear text tokens. @return e_ok if the username/password carried inside the tokens is valid, e_fail if the username/password carried inside the tokens is invalid, e_next if no recognized tokens have been found */ virtual int CheckTokens( /// an array of tokens to be checked const H225_ArrayOf_ClearToken& tokens, /// aliases for the endpoint that generated the tokens const H225_ArrayOf_AliasAddress* aliases ); /** Validate username/password carried inside the tokens. This method supports only simple MD5 pwdHash cryptoTokens. @return e_ok if the username/password carried inside the tokens is valid, e_fail if the username/password carried inside the tokens is invalid, e_next if no recognized tokens have been found */ virtual int CheckCryptoTokens( /// an array of cryptoTokens to be checked const H225_ArrayOf_CryptoH323Token& cryptoTokens, /// aliases for the endpoint that generated the tokens const H225_ArrayOf_AliasAddress * aliases, /// raw data for RAS PDU - required to validate some tokens /// like H.235 Auth Procedure I const PBYTEArray & rawPDU ); /** Retrieve username carried inside the token. @return username carried inside the token */ bool ResolveUserName( /// an array of tokens to be checked const H235_ClearToken & token, /// UserName detected. PString & username ); /** Retrieve username carried inside the token. @return username carried inside the token */ bool ResolveUserName( /// an array of tokens to be checked const H225_CryptoH323Token & crytotoken, /// the aliases of the request (only used for cryptoEPPwdEncr) const H225_ArrayOf_AliasAddress * aliases, /// UserName detected. PString & username ); /** A family of template functions that check tokens/cryptoTokens inside RAS messages. @return e_ok if the username/password carried inside the tokens is valid, e_fail if the username/password carried inside the tokens is invalid, e_next if no recognized tokens have been found */ template int doCheck( /// RAS request to be authenticated const RasPDU & request, /// list of aliases for the endpoint sending the request const H225_ArrayOf_AliasAddress* aliases = NULL, /// Registration Auth data RRQAuthData * authData = NULL ) { const RAS & req = request; bool finalResult = false; if (m_h235Authenticators == NULL) { PTRACE(4, "GKAUTH\tNo Loaded Authenticators"); return GetDefaultStatus(); } for (PINDEX i = 0; i < m_h235Authenticators->GetSize(); i++) { H235Authenticator * authenticator = (H235Authenticator *)(*m_h235Authenticators)[i].Clone(); authenticator->SetLocalId(Toolkit::GKName()); H235Authenticator::ValidationResult result; for (PINDEX t = 0; t < req.m_tokens.GetSize(); t++) { PString username; PString password; if (!ResolveUserName(req.m_tokens[t], username)) { PTRACE(4, "GKAUTH\t" << GetName() << " No username resolved from tokens."); continue; // skip to next token } if ((aliases == NULL) || (FindAlias(*aliases, username) == P_MAX_INDEX)) { PTRACE(4, "GKAUTH\t" << GetName() << " Token username " << username << " does not match aliases for Endpoint"); continue; // skip to next token } if (!InternalGetPassword(username, password)) { PTRACE(4, "GKAUTH\t" << GetName() << " password not found for " << username ); // do not return false let the authenticator decide whether it requires a password or not. } authenticator->SetRemoteId(username); authenticator->SetPassword(password); result = authenticator->ValidateClearToken(req.m_tokens[t]); if (result == H235Authenticator::e_OK) { PTRACE(4, "GKAUTH\tAuthenticator " << authenticator->GetName() << " succeeded"); if (authData) authData->m_authAliases.AppendString(username); return e_ok; } } for (PINDEX t = 0; t < req.m_cryptoTokens.GetSize(); t++) { PString username; PString password; if (!ResolveUserName(req.m_cryptoTokens[t], aliases, username)) { PTRACE(4, "GKAUTH\t" << GetName() << " No username resolved from tokens."); continue; // skip to next token } if ((aliases == NULL) || (FindAlias(*aliases, username) == P_MAX_INDEX)) { PTRACE(4, "GKAUTH\t" << GetName() << " Token username " << username << " does not match aliases for Endpoint"); continue; // skip to next token } if (!InternalGetPassword(username, password)) { PTRACE(4, "GKAUTH\t" << GetName() << " password not found for " << username ); // do not return false let the authenticator decide whether it requires a password or not. } authenticator->SetRemoteId(username); authenticator->SetPassword(password); //authenticator->SetChallenge(challengeToken); // TODO: set challenge token from GCF result = authenticator->ValidateCryptoToken(req.m_cryptoTokens[t], request->m_rasPDU); if (result == H235Authenticator::e_OK) { PTRACE(4, "GKAUTH\tAuthenticator " << authenticator->GetName() << " succeeded"); if (authData) authData->m_authAliases.AppendString(username); return e_ok; } } } return finalResult ? e_ok : GetDefaultStatus(); } /// Set new timeout for username/password pairs cache void SetCacheTimeout(long newTimeout) { m_cache->SetTimeout(newTimeout); } /** @return True if usernames should match one of endpoint aliases. */ bool GetCheckID() const { return m_checkID; } private: /** Get password for the given user. Examine password cache first. @return true if the password has been found. */ bool InternalGetPassword( const PString& id, /// get the password for this id PString& passwd /// filled with the password on return ); SimplePasswordAuth(); SimplePasswordAuth(const SimplePasswordAuth&); SimplePasswordAuth& operator=(const SimplePasswordAuth&); private: /// an encryption key used to decrypt passwords from the config file int m_encryptionKey; /// if true, generalID has to be also in the endpoint alias list bool m_checkID; /// cache for username/password pairs CacheManager* m_cache; /// list of H.235 algorithms to disable PStringArray m_disabledAlgorithms; }; #ifdef H323_H350 /// H.350 authenticator for H.235 enabled endpoints class H350PasswordAuth : public SimplePasswordAuth { public: /// build authenticator reading settings from the config H350PasswordAuth( /// name for this authenticator and for the config section to read settings from const char* authName ); virtual ~H350PasswordAuth(); protected: /** Override from SimplePasswordAuth. @return True if the password has been found for the given alias. */ virtual bool GetPassword( /// alias to check the password for const PString& alias, /// password string, if the match is found PString& password ); }; #endif /** A base class for all authenticators that validate endpoints (requests) by alias and/or IP address only. Derived authenticators usually override GetAuthConditionString virtual method only. */ class AliasAuth : public GkAuthenticator { public: enum SupportedRasChecks { /// bitmask of RAS checks implemented by this module AliasAuthRasChecks = RasInfo::flag }; AliasAuth( const char* name, /// a name for this module (a config section name) unsigned supportedRasChecks = AliasAuthRasChecks, unsigned supportedMiscChecks = 0 ); virtual ~AliasAuth(); /// an override from GkAuthenticator virtual int Check( /// RRQ to be authenticated/authorized RasPDU& request, /// authorization data (reject reason, ...) RRQAuthData& authData ); protected: /** Validate that the signaling addresses match the given condition. The condition consists of one or more auth rules. @return true if the signaling addresses match the condition. */ virtual bool doCheck( /// an array of source signaling addresses for an endpoint that sent the request const H225_ArrayOf_TransportAddress& sigaddr, /// auth condition string as returned by GetAuthConditionString const PString& condition ); /** Validate that the signaling address matches the given auth rule. @return true if the signal address matches the rule. */ virtual bool CheckAuthRule( /// a signaling address for the endpoint that sent the request const H225_TransportAddress& sigaddr, /// the auth rule to be used for checking const PString& authrule ); /** Get AliasAuth condition string for the given alias. This implementation searches RasSrv::RRQAuth section for the string. The string is then used to accept/reject the request, optionally checking its source signaliing addresses. The string consists of one or more auth rules separated by '|' or '&' character. @return The AliasAuth condition string for the given alias. */ virtual bool GetAuthConditionString( /// an alias the condition string is to be retrieved for const PString& alias, /// filled with auth condition string that has been found PString& authCond ); /// Set new timeout for username/password pairs cache void SetCacheTimeout( long newTimeout ) { m_cache->SetTimeout(newTimeout); } private: /** Get auth condition string for the given user. Examine the cache first. @return true if the auth condition string has been found. */ bool InternalGetAuthConditionString( const PString& id, /// get the password for this id PString& authCond /// filled with the auth condition string on return ); AliasAuth(); AliasAuth(const AliasAuth&); AliasAuth& operator=(const AliasAuth&); private: /// cache for username/password pairs CacheManager* m_cache; }; /** A list of authenticators. Usually created as a single global object by the RasServer. */ class GkAuthenticatorList { public: /// creates an empty list - OnRealod builds the actual stack of authenticator GkAuthenticatorList(); virtual ~GkAuthenticatorList(); /// read the config file and build a new stack of authenticator modules void OnReload(); /** Select H.235 authentication mechanisms supported both by the endpoint sending GRQ and all the authenticators, and copy these into GCF. If no common H.235 capabilities can be found, do not select any authentication mechanisms with GCF. */ void SelectH235Capability( const H225_GatekeeperRequest& grq, H225_GatekeeperConfirm& gcf ); /** Authenticate the request through all configured authenticators. Currently, only RAS requests are supported. @return true if the request should be accepted, false to reject the request. */ template bool Validate( /// the request to be validated by authenticators RasPDU& request, /// H225_RegistrationRejectReason to be set if the request is rejected unsigned& rejectReason ) { ReadLock lock(m_reloadMutex); std::list::const_iterator i = m_authenticators.begin(); while (i != m_authenticators.end()) { GkAuthenticator* auth = *i++; if (auth->IsRasCheckEnabled(RasInfo::flag)) { const int result = auth->Check(request, rejectReason); if (result == GkAuthenticator::e_ok) { PTRACE(3, "GKAUTH\t" << auth->GetName() << ' ' << request.GetTagName() << " check ok" ); if (auth->GetControlFlag() == GkAuthenticator::e_Sufficient || auth->GetControlFlag() == GkAuthenticator::e_Alternative) return true; } else if (result == GkAuthenticator::e_fail) { PTRACE(3, "GKAUTH\t" << auth->GetName() << ' ' << request.GetTagName() << " check failed"); SNMP_TRAP(8, SNMPError, Authentication, auth->GetName() + " check failed"); return false; } } } return true; } /** Authenticate and authorize RRQ through all configured authenticators. @return true if the endpoint should be registered, false to send RRJ. */ bool Validate( /// RRQ to be validated by authenticators RasPDU& request, /// authorization data (reject reason, ...) RRQAuthData& authData ); /** Authenticate and authorize (set call duration limit) ARQ through all configured authenticators. @return true if the call should be admitted, false to send ARJ. */ bool Validate( /// ARQ to be validated by authenticators RasPDU& request, /// authorization data (call duration limit, reject reason, ...) ARQAuthData& authData ); /** Authenticate and authorize (set call duration limit) Q.931/H.225 Setup through all configured authenticators. @return true if the call should be accepted, false to send ReleaseComplete. */ bool Validate( /// Q.931/H.225 Setup to be authenticated SetupMsg &setup, /// authorization data (call duration limit, reject reason, ...) SetupAuthData& authData ); /** Get a module information string for the selected module. @return The module information string for status port diplay. */ PString GetInfo( const PString &moduleName /// module to retrieve information for ) { ReadLock lock(m_reloadMutex); std::list::const_iterator i = m_authenticators.begin(); while (i != m_authenticators.end()) { GkAuthenticator* auth = *i++; if (auth->GetName() == moduleName) return auth->GetInfo(); } return moduleName + " module not found\r\n"; } private: GkAuthenticatorList(const GkAuthenticatorList& ); GkAuthenticatorList & operator=(const GkAuthenticatorList &); private: /// a list of all configured authenticators std::list m_authenticators; /// reload/destroy mutex PReadWriteMutex m_reloadMutex; /// the most common authentication capabilities /// shared by all authenticators on the list H235Authenticators m_h235authenticators; }; /** A factory template for authenticator objects. When you create your own authenticator class (derived from GkAuthenticator), you need to register it and tell the gatekeeper how to instantiate it. You can do it by putting the following code: namespace { GkAuthCreator MY_AUTH_FACTORY("MyAuthClass"); } This registers "MyAuthClass" string as the name to be used in the config for MyAuthClass authenticator. Of course, authenticator name and class name do not have to be the same. */ template struct GkAuthCreator : public Factory::Creator0 { GkAuthCreator(const char *n) : Factory::Creator0(n) { } virtual GkAuthenticator *operator()() const { return new Auth(m_id); } }; #endif // GKAUTH_H gnugk-3.4/PaxHeaders.16356/versionts.h0000644000175000001440000000005011411166002015716 xustar000000000000000020 atime=1380868658 20 ctime=1380868611 gnugk-3.4/versionts.h0000644000175000001440000000024011411166002014653 0ustar00janusers00000000000000/* Dummy file provided to be a special dependency of version.cxx to ensure that the correct build timestamp is compiled into the gatekeeper executable */ gnugk-3.4/PaxHeaders.16356/docs0000644000175000001440000000005012223461004014366 xustar000000000000000020 atime=1380868658 20 ctime=1380868612 gnugk-3.4/docs/0000755000175000001440000000000012223461004013404 5ustar00janusers00000000000000gnugk-3.4/docs/PaxHeaders.16356/manual0000644000175000001440000000005012223461004015643 xustar000000000000000020 atime=1380868658 20 ctime=1380868612 gnugk-3.4/docs/manual/0000755000175000001440000000000012223461004014661 5ustar00janusers00000000000000gnugk-3.4/docs/manual/PaxHeaders.16356/routing.sgml0000644000175000001440000000005012210670355020302 xustar000000000000000020 atime=1380868658 20 ctime=1380868612 gnugk-3.4/docs/manual/routing.sgml0000644000175000001440000012714012210670355017250 0ustar00janusers00000000000000Routing Configuration

The following sections in the config file can be used to configure how calls are routed. For GnuGk, "routing" means that the gatekeeper must find a destination IP for each new call. For example GnuGk may need to decide where to send a voice call given a particular E.164 destination; there may be multiple IP-to-ISDN gateways which it may choose from for that E.164 address. Routing decisions are typically made by examining the called name or number, but GnuGk has flexibility in what it evaluates in order to successfully route the call.

Each call gets passed down a chain of routing policies. Each policy may route the call and terminate the chain or modify it and pass it on. You can use the setting in the following sections to specify which policies to use and modify their behavior. Section [RoutingPolicy]

This section explains how routing policies are configured. An incoming call request can be routed using the following methods:

The destination is explicitly specified in the call to be routed. This policy is needed for dialing by IP address. You can define mappings for the destination IP in the section.

The classic rule; search for the destination in RegistrationTable

Route the call using information sent by the parent gatekeeper in reply to an ARQ the gatekeeper will send (only LRQs to the child will be forwarded als LRQs). You can define your parent gatekeeper using the section.

Route the call using neighbors by exchanging LRQ messages.

The destination is resolved using DNS A records or IP addresses in the called alias. This policy can be configured in the section.

Route calls by rewriting the called alias with a database query or send them directly to a destination IP. The database parameters are specified in the section.

Route calls by looking up the called alias in an LDAP server (searching the H323ID and TelephoneNo attribute) and send the call to the IP in the CallDestination attribute.

The LDAP server is configured in the section and the attributes are defined in the section.

Use the virtual queue mechanism and generate a RouteRequest event to allow an external application to make a routing decision.

Provides support for overlapped digit sending for ARQ messages. This also partially supports Setup messages (no overlapped sending - only number length validation).

ENUM (RFC3761) is a method to use DNS lookups to convert real International Direct Dialing E.164 numbers into H.323 dialing information. The default servers are section. Additional ENUM schemas for gateways aside from the default "E2U+h323" may be supported via the "enum::id" Routing policy refer section.

DNS SRV or H.323 Annex O allows for the routing of calls using a H.323 URI. Addresses can be configured as user (at) domain. H.323 URIs are stored in the SRV DNS records of the domain and are queried to find the destination. This policy can be configured in the section. Additional SRV schemas for gateways aside from the default "h323ls._udp." and "h323cs._tcp." may be supported via the "srv::id" Routing policy refer section.

RDS Resolver Discovery Service or DDDS Dynamic Delegation Discovery Service (examples in RFC3404 sect 5.2/5.3) This policy is a mechanism whereby domain name SRV records are hosted in central DNS servers. The servers are set by [RoutedMode] RDSServers and are queried in order to resolve H323+D2U NAPTR records which contain H.323 Annex O SRV records for domains. This can be used to virtually host URL domains or centralize the control of SRV records. This policy can be configured in the section.

This policy will perform a database lookup if calls to this destination should be forwarded. The configuration for this policy must be in the section.

This policy will route all calls that reach it to one endpoint specified in the section. You can use it as a fallback at the end of the policy chain to route all calls which would otherwise fail.

This policy runs the LUA script defined in section to set a call destination. NOTE: This policy is still experimental and may change!

Same as neighbor policy except the target for the LRQ messages are retrieved from a database. The database parameters are identical to the sql routing policy.

Default configuration for routing policies is as follows: [RoutingPolicy] default=explicit,internal,parent,neighbor If one policy does not match, the next policy is tried. These policies can be applied to a number of routing request types and routing input data. The different types are ARQ, LRQ, Setup and Facility (with the callForwarded reason). There is also the general routing policy, which is a default for the other types. [RoutingPolicy] h323_ID=dns,internal 002=neighbor,internal Default=internal,neighbor,parent When a message is received which requires a routing decision, all calls to an alias of the h323_ID type will be resolved using DNS. If DNS fails to resolve the alias, it is matched against the internal registration table. If a call is requested to an alias starting with 002, the neighbors will be checked first, then the internal registration table. If the requested alias is not an h323_ID or an alias starting with 002, the default policy is used by querying the internal registration table, then the neighbors, and if those fail, the parent. Routing policies are applied to the first message of a call: The ARQ for calls from registered endpoints, the Setup for calls from unregistered endpoints, the LRQ for calls from neighbors and certain Facility messages for calls that are forwarded by GnuGk using the ForwardOnFacility feature. You can specify different routing policies for each type of call by using the [RoutingPolicy::OnARQ], [RoutingPolicy::OnLRQ], [RoutingPolicy::OnSetup] and [RoutingPolicy::OnFacility] sections using the same syntax explained above. [RoutingPolicy::OnARQ] default=numberanalysis,internal,neighbor A typical ENUM routing setup would look like this: [RoutingPolicy] default=explicit,internal,enum,srv,dns,internal,parent,neighbor Section [RasSrv::RewriteE164]

This section defines the rewriting rules for dialedDigits (E.164 number). [!]original-prefix=target-prefix

If the number begins with If you dial If you dial Option: Default: N/A

Only rewrite dialDigits beginning with the specified prefix. Section [RasSrv::RewriteAlias]

This section defines the rewriting rules for aliases. This can be used to map gatekeeper assigned aliases to registered endpoints. [!]original-alias=target-alias

If the alias is Section [RasSrv::GWRewriteE164]

This section describes rewriting the dialedDigits E.164 number depending on the gateway a call has come from or is being sent to. This allows for more flexible manipulation of the dialedDigits for routing etc. Despite the name of the section, you can not only rewrite calls from and to gateways, but also calls from terminals (regular endpoints) and neighbor gatekeepers. In combination with the you can have triple stage rewriting: Call from "gw1", dialedDigits 0867822 | | V Input rules for "gw1", dialedDigits now 550867822 | | V Global rules, dialedDigits now 440867822 | | V Gateway selection, dialedDigits now 440867822, outbound gateway "gw2" | | V Output rules for "gw2", dialedDigits now 0867822 | | V Call to "gw2", dialedDigits 0867822 alias=in|out=[!]original-prefix=target-prefix[;in|out...]

If the call matches the alias, the direction and begins with To convert dialed digits into post dial digits that are sent to the remote side after the call connects as UserInputIndications, use 'I' (for Input) on the prefix side and 'P' (for Postdial) on the target side. Please note that H.245 routing through the gatekeeper must be active to send post dial digits.

Calls from and to gateways and terminals are matched by their first alias. Calls from and to neighbors are matched by the neighbor ID in the GnuGk config (the XXX in the [Neighbor::XXX] section name) or the gatekeeper identifier of the neighbor if it is set. Note that when you have multi-homed neighbors or are accepting non-neighbor LRQs, the source of the call can not always be determined and no IN rule for a neighbor will match. In these cases you should only use OUT and [RasSrv::RewriteE164] rules. If a call is received from "gw1" to If a call is sent out through "gw1" to In this example the neighbor is identified by its ID and incoming calls from NbGk will have their 01 prefix replaced by a 04 prefix. Outgoing calls will have 04 replaced with 01. [RasSrv::Neighbors] NbGk=GnuGk [Neighbor::NbGk] GatekeeperIdentifier=GK-PW-Prox Host=192.168.1.100 SendPrefixes=* AcceptPrefixes=* [RasSrv::GWRewriteE164] NbGk=in=01=04;out=04=01 In this example the neighbor is identified by its gatekeeper identifier and incoming calls from GK-PW-Prox that don't have a 0049 prefix get the prefix prepended. A call to "1234" would be rewritten to "00491234", while a call to "00496789" would proceed unchanged because the "If incoming does not start with 0049 and any number of digits after 0049, then prepend 0049" logic would be false (because we already have 0049 at the beginning.) [RasSrv::Neighbors] NbGk=GnuGk [Neighbor::NbGk] GatekeeperIdentifier=GK-PW-Prox Host=192.168.1.100 SendPrefixes=* AcceptPrefixes=* [RasSrv::GWRewriteE164] GK-PW-Prox=in=!0049.=0049. Section [Endpoint::RewriteE164]

Once you specify prefix(es) for your gatekeeper endpoint, the parent gatekeeper will route calls with For example, if you have the following configuration, [Parent GK] ID=MasterGK / \ / \ / \ / \ [Child GK] [EP3] ID=ProxyGK E164=18888200 Prefix=188886 / \ / \ / \ [EP1] [EP2] E164=601 E164=602 With this rule: 188886=6 When EP1 calls EP3 by , though the latter will take effect first. Section [Routing::DNS]

Default: 1

This switch determines whether the DNS policy should resolve hostnames or IPs in LRQs that don't terminate locally. Section [Routing::ENUM]

Default: 0

This switch toggles whether the 'enum' policy should resolve LRQs. Additional ENUM schemas may be configured by the [Routing::ENUM::id] [Routing::ENUM::2] E2U+xmpp=mygateway@mydomain.com Section [Routing::SRV]

Default: 0

This switch selects if the 'srv' policy should resolve hostnames in LRQs that don't terminate locally. Additional SRV schemas may be configured by the [Routing::SRV::id] [Routing::SRV::2] _xmpp-server._tcp=mygateway@mydomain.com Section [Routing::RDS]

Default: 0

This switch selects if the 'rds' policy should resolve hostnames in LRQs. Section [Routing::Explicit]

You can define a mapping where calls to certain IPs should be routed by the 'explicit' policy. The new destination can either be another IP or an alias destination of any type. If you rewrite the destination to something other than an IP, make sure you have other routing policies in the chain behind the 'explicit' policy that can handle the new destination. [Routing::Explicit] 192.168.1.100=10.10.1.100 192.168.1.101=10.10.1.101:1720 192.168.1.102=654251 192.168.1.103=peter 192.168.1.104=joe@company.com Section [Routing::Sql]

Rewrite the called alias with a SQL query. Supports routing OnARQ, OnLRQ and OnSetup. If the string returned from the database is 'REJECT' (upper or lower case), the call is rejected. If the string matches a dotted IP address, it is taken as destination IP otherwise it is treated as a new destination alias. If 2 columns are returned, the first is treated as the new destination alias and the second is treated as new destination IP. If the 2nd column contains 'IGNORE', the database result is treated as if it would only contain 1 result column. (This allows simpler SQL queries in some cases.) If multiple rows of destination IPs are returned they are used as alternative routes for failover and GnuGk will try them in order. When at least one destination IP is specified or the call is rejected, the SQL policy will end the routing chain. If only the alias is changed, the chain continues with this updated alias. When rejecting a call, the 2nd column can contain an integer designating the reject reason (H.225 AdmissionRejectReason for registered calls, H.225 LocationRejectReason for neighbor calls, H.225 disconnect reason for unregistered calls). If the database returns nothing, the call is passed on unchanged. Use the to define your database connection for this module. Default: N/A

Define a SQL query to fetch the new destination number. The query is parameterized - that means parameter replacement is made before each query is executed. The following parameters are defined: Some of these can be empty if they aren't included in the ARQ, LRQ or Setup message.

If the query returns no rows, the current alias is used. Otherwise, the first result row is used.

Query string examples. Note that these are examples; the actual structure and schema are user defined, as are the various field names in these examples. GnuGk is simply expecting either IP addresses or aliases as a result of the query. SELECT destination FROM routes WHERE called = '%c' SELECT concat(prefix,'%c') FROM routes WHERE prefix = LEFT('%c', 5) SELECT gatewayip FROM routes WHERE prefix = LEFT('%c',5) SELECT concat(prefix,'%c'), gatewayip FROM routes WHERE route = LEFT('%c', 5) limit 3 Default: 0

Enable basic regex rewriting where parts of the original called alias are inserted into the result of the database query. Regular expressions: {\1} - all of the original called alias {^\d(4)} - first 4 digits {\d(4)$} - last 4 digits Examples: Assuming the called alias was "12345678" and the database returns "{\1}@my.com" then all character are inserted so the new destination is "1234578@my.com". If the database returns "{^\d(4)}@my.com" the first 4 digits are inserted so the new destination is "1234@my.com" and with "{\d(4)$}@my.com" from the database, the call is sent to "4578@my.com". Section [Routing::NeighborSql]

Select which neighbor to query for a call with a database query. Use the to define your database connection for this module. Default: N/A

Define a SQL query to fetch the new neighbor IP and port. Section [Routing::NumberAnalysis]

This section defines rules for the prefix=MIN_DIGITS[:MAX_DIGITS]

If the number matches the [RoutingPolicy::OnARQ] default=numberanalysis,internal [Routing::NumberAnalysis] 0048=12 48=10 .=6:20

Calls to destinations starting with 0048 require at least 12 digits, to 48 we require 10 digits and to all other destinations at least 6 and at most 20 digits. Section [Routing::Forwarding]

This routing policy performs a database lookup if calls to an endpoint should be forwarded to another endpoint. It supports routing OnARQ, OnSetup and OnLRQ. There are different types of forwards: The destination where calls are forwarded to should either be aliases of local endpoints (incl. permanent endpoints) or IP numbers. For local aliases, GnuGk will check if the destination also has forwarding configured and take it into account. Use the to define your database connection for this module. Specifically for this module, you can specify a query to read the forwarding rules: Default: N/A

Define a SQL query to fetch the forwarding rules. The query must return 2 colums: First the code for the forwarding type and second the new destination. It must ensure that the results are ordered ascending by forwarding code. The query is parameterized - that means parameter replacement is made before each query is executed. The following parameters are defined: Some of these can be empty if they aren't included in the ARQ or Setup message. In most cases you should only use the called alias in the SQL query, since they apply only to the incoming call and won't change when looking up chained forwarding rules.

[RoutedMode] GKRouted=1 AcceptUnregisteredCalls=1 ; failover must be on for forward on timeout ActivateFailover=1 FailoverCauses=1-15,17-127 DisableRetryChecks=1 ; 10 sec alerting timeout (for forward on no answer) AlertingTimeout=10000 [RoutingPolicy] default=explicit,forwarding,internal,neighbor,explicit [Routing::Forwarding] Driver=MySQL Host=localhost Database=gnugk Username=gnugk Password=secret Query=SELECT forwardtype, destination FROM forwards WHERE called = '%c' order by forwardtype asc MinPoolSize=1 create table gnugk.forwards ( called varchar(30) not null, forwardtype smallint not null, destination varchar(30) not null default "", PRIMARY KEY (called, forwardtype) ); "1234", 1, "2000" "5678", 2, "4000" "5678", 3, "4000" "9876", 4, "5000" Section [Routing::CatchAll]

Default: (empty)

Specify an IP address to route all calls to. This overrides CatchAllAlias. Default: catchall

If CatchAllIP is not specified, then route all calls to this alias. Section [Routing::Lua]

NOTE: This policy is still experimental and may change in the next release. Please contact the GNU Gatekeeper authors if you want to use it in production. The LUA script has the following source - source IP calledAlias - called alias (only first alias) calledIP - called IP address if IP dialing was used caller - aliasses of the caller callingStationId - calling station ID callid - the call ID messageType - the message that triggered the routing process ('ARQ', 'LRQ', 'Setup' or 'Facility') clientauthid - client auth ID The LUA script can set these action - set this to either 'SKIP' or 'REJECT' if you don' want to route the call, otherwise the call is routed to destAlias or destIp (see below) rejectCode - reject reason to use with 'REJECT' destAlias - call destination alias destIP - call destination IP To access external resources, LUA scripts can use LUA libraries, eg. LuaSocket. Default: (empty)

Define the LUA script to run, right in the config file. Default: (empty)

Specify a file with a LUA script to run for the 'lua' policy. Section [RewriteCLI]

This section contains a set of rewrite rules for ANI/CLI/H.323_ID numbers (Caller ID). The rewrite process is done in two stages - inbound rewrite and outbound rewrite. The inbound rewrite is done before any other Q.931 Setup message processing (such as inbound GWRewrite, authentication, accounting, ...), and because it alters the Calling-Station-Id it will have an effect in the authorization and accounting modules. The outbound rewrite takes place just before the Setup message is to be forwarded and its effect is visible only to the callee.

An inbound rewrite rule can be matched by a caller's IP and a dialed number or an original CLI/ANI. An outbound rewrite rule can be matched by a caller's IP, callee's IP and a dialed number or a destination number (the dialed number after rewrite) or a CLI/ANI (after inbound rewrite).

This module also provides CLIR (Calling Line Identification Restriction) feature that can be configured for each endpoint (rule). Default:

In addition to rewriting a Calling-Party-Number Information Element ("IE"), the sourceAddress element of a H.225.0 Setup message can be rewritten, so both contain consistent information. Default:

When a sourceInfo element of an H.225.0 Setup message is rewritten, aliases of type H323_ID, email_ID and url_ID can be left untouched if this option is disabled. Default: N/A

A global Presentation Indicator ("PI") processing policy can be set up. This policy will be applied to all CLI rewrite rules that do not override it. Possible choices are in:CALLER_IP=[pi=[allow|restrict][,forward|apply|applyforterminals]] [cli:|dno:]number_prefix(=|*=|~=|^=|/=)NEW_CLI[,NEW_CLI]...

The The optional