Changed output and intermediate directories, tuned up compile parameters for Windows...
[pwlib.git] / src / ptclib / enum.cxx
blob132a166e61e26053ab3e3539c2435b20e3f8814d
1 /*
2 * enum.cxx
4 * Portable Windows Library
6 * Copyright (C) 2004 Post Increment
8 * The contents of this file are subject to the Mozilla Public License
9 * Version 1.0 (the "License"); you may not use this file except in
10 * compliance with the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
13 * Software distributed under the License is distributed on an "AS IS"
14 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
15 * the License for the specific language governing rights and limitations
16 * under the License.
18 * The Original Code is Portable Windows Library.
20 * The Initial Developer of the Original Code is Post Increment
22 * Contributor(s): ______________________________________.
24 * $Log$
25 * Revision 1.10 2007/04/02 05:29:54 rjongbloed
26 * Tidied some trace logs to assure all have a category (bit before a tab character) set.
28 * Revision 1.9 2005/11/30 12:47:41 csoutheren
29 * Removed tabs, reformatted some code, and changed tags for Doxygen
31 * Revision 1.8 2005/08/31 05:55:03 shorne
32 * Reworked ENUM to craigs' exacting requirements
34 * Revision 1.7 2005/08/31 04:07:53 shorne
35 * added ability to set ENUM Servers at runtime
37 * Revision 1.6 2004/08/04 10:26:39 csoutheren
38 * Changed service to be case insignificant
40 * Revision 1.5 2004/08/03 13:37:45 csoutheren
41 * Added ability to set ENUM search path from environment variable
43 * Revision 1.4 2004/07/19 13:55:41 csoutheren
44 * Work-around for crash on gcc 3.5-20040704
46 * Revision 1.3 2004/06/05 01:58:37 rjongbloed
47 * Fixed MSVC 6 compatibility
49 * Revision 1.2 2004/05/31 23:14:17 csoutheren
50 * Fixed warnings under VS.net and fixed problem with SRV records when returning multiple records
52 * Revision 1.1 2004/05/31 13:56:37 csoutheren
53 * Added implementation of ENUM resolution of E.164 numbers by DNS
57 #ifdef __GNUC__
58 #pragma implementation "enum.h"
59 #endif
61 #include <ptlib.h>
62 #include <ptclib/pdns.h>
63 #include <ptclib/enum.h>
65 #if P_DNS
67 #ifdef _WIN32
68 #define PATH_SEP ";"
69 #else
70 #define PATH_SEP ":"
71 #endif
73 static const char * PWLIB_ENUM_PATH = "PWLIB_ENUM_PATH";
75 ///////////////////////////////////////////////////////////////////////
77 PObject::Comparison PDNS::NAPTRRecord::Compare(const PObject & obj) const
79 const NAPTRRecord * other = dynamic_cast<const NAPTRRecord *>(&obj);
81 if (other == NULL)
82 return LessThan;
84 if (order < other->order)
85 return LessThan;
86 else if (order > other->order)
87 return GreaterThan;
89 if (preference < other->preference)
90 return LessThan;
91 else if (preference > other->preference)
92 return GreaterThan;
94 return EqualTo;
97 void PDNS::NAPTRRecord::PrintOn(ostream & strm) const
99 strm << "order=" << order << ", "
100 << "preference=" << preference << ", "
101 << "flags=" << flags << ", "
102 << "service=" << service << ", "
103 << "regex=" << regex << ", "
104 << "replacement=" << replacement;
107 ///////////////////////////////////////////////////////////////////////
109 struct NAPTR_DNS {
110 PUInt16b order;
111 PUInt16b preference;
113 char info[1];
115 char * GetFlagsBase() const { return (char *)&info; }
116 int GetFlagsLen() const { return (int)GetFlagsBase()[0]; }
118 char * GetServiceBase() const { return GetFlagsBase() + 1 + GetFlagsLen(); }
119 int GetServiceLen() const { return (int)GetServiceBase()[0]; }
121 char * GetRegexBase() const { return GetServiceBase() + 1 + GetServiceLen(); }
122 int GetRegexLen() const { return (int)GetRegexBase()[0]; }
124 char * GetReplacementBase() const { return GetRegexBase() + 1 + GetRegexLen(); }
125 int GetReplacementLen() const { return (int)GetReplacementBase()[0]; }
127 PString GetFlags() const { return PString(GetFlagsBase()+1, GetFlagsLen()); }
128 PString GetService() const { return PString(GetServiceBase()+1, GetServiceLen()); }
129 PString GetRegex() const { return PString(GetRegexBase()+1, GetRegexLen()); }
130 PString GetReplacement() const { return PString(GetReplacementBase()+1, GetReplacementLen()); }
133 PDNS::NAPTRRecord * PDNS::NAPTRRecordList::HandleDNSRecord(PDNS_RECORD dnsRecord, PDNS_RECORD /*results*/)
135 PDNS::NAPTRRecord * record = NULL;
137 if (
138 (dnsRecord->Flags.S.Section == DnsSectionAnswer) &&
139 (dnsRecord->wType == DNS_TYPE_NAPTR)
141 record = new NAPTRRecord();
143 NAPTR_DNS * naptr = (NAPTR_DNS *)&dnsRecord->Data;
145 record->order = naptr->order;
146 record->preference = naptr->preference;
147 record->flags = naptr->GetFlags();
148 record->service = naptr->GetService();
149 record->regex = naptr->GetRegex();
150 record->replacement = naptr->GetReplacement();
153 return record;
157 void PDNS::NAPTRRecordList::PrintOn(ostream & strm) const
159 PINDEX i;
160 for (i = 0; i < GetSize(); i++)
161 strm << (*this)[i] << endl;
164 PDNS::NAPTRRecord * PDNS::NAPTRRecordList::GetFirst(const char * service)
166 if (GetSize() == 0)
167 return NULL;
169 currentPos = 0;
170 lastOrder = operator[](0).order;
171 orderLocked = FALSE;
173 return GetNext(service);
176 PDNS::NAPTRRecord * PDNS::NAPTRRecordList::GetNext(const char * service)
178 if (GetSize() == 0)
179 return NULL;
181 while (currentPos < GetSize()) {
183 NAPTRRecord & record = operator[](currentPos);
185 // once we have a match, we cannot look at higher order records
186 // and note that the list is already sorted by preference
187 if (orderLocked && lastOrder != record.order)
188 return NULL;
190 else {
191 currentPos++;
192 lastOrder = record.order;
193 if (record.order == lastOrder) {
194 if ((service == NULL) || (record.service *= service)) {
195 orderLocked = TRUE;
196 return &record;
202 return NULL;
205 static PString ApplyRegex(const PString & orig, const PString & regexStr)
207 // must have at least 3 delimiters and two chars of text
208 if (regexStr.GetLength() < 5) {
209 PTRACE(1, "ENUM\tregex is too short: " << regexStr);
210 return PString::Empty();
213 // first char in the regex is always the delimiter
214 char delimiter = regexStr[0];
216 // break the string into match and replace strings by looking for non-escaped delimiters
217 PString strings[2];
218 PINDEX strNum = 0;
219 PINDEX pos = 1;
220 PINDEX start = pos;
221 for (pos = 1; strNum < 2 && pos < regexStr.GetLength(); pos++) {
222 if (regexStr[pos] == '\\')
223 pos++;
224 else if (regexStr[pos] == delimiter) {
225 strings[strNum] = regexStr(start, pos-1);
226 strNum++;
227 pos++;
228 start = pos;
232 // make sure we have some strings
233 // CRS: this construct avoids a gcc crash with gcc 3.5-20040704/
234 // when using the following:
235 // if (strings[0].IsEmpty() || strings[1].IsEmpty()) {
236 PString & str1 = strings[0];
237 PString & str2 = strings[1];
238 if (str1.IsEmpty() || str2.IsEmpty()) {
239 PTRACE(1, "ENUM\tregex does not parse into two string: " << regexStr);
240 return PString::Empty();
243 // get the flags
244 PString flags;
245 if (strNum == 2 && pos < regexStr.GetLength()-1) {
246 pos++;
247 flags = regexStr.Mid(pos+1).ToLower();
250 // construct the regular expression
251 PRegularExpression regex;
252 int regexFlags = PRegularExpression::Extended;
253 if (flags.Find('i') != P_MAX_INDEX)
254 regexFlags += PRegularExpression::IgnoreCase;
255 if (!regex.Compile(strings[0], regexFlags)) {
256 PTRACE(1, "ENUM\tregex does not compile : " << regexStr);
257 return PString();
260 // apply the regular expression to the original string
261 PIntArray starts(10), ends(10);
262 if (!regex.Execute(orig, starts, ends)) {
263 PTRACE(1, "ENUM\tregex does not execute : " << regexStr);
264 return PString();
267 // replace variables in the second string
268 PString value = strings[1];
269 for (pos = 0; pos < value.GetLength(); pos++) {
270 if (value[pos] == '\\' && pos < value.GetLength()-1) {
271 int var = value[pos+1]-'1'+1;
272 PString str;
273 if (var >= 0 && var < starts.GetSize() && var < ends.GetSize())
274 str = orig(starts[var], ends[var]);
275 value = value.Left(pos) + str + value.Mid(pos+2);
279 return value;
282 static PStringArray & GetENUMServers()
284 static const char * defaultDomains[] = { "e164.voxgratia.net","e164.org","e164.arpa"};
285 static PStringArray servers(
286 sizeof(defaultDomains)/sizeof(defaultDomains[0]),
287 defaultDomains
289 return servers;
292 static PMutex & GetENUMServerMutex()
294 static PMutex mutex;
295 return mutex;
298 void PDNS::SetENUMServers(const PStringArray & servers)
300 PWaitAndSignal m(GetENUMServerMutex());
301 GetENUMServers() = servers;
304 BOOL PDNS::ENUMLookup(const PString & e164,
305 const PString & service,PString & dn)
307 PWaitAndSignal m(GetENUMServerMutex());
308 PStringArray domains;
309 char * env = ::getenv(PWLIB_ENUM_PATH);
310 if (env == NULL)
311 domains += GetENUMServers();
312 else
313 domains += PString(env).Tokenise(PATH_SEP);
315 return PDNS::ENUMLookup(e164, service, domains, dn);
318 static BOOL InternalENUMLookup(const PString & e164, const PString & service, PDNS::NAPTRRecordList & records, PString & returnStr)
320 BOOL result = FALSE;
322 // get the first record that matches the service.
323 PDNS::NAPTRRecord * rec = records.GetFirst(service);
325 do {
327 // if no more records that match this service, then fail
328 if (rec == NULL)
329 break;
331 // process the flags
332 BOOL handled = FALSE;
333 BOOL terminal = TRUE;
335 for (PINDEX f = 0; !handled && f < rec->flags.GetLength(); ++f) {
336 switch (tolower(rec->flags[f])) {
338 // do an SRV lookup
339 case 's':
340 terminal = TRUE;
341 handled = FALSE;
342 break;
344 // do an A lookup
345 case 'a':
346 terminal = TRUE;
347 handled = FALSE;
348 break;
350 // apply regex and do the lookup
351 case 'u':
352 returnStr = ApplyRegex(e164, rec->regex);
353 result = TRUE;
354 terminal = TRUE;
355 handled = TRUE;
356 break;
358 // handle in a protocol specific way - not supported
359 case 'p':
360 handled = FALSE;
361 break;
363 default:
364 handled = FALSE;
368 // if no flags were accepted, then unlock the order on the record and get the next record
369 if (!handled) {
370 records.UnlockOrder();
371 rec = records.GetNext(service);
372 continue;
375 // if this was a terminal lookup, finish now
376 if (terminal)
377 break;
379 } while (!result);
381 return result;
384 BOOL PDNS::ENUMLookup(
385 const PString & _e164,
386 const PString & service,
387 const PStringArray & enumSpaces,
388 PString & returnStr
391 PString e164 = _e164;
393 if (e164[0] != '+')
394 e164 = PString('+') + e164;
396 ////////////////////////////////////////////////////////
397 // convert to domain name as per RFC 2916
399 // remove all non-digits
400 PINDEX pos = 1;
401 while (pos < e164.GetLength()) {
402 if (isdigit(e164[pos]))
403 pos++;
404 else
405 e164 = e164.Left(pos) + e164.Mid(pos+1);
408 // reverse the order of the digits, and add "." in between each digit
409 PString domain;
410 for (pos = 1; pos < e164.GetLength(); pos++) {
411 if (!domain.IsEmpty())
412 domain = PString('.') + domain;
413 domain = PString(e164[pos]) + domain;
416 for (PINDEX i = 0; i < enumSpaces.GetSize(); i++) {
418 PDNS::NAPTRRecordList records;
420 // do the initial lookup - if no answer then the lookup failed
421 if (!PDNS::GetRecords(domain + "." + enumSpaces[i], records))
422 continue;
424 if (InternalENUMLookup(e164, service, records, returnStr))
425 return TRUE;
428 return FALSE;
431 #endif
433 // End of File ///////////////////////////////////////////////////////////////