Resolve chained CNAME records
[reactos.git] / reactos / lib / 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;
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 0 );
67 if( adns_error != adns_s_ok ) {
68 return DnsIntTranslateAdnsToDNS_STATUS( adns_error );
69 }
70
71 /*
72 * adns doesn't resolve chained CNAME records (a CNAME which points to
73 * another CNAME pointing to another... pointing to an A record), according
74 * to a mailing list thread the authors believe that chained CNAME records
75 * are invalid and the DNS entries should be fixed. That's a nice academic
76 * standpoint, but there certainly are chained CNAME records out there,
77 * even some fairly major ones (at the time of this writing
78 * download.mozilla.org is a chained CNAME). Everyone else seems to resolve
79 * these fine, so we should too. So we loop here to try to resolve CNAME
80 * chains ourselves. Of course, there must be a limit to protect against
81 * CNAME loops.
82 */
83
84 #define CNAME_LOOP_MAX 16
85
86 CurrentName = (LPSTR) Name;
87 for ( CNameLoop = 0; CNameLoop < CNAME_LOOP_MAX; CNameLoop++ ) {
88 adns_error = adns_synchronous( astate,
89 CurrentName,
90 adns_r_addr,
91 quflags,
92 &answer );
93
94 if( adns_error != adns_s_ok ) {
95 adns_finish( astate );
96 if ( CurrentName != Name ) {
97 RtlFreeHeap( CurrentName, 0, GetProcessHeap() );
98 }
99 return DnsIntTranslateAdnsToDNS_STATUS( adns_error );
100 }
101
102 if( answer && answer->rrs.addr ) {
103 if ( CurrentName != Name ) {
104 RtlFreeHeap( CurrentName, 0, GetProcessHeap() );
105 }
106 *QueryResultSet =
107 (PDNS_RECORD)RtlAllocateHeap( RtlGetProcessHeap(), 0,
108 sizeof( DNS_RECORD ) );
109 if ( NULL == *QueryResultSet ) {
110 adns_finish( astate );
111 return ERROR_OUTOFMEMORY;
112 }
113 (*QueryResultSet)->pNext = NULL;
114 (*QueryResultSet)->wType = Type;
115 (*QueryResultSet)->wDataLength = sizeof(DNS_A_DATA);
116 (*QueryResultSet)->Data.A.IpAddress =
117 answer->rrs.addr->addr.inet.sin_addr.s_addr;
118 adns_finish( astate );
119 (*QueryResultSet)->pName = xstrsave( Name );
120 return NULL != (*QueryResultSet)->pName ? ERROR_SUCCESS :
121 ERROR_OUTOFMEMORY;
122 }
123 if ( NULL == answer || adns_s_prohibitedcname != answer->status ||
124 NULL == answer->cname ) {
125 adns_finish( astate );
126 if ( CurrentName != Name ) {
127 RtlFreeHeap( CurrentName, 0, GetProcessHeap() );
128 }
129 return ERROR_FILE_NOT_FOUND;
130 }
131 if ( CurrentName != Name ) {
132 RtlFreeHeap( CurrentName, 0, GetProcessHeap() );
133 }
134 CurrentName = xstrsave( answer->cname );
135 if ( NULL == CurrentName ) {
136 adns_finish( astate );
137 return ERROR_OUTOFMEMORY;
138 }
139 }
140 adns_finish( astate );
141 RtlFreeHeap( CurrentName, 0, GetProcessHeap() );
142 return ERROR_FILE_NOT_FOUND;
143 default:
144 return ERROR_OUTOFMEMORY; /* XXX arty: find a better error code. */
145 }
146 }
147
148 static PCHAR DnsWToC( const WCHAR *WideString ) {
149 int chars = wcstombs( NULL, WideString, 0 );
150 PCHAR out = RtlAllocateHeap( RtlGetProcessHeap(), 0, chars + 1 );
151 wcstombs( out, WideString, chars + 1 );
152 return out;
153 }
154
155 static PWCHAR DnsCToW( const CHAR *NarrowString ) {
156 int chars = mbstowcs( NULL, NarrowString, 0 );
157 PWCHAR out = RtlAllocateHeap( RtlGetProcessHeap(), 0,
158 (chars + 1) * sizeof(WCHAR) );
159 mbstowcs( out, NarrowString, chars + 1 );
160 return out;
161 }
162
163 DNS_STATUS WINAPI DnsQuery_W
164 ( LPCWSTR Name,
165 WORD Type,
166 DWORD Options,
167 PIP4_ARRAY Servers,
168 PDNS_RECORD *QueryResultSet,
169 PVOID *Reserved ) {
170 UINT i;
171 PCHAR Buffer;
172 DNS_STATUS Status;
173 PDNS_RECORD QueryResultWide;
174 PDNS_RECORD ConvertedRecord = 0, LastRecord = 0;
175
176 Buffer = DnsWToC( Name );
177
178 Status = DnsQuery_A( Buffer, Type, Options, Servers, &QueryResultWide,
179 Reserved );
180
181 while( Status == ERROR_SUCCESS && QueryResultWide ) {
182 switch( QueryResultWide->wType ) {
183 case DNS_TYPE_A:
184 case DNS_TYPE_WKS:
185 #ifndef __USE_W32API
186 case DNS_TYPE_AAAA:
187 case DNS_TYPE_KEY:
188 #endif
189 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
190 sizeof(DNS_RECORD) );
191 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
192 ConvertedRecord->wType = QueryResultWide->wType;
193 ConvertedRecord->wDataLength = QueryResultWide->wDataLength;
194 memcpy( ConvertedRecord, QueryResultWide,
195 QueryResultWide->wDataLength );
196 break;
197
198 case DNS_TYPE_CNAME:
199 case DNS_TYPE_PTR:
200 case DNS_TYPE_NS:
201 case DNS_TYPE_MB:
202 case DNS_TYPE_MD:
203 case DNS_TYPE_MF:
204 case DNS_TYPE_MG:
205 case DNS_TYPE_MR:
206 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
207 sizeof(DNS_RECORD) );
208 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
209 ConvertedRecord->wType = QueryResultWide->wType;
210 ConvertedRecord->wDataLength = sizeof(DNS_PTR_DATA);
211 ConvertedRecord->Data.PTR.pNameHost =
212 (PCHAR)DnsCToW( QueryResultWide->Data.PTR.pNameHost );
213 break;
214 case DNS_TYPE_MINFO:
215 #ifndef __USE_W32API
216 case DNS_TYPE_RP:
217 #endif
218 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
219 sizeof(DNS_RECORD) );
220 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
221 ConvertedRecord->wType = QueryResultWide->wType;
222 ConvertedRecord->wDataLength = sizeof(DNS_MINFO_DATA);
223 ConvertedRecord->Data.MINFO.pNameMailbox =
224 (PCHAR)DnsCToW( QueryResultWide->Data.MINFO.pNameMailbox );
225 ConvertedRecord->Data.MINFO.pNameErrorsMailbox =
226 (PCHAR)DnsCToW( QueryResultWide->Data.MINFO.pNameErrorsMailbox );
227 break;
228
229 case DNS_TYPE_MX:
230 #ifndef __USE_W32API
231 case DNS_TYPE_AFSDB:
232 case DNS_TYPE_RT:
233 #endif
234 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
235 sizeof(DNS_RECORD) );
236 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
237 ConvertedRecord->wType = QueryResultWide->wType;
238 ConvertedRecord->wDataLength = sizeof(DNS_MX_DATA);
239 ConvertedRecord->Data.MX.pNameExchange =
240 (PCHAR)DnsCToW( QueryResultWide->Data.MX.pNameExchange );
241 ConvertedRecord->Data.MX.wPreference =
242 QueryResultWide->Data.MX.wPreference;
243 break;
244
245 #ifndef __USE_W32API
246 case DNS_TYPE_TXT:
247 case DNS_TYPE_ISDN:
248 #endif
249 case DNS_TYPE_HINFO:
250 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
251 sizeof(DNS_TXT_DATA) +
252 QueryResultWide->
253 Data.TXT.dwStringCount );
254 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
255 ConvertedRecord->wType = QueryResultWide->wType;
256 ConvertedRecord->wDataLength =
257 sizeof(DNS_TXT_DATA) +
258 (sizeof(PWCHAR) * QueryResultWide->Data.TXT.dwStringCount);
259 ConvertedRecord->Data.TXT.dwStringCount =
260 QueryResultWide->Data.TXT.dwStringCount;
261 for( i = 0; i < ConvertedRecord->Data.TXT.dwStringCount; i++ ) {
262 ConvertedRecord->Data.TXT.pStringArray[i] =
263 (PCHAR)DnsCToW( QueryResultWide->Data.TXT.pStringArray[i] );
264 }
265 break;
266
267 case DNS_TYPE_NULL:
268 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
269 sizeof(DNS_NULL_DATA) +
270 QueryResultWide->
271 Data.Null.dwByteCount );
272 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
273 ConvertedRecord->wType = QueryResultWide->wType;
274 ConvertedRecord->wDataLength =
275 sizeof(DNS_NULL_DATA) + QueryResultWide->Data.Null.dwByteCount;
276 ConvertedRecord->Data.Null.dwByteCount =
277 QueryResultWide->Data.Null.dwByteCount;
278 memcpy( &ConvertedRecord->Data.Null.Data,
279 &QueryResultWide->Data.Null.Data,
280 QueryResultWide->Data.Null.dwByteCount );
281 break;
282
283 #ifndef __USE_W32API
284 case DNS_TYPE_SIG:
285 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
286 sizeof(DNS_RECORDA) );
287 ConvertedRecord->pName = DnsCToW( QueryResultWide->pName );
288 ConvertedRecord->wType = QueryResultWide->wType;
289 ConvertedRecord->wDataLength = sizeof(DNS_SIG_DATAA);
290 memcpy( &ConvertedRecord->Data.SIG,
291 &QueryResultWide->Data.SIG,
292 sizeof(QueryResultWide->Data.SIG) );
293 ConvertedRecord->Data.SIG.pNameSigner =
294 DnsCToW( QueryResultWide->Data.SIG.pNameSigner );
295 break;
296
297 case DNS_TYPE_NXT:
298 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
299 sizeof(DNS_RECORDA) );
300 ConvertedRecord->pName = DnsCToW( QueryResultWide->pName );
301 ConvertedRecord->wType = QueryResultWide->wType;
302 ConvertedRecord->wDataLength = sizeof(DNS_NXT_DATAA);
303 memcpy( &ConvertedRecord->Data.NXT,
304 &QueryResultWide->Data.NXT,
305 sizeof(QueryResultWide->Data.NXT) );
306 ConvertedRecord->Data.NXT.pNameNext =
307 DnsCToW( QueryResultWide->Data.NXT.pNameNext );
308 break;
309
310 case DNS_TYPE_SRV:
311 ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0,
312 sizeof(DNS_RECORDA) );
313 ConvertedRecord->pName = DnsCToW( QueryResultWide->pName );
314 ConvertedRecord->wType = QueryResultWide->wType;
315 ConvertedRecord->wDataLength = sizeof(DNS_SRV_DATAA);
316 memcpy( &ConvertedRecord->Data.SRV,
317 &QueryResultWide->Data.SRV,
318 sizeof(QueryResultWide->Data.SRV) );
319 ConvertedRecord->Data.SRV.pNameTarget =
320 DnsCToW( QueryResultWide->Data.SRV.pNameTarget );
321 break;
322 #endif
323 }
324
325 if( LastRecord ) {
326 LastRecord->pNext = ConvertedRecord;
327 LastRecord = LastRecord->pNext;
328 } else {
329 LastRecord = *QueryResultSet = ConvertedRecord;
330 }
331 }
332
333 LastRecord->pNext = 0;
334
335 /* The name */
336 RtlFreeHeap( RtlGetProcessHeap(), 0, Buffer );
337
338 return Status;
339 }
340
341 DNS_STATUS WINAPI DnsQuery_UTF8
342 ( LPCSTR Name,
343 WORD Type,
344 DWORD Options,
345 PIP4_ARRAY Servers,
346 PDNS_RECORD *QueryResultSet,
347 PVOID *Reserved ) {
348 return DnsQuery_UTF8( Name, Type, Options, Servers, QueryResultSet,
349 Reserved );
350 }
351
352 void DnsIntFreeRecordList( PDNS_RECORD ToDelete ) {
353 UINT i;
354 PDNS_RECORD next = 0;
355
356 while( ToDelete ) {
357 if( ToDelete->pName )
358 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->pName );
359 switch( ToDelete->wType ) {
360 case DNS_TYPE_CNAME:
361 case DNS_TYPE_PTR:
362 case DNS_TYPE_NS:
363 case DNS_TYPE_MB:
364 case DNS_TYPE_MD:
365 case DNS_TYPE_MF:
366 case DNS_TYPE_MG:
367 case DNS_TYPE_MR:
368 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.PTR.pNameHost );
369 break;
370 case DNS_TYPE_MINFO:
371 #ifndef __USE_W32API
372 case DNS_TYPE_RP:
373 RtlFreeHeap( RtlGetProcessHeap(), 0,
374 ToDelete->Data.MINFO.pNameMailbox );
375 RtlFreeHeap( RtlGetProcessHeap(), 0,
376 ToDelete->Data.MINFO.pNameErrorsMailbox );
377 break;
378
379 case DNS_TYPE_AFSDB:
380 case DNS_TYPE_RT:
381 #endif
382 case DNS_TYPE_MX:
383 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.MX.pNameExchange );
384 break;
385
386 #ifndef __USE_W32API
387 case DNS_TYPE_TXT:
388 case DNS_TYPE_ISDN:
389 #endif
390 case DNS_TYPE_HINFO:
391 for( i = 0; i < ToDelete->Data.TXT.dwStringCount; i++ ) {
392 RtlFreeHeap( RtlGetProcessHeap(), 0,
393 ToDelete->Data.TXT.pStringArray[i] );
394 }
395 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.TXT.pStringArray );
396 break;
397
398 #ifndef __USE_W32API
399 case DNS_TYPE_SIG:
400 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.SIG.pNameSigner );
401 break;
402
403 case DNS_TYPE_NXT:
404 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.NXT.pNameNext );
405 break;
406
407 case DNS_TYPE_SRV:
408 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.SRV.pNameTarget );
409 break;
410 #endif
411 }
412
413 next = ToDelete->pNext;
414 RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete );
415 ToDelete = next;
416 }
417 }