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
18 * The Original Code is Portable Windows Library.
20 * The Initial Developer of the Original Code is Post Increment
22 * Contributor(s): ______________________________________.
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
58 #pragma implementation "enum.h"
62 #include <ptclib/pdns.h>
63 #include <ptclib/enum.h>
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
);
84 if (order
< other
->order
)
86 else if (order
> other
->order
)
89 if (preference
< other
->preference
)
91 else if (preference
> other
->preference
)
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 ///////////////////////////////////////////////////////////////////////
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
;
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();
157 void PDNS::NAPTRRecordList::PrintOn(ostream
& strm
) const
160 for (i
= 0; i
< GetSize(); i
++)
161 strm
<< (*this)[i
] << endl
;
164 PDNS::NAPTRRecord
* PDNS::NAPTRRecordList::GetFirst(const char * service
)
170 lastOrder
= operator[](0).order
;
173 return GetNext(service
);
176 PDNS::NAPTRRecord
* PDNS::NAPTRRecordList::GetNext(const char * service
)
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
)
192 lastOrder
= record
.order
;
193 if (record
.order
== lastOrder
) {
194 if ((service
== NULL
) || (record
.service
*= service
)) {
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
221 for (pos
= 1; strNum
< 2 && pos
< regexStr
.GetLength(); pos
++) {
222 if (regexStr
[pos
] == '\\')
224 else if (regexStr
[pos
] == delimiter
) {
225 strings
[strNum
] = regexStr(start
, pos
-1);
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();
245 if (strNum
== 2 && pos
< regexStr
.GetLength()-1) {
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
);
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
);
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;
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);
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]),
292 static PMutex
& GetENUMServerMutex()
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
);
311 domains
+= GetENUMServers();
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
)
322 // get the first record that matches the service.
323 PDNS::NAPTRRecord
* rec
= records
.GetFirst(service
);
327 // if no more records that match this service, then fail
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
])) {
350 // apply regex and do the lookup
352 returnStr
= ApplyRegex(e164
, rec
->regex
);
358 // handle in a protocol specific way - not supported
368 // if no flags were accepted, then unlock the order on the record and get the next record
370 records
.UnlockOrder();
371 rec
= records
.GetNext(service
);
375 // if this was a terminal lookup, finish now
384 BOOL
PDNS::ENUMLookup(
385 const PString
& _e164
,
386 const PString
& service
,
387 const PStringArray
& enumSpaces
,
391 PString e164
= _e164
;
394 e164
= PString('+') + e164
;
396 ////////////////////////////////////////////////////////
397 // convert to domain name as per RFC 2916
399 // remove all non-digits
401 while (pos
< e164
.GetLength()) {
402 if (isdigit(e164
[pos
]))
405 e164
= e164
.Left(pos
) + e164
.Mid(pos
+1);
408 // reverse the order of the digits, and add "." in between each digit
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
))
424 if (InternalENUMLookup(e164
, service
, records
, returnStr
))
433 // End of File ///////////////////////////////////////////////////////////////