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