6d559fd364efb0a502f99d97771ce70468cf4d24
[reactos.git] / reactos / dll / win32 / dnsapi / dnsapi / query.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/dnsapi/dnsapi/query.c
5 * PURPOSE: DNSAPI functions built on the ADNS library.
6 * PROGRAMER: Art Yerkes
7 * UPDATE HISTORY:
8 * 12/15/03 -- Created
9 */
10
11 #include "precomp.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* DnsQuery ****************************
17 * Begin a DNS query, and allow the result to be placed in the application
18 * supplied result pointer. The result can be manipulated with the record
19 * functions.
20 *
21 * Name -- The DNS object to be queried.
22 * Type -- The type of records to be returned. These are
23 * listed in windns.h
24 * Options -- Query options. DNS_QUERY_STANDARD is the base
25 * state, and every other option takes precedence.
26 * multiple options can be combined. Listed in
27 * windns.h
28 * Servers -- List of alternate servers (optional)
29 * QueryResultSet -- Pointer to the result pointer that will be filled
30 * when the response is available.
31 * Reserved -- Response as it appears on the wire. Optional.
32 */
33
34 char *xstrsave(const char *str) {
35 char *p;
36
37 p = RtlAllocateHeap( RtlGetProcessHeap(), 0, strlen(str)+1 );
38 if ( NULL != p ) {
39 strcpy(p,str);
40 }
41 return p;
42 }
43
44 DNS_STATUS WINAPI DnsQuery_A
45 ( LPCSTR Name,
46 WORD Type,
47 DWORD Options,
48 PIP4_ARRAY Servers,
49 PDNS_RECORD *QueryResultSet,
50 PVOID *Reserved ) {
51 adns_state astate;
52 int quflags = 0, i;
53 int adns_error;
54 adns_answer *answer;
55 LPSTR CurrentName;
56 unsigned CNameLoop;
57
58 *QueryResultSet = 0;
59
60 switch( Type ) {
61 case DNS_TYPE_A:
62 adns_error = adns_init( &astate,
63 adns_if_noenv |
64 adns_if_noerrprint |
65 adns_if_noserverwarn |
66 (Servers ? adns_if_noserver : 0),
67 0 );
68
69 if( adns_error != adns_s_ok ) {
70 return DnsIntTranslateAdnsToDNS_STATUS( adns_error );
71 }
72
73 if (Servers) {
74 for( i = 0; i < Servers->AddrCount; i++ ) {
75 adns_addserver( astate, *((struct in_addr *)&Servers->AddrArray[i]) );
76 }
77 }
78
79 /*
80 * adns doesn't resolve chained CNAME records (a CNAME which points to
81 * another CNAME pointing to another... pointing to an A record), according
82 * to a mailing list thread the authors believe that chained CNAME records
83 * are invalid and the DNS entries should be fixed. That's a nice academic
84 * standpoint, but there certainly are chained CNAME records out there,
85 * even some fairly major ones (at the time of this writing
86 * download.mozilla.org is a chained CNAME). Everyone else seems to resolve
87 * these fine, so we should too. So we loop here to try to resolve CNAME
88 * chains ourselves. Of course, there must be a limit to protect against
89 * CNAME loops.
90 */
91
92 #define CNAME_LOOP_MAX 16
93
94 CurrentName = (LPSTR) Name;
95 for ( CNameLoop = 0; CNameLoop < CNAME_LOOP_MAX; CNameLoop++ ) {
96 adns_error = adns_synchronous( astate,
97 CurrentName,
98 adns_r_addr,
99 quflags,
100 &answer );
101
102 if( adns_error != adns_s_ok ) {
103 adns_finish( astate );
104 if ( CurrentName != Name ) {
105 RtlFreeHeap( RtlGetProcessHeap(), 0, CurrentName );
106 }
107 return DnsIntTranslateAdnsToDNS_STATUS( adns_error );
108 }
109
110 if( answer && answer->rrs.addr ) {
111 if ( CurrentName != Name ) {
112 RtlFreeHeap( RtlGetProcessHeap(), 0, CurrentName );
113 }
114 *QueryResultSet =
115 (PDNS_RECORD)RtlAllocateHeap( RtlGetProcessHeap(), 0,
116 sizeof( DNS_RECORD ) );
117 if ( NULL == *QueryResultSet ) {
118 adns_finish( astate );
119 return ERROR_OUTOFMEMORY;
120 }
121 (*QueryResultSet)->pNext = NULL;
122 (*QueryResultSet)->wType = Type;
123 (*QueryResultSet)->wDataLength = sizeof(DNS_A_DATA);
124 (*QueryResultSet)->Data.A.IpAddress =
125 answer->rrs.addr->addr.inet.sin_addr.s_addr;
126 adns_finish( astate );
127 (*QueryResultSet)->pName = xstrsave( Name );
128 return NULL != (*QueryResultSet)->pName ? ERROR_SUCCESS :
129 ERROR_OUTOFMEMORY;
130 }
131 if ( NULL == answer || adns_s_prohibitedcname != answer->status ||
132 NULL == answer->cname ) {
133 adns_finish( astate );
134 if ( CurrentName != Name ) {
135 RtlFreeHeap( RtlGetProcessHeap(), 0, CurrentName );
136 }
137 return ERROR_FILE_NOT_FOUND;
138 }
139 if ( CurrentName != Name ) {
140 RtlFreeHeap( RtlGetProcessHeap(), 0, CurrentName );
141 }
142 CurrentName = xstrsave( answer->cname );
143 if ( NULL == CurrentName ) {
144 adns_finish( astate );
145 return ERROR_OUTOFMEMORY;
146 }
147 }
148 adns_finish( astate );
149 RtlFreeHeap( RtlGetProcessHeap(), 0, CurrentName );
150 return ERROR_FILE_NOT_FOUND;
151 default:
152 return ERROR_OUTOFMEMORY; /* XXX arty: find a better error code. */
153 }
154 }
155
156 static PCHAR DnsWToC( const WCHAR *WideString ) {
157 int chars = wcstombs( NULL, WideString, 0 );
158 PCHAR out = RtlAllocateHeap( RtlGetProcessHeap(), 0, chars + 1 );
159 wcstombs( out, WideString, chars + 1 );
160 return out;
161 }
162
163 static PWCHAR DnsCToW( const CHAR *NarrowString ) {
164 int chars = mbstowcs( NULL, NarrowString, 0 );
165 PWCHAR out = RtlAllocateHeap( RtlGetProcessHeap(), 0,
166 (chars + 1) * sizeof(WCHAR) );
167 mbstowcs( out, NarrowString, chars + 1 );
168 return out;
169 }
170
171 DNS_STATUS WINAPI DnsQuery_W
172 ( LPCWSTR Name,
173 WORD Type,
174 DWORD Options,
175 PIP4_ARRAY Servers,
176 PDNS_RECORD *QueryResultSet,
177 PVOID *Reserved ) {
178 UINT i;
179 PCHAR Buffer;
180 DNS_STATUS Status;
181 PDNS_RECORD QueryResultWide;
182 PDNS_RECORD ConvertedRecord = 0, LastRecord = 0;
183
184 Buffer = DnsWToC( Name );
185
186 Status = DnsQuery_A( Buffer, Type, Options, Servers, &QueryResultWide,
187 Reserved );
188
189 while( Status == ERROR_SUCCESS && QueryResultWide ) {
190 switch( QueryResultWide->wType ) {
191 case DNS_TYPE_A:
192 case DNS_TYPE_WKS:
193 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
194 sizeof(DNS_RECORD) );
195 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
196 ConvertedRecord->wType = QueryResultWide->wType;
197 ConvertedRecord->wDataLength = QueryResultWide->wDataLength;
198 memcpy( ConvertedRecord, QueryResultWide,
199 QueryResultWide->wDataLength );
200 break;
201
202 case DNS_TYPE_CNAME:
203 case DNS_TYPE_PTR:
204 case DNS_TYPE_NS:
205 case DNS_TYPE_MB:
206 case DNS_TYPE_MD:
207 case DNS_TYPE_MF:
208 case DNS_TYPE_MG:
209 case DNS_TYPE_MR:
210 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
211 sizeof(DNS_RECORD) );
212 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
213 ConvertedRecord->wType = QueryResultWide->wType;
214 ConvertedRecord->wDataLength = sizeof(DNS_PTR_DATA);
215 ConvertedRecord->Data.PTR.pNameHost =
216 (PCHAR)DnsCToW( QueryResultWide->Data.PTR.pNameHost );
217 break;
218 case DNS_TYPE_MINFO:
219 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
220 sizeof(DNS_RECORD) );
221 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
222 ConvertedRecord->wType = QueryResultWide->wType;
223 ConvertedRecord->wDataLength = sizeof(DNS_MINFO_DATA);
224 ConvertedRecord->Data.MINFO.pNameMailbox =
225 (PCHAR)DnsCToW( QueryResultWide->Data.MINFO.pNameMailbox );
226 ConvertedRecord->Data.MINFO.pNameErrorsMailbox =
227 (PCHAR)DnsCToW( QueryResultWide->Data.MINFO.pNameErrorsMailbox );
228 break;
229
230 case DNS_TYPE_MX:
231 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
232 sizeof(DNS_RECORD) );
233 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
234 ConvertedRecord->wType = QueryResultWide->wType;
235 ConvertedRecord->wDataLength = sizeof(DNS_MX_DATA);
236 ConvertedRecord->Data.MX.pNameExchange =
237 (PCHAR)DnsCToW( QueryResultWide->Data.MX.pNameExchange );
238 ConvertedRecord->Data.MX.wPreference =
239 QueryResultWide->Data.MX.wPreference;
240 break;
241
242 case DNS_TYPE_HINFO:
243 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
244 sizeof(DNS_TXT_DATA) +
245 QueryResultWide->
246 Data.TXT.dwStringCount );
247 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
248 ConvertedRecord->wType = QueryResultWide->wType;
249 ConvertedRecord->wDataLength =
250 sizeof(DNS_TXT_DATA) +
251 (sizeof(PWCHAR) * QueryResultWide->Data.TXT.dwStringCount);
252 ConvertedRecord->Data.TXT.dwStringCount =
253 QueryResultWide->Data.TXT.dwStringCount;
254 for( i = 0; i < ConvertedRecord->Data.TXT.dwStringCount; i++ ) {
255 ConvertedRecord->Data.TXT.pStringArray[i] =
256 (PCHAR)DnsCToW( QueryResultWide->Data.TXT.pStringArray[i] );
257 }
258 break;
259
260 case DNS_TYPE_NULL:
261 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
262 sizeof(DNS_NULL_DATA) +
263 QueryResultWide->
264 Data.Null.dwByteCount );
265 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
266 ConvertedRecord->wType = QueryResultWide->wType;
267 ConvertedRecord->wDataLength =
268 sizeof(DNS_NULL_DATA) + QueryResultWide->Data.Null.dwByteCount;
269 ConvertedRecord->Data.Null.dwByteCount =
270 QueryResultWide->Data.Null.dwByteCount;
271 memcpy( &ConvertedRecord->Data.Null.Data,
272 &QueryResultWide->Data.Null.Data,
273 QueryResultWide->Data.Null.dwByteCount );
274 break;
275
276 }
277
278 if( LastRecord ) {
279 LastRecord->pNext = ConvertedRecord;
280 LastRecord = LastRecord->pNext;
281 } else {
282 LastRecord = *QueryResultSet = ConvertedRecord;
283 }
284 }
285
286 LastRecord->pNext = 0;
287
288 /* The name */
289 RtlFreeHeap( RtlGetProcessHeap(), 0, Buffer );
290
291 return Status;
292 }
293
294 DNS_STATUS WINAPI DnsQuery_UTF8
295 ( LPCSTR Name,
296 WORD Type,
297 DWORD Options,
298 PIP4_ARRAY Servers,
299 PDNS_RECORD *QueryResultSet,
300 PVOID *Reserved ) {
301 UNIMPLEMENTED;
302 return ERROR_OUTOFMEMORY;
303 }
304
305 void DnsIntFreeRecordList( PDNS_RECORD ToDelete ) {
306 UINT i;
307 PDNS_RECORD next = 0;
308
309 while( ToDelete ) {
310 if( ToDelete->pName )
311 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->pName );
312 switch( ToDelete->wType ) {
313 case DNS_TYPE_CNAME:
314 case DNS_TYPE_PTR:
315 case DNS_TYPE_NS:
316 case DNS_TYPE_MB:
317 case DNS_TYPE_MD:
318 case DNS_TYPE_MF:
319 case DNS_TYPE_MG:
320 case DNS_TYPE_MR:
321 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.PTR.pNameHost );
322 break;
323 case DNS_TYPE_MINFO:
324 case DNS_TYPE_MX:
325 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.MX.pNameExchange );
326 break;
327
328 case DNS_TYPE_HINFO:
329 for( i = 0; i < ToDelete->Data.TXT.dwStringCount; i++ ) {
330 RtlFreeHeap( RtlGetProcessHeap(), 0,
331 ToDelete->Data.TXT.pStringArray[i] );
332 }
333 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.TXT.pStringArray );
334 break;
335
336 }
337
338 next = ToDelete->pNext;
339 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete );
340 ToDelete = next;
341 }
342 }