////////////////////////////////////////////////////////////////// // // Routing Mechanism for GNU Gatekeeper // // Copyright (c) Citron Network Inc. 2003 // // This work is published under the GNU Public License (GPL) // see file COPYING for details. // We also explicitely grant the right to link this code // with the OpenH323 library. // // initial author: Chih-Wei Huang // initial version: 06/18/2003 // ////////////////////////////////////////////////////////////////// #if defined(_WIN32) && (_MSC_VER <= 1200) #pragma warning(disable:4786) // warning about too long debug symbol off #pragma warning(disable:4284) #endif #include #include #include #include "gk_const.h" #include "h323util.h" #include "Toolkit.h" #include "stl_supp.h" #include "RasTbl.h" #include "RasSrv.h" #include "GkClient.h" #include "GkStatus.h" #include "sigmsg.h" #include "Routing.h" 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 long DEFAULT_ROUTE_REQUEST_TIMEOUT = 10; const char* const CTIsection = "CTI::Agents"; Route::Route() : m_proxyMode(CallRec::ProxyDetect), m_flags(0) { Toolkit::Instance()->SetRerouteCauses(m_rerouteCauses); } Route::Route( const endptr &destEndpoint ) : m_destAddr(destEndpoint->GetCallSignalAddress()), m_destEndpoint(destEndpoint), m_proxyMode(CallRec::ProxyDetect), m_flags(0) { Toolkit::Instance()->SetRerouteCauses(m_rerouteCauses); } Route::Route( const PString &policyName, const H225_TransportAddress &destAddr ) : m_destAddr(destAddr), m_policy(policyName), m_proxyMode(CallRec::ProxyDetect), m_flags(0) { Toolkit::Instance()->SetRerouteCauses(m_rerouteCauses); } Route::Route( const PString &policyName, const PIPSocket::Address &destIpAddr, WORD destPort ) : m_destAddr(SocketToH225TransportAddr(destIpAddr, destPort)), m_policy(policyName), m_proxyMode(CallRec::ProxyDetect), m_flags(0) { Toolkit::Instance()->SetRerouteCauses(m_rerouteCauses); } PString Route::AsString() const { return AsDotString(m_destAddr) + " (policy: " + m_policy + ", proxy: " + PString(m_proxyMode) + ", flags: " + PString(m_flags) + ")"; } 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: " << 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); 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(); } // 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; } // class LocationRequest template<> H225_ArrayOf_AliasAddress *LocationRequest::GetAliases() { return (m_request.m_destinationInfo.GetSize() > 0) ? &m_request.m_destinationInfo : NULL; } // 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; } // 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; } bool Policy::Handle(SetupRequest& request) { if( IsActive() ) { #if PTRACING const PString tagname = request.GetWrapper()->GetTagName(); const unsigned crv = request.GetWrapper()->GetCallReference(); PTRACE(5, "ROUTING\tChecking policy " << m_name << " for request " << tagname << " CRV=" << crv ); #endif if (OnRequest(request)) { #if PTRACING PTRACE(5, "ROUTING\tPolicy " << m_name << " applied to the request " << tagname << " CRV=" << crv ); #endif return true; } } return m_next && m_next->Handle(request); } bool Policy::Handle(FacilityRequest& request) { if( IsActive() ) { #if PTRACING const PString tagname = request.GetWrapper()->GetTagName(); const unsigned crv = request.GetWrapper()->GetCallReference(); PTRACE(5, "ROUTING\tChecking policy " << m_name << " for request " << tagname << " CRV=" << crv ); #endif if (OnRequest(request)) { #if PTRACING PTRACE(5, "ROUTING\tPolicy " << m_name << " applied to the request " << tagname << " CRV=" << crv ); #endif return true; } } return m_next && m_next->Handle(request); } // 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)); 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]); return policy ? policy->HandleRas(request) : false; } bool Analyzer::Parse(LocationRequest & request) { ReadLock lock(m_reloadMutex); request.SetRejectReason(H225_LocationRejectReason::e_requestDenied); Policy *policy = ChoosePolicy(request.GetAliases(), m_rules[1]); return policy ? policy->HandleRas(request) : false; } bool Analyzer::Parse(SetupRequest & request) { ReadLock lock(m_reloadMutex); request.SetRejectReason(H225_ReleaseCompleteReason::e_calledPartyNotRegistered); Policy *policy = ChoosePolicy(request.GetAliases(), m_rules[2]); return policy ? policy->Handle(request) : false; } bool Analyzer::Parse(FacilityRequest & request) { ReadLock lock(m_reloadMutex); request.SetRejectReason(H225_ReleaseCompleteReason::e_calledPartyNotRegistered); Policy *policy = ChoosePolicy(request.GetAliases(), m_rules[3]); return policy ? policy->Handle(request) : false; } Policy *Analyzer::Create(const PString & cfg) { return Policy::Create(cfg.ToLower().Tokenise(",;|", false)); } Policy *Analyzer::ChoosePolicy(const H225_ArrayOf_AliasAddress *aliases, Rules & rules) { // 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); } // the simplest policy, the destination has been explicitly specified class ExplicitPolicy : public Policy { public: ExplicitPolicy() { m_name = "Explicit"; } protected: virtual bool OnRequest(AdmissionRequest &); // the policy doesn't apply to LocationRequest virtual bool OnRequest(SetupRequest &); virtual bool OnRequest(FacilityRequest &); }; bool ExplicitPolicy::OnRequest(AdmissionRequest & request) { H225_AdmissionRequest & arq = request.GetRequest(); if (arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) { Route route(m_name, arq.m_destCallSignalAddress); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr( route.m_destAddr ); 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)) { Route route(m_name, setup.m_destCallSignalAddress); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr( route.m_destAddr ); return request.AddRoute(route); } return false; } bool ExplicitPolicy::OnRequest(FacilityRequest & request) { H225_Facility_UUIE & facility = request.GetRequest(); if (facility.HasOptionalField(H225_Facility_UUIE::e_alternativeAddress)) { Route route(m_name, facility.m_alternativeAddress); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr( route.m_destAddr ); return request.AddRoute(route); } return false; } // the classical policy, find the dstionation 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 &); private: bool roundRobin; }; 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; RegistrationTable::Instance()->FindEndpoint( aliases, false, true, routes ); list::iterator i = routes.begin(); while (i != routes.end()) { i->m_policy = m_name; request.AddRoute(*i++); } return !routes.empty(); } // a policy to route call to parent // the policy was originally in GkClient.cxx, // but damn VC has problem to instantiate the creator class ParentPolicy : public Policy { public: ParentPolicy(); private: // override from class Policy virtual bool IsActive(); virtual bool OnRequest(AdmissionRequest &); virtual bool OnRequest(LocationRequest &); virtual bool OnRequest(SetupRequest &); virtual bool OnRequest(FacilityRequest &); GkClient *m_gkClient; }; ParentPolicy::ParentPolicy() { m_gkClient = RasServer::Instance()->GetGkClient(); m_name = "Parent"; } bool ParentPolicy::IsActive() { return m_gkClient->IsRegistered(); } 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); } // a policy to look up the destination from DNS class DNSPolicy : public AliasesPolicy { public: DNSPolicy() { m_name = "DNS"; } protected: virtual bool FindByAliases(RoutingRequest &, H225_ArrayOf_AliasAddress &); virtual bool FindByAliases(LocationRequest &, H225_ArrayOf_AliasAddress &); }; bool DNSPolicy::FindByAliases( RoutingRequest &request, H225_ArrayOf_AliasAddress &aliases ) { for (PINDEX i = 0; i < aliases.GetSize(); ++i) { PString alias(AsString(aliases[i], FALSE)); PINDEX at = alias.Find('@'); PString domain = (at != P_MAX_INDEX) ? alias.Mid(at + 1) : alias; H225_TransportAddress dest; if (GetTransportAddress(domain, GK_DEF_ENDPOINT_SIGNAL_PORT, dest)) { PIPSocket::Address addr; if (!(GetIPFromTransportAddr(dest, addr) && addr.IsValid())) continue; Route route(m_name, dest); route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(dest); request.AddRoute(route); request.SetFlag(RoutingRequest::e_aliasesChanged); // remove the domain name part H323SetAliasAddress(alias.Left(at), aliases[i]); return true; } } return false; } bool DNSPolicy::FindByAliases(LocationRequest & request, H225_ArrayOf_AliasAddress & aliases) { return DNSPolicy::FindByAliases((RoutingRequest&)request, aliases); } 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 " <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 = PMIN(PMAX(100,m_requestTimeout),20000); 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:"<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:"<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); } else { PTRACE(2,"VQueue\t(CTI) Virtual queues enabled (regex:"<GetEndpointIdentifier().GetValue()); if (RouteRequest *r = InsertRequest(epid, crv, destinationInfo, callSigAdr, duprequest)) { PString msg = PString(PString::Printf, "RouteRequest|%s|%s|%u|%s|%s", (const char *)AsDotString(caller->GetCallSignalAddress()), (const char *)epid, crv, (const char *)vqueue, (const char *)sourceInfo ); if (Toolkit::AsBool(GkConfig()->GetString("Gatekeeper::Main", "SignalCallId", 0))) { msg += PString("|") + callID; } msg += PString(";"); // 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,routereject) result = r->m_sync.Wait(m_requestTimeout); m_listMutex.Wait(); m_pendingRequests.remove(r); m_listMutex.Signal(); if( !result ) PTRACE(5,"VQueue\tRoute request (EPID: "<m_callingEpId <<", CRV="<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 destionationCallSignalAddress (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 ) { PWaitAndSignal lock(m_listMutex); // signal the command to each pending request bool foundrequest = false; RouteRequests::iterator i = m_pendingRequests.begin(); while (i != m_pendingRequests.end()) { RouteRequest *r = *i; if (r->m_callingEpId == callingEpId && r->m_crv == crv) { // replace virtual queue aliases info with agent aliases *r->m_agent = agent; if (!destinationip.IsEmpty()) // RoteToGateway *r->m_callsignaladdr = destinationip; r->m_sync.Signal(); if( !foundrequest ) { foundrequest = true; if( agent.GetSize() > 0 ) PTRACE(2,"VQueue\tRoute request (EPID :"<m_callingEpId == callingEpId && r->m_crv == crv) duprequests++; ++i; } if( duprequests ) { duplicate = true; PTRACE(5,"VQueue\tRoute request (EPID: "<GetVirtualQueue(); m_name = "VirtualQueue"; } bool VirtualQueuePolicy::IsActive() { return m_vqueue->IsActive(); } bool VirtualQueuePolicy::OnRequest(AdmissionRequest & request) { if (H225_ArrayOf_AliasAddress *aliases = request.GetAliases()) { const PString agent(AsString((*aliases)[0], FALSE)); if (m_vqueue->IsDestinationVirtualQueue(agent)) { H225_AdmissionRequest & arq = request.GetRequest(); PTRACE(5,"Routing\tPolicy "<FindByEndpointId(arq.m_endpointIdentifier); // should not be null PString * callSigAdr = new PString(); if (ep && m_vqueue->SendRouteRequest(ep, unsigned(arq.m_callReferenceValue), aliases, callSigAdr, agent, AsString(arq.m_srcInfo), AsString(arq.m_callIdentifier.m_guid))) request.SetFlag(RoutingRequest::e_aliasesChanged); if (!callSigAdr->IsEmpty()) { if (!arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) { arq.IncludeOptionalField(H225_AdmissionRequest::e_destCallSignalAddress); } PStringArray adr_parts = callSigAdr->Tokenise(":", FALSE); PString ip = adr_parts[0]; WORD port = (WORD)(adr_parts[1].AsInteger()); if (port == 0) port = 1720; arq.m_destCallSignalAddress = SocketToH225TransportAddr(ip, port); } delete callSigAdr; // 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 || aliases->GetSize() == 0) return true; } } return false; } 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 &); // TODO //virtual bool OnRequest(LocationRequest &); //virtual bool OnRequest(FacilityRequest &); 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; }; 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"; PConfig *cfg = GkConfig(); PStringToString kv = cfg->GetAllKeyValues("Routing::NumberAnalysis"); m_prefixes.resize(kv.GetSize()); for (PINDEX i = 0; i < kv.GetSize(); i++) { const PString &val = kv.GetDataAt(i); m_prefixes[i].m_prefix = 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 PTRACING 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); } #endif /* PTRACING */ } bool NumberAnalysisPolicy::OnRequest(AdmissionRequest & request) { H225_ArrayOf_AliasAddress *aliases = request.GetAliases(); if (aliases == NULL) return false; for (PINDEX i = 0; i < aliases->GetSize(); i++) { const H225_AliasAddress &alias = (*aliases)[i]; 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 i = 0; i < aliases->GetSize(); ++i) { const H225_AliasAddress &alias = (*aliases)[i]; 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; } // a policy to look up the destination from ENUM Name Server class ENUMPolicy : public AliasesPolicy { public: ENUMPolicy() { m_name = "ENUM"; } protected: virtual bool FindByAliases(RoutingRequest &, H225_ArrayOf_AliasAddress &); virtual bool FindByAliases(LocationRequest &, H225_ArrayOf_AliasAddress &); }; bool ENUMPolicy::FindByAliases(RoutingRequest & request, H225_ArrayOf_AliasAddress & aliases) { #if P_DNS for (PINDEX i = 0; i < aliases.GetSize(); ++i) { PString alias(AsString(aliases[i], FALSE)); // 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(alias[j])) break; if (j >= alias.GetLength()) { PString str; if (PDNS::ENUMLookup(alias, "E2U+h323", str)) { if (str.Left(5) *= "h323:") str = str.Mid(5); PTRACE(4, "\tENUM converted remote party " << alias << " to " << str); request.SetFlag(RoutingRequest::e_aliasesChanged); H323SetAliasAddress(str, aliases[i]); } } } #endif return false; } bool ENUMPolicy::FindByAliases(LocationRequest & request, H225_ArrayOf_AliasAddress & aliases) { return ENUMPolicy::FindByAliases((RoutingRequest&)request, aliases); } namespace { // anonymous namespace SimpleCreator ExplicitPolicyCreator("explicit"); SimpleCreator InternalPolicyCreator("internal"); SimpleCreator ParentPolicyCreator("parent"); SimpleCreator DNSPolicyCreator("dns"); SimpleCreator VirtualQueuePolicyCreator("vqueue"); SimpleCreator NumberAnalysisPolicyCreator("numberanalysis"); SimpleCreator ENUMPolicyCreator("enum"); } } // end of namespace Routing