Added IsSupportingRTP function to simplify detecting when STUN supports RTP
[pwlib.git] / src / ptclib / enum.cxx
blob9824cea8adeda457221eff04a9b7ec2765ca49e4
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.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
45 #ifdef __GNUC__
46 #pragma implementation "enum.h"
47 #endif
49 #include <ptlib.h>
50 #include <ptclib/pdns.h>
51 #include <ptclib/enum.h>
53 #if P_DNS
55 #ifdef _WIN32
56 #define PATH_SEP ";"
57 #else
58 #define PATH_SEP ":"
59 #endif
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);
69 if (other == NULL)
70 return LessThan;
72 if (order < other->order)
73 return LessThan;
74 else if (order > other->order)
75 return GreaterThan;
77 if (preference < other->preference)
78 return LessThan;
79 else if (preference > other->preference)
80 return GreaterThan;
82 return EqualTo;
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 ///////////////////////////////////////////////////////////////////////
97 struct NAPTR_DNS {
98 PUInt16b order;
99 PUInt16b preference;
101 char info[1];
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;
125 if (
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();
141 return record;
145 void PDNS::NAPTRRecordList::PrintOn(ostream & strm) const
147 PINDEX i;
148 for (i = 0; i < GetSize(); i++)
149 strm << (*this)[i] << endl;
152 PDNS::NAPTRRecord * PDNS::NAPTRRecordList::GetFirst(const char * service)
154 if (GetSize() == 0)
155 return NULL;
157 currentPos = 0;
158 lastOrder = operator[](0).order;
159 orderLocked = FALSE;
161 return GetNext(service);
164 PDNS::NAPTRRecord * PDNS::NAPTRRecordList::GetNext(const char * service)
166 if (GetSize() == 0)
167 return NULL;
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)
176 return NULL;
178 else {
179 currentPos++;
180 lastOrder = record.order;
181 if (record.order == lastOrder) {
182 if ((service == NULL) || (record.service *= service)) {
183 orderLocked = TRUE;
184 return &record;
190 return NULL;
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
205 PString strings[2];
206 PINDEX strNum = 0;
207 PINDEX pos = 1;
208 PINDEX start = pos;
209 for (pos = 1; strNum < 2 && pos < regexStr.GetLength(); pos++) {
210 if (regexStr[pos] == '\\')
211 pos++;
212 else if (regexStr[pos] == delimiter) {
213 strings[strNum] = regexStr(start, pos-1);
214 strNum++;
215 pos++;
216 start = pos;
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();
231 // get the flags
232 PString flags;
233 if (strNum == 2 && pos < regexStr.GetLength()-1) {
234 pos++;
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);
245 return PString();
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);
252 return PString();
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;
260 PString str;
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);
267 return value;
271 BOOL PDNS::ENUMLookup(
272 const PString & e164,
273 const PString & service,
274 PString & dn
277 static const char * defaultDomains[] = { "e164.voxgratia.net", "e164.org", "e164.arpa" };
279 PStringArray domains;
280 char * env = ::getenv(PWLIB_ENUM_PATH);
281 if (env == NULL)
282 domains += PStringArray(sizeof(domains)/sizeof(defaultDomains[0]), defaultDomains);
283 else
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)
291 BOOL result = FALSE;
293 // get the first record that matches the service.
294 PDNS::NAPTRRecord * rec = records.GetFirst(service);
296 do {
298 // if no more records that match this service, then fail
299 if (rec == NULL)
300 break;
302 // process the flags
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])) {
309 // do an SRV lookup
310 case 's':
311 terminal = TRUE;
312 handled = FALSE;
313 break;
315 // do an A lookup
316 case 'a':
317 terminal = TRUE;
318 handled = FALSE;
319 break;
321 // apply regex and do the lookup
322 case 'u':
323 returnStr = ApplyRegex(e164, rec->regex);
324 result = TRUE;
325 terminal = TRUE;
326 handled = TRUE;
327 break;
329 // handle in a protocol specific way - not supported
330 case 'p':
331 handled = FALSE;
332 break;
334 default:
335 handled = FALSE;
339 // if no flags were accepted, then unlock the order on the record and get the next record
340 if (!handled) {
341 records.UnlockOrder();
342 rec = records.GetNext(service);
343 continue;
346 // if this was a terminal lookup, finish now
347 if (terminal)
348 break;
350 } while (!result);
352 return result;
355 BOOL PDNS::ENUMLookup(
356 const PString & _e164,
357 const PString & service,
358 const PStringArray & enumSpaces,
359 PString & returnStr
362 PString e164 = _e164;
364 if (e164[0] != '+')
365 e164 = PString('+') + e164;
367 ////////////////////////////////////////////////////////
368 // convert to domain name as per RFC 2916
370 // remove all non-digits
371 PINDEX pos = 1;
372 while (pos < e164.GetLength()) {
373 if (isdigit(e164[pos]))
374 pos++;
375 else
376 e164 = e164.Left(pos) + e164.Mid(pos+1);
379 // reverse the order of the digits, and add "." in between each digit
380 PString domain;
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))
393 continue;
395 if (InternalENUMLookup(e164, service, records, returnStr))
396 return TRUE;
399 return FALSE;
402 #endif
404 // End of File ///////////////////////////////////////////////////////////////