xref: /reactos/dll/win32/dnsapi/query.c (revision 098564d5)
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 }
1141