[PSEH3]
[reactos.git] / reactos / lib / dnslib / sablob.c
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