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