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