[DNSAPI] Request adns to search servers and domains. By Peter Hater. CORE-10440
[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
35 *xstrsave(const char *str)
36 {
37 char *p;
38
39 p = RtlAllocateHeap(RtlGetProcessHeap(), 0, strlen(str) + 1);
40
41 if(p)
42 strcpy(p, str);
43
44 return p;
45 }
46
47 DNS_STATUS WINAPI
48 DnsQuery_A(LPCSTR Name,
49 WORD Type,
50 DWORD Options,
51 PIP4_ARRAY Servers,
52 PDNS_RECORD *QueryResultSet,
53 PVOID *Reserved)
54 {
55 adns_state astate;
56 int quflags = adns_qf_search;
57 int adns_error;
58 adns_answer *answer;
59 LPSTR CurrentName;
60 unsigned i, CNameLoop;
61
62 if (Name == NULL)
63 return ERROR_INVALID_PARAMETER;
64
65 *QueryResultSet = 0;
66
67 switch(Type)
68 {
69 case DNS_TYPE_A:
70 adns_error = adns_init(&astate, adns_if_noenv | adns_if_noerrprint | adns_if_noserverwarn, 0);
71
72 if(adns_error != adns_s_ok)
73 return DnsIntTranslateAdnsToDNS_STATUS(adns_error);
74
75 if (Servers)
76 {
77 for(i = 0; i < Servers->AddrCount; i++)
78 {
79 adns_addserver(astate, *((struct in_addr *)&Servers->AddrArray[i]));
80 }
81 }
82
83 /*
84 * adns doesn't resolve chained CNAME records (a CNAME which points to
85 * another CNAME pointing to another... pointing to an A record), according
86 * to a mailing list thread the authors believe that chained CNAME records
87 * are invalid and the DNS entries should be fixed. That's a nice academic
88 * standpoint, but there certainly are chained CNAME records out there,
89 * even some fairly major ones (at the time of this writing
90 * download.mozilla.org is a chained CNAME). Everyone else seems to resolve
91 * these fine, so we should too. So we loop here to try to resolve CNAME
92 * chains ourselves. Of course, there must be a limit to protect against
93 * CNAME loops.
94 */
95
96 #define CNAME_LOOP_MAX 16
97
98 CurrentName = (LPSTR) Name;
99
100 for (CNameLoop = 0; CNameLoop < CNAME_LOOP_MAX; CNameLoop++)
101 {
102 adns_error = adns_synchronous(astate, CurrentName, adns_r_addr, quflags, &answer);
103
104 if(adns_error != adns_s_ok)
105 {
106 adns_finish(astate);
107
108 if (CurrentName != Name)
109 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentName);
110
111 return DnsIntTranslateAdnsToDNS_STATUS(adns_error);
112 }
113
114 if(answer && answer->rrs.addr)
115 {
116 if (CurrentName != Name)
117 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentName);
118
119 *QueryResultSet = (PDNS_RECORD)RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
120
121 if (NULL == *QueryResultSet)
122 {
123 adns_finish( astate );
124 return ERROR_OUTOFMEMORY;
125 }
126
127 (*QueryResultSet)->pNext = NULL;
128 (*QueryResultSet)->wType = Type;
129 (*QueryResultSet)->wDataLength = sizeof(DNS_A_DATA);
130 (*QueryResultSet)->Data.A.IpAddress = answer->rrs.addr->addr.inet.sin_addr.s_addr;
131
132 adns_finish(astate);
133
134 (*QueryResultSet)->pName = xstrsave( Name );
135
136 return NULL != (*QueryResultSet)->pName ? ERROR_SUCCESS : ERROR_OUTOFMEMORY;
137 }
138
139 if (NULL == answer || adns_s_prohibitedcname != answer->status || NULL == answer->cname)
140 {
141 adns_finish(astate);
142
143 if (CurrentName != Name)
144 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentName);
145
146 return ERROR_FILE_NOT_FOUND;
147 }
148
149 if (CurrentName != Name)
150 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentName);
151
152 CurrentName = xstrsave(answer->cname);
153
154 if (!CurrentName)
155 {
156 adns_finish(astate);
157 return ERROR_OUTOFMEMORY;
158 }
159 }
160
161 adns_finish(astate);
162 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentName);
163 return ERROR_FILE_NOT_FOUND;
164
165 default:
166 return ERROR_OUTOFMEMORY; /* XXX arty: find a better error code. */
167 }
168 }
169
170 static PCHAR
171 DnsWToC(const WCHAR *WideString)
172 {
173 int chars = wcstombs(NULL, WideString, 0);
174 PCHAR out = RtlAllocateHeap(RtlGetProcessHeap(), 0, chars + 1);
175
176 wcstombs(out, WideString, chars + 1);
177
178 return out;
179 }
180
181 static PWCHAR
182 DnsCToW(const CHAR *NarrowString)
183 {
184 int chars = mbstowcs(NULL, NarrowString, 0);
185 PWCHAR out = RtlAllocateHeap(RtlGetProcessHeap(), 0, (chars + 1) * sizeof(WCHAR));
186
187 mbstowcs(out, NarrowString, chars + 1);
188
189 return out;
190 }
191
192 DNS_STATUS WINAPI
193 DnsQuery_W(LPCWSTR Name,
194 WORD Type,
195 DWORD Options,
196 PIP4_ARRAY Servers,
197 PDNS_RECORD *QueryResultSet,
198 PVOID *Reserved)
199 {
200 UINT i;
201 PCHAR Buffer;
202 DNS_STATUS Status;
203 PDNS_RECORD QueryResultWide;
204 PDNS_RECORD ConvertedRecord = 0, LastRecord = 0;
205
206 if (Name == NULL)
207 return ERROR_INVALID_PARAMETER;
208
209 Buffer = DnsWToC(Name);
210
211 Status = DnsQuery_A(Buffer, Type, Options, Servers, &QueryResultWide, Reserved);
212
213 while(Status == ERROR_SUCCESS && QueryResultWide)
214 {
215 switch(QueryResultWide->wType)
216 {
217 case DNS_TYPE_A:
218 case DNS_TYPE_WKS:
219 ConvertedRecord = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
220 ConvertedRecord->pName = (PCHAR)DnsCToW(QueryResultWide->pName);
221 ConvertedRecord->wType = QueryResultWide->wType;
222 ConvertedRecord->wDataLength = QueryResultWide->wDataLength;
223 memcpy(ConvertedRecord, QueryResultWide, QueryResultWide->wDataLength);
224 break;
225
226 case DNS_TYPE_CNAME:
227 case DNS_TYPE_PTR:
228 case DNS_TYPE_NS:
229 case DNS_TYPE_MB:
230 case DNS_TYPE_MD:
231 case DNS_TYPE_MF:
232 case DNS_TYPE_MG:
233 case DNS_TYPE_MR:
234 ConvertedRecord = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
235 ConvertedRecord->pName = (PCHAR)DnsCToW(QueryResultWide->pName);
236 ConvertedRecord->wType = QueryResultWide->wType;
237 ConvertedRecord->wDataLength = sizeof(DNS_PTR_DATA);
238 ConvertedRecord->Data.PTR.pNameHost = (PCHAR)DnsCToW(QueryResultWide->Data.PTR.pNameHost);
239 break;
240
241 case DNS_TYPE_MINFO:
242 ConvertedRecord = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
243 ConvertedRecord->pName = (PCHAR)DnsCToW(QueryResultWide->pName);
244 ConvertedRecord->wType = QueryResultWide->wType;
245 ConvertedRecord->wDataLength = sizeof(DNS_MINFO_DATA);
246 ConvertedRecord->Data.MINFO.pNameMailbox = (PCHAR)DnsCToW(QueryResultWide->Data.MINFO.pNameMailbox);
247 ConvertedRecord->Data.MINFO.pNameErrorsMailbox = (PCHAR)DnsCToW(QueryResultWide->Data.MINFO.pNameErrorsMailbox);
248 break;
249
250 case DNS_TYPE_MX:
251 ConvertedRecord = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
252 ConvertedRecord->pName = (PCHAR)DnsCToW(QueryResultWide->pName);
253 ConvertedRecord->wType = QueryResultWide->wType;
254 ConvertedRecord->wDataLength = sizeof(DNS_MX_DATA);
255 ConvertedRecord->Data.MX.pNameExchange = (PCHAR)DnsCToW( QueryResultWide->Data.MX.pNameExchange);
256 ConvertedRecord->Data.MX.wPreference = QueryResultWide->Data.MX.wPreference;
257 break;
258
259 case DNS_TYPE_HINFO:
260 ConvertedRecord = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_TXT_DATA) + QueryResultWide->Data.TXT.dwStringCount);
261 ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
262 ConvertedRecord->wType = QueryResultWide->wType;
263 ConvertedRecord->wDataLength = sizeof(DNS_TXT_DATA) + (sizeof(PWCHAR) * QueryResultWide->Data.TXT.dwStringCount);
264 ConvertedRecord->Data.TXT.dwStringCount = QueryResultWide->Data.TXT.dwStringCount;
265
266 for(i = 0; i < ConvertedRecord->Data.TXT.dwStringCount; i++)
267 ConvertedRecord->Data.TXT.pStringArray[i] = (PCHAR)DnsCToW(QueryResultWide->Data.TXT.pStringArray[i]);
268
269 break;
270
271 case DNS_TYPE_NULL:
272 ConvertedRecord = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_NULL_DATA) + QueryResultWide->Data.Null.dwByteCount);
273 ConvertedRecord->pName = (PCHAR)DnsCToW(QueryResultWide->pName);
274 ConvertedRecord->wType = QueryResultWide->wType;
275 ConvertedRecord->wDataLength = sizeof(DNS_NULL_DATA) + QueryResultWide->Data.Null.dwByteCount;
276 ConvertedRecord->Data.Null.dwByteCount = QueryResultWide->Data.Null.dwByteCount;
277 memcpy(&ConvertedRecord->Data.Null.Data, &QueryResultWide->Data.Null.Data, QueryResultWide->Data.Null.dwByteCount);
278 break;
279 }
280
281 if(LastRecord)
282 {
283 LastRecord->pNext = ConvertedRecord;
284 LastRecord = LastRecord->pNext;
285 }
286 else
287 {
288 LastRecord = *QueryResultSet = ConvertedRecord;
289 }
290
291 QueryResultWide = QueryResultWide->pNext;
292 }
293
294 if (LastRecord)
295 LastRecord->pNext = 0;
296
297 /* The name */
298 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
299
300 return Status;
301 }
302
303 DNS_STATUS WINAPI
304 DnsQuery_UTF8(LPCSTR Name,
305 WORD Type,
306 DWORD Options,
307 PIP4_ARRAY Servers,
308 PDNS_RECORD *QueryResultSet,
309 PVOID *Reserved)
310 {
311 UNIMPLEMENTED;
312 return ERROR_OUTOFMEMORY;
313 }
314
315 void
316 DnsIntFreeRecordList(PDNS_RECORD ToDelete)
317 {
318 UINT i;
319 PDNS_RECORD next = 0;
320
321 while(ToDelete)
322 {
323 if(ToDelete->pName)
324 RtlFreeHeap(RtlGetProcessHeap(), 0, ToDelete->pName);
325
326 switch(ToDelete->wType)
327 {
328 case DNS_TYPE_CNAME:
329 case DNS_TYPE_PTR:
330 case DNS_TYPE_NS:
331 case DNS_TYPE_MB:
332 case DNS_TYPE_MD:
333 case DNS_TYPE_MF:
334 case DNS_TYPE_MG:
335 case DNS_TYPE_MR:
336 RtlFreeHeap(RtlGetProcessHeap(), 0, ToDelete->Data.PTR.pNameHost);
337 break;
338
339 case DNS_TYPE_MINFO:
340 case DNS_TYPE_MX:
341 RtlFreeHeap(RtlGetProcessHeap(), 0, ToDelete->Data.MX.pNameExchange);
342 break;
343
344 case DNS_TYPE_HINFO:
345 for(i = 0; i < ToDelete->Data.TXT.dwStringCount; i++)
346 RtlFreeHeap(RtlGetProcessHeap(), 0, ToDelete->Data.TXT.pStringArray[i]);
347
348 RtlFreeHeap(RtlGetProcessHeap(), 0, ToDelete->Data.TXT.pStringArray);
349 break;
350 }
351
352 next = ToDelete->pNext;
353 RtlFreeHeap(RtlGetProcessHeap(), 0, ToDelete);
354 ToDelete = next;
355 }
356 }