Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

645 lines
19 KiB

/*++
Copyright (c) 1989-2001 Microsoft Corporation
Module Name:
dns.c
Abstract:
This module implements a simple DNSv6 resolver
Author:
Jiandong Ruan
Revision History:
--*/
#include "precomp.h"
#include "dns.tmh"
VOID
SmbDnsTimeout(
PKDPC Dpc,
PSMB_GETHOST_CONTEXT Context,
PVOID Unused1,
PVOID Unused2
);
PIRP __inline
SmbDnsPopResolver(
VOID
)
{
PIRP Irp;
ASSERT(Dns.ResolverNumber >= 0);
if (Dns.ResolverNumber <= 0) {
return NULL;
}
Dns.ResolverNumber--;
Irp = Dns.ResolverList[Dns.ResolverNumber];
Dns.ResolverList[Dns.ResolverNumber] = NULL;
SmbTrace(SMB_TRACE_DNS, ("remove DNS Irp %p, # of resolvers=%d", Irp, Dns.ResolverNumber));
SmbPrint(SMB_TRACE_DNS, ("remove DNS Irp %p, # of resolvers=%d\n", Irp, Dns.ResolverNumber));
ASSERT(Irp != NULL);
return Irp;
}
NTSTATUS __inline
SmbDnsPushResolver(
PIRP Irp
)
{
ASSERT(SmbCfg.DnsMaxResolver >= 1 && SmbCfg.DnsMaxResolver <= DNS_MAX_RESOLVER);
if (Dns.ResolverNumber >= SmbCfg.DnsMaxResolver) {
return STATUS_QUOTA_EXCEEDED;
}
ASSERT(IsListEmpty(&Dns.WaitingServerList));
IoMarkIrpPending(Irp);
Dns.ResolverList[Dns.ResolverNumber] = Irp;
Dns.ResolverNumber++;
SmbTrace(SMB_TRACE_DNS, ("queue DNS Irp %p, # of resolvers=%d", Irp, Dns.ResolverNumber));
SmbPrint(SMB_TRACE_DNS, ("queue DNS Irp %p, # of resolvers=%d\n", Irp, Dns.ResolverNumber));
return STATUS_SUCCESS;
}
NTSTATUS
SmbInitDnsResolver(
VOID
)
{
PAGED_CODE();
RtlZeroMemory(&Dns, sizeof(Dns));
KeInitializeSpinLock(&Dns.Lock);
InitializeListHead(&Dns.WaitingServerList);
InitializeListHead(&Dns.BeingServedList);
Dns.NextId = 1;
return STATUS_SUCCESS;
}
VOID
SmbShutdownDnsResolver(
VOID
)
{
LONG i;
PIRP Irp;
KIRQL Irql;
SMB_ACQUIRE_SPINLOCK(&Dns, Irql);
while (Irp = SmbDnsPopResolver()) {
SMB_RELEASE_SPINLOCK_DPC(&Dns);
Irp->IoStatus.Status = STATUS_SYSTEM_SHUTDOWN;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
SMB_ACQUIRE_SPINLOCK_DPC(&Dns);
}
SMB_RELEASE_SPINLOCK(&Dns, Irql);
}
VOID
SmbPassupDnsRequest(
IN PUNICODE_STRING Name,
IN PSMB_GETHOST_CONTEXT Context,
IN PIRP DnsIrp,
IN KIRQL Irql
)
{
PSMB_DNS_BUFFER DnsBuffer;
//
// Spinlock should be held
//
ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
InsertTailList(&Dns.BeingServedList, &Context->Linkage);
DnsBuffer = (PSMB_DNS_BUFFER)MmGetSystemAddressForMdlSafe(DnsIrp->MdlAddress, HighPagePriority);
ASSERT(DnsBuffer);
Context->Id = DnsBuffer->Id = Dns.NextId++;
if (0 == Dns.NextId) {
Dns.NextId = 1;
}
SmbTrace(SMB_TRACE_DNS, ("pass up DNS request: Irp %p, # of resolvers=%d", DnsIrp, Dns.ResolverNumber));
SmbPrint(SMB_TRACE_DNS, ("pass up DNS request: Irp %p, # of resolvers=%d\n", DnsIrp, Dns.ResolverNumber));
ASSERT(Name->Length <= sizeof(DnsBuffer->Name));
RtlCopyMemory(DnsBuffer->Name, Name->Buffer, Name->Length);
DnsBuffer->Name[Name->Length/sizeof(WCHAR)] = L'\0';
DnsBuffer->NameLen = (Name->Length/sizeof(WCHAR)) + 1;
DnsBuffer->Resolved = FALSE;
DnsBuffer->IpAddrsNum = 0;
DnsBuffer->RequestType = 0;
if (SmbCfg.Tcp6Available) {
DnsBuffer->RequestType |= SMB_DNS_AAAA;
if (SmbCfg.bIPv6EnableOutboundGlobal) {
DnsBuffer->RequestType |= SMB_DNS_AAAA_GLOBAL;
}
}
if (SmbCfg.Tcp4Available) {
DnsBuffer->RequestType |= SMB_DNS_A;
}
SMB_RELEASE_SPINLOCK(&Dns, Irql);
DnsIrp->IoStatus.Status = STATUS_SUCCESS;
DnsIrp->IoStatus.Information = sizeof(DnsBuffer[0]);
IoCompleteRequest(DnsIrp, IO_NETWORK_INCREMENT);
}
VOID
SmbCancelConnectAtDns(
IN PDEVICE_OBJECT Device,
IN PIRP Irp
);
BOOL
LookupLocalName(
IN PUNICODE_STRING Name,
IN PSMB_IP_ADDRESS ipaddr
)
/*++
Routine Description:
Lookup the name in local client list. If it is found,
return a loopback IP address in ipaddr.
TO BE FINISHED.
Arguments:
Return Value:
TRUE if it is found
--*/
{
OEM_STRING oemName;
CHAR NbName[NETBIOS_NAME_SIZE+1];
KIRQL Irql;
NTSTATUS status;
PLIST_ENTRY entry;
PSMB_CLIENT_ELEMENT client;
BOOL found = FALSE;
PAGED_CODE();
if (Name->Length > NETBIOS_NAME_SIZE * sizeof(WCHAR)) {
return FALSE;
}
oemName.Buffer = NbName;
oemName.MaximumLength = NETBIOS_NAME_SIZE + 1;
status = RtlUpcaseUnicodeStringToOemString(&oemName, Name, FALSE);
if (!NT_SUCCESS(status)) {
return FALSE;
}
if (oemName.Length > NETBIOS_NAME_SIZE) {
return FALSE;
}
ASSERT(oemName.Buffer == NbName);
//
// Pad the name with SPACEs
//
if (oemName.Length < NETBIOS_NAME_SIZE) {
RtlFillMemory(oemName.Buffer + oemName.Length, NETBIOS_NAME_SIZE - oemName.Length, ' ');
oemName.Length = NETBIOS_NAME_SIZE;
}
ASSERT(oemName.Length == NETBIOS_NAME_SIZE);
found = FALSE;
SMB_ACQUIRE_SPINLOCK(SmbCfg.SmbDeviceObject, Irql);
entry = SmbCfg.SmbDeviceObject->ClientList.Flink;
while (entry != &SmbCfg.SmbDeviceObject->ClientList) {
client = CONTAINING_RECORD(entry, SMB_CLIENT_ELEMENT, Linkage);
if (RtlEqualMemory(client->EndpointName, oemName.Buffer, oemName.Length)) {
found = TRUE;
}
entry = entry->Flink;
}
SMB_RELEASE_SPINLOCK(SmbCfg.SmbDeviceObject, Irql);
if (found) {
if (IsTcp6Available()) {
ipaddr->sin_family = SMB_AF_INET6;
ip6addr_getloopback(&ipaddr->ip6);
hton_ip6addr(&ipaddr->ip6);
} else {
ipaddr->sin_family = SMB_AF_INET;
ipaddr->ip4.sin4_addr = htonl(INADDR_ANY);
}
}
return found;
}
void SmbAsyncGetHostByName(
IN PUNICODE_STRING Name,
IN PSMB_GETHOST_CONTEXT Context
)
{
KIRQL Irql, CancelIrql;
PIRP DnsIrp = NULL, ClientIrp = NULL;
PAGED_CODE();
SmbPrint(SMB_TRACE_CALL, ("SmbAsyncGetHostByName %Z\n", Name));
SmbAsyncStartTimer((PSMB_ASYNC_CONTEXT)Context, (PKDEFERRED_ROUTINE)SmbDnsTimeout);
if (inet_addr6W(Name->Buffer, &Context->ipaddr[0].ip6)) {
Context->ipaddr_num = 1;
Context->ipaddr[0].sin_family = SMB_AF_INET6;
Context->status = STATUS_SUCCESS;
Context->FQDN.Length = 0;
Context->FQDN.Buffer[0] = 0;
SmbAsyncCompleteRequest((PSMB_ASYNC_CONTEXT)Context);
return;
}
Context->ipaddr[0].ip4.sin4_addr = inet_addrW(Name->Buffer);
if (Context->ipaddr[0].ip4.sin4_addr != 0 && Context->ipaddr[0].ip4.sin4_addr != (ULONG)(-1)) {
Context->ipaddr_num = 1;
Context->ipaddr[0].sin_family = SMB_AF_INET;
Context->status = STATUS_SUCCESS;
Context->FQDN.Length = 0;
Context->FQDN.Buffer[0] = 0;
SmbAsyncCompleteRequest((PSMB_ASYNC_CONTEXT)Context);
return;
}
if (LookupLocalName(Name, &Context->ipaddr[0])) {
Context->ipaddr_num = 1;
Context->status = STATUS_SUCCESS;
Context->FQDN.Length = 0;
Context->FQDN.Buffer[0] = 0;
SmbAsyncCompleteRequest((PSMB_ASYNC_CONTEXT)Context);
return;
}
//
// The name is too long
//
if (Name->Length + sizeof(WCHAR) > Context->FQDN.MaximumLength) {
Context->status = STATUS_INVALID_PARAMETER;
SmbAsyncCompleteRequest((PSMB_ASYNC_CONTEXT)Context);
return;
}
//
// It is not a IP address string, go to DNS resolver, to be implemented
//
ClientIrp = ((PSMB_CONNECT)Context->ClientContext)->PendingIRPs[SMB_PENDING_CONNECT];
ASSERT(ClientIrp);
SMB_ACQUIRE_SPINLOCK(&Dns, Irql);
IoAcquireCancelSpinLock(&CancelIrql);
if (ClientIrp->Cancel) {
IoReleaseCancelSpinLock(CancelIrql);
SMB_RELEASE_SPINLOCK(&Dns, Irql);
//
// Already cancelled
//
Context->status = STATUS_CANCELLED;
SmbAsyncCompleteRequest((PSMB_ASYNC_CONTEXT)Context);
return;
}
IoSetCancelRoutine(ClientIrp, SmbCancelConnectAtDns);
DnsIrp = SmbDnsPopResolver();
if (NULL == DnsIrp) {
RtlCopyMemory(Context->FQDN.Buffer, Name->Buffer, Name->Length);
Context->FQDN.Length = Name->Length;
Context->FQDN.Buffer[Name->Length/sizeof(WCHAR)] = L'\0';
InsertTailList(&Dns.WaitingServerList, &Context->Linkage);
IoReleaseCancelSpinLock(CancelIrql);
SMB_RELEASE_SPINLOCK(&Dns, Irql);
return;
}
IoSetCancelRoutine(DnsIrp, NULL);
IoReleaseCancelSpinLock(CancelIrql);
//
// This guy will complete the Irp and release the spinlock!!!
//
SmbPassupDnsRequest(Name, Context, DnsIrp, Irql);
}
VOID
SmbCancelDns(
IN PDEVICE_OBJECT Device,
IN PIRP Irp
)
{
KIRQL Irql;
LONG i;
BOOL Found = FALSE;
//
// Avoid deadlock, we need to release the spinlock first
//
SmbTrace(SMB_TRACE_DNS, ("Cancel DNS Irp %p", Irp));
SmbPrint(SMB_TRACE_DNS, ("Cancel DNS Irp %p\n", Irp));
IoSetCancelRoutine(Irp, NULL);
IoReleaseCancelSpinLock(Irp->CancelIrql);
//
// After the cancel spinlock is released, the IRP can be completed
// Check if it is still in our pending list
//
SMB_ACQUIRE_SPINLOCK(&Dns, Irql);
for (i = 0; i < Dns.ResolverNumber; i++) {
if (Dns.ResolverList[i] == Irp) {
Dns.ResolverNumber--;
Dns.ResolverList[i] = Dns.ResolverList[Dns.ResolverNumber];
Dns.ResolverList[Dns.ResolverNumber] = NULL;
Found = TRUE;
break;
}
}
SMB_RELEASE_SPINLOCK(&Dns, Irql);
if (Found) {
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
SmbTrace(SMB_TRACE_DNS, ("Complete Cancel DNS Irp %p", Irp));
SmbPrint(SMB_TRACE_DNS, ("Complete Cancel DNS Irp %p\n", Irp));
}
}
PSMB_GETHOST_CONTEXT
SmbDnsLookupGethostCtx(
PLIST_ENTRY queue,
PIRP Irp
)
{
PLIST_ENTRY entry;
PIRP ClientIrp;
PSMB_GETHOST_CONTEXT Context;
//
// Spinlock should be held
//
ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
entry = queue->Flink;
while (entry != queue) {
Context = CONTAINING_RECORD(entry, SMB_GETHOST_CONTEXT, Linkage);
ClientIrp = ((PSMB_CONNECT)Context->ClientContext)->PendingIRPs[SMB_PENDING_CONNECT];
if (ClientIrp == Irp) {
return Context;
}
entry = entry->Flink;
}
return NULL;
}
VOID
SmbCancelConnectAtDns(
IN PDEVICE_OBJECT Device,
IN PIRP Irp
)
{
PSMB_GETHOST_CONTEXT Context;
KIRQL Irql;
//
// Avoid deadlock, we need to release the spinlock first
//
SmbTrace(SMB_TRACE_OUTBOUND, ("Cancel Connect Irp %p", Irp));
SmbPrint(SMB_TRACE_OUTBOUND, ("Cancel Connect Irp %p\n", Irp));
IoSetCancelRoutine(Irp, NULL);
IoReleaseCancelSpinLock(Irp->CancelIrql);
SMB_ACQUIRE_SPINLOCK(&Dns, Irql);
Context = SmbDnsLookupGethostCtx(&Dns.BeingServedList, Irp);
if (NULL == Context) {
Context = SmbDnsLookupGethostCtx(&Dns.WaitingServerList, Irp);
}
if (NULL == Context) {
SMB_RELEASE_SPINLOCK(&Dns, Irql);
//
// This could happen. The DNS name resolution request could completed just before
// we acquire the spinlock Dns.Lock
//
// This ASSERT can be removed after we investigates one such case.
//
// ASSERT(0); Hit in the 04/03/2001 stress
SmbTrace(SMB_TRACE_OUTBOUND, ("Internal error: Cancel Connect Irp %p", Irp));
SmbPrint(SMB_TRACE_OUTBOUND, ("Internal error: Cancel Connect Irp %p\n", Irp));
return;
}
RemoveEntryList(&Context->Linkage);
InitializeListHead(&Context->Linkage);
SMB_RELEASE_SPINLOCK(&Dns, Irql);
Context->status = STATUS_CANCELLED;
SmbAsyncCompleteRequest((PSMB_ASYNC_CONTEXT)Context);
}
VOID
SmbDnsTimeout(
PKDPC Dpc,
PSMB_GETHOST_CONTEXT Context,
PVOID Unused1,
PVOID Unused2
)
{
KIRQL Irql;
BOOL Found;
//
// Be careful on the operations on the Context before we're sure it
// is still in the linked list.
// It could be completed and freed.
//
SMB_ACQUIRE_SPINLOCK(&Dns, Irql);
//
// Note: &Context->Linkage is safe since it doesn't access the
// storage allocated for Context!!!
//
Found = EntryIsInList(&Dns.BeingServedList, &Context->Linkage);
if (!Found) {
Found = EntryIsInList(&Dns.WaitingServerList, &Context->Linkage);
}
if (Found) {
//
// We're safe
//
RemoveEntryList(&Context->Linkage);
InitializeListHead(&Context->Linkage);
}
SMB_RELEASE_SPINLOCK(&Dns, Irql);
if (Found) {
Context->status = STATUS_BAD_NETWORK_PATH;
SmbAsyncCompleteRequest((PSMB_ASYNC_CONTEXT)Context);
}
}
NTSTATUS
SmbNewResolver(
PSMB_DEVICE Device,
PIRP Irp
)
{
KIRQL Irql, CancelIrql;
NTSTATUS status;
PIO_STACK_LOCATION IrpSp;
ULONG Size;
PSMB_DNS_BUFFER DnsBuffer;
PLIST_ENTRY entry;
PSMB_GETHOST_CONTEXT Context;
PIRP ClientIrp;
if (NULL == Irp->MdlAddress) {
return STATUS_INVALID_PARAMETER;
}
Size = MmGetMdlByteCount(Irp->MdlAddress);
if (Size < sizeof(SMB_DNS_BUFFER)) {
return STATUS_INVALID_PARAMETER;
}
DnsBuffer = (PSMB_DNS_BUFFER)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority);
if (NULL == DnsBuffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
SMB_ACQUIRE_SPINLOCK(&Dns, Irql);
if (!IsListEmpty(&Dns.BeingServedList) && DnsBuffer->Id) {
//
// Complete the pending DNS request being served by this resolver
//
entry = Dns.BeingServedList.Flink;
while (entry != &Dns.BeingServedList) {
Context = CONTAINING_RECORD(entry, SMB_GETHOST_CONTEXT, Linkage);
if (Context->Id == DnsBuffer->Id) {
break;
}
entry = entry->Flink;
}
if (entry != &Dns.BeingServedList) {
RemoveEntryList(&Context->Linkage);
InitializeListHead(&Context->Linkage);
SMB_RELEASE_SPINLOCK(&Dns, Irql);
ClientIrp = ((PSMB_CONNECT)Context->ClientContext)->PendingIRPs[SMB_PENDING_CONNECT];
ASSERT(ClientIrp);
IoAcquireCancelSpinLock(&CancelIrql);
IoSetCancelRoutine(ClientIrp, NULL);
IoReleaseCancelSpinLock(CancelIrql);
if (ClientIrp->Cancel) {
Context->status = STATUS_CANCELLED;
} else {
if (DnsBuffer->Resolved) {
USHORT BytesToCopy;
Context->status = STATUS_SUCCESS;
Context->ipaddr_num = DnsBuffer->IpAddrsNum;
if (Context->ipaddr_num > SMB_MAX_IPADDRS_PER_HOST) {
ASSERT (0);
Context->ipaddr_num = SMB_MAX_IPADDRS_PER_HOST;
}
RtlCopyMemory (Context->ipaddr, DnsBuffer->IpAddrsList,
Context->ipaddr_num * sizeof(Context->ipaddr[0]));
if (DnsBuffer->NameLen) {
//
// Return the FQDN to RDR
//
Context->pUnicodeAddress->NameBufferType = NBT_WRITTEN;
BytesToCopy = (USHORT)DnsBuffer->NameLen * sizeof(WCHAR);
if (BytesToCopy > Context->pUnicodeAddress->RemoteName.MaximumLength) {
BytesToCopy = Context->pUnicodeAddress->RemoteName.MaximumLength - sizeof(WCHAR);
}
RtlCopyMemory(Context->pUnicodeAddress->RemoteName.Buffer,
DnsBuffer->Name, BytesToCopy);
Context->pUnicodeAddress->RemoteName.Buffer[BytesToCopy/sizeof(WCHAR)] = L'\0';
Context->pUnicodeAddress->RemoteName.Length = BytesToCopy;
}
} else {
Context->status = STATUS_BAD_NETWORK_PATH;
}
}
//
// Is it better to start another thread?
// Risk: if the connection setup is stucked in tcpip,
// this thread won't be able to serve other DNS requests
//
SmbAsyncCompleteRequest((PSMB_ASYNC_CONTEXT)Context);
SMB_ACQUIRE_SPINLOCK(&Dns, Irql);
}
}
if (IsListEmpty(&Dns.WaitingServerList)) {
//
// We need to queue the IRP, setup the cancel routine.
//
IoAcquireCancelSpinLock(&CancelIrql);
if (Irp->Cancel) {
IoReleaseCancelSpinLock(CancelIrql);
SMB_RELEASE_SPINLOCK(&Dns, Irql);
SmbTrace(SMB_TRACE_DNS, ("DNS Irp %p is cancelled", Irp));
SmbPrint(SMB_TRACE_DNS, ("DNS Irp %p is cancelled\n", Irp));
return STATUS_CANCELLED;
}
IoSetCancelRoutine(Irp, SmbCancelDns);
IoReleaseCancelSpinLock(CancelIrql);
status = SmbDnsPushResolver(Irp);
if (NT_SUCCESS(status)) {
status = STATUS_PENDING;
} else {
IoAcquireCancelSpinLock(&CancelIrql);
IoSetCancelRoutine(Irp, NULL);
IoReleaseCancelSpinLock(CancelIrql);
}
SMB_RELEASE_SPINLOCK(&Dns, Irql);
return status;
}
//
// We have another guy to be served
//
Context = CONTAINING_RECORD(Dns.WaitingServerList.Flink, SMB_GETHOST_CONTEXT, Linkage);
RemoveEntryList(&Context->Linkage);
InitializeListHead(&Context->Linkage);
IoMarkIrpPending(Irp);
//
// This guy will complete the Irp and release the spinlock!!!
//
SmbPassupDnsRequest(&Context->FQDN, Context, Irp, Irql);
//
// Avoid double completion
//
return STATUS_PENDING;
}