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.6 2004/08/04 10:26:39 csoutheren
26 * Changed service to be case insignificant
28 * Revision 1.5 2004/08/03 13:37:45 csoutheren
29 * Added ability to set ENUM search path from environment variable
31 * Revision 1.4 2004/07/19 13:55:41 csoutheren
32 * Work-around for crash on gcc 3.5-20040704
34 * Revision 1.3 2004/06/05 01:58:37 rjongbloed
35 * Fixed MSVC 6 compatibility
37 * Revision 1.2 2004/05/31 23:14:17 csoutheren
38 * Fixed warnings under VS.net and fixed problem with SRV records when returning multiple records
40 * Revision 1.1 2004/05/31 13:56:37 csoutheren
41 * Added implementation of ENUM resolution of E.164 numbers by DNS
46 #pragma implementation "enum.h"
50 #include <ptclib/pdns.h>
51 #include <ptclib/enum.h>
61 static const char * PWLIB_ENUM_PATH
= "PWLIB_ENUM_PATH";
63 ///////////////////////////////////////////////////////////////////////
65 PObject::Comparison
PDNS::NAPTRRecord::Compare(const PObject
& obj
) const
67 const NAPTRRecord
* other
= dynamic_cast<const NAPTRRecord
*>(&obj
);
72 if (order
< other
->order
)
74 else if (order
> other
->order
)
77 if (preference
< other
->preference
)
79 else if (preference
> other
->preference
)
85 void PDNS::NAPTRRecord::PrintOn(ostream
& strm
) const
87 strm
<< "order=" << order
<< ", "
88 << "preference=" << preference
<< ", "
89 << "flags=" << flags
<< ", "
90 << "service=" << service
<< ", "
91 << "regex=" << regex
<< ", "
92 << "replacement=" << replacement
;
95 ///////////////////////////////////////////////////////////////////////
103 char * GetFlagsBase() const { return (char *)&info
; }
104 int GetFlagsLen() const { return (int)GetFlagsBase()[0]; }
106 char * GetServiceBase() const { return GetFlagsBase() + 1 + GetFlagsLen(); }
107 int GetServiceLen() const { return (int)GetServiceBase()[0]; }
109 char * GetRegexBase() const { return GetServiceBase() + 1 + GetServiceLen(); }
110 int GetRegexLen() const { return (int)GetRegexBase()[0]; }
112 char * GetReplacementBase() const { return GetRegexBase() + 1 + GetRegexLen(); }
113 int GetReplacementLen() const { return (int)GetReplacementBase()[0]; }
115 PString
GetFlags() const { return PString(GetFlagsBase()+1, GetFlagsLen()); }
116 PString
GetService() const { return PString(GetServiceBase()+1, GetServiceLen()); }
117 PString
GetRegex() const { return PString(GetRegexBase()+1, GetRegexLen()); }
118 PString
GetReplacement() const { return PString(GetReplacementBase()+1, GetReplacementLen()); }
121 PDNS::NAPTRRecord
* PDNS::NAPTRRecordList::HandleDNSRecord(PDNS_RECORD dnsRecord
, PDNS_RECORD
/*results*/)
123 PDNS::NAPTRRecord
* record
= NULL
;
126 (dnsRecord
->Flags
.S
.Section
== DnsSectionAnswer
) &&
127 (dnsRecord
->wType
== DNS_TYPE_NAPTR
)
129 record
= new NAPTRRecord();
131 NAPTR_DNS
* naptr
= (NAPTR_DNS
*)&dnsRecord
->Data
;
133 record
->order
= naptr
->order
;
134 record
->preference
= naptr
->preference
;
135 record
->flags
= naptr
->GetFlags();
136 record
->service
= naptr
->GetService();
137 record
->regex
= naptr
->GetRegex();
138 record
->replacement
= naptr
->GetReplacement();
145 void PDNS::NAPTRRecordList::PrintOn(ostream
& strm
) const
148 for (i
= 0; i
< GetSize(); i
++)
149 strm
<< (*this)[i
] << endl
;
152 PDNS::NAPTRRecord
* PDNS::NAPTRRecordList::GetFirst(const char * service
)
158 lastOrder
= operator[](0).order
;
161 return GetNext(service
);
164 PDNS::NAPTRRecord
* PDNS::NAPTRRecordList::GetNext(const char * service
)
169 while (currentPos
< GetSize()) {
171 NAPTRRecord
& record
= operator[](currentPos
);
173 // once we have a match, we cannot look at higher order records
174 // and note that the list is already sorted by preference
175 if (orderLocked
&& lastOrder
!= record
.order
)
180 lastOrder
= record
.order
;
181 if (record
.order
== lastOrder
) {
182 if ((service
== NULL
) || (record
.service
*= service
)) {
193 static PString
ApplyRegex(const PString
& orig
, const PString
& regexStr
)
195 // must have at least 3 delimiters and two chars of text
196 if (regexStr
.GetLength() < 5) {
197 PTRACE(1, "ENUM regex is too short: " << regexStr
);
198 return PString::Empty();
201 // first char in the regex is always the delimiter
202 char delimiter
= regexStr
[0];
204 // break the string into match and replace strings by looking for non-escaped delimiters
209 for (pos
= 1; strNum
< 2 && pos
< regexStr
.GetLength(); pos
++) {
210 if (regexStr
[pos
] == '\\')
212 else if (regexStr
[pos
] == delimiter
) {
213 strings
[strNum
] = regexStr(start
, pos
-1);
220 // make sure we have some strings
221 // CRS: this construct avoids a gcc crash with gcc 3.5-20040704/
222 // when using the following:
223 // if (strings[0].IsEmpty() || strings[1].IsEmpty()) {
224 PString
& str1
= strings
[0];
225 PString
& str2
= strings
[1];
226 if (str1
.IsEmpty() || str2
.IsEmpty()) {
227 PTRACE(1, "ENUM regex does not parse into two string: " << regexStr
);
228 return PString::Empty();
233 if (strNum
== 2 && pos
< regexStr
.GetLength()-1) {
235 flags
= regexStr
.Mid(pos
+1).ToLower();
238 // construct the regular expression
239 PRegularExpression regex
;
240 int regexFlags
= PRegularExpression::Extended
;
241 if (flags
.Find('i') != P_MAX_INDEX
)
242 regexFlags
+= PRegularExpression::IgnoreCase
;
243 if (!regex
.Compile(strings
[0], regexFlags
)) {
244 PTRACE(1, "ENUM regex does not compile : " << regexStr
);
248 // apply the regular expression to the original string
249 PIntArray
starts(10), ends(10);
250 if (!regex
.Execute(orig
, starts
, ends
)) {
251 PTRACE(1, "ENUM regex does not execute : " << regexStr
);
255 // replace variables in the second string
256 PString value
= strings
[1];
257 for (pos
= 0; pos
< value
.GetLength(); pos
++) {
258 if (value
[pos
] == '\\' && pos
< value
.GetLength()-1) {
259 int var
= value
[pos
+1]-'1'+1;
261 if (var
>= 0 && var
< starts
.GetSize() && var
< ends
.GetSize())
262 str
= orig(starts
[var
], ends
[var
]);
263 value
= value
.Left(pos
) + str
+ value
.Mid(pos
+2);
271 BOOL
PDNS::ENUMLookup(
272 const PString
& e164
,
273 const PString
& service
,
277 static const char * defaultDomains
[] = { "e164.voxgratia.net", "e164.org", "e164.arpa" };
279 PStringArray domains
;
280 char * env
= ::getenv(PWLIB_ENUM_PATH
);
282 domains
+= PStringArray(sizeof(domains
)/sizeof(defaultDomains
[0]), defaultDomains
);
284 domains
+= PString(env
).Tokenise(PATH_SEP
);
286 return PDNS::ENUMLookup(e164
, service
, domains
, dn
);
289 static BOOL
InternalENUMLookup(const PString
& e164
, const PString
& service
, PDNS::NAPTRRecordList
& records
, PString
& returnStr
)
293 // get the first record that matches the service.
294 PDNS::NAPTRRecord
* rec
= records
.GetFirst(service
);
298 // if no more records that match this service, then fail
303 BOOL handled
= FALSE
;
304 BOOL terminal
= TRUE
;
306 for (PINDEX f
= 0; !handled
&& f
< rec
->flags
.GetLength(); ++f
) {
307 switch (tolower(rec
->flags
[f
])) {
321 // apply regex and do the lookup
323 returnStr
= ApplyRegex(e164
, rec
->regex
);
329 // handle in a protocol specific way - not supported
339 // if no flags were accepted, then unlock the order on the record and get the next record
341 records
.UnlockOrder();
342 rec
= records
.GetNext(service
);
346 // if this was a terminal lookup, finish now
355 BOOL
PDNS::ENUMLookup(
356 const PString
& _e164
,
357 const PString
& service
,
358 const PStringArray
& enumSpaces
,
362 PString e164
= _e164
;
365 e164
= PString('+') + e164
;
367 ////////////////////////////////////////////////////////
368 // convert to domain name as per RFC 2916
370 // remove all non-digits
372 while (pos
< e164
.GetLength()) {
373 if (isdigit(e164
[pos
]))
376 e164
= e164
.Left(pos
) + e164
.Mid(pos
+1);
379 // reverse the order of the digits, and add "." in between each digit
381 for (pos
= 1; pos
< e164
.GetLength(); pos
++) {
382 if (!domain
.IsEmpty())
383 domain
= PString('.') + domain
;
384 domain
= PString(e164
[pos
]) + domain
;
387 for (PINDEX i
= 0; i
< enumSpaces
.GetSize(); i
++) {
389 PDNS::NAPTRRecordList records
;
391 // do the initial lookup - if no answer then the lookup failed
392 if (!PDNS::GetRecords(domain
+ "." + enumSpaces
[i
], records
))
395 if (InternalENUMLookup(e164
, service
, records
, returnStr
))
404 // End of File ///////////////////////////////////////////////////////////////