windows-nt/Source/XPSP1/NT/net/netbt/sys/nt/winsif.c

1531 lines
43 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989-1994 Microsoft Corporation
Module Name:
Winsif.c
Abstract:
This module implements all the code surrounding the WINS interface to
netbt that allows WINS to share the same 137 socket as netbt.
Author:
Jim Stewart (Jimst) 1-30-94
Revision History:
--*/
#include "precomp.h"
VOID
NbtCancelWinsIrp(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
);
VOID
NbtCancelWinsSendIrp(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
);
VOID
WinsDgramCompletion(
IN tDGRAM_SEND_TRACKING *pTracker,
IN NTSTATUS status,
IN ULONG Length
);
NTSTATUS
CheckIfLocalNameActive(
IN tREM_ADDRESS *pSendAddr
);
PVOID
WinsAllocMem(
IN tWINS_INFO *pWinsContext,
IN ULONG Size,
IN BOOLEAN Rcv
);
VOID
WinsFreeMem(
IN tWINS_INFO *pWinsContext,
IN PVOID pBuffer,
IN ULONG Size,
IN BOOLEAN Rcv
);
VOID
InitiateRefresh (
);
BOOLEAN RefreshedYet;
//
// take this define from Winsock.h since including winsock.h causes
// redefinition problems with various types.
//
#define AF_UNIX 1
#define AF_INET 2
//******************* Pageable Routine Declarations ****************
#ifdef ALLOC_PRAGMA
#pragma CTEMakePageable(PAGENBT, NTCloseWinsAddr)
#pragma CTEMakePageable(PAGENBT, InitiateRefresh)
#pragma CTEMakePageable(PAGENBT, PassNamePduToWins)
#pragma CTEMakePageable(PAGENBT, NbtCancelWinsIrp)
#pragma CTEMakePageable(PAGENBT, NbtCancelWinsSendIrp)
#pragma CTEMakePageable(PAGENBT, CheckIfLocalNameActive)
#pragma CTEMakePageable(PAGENBT, WinsDgramCompletion)
#pragma CTEMakePageable(PAGENBT, WinsFreeMem)
#pragma CTEMakePageable(PAGENBT, WinsAllocMem)
#endif
//******************* Pageable Routine Declarations ****************
tWINS_INFO *pWinsInfo;
LIST_ENTRY FreeWinsList;
HANDLE NbtDiscardableCodeHandle={0};
tDEVICECONTEXT *pWinsDeviceContext = NULL;
ULONG LastWinsSignature = 0x8000;
#define COUNT_MAX 10
//----------------------------------------------------------------------------
NTSTATUS
NTOpenWinsAddr(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp,
IN tIPADDRESS IpAddress
)
/*++
Routine Description:
This Routine handles opening the Wins Object that is used by
by WINS to send and receive name service datagrams on port 137.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status;
tWINS_INFO *pWins;
CTELockHandle OldIrq;
//
// Page in the Wins Code, if it hasn't already been paged in.
//
if ((!NbtDiscardableCodeHandle) &&
(!(NbtDiscardableCodeHandle = MmLockPagableCodeSection (NTCloseWinsAddr))))
{
return (STATUS_UNSUCCESSFUL);
}
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
//
// if the WINs endpoint structure is not allocated, then allocate it
// and initialize it.
//
if (pWinsInfo)
{
status = STATUS_UNSUCCESSFUL;
}
else if (!(pWins = NbtAllocMem(sizeof(tWINS_INFO),NBT_TAG('v'))))
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
else
{
CTEZeroMemory(pWins,sizeof(tWINS_INFO));
pWins->Verify = NBT_VERIFY_WINS_ACTIVE;
InitializeListHead(&pWins->Linkage);
InitializeListHead(&pWins->RcvList);
InitializeListHead(&pWins->SendList);
pWins->RcvMemoryMax = NbtConfig.MaxDgramBuffering;
pWins->SendMemoryMax = NbtConfig.MaxDgramBuffering;
pWins->IpAddress = IpAddress;
CTESpinLock(&NbtConfig.JointLock,OldIrq);
pWins->pDeviceContext= GetDeviceWithIPAddress(IpAddress);
pWins->WinsSignature = LastWinsSignature++;
pWinsInfo = pWins;
CTESpinFree(&NbtConfig.JointLock,OldIrq);
pIrpSp->FileObject->FsContext = (PVOID) pWinsInfo;
pIrpSp->FileObject->FsContext2 = (PVOID) NBT_WINS_TYPE;
RefreshedYet = FALSE;
status = STATUS_SUCCESS;
}
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt:Open Wins Address Rcvd, status= %X\n",status));
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTCleanUpWinsAddr(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This Routine handles closing the Wins Object that is used by
by WINS to send and receive name service datagrams on port 137.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status;
CTELockHandle OldIrq;
PLIST_ENTRY pHead, pEntry;
tWINSRCV_BUFFER *pRcv;
tWINS_INFO *pWins = NULL;
PIRP pSendIrp, pRcvIrp;
CTESpinLock(&NbtConfig.JointLock,OldIrq);
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pWins = pIrpSp->FileObject->FsContext;
if (pWinsInfo && (pWins == pWinsInfo))
{
ASSERT (NBT_VERIFY_HANDLE (pWins, NBT_VERIFY_WINS_ACTIVE));
pWins->Verify = NBT_VERIFY_WINS_DOWN;
//
// prevent any more dgram getting queued up
//
pWinsInfo = NULL;
//
// free any rcv buffers that may be queued up
//
pHead = &pWins->RcvList;
while (!IsListEmpty(pHead))
{
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt.NTCleanUpWinsAddr: Freeing Rcv buffered for Wins\n"));
pEntry = RemoveHeadList(pHead);
pRcv = CONTAINING_RECORD(pEntry,tWINSRCV_BUFFER,Linkage);
WinsFreeMem (pWins, pRcv, pRcv->DgramLength,TRUE);
}
//
// return any Send buffers that may be queued up
//
pHead = &pWins->SendList;
while (!IsListEmpty(pHead))
{
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt.NTCleanUpWinsAddr: Freeing Send Wins Address!\n"));
pEntry = RemoveHeadList(pHead);
pSendIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry);
CTESpinFree (&NbtConfig. JointLock, OldIrq);
NbtCancelCancelRoutine (pSendIrp);
CTEIoComplete (pSendIrp, STATUS_CANCELLED, 0);
CTESpinLock (&NbtConfig.JointLock, OldIrq);
}
pWins->pDeviceContext = NULL;
InsertTailList (&FreeWinsList, &pWins->Linkage);
//
// Complete any Rcv Irps that may be hanging on this request
//
if (pRcvIrp = pWins->RcvIrp)
{
pWins->RcvIrp = NULL;
pRcvIrp->IoStatus.Status = STATUS_CANCELLED;
CTESpinFree(&NbtConfig.JointLock,OldIrq);
NbtCancelCancelRoutine (pRcvIrp);
CTEIoComplete (pRcvIrp, STATUS_CANCELLED, 0);
}
else
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
status = STATUS_SUCCESS;
}
else
{
ASSERT (0);
status = STATUS_INVALID_HANDLE;
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt.NTCleanUpWinsAddr: pWins=<%p>, status=<%x>\n", pWins, status));
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTCloseWinsAddr(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This Routine handles closing the Wins Object that is used by
by WINS to send and receive name service datagrams on port 137.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status;
CTELockHandle OldIrq;
tWINS_INFO *pWins = NULL;
CTESpinLock(&NbtConfig.JointLock,OldIrq);
//
// if the WINs endpoint structure is allocated, then deallocate it
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pWins = pIrpSp->FileObject->FsContext;
if (NBT_VERIFY_HANDLE (pWins, NBT_VERIFY_WINS_DOWN))
{
pWins->Verify += 10;
RemoveEntryList (&pWins->Linkage);
CTEMemFree (pWins);
pIrpSp->FileObject->FsContext2 = (PVOID)NBT_CONTROL_TYPE;
status = STATUS_SUCCESS;
}
else
{
ASSERT (0);
status = STATUS_INVALID_HANDLE;
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt.NTCloseWinsAddr: pWins=<%p>, status=<%x>\n", pWins, status));
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
WinsSetInformation(
IN tWINS_INFO *pWins,
IN tWINS_SET_INFO *pWinsSetInfo
)
{
CTELockHandle OldIrq;
CTESpinLock(&NbtConfig.JointLock,OldIrq);
if ((pWins == pWinsInfo) &&
(pWinsSetInfo->IpAddress))
{
pWins->IpAddress = pWinsSetInfo->IpAddress;
pWins->pDeviceContext = GetDeviceWithIPAddress (pWinsSetInfo->IpAddress);
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
return (STATUS_SUCCESS);
}
//----------------------------------------------------------------------------
VOID
InitiateRefresh (
)
/*++
Routine Description:
This routine tries to refresh all names with WINS on THIS node.
Arguments:
pIrp - Wins Rcv Irp
Return Value:
STATUS_PENDING if the buffer is to be held on to , the normal case.
Notes:
--*/
{
CTELockHandle OldIrq;
PLIST_ENTRY pHead;
PLIST_ENTRY pEntry;
ULONG Count;
ULONG NumberNames;
//
// be sure all net cards have this card as the primary wins
// server since Wins has to answer name queries for this
// node.
//
CTESpinLock(&NbtConfig.JointLock,OldIrq);
if (!(NodeType & BNODE))
{
LONG i;
Count = 0;
NumberNames = 0;
for (i=0 ;i < NbtConfig.pLocalHashTbl->lNumBuckets ;i++ )
{
pHead = &NbtConfig.pLocalHashTbl->Bucket[i];
pEntry = pHead;
while ((pEntry = pEntry->Flink) != pHead)
{
NumberNames++;
}
}
while (Count < COUNT_MAX)
{
if (!(NbtConfig.GlobalRefreshState & NBT_G_REFRESHING_NOW))
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
ReRegisterLocalNames(NULL, FALSE);
break;
}
else
{
LARGE_INTEGER Timout;
NTSTATUS Locstatus;
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt:Waiting for Refresh to finish, so names can be reregistered\n"));
CTESpinFree(&NbtConfig.JointLock,OldIrq);
//
// set a timeout that should be long enough to wait
// for all names to fail registration with a down
// wins server.
//
// 2 sec*3 retries * 8 names / 5 = 9 seconds a shot.
// for a total of 90 seconds max.
//
Timout.QuadPart = Int32x32To64(
MILLISEC_TO_100NS/(COUNT_MAX/2),
(NbtConfig.uRetryTimeout*NbtConfig.uNumRetries)
*NumberNames);
Timout.QuadPart = -(Timout.QuadPart);
//
// wait for a few seconds and try again.
//
Locstatus = KeDelayExecutionThread(
KernelMode,
FALSE, // Alertable
&Timout); // Timeout
Count++;
if (Count < COUNT_MAX)
{
CTESpinLock(&NbtConfig.JointLock,OldIrq);
}
}
}
}
else
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
}
//----------------------------------------------------------------------------
NTSTATUS
RcvIrpFromWins(
IN PCTE_IRP pIrp
)
/*++
Routine Description:
This function takes the rcv irp posted by WINS and decides if there are
any datagram queued waiting to go up to WINS. If so then the datagram
is copied to the WINS buffer and passed back up. Otherwise the irp is
held by Netbt until a datagram does come in.
Arguments:
pIrp - Wins Rcv Irp
Return Value:
STATUS_PENDING if the buffer is to be held on to , the normal case.
Notes:
--*/
{
NTSTATUS status;
NTSTATUS Locstatus;
tREM_ADDRESS *pWinsBuffer;
tWINSRCV_BUFFER *pBuffer;
PLIST_ENTRY pEntry;
CTELockHandle OldIrq;
tWINS_INFO *pWins;
PIO_STACK_LOCATION pIrpSp;
PMDL pMdl;
ULONG CopyLength;
ULONG DgramLength;
ULONG BufferLength;
status = STATUS_INVALID_HANDLE;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pWins = pIrpSp->FileObject->FsContext;
CTESpinLock(&NbtConfig.JointLock,OldIrq);
if (!RefreshedYet)
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
InitiateRefresh();
CTESpinLock(&NbtConfig.JointLock,OldIrq);
RefreshedYet = TRUE;
}
if ((!pWins) || (pWins != pWinsInfo))
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
NTIoComplete(pIrp,status,0);
return(status);
}
if (!IsListEmpty(&pWins->RcvList))
{
//
// There is at least one datagram waiting to be received
//
pEntry = RemoveHeadList(&pWins->RcvList);
pBuffer = CONTAINING_RECORD(pEntry,tWINSRCV_BUFFER,Linkage);
//
// Copy the datagram and the source address to WINS buffer and return to WINS
//
if ((pMdl = pIrp->MdlAddress) &&
(pWinsBuffer = MmGetSystemAddressForMdlSafe (pIrp->MdlAddress, HighPagePriority)))
{
BufferLength = MmGetMdlByteCount(pMdl);
DgramLength = pBuffer->DgramLength;
CopyLength = (DgramLength <= BufferLength) ? DgramLength : BufferLength;
CTEMemCopy ((PVOID)pWinsBuffer, (PVOID)&pBuffer->Address.Family, CopyLength);
ASSERT(pWinsBuffer->Port);
ASSERT(pWinsBuffer->IpAddress);
if (CopyLength < DgramLength)
{
Locstatus = STATUS_BUFFER_OVERFLOW;
}
else
{
Locstatus = STATUS_SUCCESS;
}
}
else
{
CopyLength = 0;
Locstatus = STATUS_UNSUCCESSFUL;
}
//
// subtract from the total amount buffered for WINS since we are
// passing a datagram up to WINS now.
//
pWins->RcvMemoryAllocated -= pBuffer->DgramLength;
CTEMemFree(pBuffer);
//
// pass the irp up to WINS
//
CTESpinFree(&NbtConfig.JointLock,OldIrq);
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt:Returning Wins rcv Irp immediately with queued dgram, status=%X,pIrp=%X\n"
,status,pIrp));
pIrp->IoStatus.Information = CopyLength;
pIrp->IoStatus.Status = Locstatus;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
return(STATUS_SUCCESS);
}
if (pWins->RcvIrp)
{
status = STATUS_NOT_SUPPORTED;
}
else
{
status = NTCheckSetCancelRoutine(pIrp, NbtCancelWinsIrp, NULL);
if (NT_SUCCESS(status))
{
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt:Holding onto Wins Rcv Irp, pIrp =%Xstatus=%X\n", status,pIrp));
pWins->RcvIrp = pIrp;
status = STATUS_PENDING;
}
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
if (!NT_SUCCESS(status))
{
NTIoComplete(pIrp,status,0);
}
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
PassNamePduToWins (
IN tDEVICECONTEXT *pDeviceContext,
IN PVOID pSrcAddress,
IN tNAMEHDR UNALIGNED *pNameSrv,
IN ULONG uNumBytes
)
/*++
Routine Description:
This function is used to allow NBT to pass name query service Pdu's to
WINS. Wins posts a Rcv irp to Netbt. If the Irp is here then simply
copy the data to the irp and return it, otherwise buffer the data up
to a maximum # of bytes. Beyond that limit the datagrams are discarded.
If Retstatus is not success then the pdu will also be processed by
nbt. This allows nbt to process packets when wins pauses and
its list of queued buffers is exceeded.
Arguments:
pDeviceContext - card that the request can in on
pSrcAddress - source address
pNameSrv - ptr to the datagram
uNumBytes - length of datagram
Return Value:
STATUS_PENDING if the buffer is to be held on to , the normal case.
Notes:
--*/
{
NTSTATUS Retstatus;
NTSTATUS status;
tREM_ADDRESS *pWinsBuffer;
PCTE_IRP pIrp;
CTELockHandle OldIrq;
PTRANSPORT_ADDRESS pSourceAddress;
ULONG SrcAddress;
SHORT SrcPort;
//
// Get the source port and ip address, since WINS needs this information.
//
pSourceAddress = (PTRANSPORT_ADDRESS)pSrcAddress;
SrcAddress = ((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->in_addr;
SrcPort = ((PTDI_ADDRESS_IP)&pSourceAddress->Address[0].Address[0])->sin_port;
CTESpinLock(&NbtConfig.JointLock,OldIrq);
Retstatus = STATUS_SUCCESS;
if (pWinsInfo)
{
if (!pWinsInfo->RcvIrp)
{
//
// Queue the name query pdu if we have not exeeded our current queue
// length
//
if (pWinsInfo->RcvMemoryAllocated < pWinsInfo->RcvMemoryMax)
{
tWINSRCV_BUFFER *pBuffer;
pBuffer = NbtAllocMem(uNumBytes + sizeof(tWINSRCV_BUFFER)+8,NBT_TAG('v'));
if (pBuffer)
{
//
// check if it is a name reg from this node
//
if (pNameSrv->AnCount == WINS_SIGNATURE)
{
pNameSrv->AnCount = 0;
pBuffer->Address.Family = AF_UNIX;
}
else
{
pBuffer->Address.Family = AF_INET;
}
CTEMemCopy((PUCHAR)((PUCHAR)pBuffer + sizeof(tWINSRCV_BUFFER)),
(PVOID)pNameSrv,uNumBytes);
pBuffer->Address.Port = SrcPort;
pBuffer->Address.IpAddress = SrcAddress;
pBuffer->Address.LengthOfBuffer = uNumBytes;
ASSERT(pBuffer->Address.Port);
ASSERT(pBuffer->Address.IpAddress);
// total amount allocated
pBuffer->DgramLength = uNumBytes + sizeof(tREM_ADDRESS);
//
// Keep track of the total amount buffered so that we don't
// eat up all non-paged pool buffering for WINS
//
pWinsInfo->RcvMemoryAllocated += pBuffer->DgramLength;
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt:Buffering Wins Rcv - no Irp, status=%X\n"));
InsertTailList(&pWinsInfo->RcvList,&pBuffer->Linkage);
}
}
else
{
// this ret status will allow netbt to process the packet.
//
Retstatus = STATUS_INSUFFICIENT_RESOURCES;
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
else
{
PMDL pMdl;
ULONG CopyLength;
ULONG BufferLength;
//
// The recv irp is here so copy the data to its buffer and
// pass it up to WINS
//
pIrp = pWinsInfo->RcvIrp;
pWinsInfo->RcvIrp = NULL;
CTESpinFree(&NbtConfig.JointLock,OldIrq);
//
// Copy the datagram and the source address to WINS buffer and return to WINS
//
if ((!(pMdl = pIrp->MdlAddress)) ||
((BufferLength = MmGetMdlByteCount(pMdl)) < sizeof(tREM_ADDRESS)) ||
(!(pWinsBuffer = MmGetSystemAddressForMdlSafe (pIrp->MdlAddress, HighPagePriority))))
{
status = STATUS_INSUFFICIENT_RESOURCES;
CopyLength = 0;
}
else
{
if (BufferLength >= (uNumBytes + sizeof(tREM_ADDRESS)))
{
CopyLength = uNumBytes;
}
else
{
CopyLength = BufferLength - sizeof(tREM_ADDRESS);
}
//
// check if it is a name reg from this node
//
if (pNameSrv->AnCount == WINS_SIGNATURE)
{
pNameSrv->AnCount = 0;
pWinsBuffer->Family = AF_UNIX;
}
else
{
pWinsBuffer->Family = AF_INET;
}
CTEMemCopy((PVOID)((PUCHAR)pWinsBuffer + sizeof(tREM_ADDRESS)), (PVOID)pNameSrv, CopyLength);
pWinsBuffer->Port = SrcPort;
pWinsBuffer->IpAddress = SrcAddress;
pWinsBuffer->LengthOfBuffer = uNumBytes;
ASSERT(pWinsBuffer->Port);
ASSERT(pWinsBuffer->IpAddress);
//
// pass the irp up to WINS
//
if (CopyLength < uNumBytes)
{
status = STATUS_BUFFER_OVERFLOW;
}
else
{
status = STATUS_SUCCESS;
}
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt:Returning Wins Rcv Irp - data from net, Length=%X,pIrp=%X\n"
,uNumBytes,pIrp));
}
NTIoComplete(pIrp,status,CopyLength);
}
}
else
{
//
// this ret status will allow netbt to process the packet.
//
Retstatus = STATUS_INSUFFICIENT_RESOURCES;
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
return(Retstatus);
}
//----------------------------------------------------------------------------
VOID
NbtCancelWinsIrp(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine handles the cancelling a WinsRcv Irp. It must release the
cancel spin lock before returning re: IoCancelIrp().
Arguments:
Return Value:
The final status from the operation.
--*/
{
KIRQL OldIrq;
PIO_STACK_LOCATION pIrpSp;
tWINS_INFO *pWins;
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt.NbtCancelWinsIrp: Got a Cancel !!! *****************\n"));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pWins = (tWINS_INFO *)pIrpSp->FileObject->FsContext;
IoReleaseCancelSpinLock(pIrp->CancelIrql);
CTESpinLock(&NbtConfig.JointLock,OldIrq);
//
// Be sure that PassNamePduToWins has not taken the RcvIrp for a
// Rcv just now.
//
if ((NBT_VERIFY_HANDLE (pWins, NBT_VERIFY_WINS_ACTIVE)) &&
(pWins->RcvIrp == pIrp))
{
pWins->RcvIrp = NULL;
CTESpinFree(&NbtConfig.JointLock,OldIrq);
pIrp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT);
}
else
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
}
//----------------------------------------------------------------------------
VOID
NbtCancelWinsSendIrp(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine handles the cancelling a WinsRcv Irp. It must release the
cancel spin lock before returning re: IoCancelIrp().
Arguments:
Return Value:
The final status from the operation.
--*/
{
KIRQL OldIrq;
PLIST_ENTRY pHead;
PLIST_ENTRY pEntry;
PIO_STACK_LOCATION pIrpSp;
tWINS_INFO *pWins;
BOOLEAN Found;
PIRP pIrpList;
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt.NbtCancelWinsSendIrp: Got a Cancel !!! *****************\n"));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pWins = (tWINS_INFO *)pIrpSp->FileObject->FsContext;
IoReleaseCancelSpinLock(pIrp->CancelIrql);
CTESpinLock(&NbtConfig.JointLock,OldIrq);
if (pWins == pWinsInfo)
{
//
// find the matching irp on the list and remove it
//
pHead = &pWinsInfo->SendList;
pEntry = pHead;
Found = FALSE;
while ((pEntry = pEntry->Flink) != pHead)
{
pIrpList = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry);
if (pIrp == pIrpList)
{
RemoveEntryList(pEntry);
Found = TRUE;
}
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
if (Found)
{
pIrp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT);
}
}
else
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
}
//----------------------------------------------------------------------------
NTSTATUS
WinsSendDatagram(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp,
IN BOOLEAN MustSend)
/*++
Routine Description:
This Routine handles sending a datagram down to the transport. MustSend
it set true by the Send Completion routine when it attempts to send
one of the queued datagrams, in case we still don't pass the memory
allocated check and refuse to do the send - sends will just stop then without
this boolean.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status;
tWINS_INFO *pWins;
tREM_ADDRESS *pSendAddr;
PVOID pDgram;
ULONG DgramLength;
tDGRAM_SEND_TRACKING *pTracker;
CTELockHandle OldIrq;
BOOLEAN fIsWinsDevice = FALSE;
ULONG DataSize;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pWins = (tWINS_INFO *)pIrpSp->FileObject->FsContext;
status = STATUS_UNSUCCESSFUL;
if (!(pSendAddr = (tREM_ADDRESS *) MmGetSystemAddressForMdlSafe (pIrp->MdlAddress, HighPagePriority)))
{
pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Bug# 234600: Check if the DataSize is correct
//
DataSize = MmGetMdlByteCount (pIrp->MdlAddress);
if ((DataSize < sizeof(tREM_ADDRESS)) ||
((DataSize - sizeof(tREM_ADDRESS)) < pSendAddr->LengthOfBuffer))
{
pIrp->IoStatus.Status = STATUS_INVALID_BLOCK_LENGTH;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
return STATUS_INVALID_BLOCK_LENGTH;
}
//
// check if it is a name that is registered on this machine
//
if (pSendAddr->Family == AF_UNIX)
{
status = CheckIfLocalNameActive(pSendAddr);
}
CTESpinLock(&NbtConfig.JointLock,OldIrq);
if ((pWins) &&
(pWins == pWinsInfo))
{
if (pDeviceContext == pWinsDeviceContext)
{
fIsWinsDevice = TRUE;
if (!(pDeviceContext = pWinsInfo->pDeviceContext) ||
!(NBT_REFERENCE_DEVICE (pDeviceContext, REF_DEV_WINS, TRUE)))
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
// status = STATUS_INVALID_HANDLE;
status = STATUS_SUCCESS;
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
return (status);
}
}
if ((pWins->SendMemoryAllocated < pWins->SendMemoryMax) || MustSend)
{
if (pSendAddr->IpAddress != 0)
{
DgramLength = pSendAddr->LengthOfBuffer;
pDgram = WinsAllocMem (pWins, DgramLength, FALSE);
if (pDgram)
{
CTEMemCopy(pDgram, (PVOID)((PUCHAR)pSendAddr+sizeof(tREM_ADDRESS)), DgramLength);
//
// get a buffer for tracking Dgram Sends
//
status = GetTracker(&pTracker, NBT_TRACKER_SEND_WINS_DGRAM);
if (NT_SUCCESS(status))
{
pTracker->SendBuffer.pBuffer = NULL;
pTracker->SendBuffer.Length = 0;
pTracker->SendBuffer.pDgramHdr = pDgram;
pTracker->SendBuffer.HdrLength = DgramLength;
pTracker->pClientIrp = NULL;
pTracker->pDeviceContext = pDeviceContext;
pTracker->pNameAddr = NULL;
pTracker->pDestName = NULL;
pTracker->UnicodeDestName = NULL;
pTracker->pClientEle = NULL;
pTracker->AllocatedLength = DgramLength;
pTracker->ClientContext = IntToPtr(pWins->WinsSignature);
CTESpinFree(&NbtConfig.JointLock,OldIrq);
// send the Datagram...
status = UdpSendDatagram (pTracker,
ntohl(pSendAddr->IpAddress),
WinsDgramCompletion,
pTracker, // context for completion
(USHORT)ntohs(pSendAddr->Port),
NBT_NAME_SERVICE);
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt:Doing Wins Send, status=%X\n",status));
// sending the datagram could return status pending,
// but since we have buffered the dgram, return status
// success to the client
//
status = STATUS_SUCCESS;
//
// Fill in the sent size
//
pIrp->IoStatus.Information = DgramLength;
}
else
{
WinsFreeMem (pWins, (PVOID)pDgram,DgramLength,FALSE);
CTESpinFree(&NbtConfig.JointLock,OldIrq);
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
status = STATUS_INVALID_PARAMETER;
}
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
}
else
{
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt:Holding onto Buffering Wins Send, status=%X\n"));
//
// Hold onto the datagram till memory frees up
//
InsertTailList(&pWins->SendList,&pIrp->Tail.Overlay.ListEntry);
status = NTCheckSetCancelRoutine(pIrp,NbtCancelWinsSendIrp,NULL);
if (!NT_SUCCESS(status))
{
RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
CTESpinFree(&NbtConfig.JointLock,OldIrq);
NTIoComplete(pIrp,status,0);
}
else
{
status = STATUS_PENDING;
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
}
if (fIsWinsDevice)
{
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_WINS, FALSE);
}
}
else
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
status = STATUS_INVALID_HANDLE;
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
}
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
CheckIfLocalNameActive(
IN tREM_ADDRESS *pSendAddr
)
/*++
Routine Description
This routine checks if this is a name query response and if the
name is still active on the local node.
Arguments:
pMdl = ptr to WINS Mdl
Return Values:
VOID
--*/
{
NTSTATUS status;
tNAMEHDR UNALIGNED *pNameHdr;
tNAMEADDR *pResp;
UCHAR pName[NETBIOS_NAME_SIZE];
PUCHAR pScope;
ULONG lNameSize;
CTELockHandle OldIrq;
pNameHdr = (tNAMEHDR UNALIGNED *)((PUCHAR)pSendAddr + sizeof(tREM_ADDRESS));
//
// Be sure it is a name query PDU that we are checking
//
if (((pNameHdr->OpCodeFlags & NM_FLAGS_MASK) == OP_QUERY) ||
((pNameHdr->OpCodeFlags & NM_FLAGS_MASK) == OP_RELEASE))
{
status = ConvertToAscii ((PCHAR)&pNameHdr->NameRR.NameLength,
pSendAddr->LengthOfBuffer,
pName,
&pScope,
&lNameSize);
if (NT_SUCCESS(status))
{
//
// see if the name is still active in the local hash table
//
CTESpinLock(&NbtConfig.JointLock,OldIrq);
status = FindInHashTable(NbtConfig.pLocalHashTbl, pName, pScope, &pResp);
if ((pNameHdr->OpCodeFlags & NM_FLAGS_MASK) == OP_QUERY)
{
if (NT_SUCCESS(status))
{
//
// if not resolved then set to negative name query resp.
//
if (!(pResp->NameTypeState & STATE_RESOLVED))
{
pNameHdr->OpCodeFlags |= htons(NAME_ERROR);
}
}
//
// We can have a scenario where the local machine was a DC
// at one time, so it set the UNIX to tell Wins when registering
// the local name.A However, once that machine is downgraded,
// Wins will still have the UNIX flag set for that record if
// there were other DC's also present.
// Thus, we can have the following scenario where the machine
// is currently not a DC, but the UNIX flag is set in the response
// so we should not mark the name in Error. This would not
// be a problem if the client is configured with other Wins
// server addresses, but otherwise it could cause problems!
// Bug # 54659
//
else if (pName[NETBIOS_NAME_SIZE-1] != SPECIAL_GROUP_SUFFIX)
{
pNameHdr->OpCodeFlags |= htons(NAME_ERROR);
}
}
else
{
//
// check if it is a release response - if so we must have
// received a name release request, so mark the name in
// conflict and return a positive release response.
//
// Note: The case we are looking at here is if another Wins
// sent a NameRelease demand for some name to the local machine.
// Since we pass all name releases up to Wins, NetBT will
// not get a chance to determine if it is a local name when
// the release first came in.
// Typically, Wins should make the call properly as to whether
// NetBT should mark the local name in conflict or not, but
// it has been observed that Wins displayed inconsistent behavior
// setting the UNIX flag only if the local machine was the last
// to register/refresh the name (Bug # 431042).
// For now, we will remove this functionality for Group names.
//
if (pNameHdr->OpCodeFlags & OP_RESPONSE)
{
//
// Bug # 206192: If we are sending the response to
// ourselves, don't put the name into conflict
// (could be due to NbtStat -RR!)
//
if (NT_SUCCESS(status) &&
(pResp->NameTypeState & STATE_RESOLVED) &&
(pResp->NameTypeState & NAMETYPE_UNIQUE) &&
!(pNameHdr->OpCodeFlags & FL_RCODE) && // Only for positive name release response
!(SrcIsUs(ntohl(pSendAddr->IpAddress))))
{
NbtLogEvent (EVENT_NBT_NAME_RELEASE, pSendAddr->IpAddress, 0x122);
pResp->NameTypeState &= ~NAME_STATE_MASK;
pResp->NameTypeState |= STATE_CONFLICT;
pResp->ConflictMask |= pResp->AdapterMask;
}
}
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
}
//
// the name is not in the local table so fail the datagram send attempt
//
return(STATUS_SUCCESS);
}
//----------------------------------------------------------------------------
VOID
WinsDgramCompletion(
IN tDGRAM_SEND_TRACKING *pTracker,
IN NTSTATUS status,
IN ULONG Length
)
/*++
Routine Description
This routine cleans up after a data gram send.
Arguments:
pTracker
status
Length
Return Values:
VOID
--*/
{
CTELockHandle OldIrq;
LIST_ENTRY *pEntry;
PIRP pIrp;
BOOLEAN MustSend;
#ifdef _PNP_POWER_
tDEVICECONTEXT *pDeviceContext;
#endif
//
// free the buffer used for sending the data and the tracker - note
// that the datagram header and the send buffer are allocated as one
// chunk.
//
CTESpinLock(&NbtConfig.JointLock,OldIrq);
if ((pWinsInfo) &&
(pTracker->ClientContext == IntToPtr(pWinsInfo->WinsSignature)))
{
WinsFreeMem(pWinsInfo,
(PVOID)pTracker->SendBuffer.pDgramHdr,
pTracker->AllocatedLength,
FALSE);
if (!IsListEmpty(&pWinsInfo->SendList))
{
#ifdef _PNP_POWER_
//
// If there are no devices available to send this request on,
// complete all pending requests gracefully
//
if (!(pDeviceContext = pWinsInfo->pDeviceContext) ||
!(NBT_REFERENCE_DEVICE (pDeviceContext, REF_DEV_WINS, TRUE)))
{
status = STATUS_PLUGPLAY_NO_DEVICE;
while (!IsListEmpty(&pWinsInfo->SendList))
{
pEntry = RemoveHeadList(&pWinsInfo->SendList);
pIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry);
CTESpinFree(&NbtConfig.JointLock,OldIrq);
NbtCancelCancelRoutine (pIrp);
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT);
CTESpinLock(&NbtConfig.JointLock,OldIrq);
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
FreeTracker (pTracker, RELINK_TRACKER);
return;
}
#endif // _PNP_POWER_
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt:Sending another Wins Dgram that is Queued to go\n"));
pEntry = RemoveHeadList(&pWinsInfo->SendList);
pIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry);
CTESpinFree(&NbtConfig.JointLock,OldIrq);
NbtCancelCancelRoutine (pIrp);
//
// Send this next datagram
//
status = WinsSendDatagram(pDeviceContext,
pIrp,
MustSend = TRUE);
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_WINS, FALSE);
}
else
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
}
else
{
//
// just free the memory since WINS has closed its address handle.
//
CTEMemFree((PVOID)pTracker->SendBuffer.pDgramHdr);
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
FreeTracker (pTracker, RELINK_TRACKER);
}
//----------------------------------------------------------------------------
PVOID
WinsAllocMem(
IN tWINS_INFO *pWinsContext,
IN ULONG Size,
IN BOOLEAN Rcv
)
/*++
Routine Description:
This Routine handles allocating memory and keeping track of how
much has been allocated.
Arguments:
Size - number of bytes to allocate
Rcv - boolean that indicates if it is rcv or send buffering
Return Value:
ptr to the memory allocated
--*/
{
if (Rcv)
{
if (pWinsContext->RcvMemoryAllocated > pWinsContext->RcvMemoryMax)
{
return NULL;
}
else
{
pWinsContext->RcvMemoryAllocated += Size;
return (NbtAllocMem(Size,NBT_TAG('v')));
}
}
else
{
if (pWinsContext->SendMemoryAllocated > pWinsContext->SendMemoryMax)
{
return(NULL);
}
else
{
pWinsContext->SendMemoryAllocated += Size;
return(NbtAllocMem(Size,NBT_TAG('v')));
}
}
}
//----------------------------------------------------------------------------
VOID
WinsFreeMem(
IN tWINS_INFO *pWinsContext,
IN PVOID pBuffer,
IN ULONG Size,
IN BOOLEAN Rcv
)
/*++
Routine Description:
This Routine handles freeing memory and keeping track of how
much has been allocated.
Arguments:
pBuffer - buffer to free
Size - number of bytes to allocate
Rcv - boolean that indicates if it is rcv or send buffering
Return Value:
none
--*/
{
if (pWinsContext)
{
if (Rcv)
{
pWinsContext->RcvMemoryAllocated -= Size;
}
else
{
pWinsContext->SendMemoryAllocated -= Size;
}
}
CTEMemFree(pBuffer);
}