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
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.
27 * Revision 1.21 2004/11/15 23:47:18 csoutheren
28 * Fixed problem with empty SRV names
30 * Revision 1.20 2004/11/15 11:33:52 csoutheren
31 * Fixed problem in SRV record handling
33 * Revision 1.19 2004/06/24 07:36:24 csoutheren
34 * Added definitions of T_SRV and T_NAPTR for hosts that do not have these
36 * Revision 1.18 2004/06/01 23:30:38 csoutheren
37 * Removed warning under Linux
39 * Revision 1.17 2004/05/31 23:14:17 csoutheren
40 * Fixed warnings under VS.net and fixed problem with SRV records when returning multiple records
42 * Revision 1.16 2004/05/31 12:49:48 csoutheren
43 * Added handling of unknown DNS types
45 * Revision 1.15 2004/05/31 12:14:13 rjongbloed
46 * Fixed missing namespace selector on function definition.
47 * Opyimised some string processing
49 * Revision 1.14 2004/05/28 06:50:45 csoutheren
50 * Reorganised DNS functions to use templates, and exposed more internals to allow new DNS lookup types to be added
52 * Revision 1.13 2004/04/09 06:52:17 rjongbloed
53 * Removed #pargma linker command for /delayload of DLL as documentations sais that
56 * Revision 1.12 2004/02/23 23:52:19 csoutheren
57 * Added pragmas to avoid every Windows application needing to include libs explicitly
59 * Revision 1.11 2004/01/03 03:37:53 csoutheren
60 * Fixed compile problem on Linux
62 * Revision 1.10 2004/01/03 03:10:42 csoutheren
63 * Fixed more problems with looking up SRV records, especially on Windows
65 * Revision 1.9 2004/01/02 13:22:04 csoutheren
66 * Fixed problem with extracting SRV records from DNS
68 * Revision 1.8 2003/11/02 15:52:58 shawn
69 * added arpa/nameser_compat.h for Mac OS X 10.3 (Panther)
71 * Revision 1.7 2003/04/28 23:57:40 robertj
72 * Fixed Solaris compatibility
74 * Revision 1.6 2003/04/22 23:21:37 craigs
75 * Swapped includes at request of Shawn Hsiao for compatiobility with MacOSX
77 * Revision 1.5 2003/04/16 14:21:12 craigs
78 * Added set of T_SRV for MacOS
80 * Revision 1.4 2003/04/16 08:00:19 robertj
81 * Windoes psuedo autoconf support
83 * Revision 1.3 2003/04/15 08:14:32 craigs
84 * Added single string form of GetSRVRecords
86 * Revision 1.2 2003/04/15 08:05:19 craigs
87 * Added Unix implementation
89 * Revision 1.1 2003/04/15 04:06:35 craigs
95 #pragma implementation "pdns.h"
99 #include <ptclib/pdns.h>
104 /////////////////////////////////////////////////
106 #ifdef P_HAS_RESOLVER
108 static BOOL
GetDN(const BYTE
* reply
, const BYTE
* replyEnd
, BYTE
* & cp
, char * buff
)
110 int len
= dn_expand(reply
, replyEnd
, cp
, buff
, MAXDNAME
);
117 static BOOL
ProcessDNSRecords(
119 const BYTE
* replyEnd
,
124 PDNS_RECORD
* results
)
126 PDNS_RECORD lastRecord
= NULL
;
128 PINDEX rrCount
= anCount
+ nsCount
+ arCount
;
133 for (i
= 0; i
< rrCount
; i
++) {
137 section
= DnsSectionAnswer
;
138 else if (i
< nsCount
)
139 section
= DnsSectionAuthority
;
140 else // if (i < arCount)
141 section
= DnsSectionAddtional
;
144 char pName
[MAXDNAME
];
145 if (!GetDN(reply
, replyEnd
, cp
, pName
))
148 // get other common parts of the record
155 GETSHORT(dnsClass
, cp
);
162 PDNS_RECORD newRecord
= NULL
;
166 newRecord
= (PDNS_RECORD
)malloc(sizeof(DnsRecord
) + sizeof(DWORD
) + dlen
);
167 newRecord
->Data
.Null
.dwByteCount
= dlen
;
168 memcpy(&newRecord
->Data
, data
, dlen
);
172 newRecord
= (PDNS_RECORD
)malloc(sizeof(DnsRecord
));
173 memset(newRecord
, 0, sizeof(DnsRecord
));
174 GETSHORT(newRecord
->Data
.SRV
.wPriority
, data
);
175 GETSHORT(newRecord
->Data
.SRV
.wWeight
, data
);
176 GETSHORT(newRecord
->Data
.SRV
.wPort
, data
);
177 if (!GetDN(reply
, replyEnd
, data
, newRecord
->Data
.SRV
.pNameTarget
)) {
184 newRecord
= (PDNS_RECORD
)malloc(sizeof(DnsRecord
));
185 memset(newRecord
, 0, sizeof(DnsRecord
));
186 GETSHORT(newRecord
->Data
.MX
.wPreference
, data
);
187 if (!GetDN(reply
, replyEnd
, data
, newRecord
->Data
.MX
.pNameExchange
)) {
194 newRecord
= (PDNS_RECORD
)malloc(sizeof(DnsRecord
));
195 memset(newRecord
, 0, sizeof(DnsRecord
));
196 GETLONG(newRecord
->Data
.A
.IpAddress
, data
);
200 newRecord
= (PDNS_RECORD
)malloc(sizeof(DnsRecord
));
201 memset(newRecord
, 0, sizeof(DnsRecord
));
202 if (!GetDN(reply
, replyEnd
, data
, newRecord
->Data
.NS
.pNameHost
)) {
209 // initialise the new record
210 if (newRecord
!= NULL
) {
211 newRecord
->wType
= type
;
212 newRecord
->Flags
.S
.Section
= section
;
213 newRecord
->pNext
= NULL
;
214 strcpy(newRecord
->pName
, pName
);
216 if (*results
== NULL
)
217 *results
= newRecord
;
219 if (lastRecord
!= NULL
)
220 lastRecord
->pNext
= newRecord
;
222 lastRecord
= newRecord
;
230 void DnsRecordListFree(PDNS_RECORD rec
, int /* FreeType */)
232 while (rec
!= NULL
) {
233 PDNS_RECORD next
= rec
->pNext
;
239 DNS_STATUS
DnsQuery_A(const char * service
,
243 PDNS_RECORD
* results
,
258 int replyLen
= res_search(service
, C_IN
, requestType
, (BYTE
*)&reply
, sizeof(reply
));
263 BYTE
* replyStart
= reply
.buf
;
264 BYTE
* replyEnd
= reply
.buf
+ replyLen
;
265 BYTE
* cp
= reply
.buf
+ sizeof(HEADER
);
267 // ignore questions in response
269 for (i
= 0; i
< ntohs(reply
.hdr
.qdcount
); i
++) {
270 char qName
[MAXDNAME
];
271 if (!GetDN(replyStart
, replyEnd
, cp
, qName
))
276 if (!ProcessDNSRecords(replyStart
,
279 ntohs(reply
.hdr
.ancount
),
280 ntohs(reply
.hdr
.nscount
),
281 ntohs(reply
.hdr
.arcount
),
283 DnsRecordListFree(*results
, 0);
291 #endif // P_HAS_RESOLVER
293 PObject::Comparison
PDNS::SRVRecord::Compare(const PObject
& obj
) const
295 const SRVRecord
* other
= dynamic_cast<const SRVRecord
*>(&obj
);
300 if (priority
< other
->priority
)
302 else if (priority
> other
->priority
)
305 if (weight
< other
->weight
)
307 else if (weight
> other
->weight
)
313 void PDNS::SRVRecord::PrintOn(ostream
& strm
) const
315 strm
<< "host=" << hostName
<< ":" << port
<< "(" << hostAddress
<< "), "
316 << "priority=" << priority
<< ", "
317 << "weight=" << weight
;
320 /////////////////////////////////////////////////
322 PDNS::SRVRecord
* PDNS::SRVRecordList::HandleDNSRecord(PDNS_RECORD dnsRecord
, PDNS_RECORD results
)
324 PDNS::SRVRecord
* record
= NULL
;
327 (dnsRecord
->Flags
.S
.Section
== DnsSectionAnswer
) &&
328 (dnsRecord
->wType
== DNS_TYPE_SRV
) &&
329 (strlen(dnsRecord
->Data
.SRV
.pNameTarget
) > 0) &&
330 (strcmp(dnsRecord
->Data
.SRV
.pNameTarget
, ".") != 0)
332 record
= new SRVRecord();
333 record
->hostName
= PString(dnsRecord
->Data
.SRV
.pNameTarget
);
334 record
->port
= dnsRecord
->Data
.SRV
.wPort
;
335 record
->priority
= dnsRecord
->Data
.SRV
.wPriority
;
336 record
->weight
= dnsRecord
->Data
.SRV
.wWeight
;
338 // see if any A records match this hostname
339 PDNS_RECORD aRecord
= results
;
340 while (aRecord
!= NULL
) {
341 if ((dnsRecord
->Flags
.S
.Section
== DnsSectionAddtional
) && (dnsRecord
->wType
== DNS_TYPE_A
)) {
342 record
->hostAddress
= PIPSocket::Address(dnsRecord
->Data
.A
.IpAddress
);
345 aRecord
= aRecord
->pNext
;
348 // if no A record found, then get address the hard way
350 PIPSocket::GetHostAddress(record
->hostName
, record
->hostAddress
);
356 void PDNS::SRVRecordList::PrintOn(ostream
& strm
) const
359 for (i
= 0; i
< GetSize(); i
++)
360 strm
<< (*this)[i
] << endl
;
363 PDNS::SRVRecord
* PDNS::SRVRecordList::GetFirst()
368 // create a list of all prioities, to save time
375 WORD lastPri
= (*this)[0].priority
;
376 priList
[0] = lastPri
;
377 (*this)[0].used
= FALSE
;
378 for (i
= 1; i
< GetSize(); i
++) {
379 (*this)[i
].used
= FALSE
;
380 if ((*this)[i
].priority
!= lastPri
) {
381 priList
.SetSize(priPos
+1);
382 lastPri
= (*this)[i
].priority
;
383 priList
[priPos
] = lastPri
;
392 PDNS::SRVRecord
* PDNS::SRVRecordList::GetNext()
394 if (priList
.GetSize() == 0)
397 while (priPos
< priList
.GetSize()) {
399 WORD currentPri
= priList
[priPos
];
401 // find first record at current priority
403 for (firstPos
= 0; (firstPos
< GetSize()) && ((*this)[firstPos
].priority
!= currentPri
); firstPos
++)
405 if (firstPos
== GetSize())
408 // calculate total of all unused weights at this priority
409 unsigned totalWeight
= (*this)[firstPos
].weight
;
410 PINDEX i
= firstPos
+ 1;
412 while (i
< GetSize() && ((*this)[i
].priority
== currentPri
)) {
413 if (!(*this)[i
].used
) {
414 totalWeight
+= (*this)[i
].weight
;
419 // if no matches found, go to the next priority level
425 // selected the correct item
426 if (totalWeight
> 0) {
427 unsigned targetWeight
= PRandom::Number() % (totalWeight
+1);
429 for (i
= 0; i
< GetSize() && ((*this)[i
].priority
== currentPri
); i
++) {
430 if (!(*this)[i
].used
) {
431 totalWeight
+= (*this)[i
].weight
;
432 if (totalWeight
>= targetWeight
) {
433 (*this)[i
].used
= TRUE
;
440 // pick a random item at this priority
441 PINDEX j
= firstPos
+ ((count
== 0) ? 0 : (PRandom::Number() % count
) );
443 for (i
= 0; i
< GetSize() && ((*this)[i
].priority
== currentPri
); i
++) {
444 if (!(*this)[i
].used
) {
446 (*this)[i
].used
= TRUE
;
453 // go to the next priority level
460 BOOL
PDNS::GetSRVRecords(
461 const PString
& _service
,
462 const PString
& type
,
463 const PString
& domain
,
464 PDNS::SRVRecordList
& recordList
467 if (_service
.IsEmpty())
470 PStringStream service
;
471 if (_service
[0] != '_')
474 service
<< _service
<< "._" << type
<< '.' << domain
;
476 return GetSRVRecords(service
, recordList
);
480 ///////////////////////////////////////////////////////
482 PObject::Comparison
PDNS::MXRecord::Compare(const PObject
& obj
) const
484 const MXRecord
* other
= dynamic_cast<const MXRecord
*>(&obj
);
488 if (preference
< other
->preference
)
490 else if (preference
> other
->preference
)
496 void PDNS::MXRecord::PrintOn(ostream
& strm
) const
498 strm
<< "host=" << hostName
<< "(" << hostAddress
<< "), "
499 << "preference=" << preference
;
502 ///////////////////////////////////////////////////////
504 PDNS::MXRecord
* PDNS::MXRecordList::HandleDNSRecord(PDNS_RECORD dnsRecord
, PDNS_RECORD results
)
506 MXRecord
* record
= NULL
;
509 (dnsRecord
->Flags
.S
.Section
== DnsSectionAnswer
) &&
510 (dnsRecord
->wType
== DNS_TYPE_MX
) &&
511 (strlen(dnsRecord
->Data
.MX
.pNameExchange
) > 0)
513 record
= new MXRecord();
514 record
->hostName
= PString(dnsRecord
->Data
.MX
.pNameExchange
);
515 record
->preference
= dnsRecord
->Data
.MX
.wPreference
;
517 // see if any A records match this hostname
518 PDNS_RECORD aRecord
= results
;
519 while (aRecord
!= NULL
) {
520 if ((dnsRecord
->Flags
.S
.Section
== DnsSectionAddtional
) && (dnsRecord
->wType
== DNS_TYPE_A
)) {
521 record
->hostAddress
= PIPSocket::Address(dnsRecord
->Data
.A
.IpAddress
);
524 aRecord
= aRecord
->pNext
;
527 // if no A record found, then get address the hard way
529 PIPSocket::GetHostAddress(record
->hostName
, record
->hostAddress
);
535 void PDNS::MXRecordList::PrintOn(ostream
& strm
) const
538 for (i
= 0; i
< GetSize(); i
++)
539 strm
<< (*this)[i
] << endl
;
542 PDNS::MXRecord
* PDNS::MXRecordList::GetFirst()
545 for (i
= 0; i
< GetSize(); i
++)
546 (*this)[i
].used
= FALSE
;
553 PDNS::MXRecord
* PDNS::MXRecordList::GetNext()
558 if (lastIndex
>= GetSize())
561 return (PDNS::MXRecord
*)GetAt(lastIndex
++);
567 // End Of File ///////////////////////////////////////////////////////////////