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
8 /* INCLUDES ******************************************************************/
11 /* DATA **********************************************************************/
13 /* FUNCTIONS *****************************************************************/
17 FlatBuf_Arg_ReserveAlignPointer(IN PVOID Position
,
21 /* Just a little helper that we use */
22 return FlatBuf_Arg_Reserve(Position
, FreeSize
, Size
, sizeof(PVOID
));
27 SaBlob_Create(IN ULONG Count
)
30 PDNS_ARRAY DnsAddrArray
;
32 /* Allocate the blob */
33 Blob
= Dns_AllocZero(sizeof(DNS_BLOB
));
36 /* Check if it'll hold any addresses */
39 /* Create the DNS Address Array */
40 DnsAddrArray
= DnsAddrArray_Create(Count
);
43 /* Failure, free the blob */
45 SetLastError(ERROR_OUTOFMEMORY
);
49 /* Link it with the blob */
50 Blob
->DnsAddrArray
= DnsAddrArray
;
61 SaBlob_CreateFromIp4(IN LPWSTR Name
,
63 IN PIN_ADDR AddressArray
)
70 Blob
= SaBlob_Create(Count
);
71 if (!Blob
) goto Quickie
;
73 /* If we have a name */
76 /* Create a copy of it */
77 NameCopy
= Dns_CreateStringCopy_W(Name
);
78 if (!NameCopy
) goto Quickie
;
80 /* Save the pointer to the name */
81 Blob
->Name
= NameCopy
;
84 /* Loop all the addresses */
85 for (i
= 0; i
< Count
; i
++)
87 /* Add an entry for this address */
88 DnsAddrArray_AddIp4(Blob
->DnsAddrArray
, AddressArray
[i
], IpV4Address
);
95 /* Free the blob, set error and fail */
97 SetLastError(ERROR_OUTOFMEMORY
);
103 SaBlob_Free(IN PDNS_BLOB Blob
)
105 /* Make sure we got a blob */
109 Dns_Free(Blob
->Name
);
111 /* Loop the aliases */
112 while (Blob
->AliasCount
)
115 Dns_Free(Blob
->Aliases
[Blob
->AliasCount
]);
117 /* Decrease number of aliases */
121 /* Free the DNS Address Array */
122 DnsAddrArray_Free(Blob
->DnsAddrArray
);
124 /* Free the blob itself */
131 SaBlob_CreateHostent(IN OUT PULONG_PTR BufferPosition
,
132 IN OUT PSIZE_T FreeBufferSpace
,
133 IN OUT PSIZE_T HostEntrySize
,
137 IN BOOLEAN BufferAllocated
)
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;
147 ULONG HostentSize
= 0;
148 PHOSTENT Hostent
= NULL
;
149 ULONG_PTR HostentPtr
;
150 PVOID CurrentAddress
;
152 /* Check if we actually have any addresses */
155 /* Get the address family */
156 AddressFamily
= DnsAddrArray
->Addresses
[0].AddressFamily
;
158 /* Get family information */
159 FamilyInfo
= FamilyInfo_GetForFamily(AddressFamily
);
161 /* Save the current address count and their size */
162 AddressCount
= DnsAddrArray
->UsedAddresses
;
163 AddressSize
= FamilyInfo
->AddressSize
;
166 /* Calculate total size for all the addresses, and their pointers */
167 TotalSize
= AddressSize
* AddressCount
;
168 NamePointerSize
= AddressCount
* sizeof(PVOID
) + sizeof(PVOID
);
170 /* Check if we have a name */
173 /* Find out the size we'll need for a copy */
174 StringLength
= (Dns_GetBufferLengthForStringCopy(Blob
->Name
,
177 StringType
) + 1) & ~1;
180 /* Now do the same for the aliases */
181 for (i
= AliasCount
; i
; i
--)
183 /* Find out the size we'll need for a copy */
184 HostentSize
+= (Dns_GetBufferLengthForStringCopy(Blob
->Aliases
[i
],
187 StringType
) + 1) & ~1;
190 /* Find out how much the pointers will take */
191 AliasPointerSize
= AliasCount
* sizeof(PVOID
) + sizeof(PVOID
);
193 /* Calculate Hostent Size */
194 HostentSize
+= TotalSize
+
200 /* Check if we already have a buffer */
201 if (!BufferAllocated
)
203 /* We don't, allocate space ourselves */
204 HostentPtr
= (ULONG_PTR
)Dns_AllocZero(HostentSize
);
208 /* We do, so allocate space in the buffer */
209 HostentPtr
= (ULONG_PTR
)FlatBuf_Arg_ReserveAlignPointer(BufferPosition
,
214 /* Make sure we got space */
218 Hostent
= Hostent_Init((PVOID
)&HostentPtr
,
225 /* Loop the addresses */
226 for (i
= 0; i
< AddressCount
; i
++)
228 /* Get the pointer of the current address */
229 CurrentAddress
= (PVOID
)((ULONG_PTR
)&DnsAddrArray
->Addresses
[i
] +
230 FamilyInfo
->AddressOffset
);
232 /* Write the pointer */
233 Hostent
->h_addr_list
[i
] = (PCHAR
)HostentPtr
;
235 /* Copy the address */
236 RtlCopyMemory((PVOID
)HostentPtr
, CurrentAddress
, AddressSize
);
238 /* Advance the buffer */
239 HostentPtr
+= AddressSize
;
242 /* Check if we have a name */
245 /* Align our current position */
246 HostentPtr
+= 1 & ~1;
248 /* Save our name here */
249 Hostent
->h_name
= (LPSTR
)HostentPtr
;
251 /* Now copy it in the blob */
252 HostentPtr
+= Dns_StringCopy((PVOID
)HostentPtr
,
260 /* Loop the Aliases */
261 for (i
= AliasCount
; i
; i
--)
263 /* Align our current position */
264 HostentPtr
+= 1 & ~1;
266 /* Save our alias here */
267 Hostent
->h_aliases
[i
] = (LPSTR
)HostentPtr
;
269 /* Now copy it in the blob */
270 HostentPtr
+= Dns_StringCopy((PVOID
)HostentPtr
,
278 /* Check if the caller didn't have a buffer */
279 if (!BufferAllocated
)
281 /* Return the size; not needed if we had a blob, since it's internal */
282 *HostEntrySize
= *BufferPosition
- (ULONG_PTR
)HostentPtr
;
285 /* Convert to Offsets if requested */
286 if(Relative
) Hostent_ConvertToOffsets(Hostent
);
288 /* Return the full, complete, hostent */
294 SaBlob_WriteNameOrAlias(IN PDNS_BLOB Blob
,
298 /* Check if this is an alias */
301 /* It's not. Simply create a copy of the string */
302 Blob
->Name
= Dns_CreateStringCopy_W(String
);
303 if (!Blob
->Name
) return GetLastError();
307 /* Does it have a name, and less then 8 aliases? */
308 if ((Blob
->Name
) && (Blob
->AliasCount
<= 8))
310 /* Yup, create a copy of the string and increase the alias count */
311 Blob
->Aliases
[Blob
->AliasCount
] = Dns_CreateStringCopy_W(String
);
316 /* Invalid request! */
317 return ERROR_MORE_DATA
;
322 return ERROR_SUCCESS
;
327 SaBlob_WriteAddress(IN PDNS_BLOB Blob
,
328 OUT PDNS_ADDRESS DnsAddr
)
330 /* Check if we have an array yet */
331 if (!Blob
->DnsAddrArray
)
334 Blob
->DnsAddrArray
= DnsAddrArray_Create(1);
335 if (!Blob
->DnsAddrArray
) return ERROR_OUTOFMEMORY
;
338 /* Add this address */
339 return DnsAddrArray_AddAddr(Blob
->DnsAddrArray
, DnsAddr
, AF_UNSPEC
, 0) ?
346 SaBlob_IsSupportedAddrType(WORD DnsType
)
348 /* Check for valid Types that we support */
349 return (DnsType
== DNS_TYPE_A
||
350 DnsType
== DNS_TYPE_ATMA
||
351 DnsType
== DNS_TYPE_AAAA
);
356 SaBlob_WriteRecords(OUT PDNS_BLOB Blob
,
357 IN PDNS_RECORD DnsRecord
,
360 DNS_ADDRESS DnsAddress
;
361 INT ErrorCode
= STATUS_INVALID_PARAMETER
;
362 BOOLEAN WroteOnce
= FALSE
;
364 /* Zero out the Address */
365 RtlZeroMemory(&DnsAddress
, sizeof(DnsAddress
));
367 /* Loop through all the Records */
370 /* Is this not an answer? */
371 if (DnsRecord
->Flags
.S
.Section
!= DNSREC_ANSWER
)
373 /* Then simply move on to the next DNS Record */
374 DnsRecord
= DnsRecord
->pNext
;
378 /* Check the type of thsi record */
379 switch(DnsRecord
->wType
)
381 /* Regular IPv4, v6 or ATM Record */
386 /* Create a DNS Address from the record */
387 DnsAddr_BuildFromDnsRecord(DnsRecord
, &DnsAddress
);
389 /* Add it to the DNS Blob */
390 ErrorCode
= SaBlob_WriteAddress(Blob
, &DnsAddress
);
392 /* Add the name, if needed */
398 /* Write the name from the DNS Record */
399 ErrorCode
= SaBlob_WriteNameOrAlias(Blob
,
408 /* Just write the alias name */
409 ErrorCode
= SaBlob_WriteNameOrAlias(Blob
,
416 /* Check if we already have a name */
419 /* We don't, so add this as a name */
420 ErrorCode
= SaBlob_WriteNameOrAlias(Blob
,
426 /* We do, so add it as an alias */
427 ErrorCode
= SaBlob_WriteNameOrAlias(Blob
,
437 DnsRecord
= DnsRecord
->pNext
;
440 /* Return error code */
446 SaBlob_CreateFromRecords(IN PDNS_RECORD DnsRecord
,
447 IN BOOLEAN DoAliases
,
450 PDNS_RECORD LocalDnsRecord
;
451 ULONG ProcessedCount
= 0;
454 DNS_ADDRESS DnsAddress
;
456 /* Find out how many DNS Addresses to allocate */
457 LocalDnsRecord
= DnsRecord
;
458 while (LocalDnsRecord
)
460 /* Make sure this record is an answer */
461 if ((LocalDnsRecord
->Flags
.S
.Section
== DNSREC_ANSWER
) &&
462 (SaBlob_IsSupportedAddrType(LocalDnsRecord
->wType
)))
464 /* Increase number of records to process */
468 /* Move to the next record */
469 LocalDnsRecord
= LocalDnsRecord
->pNext
;
472 /* Create the DNS Blob */
473 DnsBlob
= SaBlob_Create(ProcessedCount
);
477 ErrorCode
= GetLastError();
481 /* Write the record to the DNS Blob */
482 ErrorCode
= SaBlob_WriteRecords(DnsBlob
, DnsRecord
, TRUE
);
483 if (ErrorCode
!= NO_ERROR
)
485 /* We failed... but do we still have valid data? */
486 if ((DnsBlob
->Name
) || (DnsBlob
->AliasCount
))
488 /* We'll just assume success then */
489 ErrorCode
= NO_ERROR
;
493 /* Ok, last chance..do you have a DNS Address Array? */
494 if ((DnsBlob
->DnsAddrArray
) &&
495 (DnsBlob
->DnsAddrArray
->UsedAddresses
))
497 /* Boy are you lucky! */
498 ErrorCode
= NO_ERROR
;
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
)))
512 /* Get a DNS Address Structure */
513 if (Dns_ReverseNameToDnsAddr_W(&DnsAddress
, DnsRecord
->pName
))
515 /* Add it to the Blob */
516 if (SaBlob_WriteAddress(DnsBlob
, &DnsAddress
)) ErrorCode
= NO_ERROR
;
520 /* Ok...do we still not have a name? */
521 if (!(DnsBlob
->Name
) && (DoAliases
) && (LocalDnsRecord
))
523 /* We have an local DNS Record, so just use it to write the name */
524 ErrorCode
= SaBlob_WriteNameOrAlias(DnsBlob
,
525 LocalDnsRecord
->pName
,
530 /* Check error code */
531 if (ErrorCode
!= NO_ERROR
)
533 /* Free the blob and set the error */
534 SaBlob_Free(DnsBlob
);
536 SetLastError(ErrorCode
);
545 SaBlob_Query(IN LPWSTR Name
,
549 IN DWORD AddressFamily
)
551 PDNS_RECORD DnsRecord
= NULL
;
553 PDNS_BLOB DnsBlob
= NULL
;
554 LPWSTR LocalName
, LocalNameCopy
;
556 /* If they want reserved data back, clear it out in case we fail */
557 if (Reserved
) *Reserved
= NULL
;
560 ErrorCode
= DnsQuery_W(Name
,
566 if (ErrorCode
!= ERROR_SUCCESS
)
568 /* We failed... did the caller use reserved data? */
569 if (Reserved
&& *Reserved
)
571 /* He did, and it was valid. Free it */
572 DnsApiFree(*Reserved
);
576 /* Normalize error code */
577 if (ErrorCode
== RPC_S_SERVER_UNAVAILABLE
) ErrorCode
= WSATRY_AGAIN
;
581 /* Now create the Blob from the DNS Records */
582 DnsBlob
= SaBlob_CreateFromRecords(DnsRecord
, TRUE
, DnsType
);
585 /* Failed, get error code */
586 ErrorCode
= GetLastError();
590 /* Make sure it has a name */
593 /* It doesn't, fail */
594 ErrorCode
= DNS_INFO_NO_RECORDS
;
598 /* Check if the name is local or loopback */
599 if (!(DnsNameCompare_W(DnsBlob
->Name
, L
"localhost")) &&
600 !(DnsNameCompare_W(DnsBlob
->Name
, L
"loopback")))
602 /* Nothing left to do, exit! */
606 /* This is a local name...query it */
607 LocalName
= DnsQueryConfigAllocEx(DnsConfigFullHostName_W
, NULL
, NULL
);
610 /* Create a copy for the caller */
611 LocalNameCopy
= Dns_CreateStringCopy_W(LocalName
);
614 /* Overwrite the one in the blob */
615 DnsBlob
->Name
= LocalNameCopy
;
619 /* We failed to make a copy, free memory */
620 DnsApiFree(LocalName
);
625 /* Free the DNS Record if we have one */
626 if (DnsRecord
) DnsRecordListFree(DnsRecord
, DnsFreeRecordList
);
628 /* Check if this is a failure path with an active blob */
629 if ((ErrorCode
!= ERROR_SUCCESS
) && (DnsBlob
))
632 SaBlob_Free(DnsBlob
);
636 /* Set the last error and return */
637 SetLastError(ErrorCode
);