1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS DNS Shared Library 4 * FILE: lib/dnslib/sablob.c 5 * PURPOSE: Functions for the Saved Answer Blob Implementation 6 */ 7 8 /* INCLUDES ******************************************************************/ 9 #include "precomp.h" 10 11 /* DATA **********************************************************************/ 12 13 /* FUNCTIONS *****************************************************************/ 14 15 PVOID 16 WINAPI 17 FlatBuf_Arg_ReserveAlignPointer(IN PVOID Position, 18 IN PSIZE_T FreeSize, 19 IN SIZE_T Size) 20 { 21 /* Just a little helper that we use */ 22 return FlatBuf_Arg_Reserve(Position, FreeSize, Size, sizeof(PVOID)); 23 } 24 25 PDNS_BLOB 26 WINAPI 27 SaBlob_Create(IN ULONG Count) 28 { 29 PDNS_BLOB Blob; 30 PDNS_ARRAY DnsAddrArray; 31 32 /* Allocate the blob */ 33 Blob = Dns_AllocZero(sizeof(DNS_BLOB)); 34 if (Blob) 35 { 36 /* Check if it'll hold any addresses */ 37 if (Count) 38 { 39 /* Create the DNS Address Array */ 40 DnsAddrArray = DnsAddrArray_Create(Count); 41 if (!DnsAddrArray) 42 { 43 /* Failure, free the blob */ 44 SaBlob_Free(Blob); 45 SetLastError(ERROR_OUTOFMEMORY); 46 } 47 else 48 { 49 /* Link it with the blob */ 50 Blob->DnsAddrArray = DnsAddrArray; 51 } 52 } 53 } 54 55 /* Return the blob */ 56 return Blob; 57 } 58 59 PDNS_BLOB 60 WINAPI 61 SaBlob_CreateFromIp4(IN LPWSTR Name, 62 IN ULONG Count, 63 IN PIN_ADDR AddressArray) 64 { 65 PDNS_BLOB Blob; 66 LPWSTR NameCopy; 67 ULONG i; 68 69 /* Create the blob */ 70 Blob = SaBlob_Create(Count); 71 if (!Blob) goto Quickie; 72 73 /* If we have a name */ 74 if (Name) 75 { 76 /* Create a copy of it */ 77 NameCopy = Dns_CreateStringCopy_W(Name); 78 if (!NameCopy) goto Quickie; 79 80 /* Save the pointer to the name */ 81 Blob->Name = NameCopy; 82 } 83 84 /* Loop all the addresses */ 85 for (i = 0; i < Count; i++) 86 { 87 /* Add an entry for this address */ 88 DnsAddrArray_AddIp4(Blob->DnsAddrArray, AddressArray[i], IpV4Address); 89 } 90 91 /* Return the blob */ 92 return Blob; 93 94 Quickie: 95 /* Free the blob, set error and fail */ 96 SaBlob_Free(Blob); 97 SetLastError(ERROR_OUTOFMEMORY); 98 return NULL; 99 } 100 101 VOID 102 WINAPI 103 SaBlob_Free(IN PDNS_BLOB Blob) 104 { 105 /* Make sure we got a blob */ 106 if (Blob) 107 { 108 /* Free the name */ 109 Dns_Free(Blob->Name); 110 111 /* Loop the aliases */ 112 while (Blob->AliasCount) 113 { 114 /* Free the alias */ 115 Dns_Free(Blob->Aliases[Blob->AliasCount]); 116 117 /* Decrease number of aliases */ 118 Blob->AliasCount--; 119 } 120 121 /* Free the DNS Address Array */ 122 DnsAddrArray_Free(Blob->DnsAddrArray); 123 124 /* Free the blob itself */ 125 Dns_Free(Blob); 126 } 127 } 128 129 PHOSTENT 130 WINAPI 131 SaBlob_CreateHostent(IN OUT PULONG_PTR BufferPosition, 132 IN OUT PSIZE_T FreeBufferSpace, 133 IN OUT PSIZE_T HostEntrySize, 134 IN PDNS_BLOB Blob, 135 IN DWORD StringType, 136 IN BOOLEAN Relative, 137 IN BOOLEAN BufferAllocated) 138 { 139 PDNS_ARRAY DnsAddrArray = Blob->DnsAddrArray; 140 ULONG AliasCount = Blob->AliasCount; 141 WORD AddressFamily = AF_UNSPEC; 142 ULONG AddressCount = 0, AddressSize = 0, TotalSize, NamePointerSize; 143 ULONG AliasPointerSize; 144 PDNS_FAMILY_INFO FamilyInfo = NULL; 145 ULONG StringLength = 0; 146 ULONG i; 147 ULONG HostentSize = 0; 148 PHOSTENT Hostent = NULL; 149 ULONG_PTR HostentPtr; 150 PVOID CurrentAddress; 151 152 /* Check if we actually have any addresses */ 153 if (DnsAddrArray) 154 { 155 /* Get the address family */ 156 AddressFamily = DnsAddrArray->Addresses[0].AddressFamily; 157 158 /* Get family information */ 159 FamilyInfo = FamilyInfo_GetForFamily(AddressFamily); 160 161 /* Save the current address count and their size */ 162 AddressCount = DnsAddrArray->UsedAddresses; 163 AddressSize = FamilyInfo->AddressSize; 164 } 165 166 /* Calculate total size for all the addresses, and their pointers */ 167 TotalSize = AddressSize * AddressCount; 168 NamePointerSize = AddressCount * sizeof(PVOID) + sizeof(PVOID); 169 170 /* Check if we have a name */ 171 if (Blob->Name) 172 { 173 /* Find out the size we'll need for a copy */ 174 StringLength = (Dns_GetBufferLengthForStringCopy(Blob->Name, 175 0, 176 UnicodeString, 177 StringType) + 1) & ~1; 178 } 179 180 /* Now do the same for the aliases */ 181 for (i = AliasCount; i; i--) 182 { 183 /* Find out the size we'll need for a copy */ 184 HostentSize += (Dns_GetBufferLengthForStringCopy(Blob->Aliases[i], 185 0, 186 UnicodeString, 187 StringType) + 1) & ~1; 188 } 189 190 /* Find out how much the pointers will take */ 191 AliasPointerSize = AliasCount * sizeof(PVOID) + sizeof(PVOID); 192 193 /* Calculate Hostent Size */ 194 HostentSize += TotalSize + 195 NamePointerSize + 196 AliasPointerSize + 197 StringLength + 198 sizeof(HOSTENT); 199 200 /* Check if we already have a buffer */ 201 if (!BufferAllocated) 202 { 203 /* We don't, allocate space ourselves */ 204 HostentPtr = (ULONG_PTR)Dns_AllocZero(HostentSize); 205 } 206 else 207 { 208 /* We do, so allocate space in the buffer */ 209 HostentPtr = (ULONG_PTR)FlatBuf_Arg_ReserveAlignPointer(BufferPosition, 210 FreeBufferSpace, 211 HostentSize); 212 } 213 214 /* Make sure we got space */ 215 if (HostentPtr) 216 { 217 /* Initialize it */ 218 Hostent = Hostent_Init((PVOID)&HostentPtr, 219 AddressFamily, 220 AddressSize, 221 AddressCount, 222 AliasCount); 223 } 224 225 /* Loop the addresses */ 226 for (i = 0; i < AddressCount; i++) 227 { 228 /* Get the pointer of the current address */ 229 CurrentAddress = (PVOID)((ULONG_PTR)&DnsAddrArray->Addresses[i] + 230 FamilyInfo->AddressOffset); 231 232 /* Write the pointer */ 233 Hostent->h_addr_list[i] = (PCHAR)HostentPtr; 234 235 /* Copy the address */ 236 RtlCopyMemory((PVOID)HostentPtr, CurrentAddress, AddressSize); 237 238 /* Advance the buffer */ 239 HostentPtr += AddressSize; 240 } 241 242 /* Check if we have a name */ 243 if (Blob->Name) 244 { 245 /* Align our current position */ 246 HostentPtr += 1 & ~1; 247 248 /* Save our name here */ 249 Hostent->h_name = (LPSTR)HostentPtr; 250 251 /* Now copy it in the blob */ 252 HostentPtr += Dns_StringCopy((PVOID)HostentPtr, 253 NULL, 254 Blob->Name, 255 0, 256 UnicodeString, 257 StringType); 258 } 259 260 /* Loop the Aliases */ 261 for (i = AliasCount; i; i--) 262 { 263 /* Align our current position */ 264 HostentPtr += 1 & ~1; 265 266 /* Save our alias here */ 267 Hostent->h_aliases[i] = (LPSTR)HostentPtr; 268 269 /* Now copy it in the blob */ 270 HostentPtr += Dns_StringCopy((PVOID)HostentPtr, 271 NULL, 272 Blob->Aliases[i], 273 0, 274 UnicodeString, 275 StringType); 276 } 277 278 /* Check if the caller didn't have a buffer */ 279 if (!BufferAllocated) 280 { 281 /* Return the size; not needed if we had a blob, since it's internal */ 282 *HostEntrySize = *BufferPosition - (ULONG_PTR)HostentPtr; 283 } 284 285 /* Convert to Offsets if requested */ 286 if(Relative) Hostent_ConvertToOffsets(Hostent); 287 288 /* Return the full, complete, hostent */ 289 return Hostent; 290 } 291 292 INT 293 WINAPI 294 SaBlob_WriteNameOrAlias(IN PDNS_BLOB Blob, 295 IN LPWSTR String, 296 IN BOOLEAN IsAlias) 297 { 298 /* Check if this is an alias */ 299 if (!IsAlias) 300 { 301 /* It's not. Simply create a copy of the string */ 302 Blob->Name = Dns_CreateStringCopy_W(String); 303 if (!Blob->Name) return GetLastError(); 304 } 305 else 306 { 307 /* Does it have a name, and less then 8 aliases? */ 308 if ((Blob->Name) && (Blob->AliasCount <= 8)) 309 { 310 /* Yup, create a copy of the string and increase the alias count */ 311 Blob->Aliases[Blob->AliasCount] = Dns_CreateStringCopy_W(String); 312 Blob->AliasCount++; 313 } 314 else 315 { 316 /* Invalid request! */ 317 return ERROR_MORE_DATA; 318 } 319 } 320 321 /* Return Success */ 322 return ERROR_SUCCESS; 323 } 324 325 INT 326 WINAPI 327 SaBlob_WriteAddress(IN PDNS_BLOB Blob, 328 OUT PDNS_ADDRESS DnsAddr) 329 { 330 /* Check if we have an array yet */ 331 if (!Blob->DnsAddrArray) 332 { 333 /* Allocate one! */ 334 Blob->DnsAddrArray = DnsAddrArray_Create(1); 335 if (!Blob->DnsAddrArray) return ERROR_OUTOFMEMORY; 336 } 337 338 /* Add this address */ 339 return DnsAddrArray_AddAddr(Blob->DnsAddrArray, DnsAddr, AF_UNSPEC, 0) ? 340 ERROR_SUCCESS: 341 ERROR_MORE_DATA; 342 } 343 344 BOOLEAN 345 WINAPI 346 SaBlob_IsSupportedAddrType(WORD DnsType) 347 { 348 /* Check for valid Types that we support */ 349 return (DnsType == DNS_TYPE_A || 350 DnsType == DNS_TYPE_ATMA || 351 DnsType == DNS_TYPE_AAAA); 352 } 353 354 INT 355 WINAPI 356 SaBlob_WriteRecords(OUT PDNS_BLOB Blob, 357 IN PDNS_RECORD DnsRecord, 358 IN BOOLEAN DoAlias) 359 { 360 DNS_ADDRESS DnsAddress; 361 INT ErrorCode = STATUS_INVALID_PARAMETER; 362 BOOLEAN WroteOnce = FALSE; 363 364 /* Zero out the Address */ 365 RtlZeroMemory(&DnsAddress, sizeof(DnsAddress)); 366 367 /* Loop through all the Records */ 368 while (DnsRecord) 369 { 370 /* Is this not an answer? */ 371 if (DnsRecord->Flags.S.Section != DNSREC_ANSWER) 372 { 373 /* Then simply move on to the next DNS Record */ 374 DnsRecord = DnsRecord->pNext; 375 continue; 376 } 377 378 /* Check the type of thsi record */ 379 switch(DnsRecord->wType) 380 { 381 /* Regular IPv4, v6 or ATM Record */ 382 case DNS_TYPE_A: 383 case DNS_TYPE_AAAA: 384 case DNS_TYPE_ATMA: 385 386 /* Create a DNS Address from the record */ 387 DnsAddr_BuildFromDnsRecord(DnsRecord, &DnsAddress); 388 389 /* Add it to the DNS Blob */ 390 ErrorCode = SaBlob_WriteAddress(Blob, &DnsAddress); 391 392 /* Add the name, if needed */ 393 if ((DoAlias) && 394 (!WroteOnce) && 395 (!Blob->Name) && 396 (DnsRecord->pName)) 397 { 398 /* Write the name from the DNS Record */ 399 ErrorCode = SaBlob_WriteNameOrAlias(Blob, 400 DnsRecord->pName, 401 FALSE); 402 WroteOnce = TRUE; 403 } 404 break; 405 406 case DNS_TYPE_CNAME: 407 408 /* Just write the alias name */ 409 ErrorCode = SaBlob_WriteNameOrAlias(Blob, 410 DnsRecord->pName, 411 TRUE); 412 break; 413 414 case DNS_TYPE_PTR: 415 416 /* Check if we already have a name */ 417 if (Blob->Name) 418 { 419 /* We don't, so add this as a name */ 420 ErrorCode = SaBlob_WriteNameOrAlias(Blob, 421 DnsRecord->pName, 422 FALSE); 423 } 424 else 425 { 426 /* We do, so add it as an alias */ 427 ErrorCode = SaBlob_WriteNameOrAlias(Blob, 428 DnsRecord->pName, 429 TRUE); 430 } 431 break; 432 default: 433 break; 434 } 435 436 /* Next record */ 437 DnsRecord = DnsRecord->pNext; 438 } 439 440 /* Return error code */ 441 return ErrorCode; 442 } 443 444 PDNS_BLOB 445 WINAPI 446 SaBlob_CreateFromRecords(IN PDNS_RECORD DnsRecord, 447 IN BOOLEAN DoAliases, 448 IN DWORD DnsType) 449 { 450 PDNS_RECORD LocalDnsRecord; 451 ULONG ProcessedCount = 0; 452 PDNS_BLOB DnsBlob; 453 INT ErrorCode; 454 DNS_ADDRESS DnsAddress; 455 456 /* Find out how many DNS Addresses to allocate */ 457 LocalDnsRecord = DnsRecord; 458 while (LocalDnsRecord) 459 { 460 /* Make sure this record is an answer */ 461 if ((LocalDnsRecord->Flags.S.Section == DNSREC_ANSWER) && 462 (SaBlob_IsSupportedAddrType(LocalDnsRecord->wType))) 463 { 464 /* Increase number of records to process */ 465 ProcessedCount++; 466 } 467 468 /* Move to the next record */ 469 LocalDnsRecord = LocalDnsRecord->pNext; 470 } 471 472 /* Create the DNS Blob */ 473 DnsBlob = SaBlob_Create(ProcessedCount); 474 if (!DnsBlob) 475 { 476 /* Fail */ 477 ErrorCode = GetLastError(); 478 goto Quickie; 479 } 480 481 /* Write the record to the DNS Blob */ 482 ErrorCode = SaBlob_WriteRecords(DnsBlob, DnsRecord, TRUE); 483 if (ErrorCode != NO_ERROR) 484 { 485 /* We failed... but do we still have valid data? */ 486 if ((DnsBlob->Name) || (DnsBlob->AliasCount)) 487 { 488 /* We'll just assume success then */ 489 ErrorCode = NO_ERROR; 490 } 491 else 492 { 493 /* Ok, last chance..do you have a DNS Address Array? */ 494 if ((DnsBlob->DnsAddrArray) && 495 (DnsBlob->DnsAddrArray->UsedAddresses)) 496 { 497 /* Boy are you lucky! */ 498 ErrorCode = NO_ERROR; 499 } 500 } 501 502 /* Buh-bye! */ 503 goto Quickie; 504 } 505 506 /* Check if this is a PTR record */ 507 if ((DnsRecord->wType == DNS_TYPE_PTR) || 508 ((DnsType == DNS_TYPE_PTR) && 509 (DnsRecord->wType == DNS_TYPE_CNAME) && 510 (DnsRecord->Flags.S.Section == DNSREC_ANSWER))) 511 { 512 /* Get a DNS Address Structure */ 513 if (Dns_ReverseNameToDnsAddr_W(&DnsAddress, DnsRecord->pName)) 514 { 515 /* Add it to the Blob */ 516 if (SaBlob_WriteAddress(DnsBlob, &DnsAddress)) ErrorCode = NO_ERROR; 517 } 518 } 519 520 /* Ok...do we still not have a name? */ 521 if (!(DnsBlob->Name) && (DoAliases) && (LocalDnsRecord)) 522 { 523 /* We have an local DNS Record, so just use it to write the name */ 524 ErrorCode = SaBlob_WriteNameOrAlias(DnsBlob, 525 LocalDnsRecord->pName, 526 FALSE); 527 } 528 529 Quickie: 530 /* Check error code */ 531 if (ErrorCode != NO_ERROR) 532 { 533 /* Free the blob and set the error */ 534 SaBlob_Free(DnsBlob); 535 DnsBlob = NULL; 536 SetLastError(ErrorCode); 537 } 538 539 /* Return */ 540 return DnsBlob; 541 } 542 543 PDNS_BLOB 544 WINAPI 545 SaBlob_Query(IN LPWSTR Name, 546 IN WORD DnsType, 547 IN ULONG Flags, 548 IN PVOID *Reserved, 549 IN DWORD AddressFamily) 550 { 551 PDNS_RECORD DnsRecord = NULL; 552 INT ErrorCode; 553 PDNS_BLOB DnsBlob = NULL; 554 LPWSTR LocalName, LocalNameCopy; 555 556 /* If they want reserved data back, clear it out in case we fail */ 557 if (Reserved) *Reserved = NULL; 558 559 /* Query DNS */ 560 ErrorCode = DnsQuery_W(Name, 561 DnsType, 562 Flags, 563 NULL, 564 &DnsRecord, 565 Reserved); 566 if (ErrorCode != ERROR_SUCCESS) 567 { 568 /* We failed... did the caller use reserved data? */ 569 if (Reserved && *Reserved) 570 { 571 /* He did, and it was valid. Free it */ 572 DnsApiFree(*Reserved); 573 *Reserved = NULL; 574 } 575 576 /* Normalize error code */ 577 if (ErrorCode == RPC_S_SERVER_UNAVAILABLE) ErrorCode = WSATRY_AGAIN; 578 goto Quickie; 579 } 580 581 /* Now create the Blob from the DNS Records */ 582 DnsBlob = SaBlob_CreateFromRecords(DnsRecord, TRUE, DnsType); 583 if (!DnsBlob) 584 { 585 /* Failed, get error code */ 586 ErrorCode = GetLastError(); 587 goto Quickie; 588 } 589 590 /* Make sure it has a name */ 591 if (!DnsBlob->Name) 592 { 593 /* It doesn't, fail */ 594 ErrorCode = DNS_INFO_NO_RECORDS; 595 goto Quickie; 596 } 597 598 /* Check if the name is local or loopback */ 599 if (!(DnsNameCompare_W(DnsBlob->Name, L"localhost")) && 600 !(DnsNameCompare_W(DnsBlob->Name, L"loopback"))) 601 { 602 /* Nothing left to do, exit! */ 603 goto Quickie; 604 } 605 606 /* This is a local name...query it */ 607 LocalName = DnsQueryConfigAllocEx(DnsConfigFullHostName_W, NULL, NULL); 608 if (LocalName) 609 { 610 /* Create a copy for the caller */ 611 LocalNameCopy = Dns_CreateStringCopy_W(LocalName); 612 if (LocalNameCopy) 613 { 614 /* Overwrite the one in the blob */ 615 DnsBlob->Name = LocalNameCopy; 616 } 617 else 618 { 619 /* We failed to make a copy, free memory */ 620 DnsApiFree(LocalName); 621 } 622 } 623 624 Quickie: 625 /* Free the DNS Record if we have one */ 626 if (DnsRecord) DnsRecordListFree(DnsRecord, DnsFreeRecordList); 627 628 /* Check if this is a failure path with an active blob */ 629 if ((ErrorCode != ERROR_SUCCESS) && (DnsBlob)) 630 { 631 /* Free the blob */ 632 SaBlob_Free(DnsBlob); 633 DnsBlob = NULL; 634 } 635 636 /* Set the last error and return */ 637 SetLastError(ErrorCode); 638 return DnsBlob; 639 } 640 641