1 /*
2  * pdns.h
3  *
4  * PWLib library for DNS lookup services
5  *
6  * Portable Windows Library
7  *
8  * Copyright (c) 2003 Equivalence Pty. Ltd.
9  *
10  * The contents of this file are subject to the Mozilla Public License
11  * Version 1.0 (the "License"); you may not use this file except in
12  * compliance with the License. You may obtain a copy of the License at
13  * http://www.mozilla.org/MPL/
14  *
15  * Software distributed under the License is distributed on an "AS IS"
16  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17  * the License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * The Original Code is Portable Windows Library.
21  *
22  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23  *
24  * Contributor(s): ______________________________________.
25  *
26  * $Revision: 28568 $
27  * $Author: rjongbloed $
28  * $Date: 2012-11-22 00:16:50 -0600 (Thu, 22 Nov 2012) $
29  */
30 
31 #ifndef PTLIB_PDNS_H
32 #define PTLIB_PDNS_H
33 
34 #if P_DNS
35 
36 #ifdef P_USE_PRAGMA
37 #pragma interface
38 #endif
39 
40 #include <ptlib/sockets.h>
41 
42 #include <ptclib/random.h>
43 #include <ptclib/url.h>
44 
45 #if defined(_WIN32)
46 
47   #include <windns.h>
48   #include <ntverp.h>
49 
50   // Accommodate spelling error in windns.h
51   enum { DnsSectionAdditional = DnsSectionAddtional };
52 
53   #if VER_PRODUCTBUILD < 6000
54     typedef struct
55     {
56         WORD            wOrder;
57         WORD            wPreference;
58         PSTR            pFlags;
59         PSTR            pService;
60         PSTR            pRegularExpression;
61         PSTR            pReplacement;
62     }
63     DNS_NAPTR_DATA;
64   #endif
65 
66 #else /* _WIN32 */
67 
68   #define  P_HAS_RESOLVER 1         // set if using Unix-style DNS routines
69   #include <arpa/nameser.h>
70   #include <resolv.h>
71   #if defined(P_MACOSX) && (P_MACOSX >= 700)
72     #include <arpa/nameser_compat.h>
73   #endif
74 
75 #endif  // _WIN32
76 
77 
78 #ifdef P_HAS_RESOLVER
79 
80 //////////////////////////////////////////////////////////////////////////
81 //
82 // these classes provide an emulation of the Microsoft DNS API
83 // on non-Window systems
84 //
85 
86 #ifndef T_SRV
87 #define T_SRV   33
88 #endif
89 
90 #ifndef T_NAPTR
91 #define T_NAPTR   35
92 #endif
93 
94 
95 #define DNS_STATUS  int
96 #define DNS_TYPE_SRV  T_SRV
97 #define DNS_TYPE_MX  T_MX
98 #define DNS_TYPE_A  T_A
99 #define DNS_TYPE_AAAA  T_AAAA
100 #define DNS_TYPE_NAPTR  T_NAPTR
101 #define DnsFreeRecordList 1
102 #define DNS_QUERY_STANDARD 0
103 #define DNS_QUERY_BYPASS_CACHE 0
104 
105 typedef struct _DnsAData {
106   DWORD IpAddress;
107 } DNS_A_DATA;
108 
109 typedef struct _DnsAAAAData {
110   DWORD Ip6Address[4];
111 } DNS_AAAA_DATA;
112 
113 typedef struct {
114   char   pNameExchange[MAXDNAME];
115   WORD   wPreference;
116 } DNS_MX_DATA;
117 
118 typedef struct {
119   char pNameHost[MAXDNAME];
120 } DNS_PTR_DATA;
121 
122 typedef struct _DnsSRVData {
123   char   pNameTarget[MAXDNAME];
124   WORD   wPriority;
125   WORD   wWeight;
126   WORD   wPort;
127 } DNS_SRV_DATA;
128 
129 typedef struct _DnsNULLData {
130   DWORD  dwByteCount;
131   char   data[1];
132 } DNS_NULL_DATA;
133 
134 typedef struct _DnsRecordFlags
135 {
136   unsigned   Section     : 2;
137   unsigned   Delete      : 1;
138   unsigned   CharSet     : 2;
139   unsigned   Unused      : 3;
140   unsigned   Reserved    : 24;
141 } DNS_RECORD_FLAGS;
142 
143 typedef enum _DnsSection
144 {
145   DnsSectionQuestion,
146   DnsSectionAnswer,
147   DnsSectionAuthority,
148   DnsSectionAdditional,
149 } DNS_SECTION;
150 
151 
152 class DnsRecord {
153   public:
154     DnsRecord * pNext;
155     char        pName[MAXDNAME];
156     WORD        wType;
157     WORD        wDataLength;
158 
159     union {
160       DWORD               DW;     ///< flags as DWORD
161       DNS_RECORD_FLAGS    S;      ///< flags as structure
162     } Flags;
163 
164     union {
165       DNS_A_DATA     A;
166       DNS_AAAA_DATA  AAAA;
167       DNS_MX_DATA    MX;
168       DNS_PTR_DATA   NS;
169       DNS_SRV_DATA   SRV;
170       DNS_NULL_DATA  Null;
171     } Data;
172 };
173 
174 typedef DnsRecord DNS_RECORD;
175 typedef DnsRecord * PDNS_RECORD;
176 
177 
178 extern void DnsRecordListFree(PDNS_RECORD rec, int FreeType);
179 extern PDNS_RECORD DnsRecordSetCopy(PDNS_RECORD src);
180 
181 extern DNS_STATUS DnsQuery_A(const char * service,
182           WORD requestType,
183           DWORD options,
184           void *,
185           PDNS_RECORD * results,
186           void *);
187 
188 
189 #endif // P_HAS_RESOLVER
190 
191 namespace PDNS {
192 
193 ///////////////////////////////////////////////////////////////////////////
194 
195 DNS_STATUS Cached_DnsQuery(
196     const char * name,
197     WORD       type,
198     DWORD      options,
199     void *     extra,
200     PDNS_RECORD * queryResults,
201     void * reserved
202 );
203 
204 
205 
206 //////////////////////////////////////////////////////////////////////////
207 //
208 //  this template automates the creation of a list of records for
209 //  a specific type of DNS lookup
210 //
211 
212 template <unsigned type, class RecordListType, class RecordType>
Lookup(const PString & name,RecordListType & recordList)213 PBoolean Lookup(const PString & name, RecordListType & recordList)
214 {
215   if (name.IsEmpty())
216     return false;
217 
218   recordList.RemoveAll();
219 
220   PDNS_RECORD results = NULL;
221   DNS_STATUS status = Cached_DnsQuery((const char *)name,
222                                       type,
223                                       DNS_QUERY_STANDARD,
224                                       NULL,
225                                       &results,
226                                       NULL);
227   if (status != 0)
228     return false;
229 
230   // find records matching the correct type
231   PDNS_RECORD dnsRecord = results;
232   while (dnsRecord != NULL) {
233     RecordType * record = recordList.HandleDNSRecord(dnsRecord, results);
234     if (record != NULL)
235       recordList.Append(record);
236     dnsRecord = dnsRecord->pNext;
237   }
238 
239   if (results != NULL)
240     DnsRecordListFree(results, DnsFreeRecordList);
241 
242   return recordList.GetSize() != 0;
243 }
244 
245 /////////////////////////////////////////////////////////////
246 
247 class SRVRecord : public PObject
248 {
249   PCLASSINFO(SRVRecord, PObject);
250   public:
SRVRecord()251     SRVRecord()
252     { used = false; }
253 
254     Comparison Compare(const PObject & obj) const;
255     void PrintOn(ostream & strm) const;
256 
257     PString            hostName;
258     PIPSocket::Address hostAddress;
259     PBoolean               used;
260     WORD port;
261     WORD priority;
262     WORD weight;
263 };
264 
265 PDECLARE_SORTED_LIST(SRVRecordList, PDNS::SRVRecord)
266   public:
267     void PrintOn(ostream & strm) const;
268 
269     SRVRecord * GetFirst();
270     SRVRecord * GetNext();
271 
272     PDNS::SRVRecord * HandleDNSRecord(PDNS_RECORD dnsRecord, PDNS_RECORD results);
273 
274   protected:
275     PINDEX     priPos;
276     PWORDArray priList;
277 };
278 
279 /**
280   * return a list of DNS SRV record with the specified service type
281   */
282 
GetRecords(const PString & service,SRVRecordList & serviceList)283 inline PBoolean GetRecords(const PString & service, SRVRecordList & serviceList)
284 { return Lookup<DNS_TYPE_SRV, SRVRecordList, SRVRecord>(service, serviceList); }
285 
286 /**
287   * provided for backwards compatibility
288   */
GetSRVRecords(const PString & service,SRVRecordList & serviceList)289 inline PBoolean GetSRVRecords(
290       const PString & service,
291       SRVRecordList & serviceList
292 )
293 { return GetRecords(service, serviceList); }
294 
295 /**
296   * return a list of DNS SRV record with the specified service, type and domain
297   */
298 
299 PBoolean GetSRVRecords(
300       const PString & service,
301       const PString & type,
302       const PString & domain,
303       SRVRecordList & serviceList
304 );
305 
306 /**
307   * Perform a DNS lookup of the specified service
308   * @return true if the service could be resolved, else false
309   */
310 
311 PBoolean LookupSRV(
312          const PString & srvQuery,
313          WORD defaultPort,
314          PIPSocketAddressAndPortVector & addrList
315 );
316 
317 PBoolean LookupSRV(
318          const PString & domain,                  ///< domain to lookup
319          const PString & service,                 ///< service to use
320          WORD defaultPort,                        ///< default por to use
321          PIPSocketAddressAndPortVector & addrList ///< returned list of sockets and ports
322 );
323 
324 PBoolean LookupSRV(
325          const PURL & url,          ///< URL to lookup
326          const PString & service,   ///< service to use
327          PStringList & returnStr    ///< resolved addresses, if return value is true
328 );
329 
330 ////////////////////////////////////////////////////////////////
331 
332 class MXRecord : public PObject
333 {
334   PCLASSINFO(MXRecord, PObject);
335   public:
MXRecord()336     MXRecord()
337     { used = false; }
338     Comparison Compare(const PObject & obj) const;
339     void PrintOn(ostream & strm) const;
340 
341     PString            hostName;
342     PIPSocket::Address hostAddress;
343     PBoolean               used;
344     WORD               preference;
345 };
346 
347 PDECLARE_SORTED_LIST(MXRecordList, PDNS::MXRecord)
348   public:
349     void PrintOn(ostream & strm) const;
350 
351     MXRecord * GetFirst();
352     MXRecord * GetNext();
353 
354     PDNS::MXRecord * HandleDNSRecord(PDNS_RECORD dnsRecord, PDNS_RECORD results);
355 
356   protected:
357     PINDEX lastIndex;
358 };
359 
360 /**
361   * return a list of MX records for the specified domain
362   */
363 inline PBoolean GetRecords(
364       const PString & domain,
365       MXRecordList & serviceList
366 )
367 { return Lookup<DNS_TYPE_MX, MXRecordList, MXRecord>(domain, serviceList); }
368 
369 /**
370   * provided for backwards compatibility
371   */
372 inline PBoolean GetMXRecords(
373       const PString & domain,
374       MXRecordList & serviceList
375 )
376 {
377   return GetRecords(domain, serviceList);
378 }
379 
380 
381 }; // namespace PDNS
382 
383 #endif // P_DNS
384 
385 #endif // PTLIB_PDNS_H
386 
387 
388 // End Of File ///////////////////////////////////////////////////////////////
389