1 /*
2  * pdns.cxx
3  *
4  * Portable Windows Library
5  *
6  * Copyright (c) 2003 Equivalence Pty. Ltd.
7  *
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/
12  *
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.
17  *
18  * The Original Code is Portable Windows Library.
19  *
20  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
21  *
22  * Contributor(s): ______________________________________.
23  *
24  * Copyright 2003 Equivalence Pty. Ltd.
25  *
26  * $Revision: 28568 $
27  * $Author: rjongbloed $
28  * $Date: 2012-11-22 00:16:50 -0600 (Thu, 22 Nov 2012) $
29  */
30 
31 #ifdef __GNUC__
32 #pragma implementation "pdns.h"
33 #endif
34 
35 #include <ptlib.h>
36 #include <ptclib/pdns.h>
37 #include <ptclib/url.h>
38 #include <ptlib/ipsock.h>
39 
40 #define new PNEW
41 
42 #define RESOLVER_CACHE_TIMEOUT  30000
43 
44 #if P_DNS
45 
46 #ifdef _WIN32
47   #pragma comment(lib, "DnsAPI.Lib")
48   #pragma message("DNS support enabled")
49 #endif
50 
51 
52 /////////////////////////////////////////////////
53 
GetDNSMutex()54 static PMutex & GetDNSMutex()
55 {
56   static PMutex mutex;
57   return mutex;
58 }
59 
60 
61 struct DNSCacheInfo {
DNSCacheInfoDNSCacheInfo62   DNSCacheInfo() : m_results(NULL), m_status(-1) { }
63   PTime         m_time;
64   PDNS_RECORD   m_results;
65   DNS_STATUS    m_status;
66 };
67 
68 typedef std::map<std::string, DNSCacheInfo> DNSCache;
69 
70 static PTime g_lastAgeTime(0);
71 static DNSCache g_dnsCache;
72 
73 
74 
75 #ifdef P_HAS_RESOLVER
76 
GetDN(const BYTE * reply,const BYTE * replyEnd,BYTE * & cp,char * buff)77 static PBoolean GetDN(const BYTE * reply, const BYTE * replyEnd, BYTE * & cp, char * buff)
78 {
79   int len = dn_expand(reply, replyEnd, cp, buff, MAXDNAME);
80   if (len < 0)
81     return PFalse;
82   cp += len;
83   return PTrue;
84 }
85 
ProcessDNSRecords(const BYTE * reply,const BYTE * replyEnd,BYTE * cp,PINDEX anCount,PINDEX nsCount,PINDEX arCount,PDNS_RECORD * results)86 static PBoolean ProcessDNSRecords(
87         const BYTE * reply,
88         const BYTE * replyEnd,
89               BYTE * cp,
90             PINDEX anCount,
91             PINDEX nsCount,
92             PINDEX arCount,
93      PDNS_RECORD * results)
94 {
95   PDNS_RECORD lastRecord = NULL;
96 
97   PINDEX rrCount = anCount + nsCount + arCount;
98   nsCount += anCount;
99   arCount += nsCount;
100 
101   PINDEX i;
102   for (i = 0; i < rrCount; i++) {
103 
104     int section;
105     if (i < anCount)
106       section = DnsSectionAnswer;
107     else if (i < nsCount)
108       section = DnsSectionAuthority;
109     else // if (i < arCount)
110       section = DnsSectionAdditional;
111 
112     // get the name
113     char pName[MAXDNAME];
114     if (!GetDN(reply, replyEnd, cp, pName))
115       return PFalse;
116 
117     // get other common parts of the record
118     WORD  type;
119     //WORD  dnsClass;
120     //DWORD ttl;
121     WORD  dlen;
122 
123     GETSHORT(type, cp);
124     cp += 2; // GETSHORT(dnsClass, cp);
125     cp += 4; // GETLONG (ttl,      cp);
126     GETSHORT(dlen, cp);
127 
128     BYTE * data = cp;
129     cp += dlen;
130 
131     PDNS_RECORD newRecord  = NULL;
132 
133     switch (type) {
134       default:
135         newRecord = (PDNS_RECORD)malloc(sizeof(DnsRecord) + sizeof(DWORD) + dlen);
136         newRecord->Data.Null.dwByteCount = dlen;
137         memcpy(&newRecord->Data, data, dlen);
138         break;
139 
140       case T_SRV:
141         newRecord = (PDNS_RECORD)malloc(sizeof(DnsRecord));
142         memset(newRecord, 0, sizeof(DnsRecord));
143         GETSHORT(newRecord->Data.SRV.wPriority, data);
144         GETSHORT(newRecord->Data.SRV.wWeight, data);
145         GETSHORT(newRecord->Data.SRV.wPort, data);
146         if (!GetDN(reply, replyEnd, data, newRecord->Data.SRV.pNameTarget)) {
147           free(newRecord);
148           return PFalse;
149         }
150         break;
151 
152       case T_MX:
153         newRecord = (PDNS_RECORD)malloc(sizeof(DnsRecord));
154         memset(newRecord, 0, sizeof(DnsRecord));
155         GETSHORT(newRecord->Data.MX.wPreference,  data);
156         if (!GetDN(reply, replyEnd, data, newRecord->Data.MX.pNameExchange)) {
157           free(newRecord);
158           return PFalse;
159         }
160         break;
161 
162       case T_A:
163         newRecord = (PDNS_RECORD)malloc(sizeof(DnsRecord));
164         memset(newRecord, 0, sizeof(DnsRecord));
165         GETLONG(newRecord->Data.A.IpAddress, data);
166         break;
167 
168       case T_AAAA:
169         newRecord = (PDNS_RECORD)malloc(sizeof(DnsRecord));
170         memset(newRecord, 0, sizeof(DnsRecord));
171         GETLONG(newRecord->Data.AAAA.Ip6Address[0], data);
172         GETLONG(newRecord->Data.AAAA.Ip6Address[1], data);
173         GETLONG(newRecord->Data.AAAA.Ip6Address[2], data);
174         GETLONG(newRecord->Data.AAAA.Ip6Address[3], data);
175         break;
176 
177       case T_NS:
178         newRecord = (PDNS_RECORD)malloc(sizeof(DnsRecord));
179         memset(newRecord, 0, sizeof(DnsRecord));
180         if (!GetDN(reply, replyEnd, data, newRecord->Data.NS.pNameHost)) {
181           delete newRecord;
182           return PFalse;
183         }
184         break;
185     }
186 
187     // initialise the new record
188     if (newRecord != NULL) {
189       newRecord->wType = type;
190       newRecord->Flags.S.Section = section;
191       newRecord->pNext = NULL;
192       strcpy(newRecord->pName, pName);
193 
194       if (*results == NULL)
195         *results = newRecord;
196 
197       if (lastRecord != NULL)
198         lastRecord->pNext = newRecord;
199 
200       lastRecord = newRecord;
201       newRecord = NULL;
202     }
203   }
204 
205   return PTrue;
206 }
207 
DnsQuery_A(const char * service,WORD requestType,DWORD options,void *,PDNS_RECORD * results,void *)208 DNS_STATUS DnsQuery_A(const char * service,
209                               WORD requestType,
210                              DWORD options,
211                             void *,
212                      PDNS_RECORD * results,
213                             void *)
214 {
215 #if defined(P_NETBSD)
216   struct __res_state myRes;
217 #endif
218   if (results == NULL)
219     return -1;
220 
221   *results = NULL;
222 
223 #if P_HAS_RES_NINIT
224 #if defined(P_NETBSD)
225   res_ninit(&myRes);
226 #else
227   res_ninit(&_res);
228 #endif
229 #else
230   res_init();
231   GetDNSMutex().Wait();
232 #endif
233 
234   union {
235     HEADER hdr;
236     BYTE buf[PACKETSZ];
237   } reply;
238 
239 #if P_HAS_RES_NINIT
240   int replyLen = res_nsearch(
241 #if defined(P_NETBSD)
242       &myRes,
243 #else
244       &_res,
245 #endif
246       service, C_IN, requestType, (BYTE *)&reply, sizeof(reply));
247 #else
248   int replyLen = res_search(service, C_IN, requestType, (BYTE *)&reply, sizeof(reply));
249   GetDNSMutex().Signal();
250 #endif
251 
252   if (replyLen < 1)
253     return -1;
254 
255   BYTE * replyStart = reply.buf;
256   BYTE * replyEnd   = reply.buf + replyLen;
257   BYTE * cp         = reply.buf + sizeof(HEADER);
258 
259   // ignore questions in response
260   uint16_t i;
261   for (i = 0; i < ntohs(reply.hdr.qdcount); i++) {
262     char qName[MAXDNAME];
263     if (!GetDN(replyStart, replyEnd, cp, qName))
264       return -1;
265     cp += QFIXEDSZ;
266   }
267 
268   if (!ProcessDNSRecords(
269        replyStart,
270        replyEnd,
271        cp,
272        ntohs(reply.hdr.ancount),
273        ntohs(reply.hdr.nscount),
274        ntohs(reply.hdr.arcount),
275        results)) {
276     DnsRecordListFree(*results, DnsFreeRecordList);
277     return -1;
278   }
279 
280   return 0;
281 }
282 
283 
DnsRecordSetCopy(PDNS_RECORD src)284 PDNS_RECORD DnsRecordSetCopy(PDNS_RECORD src)
285 {
286   PDNS_RECORD result = NULL;
287   PDNS_RECORD dst = NULL;
288   PDNS_RECORD rec;
289 
290   while (src != NULL) {
291     rec = (PDNS_RECORD)malloc(sizeof(DNS_RECORD));
292     memcpy(rec, src, sizeof(DNS_RECORD));
293     if (result == NULL)
294       result = rec;
295     rec->pNext = NULL;
296     if (dst != NULL)
297       dst->pNext = rec;
298     src = src->pNext;
299     dst = rec;
300   }
301 
302   return result;
303 }
304 
305 
DnsRecordListFree(PDNS_RECORD rec,int)306 void DnsRecordListFree(PDNS_RECORD rec, int /* FreeType */)
307 {
308   while (rec != NULL) {
309     PDNS_RECORD next = rec->pNext;
310     free(rec);
311     rec = next;
312   }
313 }
314 
315 
316 #endif // P_HAS_RESOLVER
317 
318 
Compare(const PObject & obj) const319 PObject::Comparison PDNS::SRVRecord::Compare(const PObject & obj) const
320 {
321   const SRVRecord * other = dynamic_cast<const SRVRecord *>(&obj);
322 
323   if (other == NULL)
324     return LessThan;
325 
326   if (priority < other->priority)
327     return LessThan;
328   else if (priority > other->priority)
329     return GreaterThan;
330 
331   if (weight < other->weight)
332     return LessThan;
333   else if (weight > other->weight)
334     return GreaterThan;
335 
336   return EqualTo;
337 }
338 
PrintOn(ostream & strm) const339 void PDNS::SRVRecord::PrintOn(ostream & strm) const
340 {
341   strm << "host=" << hostName << ":" << port << "(" << hostAddress << "), "
342        << "priority=" << priority << ", "
343        << "weight=" << weight;
344 }
345 
346 /////////////////////////////////////////////////
347 
HandleDNSRecord(PDNS_RECORD dnsRecord,PDNS_RECORD results)348 PDNS::SRVRecord * PDNS::SRVRecordList::HandleDNSRecord(PDNS_RECORD dnsRecord, PDNS_RECORD results)
349 {
350   PDNS::SRVRecord * record = NULL;
351 
352   if (
353       (dnsRecord->Flags.S.Section == DnsSectionAnswer) &&
354       (dnsRecord->wType == DNS_TYPE_SRV) &&
355 #ifndef _WIN32_WCE
356       (strlen(dnsRecord->Data.SRV.pNameTarget) > 0) &&
357       (strcmp(dnsRecord->Data.SRV.pNameTarget, ".") != 0)
358 #else
359       (wcslen(dnsRecord->Data.SRV.pNameTarget) > 0) &&
360       (wcscmp(dnsRecord->Data.SRV.pNameTarget, L".") != 0)
361 #endif
362       ) {
363     record = new SRVRecord();
364     record->hostName = PString(dnsRecord->Data.SRV.pNameTarget);
365     record->port     = dnsRecord->Data.SRV.wPort;
366     record->priority = dnsRecord->Data.SRV.wPriority;
367     record->weight   = dnsRecord->Data.SRV.wWeight;
368 
369     // see if any A or AAAA records match this hostname
370     PDNS_RECORD aRecord = results;
371     while (aRecord != NULL) {
372       if ((dnsRecord->Flags.S.Section == DnsSectionAdditional) && (dnsRecord->wType == DNS_TYPE_A)) {
373         record->hostAddress = PIPSocket::Address(dnsRecord->Data.A.IpAddress);
374         break;
375       }
376       if ((dnsRecord->Flags.S.Section == DnsSectionAdditional) && (dnsRecord->wType == DNS_TYPE_AAAA)) {
377         record->hostAddress = PIPSocket::Address(16, (BYTE *)&dnsRecord->Data.AAAA.Ip6Address);
378         break;
379       }
380       aRecord = aRecord->pNext;
381     }
382 
383     // if no A or AAAA record found, then get address the hard way
384     if (aRecord == NULL)
385       PIPSocket::GetHostAddress(record->hostName, record->hostAddress);
386   }
387 
388   return record;
389 }
390 
PrintOn(ostream & strm) const391 void PDNS::SRVRecordList::PrintOn(ostream & strm) const
392 {
393   PINDEX i;
394   for (i = 0; i < GetSize(); i++)
395     strm << (*this)[i] << endl;
396 }
397 
GetFirst()398 PDNS::SRVRecord * PDNS::SRVRecordList::GetFirst()
399 {
400   if (GetSize() == 0)
401     return NULL;
402 
403   // create a list of all prioities, to save time
404   priPos = 0;
405   priList.SetSize(0);
406 
407   PINDEX i;
408   if (GetSize() > 0) {
409     priList.SetSize(1);
410     WORD lastPri = (*this)[0].priority;
411     priList[0] = lastPri;
412     (*this)[0].used = PFalse;
413     for (i = 1; i < GetSize(); i++) {
414       (*this)[i].used = PFalse;
415       if ((*this)[i].priority != lastPri) {
416         priPos++;
417         priList.SetSize(priPos);
418         lastPri = (*this)[i].priority;
419         priList[priPos] = lastPri;
420       }
421     }
422   }
423 
424   priPos = 0;
425   return GetNext();
426 }
427 
GetNext()428 PDNS::SRVRecord * PDNS::SRVRecordList::GetNext()
429 {
430   if (priList.GetSize() == 0)
431     return NULL;
432 
433   while (priPos < priList.GetSize()) {
434 
435     WORD currentPri = priList[priPos];
436 
437     // find first record at current priority
438     PINDEX firstPos;
439     for (firstPos = 0; (firstPos < GetSize()) && ((*this)[firstPos].priority != currentPri); firstPos++)
440       ;
441     if (firstPos == GetSize())
442       return NULL;
443 
444     // calculate total of all unused weights at this priority
445     unsigned totalWeight = (*this)[firstPos].weight;
446     PINDEX i = firstPos + 1;
447     PINDEX count = 1;
448     while (i < GetSize() && ((*this)[i].priority == currentPri)) {
449       if (!(*this)[i].used) {
450         totalWeight += (*this)[i].weight;
451         count ++;
452       }
453       ++i;
454     }
455 
456     // if no matches found, go to the next priority level
457     if (count == 0) {
458       priPos++;
459       continue;
460     }
461 
462     // selected the correct item
463     if (totalWeight > 0) {
464       unsigned targetWeight = PRandom::Number() % (totalWeight+1);
465       totalWeight = 0;
466       for (i = 0; i < GetSize() && ((*this)[i].priority == currentPri); i++) {
467         if (!(*this)[i].used) {
468           totalWeight += (*this)[i].weight;
469           if (totalWeight >= targetWeight) {
470             (*this)[i].used = PTrue;
471             return &(*this)[i];
472           }
473         }
474       }
475     }
476 
477     // pick a random item at this priority
478     PINDEX j = (count <= 1) ? 0 : (PRandom::Number() % count);
479     count = 0;
480     for (i = firstPos; i < GetSize() && ((*this)[i].priority == currentPri); i++) {
481       if (!(*this)[i].used) {
482         if (count == j) {
483           (*this)[i].used = PTrue;
484           return &(*this)[i];
485         }
486         count++;
487       }
488     }
489 
490     // go to the next priority level
491     priPos++;
492   }
493 
494   return NULL;
495 }
496 
GetSRVRecords(const PString & _service,const PString & type,const PString & domain,PDNS::SRVRecordList & recordList)497 PBoolean PDNS::GetSRVRecords(
498   const PString & _service,
499   const PString & type,
500   const PString & domain,
501   PDNS::SRVRecordList & recordList
502 )
503 {
504   if (_service.IsEmpty())
505     return PFalse;
506 
507   PStringStream service;
508   if (_service[0] != '_')
509     service << '_';
510 
511   service << _service << "._" << type << '.' << domain;
512 
513   return GetSRVRecords(service, recordList);
514 }
515 
LookupSRV(const PURL & url,const PString & service,PStringList & returnList)516 PBoolean PDNS::LookupSRV(
517            const PURL & url,
518         const PString & service,
519           PStringList & returnList)
520 {
521   WORD defaultPort = url.GetPort();
522   PIPSocketAddressAndPortVector info;
523 
524   if (!LookupSRV(url.GetHostName(), service, defaultPort, info)) {
525     PTRACE(2,"DNS\tSRV Lookup Fail no domain " << url );
526     return PFalse;
527   }
528 
529   PString user = url.GetUserName();
530   if (user.GetLength() > 0)
531     user = user + "@";
532 
533   PIPSocketAddressAndPortVector::const_iterator r;
534   for (r = info.begin(); r != info.end(); ++r) {
535     if (r->GetAddress().GetVersion() == 6)
536       returnList.AppendString(user + "[" + r->GetAddress().AsString() + "]:" + PString(r->GetPort()));
537     else
538       returnList.AppendString(user + r->AsString(':'));
539   }
540 
541   return returnList.GetSize() != 0;;
542 }
543 
LookupSRV(const PString & domain,const PString & service,WORD defaultPort,PIPSocketAddressAndPortVector & addrList)544 PBoolean PDNS::LookupSRV(
545          const PString & domain,            ///< domain to lookup
546          const PString & service,           ///< service to use
547                     WORD defaultPort,       ///< default port to use
548          PIPSocketAddressAndPortVector & addrList  ///< list of sockets and ports
549 )
550 {
551   if (domain.IsEmpty()) {
552     PTRACE(1,"DNS\tSRV lookup failed - no domain specified");
553     return PFalse;
554   }
555 
556   PString srvLookupStr = service;
557   if (srvLookupStr.Right(1) != ".")
558     srvLookupStr += ".";
559   srvLookupStr += domain;
560 
561   PTRACE(4,"DNS\tSRV Lookup \"" << srvLookupStr << '"');
562   return LookupSRV(srvLookupStr, defaultPort, addrList);
563 }
564 
LookupSRV(const PString & srvLookupStr,WORD defaultPort,PIPSocketAddressAndPortVector & addrList)565 PBoolean PDNS::LookupSRV(
566               const PString & srvLookupStr,
567               WORD defaultPort,
568               PIPSocketAddressAndPortVector & addrList
569 )
570 {
571 
572   PDNS::SRVRecordList srvRecords;
573   PBoolean found = PDNS::GetRecords(srvLookupStr, srvRecords);
574   if (found) {
575     PTRACE(5,"DNS\tSRV Record found \"" << srvLookupStr << '"');
576     PDNS::SRVRecord * recPtr = srvRecords.GetFirst();
577     while (recPtr != NULL) {
578       PIPSocketAddressAndPort addrAndPort;
579       addrAndPort.SetAddress(recPtr->hostAddress, recPtr->port > 0 ? recPtr->port : defaultPort);
580       addrList.push_back(addrAndPort);
581 
582       recPtr = srvRecords.GetNext();
583     }
584   }
585 
586   return found;
587 }
588 
589 ///////////////////////////////////////////////////////
590 
Compare(const PObject & obj) const591 PObject::Comparison PDNS::MXRecord::Compare(const PObject & obj) const
592 {
593   const MXRecord * other = dynamic_cast<const MXRecord *>(&obj);
594   if (other == NULL)
595     return LessThan;
596 
597   if (preference < other->preference)
598     return LessThan;
599   else if (preference > other->preference)
600     return GreaterThan;
601 
602   return EqualTo;
603 }
604 
PrintOn(ostream & strm) const605 void PDNS::MXRecord::PrintOn(ostream & strm) const
606 {
607   strm << "host=" << hostName << "(" << hostAddress << "), "
608        << "preference=" << preference;
609 }
610 
611 ///////////////////////////////////////////////////////
612 
HandleDNSRecord(PDNS_RECORD dnsRecord,PDNS_RECORD results)613 PDNS::MXRecord * PDNS::MXRecordList::HandleDNSRecord(PDNS_RECORD dnsRecord, PDNS_RECORD results)
614 {
615   MXRecord * record = NULL;
616 
617   if (
618       (dnsRecord->Flags.S.Section == DnsSectionAnswer) &&
619       (dnsRecord->wType == DNS_TYPE_MX) &&
620 #ifndef _WIN32_WCE
621       (strlen(dnsRecord->Data.MX.pNameExchange) > 0)
622 #else
623       (wcslen(dnsRecord->Data.MX.pNameExchange) > 0)
624 #endif
625      ) {
626     record = new MXRecord();
627     record->hostName   = PString(dnsRecord->Data.MX.pNameExchange);
628     record->preference = dnsRecord->Data.MX.wPreference;
629 
630     // see if any A records match this hostname
631     PDNS_RECORD aRecord = results;
632     while (aRecord != NULL) {
633       if ((dnsRecord->Flags.S.Section == DnsSectionAdditional) && (dnsRecord->wType == DNS_TYPE_A)) {
634         record->hostAddress = PIPSocket::Address(dnsRecord->Data.A.IpAddress);
635         break;
636       }
637       if ((dnsRecord->Flags.S.Section == DnsSectionAdditional) && (dnsRecord->wType == DNS_TYPE_AAAA)) {
638         record->hostAddress = PIPSocket::Address(16, (BYTE *)&dnsRecord->Data.AAAA.Ip6Address);
639         break;
640       }
641       aRecord = aRecord->pNext;
642     }
643 
644     // if no A record found, then get address the hard way
645     if (aRecord == NULL)
646       PIPSocket::GetHostAddress(record->hostName, record->hostAddress);
647   }
648 
649   return record;
650 }
651 
PrintOn(ostream & strm) const652 void PDNS::MXRecordList::PrintOn(ostream & strm) const
653 {
654   PINDEX i;
655   for (i = 0; i < GetSize(); i++)
656     strm << (*this)[i] << endl;
657 }
658 
GetFirst()659 PDNS::MXRecord * PDNS::MXRecordList::GetFirst()
660 {
661   PINDEX i;
662   for (i = 0; i < GetSize(); i++)
663     (*this)[i].used = PFalse;
664 
665   lastIndex = 0;
666 
667   return GetNext();
668 }
669 
GetNext()670 PDNS::MXRecord * PDNS::MXRecordList::GetNext()
671 {
672   if (GetSize() == 0)
673     return NULL;
674 
675   if (lastIndex >= GetSize())
676     return NULL;
677 
678   return (PDNS::MXRecord *)GetAt(lastIndex++);
679 }
680 
681 /////////////////////////////////////////////////////////////////
682 
Cached_DnsQuery(const char * name,WORD type,DWORD options,void *,PDNS_RECORD * queryResults,void *)683 DNS_STATUS PDNS::Cached_DnsQuery(
684     const char * name,
685     WORD       type,
686     DWORD      options,
687     void *     ,
688     PDNS_RECORD * queryResults,
689     void * )
690 {
691   PTime now;
692   PWaitAndSignal m(GetDNSMutex());
693 
694   DNSCache::iterator r;
695 
696   // age entries in cache
697   if ((now - g_lastAgeTime) > RESOLVER_CACHE_TIMEOUT) {
698     g_lastAgeTime = now;
699 
700     r = g_dnsCache.begin();
701     while (r != g_dnsCache.end()) {
702       if ((now - r->second.m_time) < RESOLVER_CACHE_TIMEOUT)
703         ++r;
704       else {
705         PTRACE(5, "DNS\tQuery aged \"" << r->first << '"');
706         DnsRecordListFree(r->second.m_results, DnsFreeRecordList);
707         g_dnsCache.erase(r++);
708       }
709     }
710   }
711 
712   // see if cache contains the entry we need
713   string key;
714   {
715     std::stringstream strm;
716     strm << name << '\t' << type << '\t' << options;
717     key = strm.str();
718   }
719 
720   r = g_dnsCache.find(key);
721   if (r == g_dnsCache.end()) {
722     PTRACE(5, "DNS\tSRV physical lookup \"" << key << '"');
723 
724     // else do the lookup and put it into the cache
725     DNSCacheInfo info;
726     info.m_status = DnsQuery_A((const char *)name,
727                                type,
728                                DNS_QUERY_STANDARD,
729                                NULL,
730                                &info.m_results,
731                                NULL);
732 #if PTRACING
733     if (info.m_status != 0)
734       PTRACE(3, "DNS\tQuery failed: error=" << info.m_status);
735     else {
736       PTRACE(6, "DNS\tQuery success: " << info.m_results);
737       for (PDNS_RECORD rec = info.m_results; rec != NULL; rec = rec->pNext)
738         PTRACE(6, "DNS\tQuery: name=\"" << PString(rec->pName)
739                << "\", type=" << rec->wType << ", len=" << rec->wDataLength);
740       PTRACE(6, "DNS\tQuery done");
741     }
742 #endif
743 
744     r = g_dnsCache.insert(DNSCache::value_type(key, info)).first;
745   }
746 
747   *queryResults = DnsRecordSetCopy(r->second.m_results);
748   return r->second.m_status;
749 }
750 
751 
752 /////////////////////////////////////////////////////////////////
753 
754 #else
755 
756   #ifdef _MSC_VER
757     #pragma message("DNS support DISABLED")
758   #endif
759 
760 #endif // P_DNS
761 
762 
763 // End Of File ///////////////////////////////////////////////////////////////
764