xref: /reactos/sdk/lib/dnslib/sablob.c (revision 50cf16b3)
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