|
|
/*++
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; }
|