Uncommented beaudio code
[pwlib.git] / src / ptclib / pdns.cxx
blobc879e9f015bf37b9b3daa47eee338915c407020c
1 /*
2 * pdns.cxx
4 * Portable Windows Library
6 * Copyright (c) 2003 Equivalence Pty. Ltd.
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 Equivalence Pty. Ltd.
22 * Contributor(s): ______________________________________.
24 * Copyright 2003 Equivalence Pty. Ltd.
26 * $Log$
27 * Revision 1.12 2004/02/23 23:52:19 csoutheren
28 * Added pragmas to avoid every Windows application needing to include libs explicitly
30 * Revision 1.11 2004/01/03 03:37:53 csoutheren
31 * Fixed compile problem on Linux
33 * Revision 1.10 2004/01/03 03:10:42 csoutheren
34 * Fixed more problems with looking up SRV records, especially on Windows
36 * Revision 1.9 2004/01/02 13:22:04 csoutheren
37 * Fixed problem with extracting SRV records from DNS
39 * Revision 1.8 2003/11/02 15:52:58 shawn
40 * added arpa/nameser_compat.h for Mac OS X 10.3 (Panther)
42 * Revision 1.7 2003/04/28 23:57:40 robertj
43 * Fixed Solaris compatibility
45 * Revision 1.6 2003/04/22 23:21:37 craigs
46 * Swapped includes at request of Shawn Hsiao for compatiobility with MacOSX
48 * Revision 1.5 2003/04/16 14:21:12 craigs
49 * Added set of T_SRV for MacOS
51 * Revision 1.4 2003/04/16 08:00:19 robertj
52 * Windoes psuedo autoconf support
54 * Revision 1.3 2003/04/15 08:14:32 craigs
55 * Added single string form of GetSRVRecords
57 * Revision 1.2 2003/04/15 08:05:19 craigs
58 * Added Unix implementation
60 * Revision 1.1 2003/04/15 04:06:35 craigs
61 * Initial version
65 #ifdef __GNUC__
66 #pragma implementation "pdns.h"
67 #endif
69 #include <ptlib.h>
71 #include <ptclib/pdns.h>
73 #if P_DNS
75 #include <ptclib/random.h>
77 #if defined(_WIN32)
79 #include <windns.h>
81 #pragma comment(lib, P_DNS_LIBRARY)
82 #pragma comment(linker, "/delayload:dnsapi.dll")
83 #pragma comment(lib, "Delayimp.lib")
85 #else
87 #define P_HAS_RESOLVER 1
89 #if P_HAS_RESOLVER
90 #include <arpa/nameser.h>
91 #include <resolv.h>
92 #if defined(P_MACOSX) && (P_MACOSX >= 700)
93 #include <arpa/nameser_compat.h>
94 #endif
95 #endif
97 #endif
99 #ifndef T_SRV
100 #define T_SRV 33
101 #endif
104 /////////////////////////////////////////////////
106 PDNS::Record::Record()
108 used = FALSE;
112 /////////////////////////////////////////////////
115 #ifdef P_HAS_RESOLVER
117 #define DNS_STATUS int
118 #define DNS_TYPE_SRV T_SRV
119 #define DNS_TYPE_MX T_MX
120 #define DNS_TYPE_A T_A
121 #define DnsFreeRecordList 0
122 #define DNS_QUERY_STANDARD 0
123 #define DNS_QUERY_BYPASS_CACHE 0
125 typedef struct _DnsAData {
126 DWORD IpAddress;
127 } DNS_A_DATA;
129 typedef struct {
130 char pNameExchange[MAXDNAME];
131 WORD wPreference;
133 } DNS_MX_DATA;
135 typedef struct {
136 char pNameHost[MAXDNAME];
137 } DNS_PTR_DATA;
139 typedef struct _DnsSRVData {
140 public:
141 char pNameTarget[MAXDNAME];
142 WORD wPriority;
143 WORD wWeight;
144 WORD wPort;
146 } DNS_SRV_DATA;
148 typedef struct _DnsRecordFlags
150 unsigned Section : 2;
151 unsigned Delete : 1;
152 unsigned CharSet : 2;
153 unsigned Unused : 3;
154 unsigned Reserved : 24;
155 } DNS_RECORD_FLAGS;
157 typedef enum _DnsSection
159 DnsSectionQuestion,
160 DnsSectionAnswer,
161 DnsSectionAuthority,
162 DnsSectionAddtional,
163 } DNS_SECTION;
166 class DnsRecord {
167 public:
168 DnsRecord * pNext;
169 char pName[MAXDNAME];
170 WORD wType;
171 WORD wDataLength;
173 union {
174 DWORD DW; // flags as DWORD
175 DNS_RECORD_FLAGS S; // flags as structure
176 } Flags;
178 union {
179 DNS_A_DATA A;
180 DNS_MX_DATA MX;
181 DNS_PTR_DATA NS;
182 DNS_SRV_DATA SRV;
183 } Data;
186 typedef DnsRecord * PDNS_RECORD;
188 static BOOL GetDN(const BYTE * reply, const BYTE * replyEnd, BYTE * & cp, char * buff)
190 int len = dn_expand(reply, replyEnd, cp, buff, MAXDNAME);
191 if (len < 0)
192 return FALSE;
193 cp += len;
194 return TRUE;
197 static BOOL ProcessDNSRecords(
198 const BYTE * reply,
199 const BYTE * replyEnd,
200 BYTE * cp,
201 PINDEX anCount,
202 PINDEX nsCount,
203 PINDEX arCount,
204 PDNS_RECORD * results)
206 PDNS_RECORD lastRecord = NULL;
207 PDNS_RECORD newRecord = NULL;
209 PINDEX rrCount = anCount + nsCount + arCount;
210 nsCount += anCount;
211 arCount += nsCount;
213 PINDEX i;
214 for (i = 0; i < rrCount; i++) {
216 if (newRecord == NULL)
217 newRecord = new DnsRecord;
219 memset(newRecord, 0, sizeof(DnsRecord));
221 if (i < anCount)
222 newRecord->Flags.S.Section = DnsSectionAnswer;
223 else if (i < nsCount)
224 newRecord->Flags.S.Section = DnsSectionAuthority;
225 else if (i < arCount)
226 newRecord->Flags.S.Section = DnsSectionAddtional;
228 // get the name
229 if (!GetDN(reply, replyEnd, cp, newRecord->pName)) {
230 delete newRecord;
231 return FALSE;
234 // get other common parts of the record
235 WORD type;
236 WORD dnsClass;
237 DWORD ttl;
238 WORD dlen;
240 GETSHORT(type, cp);
241 GETSHORT(dnsClass, cp);
242 GETLONG (ttl, cp);
243 GETSHORT(dlen, cp);
245 newRecord->wType = type;
247 BYTE * data = cp;
248 cp += dlen;
249 BOOL ok = TRUE;
251 switch (type) {
252 case T_SRV:
253 GETSHORT(newRecord->Data.SRV.wPriority, data);
254 GETSHORT(newRecord->Data.SRV.wWeight, data);
255 GETSHORT(newRecord->Data.SRV.wPort, data);
256 if (!GetDN(reply, replyEnd, data, newRecord->Data.SRV.pNameTarget)) {
257 delete newRecord;
258 return FALSE;
260 break;
262 case T_MX:
263 GETSHORT(newRecord->Data.MX.wPreference, data);
264 if (!GetDN(reply, replyEnd, data, newRecord->Data.MX.pNameExchange)) {
265 delete newRecord;
266 return FALSE;
268 break;
270 case T_A:
271 GETLONG(newRecord->Data.A.IpAddress, data);
272 break;
274 case T_NS:
275 if (!GetDN(reply, replyEnd, data, newRecord->Data.NS.pNameHost)) {
276 delete newRecord;
277 return FALSE;
279 break;
281 default:
282 ok = FALSE;
283 break;
286 if (ok) {
287 if (*results == NULL)
288 *results = newRecord;
290 newRecord->pNext = NULL;
292 if (lastRecord != NULL)
293 lastRecord->pNext = newRecord;
295 lastRecord = newRecord;
296 newRecord = NULL;
300 delete newRecord;
302 return TRUE;
305 void DnsRecordListFree(PDNS_RECORD rec, int /* FreeType */)
307 while (rec != NULL) {
308 PDNS_RECORD next = rec->pNext;
309 delete rec;
310 rec = next;
314 DNS_STATUS DnsQuery_A(const char * service,
315 WORD requestType,
316 DWORD options,
317 void *,
318 PDNS_RECORD * results,
319 void *)
321 if (results == NULL)
322 return -1;
324 *results = NULL;
326 res_init();
328 union {
329 HEADER hdr;
330 BYTE buf[PACKETSZ];
331 } reply;
333 int replyLen = res_search(service, C_IN, requestType, (BYTE *)&reply, sizeof(reply));
335 if (replyLen < 1)
336 return -1;
338 BYTE * replyStart = reply.buf;
339 BYTE * replyEnd = reply.buf + replyLen;
340 BYTE * cp = reply.buf + sizeof(HEADER);
342 // ignore questions in response
343 unsigned i;
344 for (i = 0; i < ntohs(reply.hdr.qdcount); i++) {
345 char qName[MAXDNAME];
346 if (!GetDN(replyStart, replyEnd, cp, qName))
347 return -1;
348 cp += QFIXEDSZ;
351 if (!ProcessDNSRecords(replyStart,
352 replyEnd,
354 ntohs(reply.hdr.ancount),
355 ntohs(reply.hdr.nscount),
356 ntohs(reply.hdr.arcount),
357 results)) {
358 DnsRecordListFree(*results, 0);
359 return -1;
362 return 0;
366 #endif // P_HAS_RESOLVER
369 /////////////////////////////////////////////////
371 PObject::Comparison PDNS::SRVRecord::Compare(const PObject & obj) const
373 PDNS::SRVRecord & other = (PDNS::SRVRecord &)obj;
374 if (priority < other.priority)
375 return LessThan;
376 else if (priority > other.priority)
377 return GreaterThan;
378 if (weight < other.weight)
379 return LessThan;
380 else if (weight > other.weight)
381 return GreaterThan;
382 return EqualTo;
385 void PDNS::SRVRecord::PrintOn(ostream & strm) const
387 strm << "host=" << hostName << ":" << port << "(" << hostAddress << "), "
388 << "priority=" << priority << ", "
389 << "weight=" << weight;
392 /////////////////////////////////////////////////
394 void PDNS::SRVRecordList::PrintOn(ostream & strm) const
396 PINDEX i;
397 for (i = 0; i < GetSize(); i++)
398 strm << (*this)[i] << endl;
401 PDNS::SRVRecord * PDNS::SRVRecordList::GetFirst()
403 if (GetSize() == 0)
404 return NULL;
406 // create a list of all prioities, to save time
407 priPos = 0;
408 priList.SetSize(0);
410 PINDEX i;
411 if (GetSize() > 0) {
412 priList.SetSize(1);
413 WORD lastPri = (*this)[0].priority;
414 priList[0] = lastPri;
415 (*this)[0].used = FALSE;
416 for (i = 1; i < GetSize(); i++) {
417 (*this)[i].used = FALSE;
418 if ((*this)[i].priority != lastPri) {
419 priList.SetSize(priPos+1);
420 lastPri = (*this)[i].priority;
421 priList[priPos] = lastPri;
426 priPos = 0;
427 return GetNext();
430 PDNS::SRVRecord * PDNS::SRVRecordList::GetNext()
432 if (priList.GetSize() == 0)
433 return NULL;
435 while (priPos < priList.GetSize()) {
437 WORD currentPri = priList[priPos];
439 // find first record at current priority
440 PINDEX firstPos;
441 for (firstPos = 0; (firstPos < GetSize()) && ((*this)[firstPos].priority != currentPri); firstPos++)
443 if (firstPos == GetSize())
444 return NULL;
446 // calculate total of all unused weights at this priority
447 unsigned totalWeight = (*this)[firstPos].weight;
448 PINDEX i = firstPos + 1;
449 PINDEX count = 1;
450 while (i < GetSize() && ((*this)[i].priority == currentPri)) {
451 if (!(*this)[i].used) {
452 totalWeight += (*this)[i].weight;
453 count ++;
457 // if no matches found, go to the next priority level
458 if (count == 0) {
459 priPos++;
460 continue;
463 // selected the correct item
464 if (totalWeight > 0) {
465 unsigned targetWeight = PRandom::Number() % (totalWeight+1);
466 totalWeight = 0;
467 for (i = 0; i < GetSize() && ((*this)[i].priority == currentPri); i++) {
468 if (!(*this)[i].used) {
469 totalWeight += (*this)[i].weight;
470 if (totalWeight >= targetWeight) {
471 (*this)[i].used = TRUE;
472 return &(*this)[i];
478 // pick a random item at this priority
479 PINDEX j = firstPos + ((count == 0) ? 0 : (PRandom::Number() % count) );
480 count = 0;
481 for (i = 0; i < GetSize() && ((*this)[i].priority == currentPri); i++) {
482 if (!(*this)[i].used) {
483 if (count == j) {
484 (*this)[i].used = TRUE;
485 return &(*this)[i];
487 count++;
491 // go to the next priority level
492 priPos++;
495 return NULL;
498 ///////////////////////////////////////////////////////
500 BOOL PDNS::GetSRVRecords(const PString & _service,
501 const PString & type,
502 const PString & domain,
503 SRVRecordList & recordList)
506 if (_service.IsEmpty())
507 return FALSE;
509 PString service;
510 if (_service[0] != '_')
511 service = PString("_") + _service;
512 else
513 service = _service;
515 service += PString("._") + type + "." + domain;
517 return GetSRVRecords(service, recordList);
520 BOOL PDNS::GetSRVRecords(const PString & service, PDNS::SRVRecordList & recordList)
522 recordList.RemoveAll();
524 PDNS_RECORD results = NULL;
525 DNS_STATUS status = DnsQuery_A((const char *)service,
526 DNS_TYPE_SRV,
527 DNS_QUERY_STANDARD | DNS_QUERY_BYPASS_CACHE,
528 NULL,
529 &results,
530 NULL);
531 if (status != 0)
532 return FALSE;
534 // find SRV records
535 PDNS_RECORD dnsRecord = results;
536 while (dnsRecord != NULL) {
537 if (
538 (dnsRecord->Flags.S.Section == DnsSectionAnswer) &&
539 (dnsRecord->wType == DNS_TYPE_SRV) &&
540 (strcmp(dnsRecord->Data.SRV.pNameTarget, ".") != 0)
542 SRVRecord * record = new SRVRecord();
543 record->hostName = PString(dnsRecord->Data.SRV.pNameTarget);
544 record->port = results->Data.SRV.wPort;
545 record->priority = results->Data.SRV.wPriority;
546 record->weight = results->Data.SRV.wWeight;
548 // see if any A records match this hostname
549 PDNS_RECORD aRecord = results;
550 while (aRecord != NULL) {
551 if ((dnsRecord->Flags.S.Section == DnsSectionAddtional) && (dnsRecord->wType == DNS_TYPE_A)) {
552 record->hostAddress = PIPSocket::Address(dnsRecord->Data.A.IpAddress);
553 break;
555 aRecord = aRecord->pNext;
558 // if no A record found, then get address the hard way
559 if (aRecord == NULL)
560 PIPSocket::GetHostAddress(record->hostName, record->hostAddress);
562 // add record to the list
563 recordList.Append(record);
565 dnsRecord = dnsRecord->pNext;
568 if (results != NULL)
569 DnsRecordListFree(results, DnsFreeRecordList);
571 return recordList.GetSize() != 0;
574 ///////////////////////////////////////////////////////
576 PObject::Comparison PDNS::MXRecord::Compare(const PObject & obj) const
578 PDNS::MXRecord & other = (PDNS::MXRecord &)obj;
579 if (preference < other.preference)
580 return LessThan;
581 else if (preference > other.preference)
582 return GreaterThan;
583 return EqualTo;
586 void PDNS::MXRecord::PrintOn(ostream & strm) const
588 strm << "host=" << hostName << "(" << hostAddress << "), "
589 << "preference=" << preference;
592 ///////////////////////////////////////////////////////
594 BOOL PDNS::GetMXRecords(const PString & domain, MXRecordList & recordList)
596 if (domain.IsEmpty())
597 return FALSE;
599 recordList.RemoveAll();
601 PDNS_RECORD results = NULL;
602 DNS_STATUS status = DnsQuery_A((const char *)domain,
603 DNS_TYPE_MX,
604 DNS_QUERY_STANDARD,
605 NULL,
606 &results,
607 NULL);
608 if (status != 0)
609 return FALSE;
611 // find MX records
612 PDNS_RECORD dnsRecord = results;
613 while (dnsRecord != NULL) {
614 if ((dnsRecord->Flags.S.Section == DnsSectionAnswer) && (dnsRecord->wType == DNS_TYPE_MX)) {
615 MXRecord * record = new MXRecord();
616 record->hostName = PString(dnsRecord->Data.MX.pNameExchange);
617 record->preference = dnsRecord->Data.MX.wPreference;
619 // see if any A records match this hostname
620 PDNS_RECORD aRecord = results;
621 while (aRecord != NULL) {
622 if ((dnsRecord->Flags.S.Section == DnsSectionAddtional) && (dnsRecord->wType == DNS_TYPE_A)) {
623 record->hostAddress = PIPSocket::Address(dnsRecord->Data.A.IpAddress);
624 break;
626 aRecord = aRecord->pNext;
629 // if no A record found, then get address the hard way
630 if (aRecord == NULL)
631 PIPSocket::GetHostAddress(record->hostName, record->hostAddress);
633 // add record to the list
634 recordList.Append(record);
636 dnsRecord = dnsRecord->pNext;
639 if (results != NULL)
640 DnsRecordListFree(results, DnsFreeRecordList);
642 return recordList.GetSize() != 0;
645 ///////////////////////////////////////////////////////
647 void PDNS::MXRecordList::PrintOn(ostream & strm) const
649 PINDEX i;
650 for (i = 0; i < GetSize(); i++)
651 strm << (*this)[i] << endl;
654 PDNS::MXRecord * PDNS::MXRecordList::GetFirst()
656 PINDEX i;
657 for (i = 0; i < GetSize(); i++)
658 (*this)[i].used = FALSE;
660 lastIndex = 0;
662 return GetNext();
665 PDNS::MXRecord * PDNS::MXRecordList::GetNext()
667 if (GetSize() == 0)
668 return NULL;
670 if (lastIndex >= GetSize())
671 return NULL;
673 return (PDNS::MXRecord *)GetAt(lastIndex++);
677 #endif // P_DNS
680 // End Of File ///////////////////////////////////////////////////////////////