[ADNS] Add function to return number of DNS servers available for query.
[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 #include <winreg.h>
13 #include <iphlpapi.h>
14 #include <strsafe.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* DnsQuery ****************************
20 * Begin a DNS query, and allow the result to be placed in the application
21 * supplied result pointer. The result can be manipulated with the record
22 * functions.
23 *
24 * Name -- The DNS object to be queried.
25 * Type -- The type of records to be returned. These are
26 * listed in windns.h
27 * Options -- Query options. DNS_QUERY_STANDARD is the base
28 * state, and every other option takes precedence.
29 * multiple options can be combined. Listed in
30 * windns.h
31 * Servers -- List of alternate servers (optional)
32 * QueryResultSet -- Pointer to the result pointer that will be filled
33 * when the response is available.
34 * Reserved -- Response as it appears on the wire. Optional.
35 */
36
37 static PCHAR
38 DnsWToC(const WCHAR *WideString)
39 {
40 PCHAR AnsiString;
41 int AnsiLen = WideCharToMultiByte(CP_ACP,
42 0,
43 WideString,
44 -1,
45 NULL,
46 0,
47 NULL,
48 0);
49 if (AnsiLen == 0)
50 return NULL;
51 AnsiString = RtlAllocateHeap(RtlGetProcessHeap(), 0, AnsiLen);
52 if (AnsiString == NULL)
53 {
54 return NULL;
55 }
56 WideCharToMultiByte(CP_ACP,
57 0,
58 WideString,
59 -1,
60 AnsiString,
61 AnsiLen,
62 NULL,
63 0);
64
65 return AnsiString;
66 }
67
68 static PWCHAR
69 DnsCToW(const CHAR *NarrowString)
70 {
71 PWCHAR WideString;
72 int WideLen = MultiByteToWideChar(CP_ACP,
73 0,
74 NarrowString,
75 -1,
76 NULL,
77 0);
78 if (WideLen == 0)
79 return NULL;
80 WideLen *= sizeof(WCHAR);
81 WideString = RtlAllocateHeap(RtlGetProcessHeap(), 0, WideLen);
82 if (WideString == NULL)
83 {
84 return NULL;
85 }
86 MultiByteToWideChar(CP_ACP,
87 0,
88 NarrowString,
89 -1,
90 WideString,
91 WideLen);
92
93 return WideString;
94 }
95
96 DNS_STATUS WINAPI
97 DnsQuery_A(LPCSTR Name,
98 WORD Type,
99 DWORD Options,
100 PIP4_ARRAY Servers,
101 PDNS_RECORD *QueryResultSet,
102 PVOID *Reserved)
103 {
104 UINT i;
105 PWCHAR Buffer;
106 DNS_STATUS Status;
107 PDNS_RECORD QueryResultWide;
108 PDNS_RECORD ConvertedRecord = 0, LastRecord = 0;
109
110 if (Name == NULL)
111 return ERROR_INVALID_PARAMETER;
112 if (QueryResultSet == NULL)
113 return ERROR_INVALID_PARAMETER;
114
115 Buffer = DnsCToW(Name);
116
117 Status = DnsQuery_W(Buffer, Type, Options, Servers, &QueryResultWide, Reserved);
118
119 while (Status == ERROR_SUCCESS && QueryResultWide)
120 {
121 switch (QueryResultWide->wType)
122 {
123 case DNS_TYPE_A:
124 case DNS_TYPE_WKS:
125 ConvertedRecord = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
126 ConvertedRecord->pName = DnsWToC((PWCHAR)QueryResultWide->pName);
127 ConvertedRecord->wType = QueryResultWide->wType;
128 ConvertedRecord->wDataLength = QueryResultWide->wDataLength;
129 memcpy(&ConvertedRecord->Data, &QueryResultWide->Data, QueryResultWide->wDataLength);
130 break;
131
132 case DNS_TYPE_CNAME:
133 case DNS_TYPE_PTR:
134 case DNS_TYPE_NS:
135 case DNS_TYPE_MB:
136 case DNS_TYPE_MD:
137 case DNS_TYPE_MF:
138 case DNS_TYPE_MG:
139 case DNS_TYPE_MR:
140 ConvertedRecord = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
141 ConvertedRecord->pName = DnsWToC((PWCHAR)QueryResultWide->pName);
142 ConvertedRecord->wType = QueryResultWide->wType;
143 ConvertedRecord->wDataLength = sizeof(DNS_PTR_DATA);
144 ConvertedRecord->Data.PTR.pNameHost = DnsWToC((PWCHAR)QueryResultWide->Data.PTR.pNameHost);
145 break;
146
147 case DNS_TYPE_MINFO:
148 ConvertedRecord = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
149 ConvertedRecord->pName = DnsWToC((PWCHAR)QueryResultWide->pName);
150 ConvertedRecord->wType = QueryResultWide->wType;
151 ConvertedRecord->wDataLength = sizeof(DNS_MINFO_DATA);
152 ConvertedRecord->Data.MINFO.pNameMailbox = DnsWToC((PWCHAR)QueryResultWide->Data.MINFO.pNameMailbox);
153 ConvertedRecord->Data.MINFO.pNameErrorsMailbox = DnsWToC((PWCHAR)QueryResultWide->Data.MINFO.pNameErrorsMailbox);
154 break;
155
156 case DNS_TYPE_MX:
157 ConvertedRecord = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
158 ConvertedRecord->pName = DnsWToC((PWCHAR)QueryResultWide->pName);
159 ConvertedRecord->wType = QueryResultWide->wType;
160 ConvertedRecord->wDataLength = sizeof(DNS_MX_DATA);
161 ConvertedRecord->Data.MX.pNameExchange = DnsWToC((PWCHAR)QueryResultWide->Data.MX.pNameExchange);
162 ConvertedRecord->Data.MX.wPreference = QueryResultWide->Data.MX.wPreference;
163 break;
164
165 case DNS_TYPE_HINFO:
166 ConvertedRecord = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_TXT_DATA) + QueryResultWide->Data.TXT.dwStringCount);
167 ConvertedRecord->pName = DnsWToC((PWCHAR)QueryResultWide->pName);
168 ConvertedRecord->wType = QueryResultWide->wType;
169 ConvertedRecord->wDataLength = sizeof(DNS_TXT_DATA) + (sizeof(PCHAR) * QueryResultWide->Data.TXT.dwStringCount);
170 ConvertedRecord->Data.TXT.dwStringCount = QueryResultWide->Data.TXT.dwStringCount;
171
172 for (i = 0; i < ConvertedRecord->Data.TXT.dwStringCount; i++)
173 ConvertedRecord->Data.TXT.pStringArray[i] = DnsWToC((PWCHAR)QueryResultWide->Data.TXT.pStringArray[i]);
174
175 break;
176
177 case DNS_TYPE_NULL:
178 ConvertedRecord = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_NULL_DATA) + QueryResultWide->Data.Null.dwByteCount);
179 ConvertedRecord->pName = DnsWToC((PWCHAR)QueryResultWide->pName);
180 ConvertedRecord->wType = QueryResultWide->wType;
181 ConvertedRecord->wDataLength = sizeof(DNS_NULL_DATA) + QueryResultWide->Data.Null.dwByteCount;
182 ConvertedRecord->Data.Null.dwByteCount = QueryResultWide->Data.Null.dwByteCount;
183 memcpy(&ConvertedRecord->Data.Null.Data, &QueryResultWide->Data.Null.Data, QueryResultWide->Data.Null.dwByteCount);
184 break;
185 }
186
187 if (LastRecord)
188 {
189 LastRecord->pNext = ConvertedRecord;
190 LastRecord = LastRecord->pNext;
191 }
192 else
193 {
194 LastRecord = *QueryResultSet = ConvertedRecord;
195 }
196
197 QueryResultWide = QueryResultWide->pNext;
198 }
199
200 if (LastRecord)
201 LastRecord->pNext = 0;
202
203 /* The name */
204 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
205 /* The result*/
206 if (QueryResultWide) DnsIntFreeRecordList(QueryResultWide);
207
208 return Status;
209 }
210
211 WCHAR
212 *xstrsave(const WCHAR *str)
213 {
214 WCHAR *p;
215 size_t len = 0;
216
217 /* FIXME: how much instead of MAX_PATH? */
218 StringCbLengthW(str, MAX_PATH, &len);
219 len+=sizeof(WCHAR);
220
221 p = RtlAllocateHeap(RtlGetProcessHeap(), 0, len);
222
223 if (p)
224 StringCbCopyW(p, len, str);
225
226 return p;
227 }
228
229 CHAR
230 *xstrsaveA(const CHAR *str)
231 {
232 CHAR *p;
233 size_t len = 0;
234
235 /* FIXME: how much instead of MAX_PATH? */
236 StringCbLengthA(str, MAX_PATH, &len);
237 len++;
238
239 p = RtlAllocateHeap(RtlGetProcessHeap(), 0, len);
240
241 if (p)
242 StringCbCopyA(p, len, str);
243
244 return p;
245 }
246
247 HANDLE
248 OpenNetworkDatabase(LPCWSTR Name)
249 {
250 PWSTR ExpandedPath;
251 PWSTR DatabasePath;
252 INT ErrorCode;
253 HKEY DatabaseKey;
254 DWORD RegType;
255 DWORD RegSize = 0;
256 size_t StringLength;
257 HANDLE ret;
258
259 ExpandedPath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
260 if (!ExpandedPath)
261 return INVALID_HANDLE_VALUE;
262
263 /* Open the database path key */
264 ErrorCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
265 L"System\\CurrentControlSet\\Services\\Tcpip\\Parameters",
266 0,
267 KEY_READ,
268 &DatabaseKey);
269 if (ErrorCode == NO_ERROR)
270 {
271 /* Read the actual path */
272 ErrorCode = RegQueryValueExW(DatabaseKey,
273 L"DatabasePath",
274 NULL,
275 &RegType,
276 NULL,
277 &RegSize);
278
279 DatabasePath = HeapAlloc(GetProcessHeap(), 0, RegSize);
280 if (!DatabasePath)
281 {
282 HeapFree(GetProcessHeap(), 0, ExpandedPath);
283 return INVALID_HANDLE_VALUE;
284 }
285
286 /* Read the actual path */
287 ErrorCode = RegQueryValueExW(DatabaseKey,
288 L"DatabasePath",
289 NULL,
290 &RegType,
291 (LPBYTE)DatabasePath,
292 &RegSize);
293
294 /* Close the key */
295 RegCloseKey(DatabaseKey);
296
297 /* Expand the name */
298 ExpandEnvironmentStringsW(DatabasePath, ExpandedPath, MAX_PATH);
299
300 HeapFree(GetProcessHeap(), 0, DatabasePath);
301 }
302 else
303 {
304 /* Use defalt path */
305 GetSystemDirectoryW(ExpandedPath, MAX_PATH);
306 StringCchLengthW(ExpandedPath, MAX_PATH, &StringLength);
307 if (ExpandedPath[StringLength - 1] != L'\\')
308 {
309 /* It isn't, so add it ourselves */
310 StringCchCatW(ExpandedPath, MAX_PATH, L"\\");
311 }
312 StringCchCatW(ExpandedPath, MAX_PATH, L"DRIVERS\\ETC\\");
313 }
314
315 /* Make sure that the path is backslash-terminated */
316 StringCchLengthW(ExpandedPath, MAX_PATH, &StringLength);
317 if (ExpandedPath[StringLength - 1] != L'\\')
318 {
319 /* It isn't, so add it ourselves */
320 StringCchCatW(ExpandedPath, MAX_PATH, L"\\");
321 }
322
323 /* Add the database name */
324 StringCchCatW(ExpandedPath, MAX_PATH, Name);
325
326 /* Return a handle to the file */
327 ret = CreateFileW(ExpandedPath,
328 FILE_READ_DATA,
329 FILE_SHARE_READ,
330 NULL,
331 OPEN_EXISTING,
332 FILE_ATTRIBUTE_NORMAL,
333 NULL);
334
335 HeapFree(GetProcessHeap(), 0, ExpandedPath);
336 return ret;
337 }
338
339 /* This function is far from perfect but it works enough */
340 IP4_ADDRESS
341 CheckForCurrentHostname(CONST CHAR * Name, PFIXED_INFO network_info)
342 {
343 PCHAR TempName;
344 DWORD AdapterAddressesSize, Status;
345 IP4_ADDRESS ret = 0, Address;
346 PIP_ADAPTER_ADDRESSES Addresses = NULL, pip;
347 BOOL Found = FALSE;
348
349 if (network_info->DomainName[0])
350 {
351 size_t StringLength;
352 size_t TempSize = 2;
353 StringCchLengthA(network_info->HostName, sizeof(network_info->HostName), &StringLength);
354 TempSize += StringLength;
355 StringCchLengthA(network_info->DomainName, sizeof(network_info->DomainName), &StringLength);
356 TempSize += StringLength;
357 TempName = RtlAllocateHeap(RtlGetProcessHeap(), 0, TempSize);
358 StringCchCopyA(TempName, TempSize, network_info->HostName);
359 StringCchCatA(TempName, TempSize, ".");
360 StringCchCatA(TempName, TempSize, network_info->DomainName);
361 }
362 else
363 {
364 TempName = RtlAllocateHeap(RtlGetProcessHeap(), 0, 1);
365 TempName[0] = 0;
366 }
367 Found = !stricmp(Name, network_info->HostName) || !stricmp(Name, TempName);
368 RtlFreeHeap(RtlGetProcessHeap(), 0, TempName);
369 if (!Found)
370 {
371 return 0;
372 }
373 /* get adapter info */
374 AdapterAddressesSize = 0;
375 GetAdaptersAddresses(AF_INET,
376 GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER |
377 GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST,
378 NULL,
379 Addresses,
380 &AdapterAddressesSize);
381 if (!AdapterAddressesSize)
382 {
383 return 0;
384 }
385 Addresses = RtlAllocateHeap(RtlGetProcessHeap(), 0, AdapterAddressesSize);
386 Status = GetAdaptersAddresses(AF_INET,
387 GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER |
388 GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST,
389 NULL,
390 Addresses,
391 &AdapterAddressesSize);
392 if (Status)
393 {
394 RtlFreeHeap(RtlGetProcessHeap(), 0, Addresses);
395 return 0;
396 }
397 for (pip = Addresses; pip != NULL; pip = pip->Next) {
398 Address = ((LPSOCKADDR_IN)pip->FirstUnicastAddress->Address.lpSockaddr)->sin_addr.S_un.S_addr;
399 if (Address != ntohl(INADDR_LOOPBACK))
400 break;
401 }
402 if (Address && Address != ntohl(INADDR_LOOPBACK))
403 {
404 ret = Address;
405 }
406 RtlFreeHeap(RtlGetProcessHeap(), 0, Addresses);
407 return ret;
408 }
409
410 BOOL
411 ParseV4Address(LPCSTR AddressString,
412 OUT PDWORD pAddress)
413 {
414 CHAR * cp = (CHAR *)AddressString;
415 DWORD val, base;
416 unsigned char c;
417 DWORD parts[4], *pp = parts;
418 if (!AddressString)
419 return FALSE;
420 if (!isdigit(*cp)) return FALSE;
421
422 again:
423 /*
424 * Collect number up to ``.''.
425 * Values are specified as for C:
426 * 0x=hex, 0=octal, other=decimal.
427 */
428 val = 0; base = 10;
429 if (*cp == '0') {
430 if (*++cp == 'x' || *cp == 'X')
431 base = 16, cp++;
432 else
433 base = 8;
434 }
435 while ((c = *cp)) {
436 if (isdigit(c)) {
437 val = (val * base) + (c - '0');
438 cp++;
439 continue;
440 }
441 if (base == 16 && isxdigit(c)) {
442 val = (val << 4) + (c + 10 - (islower(c) ? 'a' : 'A'));
443 cp++;
444 continue;
445 }
446 break;
447 }
448 if (*cp == '.') {
449 /*
450 * Internet format:
451 * a.b.c.d
452 */
453 if (pp >= parts + 4) return FALSE;
454 *pp++ = val;
455 cp++;
456 goto again;
457 }
458 /*
459 * Check for trailing characters.
460 */
461 if (*cp && *cp > ' ') return FALSE;
462
463 if (pp >= parts + 4) return FALSE;
464 *pp++ = val;
465 /*
466 * Concoct the address according to
467 * the number of parts specified.
468 */
469 if ((DWORD)(pp - parts) != 4) return FALSE;
470 if (parts[0] > 0xff || parts[1] > 0xff || parts[2] > 0xff || parts[3] > 0xff) return FALSE;
471 val = (parts[3] << 24) | (parts[2] << 16) | (parts[1] << 8) | parts[0];
472
473 if (pAddress)
474 *pAddress = val;
475
476 return TRUE;
477 }
478
479 /* This function is far from perfect but it works enough */
480 IP4_ADDRESS
481 FindEntryInHosts(CONST CHAR * name)
482 {
483 BOOL Found = FALSE;
484 HANDLE HostsFile;
485 CHAR HostsDBData[BUFSIZ] = { 0 };
486 PCHAR AddressStr, DnsName = NULL, AddrTerm, NameSt, NextLine, ThisLine, Comment;
487 UINT ValidData = 0;
488 DWORD ReadSize;
489 DWORD Address;
490
491 /* Open the network database */
492 HostsFile = OpenNetworkDatabase(L"hosts");
493 if (HostsFile == INVALID_HANDLE_VALUE)
494 {
495 WSASetLastError(WSANO_RECOVERY);
496 return 0;
497 }
498
499 while (!Found && ReadFile(HostsFile,
500 HostsDBData + ValidData,
501 sizeof(HostsDBData) - ValidData,
502 &ReadSize,
503 NULL))
504 {
505 ValidData += ReadSize;
506 ReadSize = 0;
507 NextLine = ThisLine = HostsDBData;
508
509 /* Find the beginning of the next line */
510 while ((NextLine < HostsDBData + ValidData) &&
511 (*NextLine != '\r') &&
512 (*NextLine != '\n'))
513 {
514 NextLine++;
515 }
516
517 /* Zero and skip, so we can treat what we have as a string */
518 if (NextLine > HostsDBData + ValidData)
519 break;
520
521 *NextLine = 0;
522 NextLine++;
523
524 Comment = strchr(ThisLine, '#');
525 if (Comment)
526 *Comment = 0; /* Terminate at comment start */
527
528 AddressStr = ThisLine;
529 /* Find the first space separating the IP address from the DNS name */
530 AddrTerm = strchr(ThisLine, ' ');
531 if (AddrTerm)
532 {
533 /* Terminate the address string */
534 *AddrTerm = 0;
535
536 /* Find the last space before the DNS name */
537 NameSt = strrchr(ThisLine, ' ');
538
539 /* If there is only one space (the one we removed above), then just use the address terminator */
540 if (!NameSt)
541 NameSt = AddrTerm;
542
543 /* Move from the space to the first character of the DNS name */
544 NameSt++;
545
546 DnsName = NameSt;
547
548 if (!stricmp(name, DnsName) || !stricmp(name, AddressStr))
549 {
550 Found = TRUE;
551 break;
552 }
553 }
554
555 /* Get rid of everything we read so far */
556 while (NextLine <= HostsDBData + ValidData &&
557 isspace(*NextLine))
558 {
559 NextLine++;
560 }
561
562 if (HostsDBData + ValidData - NextLine <= 0)
563 break;
564
565 memmove(HostsDBData, NextLine, HostsDBData + ValidData - NextLine);
566 ValidData -= NextLine - HostsDBData;
567 }
568
569 CloseHandle(HostsFile);
570
571 if (!Found)
572 {
573 WSASetLastError(WSANO_DATA);
574 return 0;
575 }
576
577 if (strstr(AddressStr, ":"))
578 {
579 WSASetLastError(WSAEINVAL);
580 return 0;
581 }
582
583 if (!ParseV4Address(AddressStr, &Address))
584 {
585 WSASetLastError(WSAEINVAL);
586 return 0;
587 }
588
589 return Address;
590 }
591
592 DNS_STATUS WINAPI
593 DnsQuery_W(LPCWSTR Name,
594 WORD Type,
595 DWORD Options,
596 PIP4_ARRAY Servers,
597 PDNS_RECORD *QueryResultSet,
598 PVOID *Reserved)
599 {
600 adns_state astate;
601 int quflags = (Options & DNS_QUERY_NO_RECURSION) == 0 ? adns_qf_search : 0;
602 int adns_error;
603 adns_answer *answer;
604 LPSTR CurrentName;
605 unsigned i, CNameLoop;
606 PFIXED_INFO network_info;
607 ULONG network_info_blen = 0;
608 DWORD network_info_result;
609 PIP_ADDR_STRING pip;
610 IP4_ADDRESS Address;
611 struct in_addr addr;
612 PCHAR HostWithDomainName;
613 PCHAR AnsiName;
614 size_t NameLen = 0;
615
616 if (Name == NULL)
617 return ERROR_INVALID_PARAMETER;
618 if (QueryResultSet == NULL)
619 return ERROR_INVALID_PARAMETER;
620 if ((Options & DNS_QUERY_WIRE_ONLY) != 0 && (Options & DNS_QUERY_NO_WIRE_QUERY) != 0)
621 return ERROR_INVALID_PARAMETER;
622
623 *QueryResultSet = 0;
624
625 switch (Type)
626 {
627 case DNS_TYPE_A:
628 /* FIXME: how much instead of MAX_PATH? */
629 NameLen = WideCharToMultiByte(CP_ACP,
630 0,
631 Name,
632 -1,
633 NULL,
634 0,
635 NULL,
636 0);
637 AnsiName = RtlAllocateHeap(RtlGetProcessHeap(), 0, NameLen);
638 if (NULL == AnsiName)
639 {
640 return ERROR_OUTOFMEMORY;
641 }
642 WideCharToMultiByte(CP_ACP,
643 0,
644 Name,
645 -1,
646 AnsiName,
647 NameLen,
648 NULL,
649 0);
650 NameLen--;
651 /* Is it an IPv4 address? */
652 if (ParseV4Address(AnsiName, &Address))
653 {
654 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
655 *QueryResultSet = (PDNS_RECORD)RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
656
657 if (NULL == *QueryResultSet)
658 {
659 return ERROR_OUTOFMEMORY;
660 }
661
662 (*QueryResultSet)->pNext = NULL;
663 (*QueryResultSet)->wType = Type;
664 (*QueryResultSet)->wDataLength = sizeof(DNS_A_DATA);
665 (*QueryResultSet)->Data.A.IpAddress = Address;
666
667 (*QueryResultSet)->pName = (LPSTR)xstrsave(Name);
668
669 return (*QueryResultSet)->pName ? ERROR_SUCCESS : ERROR_OUTOFMEMORY;
670 }
671
672 /* Check allowed characters
673 * According to RFC a-z,A-Z,0-9,-,_, but can't start or end with - or _
674 */
675 if (AnsiName[0] == '-' || AnsiName[0] == '_' || AnsiName[NameLen - 1] == '-' ||
676 AnsiName[NameLen - 1] == '_' || strstr(AnsiName, "..") != NULL)
677 {
678 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
679 return ERROR_INVALID_NAME;
680 }
681 i = 0;
682 while (i < NameLen)
683 {
684 if (!((AnsiName[i] >= 'a' && AnsiName[i] <= 'z') ||
685 (AnsiName[i] >= 'A' && AnsiName[i] <= 'Z') ||
686 (AnsiName[i] >= '0' && AnsiName[i] <= '9') ||
687 AnsiName[i] == '-' || AnsiName[i] == '_' || AnsiName[i] == '.'))
688 {
689 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
690 return ERROR_INVALID_NAME;
691 }
692 i++;
693 }
694
695 if ((Options & DNS_QUERY_NO_HOSTS_FILE) == 0)
696 {
697 if ((Address = FindEntryInHosts(AnsiName)) != 0)
698 {
699 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
700 *QueryResultSet = (PDNS_RECORD)RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
701
702 if (NULL == *QueryResultSet)
703 {
704 return ERROR_OUTOFMEMORY;
705 }
706
707 (*QueryResultSet)->pNext = NULL;
708 (*QueryResultSet)->wType = Type;
709 (*QueryResultSet)->wDataLength = sizeof(DNS_A_DATA);
710 (*QueryResultSet)->Data.A.IpAddress = Address;
711
712 (*QueryResultSet)->pName = (LPSTR)xstrsave(Name);
713
714 return (*QueryResultSet)->pName ? ERROR_SUCCESS : ERROR_OUTOFMEMORY;
715 }
716 }
717
718 network_info_result = GetNetworkParams(NULL, &network_info_blen);
719 network_info = (PFIXED_INFO)RtlAllocateHeap(RtlGetProcessHeap(), 0, (size_t)network_info_blen);
720 if (NULL == network_info)
721 {
722 return ERROR_OUTOFMEMORY;
723 }
724
725 network_info_result = GetNetworkParams(network_info, &network_info_blen);
726 if (network_info_result != ERROR_SUCCESS)
727 {
728 RtlFreeHeap(RtlGetProcessHeap(), 0, network_info);
729 return network_info_result;
730 }
731
732 if ((Address = CheckForCurrentHostname(NameLen != 0 ? AnsiName : network_info->HostName, network_info)) != 0)
733 {
734 size_t TempLen = 2, StringLength = 0;
735 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
736 StringCchLengthA(network_info->HostName, sizeof(network_info->HostName), &StringLength);
737 TempLen += StringLength;
738 StringCchLengthA(network_info->DomainName, sizeof(network_info->DomainName), &StringLength);
739 TempLen += StringLength;
740 HostWithDomainName = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), 0, TempLen);
741 StringCchCopyA(HostWithDomainName, TempLen, network_info->HostName);
742 if (network_info->DomainName[0])
743 {
744 StringCchCatA(HostWithDomainName, TempLen, ".");
745 StringCchCatA(HostWithDomainName, TempLen, network_info->DomainName);
746 }
747 RtlFreeHeap(RtlGetProcessHeap(), 0, network_info);
748 *QueryResultSet = (PDNS_RECORD)RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
749
750 if (NULL == *QueryResultSet)
751 {
752 RtlFreeHeap(RtlGetProcessHeap(), 0, HostWithDomainName);
753 return ERROR_OUTOFMEMORY;
754 }
755
756 (*QueryResultSet)->pNext = NULL;
757 (*QueryResultSet)->wType = Type;
758 (*QueryResultSet)->wDataLength = sizeof(DNS_A_DATA);
759 (*QueryResultSet)->Data.A.IpAddress = Address;
760
761 (*QueryResultSet)->pName = (LPSTR)DnsCToW(HostWithDomainName);
762
763 RtlFreeHeap(RtlGetProcessHeap(), 0, HostWithDomainName);
764 return (*QueryResultSet)->pName ? ERROR_SUCCESS : ERROR_OUTOFMEMORY;
765 }
766
767 if ((Options & DNS_QUERY_NO_WIRE_QUERY) != 0)
768 {
769 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
770 RtlFreeHeap(RtlGetProcessHeap(), 0, network_info);
771 return ERROR_FILE_NOT_FOUND;
772 }
773
774 adns_error = adns_init(&astate, adns_if_noenv | adns_if_noerrprint | adns_if_noserverwarn, 0);
775 if (adns_error != adns_s_ok)
776 {
777 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
778 RtlFreeHeap(RtlGetProcessHeap(), 0, network_info);
779 return DnsIntTranslateAdnsToDNS_STATUS(adns_error);
780 }
781 for (pip = &(network_info->DnsServerList); pip; pip = pip->Next)
782 {
783 addr.s_addr = inet_addr(pip->IpAddress.String);
784 if ((addr.s_addr != INADDR_ANY) && (addr.s_addr != INADDR_NONE))
785 adns_addserver(astate, addr);
786 }
787 if (network_info->DomainName[0])
788 {
789 adns_ccf_search(astate, "LOCALDOMAIN", -1, network_info->DomainName);
790 }
791 RtlFreeHeap(RtlGetProcessHeap(), 0, network_info);
792
793 if (Servers)
794 {
795 for (i = 0; i < Servers->AddrCount; i++)
796 {
797 adns_addserver(astate, *((struct in_addr *)&Servers->AddrArray[i]));
798 }
799 }
800
801 if (!adns_numservers(astate))
802 {
803 /* There are no servers to query so bail out */
804 adns_finish(astate);
805 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
806 return ERROR_FILE_NOT_FOUND;
807 }
808
809 /*
810 * adns doesn't resolve chained CNAME records (a CNAME which points to
811 * another CNAME pointing to another... pointing to an A record), according
812 * to a mailing list thread the authors believe that chained CNAME records
813 * are invalid and the DNS entries should be fixed. That's a nice academic
814 * standpoint, but there certainly are chained CNAME records out there,
815 * even some fairly major ones (at the time of this writing
816 * download.mozilla.org is a chained CNAME). Everyone else seems to resolve
817 * these fine, so we should too. So we loop here to try to resolve CNAME
818 * chains ourselves. Of course, there must be a limit to protect against
819 * CNAME loops.
820 */
821
822 #define CNAME_LOOP_MAX 16
823
824 CurrentName = AnsiName;
825
826 for (CNameLoop = 0; CNameLoop < CNAME_LOOP_MAX; CNameLoop++)
827 {
828 adns_error = adns_synchronous(astate, CurrentName, adns_r_addr, quflags, &answer);
829
830 if (adns_error != adns_s_ok)
831 {
832 adns_finish(astate);
833
834 if (CurrentName != AnsiName)
835 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentName);
836
837 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
838 return DnsIntTranslateAdnsToDNS_STATUS(adns_error);
839 }
840
841 if (answer && answer->rrs.addr)
842 {
843 if (CurrentName != AnsiName)
844 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentName);
845
846 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
847 *QueryResultSet = (PDNS_RECORD)RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(DNS_RECORD));
848
849 if (NULL == *QueryResultSet)
850 {
851 adns_finish(astate);
852 return ERROR_OUTOFMEMORY;
853 }
854
855 (*QueryResultSet)->pNext = NULL;
856 (*QueryResultSet)->wType = Type;
857 (*QueryResultSet)->wDataLength = sizeof(DNS_A_DATA);
858 (*QueryResultSet)->Data.A.IpAddress = answer->rrs.addr->addr.inet.sin_addr.s_addr;
859
860 adns_finish(astate);
861
862 (*QueryResultSet)->pName = (LPSTR)xstrsave(Name);
863
864 return (*QueryResultSet)->pName ? ERROR_SUCCESS : ERROR_OUTOFMEMORY;
865 }
866
867 if (NULL == answer || adns_s_prohibitedcname != answer->status || NULL == answer->cname)
868 {
869 adns_finish(astate);
870
871 if (CurrentName != AnsiName)
872 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentName);
873
874 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
875 return ERROR_FILE_NOT_FOUND;
876 }
877
878 if (CurrentName != AnsiName)
879 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentName);
880
881 CurrentName = (LPSTR)xstrsaveA(answer->cname);
882
883 if (!CurrentName)
884 {
885 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
886 adns_finish(astate);
887 return ERROR_OUTOFMEMORY;
888 }
889 }
890
891 adns_finish(astate);
892 RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiName);
893 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentName);
894 return ERROR_FILE_NOT_FOUND;
895
896 default:
897 return ERROR_OUTOFMEMORY; /* XXX arty: find a better error code. */
898 }
899 }
900
901 DNS_STATUS WINAPI
902 DnsQuery_UTF8(LPCSTR Name,
903 WORD Type,
904 DWORD Options,
905 PIP4_ARRAY Servers,
906 PDNS_RECORD *QueryResultSet,
907 PVOID *Reserved)
908 {
909 UNIMPLEMENTED;
910 return ERROR_OUTOFMEMORY;
911 }
912
913 void
914 DnsIntFreeRecordList(PDNS_RECORD ToDelete)
915 {
916 UINT i;
917 PDNS_RECORD next = 0;
918
919 while(ToDelete)
920 {
921 if(ToDelete->pName)
922 RtlFreeHeap(RtlGetProcessHeap(), 0, ToDelete->pName);
923
924 switch(ToDelete->wType)
925 {
926 case DNS_TYPE_CNAME:
927 case DNS_TYPE_PTR:
928 case DNS_TYPE_NS:
929 case DNS_TYPE_MB:
930 case DNS_TYPE_MD:
931 case DNS_TYPE_MF:
932 case DNS_TYPE_MG:
933 case DNS_TYPE_MR:
934 RtlFreeHeap(RtlGetProcessHeap(), 0, ToDelete->Data.PTR.pNameHost);
935 break;
936
937 case DNS_TYPE_MINFO:
938 case DNS_TYPE_MX:
939 RtlFreeHeap(RtlGetProcessHeap(), 0, ToDelete->Data.MX.pNameExchange);
940 break;
941
942 case DNS_TYPE_HINFO:
943 for(i = 0; i < ToDelete->Data.TXT.dwStringCount; i++)
944 RtlFreeHeap(RtlGetProcessHeap(), 0, ToDelete->Data.TXT.pStringArray[i]);
945
946 RtlFreeHeap(RtlGetProcessHeap(), 0, ToDelete->Data.TXT.pStringArray);
947 break;
948 }
949
950 next = ToDelete->pNext;
951 RtlFreeHeap(RtlGetProcessHeap(), 0, ToDelete);
952 ToDelete = next;
953 }
954 }