/* * enum.cxx * * Portable Windows Library * * Copyright (C) 2004 Post Increment * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Portable Windows Library. * * The Initial Developer of the Original Code is Post Increment * * Contributor(s): ______________________________________. * * $Log: enum.cxx,v $ * Revision 1.9 2005/11/30 12:47:41 csoutheren * Removed tabs, reformatted some code, and changed tags for Doxygen * * Revision 1.8 2005/08/31 05:55:03 shorne * Reworked ENUM to craigs' exacting requirements * * Revision 1.7 2005/08/31 04:07:53 shorne * added ability to set ENUM Servers at runtime * * Revision 1.6 2004/08/04 10:26:39 csoutheren * Changed service to be case insignificant * * Revision 1.5 2004/08/03 13:37:45 csoutheren * Added ability to set ENUM search path from environment variable * * Revision 1.4 2004/07/19 13:55:41 csoutheren * Work-around for crash on gcc 3.5-20040704 * * Revision 1.3 2004/06/05 01:58:37 rjongbloed * Fixed MSVC 6 compatibility * * Revision 1.2 2004/05/31 23:14:17 csoutheren * Fixed warnings under VS.net and fixed problem with SRV records when returning multiple records * * Revision 1.1 2004/05/31 13:56:37 csoutheren * Added implementation of ENUM resolution of E.164 numbers by DNS * */ #ifdef __GNUC__ #pragma implementation "enum.h" #endif #include #include #include #if P_DNS #ifdef _WIN32 #define PATH_SEP ";" #else #define PATH_SEP ":" #endif static const char * PWLIB_ENUM_PATH = "PWLIB_ENUM_PATH"; /////////////////////////////////////////////////////////////////////// PObject::Comparison PDNS::NAPTRRecord::Compare(const PObject & obj) const { const NAPTRRecord * other = dynamic_cast(&obj); if (other == NULL) return LessThan; if (order < other->order) return LessThan; else if (order > other->order) return GreaterThan; if (preference < other->preference) return LessThan; else if (preference > other->preference) return GreaterThan; return EqualTo; } void PDNS::NAPTRRecord::PrintOn(ostream & strm) const { strm << "order=" << order << ", " << "preference=" << preference << ", " << "flags=" << flags << ", " << "service=" << service << ", " << "regex=" << regex << ", " << "replacement=" << replacement; } /////////////////////////////////////////////////////////////////////// struct NAPTR_DNS { PUInt16b order; PUInt16b preference; char info[1]; char * GetFlagsBase() const { return (char *)&info; } int GetFlagsLen() const { return (int)GetFlagsBase()[0]; } char * GetServiceBase() const { return GetFlagsBase() + 1 + GetFlagsLen(); } int GetServiceLen() const { return (int)GetServiceBase()[0]; } char * GetRegexBase() const { return GetServiceBase() + 1 + GetServiceLen(); } int GetRegexLen() const { return (int)GetRegexBase()[0]; } char * GetReplacementBase() const { return GetRegexBase() + 1 + GetRegexLen(); } int GetReplacementLen() const { return (int)GetReplacementBase()[0]; } PString GetFlags() const { return PString(GetFlagsBase()+1, GetFlagsLen()); } PString GetService() const { return PString(GetServiceBase()+1, GetServiceLen()); } PString GetRegex() const { return PString(GetRegexBase()+1, GetRegexLen()); } PString GetReplacement() const { return PString(GetReplacementBase()+1, GetReplacementLen()); } }; PDNS::NAPTRRecord * PDNS::NAPTRRecordList::HandleDNSRecord(PDNS_RECORD dnsRecord, PDNS_RECORD /*results*/) { PDNS::NAPTRRecord * record = NULL; if ( (dnsRecord->Flags.S.Section == DnsSectionAnswer) && (dnsRecord->wType == DNS_TYPE_NAPTR) ) { record = new NAPTRRecord(); NAPTR_DNS * naptr = (NAPTR_DNS *)&dnsRecord->Data; record->order = naptr->order; record->preference = naptr->preference; record->flags = naptr->GetFlags(); record->service = naptr->GetService(); record->regex = naptr->GetRegex(); record->replacement = naptr->GetReplacement(); } return record; } void PDNS::NAPTRRecordList::PrintOn(ostream & strm) const { PINDEX i; for (i = 0; i < GetSize(); i++) strm << (*this)[i] << endl; } PDNS::NAPTRRecord * PDNS::NAPTRRecordList::GetFirst(const char * service) { if (GetSize() == 0) return NULL; currentPos = 0; lastOrder = operator[](0).order; orderLocked = FALSE; return GetNext(service); } PDNS::NAPTRRecord * PDNS::NAPTRRecordList::GetNext(const char * service) { if (GetSize() == 0) return NULL; while (currentPos < GetSize()) { NAPTRRecord & record = operator[](currentPos); // once we have a match, we cannot look at higher order records // and note that the list is already sorted by preference if (orderLocked && lastOrder != record.order) return NULL; else { currentPos++; lastOrder = record.order; if (record.order == lastOrder) { if ((service == NULL) || (record.service *= service)) { orderLocked = TRUE; return &record; } } } } return NULL; } static PString ApplyRegex(const PString & orig, const PString & regexStr) { // must have at least 3 delimiters and two chars of text if (regexStr.GetLength() < 5) { PTRACE(1, "ENUM regex is too short: " << regexStr); return PString::Empty(); } // first char in the regex is always the delimiter char delimiter = regexStr[0]; // break the string into match and replace strings by looking for non-escaped delimiters PString strings[2]; PINDEX strNum = 0; PINDEX pos = 1; PINDEX start = pos; for (pos = 1; strNum < 2 && pos < regexStr.GetLength(); pos++) { if (regexStr[pos] == '\\') pos++; else if (regexStr[pos] == delimiter) { strings[strNum] = regexStr(start, pos-1); strNum++; pos++; start = pos; } } // make sure we have some strings // CRS: this construct avoids a gcc crash with gcc 3.5-20040704/ // when using the following: // if (strings[0].IsEmpty() || strings[1].IsEmpty()) { PString & str1 = strings[0]; PString & str2 = strings[1]; if (str1.IsEmpty() || str2.IsEmpty()) { PTRACE(1, "ENUM regex does not parse into two string: " << regexStr); return PString::Empty(); } // get the flags PString flags; if (strNum == 2 && pos < regexStr.GetLength()-1) { pos++; flags = regexStr.Mid(pos+1).ToLower(); } // construct the regular expression PRegularExpression regex; int regexFlags = PRegularExpression::Extended; if (flags.Find('i') != P_MAX_INDEX) regexFlags += PRegularExpression::IgnoreCase; if (!regex.Compile(strings[0], regexFlags)) { PTRACE(1, "ENUM regex does not compile : " << regexStr); return PString(); } // apply the regular expression to the original string PIntArray starts(10), ends(10); if (!regex.Execute(orig, starts, ends)) { PTRACE(1, "ENUM regex does not execute : " << regexStr); return PString(); } // replace variables in the second string PString value = strings[1]; for (pos = 0; pos < value.GetLength(); pos++) { if (value[pos] == '\\' && pos < value.GetLength()-1) { int var = value[pos+1]-'1'+1; PString str; if (var >= 0 && var < starts.GetSize() && var < ends.GetSize()) str = orig(starts[var], ends[var]); value = value.Left(pos) + str + value.Mid(pos+2); } } return value; } static PStringArray & GetENUMServers() { static const char * defaultDomains[] = { "e164.voxgratia.net","e164.org","e164.arpa"}; static PStringArray servers( sizeof(defaultDomains)/sizeof(defaultDomains[0]), defaultDomains ); return servers; } static PMutex & GetENUMServerMutex() { static PMutex mutex; return mutex; } void PDNS::SetENUMServers(const PStringArray & servers) { PWaitAndSignal m(GetENUMServerMutex()); GetENUMServers() = servers; } BOOL PDNS::ENUMLookup(const PString & e164, const PString & service,PString & dn) { PWaitAndSignal m(GetENUMServerMutex()); PStringArray domains; char * env = ::getenv(PWLIB_ENUM_PATH); if (env == NULL) domains += GetENUMServers(); else domains += PString(env).Tokenise(PATH_SEP); return PDNS::ENUMLookup(e164, service, domains, dn); } static BOOL InternalENUMLookup(const PString & e164, const PString & service, PDNS::NAPTRRecordList & records, PString & returnStr) { BOOL result = FALSE; // get the first record that matches the service. PDNS::NAPTRRecord * rec = records.GetFirst(service); do { // if no more records that match this service, then fail if (rec == NULL) break; // process the flags BOOL handled = FALSE; BOOL terminal = TRUE; for (PINDEX f = 0; !handled && f < rec->flags.GetLength(); ++f) { switch (tolower(rec->flags[f])) { // do an SRV lookup case 's': terminal = TRUE; handled = FALSE; break; // do an A lookup case 'a': terminal = TRUE; handled = FALSE; break; // apply regex and do the lookup case 'u': returnStr = ApplyRegex(e164, rec->regex); result = TRUE; terminal = TRUE; handled = TRUE; break; // handle in a protocol specific way - not supported case 'p': handled = FALSE; break; default: handled = FALSE; } } // if no flags were accepted, then unlock the order on the record and get the next record if (!handled) { records.UnlockOrder(); rec = records.GetNext(service); continue; } // if this was a terminal lookup, finish now if (terminal) break; } while (!result); return result; } BOOL PDNS::ENUMLookup( const PString & _e164, const PString & service, const PStringArray & enumSpaces, PString & returnStr ) { PString e164 = _e164; if (e164[0] != '+') e164 = PString('+') + e164; //////////////////////////////////////////////////////// // convert to domain name as per RFC 2916 // remove all non-digits PINDEX pos = 1; while (pos < e164.GetLength()) { if (isdigit(e164[pos])) pos++; else e164 = e164.Left(pos) + e164.Mid(pos+1); } // reverse the order of the digits, and add "." in between each digit PString domain; for (pos = 1; pos < e164.GetLength(); pos++) { if (!domain.IsEmpty()) domain = PString('.') + domain; domain = PString(e164[pos]) + domain; } for (PINDEX i = 0; i < enumSpaces.GetSize(); i++) { PDNS::NAPTRRecordList records; // do the initial lookup - if no answer then the lookup failed if (!PDNS::GetRecords(domain + "." + enumSpaces[i], records)) continue; if (InternalENUMLookup(e164, service, records, returnStr)) return TRUE; } return FALSE; } #endif // End of File ///////////////////////////////////////////////////////////////