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.
 
 
 
 
 
 

6984 lines
221 KiB

/*++
Copyright (c) 1989-1993 Microsoft Corporation
Module Name:
Ntisol.h
Abstract:
This file contains the interface between the TDI interface on the top
of NBT and the OS independent code. It takes the parameters out of the
irps and puts in into procedure calls for the OS independent code (which
is mostly in name.c).
Author:
Jim Stewart (Jimst) 10-2-92
Revision History:
Notes:
The Nbt routines have been modified to include an additional parameter, i.e,
the transport type. This transport type is used primarily to distinguish the
NETBIOS over TCP/IP implementation from the Messaging Over TCP/IP implementation.
The primary difference between the two being that the later uses the NETBT framing
without the associated NETBIOS name registartion/resolution. It primarily uses
DNS for name resolution. All the names that are registered for the new transport
are local names and are not defended on the network.
The primary usage is in conjuntion with an extended NETBIOS address type defined
in tdi.h. The NETBIOS name resolution/registration traffic occurs in two phases.
The first phase contains all the broadcast traffic that ensues during NETBIOS
name registration. Subsequently the NETBT implementation queries the remote
adapter status to choose the appropriate called name. This approach results in
additional traffic for querying the remote adapter status. The new address type
defined in tdi.h enables the client of netbt to supply the name to be used in
NETBT session setup. This avoids the network traffic for querying the adapter
status.
The original design which has not been fully implemented involved exposing two
device objects from the NetBt driver -- the NetBt device object which would be
the full implementation of NETBIOS over TCP/IP and the MoTcp device object which
would be the implementation of Messaging over TCP/IP. The MoTcp device object
would use the same port address as NetBt and use the same session setup protocol
to talk to remote machines running old NetBt drivers and machines running new
NetBt drivers.
The transport type variations combined with the address type changes present us
with four different cases which need to be handled -- the NetBt transport being
presented with a TDI_ADDRESS_NETBIOS_EX structure, the NetBt transport being
prsented with a TDI_ADDRESS_NETBIOS structure and the same two cases for the
MoTcp transport.
--*/
#include "precomp.h"
#include "ntprocs.h"
#include <ipinfo.h>
#include <ntddtcp.h> // for IOCTL_TCP_SET_INFORMATION_EX
#ifdef RASAUTODIAL
#include <acd.h>
#include <acdapi.h>
#endif // RASAUTODIAL
#include <tcpinfo.h>
#include <tdiinfo.h>
#include "ntisol.tmh"
#if BACK_FILL
#define SESSION_HDR_SIZE sizeof(tSESSIONHDR)
#endif
NTSTATUS
SendCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
NTSendCleanupConnection(
IN tCONNECTELE *pConnEle,
IN PVOID pCompletionRoutine,
IN PVOID Context,
IN PIRP pIrp);
VOID
DpcSendSession(
IN PKDPC pDpc,
IN PVOID Context,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
NBT_WORK_ITEM_CONTEXT *
FindLmhSvcRequest(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp,
IN tLMHSVC_REQUESTS *pLmhRequest
);
NTSTATUS
NbtCancelCancelRoutine(
IN PIRP pIrp
);
#ifdef RASAUTODIAL
extern ACD_DRIVER AcdDriverG;
BOOLEAN
NbtCancelPostConnect(
IN PIRP pIrp
);
#endif // RASAUTODIAL
NTSTATUS
NbtQueryGetAddressInfo(
IN PIO_STACK_LOCATION pIrpSp,
OUT PVOID *ppBuffer,
OUT ULONG *pSize
);
VOID
NbtCancelConnect(
IN PDEVICE_OBJECT pDeviceContext,
IN PIRP pIrp
);
VOID
NbtCancelReceive(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
);
NTSTATUS
GetIpAddrs(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp
);
typedef struct
{
struct _DeviceContext *pDeviceContext;
PIRP pClientIrp;
PIRP pLocalIrp;
tCONNECTELE *pConnEle;
PTA_NETBT_INTERNAL_ADDRESS pTransportAddress;
TDI_CONNECTION_INFORMATION LocalConnectionInformation;
BOOLEAN ProcessingDone;
TDI_ADDRESS_NETBIOS_UNICODE_EX *pUnicodeAddress; // First Readable buffer in the transport address list
TDI_ADDRESS_NETBIOS_UNICODE_EX *pReturnBuffer; // First writable buffer in the transport address list
LONG CurrIndex, NumberOfAddresses;
LONG TaAddressLength, RemainingAddressLength;
PUCHAR pTaAddress;
} NBT_DELAYED_CONNECT_CONTEXT, *PNBT_DELAYED_CONNECT_CONTEXT;
extern POBJECT_TYPE *IoFileObjectType;
NTSTATUS
InitDelayedNbtProcessConnect(
IN PNBT_DELAYED_CONNECT_CONTEXT pDelConnCtx
);
HANDLE
NbtOpenParametersRegistry(
VOID
);
//******************* Pageable Routine Declarations ****************
#ifdef ALLOC_PRAGMA
#pragma CTEMakePageable(PAGE, NTOpenControl)
#pragma CTEMakePageable(PAGE, NTOpenAddr)
#pragma CTEMakePageable(PAGE, NTCloseAddress)
#pragma CTEMakePageable(PAGE, NTOpenConnection)
#pragma CTEMakePageable(PAGE, NTAssocAddress)
#pragma CTEMakePageable(PAGE, NTCloseConnection)
#pragma CTEMakePageable(PAGE, NTSetSharedAccess)
#pragma CTEMakePageable(PAGE, NTCheckSharedAccess)
#pragma CTEMakePageable(PAGE, NTCleanUpConnection)
#pragma CTEMakePageable(PAGE, NTCleanUpAddress)
#pragma CTEMakePageable(PAGE, NTDisAssociateAddress)
#pragma CTEMakePageable(PAGE, NTListen)
#pragma CTEMakePageable(PAGE, DelayedNbtProcessConnect)
#pragma CTEMakePageable(PAGE, InitDelayedNbtProcessConnect)
#pragma CTEMakePageable(PAGE, DispatchIoctls)
#pragma CTEMakePageable(PAGE, NTSendDatagram)
#pragma CTEMakePageable(PAGE, NTSetInformation)
#pragma CTEMakePageable(PAGE, NTSetEventHandler)
#pragma CTEMakePageable(PAGE, NbtOpenParametersRegistry)
//
// Should not be pageable since AFD can call us at raised Irql in case of AcceptEx.
//
// #pragma CTEMakePageable(PAGE, NTQueryInformation)
#endif
//******************* Pageable Routine Declarations ****************
int check_unicode_string(IN PUNICODE_STRING str);
//----------------------------------------------------------------------------
NTSTATUS
NTOpenControl(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles opening the control object, which represents the
driver itself. For example QueryInformation uses the control object
as the destination of the Query message.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status;
CTEPagedCode();
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pIrpSp->FileObject->FsContext2 = (PVOID)(NBT_CONTROL_TYPE);
// return a ptr the control endpoint
pIrpSp->FileObject->FsContext = (PVOID)pNbtGlobConfig->pControlObj;
//
// the following call opens a control object with the transport below since
// several of the query information calls are passed directly on to the
// transport below.
//
if (!pDeviceContext->pControlFileObject)
{
status = NbtTdiOpenControl(pDeviceContext);
}
else
status = STATUS_SUCCESS;
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTOpenAddr(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp,
IN PFILE_FULL_EA_INFORMATION ea)
/*++
Routine Description:
This Routine handles converting an Open Address Request from an IRP to
a procedure call so that NbtOpenAddress can be called in an OS independent
manner.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
TDI_REQUEST Request;
TA_ADDRESS *pAddress;
int j;
NTSTATUS status=STATUS_INVALID_ADDRESS_COMPONENT;
ULONG BufferLength, MinBufferLength;
TRANSPORT_ADDRESS UNALIGNED *pTransportAddr; // structure containing counted array of TA_ADDRESS
CTEPagedCode();
// make up the Request data structure from the IRP info
Request.Handle.AddressHandle = NULL;
//
// Verify Minimum Buffer length!
// Bug#: 120683
//
BufferLength = ea->EaValueLength;
if (BufferLength < sizeof(TA_NETBIOS_ADDRESS))
{
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("Nbt.NTOpenAddr[1]: ...Rejecting Open Address request -- BufferLength<%d> < Min<%d>\n",
BufferLength, sizeof(TA_NETBIOS_ADDRESS)));
NbtTrace(NBT_TRACE_LOCALNAMES, ("Rejecting Open Address request -- BufferLength<%d> < Min<%d>",
BufferLength, sizeof(TA_NETBIOS_ADDRESS)));
return (status);
}
MinBufferLength = FIELD_OFFSET(TRANSPORT_ADDRESS,Address); // Set for Address[0]
pTransportAddr = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1];
pAddress = (TA_ADDRESS *) &pTransportAddr->Address[0]; // this includes the address type + the actual address
//
// The Transport Address information is packed as follows:
// Field: Length:
// ------ -------
// TAAddressCount --> LONG
//
// Address[0].AddressLength --> USHORT
// Address[0].AddressType --> USHORT
// Address[0].Address.. --> Address[0].AddressLength
//
// Address[1].AddressLength --> USHORT
// Address[1].AddressType --> USHORT
// Address[1].Address.. --> Address[1].AddressLength
// :
//
// loop through the addresses passed in until ONE is successfully used
// *TODO* do we need this loop or can we just assume the name is at the start of the address buffer...
// *TODO* does this need to handle multiple names??
for (j=0; BufferLength >= MinBufferLength && j<pTransportAddr->TAAddressCount ;j++ )
{
//
// do we have enough data to access the "AddressLength" field?
//
MinBufferLength += FIELD_OFFSET(TA_ADDRESS,Address);
if (BufferLength < MinBufferLength) {
return (status);
}
//
// Now we can safely access AddressLength
//
MinBufferLength += pAddress->AddressLength;
if (BufferLength < MinBufferLength) {
return (status);
}
//
// We support only 2 address types:
//
if (pAddress->AddressType == TDI_ADDRESS_TYPE_NETBIOS &&
pAddress->AddressLength >= sizeof(TDI_ADDRESS_NETBIOS)) {
status = STATUS_SUCCESS;
break;
} else if (pAddress->AddressType == TDI_ADDRESS_TYPE_NETBIOS_EX &&
pAddress->AddressLength >= sizeof(TDI_ADDRESS_NETBIOS_EX)) {
status = STATUS_SUCCESS;
break;
}
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("Nbt.NTOpenAddr[2]: ...Rejecting Open Address request for AddressType=<%d>\n",
pAddress->AddressType));
//
// Set pAddress to point to the next address
//
pAddress = (TA_ADDRESS *) ((PUCHAR)pAddress
+ FIELD_OFFSET(TA_ADDRESS,Address)
+ pAddress->AddressLength);
}
if (status == STATUS_SUCCESS) // We found a valid address type!
{
// call the non-NT specific function to open an address
status = NbtOpenAddress(&Request,
pAddress,
pDeviceContext->IpAddress,
pDeviceContext,
(PVOID)pIrp);
if (status != STATUS_SUCCESS) {
NbtTrace(NBT_TRACE_NAMESRV, ("NbtOpenAddress returns %!status!", status));
}
}
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTCloseAddress(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles converting a Close Address Request from an IRP to
a procedure call so that NbtCloseAddress can be called in an OS independent
manner.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
TDI_REQUEST Request;
TDI_REQUEST_STATUS RequestStatus;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status;
tCLIENTELE *pClientEle;
CTEPagedCode();
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pClientEle = Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pClientEle, NBT_VERIFY_CLIENT, NBT_VERIFY_CLIENT_DOWN))
{
ASSERTMSG ("Nbt.NTCloseAddress: ERROR - Invalid Address Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
status = NbtCloseAddress (&Request, &RequestStatus, pDeviceContext, (PVOID)pIrp);
NbtTrace(NBT_TRACE_NAMESRV, ("NbtCloseAddress returns %!status! for ClientEle=%p", status, pClientEle));
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTOpenConnection(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp,
IN PFILE_FULL_EA_INFORMATION ea)
/*++
Routine Description:
This Routine handles converting an Open Connection Request from an IRP to
a procedure call so that NbtOpenConnection can be called in an OS independent
manner. The connection must be associated with an address before it
can be used, except for in inbound call where the client returns the
connection ID in the accept.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
TDI_REQUEST Request;
PIO_STACK_LOCATION pIrpSp;
CONNECTION_CONTEXT ConnectionContext;
NTSTATUS status;
PFILE_OBJECT pFileObject;
ULONG BufferLength;
tCONNECTELE *pConnEle;
CTEPagedCode();
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
// make up the Request data structure from the IRP info
Request.Handle.ConnectionContext = NULL;
//
// Verify Minimum Buffer length!
// Bug#: 120682
//
BufferLength = ea->EaValueLength;
if (BufferLength < sizeof(CONNECTION_CONTEXT))
{
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("Nbt.NTOpenConnection: ERROR -- Open Connection request -- (BufferLength=%d < Min=%d)\n",
BufferLength, sizeof(CONNECTION_CONTEXT)));
ASSERT(0);
return (STATUS_INVALID_ADDRESS_COMPONENT);
}
// the connection context value is stored in the string just after the
// name "connectionContext", and it is most likely unaligned, so just
// copy it out.( 4 bytes of copying ).
CTEMemCopy(&ConnectionContext,
(CONNECTION_CONTEXT)&ea->EaName[ea->EaNameLength+1],
sizeof(CONNECTION_CONTEXT));
// call the non-NT specific function to open an address
status = NbtOpenConnection (&Request, ConnectionContext, pDeviceContext);
pFileObject = pIrpSp->FileObject;
if (!NT_SUCCESS(status))
{
pFileObject->FsContext = NULL;
NbtTrace(NBT_TRACE_OUTBOUND, ("NbtOpenConnection returns %!status!", status));
}
else if (Request.Handle.ConnectionContext)
{
// fill the IRP with successful completion information so we can
// find the connection object given the fileObject later.
pConnEle = pFileObject->FsContext = Request.Handle.ConnectionContext;
if (!NBT_VERIFY_HANDLE (pConnEle, NBT_VERIFY_CONNECTION))
{
ASSERTMSG ("Nbt.NTOpenConnection: ERROR - Invalid Connection Handle\n", 0);
return (STATUS_UNSUCCESSFUL);
}
pFileObject->FsContext2 = (PVOID)(NBT_CONNECTION_TYPE);
pConnEle->pClientFileObject = pFileObject;
NbtTrace(NBT_TRACE_OUTBOUND, ("New connection %p", pConnEle));
status = STATUS_SUCCESS;
}
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTAssocAddress(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles converting an Associate Address Request from an IRP to
a procedure call so that NbtAssociateAddress can be called in an OS independent
manner.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
TDI_REQUEST Request;
PIO_STACK_LOCATION pIrpSp;
PFILE_OBJECT fileObject;
PTDI_REQUEST_KERNEL_ASSOCIATE parameters; // holds address handle
NTSTATUS status;
tCONNECTELE *pConnEle;
tCLIENTELE *pClientEle;
CTEPagedCode();
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pConnEle = Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE (pConnEle, NBT_VERIFY_CONNECTION))
{
ASSERTMSG ("Nbt.NTAssocAddress: ERROR - Invalid Connection Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
// the address handle is buried in the Irp...
parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)&pIrpSp->Parameters;
// now get a pointer to the file object, which points to the address
// element by calling a kernel routine to convert this filehandle into
// a file pointer.
status = ObReferenceObjectByHandle (parameters->AddressHandle,
FILE_READ_DATA,
*IoFileObjectType,
pIrp->RequestorMode,
(PVOID *)&fileObject,
NULL);
IF_DBG(NBT_DEBUG_HANDLES)
KdPrint (("\t ++<%x>====><%x>\tNTAssocAddress->ObReferenceObject, Status = <%x>\n", parameters->AddressHandle, fileObject, status));
if ((NT_SUCCESS(status)) &&
(fileObject->DeviceObject->DriverObject == NbtConfig.DriverObject) && // Bug# 202349
NBT_VERIFY_HANDLE(((tDEVICECONTEXT*)fileObject->DeviceObject), NBT_VERIFY_DEVCONTEXT) && // Bug# 202349
(PtrToUlong(fileObject->FsContext2) == TDI_TRANSPORT_ADDRESS_FILE))
{
pClientEle = fileObject->FsContext;
if (NBT_VERIFY_HANDLE (pClientEle, NBT_VERIFY_CLIENT))
{
// call the non-NT specific function to associate the address with
// the connection
status = NbtAssociateAddress (&Request, pClientEle, (PVOID)pIrp);
NbtTrace(NBT_TRACE_OUTBOUND, ("NbtAssociateAddress returns %!status!", status));
}
else
{
ASSERTMSG ("Nbt.NTAssocAddress: ERROR - Invalid Address Handle\n", 0);
status = STATUS_INVALID_HANDLE;
}
// we are done with the file object, so release the reference
ObDereferenceObject((PVOID)fileObject);
return(status);
}
else
{
return(STATUS_INVALID_HANDLE);
}
}
//----------------------------------------------------------------------------
NTSTATUS
NTCloseConnection(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles converting a Close Connection Request from an IRP to
a procedure call so that NbtCloseConnection can be called in an OS independent
manner.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
TDI_REQUEST Request;
TDI_REQUEST_STATUS RequestStatus;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status;
tCONNECTELE *pConnEle;
CTEPagedCode();
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pConnEle = Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
ASSERTMSG ("Nbt.NTCloseConnection: ERROR - Invalid Connection Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
status = NbtCloseConnection(
&Request,
&RequestStatus,
pDeviceContext,
(PVOID)pIrp);
NbtTrace(NBT_TRACE_OUTBOUND, ("Close connection %p returns %!status!", pConnEle, status));
return(status);
}
//----------------------------------------------------------------------------
VOID
NTSetFileObjectContexts(
IN PIRP pIrp,
IN PVOID FsContext,
IN PVOID FsContext2)
/*++
Routine Description:
This Routine handles fills in two context values in the Irp stack location,
that has to be done in an OS-dependent manner. This routine is called
from NbtOpenAddress() when a name is being registered on the network( i.e.
as a result of OpenAddress).
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PIO_STACK_LOCATION pIrpSp;
PFILE_OBJECT pFileObject;
//
// fill the IRP with context information so we can
// find the address object given the fileObject later.
//
// This must be done here, rather than after the call to NbtOpenAddress
// because that call can complete the Irp before it returns. Soooo,
// in the complete routine for the Irp, if the completion code is not
// good, it Nulls these two context values.
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pFileObject = pIrpSp->FileObject;
pFileObject->FsContext = FsContext;
pFileObject->FsContext2 =FsContext2;
}
//----------------------------------------------------------------------------
VOID
NTClearFileObjectContext(
IN PIRP pIrp
)
/*++
Routine Description:
This Routine clears the context value in the file object when an address
object is closed.
Arguments:
pIrp - a ptr to an IRP
Return Value:
none
--*/
{
PIO_STACK_LOCATION pIrpSp;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
CHECK_PTR(pIrpSp->FileObject);
pIrpSp->FileObject->FsContext = NULL;
}
//----------------------------------------------------------------------------
NTSTATUS
NTSetSharedAccess(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp,
IN tADDRESSELE *pAddress)
/*++
Routine Description:
This Routine handles setting the shared access on the file object.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PACCESS_STATE AccessState;
ULONG DesiredAccess;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status;
static GENERIC_MAPPING AddressGenericMapping = { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL };
CTEPagedCode();
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
if ((pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) ||
(pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE))
{
DesiredAccess = (ULONG)FILE_SHARE_READ;
}
else
{
DesiredAccess = (ULONG)0;
}
IoSetShareAccess (FILE_READ_DATA, DesiredAccess, pIrpSp->FileObject, &pAddress->ShareAccess);
// assign the security descriptor ( need to to do this with the spinlock
// released because the descriptor is not mapped. Assign and CheckAccess
// are synchronized using a Resource.
AccessState = pIrpSp->Parameters.Create.SecurityContext->AccessState;
status = SeAssignSecurity (NULL, // Parent Descriptor
AccessState->SecurityDescriptor,
&pAddress->SecurityDescriptor,
FALSE, // is a directory
&AccessState->SubjectSecurityContext,
&AddressGenericMapping,
NonPagedPool);
if (!NT_SUCCESS(status))
{
IoRemoveShareAccess (pIrpSp->FileObject, &pAddress->ShareAccess);
}
return status;
}
//----------------------------------------------------------------------------
NTSTATUS
NTCheckSharedAccess(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp,
IN tADDRESSELE *pAddress)
/*++
Routine Description:
This Routine handles setting the shared access on the file object.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PACCESS_STATE AccessState;
ACCESS_MASK GrantedAccess;
BOOLEAN AccessAllowed;
ULONG DesiredAccess;
PIO_STACK_LOCATION pIrpSp;
BOOLEAN duplicate=FALSE;
NTSTATUS status;
ULONG DesiredShareAccess;
static GENERIC_MAPPING AddressGenericMapping =
{ READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL };
CTEPagedCode();
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
if ((pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) ||
(pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE))
DesiredAccess = (ULONG)FILE_SHARE_READ;
else
DesiredAccess = (ULONG)0;
//
// The address already exists. Check the ACL and see if we
// can access it. If so, simply use this address as our address.
//
AccessState = pIrpSp->Parameters.Create.SecurityContext->AccessState;
status = STATUS_SUCCESS;
// *TODO* check that this routine is doing the right thing...
//
AccessAllowed = SeAccessCheck(
pAddress->SecurityDescriptor,
&AccessState->SubjectSecurityContext,
FALSE, // tokens locked
pIrpSp->Parameters.Create.SecurityContext->DesiredAccess,
(ACCESS_MASK)0, // previously granted
NULL, // privileges
&AddressGenericMapping,
pIrp->RequestorMode,
&GrantedAccess,
&status);
// use the status from the IoCheckShareAccess as the return access
// event if SeAccessCheck fails....
//
// Hmmm .... Compare DesiredAccess to GrantedAccess?
//
//
// Now check that we can obtain the desired share
// access. We use read access to control all access.
//
DesiredShareAccess = (ULONG)
(((pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) ||
(pIrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ?
FILE_SHARE_READ : 0);
//ACQUIRE_SPIN_LOCK (&pDeviceContext->SpinLock, &oldirql);
status = IoCheckShareAccess(
FILE_READ_DATA,
DesiredAccess,
pIrpSp->FileObject,
&pAddress->ShareAccess,
TRUE);
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTCleanUpAddress(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles the first stage of releasing an address object.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
NTSTATUS status;
tCLIENTELE *pClientEle;
PIO_STACK_LOCATION pIrpSp;
CTEPagedCode();
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NTCleanUpAddress: Cleanup Address Hit ***\n"));
//
// Disconnect any active connections, and for each connection that is not
// in use, remove one from the free list to the transport below.
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pClientEle = (tCLIENTELE *) pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE (pClientEle, NBT_VERIFY_CLIENT))
{
ASSERTMSG ("Nbt.NTCleanUpAddress: ERROR - Invalid Address Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
CTEVerifyHandle(pClientEle,NBT_VERIFY_CLIENT,tCLIENTELE,&status);
status = NbtCleanUpAddress(pClientEle,pDeviceContext);
NbtTrace(NBT_TRACE_NAMESRV, ("Cleanup address %p returns %!status!", pClientEle, status));
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTCleanUpConnection(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles running down a connection in preparation for a close
that will come in next. NtClose hits this entry first, and then it hits
the NTCloseConnection next. If the connection was outbound, then the
address object must be closed as well as the connection. This routine
mainly deals with the pLowerconn connection to the transport whereas
NbtCloseConnection deals with closing pConnEle, the connection to the client.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION pIrpSp;
tCONNECTELE *pConnEle;
CTEPagedCode();
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pConnEle = (tCONNECTELE *) pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
ASSERTMSG ("Nbt.NTCleanUpConnection: ERROR - Invalid Connection Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
//CTEVerifyHandle(pConnEle,NBT_VERIFY_CONNECTION,tCONNECTELE,&status);
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NTCleanUpConnection: Cleanup Connection Hit state= %X\n",pConnEle->state));
pConnEle->ConnectionCleanedUp = TRUE;
status = NbtCleanUpConnection(pConnEle,pDeviceContext);
ASSERT (status != STATUS_PENDING);
NbtTrace(NBT_TRACE_NAMESRV, ("Cleanup connection %p returns %!status!", pConnEle, status));
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTAccept(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles passing an accept for an inbound connect indication to
the OS independent code.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
NTSTATUS status;
TDI_REQUEST TdiRequest;
PIO_STACK_LOCATION pIrpSp;
PTDI_REQUEST_KERNEL_ACCEPT pRequest;
tCONNECTELE *pConnEle;
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NTAccept: ** Got an Accept from the Client **\n"));
// pull the junk out of the Irp and call the non-OS specific routine.
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
// the Parameters value points to a Request structure...
pRequest = (PTDI_REQUEST_KERNEL_ACCEPT)&pIrpSp->Parameters;
// the pConnEle ptr was stored in the FsContext value when the connection
// was initially created.
pConnEle = TdiRequest.Handle.ConnectionContext = pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
ASSERTMSG ("Nbt.NTAccept: ERROR - Invalid Connection Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
status = NbtAccept(
&TdiRequest,
pRequest->RequestConnectionInformation,
pRequest->ReturnConnectionInformation,
pIrp);
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTDisAssociateAddress(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
NTSTATUS status;
TDI_REQUEST TdiRequest;
PIO_STACK_LOCATION pIrpSp;
PTDI_REQUEST_KERNEL_ACCEPT pRequest;
tCONNECTELE *pConnEle;
CTEPagedCode();
// pull the junk out of the Irp and call the non-OS specific routine.
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
// the Parameters value points to a Request structure...
pRequest = (PTDI_REQUEST_KERNEL_ACCEPT)&pIrpSp->Parameters;
// the pConnEle ptr was stored in the FsContext value when the connection
// was initially created.
pConnEle = TdiRequest.Handle.ConnectionContext = pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
ASSERTMSG ("Nbt.NTCloseAddress: ERROR - Invalid Address Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
status = NbtDisassociateAddress(&TdiRequest);
return(status);
}
LONG
NextTransportAddress(
IN PNBT_DELAYED_CONNECT_CONTEXT pDelConnCtx
)
/*++
Move the pointer to the next address.
--*/
{
pDelConnCtx->RemainingAddressLength -= (pDelConnCtx->TaAddressLength + FIELD_OFFSET(TRANSPORT_ADDRESS,Address));
pDelConnCtx->pTaAddress += pDelConnCtx->TaAddressLength + FIELD_OFFSET(TRANSPORT_ADDRESS,Address);
RtlCopyMemory(&pDelConnCtx->TaAddressLength,
(pDelConnCtx->pTaAddress+FIELD_OFFSET(TA_ADDRESS,AddressLength)), sizeof(USHORT));
pDelConnCtx->CurrIndex++;
/*
* make sure we don't overrun the buffer
*/
if(pDelConnCtx->RemainingAddressLength < (pDelConnCtx->TaAddressLength + FIELD_OFFSET(TRANSPORT_ADDRESS,Address))) {
KdPrint(("netbt!NextTransportAddress: insufficient TaAddress buffer size\n"));
pDelConnCtx->CurrIndex = pDelConnCtx->NumberOfAddresses;
}
return pDelConnCtx->CurrIndex;
}
NTSTATUS
InitDelayedNbtProcessConnect(
IN PNBT_DELAYED_CONNECT_CONTEXT pDelConnCtx
)
/*++
Reset the NBT_DELAYED_CONNECT_CONTEXT
Find the first readable unicode address and writable buffer. In compound address case, NetBT will first try
to establish the connection using the first readable unicode address. If this fails, it will attempt to use
OEM address, ie. only one readable unicode address is effective. If DNS name resolution is used, NetBT will
return the result in the first writable buffer and update the NameBufferType to NBT_WRITTEN.
--*/
{
PTDI_REQUEST_KERNEL pRequestKernel;
PIO_STACK_LOCATION pIrpSp;
PTRANSPORT_ADDRESS pRemoteAddress;
PUCHAR pTaAddress;
enum eNameBufferType NameBufferType, UnicodeAddressNameBufferType;
NTSTATUS status;
tCONNECTELE *pConnEle = NULL;
CTEPagedCode();
pIrpSp = IoGetCurrentIrpStackLocation(pDelConnCtx->pClientIrp);
pRequestKernel = (PTDI_REQUEST_KERNEL) &pIrpSp->Parameters;
pRemoteAddress = pRequestKernel->RequestConnectionInformation->RemoteAddress;
pDelConnCtx->NumberOfAddresses = pRemoteAddress->TAAddressCount;
pDelConnCtx->RemainingAddressLength = pRequestKernel->RequestConnectionInformation->RemoteAddressLength;
pDelConnCtx->pTaAddress = (PCHAR)&pRemoteAddress->Address[0];
RtlCopyMemory(&pDelConnCtx->TaAddressLength,
(pDelConnCtx->pTaAddress+FIELD_OFFSET(TA_ADDRESS,AddressLength)), sizeof(USHORT));
pDelConnCtx->CurrIndex = 0;
/*
* Find the first writable buffer and readable unicode address
*/
pDelConnCtx->pReturnBuffer = NULL;
pDelConnCtx->pUnicodeAddress = NULL;
for (pDelConnCtx->CurrIndex = 0; pDelConnCtx->CurrIndex < pDelConnCtx->NumberOfAddresses;
NextTransportAddress(pDelConnCtx)) {
USHORT TaAddressType;
RtlCopyMemory(&TaAddressType, (pDelConnCtx->pTaAddress+FIELD_OFFSET(TA_ADDRESS,AddressType)), sizeof(USHORT));
if (TaAddressType != TDI_ADDRESS_TYPE_NETBIOS_UNICODE_EX) {
continue;
}
RtlCopyMemory(&NameBufferType,
pDelConnCtx->pTaAddress +
FIELD_OFFSET(TA_ADDRESS,Address)+
FIELD_OFFSET(TDI_ADDRESS_NETBIOS_UNICODE_EX,NameBufferType),
sizeof(NameBufferType));
if (NameBufferType != NBT_READONLY && NameBufferType != NBT_WRITEONLY &&
NameBufferType != NBT_READWRITE && NameBufferType != NBT_WRITTEN) {
return STATUS_INVALID_ADDRESS;
}
if (NameBufferType == NBT_READONLY || NameBufferType == NBT_READWRITE) {
if (pDelConnCtx->pUnicodeAddress == NULL) {
pDelConnCtx->pUnicodeAddress = (TDI_ADDRESS_NETBIOS_UNICODE_EX*)
(pDelConnCtx->pTaAddress + FIELD_OFFSET(TA_ADDRESS,Address));
UnicodeAddressNameBufferType = NameBufferType;
}
}
if (NameBufferType == NBT_WRITEONLY) {
pDelConnCtx->pReturnBuffer = (TDI_ADDRESS_NETBIOS_UNICODE_EX*)
(pDelConnCtx->pTaAddress + FIELD_OFFSET(TA_ADDRESS,Address));
break;
}
if (NameBufferType == NBT_READWRITE) {
pDelConnCtx->pReturnBuffer = (TDI_ADDRESS_NETBIOS_UNICODE_EX*)
(pDelConnCtx->pTaAddress + FIELD_OFFSET(TA_ADDRESS,Address));
/*
* Only when no WRITEONLY buffer is presented, can we use a READWRITE buffer. So continue searching.
*/
}
}
pDelConnCtx->NumberOfAddresses = pRemoteAddress->TAAddressCount;
pDelConnCtx->RemainingAddressLength = pRequestKernel->RequestConnectionInformation->RemoteAddressLength;
pDelConnCtx->pTaAddress = (PCHAR)&pRemoteAddress->Address[0];
RtlCopyMemory(&pDelConnCtx->TaAddressLength,
(pDelConnCtx->pTaAddress+FIELD_OFFSET(TA_ADDRESS,AddressLength)), sizeof(USHORT));
pDelConnCtx->CurrIndex = 0;
/*
* Setup the first local transport address
*/
if (pDelConnCtx->pUnicodeAddress != NULL) {
pTaAddress = ((PUCHAR)pDelConnCtx->pUnicodeAddress - FIELD_OFFSET(TA_ADDRESS,Address));
} else {
pTaAddress = pDelConnCtx->pTaAddress;
}
status = NewInternalAddressFromTransportAddress(
(PTRANSPORT_ADDRESS) (pTaAddress-FIELD_OFFSET(TRANSPORT_ADDRESS,Address)),
pDelConnCtx->RemainingAddressLength, &pDelConnCtx->pTransportAddress);
if (status != STATUS_SUCCESS) {
ASSERT(pDelConnCtx->pTransportAddress == NULL);
return status;
}
if (pDelConnCtx->pUnicodeAddress == NULL || UnicodeAddressNameBufferType != NBT_READWRITE) {
pDelConnCtx->pTransportAddress->Address[0].Address[0].pNetbiosUnicodeEX = pDelConnCtx->pReturnBuffer;
}
ASSERT(pDelConnCtx->pTransportAddress);
pConnEle = pIrpSp->FileObject->FsContext;
if (NULL == pConnEle || !NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN)) {
return STATUS_INVALID_HANDLE;
}
pDelConnCtx->pConnEle = pConnEle;
NBT_REFERENCE_CONNECTION (pConnEle, REF_CONN_MULTIPLE_CONNECT); // so we don't delete the connection
return STATUS_SUCCESS;
}
NTSTATUS
NextDelayedNbtProcessConnect(
IN PNBT_DELAYED_CONNECT_CONTEXT pDelConnCtx
)
/*++
Move the pointer to the next address.
--*/
{
USHORT TaAddressType;
enum eNameBufferType NameBufferType;
PTA_NETBT_INTERNAL_ADDRESS pTransportAddress = NULL;
TDI_ADDRESS_NETBT_INTERNAL *pAddr;
PIO_STACK_LOCATION pIrpSp;
tCONNECTELE *pConnEle;
NTSTATUS status;
pIrpSp = IoGetCurrentIrpStackLocation(pDelConnCtx->pClientIrp);
pConnEle = pIrpSp->FileObject->FsContext;
ASSERT (pConnEle->pIrp == NULL);
ASSERT(pDelConnCtx->pTransportAddress);
status = STATUS_SUCCESS;
while(1) {
/*
* Free memory allocated in previous loop
*/
if (pTransportAddress) {
DeleteInternalAddress(pTransportAddress);
pTransportAddress = NULL;
}
if (pDelConnCtx->pUnicodeAddress == NULL) {
NextTransportAddress(pDelConnCtx);
} else {
pDelConnCtx->pUnicodeAddress = NULL;
}
if (pDelConnCtx->CurrIndex >= pDelConnCtx->NumberOfAddresses) {
break;
}
/*
* Skip UNICODE address.
* UNICODE address is always done first, ie. just after InitDelayedNbtProcessConnect gets called.
*/
RtlCopyMemory(&TaAddressType, (pDelConnCtx->pTaAddress+FIELD_OFFSET(TA_ADDRESS,AddressType)), sizeof(USHORT));
if (TaAddressType != TDI_ADDRESS_TYPE_NETBIOS && TaAddressType != TDI_ADDRESS_TYPE_NETBIOS_EX) {
continue;
}
/*
* Since we only do OEM address, we can safely call NewInternalAddressFromTransportAddress (this guy will
* call Rtl* to convert UNICODE to OEM in UNICODE address case so that we may hit bug check.)
*/
status = NewInternalAddressFromTransportAddress(
(PTRANSPORT_ADDRESS) (pDelConnCtx->pTaAddress-FIELD_OFFSET(TRANSPORT_ADDRESS,Address)),
pDelConnCtx->RemainingAddressLength, &pTransportAddress);
if (status != STATUS_SUCCESS) {
ASSERT(pTransportAddress == NULL);
continue;
}
ASSERT(pTransportAddress);
pAddr = pTransportAddress->Address[0].Address;
/*
* Always attach a writable buffer in OEM address case
*/
pAddr->pNetbiosUnicodeEX = pDelConnCtx->pReturnBuffer;
/*
* Skip any address which is same as previous one.
* Since the previous one fails, there is no point to use it again.
*/
if (IsDeviceNetbiosless(pDelConnCtx->pDeviceContext) ||
(pDelConnCtx->pLocalIrp->IoStatus.Status == STATUS_HOST_UNREACHABLE)) {
OEM_STRING RemoteName, PreviouseRemoteName;
CTEMemCopy (&RemoteName, &pAddr->OEMRemoteName, sizeof(OEM_STRING));
CTEMemCopy (&PreviouseRemoteName,
&pDelConnCtx->pTransportAddress->Address[0].Address[0].OEMRemoteName, sizeof(OEM_STRING));
if ((RemoteName.Length) && (RemoteName.Length == PreviouseRemoteName.Length) &&
(CTEMemEqu (RemoteName.Buffer, PreviouseRemoteName.Buffer, RemoteName.Length))) {
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("Nbt.DelayedNbtProcessConnect: Irp=<%x>, Names match!<%16.16s:%x>, Types=<%x:%x>\n",
pDelConnCtx->pClientIrp, pAddr->OEMRemoteName.Buffer, pAddr->OEMRemoteName.Buffer[15],
pAddr->AddressType, pDelConnCtx->pTransportAddress->Address[0].Address[0].AddressType));
continue;
}
}
if (pConnEle->RemoteNameDoesNotExistInDNS) {
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("netbt!DelayedNbtProcessConnect: Skipping address type %lx length %lx\n"
"\t\tfor nonexistent name, pIrp %lx, pLocalIrp %lx\n",
TaAddressType, pDelConnCtx->TaAddressLength,
pDelConnCtx->pClientIrp, pDelConnCtx->pLocalIrp));
// If the address type is such that we rely on DNS name resolution and
// if a prior attempt failed, there is no point in reissuing the request.
// We can fail them without having to go on the NET.
if (TaAddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) {
status = STATUS_BAD_NETWORK_PATH;
continue;
} else if (pDelConnCtx->TaAddressLength != TDI_ADDRESS_LENGTH_NETBIOS) {
ASSERT(TaAddressType == TDI_ADDRESS_TYPE_NETBIOS);
status = STATUS_INVALID_ADDRESS_COMPONENT;
continue;
}
}
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("netbt!DelayedNbtProcessConnect: Sending local irp=%lx, %lx of %lx\n"
"\t\t\t\tTA=%lx Length=%lx\n",
pDelConnCtx->pLocalIrp, pDelConnCtx->CurrIndex+1, pDelConnCtx->NumberOfAddresses,
pDelConnCtx->pTaAddress, pDelConnCtx->TaAddressLength));
DeleteInternalAddress(pDelConnCtx->pTransportAddress);
pDelConnCtx->pTransportAddress = pTransportAddress;
pTransportAddress = NULL;
break;
}
if (pTransportAddress) {
DeleteInternalAddress(pTransportAddress);
pTransportAddress = NULL;
}
return status;
}
VOID
DoneDelayedNbtProcessConnect(
IN PNBT_DELAYED_CONNECT_CONTEXT pDelConnCtx,
NTSTATUS status
)
/*++
1. Complete the client IRP
2. Cleanup everything
--*/
{
tCONNECTELE *pConnEle = NULL;
ASSERT(pDelConnCtx->pLocalIrp);
ASSERT(pDelConnCtx->pClientIrp);
NbtCancelCancelRoutine(pDelConnCtx->pClientIrp);
pConnEle = pDelConnCtx->pConnEle;
NBT_DEREFERENCE_CONNECTION (pConnEle, REF_CONN_MULTIPLE_CONNECT); // so we don't delete the connection
ASSERT(status != STATUS_PENDING);
if (pDelConnCtx->pLocalIrp) {
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("netbt!DoneDelayedNbtProcessConnect: Freeing Local Irp=<%x>\n", pDelConnCtx->pLocalIrp));
IoFreeIrp(pDelConnCtx->pLocalIrp);
}
if (pDelConnCtx->pTransportAddress) {
DeleteInternalAddress(pDelConnCtx->pTransportAddress);
}
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("netbt!DoneDelayed...: Connect Complete, LocalIrp=<%x>, ClientIrp=<%x>, Status=<%x>\n",
pDelConnCtx->pLocalIrp, pDelConnCtx->pClientIrp, status));
NbtTrace(NBT_TRACE_OUTBOUND, ("Complete connection request pIrp=%p pLocalIrp=%p with %!status!",
pDelConnCtx->pClientIrp, pDelConnCtx->pLocalIrp, status));
NTIoComplete (pDelConnCtx->pClientIrp, status, 0);
pDelConnCtx->pLocalIrp = NULL;
pDelConnCtx->pClientIrp = NULL;
pDelConnCtx->pTransportAddress = NULL;
CTEFreeMem(pDelConnCtx);
}
//----------------------------------------------------------------------------
NTSTATUS
NbtpConnectCompletionRoutine(
PDEVICE_OBJECT pDeviceObject,
PIRP pIrp,
PVOID pCompletionContext
)
/*++
Routine Description:
This Routine is the completion routine for local IRPS that are generated
to handle compound transport addresses
Arguments:
pDeviceObject - the device object
pIrp - a ptr to an IRP
pCompletionContext - the completion context
Return Value:
NTSTATUS - status of the request
--*/
{
PNBT_DELAYED_CONNECT_CONTEXT pDelConnCtx;
NTSTATUS Status, Status2;
PIRP pLocalIrp;
tDEVICECONTEXT *pDeviceContext;
pDelConnCtx = pCompletionContext;
pDeviceContext = pDelConnCtx->pDeviceContext;
pLocalIrp = pDelConnCtx->pLocalIrp;
ASSERT (pIrp == pLocalIrp);
Status = pLocalIrp->IoStatus.Status;
ASSERT(Status != STATUS_PENDING);
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: %!status! pParentIrp %p",
pIrp, Status, pDelConnCtx->pClientIrp));
pDelConnCtx->ProcessingDone = TRUE;
/*
* Let's move to next address
*/
Status2 = NextDelayedNbtProcessConnect(pDelConnCtx);
/*
* Are we done
*/
if (Status == STATUS_CANCELLED || Status == STATUS_SUCCESS || Status2 != STATUS_SUCCESS ||
pDelConnCtx->CurrIndex >= pDelConnCtx->NumberOfAddresses) {
if (Status2 != STATUS_SUCCESS) {
Status = Status2;
}
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("Nbt.NbtpC...:==>Connect Complete, LocalIrp=<%x>, ClientIrp=<%x>, Status=<%x> <==\n",
pIrp,pDelConnCtx->pClientIrp, Status));
if (Status == STATUS_HOST_UNREACHABLE) {
Status = STATUS_BAD_NETWORK_PATH;
}
DoneDelayedNbtProcessConnect(pDelConnCtx, Status);
return STATUS_MORE_PROCESSING_REQUIRED;
}
/*
* Start worker thread to process the Connect request on the next address
*/
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("NbtpConnectCompletionRoutine: queuing worker item, local irp=%lx, previous status=%lx\n",
pIrp, Status));
if (STATUS_SUCCESS != NTQueueToWorkerThread(NULL, DelayedNbtProcessConnect,
NULL,
pDelConnCtx,
NULL,
pDeviceContext,
FALSE))
{
KdPrint(("Nbt.NbtpConnectCompletionRoutine: Failed to Enqueue Worker thread\n"));
DoneDelayedNbtProcessConnect(pDelConnCtx, STATUS_INSUFFICIENT_RESOURCES);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
//----------------------------------------------------------------------------
NTSTATUS
DelayedNbtProcessConnect(
IN tDGRAM_SEND_TRACKING *pUnused1,
IN PVOID pClientContext,
IN PVOID pUnused2,
IN tDEVICECONTEXT *pUnused3
)
/*++
Routine Description:
This Routine is the worker thread for processing Connect Requests.
Arguments:
pContext
Return Value:
NONE
--*/
{
PNBT_DELAYED_CONNECT_CONTEXT pDelConnCtx = NULL;
PIRP pIrp = NULL, pLocalIrp = NULL;
PIO_STACK_LOCATION pIrpSp = NULL;
tCONNECTELE *pConnEle = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PTDI_REQUEST_KERNEL pRequestKernel = NULL;
CTEPagedCode();
pDelConnCtx = (PNBT_DELAYED_CONNECT_CONTEXT) pClientContext;
pIrp = pDelConnCtx->pClientIrp;
pLocalIrp = pDelConnCtx->pLocalIrp;
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("netbt!DelayedNbtProcessConnect: Enter with local irp=%lx, %lx of %lx\n"
"\t\t\t\tTA=%lx Length=%lx\n",
pLocalIrp, pDelConnCtx->CurrIndex+1, pDelConnCtx->NumberOfAddresses,
pDelConnCtx->pTaAddress, pDelConnCtx->TaAddressLength));
pConnEle = pDelConnCtx->pConnEle;
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN)) {
DbgPrint ("Nbt.DelayedNbtProcessConnect: ERROR - Invalid Connection Handle\n");
DoneDelayedNbtProcessConnect(pDelConnCtx,
(pDelConnCtx->ProcessingDone)? pLocalIrp->IoStatus.Status: STATUS_UNSUCCESSFUL);
return STATUS_UNSUCCESSFUL;
}
CHECK_PTR (pConnEle);
Status = STATUS_UNSUCCESSFUL;
/*
* Set the Cancel routine and ensure that the original IRP was not cancelled before continuing.
*/
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint (("Nbt.DelayedNbtProcessConnect: Setting Cancel=<NbtCancelConnect> for Irp:Device <%x:%x>\n",
pIrp, pDelConnCtx->pDeviceContext));
if (STATUS_CANCELLED == NTCheckSetCancelRoutine(pIrp, NbtCancelConnect, pDelConnCtx->pDeviceContext)) {
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("Nbt.DelayedNbtProcessConnect: Irp <%x> was cancelled\n", pIrp));
pConnEle->pIrp = NULL;
DoneDelayedNbtProcessConnect(pDelConnCtx, STATUS_CANCELLED);
return STATUS_CANCELLED;
}
/*
* InitDelayedNbtProcessConnect/NextDelayedNbtProcessConnect has set up pDelConnCtx->pTransportAddress properly
*/
ASSERT(pDelConnCtx->pTransportAddress);
pConnEle->AddressType = pDelConnCtx->pTransportAddress->Address[0].Address[0].AddressType;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pRequestKernel = (PTDI_REQUEST_KERNEL) &pIrpSp->Parameters;
pDelConnCtx->LocalConnectionInformation = *(pRequestKernel->RequestConnectionInformation);
pDelConnCtx->LocalConnectionInformation.RemoteAddress = pDelConnCtx->pTransportAddress;
pDelConnCtx->LocalConnectionInformation.RemoteAddressLength = pDelConnCtx->pTransportAddress->Address[0].AddressLength;
//
// Save the thread info for debugging purposes!
//
pLocalIrp->Tail.Overlay.Thread = PsGetCurrentThread();
TdiBuildConnect (pLocalIrp,
&pDelConnCtx->pDeviceContext->DeviceObject,
pIrpSp->FileObject,
NbtpConnectCompletionRoutine,
pDelConnCtx,
pRequestKernel->RequestSpecific,
&pDelConnCtx->LocalConnectionInformation,
pRequestKernel->ReturnConnectionInformation);
Status = IoCallDriver(&pDelConnCtx->pDeviceContext->DeviceObject,pLocalIrp);
if (Status != STATUS_PENDING) {
IF_DBG(NBT_DEBUG_NETBIOS_EX)
KdPrint(("Nbt.DelayedNbtProcessConnect: IoCallDriver returned %lx for irp %lx (%lx)\n",
Status,pIrp,pLocalIrp));
// ASSERT(0);
}
return STATUS_PENDING;
}
//----------------------------------------------------------------------------
NTSTATUS
NTConnect(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles calling the non OS specific code to open a session
connection to a destination.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PIO_STACK_LOCATION pIrpSp;
PTRANSPORT_ADDRESS pRemoteAddress;
PTDI_REQUEST_KERNEL pRequestKernel;
PIRP pLocalIrp;
PNBT_DELAYED_CONNECT_CONTEXT pDelConnCtx;
NTSTATUS Status;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pRequestKernel = (PTDI_REQUEST_KERNEL)&pIrpSp->Parameters;
NbtTrace(NBT_TRACE_OUTBOUND, ("TDI_CONNECT pIrp %p", pIrp));
try
{
pRemoteAddress = pRequestKernel->RequestConnectionInformation->RemoteAddress;
if (pRequestKernel->RequestConnectionInformation->RemoteAddressLength < sizeof(TRANSPORT_ADDRESS)) {
return STATUS_INVALID_ADDRESS_COMPONENT;
}
}
except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint (("Nbt.NTConnect: Exception <0x%x> trying to access Connection info\n", GetExceptionCode()));
return STATUS_INVALID_ADDRESS_COMPONENT;
}
if (pIrpSp->CompletionRoutine != NbtpConnectCompletionRoutine) {
pDelConnCtx = NbtAllocMem(sizeof(NBT_DELAYED_CONNECT_CONTEXT),NBT_TAG('e'));
if (!pDelConnCtx) {
return(STATUS_INSUFFICIENT_RESOURCES);
}
CTEZeroMemory(pDelConnCtx, sizeof(NBT_DELAYED_CONNECT_CONTEXT));
pLocalIrp = IoAllocateIrp(pDeviceContext->DeviceObject.StackSize,FALSE);
if (!pLocalIrp) {
CTEFreeMem(pDelConnCtx);
return(STATUS_INSUFFICIENT_RESOURCES);
}
pDelConnCtx->pDeviceContext = pDeviceContext;
pDelConnCtx->pClientIrp = pIrp;
pDelConnCtx->pLocalIrp = pLocalIrp;
pDelConnCtx->pTransportAddress = NULL;
pDelConnCtx->ProcessingDone = FALSE;
Status = InitDelayedNbtProcessConnect(pDelConnCtx);
if (!NT_SUCCESS(Status)) {
CTEFreeMem(pDelConnCtx);
IoFreeIrp(pLocalIrp);
return Status;
}
NbtTrace(NBT_TRACE_OUTBOUND, ("Connection request pIrp=%p pLocalIrp=%p", pIrp, pLocalIrp));
//return (DelayedNbtProcessConnect (NULL, pDelConnCtx, NULL, NULL));
DelayedNbtProcessConnect (NULL, pDelConnCtx, NULL, NULL);
// Ignore the return from DelayedNbtProcessConnect and always return STATUS_PENDING;
// our client completion routine will take care of completing the IRP
// Otherwise, we will complete the IRP twice.
return STATUS_PENDING;
}
else
{
TDI_REQUEST Request;
tCONNECTELE *pConnEle;
// call the non-NT specific function to setup the connection
pConnEle = Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
ASSERTMSG ("Nbt.NTConnect: ERROR - Invalid Connection Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
/*
* A user mode process may send us a faked request with a completion routine
* equal to NbtpConnectCompletionRoutine.
* Never let it pass through.
*/
if (pIrp->RequestorMode != KernelMode) {
ASSERTMSG ("Nbt.NTConnect: ERROR - Invalid request\n", 0);
return (STATUS_INVALID_PARAMETER);
}
return NbtConnect(&Request,
pRequestKernel->RequestSpecific, // Ulong
pRequestKernel->RequestConnectionInformation,
pIrp);
}
}
//----------------------------------------------------------------------------
NTSTATUS
NTDisconnect(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles calling the Non OS specific code to disconnect a
session.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
TDI_REQUEST Request;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status;
PTDI_REQUEST_KERNEL pRequestKernel;
tCONNECTELE *pConnEle;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pRequestKernel = (PTDI_REQUEST_KERNEL)&pIrpSp->Parameters;
pConnEle = Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
ASSERTMSG ("Nbt.NTDisconnect: ERROR - Invalid Connection Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
// call the non-NT specific function to setup the connection
NbtTrace(NBT_TRACE_DISCONNECT, ("Client Disconnects %!NBTNAME!<%02x>",
pConnEle->RemoteName, (unsigned char)pConnEle->RemoteName[15]));
status = NbtDisconnect(
&Request,
pRequestKernel->RequestSpecific, // Large Integer
(ULONG) pRequestKernel->RequestFlags,
pRequestKernel->RequestConnectionInformation,
pRequestKernel->ReturnConnectionInformation,
pIrp
);
NbtTrace(NBT_TRACE_DISCONNECT, ("Client Disconnects status: %!status!", status));
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTListen(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
NTSTATUS status;
TDI_REQUEST Request;
PTDI_REQUEST_KERNEL pRequestKernel;
PIO_STACK_LOCATION pIrpSp;
tCONNECTELE *pConnEle;
CTEPagedCode();
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NTListen: Got a LISTEN !!! *****************\n"));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pRequestKernel = (PTDI_REQUEST_KERNEL)&pIrpSp->Parameters;
pConnEle = Request.Handle.ConnectionContext = pIrpSp->FileObject->FsContext;
if (NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
try
{
PCHAR pName;
ULONG lNameType;
ULONG NameLen;
// Initialize Request data (may be needed by Vxd)
Request.RequestNotifyObject = NULL;
Request.RequestContext = NULL;
// call the non-NT specific function to setup the connection
status = NbtListen (&Request,
(ULONG) pRequestKernel->RequestFlags, // Ulong
pRequestKernel->RequestConnectionInformation,
pRequestKernel->ReturnConnectionInformation,
pIrp);
}
except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint (("Nbt.NTListen: Exception <0x%x> trying to access buffer\n", GetExceptionCode()));
status = STATUS_INVALID_ADDRESS;
}
}
else
{
ASSERTMSG ("Nbt.NTListen: ERROR - Invalid Connection Handle\n", 0);
status = STATUS_INVALID_HANDLE; // Bug# 202340: Have to complete Irp here!
}
if (status != STATUS_PENDING)
{
NTIoComplete(pIrp,status,0);
}
return(status);
}
//----------------------------------------------------------------------------
NBT_WORK_ITEM_CONTEXT *
FindLmhSvcRequest(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp,
IN tLMHSVC_REQUESTS *pLmhRequest
)
/*++
Routine Description:
This routine handles the cancelling a Query to LmHost, so that the client's
irp can be returned to the client. This cancellation is instigated
by the client (i.e. RDR).
Arguments:
Return Value:
The final status from the operation.
--*/
{
tDGRAM_SEND_TRACKING *pTracker;
NBT_WORK_ITEM_CONTEXT *Context;
BOOLEAN FoundIt = FALSE;
PLIST_ENTRY pHead;
PLIST_ENTRY pEntry;
if (pLmhRequest->ResolvingNow && pLmhRequest->Context)
{
// this is the session setup tracker
//
Context = (NBT_WORK_ITEM_CONTEXT *) pLmhRequest->Context;
pTracker = (tDGRAM_SEND_TRACKING *) Context->pClientContext;
if (pTracker->pClientIrp == pIrp)
{
pLmhRequest->Context = NULL;
FoundIt = TRUE;
}
}
else
{
//
// go through the list of Queued requests to find the correct one
// and cancel it
//
pHead = pEntry = &pLmhRequest->ToResolve;
while ((pEntry = pEntry->Flink) != pHead)
{
Context = CONTAINING_RECORD (pEntry,NBT_WORK_ITEM_CONTEXT,Linkage);
// this is the session setup tracker
//
pTracker = (tDGRAM_SEND_TRACKING *)Context->pClientContext;
if (pTracker->pClientIrp == pIrp)
{
RemoveEntryList(pEntry);
FoundIt = TRUE;
break;
}
}
}
return (FoundIt ? Context : NULL);
}
//----------------------------------------------------------------------------
NTSTATUS
QueryProviderCompletion(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This routine handles the completion event when the Query Provider
Information completes. This routine must decrement the MaxDgramSize
and max send size by the respective NBT header sizes.
Arguments:
DeviceObject - unused.
Irp - Supplies Irp that the transport has finished processing.
Context - not used
Return Value:
The final status from the operation (success or an exception).
--*/
{
PTDI_PROVIDER_INFO pProvider;
ULONG HdrSize;
ULONG SubnetAddr;
ULONG ThisSubnetAddr;
PLIST_ENTRY pHead;
PLIST_ENTRY pEntry;
tDEVICECONTEXT *pDeviceContext;
tDEVICECONTEXT *pDevContext;
CTELockHandle OldIrq;
if (NT_SUCCESS(Irp->IoStatus.Status))
{
pDeviceContext = (tDEVICECONTEXT *)DeviceContext;
pProvider = (PTDI_PROVIDER_INFO)MmGetMdlVirtualAddress(Irp->MdlAddress);
//
// Set the correct service flags to indicate what Netbt supports.
//
pProvider->ServiceFlags = TDI_SERVICE_MESSAGE_MODE |
TDI_SERVICE_CONNECTION_MODE |
TDI_SERVICE_CONNECTIONLESS_MODE |
TDI_SERVICE_ERROR_FREE_DELIVERY |
TDI_SERVICE_BROADCAST_SUPPORTED |
TDI_SERVICE_MULTICAST_SUPPORTED |
TDI_SERVICE_DELAYED_ACCEPTANCE |
TDI_SERVICE_ROUTE_DIRECTED |
TDI_SERVICE_FORCE_ACCESS_CHECK;
pProvider->MinimumLookaheadData = 128;
if (pProvider->MaxSendSize > sizeof(tSESSIONHDR))
{
//
// Nbt has just a two byte + 1 bit session message length, so it
// can't have a send size larger than 1ffff
//
if (pProvider->MaxSendSize > (0x1FFFF + sizeof(tSESSIONHDR)))
{
pProvider->MaxSendSize = 0x1FFFF;
}
else
{
pProvider->MaxSendSize -= sizeof(tSESSIONHDR);
}
}
else
{
pProvider->MaxSendSize = 0;
}
// subtract the datagram hdr size and the scope size (times 2)
HdrSize = DGRAM_HDR_SIZE + (NbtConfig.ScopeLength << 1);
if ((!IsDeviceNetbiosless (pDeviceContext)) &&
(pProvider->MaxDatagramSize > HdrSize))
{
pProvider->MaxDatagramSize -= HdrSize;
if (pProvider->MaxDatagramSize > MAX_NBT_DGRAM_SIZE)
{
pProvider->MaxDatagramSize = MAX_NBT_DGRAM_SIZE;
}
}
else
{
pProvider->MaxDatagramSize = 0;
}
//
// We need to hold the JointLock before we traverse
// the list of Devices
//
CTESpinLock(&NbtConfig.JointLock,OldIrq);
//
// Check if any of the adapters with the same subnet address have
// the PointtoPoint bit set - and if so set it in the response.
//
SubnetAddr = pDeviceContext->IpAddress & pDeviceContext->SubnetMask;
pEntry = pHead = &NbtConfig.DeviceContexts;
while ((pEntry = pEntry->Flink) != pHead)
{
pDevContext = CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage);
ThisSubnetAddr = pDevContext->IpAddress & pDevContext->SubnetMask;
if ((SubnetAddr == ThisSubnetAddr) &&
(pDevContext->IpInterfaceFlags & IP_INTFC_FLAG_P2P))
{
pProvider->ServiceFlags |= TDI_SERVICE_POINT_TO_POINT;
break;
}
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
//
// Must return a non-error status otherwise the IO system will not copy
// back into the users buffer.
//
return(STATUS_SUCCESS);
}
//----------------------------------------------------------------------------
NTSTATUS
NTQueryInformation(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PIO_STACK_LOCATION pIrpSp;
PTDI_REQUEST_KERNEL_QUERY_INFORMATION Query;
NTSTATUS status = STATUS_UNSUCCESSFUL;
NTSTATUS Locstatus;
PVOID pBuffer = NULL;
LONG Size ;
PTA_NETBIOS_ADDRESS BroadcastAddress;
ULONG AddressLength;
ULONG BytesCopied = 0;
PDEVICE_OBJECT pDeviceObject;
//
// Should not be pageable since AFD can call us at raised Irql in case of AcceptEx.
//
// CTEPagedCode();
if (pDeviceContext == pWinsDeviceContext)
{
NTIoComplete(pIrp, STATUS_INVALID_DEVICE_REQUEST, 0);
return (STATUS_INVALID_DEVICE_REQUEST);
}
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
Query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&pIrpSp->Parameters;
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NTQueryInformation: Query type = %X\n",Query->QueryType));
switch (Query->QueryType)
{
case TDI_QUERY_BROADCAST_ADDRESS:
{
// the broadcast address is the netbios name "*0000000..."
if ((!pIrp->MdlAddress) ||
(!(BroadcastAddress = (PTA_NETBIOS_ADDRESS)NbtAllocMem(sizeof(TA_NETBIOS_ADDRESS),NBT_TAG('b')))))
{
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
AddressLength = sizeof(TA_NETBIOS_ADDRESS);
BroadcastAddress->TAAddressCount = 1;
BroadcastAddress->Address[0].AddressLength = NETBIOS_NAME_SIZE +
sizeof(USHORT);
BroadcastAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
BroadcastAddress->Address[0].Address[0].NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP;
// the broadcast address to NetBios is "* 000000...", an * followed
// by 15 zeroes.
CTEZeroMemory(BroadcastAddress->Address[0].Address[0].NetbiosName,
NETBIOS_NAME_SIZE);
BroadcastAddress->Address[0].Address[0].NetbiosName[0] = '*';
status = TdiCopyBufferToMdl (
(PVOID)BroadcastAddress,
0,
AddressLength,
pIrp->MdlAddress,
0,
(PULONG)&pIrp->IoStatus.Information);
BytesCopied = (ULONG) pIrp->IoStatus.Information;
CTEMemFree((PVOID)BroadcastAddress);
break;
}
case TDI_QUERY_PROVIDER_INFO:
{
//
// Simply pass the Irp on by to the Transport, and let it
// fill in the provider info
//
if (!pDeviceContext->IpAddress)
{
status = STATUS_INVALID_DEVICE_STATE;
break;
}
if (StreamsStack)
{
TdiBuildQueryInformation(pIrp,
pDeviceContext->pFileObjects->pDgramDeviceObject,
pDeviceContext->pFileObjects->pDgramFileObject,
QueryProviderCompletion,
NULL,
TDI_QUERY_PROVIDER_INFO,
pIrp->MdlAddress);
}
else
{
TdiBuildQueryInformation(pIrp,
pDeviceContext->pControlDeviceObject,
pDeviceContext->pControlFileObject,
QueryProviderCompletion,
NULL,
TDI_QUERY_PROVIDER_INFO,
pIrp->MdlAddress);
}
CHECK_COMPLETION(pIrp);
status = IoCallDriver(pDeviceContext->pControlDeviceObject,pIrp);
//
// we must return the next drivers ret code back to the IO subsystem
//
return(status);
}
case TDI_QUERY_ADAPTER_STATUS:
{
if (!pIrp->MdlAddress)
{
break;
}
Size = MmGetMdlByteCount (pIrp->MdlAddress);
//
// check if it is a remote or local adapter status
//
if (Query->RequestConnectionInformation &&
Query->RequestConnectionInformation->RemoteAddress)
{
PCHAR pName;
ULONG lNameType;
ULONG NameLen;
tDGRAM_SEND_TRACKING *pTracker;
TDI_ADDRESS_NETBT_INTERNAL TdiAddr;
//
//
// in case the call results in a name query on the wire...
//
IoMarkIrpPending(pIrp);
status = STATUS_SUCCESS;
if (pIrp->RequestorMode != KernelMode) {
try
{
ProbeForRead(Query->RequestConnectionInformation->RemoteAddress,
Query->RequestConnectionInformation->RemoteAddressLength,
sizeof(BYTE));
} except(EXCEPTION_EXECUTE_HANDLER) {
status = STATUS_INVALID_PARAMETER;
}
}
if (NT_SUCCESS(status) && NT_SUCCESS (status = GetNetBiosNameFromTransportAddress(
(PTRANSPORT_ADDRESS) Query->RequestConnectionInformation->RemoteAddress,
Query->RequestConnectionInformation->RemoteAddressLength, &TdiAddr)))
{
pName = TdiAddr.OEMRemoteName.Buffer;
NameLen = TdiAddr.OEMRemoteName.Length;
lNameType = TdiAddr.NameType;
if ((lNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) &&
(NameLen == NETBIOS_NAME_SIZE) &&
(NT_SUCCESS (status = GetTracker(&pTracker, NBT_TRACKER_ADAPTER_STATUS))))
{
pTracker->ClientContext = pIrp;
status = NbtSendNodeStatus (pDeviceContext,
pName,
NULL,
pTracker,
CopyNodeStatusResponseCompletion);
// only complete the irp (below) for failure status's
if (status == STATUS_PENDING)
{
return(status);
}
//
// We cannot have a Success status returned here!
//
if (status == STATUS_SUCCESS)
{
ASSERT (0);
status = STATUS_UNSUCCESSFUL;
}
FreeTracker (pTracker, RELINK_TRACKER);
}
else if (NT_SUCCESS(status))
{
status = STATUS_INVALID_PARAMETER; // The NameType or NameLen must be wrong!
}
}
// the request has been satisfied, so unmark the pending
// since we will return the irp below
//
pIrpSp->Control &= ~SL_PENDING_RETURNED;
}
else
{
// return an array of netbios names that are registered
status = NbtQueryAdapterStatus(pDeviceContext,
&pBuffer,
&Size,
NBT_LOCAL);
}
break;
}
case TDI_QUERY_CONNECTION_INFO:
{
tCONNECTELE *pConnectEle;
tLOWERCONNECTION *pLowerConn;
KIRQL OldIrq1, OldIrq2;
// pass to transport to get the current throughput, delay and
// reliability numbers
//
pConnectEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pConnectEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
ASSERTMSG ("Nbt.NTQueryInformation: ERROR - Invalid Connection Handle\n", 0);
status = STATUS_INVALID_HANDLE;
break;
}
CTESpinLock(pConnectEle, OldIrq1);
pLowerConn = (tLOWERCONNECTION *)pConnectEle->pLowerConnId;
if (!NBT_VERIFY_HANDLE (pLowerConn, NBT_VERIFY_LOWERCONN))
{
status = STATUS_CONNECTION_INVALID;
CTESpinFree(pConnectEle, OldIrq1);
break;
}
CTESpinLock(pLowerConn, OldIrq2);
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_QUERY_INFO); // Bug # 212632
CTESpinFree(pLowerConn, OldIrq2);
CTESpinFree(pConnectEle, OldIrq1);
//
// Simply pass the Irp on by to the Transport, and let it
// fill in the info
//
pDeviceObject = IoGetRelatedDeviceObject( pLowerConn->pFileObject );
TdiBuildQueryInformation(pIrp,
pDeviceObject,
pLowerConn->pFileObject,
NULL, NULL,
TDI_QUERY_CONNECTION_INFO,
pIrp->MdlAddress);
status = IoCallDriver(pDeviceObject,pIrp);
NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_QUERY_INFO, FALSE);
//
// we must return the next drivers ret code back to the IO subsystem
//
return(status);
}
case TDI_QUERY_FIND_NAME:
{
//
//
// in case the call results in a name query on the wire...
//
if (pIrp->MdlAddress)
{
//
// Verify the request address space
//
try
{
status = STATUS_INVALID_ADDRESS_COMPONENT;
if (pIrp->RequestorMode == KernelMode)
{
//
// Since the TdiBuildQueryInformation macro NULLs out the
// RequestConnectionInformation field, we need to dereference
// it under Try/Except to ensure that the caller has filled
// the fields in properly
//
PTRANSPORT_ADDRESS pRemoteAddress=Query->RequestConnectionInformation->RemoteAddress;
if ((Query->RequestConnectionInformation->RemoteAddressLength
< sizeof(TRANSPORT_ADDRESS)) ||
(pRemoteAddress->TAAddressCount < 1) ||
(pRemoteAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_NETBIOS))
{
break;
}
}
else // User-mode client
{
ProbeForRead(Query->RequestConnectionInformation->RemoteAddress,
Query->RequestConnectionInformation->RemoteAddressLength,
sizeof(BYTE));
}
}
except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint (("Nbt.TDI_QUERY_FIND_NAME: Exception <0x%x> during Probe\n",
GetExceptionCode()));
break;
}
IoMarkIrpPending(pIrp);
status = NbtQueryFindName(Query->RequestConnectionInformation, pDeviceContext, pIrp, FALSE);
if (status == STATUS_PENDING)
{
return(status);
}
// the request has been satisfied, so unmark the pending
// since we will return the irp below
//
pIrpSp->Control &= ~SL_PENDING_RETURNED;
}
break;
}
case TDI_QUERY_ADDRESS_INFO:
{
if (pIrp->MdlAddress)
{
status = NbtQueryGetAddressInfo (pIrpSp, &pBuffer, &Size);
}
break;
}
case TDI_QUERY_SESSION_STATUS:
default:
{
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt Query Info NOT SUPPORTED = %X\n",Query->QueryType));
status = STATUS_NOT_SUPPORTED;
break;
}
} // switch
if (!NT_ERROR(status) && // allow buffer overflow to pass by
((Query->QueryType == TDI_QUERY_ADAPTER_STATUS) ||
(Query->QueryType == TDI_QUERY_ADDRESS_INFO)))
{
status = TdiCopyBufferToMdl (pBuffer, 0, Size, pIrp->MdlAddress, 0, &BytesCopied);
CTEMemFree((PVOID)pBuffer);
}
//
// either Success or an Error
// so complete the irp
//
NTIoComplete(pIrp,status,BytesCopied);
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NbtQueryGetAddressInfo(
IN PIO_STACK_LOCATION pIrpSp,
OUT PVOID *ppBuffer,
OUT ULONG *pSize
)
{
NTSTATUS status;
BOOLEAN IsGroup;
PLIST_ENTRY p;
tADDRESSELE *pAddressEle;
tNAMEADDR *pNameAddr;
tADDRESS_INFO *pAddressInfo;
tCLIENTELE *pClientEle;
tCONNECTELE *pConnectEle;
CTELockHandle OldIrq;
CTELockHandle OldIrq1;
PNBT_ADDRESS_PAIR_INFO pAddressPairInfo;
//
// We are not sure whether this is a ConnectionContext or a ClientContext!
//
pConnectEle = (tCONNECTELE *) pClientEle = pIrpSp->FileObject->FsContext;
if (NBT_VERIFY_HANDLE2 (pConnectEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
//
// We crashed here since the pLowerConn was NULL below.
// Check the state of the connection, since it is possible that the connection
// was aborted and the disconnect indicated, but this query came in before the client
// got the disconnect indication.
// If the state is idle (in case of TDI_DISCONNECT_ABORT) or DISCONNECTED
// (TDI_DISCONNECT_RELEASE), error out.
// Also check for NBT_ASSOCIATED.
//
// NOTE: If NbtOpenConnection is unable to allocate the lower conn block (say, if the session fileobj
// has not been created yet), the state will be still be IDLE, so we are covered here.
//
CTESpinLock(pConnectEle,OldIrq);
if (pConnectEle->Verify != NBT_VERIFY_CONNECTION)
{
CTESpinFree(pConnectEle,OldIrq);
return (STATUS_INVALID_HANDLE);
}
else if ((pConnectEle->state <= NBT_ASSOCIATED) || // includes NBT_IDLE
(pConnectEle->state == NBT_DISCONNECTED))
{
CTESpinFree(pConnectEle,OldIrq);
return (STATUS_CONNECTION_DISCONNECTED);
}
//
// A TdiQueryInformation() call requesting TDI_QUERY_ADDRESS_INFO
// on a connection. Fill in a TDI_ADDRESS_INFO containing both the
// NetBIOS address and the IP address of the remote. Some of the
// fields are fudged.
//
if (pAddressPairInfo = NbtAllocMem(sizeof (NBT_ADDRESS_PAIR_INFO), NBT_TAG('c')))
{
memset ( pAddressPairInfo, 0, sizeof(NBT_ADDRESS_PAIR_INFO) );
pAddressPairInfo->ActivityCount = 1;
pAddressPairInfo->AddressPair.TAAddressCount = 2;
pAddressPairInfo->AddressPair.AddressIP.AddressType = TDI_ADDRESS_TYPE_IP;
pAddressPairInfo->AddressPair.AddressIP.AddressLength = TDI_ADDRESS_LENGTH_IP;
pAddressPairInfo->AddressPair.AddressNetBIOS.AddressType = TDI_ADDRESS_TYPE_NETBIOS;
pAddressPairInfo->AddressPair.AddressNetBIOS.AddressLength = TDI_ADDRESS_LENGTH_NETBIOS;
pAddressPairInfo->AddressPair.AddressNetBIOS.Address.NetbiosNameType =
TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
memcpy( &pAddressPairInfo->AddressPair.AddressNetBIOS.Address.NetbiosName[0],
&pConnectEle->RemoteName[0],
NETBIOS_NAME_SIZE);
//
// Check for NULL (should not be NULL here since we check for states above).
//
if (pConnectEle->pLowerConnId)
{
pAddressPairInfo->AddressPair.AddressIP.Address.in_addr =
pConnectEle->pLowerConnId->SrcIpAddr;
*ppBuffer = (PVOID)pAddressPairInfo;
*pSize = sizeof(NBT_ADDRESS_PAIR_INFO);
status = STATUS_SUCCESS;
}
else
{
DbgPrint("pLowerConn NULL in pConnEle%lx, state: %lx\n", pConnectEle, pConnectEle->state);
CTEMemFree ((PVOID)pAddressPairInfo);
status = STATUS_CONNECTION_DISCONNECTED;
}
}
else
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
CTESpinFree(pConnectEle,OldIrq);
}
else if (NBT_VERIFY_HANDLE2 (pClientEle, NBT_VERIFY_CLIENT, NBT_VERIFY_CLIENT_DOWN))
{
pAddressInfo = NbtAllocMem(sizeof(tADDRESS_INFO),NBT_TAG('c'));
if (pAddressInfo)
{
//
// count the clients attached to this address
// We need to spinlock the address element, which
// is why this routine is not pageable
//
pAddressInfo->ActivityCount = 0;
pAddressEle = pClientEle->pAddress;
//
// The Client can be removed from the AddressEle only under the JointLock,
// so we need to hold that while counting the Clients on htis address
//
CTESpinLock(&NbtConfig.JointLock,OldIrq);
CTESpinLock(pAddressEle,OldIrq1);
for (p = pAddressEle->ClientHead.Flink; p != &pAddressEle->ClientHead; p = p->Flink)
{
++pAddressInfo->ActivityCount;
}
CTESpinFree(pAddressEle,OldIrq1);
CTESpinFree(&NbtConfig.JointLock,OldIrq);
pNameAddr = pAddressEle->pNameAddr;
IsGroup = (pNameAddr->NameTypeState & NAMETYPE_UNIQUE) ? FALSE : TRUE;
TdiBuildNetbiosAddress((PUCHAR)pNameAddr->Name, IsGroup, &pAddressInfo->NetbiosAddress);
*ppBuffer = (PVOID)pAddressInfo;
*pSize = sizeof(tADDRESS_INFO);
status = STATUS_SUCCESS;
}
else
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else // neither a client nor a connection context!
{
ASSERTMSG ("Nbt.NbtQueryGetAddressInfo: ERROR - Invalid Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
return status;
}
//----------------------------------------------------------------------------
NTSTATUS
NbtGetInterfaceInfo(
IN PIRP pIrp
)
/*++
Routine Description:
gets the interface to index mapping info
for all the interfaces
Arguments:
Irp - Pointer to I/O request packet to cancel.
IrpSp - pointer to current stack
Return Value:
NTSTATUS Indicates status success or failure
Notes:
Function does not pend.
--*/
{
NTSTATUS LocStatus, Status = STATUS_SUCCESS;
ULONG InfoBufferLen, MaxSize, i=0;
NETBT_INTERFACE_INFO *pInterfaceInfo;
KIRQL OldIrq;
PLIST_ENTRY pEntry,pHead;
tDEVICECONTEXT *pDeviceContext;
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation (pIrp);
CTESpinLock(&NbtConfig.JointLock,OldIrq);
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NbtGetInterfaceInfo: AdapterCount=<%x>\n", NbtConfig.AdapterCount));
InfoBufferLen = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
MaxSize = (NbtConfig.AdapterCount+1)*sizeof(NETBT_ADAPTER_INDEX_MAP)+sizeof(ULONG);
if (MaxSize <= InfoBufferLen)
{
if (pInterfaceInfo = NbtAllocMem (MaxSize,NBT_TAG('P')))
{
pEntry = pHead = &NbtConfig.DeviceContexts;
while ((pEntry = pEntry->Flink) != pHead)
{
pDeviceContext = CONTAINING_RECORD(pEntry, tDEVICECONTEXT, Linkage);
CTEMemCopy (&pInterfaceInfo->Adapter[i].Name,
pDeviceContext->ExportName.Buffer,
pDeviceContext->ExportName.Length);
pInterfaceInfo->Adapter[i].Name[pDeviceContext->ExportName.Length/2] = 0;
pInterfaceInfo->Adapter[i].Index = i;
i++;
}
pInterfaceInfo->NumAdapters = i;
Status = TdiCopyBufferToMdl (pInterfaceInfo,
0,
i*sizeof(NETBT_ADAPTER_INDEX_MAP)+sizeof(ULONG),
pIrp->MdlAddress,
0,
(PULONG)&pIrp->IoStatus.Information);
CTEMemFree (pInterfaceInfo);
}
else
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
//KdPrint(("GetInterfaceInfo Buffer Overflow %x\n", pIrp));
//pIrp->IoStatus.Information = sizeof(ULONG);
Status = STATUS_BUFFER_OVERFLOW;
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
//KdPrint(("GetInterfaceInfo exit status %x\n", Status));
return Status;
}
//----------------------------------------------------------------------------
NTSTATUS
NbtFlushEntryFromRemoteHashTable(
tNAME *pRemoteName
)
{
NTSTATUS status;
KIRQL OldIrq;
tNAMEADDR *pNameAddr = NULL;
CTESpinLock(&NbtConfig.JointLock,OldIrq);
status = FindInHashTable (NbtConfig.pRemoteHashTbl, pRemoteName->Name, NbtConfig.pScope, &pNameAddr);
if (NT_SUCCESS (status))
{
if (pNameAddr->RefCount <= 1)
{
NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE);
}
else
{
status = STATUS_UNSUCCESSFUL;
}
}
else
{
status = STATUS_RESOURCE_NAME_NOT_FOUND;
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
return (status);
}
//----------------------------------------------------------------------------
NTSTATUS
SetTcpInfo(
IN HANDLE FileHandle,
IN PVOID pInfoBuffer,
IN ULONG InfoBufferLength
)
{
IO_STATUS_BLOCK IoStatus;
HANDLE event;
BOOLEAN fAttached = FALSE;
NTSTATUS status;
CTEAttachFsp(&fAttached, REF_FSP_SET_TCP_INFO);
status = ZwCreateEvent (&event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);
if (NT_SUCCESS(status))
{
//
// Make the actual TDI call
//
IoStatus.Status = STATUS_SUCCESS;
status = ZwDeviceIoControlFile (FileHandle,
event,
NULL,
NULL,
&IoStatus,
IOCTL_TCP_SET_INFORMATION_EX,
pInfoBuffer,
InfoBufferLength,
NULL,
0);
//
// If the call pended and we were supposed to wait for completion,
// then wait.
//
if (status == STATUS_PENDING)
{
status = NtWaitForSingleObject (event, FALSE, NULL);
ASSERT(status == STATUS_SUCCESS);
} else if (!NT_SUCCESS(status) && IoStatus.Status == STATUS_SUCCESS) {
//
// Set the IoStatus.Status if it hasn't been set so that we could
// return the correct status below.
//
IoStatus.Status = status;
}
status = ZwClose (event);
ASSERT (NT_SUCCESS(status));
status = IoStatus.Status;
}
CTEDetachFsp(fAttached, REF_FSP_SET_TCP_INFO);
return (status);
}
//----------------------------------------------------------------------------
NTSTATUS
NbtClientSetTcpInfo(
IN tCONNECTELE *pConnEle,
IN PVOID pInfoBuffer,
IN ULONG InfoBufferLength
)
/*++
Routine Description:
Sets the Tcp connection information as requested
by the client
Arguments:
pConnEle - NetBT's Connection object
pInfoBuffer - pointer to TCP_REQUEST_SET_INFORMATION_EX structure
pInfoBufferLength - length of pInfoBuffer
Return Value:
NTSTATUS Indicates status success or failure
Notes:
Function does not pend.
--*/
{
NTSTATUS status;
tLOWERCONNECTION *pLowerConn;
KIRQL OldIrq1, OldIrq2;
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
return STATUS_INVALID_HANDLE;
}
CTESpinLock(pConnEle, OldIrq1);
if ((!NBT_VERIFY_HANDLE ((pLowerConn = pConnEle->pLowerConnId), NBT_VERIFY_LOWERCONN)) ||
(pLowerConn->RefCount > 500)) // if queued for WipeOutLowerConn
{
CTESpinFree(pConnEle, OldIrq1);
return STATUS_BAD_NETWORK_PATH;
}
CTESpinLock(pLowerConn, OldIrq2);
//
// We have verified that the lower connection is up -- reference it
// so that the FileObject does not get Dereferenced by some disconnect
// from the transport
//
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_SET_TCP_INFO);
CTESpinFree(pLowerConn, OldIrq2);
CTESpinFree(pConnEle, OldIrq1);
status = SetTcpInfo (pLowerConn->FileHandle, pInfoBuffer, InfoBufferLength);
NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_SET_TCP_INFO, FALSE);
return (status);
}
//----------------------------------------------------------------------------
NTSTATUS
NbtSetTcpInfo(
IN HANDLE FileHandle,
IN ULONG ToiId,
IN ULONG ToiType,
IN ULONG InfoBufferValue
)
{
NTSTATUS Status;
ULONG BufferLength;
TCP_REQUEST_SET_INFORMATION_EX *pTcpInfo;
TCPSocketOption *pSockOption;
BufferLength = sizeof(TCP_REQUEST_SET_INFORMATION_EX) + sizeof(TCPSocketOption);
if (!(pTcpInfo = (TCP_REQUEST_SET_INFORMATION_EX *) NbtAllocMem (BufferLength,NBT_TAG2('22'))))
{
return (STATUS_INSUFFICIENT_RESOURCES);
}
CTEZeroMemory(pTcpInfo, BufferLength);
pSockOption = (TCPSocketOption *) (&pTcpInfo->Buffer[0]);
pTcpInfo->ID.toi_entity.tei_entity = CO_TL_ENTITY;
pTcpInfo->ID.toi_class = INFO_CLASS_PROTOCOL;
pTcpInfo->BufferSize = sizeof (TCPSocketOption);
//
// Set the Configured values
//
pTcpInfo->ID.toi_id = ToiId;
pTcpInfo->ID.toi_type = ToiType;
pSockOption->tso_value = InfoBufferValue;
Status = SetTcpInfo (FileHandle, pTcpInfo, BufferLength);
if (!NT_SUCCESS(Status))
{
KdPrint (("Nbt.NbtSetTcpInfo: SetTcpInfo FAILed <%x>, Id=<0x%x>, Type=<0x%x>, Value=<%x>\n",
Status, ToiId, ToiType, InfoBufferValue));
}
CTEMemFree (pTcpInfo);
return (Status);
}
//----------------------------------------------------------------------------
NTSTATUS
NbtSetSmbBindingInfo2(
IN tDEVICECONTEXT *pDeviceContext,
IN NETBT_SMB_BIND_REQUEST *pSmbRequest
)
{
ULONG i, Operation;
PLIST_ENTRY pEntry,pHead;
KIRQL OldIrq;
ULONG NumBindings = 0;
CTEULONGLONG AddedAdapterMask = 0;
CTEULONGLONG DeletedAdapterMask = 0;
CTEULONGLONG BindListAdapterMask = 0;
CTEULONGLONG OriginalMask;
tDEVICECONTEXT *pDeviceContextBind = NULL;
NTSTATUS LocNtStatus = STATUS_SUCCESS;
if (!IsDeviceNetbiosless (pDeviceContext)) {
return (STATUS_UNSUCCESSFUL);
}
if (NULL == pSmbRequest) {
return STATUS_INVALID_PARAMETER;
}
if (pSmbRequest->RequestType == SMB_SERVER) {
OriginalMask = NbtConfig.ServerMask;
} else if (pSmbRequest->RequestType == SMB_CLIENT) {
OriginalMask = NbtConfig.ClientMask;
} else {
ASSERT(0);
return STATUS_INVALID_PARAMETER;
}
if (pSmbRequest->MultiSZBindList)
{
NTSTATUS status;
tDEVICES *pBindings = NULL;
ULONG MaxBindings;
MaxBindings = NBT_MAXIMUM_BINDINGS;
while (MaxBindings < 5000) {
status = STATUS_INSUFFICIENT_RESOURCES;
MaxBindings *= 2;
pBindings = NbtAllocMem ((sizeof(tDEVICES)+MaxBindings*sizeof(UNICODE_STRING)), NBT_TAG2('26'));
if (pBindings == NULL) {
break;
}
NumBindings = 0;
status = NbtParseMultiSzEntries (pSmbRequest->MultiSZBindList, (PVOID)(-1), MaxBindings, pBindings, &NumBindings);
if (status != STATUS_BUFFER_OVERFLOW) {
break;
}
CTEMemFree (pBindings);
pBindings = NULL;
}
if (status != STATUS_SUCCESS) {
if (pBindings) { // NbtParseMultiSzEntries can return failure other than STATUS_BUFFER_OVERFLOW
CTEMemFree (pBindings);
}
KdPrint(("Nbt.NbtSetSmbBindingInfo[STATUS_INSUFFICIENT_RESOURCES]: MaxBindings = <%d>\n",
MaxBindings));
return status;
}
ASSERT(pBindings);
//
// First, get the complete list of all bindings
//
for (i=0; i<NumBindings; i++)
{
if (pDeviceContextBind = NbtFindAndReferenceDevice (&pBindings->Names[i], FALSE))
{
BindListAdapterMask |= pDeviceContextBind->AdapterMask;
NBT_DEREFERENCE_DEVICE (pDeviceContextBind, REF_DEV_FIND_REF, FALSE);
}
}
CTEMemFree (pBindings);
}
else if (pSmbRequest->pDeviceName)
{
KdPrint (("Nbt.NbtSetSmbBindingInfo[WARNING]: NULL MultiSZBindList string!\n"));
BindListAdapterMask = OriginalMask;
if (pDeviceContextBind = NbtFindAndReferenceDevice (pSmbRequest->pDeviceName, FALSE))
{
switch (pSmbRequest->PnPOpCode)
{
case (TDI_PNP_OP_ADD):
{
BindListAdapterMask |= pDeviceContextBind->AdapterMask;
break;
}
case (TDI_PNP_OP_DEL):
{
BindListAdapterMask &= (~pDeviceContextBind->AdapterMask);
break;
}
default:
{
break;
}
}
NBT_DEREFERENCE_DEVICE (pDeviceContextBind, REF_DEV_FIND_REF, FALSE);
}
else
{
return (STATUS_SUCCESS);
}
}
else
{
ASSERTMSG ("Nbt.NbtSetSmbBindingInfo[ERROE]: NULL MultiSZBindList and NULL pDeviceName!\n", 0);
return (STATUS_UNSUCCESSFUL);
}
IF_DBG(NBT_DEBUG_PNP_POWER)
KdPrint (("Nbt.NbtSetSmbBindingInfo: PnPOpCode=<%x>, Bindings=<%d>, BindMask=[%lx:%lx]==>[%lx:%lx]\n",
pSmbRequest->PnPOpCode, NumBindings, OriginalMask, BindListAdapterMask));
CTESpinLock(&NbtConfig.JointLock,OldIrq);
// NetBT used to incrementally add interface to tcpip.
// However, for some reason, the list in tcpip and netbt
// may be different. To increase the reliability, I
// remove the incrementally adding.
//
// AddedAdapterMask = BindListAdapterMask & (~OriginalMask)
AddedAdapterMask = BindListAdapterMask; // Always add device
DeletedAdapterMask = OriginalMask & (~BindListAdapterMask); // Devices Removed
if ((!AddedAdapterMask) && (!DeletedAdapterMask))
{
//
// If there are no adapters to be added or deleted, just return
//
CTESpinFree(&NbtConfig.JointLock,OldIrq);
return (STATUS_SUCCESS);
}
if (pSmbRequest->RequestType == SMB_SERVER) {
NbtConfig.ServerMask = BindListAdapterMask;
} else if (pSmbRequest->RequestType == SMB_CLIENT) {
NbtConfig.ClientMask = BindListAdapterMask;
CTESpinFree(&NbtConfig.JointLock,OldIrq);
return STATUS_SUCCESS;
} else {
ASSERT(0);
}
pEntry = pHead = &NbtConfig.DeviceContexts;
while ((pEntry = pEntry->Flink) != pHead)
{
pDeviceContextBind = CONTAINING_RECORD(pEntry, tDEVICECONTEXT, Linkage);
if (pDeviceContext->IPInterfaceContext == (ULONG)-1) // For Cluster devices, etc
{
continue;
}
ASSERT(0 == (AddedAdapterMask & DeletedAdapterMask));
if (AddedAdapterMask & pDeviceContextBind->AdapterMask)
{
AddedAdapterMask &= ~(pDeviceContextBind->AdapterMask);
Operation = AO_OPTION_ADD_IFLIST;
}
else if (DeletedAdapterMask & pDeviceContextBind->AdapterMask)
{
DeletedAdapterMask &= ~(pDeviceContextBind->AdapterMask);
Operation = AO_OPTION_DEL_IFLIST;
}
else
{
continue;
}
NBT_REFERENCE_DEVICE (pDeviceContextBind, REF_DEV_FIND_REF, TRUE);
CTESpinFree(&NbtConfig.JointLock,OldIrq);
IF_DBG(NBT_DEBUG_PNP_POWER)
KdPrint(("Nbt.NbtSetSmbBindingInfo: %sing Device=%wZ\n",
(Operation == AO_OPTION_ADD_IFLIST ? "ADD" : "REMOV"), &pDeviceContextBind->BindName));
//
// Set the Session port info
//
if (pDeviceContext->hSession)
{
LocNtStatus = NbtSetTcpInfo (pDeviceContext->hSession,
Operation,
INFO_TYPE_ADDRESS_OBJECT,
pDeviceContextBind->IPInterfaceContext);
if (!NT_SUCCESS(LocNtStatus)) {
NbtConfig.ServerMask &= ~(pDeviceContextBind->AdapterMask);
}
}
//
// Now, set the same for the Datagram port
//
if ((pDeviceContext->pFileObjects) &&
(pDeviceContext->pFileObjects->hDgram))
{
NbtSetTcpInfo (pDeviceContext->pFileObjects->hDgram,
Operation,
INFO_TYPE_ADDRESS_OBJECT,
pDeviceContextBind->IPInterfaceContext);
}
CTESpinLock(&NbtConfig.JointLock,OldIrq);
NBT_DEREFERENCE_DEVICE (pDeviceContextBind, REF_DEV_FIND_REF, TRUE);
//
// Set to restart from the beginning
//
pEntry = &NbtConfig.DeviceContexts;
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------
NTSTATUS
NbtSetSmbBindingInfo(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp,
IN PIO_STACK_LOCATION pIrpSp
)
{
NETBT_SMB_BIND_REQUEST *pSmbRequest = (PNETBT_SMB_BIND_REQUEST)pIrp->AssociatedIrp.SystemBuffer;
PWSTR pBindListCache = NULL;
PWSTR pBindList = NULL;
PWSTR pOldBindList = NULL;
PWSTR *pTarget = NULL;
ULONG uInputLength = 0;
ULONG uLength = 0;
NTSTATUS status = STATUS_SUCCESS;
tDEVICECONTEXT *pSavedSmbDevice = NULL;
KIRQL OldIrq = 0;
CTESpinLock(&NbtConfig.JointLock,OldIrq);
if (pNbtSmbDevice && !gbDestroyingSmbDevice) {
pSavedSmbDevice = pNbtSmbDevice;
NBT_REFERENCE_DEVICE (pSavedSmbDevice, REF_DEV_SMB_BIND, TRUE);
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
if (NULL == pSavedSmbDevice) {
status = STATUS_DEVICE_BUSY;
goto error;
}
uInputLength = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
if (!(IsDeviceNetbiosless (pDeviceContext)) ||
(!pSmbRequest) ||
(uInputLength < sizeof(NETBT_SMB_BIND_REQUEST)))
{
KdPrint(("Nbt.NbtSetSmbBindingInfo: ERROR: pSmbRequest=<%p>, pDevice=<%p>\n",
pSmbRequest, pDeviceContext));
status = STATUS_UNSUCCESSFUL;
goto error;
}
if (pIrp->RequestorMode != KernelMode) {
status = STATUS_ACCESS_DENIED;
goto error;
}
status = NbtSetSmbBindingInfo2 (pDeviceContext, pSmbRequest);
if (NT_SUCCESS(status) && pSmbRequest->MultiSZBindList) {
//
// Cache the binding info
//
pBindList = pSmbRequest->MultiSZBindList;
uInputLength = 0;
while (*pBindList) {
uLength = wcslen (pBindList) + 1;
uInputLength += uLength;
pBindList += uLength;
}
uInputLength++;
uInputLength *= sizeof(WCHAR);
pBindList = pSmbRequest->MultiSZBindList;
if (pSmbRequest->RequestType == SMB_SERVER) {
pTarget = &NbtConfig.pServerBindings;
} else if (pSmbRequest->RequestType == SMB_CLIENT) {
pTarget = &NbtConfig.pClientBindings;
} else {
ASSERT(0);
}
pBindListCache = NbtAllocMem (uInputLength, NBT_TAG2('27'));
if (NULL != pBindListCache) {
RtlCopyMemory (pBindListCache, pBindList, uInputLength);
pOldBindList = InterlockedExchangePointer (pTarget, pBindListCache);
//
// Free the old copy if any
//
if (NULL != pOldBindList) {
CTEFreeMem (pOldBindList);
pOldBindList = NULL;
}
}
}
error:
if (pSavedSmbDevice) {
NBT_DEREFERENCE_DEVICE (pSavedSmbDevice, REF_DEV_SMB_BIND, FALSE);
}
return status;
}
NTSTATUS
NbtEnableNetbiosSmb(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp,
IN PIO_STACK_LOCATION pIrpSp
)
{
NTSTATUS status = STATUS_SUCCESS;
if (pNbtSmbDevice) {
goto error;
}
pNbtSmbDevice = NbtCreateSmbDevice();
if (NULL == pNbtSmbDevice) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto error;
}
NbtConfig.SMBDeviceEnabled = TRUE;
if (AddressCount > 0) {
NbtNotifyTdiClients (pNbtSmbDevice, NBT_TDI_REGISTER);
}
error:
return status;
}
VOID
NbtSleep(
ULONG TimeToSleep
)
{
KEVENT NeverTriggered = { 0 };
NTSTATUS status = STATUS_SUCCESS;
LARGE_INTEGER liTimeOut = { 0 };
liTimeOut.QuadPart = UInt32x32To64(TimeToSleep, 10000UL);
liTimeOut.QuadPart = -liTimeOut.QuadPart;
KeInitializeEvent(&NeverTriggered, NotificationEvent, FALSE);
status = KeWaitForSingleObject(
&NeverTriggered,
Executive,
KernelMode,
FALSE,
&liTimeOut
);
ASSERT (status == STATUS_TIMEOUT);
}
NTSTATUS
NbtDisableNetbiosSmb(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp,
IN PIO_STACK_LOCATION pIrpSp
)
{
NTSTATUS status = STATUS_SUCCESS;
KIRQL OldIrq = 0;
tDEVICECONTEXT *pSavedSmbDevice = NULL;
int i = 0;
if (NULL == pNbtSmbDevice) {
goto error;
}
//
// Prevent the pNbtSmbDevice from being used
//
// NetBT could access the pNbtSmbDevice in 2 scenarios:
// 1. referenced, but without holding the JointLock.
// 2. No reference but with JointLock held.
//
gbDestroyingSmbDevice = TRUE;
ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
//
// Wait for the refcount to become 1 for at most 8 second
//
for(i = 0; i < 8 && (*(volatile*)(&pNbtSmbDevice->RefCount) != 1); i++) {
NbtSleep(1000);
}
if (*(volatile*)(&pNbtSmbDevice->RefCount) != 1) {
gbDestroyingSmbDevice = FALSE;
status = STATUS_DEVICE_BUSY;
goto error;
}
//
// From now on, we're sure that scenario 1 couldn't
// happen any more.
//
//
// Set pNbtSmbDevice to NULL.
//
CTESpinLock(&NbtConfig.JointLock,OldIrq);
pSavedSmbDevice = pNbtSmbDevice;
pNbtSmbDevice = NULL;
NbtConfig.SMBDeviceEnabled = FALSE;
CTESpinFree(&NbtConfig.JointLock,OldIrq);
//
// From now on, we're sure that scenario 2 couldn't
// happen any more.
//
NbtNotifyTdiClients (pSavedSmbDevice, NBT_TDI_DEREGISTER);
status = NbtDestroyDevice (pSavedSmbDevice, TRUE);
KdPrint(("NbtDisableSmbDevice -- status=<%x>\n", status));
status = STATUS_SUCCESS;
gbDestroyingSmbDevice = FALSE;
error:
return status;
}
HANDLE
NbtOpenParametersRegistry(
VOID
)
{
HANDLE NbtConfigHandle = NULL;
HANDLE ParametersHandle = NULL;
NTSTATUS status = STATUS_SUCCESS;
OBJECT_ATTRIBUTES TmpObjectAttributes = { 0 };
ULONG Disposition = 0;
extern NTSTATUS NbtOpenRegistry(
IN HANDLE NbConfigHandle,
IN PWSTR String,
OUT PHANDLE pHandle
);
CTEPagedCode();
InitializeObjectAttributes (&TmpObjectAttributes,
&NbtConfig.pRegistry,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL
);
status = ZwCreateKey (&NbtConfigHandle,
KEY_READ,
&TmpObjectAttributes,
0,
NULL,
0,
&Disposition
);
if (!NT_SUCCESS(status)) {
goto error;
}
status = NbtOpenRegistry (NbtConfigHandle, L"Parameters", &ParametersHandle);
ZwClose(NbtConfigHandle);
error:
return ParametersHandle;
}
NTSTATUS
NbtEnableDisableNetbiosSmb(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp,
IN PIO_STACK_LOCATION pIrpSp
)
{
NTSTATUS status = STATUS_SUCCESS;
PDWORD pdwEnableSmb = (PDWORD)(pIrp->AssociatedIrp.SystemBuffer);
DWORD dwSMBDeviceEnabled = TRUE;
HANDLE hParm = NULL;
if (InterlockedIncrement(&gdwPendingEnableDisableSmbDevice) > 1) {
status = STATUS_DEVICE_BUSY;
goto error;
}
if (pIrp->RequestorMode != KernelMode) {
status = STATUS_ACCESS_DENIED;
goto error;
}
if (pDeviceContext == pNbtSmbDevice || KeGetCurrentIrql() != PASSIVE_LEVEL) {
status = STATUS_INVALID_PARAMETER;
goto error;
}
if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof(DWORD)) {
status = STATUS_INVALID_PARAMETER;
goto error;
}
switch (*pdwEnableSmb) {
case NETBT_ENABLE_NETBIOS_SMB:
dwSMBDeviceEnabled = TRUE;
KdPrint(("Enable NetbiosSmb\n"));
NbtTrace(NBT_TRACE_PNP, ("Enable NetbiosSmb"));
break;
case NETBT_DISABLE_NETBIOS_SMB:
dwSMBDeviceEnabled = FALSE;
KdPrint(("Disable NetbiosSmb\n"));
NbtTrace(NBT_TRACE_PNP, ("Disable NetbiosSmb"));
break;
case NETBT_RESTORE_NETBIOS_SMB:
KdPrint(("Restore NetbiosSmb\n"));
NbtTrace(NBT_TRACE_PNP, ("Restore NetbiosSmb"));
hParm = NbtOpenParametersRegistry();
if (hParm == NULL) {
status = STATUS_UNSUCCESSFUL;
goto error;
}
dwSMBDeviceEnabled = CTEReadSingleIntParameter(hParm,
WS_SMB_DEVICE_ENABLED,
1, // Enabled by default
0);
ZwClose(hParm);
hParm = NULL;
break;
default:
status = STATUS_INVALID_PARAMETER;
goto error;
}
if (dwSMBDeviceEnabled) {
status = NbtEnableNetbiosSmb(pDeviceContext, pIrp, pIrpSp);
KdPrint(("NbtEanbleNetbiosSmb return 0x%08lx\n", status));
NbtTrace(NBT_TRACE_PNP, ("NbtEnableNetbiosSmb return %!status!", status));
} else {
status = NbtDisableNetbiosSmb(pDeviceContext, pIrp, pIrpSp);
KdPrint(("NbtDisableNetbiosSmb return 0x%08lx\n", status));
NbtTrace(NBT_TRACE_PNP, ("NbtDisableNetbiosSmb return %!status!", status));
}
error:
InterlockedDecrement(&gdwPendingEnableDisableSmbDevice);
return status;
}
//----------------------------------------------------------------------------
NTSTATUS
DispatchIoctls(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp,
IN PIO_STACK_LOCATION pIrpSp
)
/*++
Routine Description:
This Routine handles calling the OS independent routine depending on
the Ioctl passed in.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
NTSTATUS status=STATUS_UNSUCCESSFUL;
ULONG ControlCode;
ULONG Size;
PVOID pBuffer;
ControlCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
NbtTrace(NBT_TRACE_IOCTL, ("pDeviceContext %p: pIrp %p, IOCTL 0x%08x function %d %Z",
pDeviceContext,
pIrp,
ControlCode,
(ControlCode >> 2) & 0xfff,
&pDeviceContext->BindName));
switch (ControlCode)
{
case IOCTL_NETBT_REREAD_REGISTRY:
{
//
// This function can be called from either IOCTL or PnP path.
// IOCTL path can be called from non-admin, which can be potentially
// exploited.
// Therefore pass FALSE to tell the NTReReadRegistry not to do
// name refreshing.
//
status = NTReReadRegistry(pDeviceContext, FALSE);
break;
}
case IOCTL_NETBT_ENABLE_EXTENDED_ADDR:
{
//
// Enable extended addressing - pass up IP addrs on Datagram Recvs.
//
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp);
tCLIENTELE *pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pClientEle, NBT_VERIFY_CLIENT, NBT_VERIFY_CLIENT_DOWN))
{
//
// To make the stresser (devctl.exe) happy. [JRuan 12/18/2000]
//
// ASSERTMSG ("Nbt.DispatchIoctls: ERROR - Invalid Address Handle\n", 0);
status = STATUS_INVALID_HANDLE;
}
else if (pIrpSp->FileObject->FsContext2 != (PVOID)NBT_ADDRESS_TYPE)
{
status = STATUS_INVALID_ADDRESS;
}
else
{
pClientEle->ExtendedAddress = TRUE;
status = STATUS_SUCCESS;
}
break;
}
case IOCTL_NETBT_DISABLE_EXTENDED_ADDR:
{
//
// Disable extended addressing - dont pass up IP addrs on Datagram Recvs.
//
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp);
tCLIENTELE *pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pClientEle, NBT_VERIFY_CLIENT, NBT_VERIFY_CLIENT_DOWN))
{
// ASSERTMSG ("Nbt.DispatchIoctls: ERROR - Invalid Address Handle\n", 0);
status = STATUS_INVALID_HANDLE;
}
else if (pIrpSp->FileObject->FsContext2 != (PVOID)NBT_ADDRESS_TYPE)
{
status = STATUS_INVALID_ADDRESS;
}
else
{
pClientEle->ExtendedAddress = FALSE;
status = STATUS_SUCCESS;
}
break;
}
case IOCTL_NETBT_GET_WINS_ADDR:
{
if ((pIrp->MdlAddress) &&
((Size = MmGetMdlByteCount (pIrp->MdlAddress)) >= sizeof(tWINS_ADDRESSES)) &&
(pBuffer = (PVOID) MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority)))
{
status = STATUS_SUCCESS;
if (Size >= sizeof(tWINS_NODE_INFO))
{
tWINS_NODE_INFO UNALIGNED *pWinsBuffer = (tWINS_NODE_INFO *) pBuffer;
CTEMemCopy (&pWinsBuffer->AllNameServers,
pDeviceContext->lAllNameServers,
(sizeof(tIPADDRESS)*(2+MAX_NUM_OTHER_NAME_SERVERS)));
pWinsBuffer->NumOtherServers = pDeviceContext->lNumOtherServers;
pWinsBuffer->LastResponsive = pDeviceContext->lLastResponsive;
pWinsBuffer->NetbiosEnabled = pDeviceContext->NetbiosEnabled;
pWinsBuffer->NodeType = NodeType;
pIrp->IoStatus.Information = sizeof(tWINS_NODE_INFO);
}
else
{
tWINS_ADDRESSES UNALIGNED *pWinsBuffer = (tWINS_ADDRESSES *) pBuffer;
pWinsBuffer->PrimaryWinsServer = pDeviceContext->lNameServerAddress;
pWinsBuffer->BackupWinsServer = pDeviceContext->lBackupServer;
pIrp->IoStatus.Information = sizeof(tWINS_ADDRESSES);
}
}
break;
}
case IOCTL_NETBT_GET_IP_ADDRS:
{
status = GetIpAddrs (pDeviceContext, pIrp);
break;
}
case IOCTL_NETBT_GET_IP_SUBNET:
{
ULONG Length;
PULONG pIpAddr;
//
// return this devicecontext's ip address and all the other
// ip addrs after it.
//
if (pIrp->MdlAddress)
{
Length = MmGetMdlByteCount( pIrp->MdlAddress );
if (Length < 2*sizeof(ULONG))
{
status = STATUS_BUFFER_OVERFLOW;
}
else if (pIpAddr = (PULONG )MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority))
{
//
// Put this adapter first in the list
//
*((tIPADDRESS UNALIGNED *) pIpAddr) = pDeviceContext->AssignedIpAddress;
pIpAddr++;
*((tIPADDRESS UNALIGNED *) pIpAddr) = pDeviceContext->SubnetMask;
pIpAddr++;
if (Length >= 3*sizeof(ULONG))
{
*((tIPADDRESS UNALIGNED *) pIpAddr) = pDeviceContext->DeviceType;
}
status = STATUS_SUCCESS;
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
break;
}
//
// The following Ioctl is used mainly by the Server service
//
case IOCTL_NETBT_SET_TCP_CONNECTION_INFO:
{
status = NbtClientSetTcpInfo ((tCONNECTELE *) pIrpSp->FileObject->FsContext,
pIrp->AssociatedIrp.SystemBuffer,
pIrpSp->Parameters.DeviceIoControl.InputBufferLength);
break;
}
//
// The following Ioctls are used mainly by NbtStat.exe for diagnostic purposes
//
case IOCTL_NETBT_GET_INTERFACE_INFO:
{
status = NbtGetInterfaceInfo (pIrp);
break;
}
case IOCTL_NETBT_PURGE_CACHE:
{
DelayedNbtResyncRemoteCache (NULL, NULL, NULL, NULL);
status = STATUS_SUCCESS;
break;
}
case IOCTL_NETBT_GET_CONNECTIONS:
{
if (pIrp->MdlAddress)
{
Size = MmGetMdlByteCount( pIrp->MdlAddress ) ;
// return an array of netbios names that are registered
status = NbtQueryConnectionList (pDeviceContext, &pBuffer, &Size);
}
break;
}
case IOCTL_NETBT_ADAPTER_STATUS:
{
if (pIrp->MdlAddress)
{
PIO_STACK_LOCATION pIrpSp;
tIPANDNAMEINFO *pIpAndNameInfo;
PCHAR pName;
ULONG lNameType;
ULONG NameLen;
ULONG IpAddrsList[2];
tIPADDRESS *pIpAddrs = NULL;
tDGRAM_SEND_TRACKING *pTracker;
//
// in case the call results in a name query on the wire...
//
IoMarkIrpPending(pIrp);
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pIpAndNameInfo = pIrp->AssociatedIrp.SystemBuffer;
NameLen = pIrpSp->Parameters.DeviceIoControl.InputBufferLength
- FIELD_OFFSET(tIPANDNAMEINFO,NetbiosAddress);
//
// Bug# 125288+120947: Make sure the data passed in + the Address type are good
//
if ((pIpAndNameInfo) &&
(pIrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(tIPANDNAMEINFO)))
{
TDI_ADDRESS_NETBT_INTERNAL TdiAddr;
// this routine gets a ptr to the netbios name out of the wierd
// TDI address syntax.
status = GetNetBiosNameFromTransportAddress(
(PTRANSPORT_ADDRESS) &pIpAndNameInfo->NetbiosAddress,
NameLen, &TdiAddr);
pName = TdiAddr.OEMRemoteName.Buffer;
NameLen = TdiAddr.OEMRemoteName.Length;
lNameType = TdiAddr.NameType;
}
if (NT_SUCCESS(status) &&
(lNameType == TDI_ADDRESS_NETBIOS_TYPE_UNIQUE) &&
(NameLen == NETBIOS_NAME_SIZE))
{
//
// Nbtstat sends down * in the first byte on Nbtstat -A <IP address>
// Make sure we let that case go ahead.
//
if (!pDeviceContext->NetbiosEnabled) {
status = STATUS_INVALID_DEVICE_STATE;
}
else if ((pName[0] == '*') && (pIpAndNameInfo->IpAddress == 0))
{
status = STATUS_BAD_NETWORK_PATH;
}
else if (NT_SUCCESS (status = GetTracker(&pTracker, NBT_TRACKER_ADAPTER_STATUS)))
{
if (pIpAndNameInfo->IpAddress)
{
IpAddrsList[0] = pIpAndNameInfo->IpAddress;
IpAddrsList[1] = 0;
pIpAddrs = IpAddrsList;
}
pTracker->ClientContext = pIrp;
status = NbtSendNodeStatus(pDeviceContext,
pName,
pIpAddrs,
pTracker,
CopyNodeStatusResponseCompletion);
// only complete the irp (below) for failure status's
if (status == STATUS_PENDING)
{
return(status);
}
//
// We cannot have a Success status returned here!
//
if (status == STATUS_SUCCESS)
{
ASSERT (0);
status = STATUS_UNSUCCESSFUL;
}
FreeTracker (pTracker, RELINK_TRACKER);
}
}
else if (NT_SUCCESS(status))
{
status = STATUS_INVALID_PARAMETER; // The NameType or NameLen must be wrong!
}
// the request has been satisfied, so unmark the pending
// since we will return the irp below
//
pIrpSp->Control &= ~SL_PENDING_RETURNED;
}
break;
}
case IOCTL_NETBT_GET_REMOTE_NAMES:
{
if (pIrp->MdlAddress)
{
Size = MmGetMdlByteCount( pIrp->MdlAddress ) ;
// return an array of netbios names that are registered
status = NbtQueryAdapterStatus(pDeviceContext, &pBuffer, &Size, NBT_REMOTE);
}
break;
}
case IOCTL_NETBT_GET_BCAST_NAMES:
{
if (pIrp->MdlAddress)
{
Size = MmGetMdlByteCount( pIrp->MdlAddress ) ;
// return an array of netbios names that are registered
status = NbtQueryBcastVsWins(pDeviceContext,&pBuffer,&Size);
}
break;
}
case IOCTL_NETBT_NAME_RELEASE_REFRESH:
{
status = ReRegisterLocalNames (NULL, TRUE);
break;
}
//
// The following Ioctls are used by the Cluster code
//
case IOCTL_NETBT_ADD_INTERFACE:
{
//
// Creates a dummy devicecontext which can be primed by the layer above
// with a DHCP address. This is to support multiple IP addresses per adapter
// for the Clusters group; but can be used by any module that needs support
// for more than one IP address per adapter. This private interface hides the
// devices thus created from the setup/regisrty and that is fine since the
// component (say, the clusters client) takes the responsibility for ensuring
// that the server (above us) comes to know of this new device.
//
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp);
pBuffer = pIrp->AssociatedIrp.SystemBuffer;
Size = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
//
// return the export string created.
//
status = NbtAddNewInterface(pIrp, pBuffer, Size);
IF_DBG(NBT_DEBUG_PNP_POWER)
KdPrint(("Nbt.DispatchIoctls: ADD_INTERFACE -- status=<%x>\n", status));
NTIoComplete(pIrp,status,(ULONG)-1);
return status;
}
case IOCTL_NETBT_DELETE_INTERFACE:
{
//
// Dereference this device for the Reference taken in the
// Dispatch routine so that the cleanup can proceed properly
//
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_DISPATCH, FALSE);
if (pDeviceContext->DeviceType == NBT_DEVICE_CLUSTER)
{
//
// Delete the device this came down on..
//
status = NbtDestroyDevice (pDeviceContext, TRUE);
}
else
{
KdPrint(("Nbt.DispatchIoctls: ERROR: DELETE_INTERFACE <%x>, pDevice=<%p>\n",
status, pDeviceContext));
}
break;
}
case IOCTL_NETBT_QUERY_INTERFACE_INSTANCE:
{
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (pIrp);
//
// Validate input/output buffer size
//
Size = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
if (Size < sizeof(NETBT_ADD_DEL_IF))
{
// IF_DBG(NBT_DEBUG_PNP_POWER)
KdPrint(("Nbt.DispatchIoctls: QUERY_INTERFACE_INSTANCE: Output buffer too small\n"));
status = STATUS_INVALID_PARAMETER;
}
else
{
PNETBT_ADD_DEL_IF pAddDelIf = (PNETBT_ADD_DEL_IF)pIrp->AssociatedIrp.SystemBuffer;
status = STATUS_SUCCESS;
ASSERT(pDeviceContext->DeviceType == NBT_DEVICE_CLUSTER);
pAddDelIf->InstanceNumber = pDeviceContext->InstanceNumber;
pAddDelIf->Status = status;
pIrp->IoStatus.Information = sizeof(NETBT_ADD_DEL_IF);
NTIoComplete(pIrp,status,(ULONG)-1);
return status;
}
break;
}
case IOCTL_NETBT_NEW_IPADDRESS:
{
tNEW_IP_ADDRESS *pNewAddress = (tNEW_IP_ADDRESS *)pIrp->AssociatedIrp.SystemBuffer;
status = STATUS_UNSUCCESSFUL;
//
// Bug# 202320: Make sure the data passed in is valid
//
if ((pDeviceContext->DeviceType == NBT_DEVICE_CLUSTER) &&
(pNewAddress) &&
(pIrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(tNEW_IP_ADDRESS)))
{
KdPrint (("Nbt.DispatchIoctls: Calling NbtNewDhcpAddress on ClusterDevice <%x>!\n",
pDeviceContext));
pDeviceContext->AssignedIpAddress = ntohl (pNewAddress->IpAddress);
status = NbtNewDhcpAddress (pDeviceContext, pNewAddress->IpAddress, pNewAddress->SubnetMask);
ASSERT (pDeviceContext->AssignedIpAddress == pDeviceContext->IpAddress);
IF_DBG(NBT_DEBUG_PNP_POWER)
KdPrint (("Nbt.DispatchIoctls: NEW_IPADDRESS, status=<%x>, IP=<%x>, pDevice=<%p>\n",
status, pNewAddress->IpAddress, pDeviceContext));
}
else
{
KdPrint(("Nbt.DispatchIoctls: ERROR: NEW_IPADDRESS status=<%x>, pDevice=<%p>\n",
status, pDeviceContext));
}
break;
}
case IOCTL_NETBT_SET_WINS_ADDRESS:
{
//
// Sets the WINS addresses for a dynamic adapter
//
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation (pIrp);
//
// Validate input/output buffer size
//
Size = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
if (Size < sizeof(NETBT_SET_WINS_ADDR))
{
// IF_DBG(NBT_DEBUG_PNP_POWER)
KdPrint(("NbtSetWinsAddr: Input buffer too small for struct\n"));
status = STATUS_INVALID_PARAMETER;
}
else if (pDeviceContext->DeviceType == NBT_DEVICE_CLUSTER)
{
PNETBT_SET_WINS_ADDR pSetWinsAddr = (PNETBT_SET_WINS_ADDR)pIrp->AssociatedIrp.SystemBuffer;
status = STATUS_SUCCESS;
pDeviceContext->lNameServerAddress = pSetWinsAddr->PrimaryWinsAddr;
pDeviceContext->lBackupServer = pSetWinsAddr->SecondaryWinsAddr;
pDeviceContext->SwitchedToBackup = 0;
pDeviceContext->RefreshToBackup = 0;
pSetWinsAddr->Status = status;
pIrp->IoStatus.Information = 0; // We are not copying any data to the Output buffers
NTIoComplete (pIrp,status,(ULONG)-1);
return status;
}
else
{
KdPrint(("Nbt.DispatchIoctls: ERROR: SET_WINS_ADDRESS <%x>, pDevice=<%p>\n",
status, pDeviceContext));
}
break;
}
//
// The following Ioctls are used by the LmHost Services Dll (lmhSvc.dll) to
// help NetBT ping addresses in user space or resolve names in Dns
//
case IOCTL_NETBT_DNS_NAME_RESOLVE:
{
if (pIrp->MdlAddress)
{
Size = MmGetMdlByteCount( pIrp->MdlAddress ) ;
if (Size < sizeof (tIPADDR_BUFFER_DNS))
{
// IF_DBG(NBT_DEBUG_PNP_POWER)
KdPrint(("Nbt.DnsNameResolve: Input buffer size=<%d> < tIPADDR_BUFFER_DNS=<%d>\n",
Size, sizeof (tIPADDR_BUFFER_DNS)));
status = STATUS_INVALID_PARAMETER;
}
else if (pBuffer = MmGetSystemAddressForMdlSafe (pIrp->MdlAddress, NormalPagePriority))
{
// return an array of netbios names that are registered
status = NtProcessLmHSvcIrp (pDeviceContext,pBuffer,Size,pIrp,NBT_RESOLVE_WITH_DNS);
return(status);
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
break;
}
case IOCTL_NETBT_CHECK_IP_ADDR:
{
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Ioctl Value is %X (IOCTL_NETBT_CHECK_IP_ADDR)\n",ControlCode));
if (pIrp->MdlAddress)
{
Size = MmGetMdlByteCount( pIrp->MdlAddress ) ;
if (Size < sizeof (tIPADDR_BUFFER_DNS))
{
// IF_DBG(NBT_DEBUG_PNP_POWER)
KdPrint(("Nbt.CheckIpAddr: Input buffer size=<%d> < tIPADDR_BUFFER_DNS=<%d>\n",
Size, sizeof (tIPADDR_BUFFER_DNS)));
status = STATUS_INVALID_PARAMETER;
}
else if (pBuffer = MmGetSystemAddressForMdlSafe (pIrp->MdlAddress, NormalPagePriority))
{
// return an array of netbios names that are registered
status = NtProcessLmHSvcIrp (pDeviceContext,pBuffer,Size,pIrp,NBT_PING_IP_ADDRS);
return(status);
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
break;
}
//
// The following Ioctl is used by the DNS resolver to resolve names through Wins/Bcast
//
case IOCTL_NETBT_FIND_NAME:
{
tIPADDR_BUFFER *pIpAddrBuffer;
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation (pIrp);
//
// in case the call results in a name query on the wire...
//
IoMarkIrpPending(pIrp);
//
// Bug# 120957: Make sure the data passed in + the Address type are good
// Bug# 234627: Verify non-NULL MdlAddress ptr
//
pIpAddrBuffer = pIrp->AssociatedIrp.SystemBuffer;
if ((pIrp->MdlAddress) && // to copy the data back!
(pIpAddrBuffer) &&
(pIrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(tIPADDR_BUFFER)))
{
status = NbtQueryFindName((PTDI_CONNECTION_INFORMATION)pIpAddrBuffer,
pDeviceContext,
pIrp,
TRUE);
}
if (status == STATUS_PENDING)
{
return(status);
}
// the request has been satisfied, so unmark the pending
// since we will return the irp below
//
pIrpSp->Control &= ~SL_PENDING_RETURNED;
break;
}
//
// The following Ioctls are used by the Wins server
//
case IOCTL_NETBT_WINS_RCV:
{
tWINS_INFO *pWins = pIrpSp->FileObject->FsContext;
if ((pDeviceContext == pWinsDeviceContext) &&
(NBT_VERIFY_HANDLE (pWins, NBT_VERIFY_WINS_ACTIVE)))
{
if (pIrp->MdlAddress)
{
status = RcvIrpFromWins(pIrp);
return(status);
}
}
else
{
status = STATUS_INVALID_HANDLE;
}
break;
}
case IOCTL_NETBT_WINS_SEND:
{
tWINS_INFO *pWins = pIrpSp->FileObject->FsContext;
BOOLEAN MustSend;
if ((pDeviceContext == pWinsDeviceContext) &&
(NBT_VERIFY_HANDLE (pWins, NBT_VERIFY_WINS_ACTIVE)))
{
if ((pIrp->MdlAddress) && !(IsListEmpty(&NbtConfig.DeviceContexts)))
{
status = WinsSendDatagram (pDeviceContext,pIrp,(MustSend = FALSE));
return(status);
}
}
else
{
status = STATUS_INVALID_HANDLE;
}
break;
}
case IOCTL_NETBT_WINS_SET_INFO:
{
tWINS_INFO *pWins = pIrpSp->FileObject->FsContext;
tWINS_SET_INFO *pWinsSetInfo = (tWINS_SET_INFO *) pIrp->AssociatedIrp.SystemBuffer;
if ((pDeviceContext == pWinsDeviceContext) &&
(NBT_VERIFY_HANDLE (pWins, NBT_VERIFY_WINS_ACTIVE)))
{
//
// Validate input/output buffer size
//
Size = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
if (Size >= sizeof(tWINS_SET_INFO))
{
status = WinsSetInformation (pWins, pWinsSetInfo);
}
else
{
IF_DBG(NBT_DEBUG_WINS)
KdPrint(("Nbt.DispatchIoctls[IOCTL_NETBT_WINS_SET_INFO]: Input buffer too small\n"));
status = STATUS_INVALID_PARAMETER;
}
}
else
{
status = STATUS_INVALID_HANDLE;
}
break;
}
//
// The following Ioctl is used by the Remote boot code
//
case IOCTL_NETBT_ADD_TO_REMOTE_TABLE:
{
tREMOTE_CACHE *pRemoteEntry = (tREMOTE_CACHE *) pIrp->AssociatedIrp.SystemBuffer;
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation (pIrp);
//
// Validate input/output buffer size
//
Size = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
if (Size >= sizeof(tREMOTE_CACHE)) {
//
// We need only the name, IpAddress, name_flags, and Ttl fields
//
status = NbtAddEntryToRemoteHashTable (pDeviceContext,
NAME_RESOLVED_BY_CLIENT,
pRemoteEntry->name,
pRemoteEntry->IpAddress,
pRemoteEntry->Ttl,
pRemoteEntry->name_flags);
}
else
{
IF_DBG(NBT_DEBUG_PNP_POWER)
KdPrint(("Nbt.DispatchIoctls[IOCTL_NETBT_ADD_TO_REMOTE_TABLE]: Input buffer too small for struct\n"));
status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
//
// The following Ioctl is used by DsGetDcName
//
case IOCTL_NETBT_REMOVE_FROM_REMOTE_TABLE:
{
tNAME *pRemoteName = (tNAME *) pIrp->AssociatedIrp.SystemBuffer;
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation (pIrp);
//
// Validate input/output buffer size
//
Size = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
if (Size >= sizeof(tNAME))
{
//
// We need only the name
//
status = NbtFlushEntryFromRemoteHashTable (pRemoteName);
}
else
{
IF_DBG(NBT_DEBUG_PNP_POWER)
KdPrint(("Nbt.DispatchIoctls[IOCTL_NETBT_REMOVE_FROM_REMOTE_TABLE]: Input buffer too small\n"));
status = STATUS_INVALID_PARAMETER;
}
break;
}
//
// The following Ioctl is used by the Rdr/Srv to add/remove addresses from the SmbDevice
//
case IOCTL_NETBT_SET_SMBDEVICE_BIND_INFO:
{
ASSERT (NULL == pNbtSmbDevice || pDeviceContext == pNbtSmbDevice);
status = NbtSetSmbBindingInfo (pDeviceContext, pIrp, pIrpSp);
break;
}
case IOCTL_NETBT_ENABLE_DISABLE_NETBIOS_SMB:
//
// Private API for the new smb driver (smb.sys)
//
status = NbtEnableDisableNetbiosSmb(pDeviceContext, pIrp, pIrpSp);
break;
default:
{
status = STATUS_INVALID_PARAMETER;
break;
}
} // switch
NbtTrace(NBT_TRACE_IOCTL, ("pDeviceContext %p: pIrp %p %!status!",
pDeviceContext,
pIrp,
status
));
//
// copy the reponse to the client's Mdl
//
if (!NT_ERROR(status) && // allow buffer overflow to pass by
((ControlCode == IOCTL_NETBT_GET_REMOTE_NAMES) ||
(ControlCode == IOCTL_NETBT_GET_BCAST_NAMES) ||
(ControlCode == IOCTL_NETBT_GET_CONNECTIONS)) )
{
status = TdiCopyBufferToMdl (pBuffer, 0, Size, pIrp->MdlAddress, 0,
(PULONG) &pIrp->IoStatus.Information);
CTEMemFree((PVOID)pBuffer);
}
//
// either Success or an Error
// so complete the irp
//
NTIoComplete(pIrp,status,0);
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
GetIpAddrs(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine performs the IOCTL_GET_IP_ADDRS function.
It is in its own routine because it is non paged.
Arguments:
pDeviceContext -
pIrp -
Return Value:
NTSTATUS -
--*/
{
ULONG Length;
PULONG pIpAddr;
PLIST_ENTRY pEntry,pHead;
tDEVICECONTEXT *pDevContext;
KIRQL OldIrq;
tIPADDRESS IpAddr;
//
// return this devicecontext's ip address and all the other
// ip addrs after it.
//
if (!pIrp->MdlAddress)
{
return STATUS_INVALID_PARAMETER;
}
else if ((Length = MmGetMdlByteCount (pIrp->MdlAddress)) < sizeof(ULONG))
{
return STATUS_BUFFER_TOO_SMALL;
}
else if (!(pIpAddr = (PULONG )MmGetSystemAddressForMdlSafe (pIrp->MdlAddress, NormalPagePriority)))
{
return STATUS_UNSUCCESSFUL;
}
//
// Put this adapter first in the list
// Don't include the smb device, its address is uninteresting
if (!IsDeviceNetbiosless (pDeviceContext))
{
*((tIPADDRESS UNALIGNED *) pIpAddr) = pDeviceContext->AssignedIpAddress;
pIpAddr++;
Length -= sizeof(ULONG);
}
// Return the others
CTESpinLock(&NbtConfig.JointLock,OldIrq);
pEntry = pHead = &NbtConfig.DeviceContexts;
while ((pEntry = pEntry->Flink) != pHead)
{
if (Length < sizeof(ULONG))
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
return STATUS_BUFFER_OVERFLOW;
}
pDevContext = CONTAINING_RECORD(pEntry, tDEVICECONTEXT, Linkage);
if ((pDevContext != pDeviceContext) &&
(pDevContext->AssignedIpAddress))
{
*((tIPADDRESS UNALIGNED *) pIpAddr) = pDevContext->AssignedIpAddress;
pIpAddr++;
Length -= sizeof(ULONG);
}
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
//
// put a -1 address on the end
//
if (Length < sizeof(ULONG))
{
return STATUS_BUFFER_OVERFLOW;
}
IpAddr = -1;
*((tIPADDRESS UNALIGNED *) pIpAddr) = IpAddr;
return STATUS_SUCCESS;
} // GetIpAddrs
//----------------------------------------------------------------------------
NTSTATUS
NTReceive(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This Routine handles Queuing a receive buffer on a connection or passing
the recieve buffer to the transport if there is outstanding data waiting
to be received on the connection.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
NTSTATUS status=STATUS_UNSUCCESSFUL;
PIO_STACK_LOCATION pIrpSp;
tCONNECTELE *pConnEle;
KIRQL OldIrq, OldIrq1;
ULONG ToCopy;
ULONG ClientRcvLen;
tLOWERCONNECTION *pLowerConn;
ULONG RemainingPdu;
PTDI_REQUEST_KERNEL_RECEIVE pParams;
PTDI_REQUEST_KERNEL_RECEIVE pClientParams;
ULONG BytesCopied;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pConnEle = pIrpSp->FileObject->FsContext;
if ((pConnEle) &&
(pConnEle->state == NBT_SESSION_UP))
{
CTESpinLock(pConnEle,OldIrq1);
PUSH_LOCATION(0x30);
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
CTESpinFree(pConnEle,OldIrq1);
ASSERTMSG ("Nbt.NTReceive: ERROR - Invalid Connection Handle\n", 0);
status = STATUS_INVALID_HANDLE;
}
else if (pLowerConn = pConnEle->pLowerConnId)
{
status = STATUS_SUCCESS;
}
else
{
CTESpinFree(pConnEle,OldIrq1);
status = STATUS_REMOTE_DISCONNECT;
}
}
if (STATUS_SUCCESS != status)
{
PUSH_LOCATION(0x47);
//
// session in wrong state so reject the buffer posting
// complete the irp, since there must have been some sort of error
// to get to here
//
NTIoComplete(pIrp, status, 0);
return(status);
}
PUSH_LOCATION(0x31);
//
// We are already holding the ConnEle lock
CTESpinLock(pLowerConn,OldIrq);
if (pLowerConn->StateRcv != PARTIAL_RCV)
{
// **** Fast Path Code ****
//
// Queue this receive buffer on to the Rcv Head
//
InsertTailList(&pConnEle->RcvHead, &pIrp->Tail.Overlay.ListEntry);
status = NTCheckSetCancelRoutine(pIrp,(PVOID)NbtCancelReceive,pDeviceContext);
if (!NT_SUCCESS(status))
{
RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
CTESpinFree(pLowerConn,OldIrq);
CTESpinFree(pConnEle,OldIrq1);
NTIoComplete(pIrp,status,0);
return(status);
}
else
{
//
// if the irp is not cancelled, returning pending
//
CTESpinFree(pLowerConn,OldIrq);
CTESpinFree(pConnEle,OldIrq1);
return(STATUS_PENDING);
}
}
else
{
// ***** Partial Rcv - Data Still in Transport *****
BOOLEAN ZeroLengthSend;
PUSH_LOCATION(0x32);
IF_DBG(NBT_DEBUG_RCV)
KdPrint(("Nbt.NTReceive: A Rcv Buffer posted data in Xport,InXport= %X,InIndic %X RcvIndicated %X\n",
pConnEle->BytesInXport,pLowerConn->BytesInIndicate,
pConnEle->ReceiveIndicated));
// get the MDL chain length
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters;
// Reset the Irp pending flag
pIrpSp->Control &= ~SL_PENDING_RETURNED;
// fill in the next irp stack location with our completion routine.
pIrpSp = IoGetNextIrpStackLocation(pIrp);
pIrpSp->CompletionRoutine = CompletionRcv;
pIrpSp->Context = (PVOID)pConnEle->pLowerConnId;
pIrpSp->Flags = 0;
// set flags so the completion routine is always invoked.
pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL;
pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
pIrpSp->MinorFunction = TDI_RECEIVE;
pIrpSp->DeviceObject = IoGetRelatedDeviceObject(pConnEle->pLowerConnId->pFileObject);
pIrpSp->FileObject = pConnEle->pLowerConnId->pFileObject;
pParams = (PTDI_REQUEST_KERNEL_RECEIVE)&pIrpSp->Parameters;
pParams->ReceiveFlags = pClientParams->ReceiveFlags;
// Since this irp is going to traverse through CompletionRcv, we
// need to set the following, since it undoes this stuff.
// This also prevents the LowerConn from being blown away before
// the irp has returned from the transport
//
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_RCV_HANDLER);
//
// pass the receive buffer directly to the transport, decrementing
// the number of receive bytes that have been indicated
//
ASSERT(pConnEle->TotalPcktLen >= pConnEle->BytesRcvd);
if (pClientParams->ReceiveLength > (pConnEle->TotalPcktLen - pConnEle->BytesRcvd))
{
pParams->ReceiveLength = pConnEle->TotalPcktLen - pConnEle->BytesRcvd;
}
else
{
pParams->ReceiveLength = pClientParams->ReceiveLength;
}
ClientRcvLen = pParams->ReceiveLength;
//
// Set the amount of data that we will receive so when the
// irp completes in completionRcv, we can fill in that
// info in the Irp
//
pConnEle->CurrentRcvLen = ClientRcvLen;
// if a zero length send occurs, then ReceiveIndicated is set
// to zero with the state set to RcvPartial. Or, the client may
// pass down an Irp with no MDL in it!!
//
if ((pConnEle->ReceiveIndicated == 0) || !pIrp->MdlAddress)
{
ZeroLengthSend = TRUE;
}
else
{
ZeroLengthSend = FALSE;
}
// calculate how many bytes are still remaining for the client.
if (pConnEle->ReceiveIndicated > ClientRcvLen)
{
PUSH_LOCATION(0x40);
pConnEle->ReceiveIndicated -= ClientRcvLen;
}
else
{
pConnEle->ReceiveIndicated = 0;
}
if (pLowerConn->BytesInIndicate || ZeroLengthSend)
{
PMDL Mdl;
PUSH_LOCATION(0x33);
if (ClientRcvLen > pLowerConn->BytesInIndicate)
{
ToCopy = pLowerConn->BytesInIndicate;
}
else
{
PUSH_LOCATION(0x41);
ToCopy = ClientRcvLen;
}
// copy data from the indicate buffer to the client's buffer,
// remembering that there is a session header in the indicate
// buffer at the start of it... so skip that. The
// client can pass down a null Mdl address for a zero length
// rcv so check for that.
//
if (Mdl = pIrp->MdlAddress)
{
TdiCopyBufferToMdl(MmGetMdlVirtualAddress(pLowerConn->pIndicateMdl),
0, // src offset
ToCopy,
Mdl,
0, // dest offset
&BytesCopied);
}
else
{
BytesCopied = 0;
}
// client's MDL is too short...
if (BytesCopied != ToCopy)
{
PUSH_LOCATION(0x42);
IF_DBG(NBT_DEBUG_INDICATEBUFF)
KdPrint(("Nbt:Receive Buffer too short for Indicate buff BytesCopied %X, ToCopy %X\n",
BytesCopied, ToCopy));
// ToCopy = BytesCopied;
// so the irp will be completed, below
ClientRcvLen = BytesCopied;
}
pLowerConn->BytesInIndicate -= (USHORT)BytesCopied;
// this case is only if the irp is full and should be returned
// now.
if (BytesCopied == ClientRcvLen)
{
PUSH_LOCATION(0x34);
// check if the indicate buffer is empty now. If not, then
// move the data forward to the start of the buffer.
//
if (pLowerConn->BytesInIndicate)
{
PUSH_LOCATION(0x43);
CopyToStartofIndicate(pLowerConn,BytesCopied);
}
//
// the irp is full so complete it
//
// the client MDL is full, so complete his irp
// CompletionRcv increments the number of bytes rcvd
// for this session pdu (pConnEle->BytesRcvd).
pIrp->IoStatus.Information = BytesCopied;
pIrp->IoStatus.Status = STATUS_SUCCESS;
// since we are completing it and TdiRcvHandler did not set the next
// one.
//
ASSERT(pIrp->CurrentLocation > 1);
IoSetNextIrpStackLocation(pIrp);
// we need to track how much of the client's MDL has filled
// up to know when to return it. CompletionRcv subtracts
// from this value as it receives bytes.
pConnEle->FreeBytesInMdl = ClientRcvLen;
pConnEle->CurrentRcvLen = ClientRcvLen;
CTESpinFree(pLowerConn,OldIrq);
CTESpinFree(pConnEle,OldIrq1);
IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT);
return(STATUS_SUCCESS);
}
else
{
//
// clear the number of bytes in the indicate buffer since the client
// has taken more than the data left in the Indicate buffer
//
pLowerConn->BytesInIndicate = 0;
// decrement the client rcv len by the amount already put into the
// client Mdl
//
ClientRcvLen -= BytesCopied;
IF_DBG(NBT_DEBUG_RCV)
KdPrint(("Nbt: Pass Client Irp to Xport BytesinXport %X, ClientRcvLen %X\n",
pConnEle->BytesInXport,ClientRcvLen));
//
// Set the amount left inthe transport after this irp
// completes
if (pConnEle->BytesInXport < ClientRcvLen )
{
pConnEle->BytesInXport = 0;
}
else
{
pConnEle->BytesInXport -= ClientRcvLen;
}
// Adjust the number of bytes in the Mdl chain so far since the
// completion routine will only count the bytes filled in by the
// transport
pConnEle->BytesRcvd += BytesCopied;
// the client is going to take more data from the transport with
// this Irp. Set the new Rcv Length that accounts for the data just
// copied to the Irp.
//
pParams->ReceiveLength = ClientRcvLen;
IF_DBG(NBT_DEBUG_RCV)
KdPrint(("Nbt:ClientRcvLen = %X, LeftinXport= %X BytesCopied= %X %X\n",ClientRcvLen,
pConnEle->BytesInXport,BytesCopied,pLowerConn));
// set the state to this so we can undo the MDL footwork
// in completion rcv - since we have made a partial MDL and
// put that at the start of the chain.
//
SET_STATERCV_LOWER(pLowerConn, FILL_IRP, FillIrp);
// Note that the Irp Mdl address changes below
// when MakePartialMdl is called so this line cannot
// be moved to the common code below!!
pLowerConn->pMdl = pIrp->MdlAddress;
// setup the next MDL so we can create a partial mdl correctly
// in TdiReceiveHandler
//
pConnEle->pNextMdl = pIrp->MdlAddress;
// Build a partial Mdl to represent the client's Mdl chain since
// we have copied data to it, and the transport must copy
// more data to it after that data.
//
// Force the system to map and lock the user buffer
MmGetSystemAddressForMdlSafe (pIrp->MdlAddress, HighPagePriority);
MakePartialMdl(pConnEle,pIrp,BytesCopied);
// pass the Irp to the transport
//
//
IF_DBG(NBT_DEBUG_RCV)
KdPrint(("Nbt:Calling IoCallDriver\n"));
ASSERT(pIrp->CurrentLocation > 1);
}
}
else
{
PUSH_LOCATION(0x36);
IF_DBG(NBT_DEBUG_RCV)
KdPrint(("Nbt.NTReceive: Pass Irp To Xport Bytes in Xport %X, ClientRcvLen %X, RcvIndicated %X\n",
pConnEle->BytesInXport,ClientRcvLen,pConnEle->ReceiveIndicated));
//
// there are no bytes in the indicate buffer, so just pass the
// irp on down to the transport
//
//
// Decide the next state depending on whether the transport currently
// has enough data for this irp
//
if (pConnEle->BytesInXport < ClientRcvLen)
{
PUSH_LOCATION(0x37);
pConnEle->BytesInXport = 0;
//
// to get to here, the implication is that ReceiveIndicated
// equals zero too!! Since ReceiveInd cannot be more than
// BytesInXport, so we can change the state to fill irp without
// worrying about overwriting PartialRcv
//
SET_STATERCV_LOWER(pLowerConn, FILL_IRP, FillIrp);
// setup the next MDL so we can create a partial mdl correctly
// in TdiReceiveHandler
//
pConnEle->pNextMdl = pIrp->MdlAddress;
}
else
{
PUSH_LOCATION(0x38);
pConnEle->BytesInXport -= ClientRcvLen;
// set the state to this so we know what to do in completion rcv
//
if (pConnEle->ReceiveIndicated == 0)
{
PUSH_LOCATION(0x39);
SET_STATERCV_LOWER(pLowerConn, NORMAL, Normal);
}
}
//
// save the Irp so we can reconstruct things later
//
pLowerConn->pMdl = pIrp->MdlAddress;
}
// *** Common Code to passing irp to transport - when there is
// data in the indicate buffer and when there isn't
// keep track of data in MDL so we know when it is full
// and we need to return it to the user
//
pConnEle->FreeBytesInMdl = pParams->ReceiveLength;
// Force the system to map and lock the user buffer
MmGetSystemAddressForMdlSafe (pIrp->MdlAddress, HighPagePriority);
//
// Null the Irp since we are passing it to the transport.
//
pConnEle->pIrpRcv = NULL;
CTESpinFree(pLowerConn,OldIrq);
CTESpinFree(pConnEle,OldIrq1);
CHECK_COMPLETION(pIrp);
status = IoCallDriver(IoGetRelatedDeviceObject(pLowerConn->pFileObject),pIrp);
IF_DBG(NBT_DEBUG_RCV)
KdPrint(("Nbt.NTReceive: Returning=<%x>, IoStatus.Status=<%x>, IoStatus.Information=<%x>\n",
status, pIrp->IoStatus.Status, pIrp->IoStatus.Information));
}
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTReceiveDatagram(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles receiving a datagram by passing the datagram rcv
buffer to the non-OS specific code.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION pIrpSp;
PTDI_REQUEST_KERNEL_RECEIVEDG pTdiRequest;
TDI_REQUEST Request;
ULONG ReceivedLength;
tCLIENTELE *pClientEle;
IF_DBG(NBT_DEBUG_RCV)
KdPrint(("Nbt: Got a Receive datagram that NBT was NOT \n"));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pClientEle, NBT_VERIFY_CLIENT, NBT_VERIFY_CLIENT_DOWN))
{
ASSERTMSG ("Nbt.NTReceiveDatagram: ERROR - Invalid Address Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
// get the sending information out of the irp
pTdiRequest = (PTDI_REQUEST_KERNEL_RECEIVEDG)&pIrpSp->Parameters;
Request.Handle.AddressHandle = pClientEle;
status = NbtReceiveDatagram(
&Request,
pTdiRequest->ReceiveDatagramInformation,
pTdiRequest->ReturnDatagramInformation,
pTdiRequest->ReceiveLength,
&ReceivedLength,
(PVOID)pIrp->MdlAddress, // user data
(tDEVICECONTEXT *)pDeviceContext,
pIrp);
if (status != STATUS_PENDING)
{
NTIoComplete(pIrp,status,ReceivedLength);
}
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTSend(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles sending session pdus across a connection. It is
all OS specific code.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status;
PTDI_REQUEST_KERNEL_SEND pTdiRequest;
PMDL pMdl;
PSINGLE_LIST_ENTRY pSingleListEntry;
tSESSIONHDR *pSessionHdr;
tCONNECTELE *pConnEle;
KIRQL OldIrq;
KIRQL OldIrq1;
PTDI_REQUEST_KERNEL_SEND pParams;
PFILE_OBJECT pFileObject;
tLOWERCONNECTION *pLowerConn;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
//
// This function could be called directly also while bypassing the
// Io subsystem, so we need to recheck the DeviceContext here
//
if (!NBT_REFERENCE_DEVICE (pDeviceContext, REF_DEV_DISPATCH, FALSE))
{
// IF_DBG(NBT_DEBUG_SEND)
KdPrint(("Nbt.NTSend: Invalid Device=<%x>\n", pDeviceContext));
status = STATUS_INVALID_DEVICE_STATE;
goto ErrorInvalidDevice;
}
// get the sending information out of the irp
pTdiRequest = (PTDI_REQUEST_KERNEL_SEND)&pIrpSp->Parameters;
pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
ASSERTMSG ("Nbt.NTSend: ERROR - Invalid Connection Handle\n", 0);
status = STATUS_INVALID_HANDLE;
goto ErrorExit; // Irp has to be completed in this routine! Bug# 202340
}
CTESpinLock(pConnEle,OldIrq);
if (!(pLowerConn = pConnEle->pLowerConnId))
{
CTESpinFree(pConnEle,OldIrq);
IF_DBG(NBT_DEBUG_SEND)
KdPrint(("Nbt.NTSend: attempting send when LowerConn has been freed!\n"));
status = STATUS_INVALID_HANDLE;
goto ErrorExit; // to save on indent levels use a goto here
}
//
// make sure lowerconn stays valid until the irp is done
//
CTESpinLock(pLowerConn,OldIrq1);
NBT_REFERENCE_LOWERCONN (pLowerConn, REF_LOWC_SEND);
CTESpinFree(pLowerConn,OldIrq1);
// check the state of the connection
if (pConnEle->state == NBT_SESSION_UP)
{
//
// send the data on downward to tcp
// allocate an MDL to allow us to put the session hdr in first and then
// put the users buffer on after that, chained to the session hdr MDL.
//
#if BACK_FILL
{
PMDL SmbMdl;
SmbMdl = (PMDL)pIrp->MdlAddress;
// Check if network header type is set
// if yes, then we can back fill the nbt session header
if ((SmbMdl) && (SmbMdl->MdlFlags & MDL_NETWORK_HEADER))
{
pSessionHdr = (tSESSIONHDR *)((ULONG_PTR)SmbMdl->MappedSystemVa - SESSION_HDR_SIZE);
(ULONG_PTR)SmbMdl->MappedSystemVa -= SESSION_HDR_SIZE;
SmbMdl->ByteOffset -= SESSION_HDR_SIZE;
SmbMdl->ByteCount+= SESSION_HDR_SIZE;
pSessionHdr->UlongLength = htonl(pTdiRequest->SendLength);
IF_DBG(NBT_DEBUG_SEND)
KdPrint(("Nbt: Backfilled mdl %x %x\n", pSessionHdr, SmbMdl));
}
else
{
CTESpinLockAtDpc(&NbtConfig);
if (NbtConfig.SessionMdlFreeSingleList.Next)
{
pSingleListEntry = PopEntryList(&NbtConfig.SessionMdlFreeSingleList);
pMdl = CONTAINING_RECORD(pSingleListEntry,MDL,Next);
ASSERT ( MmGetMdlByteCount ( pMdl ) == sizeof ( tSESSIONHDR ) );
}
else
{
NbtGetMdl(&pMdl,eNBT_FREE_SESSION_MDLS);
if (!pMdl)
{
IF_DBG(NBT_DEBUG_SEND)
KdPrint(("Nbt:Unable to get an MDL for a session send!\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
CTESpinFreeAtDpc(&NbtConfig);
CTESpinFree(pConnEle,OldIrq);
// to save on indent levels use a goto here
goto ErrorExit1;
}
}
CTESpinFreeAtDpc(&NbtConfig);
// get the session hdr address out of the MDL
pSessionHdr = (tSESSIONHDR *)MmGetMdlVirtualAddress(pMdl);
// the type of PDU is always a session message, since the session
// request is sent when the client issues a "connect" rather than a send
//
pSessionHdr->UlongLength = htonl(pTdiRequest->SendLength);
// get the device object and file object for the TCP transport underneath
// link the user buffer on the end of the session header Mdl on the Irp
//
pMdl->Next = pIrp->MdlAddress;
pIrp->MdlAddress = pMdl;
}
}
#else
CTESpinLockAtDpc(&NbtConfig);
if (NbtConfig.SessionMdlFreeSingleList.Next)
{
pSingleListEntry = PopEntryList(&NbtConfig.SessionMdlFreeSingleList);
pMdl = CONTAINING_RECORD(pSingleListEntry,MDL,Next);
ASSERT ( MmGetMdlByteCount ( pMdl ) == sizeof ( tSESSIONHDR ) );
}
else
{
NbtGetMdl(&pMdl,eNBT_FREE_SESSION_MDLS);
if (!pMdl)
{
IF_DBG(NBT_DEBUG_SEND)
KdPrint(("Nbt:Unable to get an MDL for a session send!\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
CTESpinFreeAtDpc(&NbtConfig);
CTESpinFree(pConnEle,OldIrq);
// to save on indent levels use a goto here
goto ErrorExit1;
}
}
CTESpinFreeAtDpc(&NbtConfig);
// get the session hdr address out of the MDL
pSessionHdr = (tSESSIONHDR *)MmGetMdlVirtualAddress(pMdl);
// the type of PDU is always a session message, since the session
// request is sent when the client issues a "connect" rather than a send
//
pSessionHdr->UlongLength = htonl(pTdiRequest->SendLength);
// get the device object and file object for the TCP transport underneath
// link the user buffer on the end of the session header Mdl on the Irp
//
pMdl->Next = pIrp->MdlAddress;
pIrp->MdlAddress = pMdl;
#endif //BACK_FILL
pIrpSp = IoGetNextIrpStackLocation(pIrp);
pParams = (PTDI_REQUEST_KERNEL_SEND)&pIrpSp->Parameters;
pParams->SendFlags = pTdiRequest->SendFlags;
pParams->SendLength = pTdiRequest->SendLength + sizeof(tSESSIONHDR);
pIrpSp->CompletionRoutine = SendCompletion;
pIrpSp->Context = (PVOID)pLowerConn;
pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL;
pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
pIrpSp->MinorFunction = TDI_SEND;
pFileObject = pLowerConn->pFileObject;
pLowerConn->BytesSent += pTdiRequest->SendLength;
pIrpSp->FileObject = pFileObject;
pIrpSp->DeviceObject = IoGetRelatedDeviceObject(pFileObject);
CTESpinFree(pConnEle,OldIrq);
CHECK_COMPLETION(pIrp);
#if FAST_DISP
//if we are all set to do fast path, do so now.
if (pLowerConn->FastSend)
{
IoSetNextIrpStackLocation(pIrp);
IF_DBG(NBT_DEBUG_SEND)
KdPrint(("Nbt.NTSend: Fasttdi path %x %x \n", pIrp, pIrpSp));
status = pLowerConn->FastSend (pIrp, pIrpSp);
}
else
{
status = IoCallDriver (IoGetRelatedDeviceObject (pFileObject), pIrp);
}
#else
status = IoCallDriver(IoGetRelatedDeviceObject(pFileObject),pIrp);
#endif
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_DISPATCH, FALSE);
return(status);
} //correct state
CTESpinFree(pConnEle,OldIrq);
IF_DBG(NBT_DEBUG_SEND)
KdPrint(("Nbt:Invalid state for connection on an attempted send, %X\n",
pConnEle));
status = STATUS_INVALID_HANDLE;
ErrorExit1:
//
// Dereference pLowerConn->RefCount, referenced above.
//
NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_SEND, FALSE);
ErrorExit:
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_DISPATCH, FALSE);
ErrorInvalidDevice:
//
// Reset the Irp pending flag
//
pIrpSp->Control &= ~SL_PENDING_RETURNED;
//
// complete the irp, since there must have been some sort of error
// to get to here
//
NTIoComplete (pIrp, status, 0);
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
SendCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This routine handles the completion event when the send completes with
the underlying transport. It must put the session hdr buffer back in
the correct free list and free the active q entry and put it back on
its free list.
Arguments:
DeviceObject - unused.
Irp - Supplies Irp that the transport has finished processing.
Context - Supplies the pConnectEle - the connection data structure
Return Value:
The final status from the operation (success or an exception).
--*/
{
PMDL pMdl;
tLOWERCONNECTION *pLowerConn;
//
// Do some checking to keep the Io system happy - propagate the pending
// bit up the irp stack frame.... if it was set by the driver below then
// it must be set by me
//
if (Irp->PendingReturned)
{
IoMarkIrpPending(Irp);
}
// put the MDL we back on its free list and put the clients mdl back on the Irp
// as it was before the send
pMdl = Irp->MdlAddress;
#if BACK_FILL
// If the header is back filled
// we should adjust the pointers back to where it was.
if (pMdl->MdlFlags & MDL_NETWORK_HEADER)
{
(ULONG_PTR)pMdl->MappedSystemVa += SESSION_HDR_SIZE;
pMdl->ByteOffset += SESSION_HDR_SIZE;
pMdl->ByteCount -= SESSION_HDR_SIZE;
IF_DBG(NBT_DEBUG_SEND)
KdPrint(("Nbt: Done with Backfilled mdl %x\n", pMdl));
}
else
{
Irp->MdlAddress = pMdl->Next;
ASSERT ( MmGetMdlByteCount ( pMdl ) == SESSION_HDR_SIZE );
#if DBG
IF_DBG(NBT_DEBUG_SEND)
{
PMDL pMdl1;
ULONG ulen1,ulen2,ulen3;
UCHAR uc;
tSESSIONHDR *pSessionHdr;
PSINGLE_LIST_ENTRY pSingleListEntry;
KIRQL OldIrq;
pSessionHdr = (tSESSIONHDR *)MmGetMdlVirtualAddress(pMdl);
ulen1 = htonl ( pSessionHdr->UlongLength );
for ( ulen2 = 0 , pMdl1 = pMdl ; ( pMdl1 = pMdl1->Next ) != NULL ; )
{
ulen3 = MmGetMdlByteCount ( pMdl1 );
ASSERT ( ulen3 > 0 );
uc = ( ( UCHAR * ) MmGetMdlVirtualAddress ( pMdl1 ) ) [ ulen3 - 1 ];
ulen2 += ulen3;
}
ASSERT ( ulen2 == ulen1 );
CTESpinLock(&NbtConfig,OldIrq);
for ( pSingleListEntry = &NbtConfig.SessionMdlFreeSingleList ;
( pSingleListEntry = pSingleListEntry->Next ) != NULL ;
)
{
pMdl1 = CONTAINING_RECORD(pSingleListEntry,MDL,Next);
ASSERT ( pMdl1 != pMdl );
}
CTESpinFree(&NbtConfig,OldIrq);
}
#endif // DBG
ExInterlockedPushEntryList(&NbtConfig.SessionMdlFreeSingleList,
(PSINGLE_LIST_ENTRY)pMdl,
&NbtConfig.LockInfo.SpinLock);
}
#else
Irp->MdlAddress = pMdl->Next;
ASSERT ( MmGetMdlByteCount ( pMdl ) == sizeof ( tSESSIONHDR ) );
#if DBG
IF_DBG(NBT_DEBUG_SEND)
{
PMDL pMdl1;
ULONG ulen1,ulen2,ulen3;
UCHAR uc;
tSESSIONHDR *pSessionHdr;
PSINGLE_LIST_ENTRY pSingleListEntry;
KIRQL OldIrq;
pSessionHdr = (tSESSIONHDR *)MmGetMdlVirtualAddress(pMdl);
ulen1 = htonl ( pSessionHdr->UlongLength );
for ( ulen2 = 0 , pMdl1 = pMdl ; ( pMdl1 = pMdl1->Next ) != NULL ; )
{
ulen3 = MmGetMdlByteCount ( pMdl1 );
ASSERT ( ulen3 > 0 );
uc = ( ( UCHAR * ) MmGetMdlVirtualAddress ( pMdl1 ) ) [ ulen3 - 1 ];
ulen2 += ulen3;
}
ASSERT ( ulen2 == ulen1 );
CTESpinLock(&NbtConfig,OldIrq);
for ( pSingleListEntry = &NbtConfig.SessionMdlFreeSingleList ;
( pSingleListEntry = pSingleListEntry->Next ) != NULL ;
)
{
pMdl1 = CONTAINING_RECORD(pSingleListEntry,MDL,Next);
ASSERT ( pMdl1 != pMdl );
}
CTESpinFree(&NbtConfig,OldIrq);
}
#endif // DBG
ExInterlockedPushEntryList(&NbtConfig.SessionMdlFreeSingleList,
(PSINGLE_LIST_ENTRY)pMdl,
&NbtConfig.LockInfo.SpinLock);
#endif //BACK_FILL
// fill in the sent size so that it substracts off the session header size
//
if (Irp->IoStatus.Information > sizeof(tSESSIONHDR))
{
Irp->IoStatus.Information -= sizeof(tSESSIONHDR);
}
else
{
// nothing was sent
Irp->IoStatus.Information = 0;
IF_DBG(NBT_DEBUG_SEND)
KdPrint(("Nbt:Zero Send Length for a session send!\n"));
}
//
// we incremented this before the send: deref it now
//
pLowerConn = (tLOWERCONNECTION *)Context;
ASSERT (NBT_VERIFY_HANDLE (pLowerConn, NBT_VERIFY_LOWERCONN));
NBT_DEREFERENCE_LOWERCONN (pLowerConn, REF_LOWC_SEND, FALSE);
return(STATUS_SUCCESS);
UNREFERENCED_PARAMETER( DeviceObject );
}
//----------------------------------------------------------------------------
NTSTATUS
NTSendDatagram(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles sending a datagram down to the transport.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status;
LONG lSentLength;
TDI_REQUEST Request;
PTDI_REQUEST_KERNEL_SENDDG pTdiRequest;
tCLIENTELE *pClientEle;
CTEPagedCode();
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pClientEle, NBT_VERIFY_CLIENT, NBT_VERIFY_CLIENT_DOWN))
{
ASSERTMSG ("Nbt.SendDatagram: ERROR - Invalid Address Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
// CTEVerifyHandle(pClientEle,NBT_VERIFY_CLIENT,tCLIENTELE,&status);
// get the sending information out of the irp
pTdiRequest = (PTDI_REQUEST_KERNEL_SENDDG)&pIrpSp->Parameters;
Request.Handle.AddressHandle = pClientEle;
lSentLength = 0;
//
// Mark IRP pending here
//
IoMarkIrpPending(pIrp);
status = NbtSendDatagram (&Request,
pTdiRequest->SendDatagramInformation,
pTdiRequest->SendLength,
&lSentLength,
(PVOID)pIrp->MdlAddress, // user data
(tDEVICECONTEXT *)pDeviceContext,
pIrp);
if (status != STATUS_PENDING)
{
//
// either Success or an Error
//
NTIoComplete(pIrp,status,lSentLength);
}
//
// To make driver verifier and IO system happy, always return
// STATUS_PENDING for the request marked as PENDING
//
return STATUS_PENDING;
}
//----------------------------------------------------------------------------
NTSTATUS
NTSetInformation(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles sets up event handlers that the client passes in.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
// *TODO*
CTEPagedCode();
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt:************ Got a Set Information that was NOT expected *******\n"));
return(STATUS_SUCCESS);
}
//----------------------------------------------------------------------------
NTSTATUS
NTQueueToWorkerThread(
IN OUT NBT_WORK_ITEM_CONTEXT * pContext,
IN PVOID DelayedWorkerRoutine,
IN tDGRAM_SEND_TRACKING *pTracker,
IN PVOID pClientContext,
IN PVOID ClientCompletion,
IN tDEVICECONTEXT *pDeviceContext,
IN BOOLEAN fJointLockHeld
)
/*++
Routine Description:
This routine simply queues a request on an excutive worker thread
for later execution. Scanning the LmHosts file must be down this way.
Arguments:
pContext - the workitem context. If it is NULL, this function will
allocate one. Otherwise, it will use the pass in context.
pTracker - the tracker block for context
DelayedWorkerRoutine- the routine for the Workerthread to call
pDeviceContext - the device context which is this delayed event
pertains to. This could be NULL (meaning it's an event
pertaining to not any specific device context)
Return Value:
--*/
{
KIRQL OldIrq = 0;
if ((pDeviceContext) &&
(!NBT_REFERENCE_DEVICE(pDeviceContext, REF_DEV_WORKER, fJointLockHeld)))
{
// ASSERT(NULL == pContext);
return STATUS_INVALID_DEVICE_REQUEST;
}
#if 0
//
// Dont bother queuing a workitem if we're running
// in the same context of the work thread and we're
// at PASSIVE_LEVEL
//
if (KeGetCurrentThread() == NbtConfig.pWorkThread &&
KeGetCurrentIrql() == PASSIVE_LEVEL) {
KdPrint(("WorkItem: Fast Path\n"));
(*((PNBT_WORKER_THREAD_ROUTINE)DelayedWorkerRoutine))(
pTracker,
pClientContext,
ClientCompletion,
pDeviceContext
);
if (pDeviceContext) {
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_WORKER, fJointLockHeld);
}
return (STATUS_SUCCESS);
}
#endif
//
// Allocate a context if
// 1. the caller doesn't have one, or
// 2. the context is already in queue
// this is a bug in the caller. In this case,
// we try to recover from the failure.
//
if (NULL == pContext || pContext->bQueued) {
//
// ASSERT out if the caller is giving us a pContext
// which has already been in queue.
//
ASSERT(NULL == pContext);
pContext = (NBT_WORK_ITEM_CONTEXT *)NbtAllocMem(sizeof(NBT_WORK_ITEM_CONTEXT),NBT_TAG2('22'));
if (NULL == pContext) {
if (pDeviceContext) {
NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_WORKER, fJointLockHeld);
}
return (STATUS_INSUFFICIENT_RESOURCES);
}
pContext->bSpecialAlloc = TRUE;
pContext->bQueued = FALSE;
} else {
pContext->bSpecialAlloc = FALSE;
}
//
// From now on, we cannot fail
//
pContext->bQueued = TRUE;
pContext->pTracker = pTracker;
pContext->pClientContext = pClientContext;
pContext->ClientCompletion = ClientCompletion;
pContext->pDeviceContext = pDeviceContext;
pContext->WorkerRoutine = DelayedWorkerRoutine;
InitializeListHead(&pContext->Linkage);
CTESpinLock(&NbtConfig.WorkerQLock,OldIrq);
InsertTailList(&NbtConfig.WorkerQList, &pContext->Linkage);
InterlockedIncrement(&NbtConfig.NumWorkItemQueued);
if (!NbtConfig.Unloading &&
!InterlockedExchange(&NbtConfig.bSystemWorkThreadQueued, TRUE)) {
ExInitializeWorkItem(&NbtConfig.SystemWorkItem, NTExecuteWorker, &NbtConfig.SystemWorkItem);
ExQueueWorkItem(&NbtConfig.SystemWorkItem, CriticalWorkQueue);
}
CTESpinFree(&NbtConfig.WorkerQLock,OldIrq);
return (STATUS_SUCCESS);
}
//----------------------------------------------------------------------------
VOID
NTExecuteWorker(
IN PVOID pUnused
)
/*++
Routine Description:
This routine handles executing delayed requests at non-Dpc level. If
the Device is currently being unloaded, we let the Unload Handler
complete the request.
Arguments:
pUnused - Unused
Return Value:
none
--*/
{
NBT_WORK_ITEM_CONTEXT * pContext = NULL;
PNBT_WORKER_THREAD_ROUTINE pDelayedWorkerRoutine = NULL;
KIRQL OldIrq;
PLIST_ENTRY pEntry = NULL;
tDEVICECONTEXT * pDeviceContext = NULL;
BOOL bSpecialAlloc = FALSE;
CTESpinLock(&NbtConfig.WorkerQLock,OldIrq);
ASSERT(NULL == NbtConfig.pWorkThread);
NbtConfig.pWorkThread = KeGetCurrentThread();
while(!IsListEmpty(&NbtConfig.WorkerQList)) {
pEntry = RemoveHeadList(&NbtConfig.WorkerQList);
InterlockedDecrement(&NbtConfig.NumWorkItemQueued);
CTESpinFree(&NbtConfig.WorkerQLock,OldIrq);
pContext = CONTAINING_RECORD(pEntry, NBT_WORK_ITEM_CONTEXT, Linkage);
pContext->bQueued = FALSE;
pDelayedWorkerRoutine = (PNBT_WORKER_THREAD_ROUTINE)pContext->WorkerRoutine;
//
// Save the value because pContext can be freed by the worker routine
//
pDeviceContext = pContext->pDeviceContext;
bSpecialAlloc = pContext->bSpecialAlloc;
(*pDelayedWorkerRoutine) (pContext->pTracker,
pContext->pClientContext,
pContext->ClientCompletion,
pDeviceContext
);
//
// Use the saved copy after the delayed worker routine is called
//
if (bSpecialAlloc) {
CTEMemFree((PVOID) pContext);
}
pContext = NULL;
if (pDeviceContext) {
NBT_DEREFERENCE_DEVICE(pDeviceContext, REF_DEV_WORKER, FALSE);
}
CTESpinLock(&NbtConfig.WorkerQLock,OldIrq);
}
//
// set the flag to FALSE before releasing the spinlock
//
InterlockedExchange(&NbtConfig.bSystemWorkThreadQueued, FALSE);
NbtConfig.pWorkThread = NULL;
CTESpinFree(&NbtConfig.WorkerQLock,OldIrq);
}
//----------------------------------------------------------------------------
VOID
SecurityDelete(
IN PVOID pContext
)
/*++
Routine Description:
This routine handles deleting a security context at non-dpc level.
Arguments:
Return Value:
none
--*/
{
PSECURITY_CLIENT_CONTEXT pClientSecurity;
pClientSecurity = (PSECURITY_CLIENT_CONTEXT)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext;
SeDeleteClientSecurity(pClientSecurity);
CTEMemFree(pContext);
}
//----------------------------------------------------------------------------
NTSTATUS
NTSendSession(
IN tDGRAM_SEND_TRACKING *pTracker,
IN tLOWERCONNECTION *pLowerConn,
IN PVOID pCompletion
)
/*++
Routine Description:
This Routine handles seting up a DPC to send a session pdu so that the stack
does not get wound up in multiple sends for the keep alive timeout case.
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PKDPC pDpc;
if (pDpc = NbtAllocMem(sizeof(KDPC),NBT_TAG('f')))
{
KeInitializeDpc(pDpc, DpcSendSession, (PVOID)pTracker);
KeInsertQueueDpc(pDpc,(PVOID)pLowerConn,pCompletion);
return (STATUS_SUCCESS);
}
return (STATUS_INSUFFICIENT_RESOURCES);
}
//----------------------------------------------------------------------------
VOID
DpcSendSession(
IN PKDPC pDpc,
IN PVOID Context,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
This routine simply calls TcpSendSession from a Dpc started in
in NTSendSession (above).
Arguments:
Return Value:
--*/
{
CTEMemFree((PVOID)pDpc);
TcpSendSession((tDGRAM_SEND_TRACKING *)Context,
(tLOWERCONNECTION *)SystemArgument1,
(PVOID)SystemArgument2);
}
//----------------------------------------------------------------------------
NTSTATUS
NTSetEventHandler(
IN tDEVICECONTEXT *pDeviceContext,
IN PIRP pIrp)
/*++
Routine Description:
This Routine handles
Arguments:
pIrp - a ptr to an IRP
Return Value:
NTSTATUS - status of the request
--*/
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status;
tCLIENTELE *pClientEle;
PTDI_REQUEST_KERNEL_SET_EVENT pKeSetEvent;
CTEPagedCode();
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pClientEle = pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pClientEle, NBT_VERIFY_CLIENT, NBT_VERIFY_CLIENT_DOWN))
{
ASSERTMSG ("Nbt.NTSetEventHandler: ERROR - Invalid Address Handle\n", 0);
return (STATUS_INVALID_HANDLE);
}
pKeSetEvent = (PTDI_REQUEST_KERNEL_SET_EVENT)&pIrpSp->Parameters;
// call the not NT specific routine to setup the event handler in the
// nbt data structures
status = NbtSetEventHandler(
pClientEle,
pKeSetEvent->EventType,
pKeSetEvent->EventHandler,
pKeSetEvent->EventContext);
return(status);
}
//----------------------------------------------------------------------------
VOID
NTIoComplete(
IN PIRP pIrp,
IN NTSTATUS Status,
IN ULONG SentLength)
/*++
Routine Description:
This Routine handles calling the NT I/O system to complete an I/O.
Arguments:
status - a completion status for the Irp
Return Value:
NTSTATUS - status of the request
--*/
{
KIRQL OldIrq;
#if DBG
if (!NT_SUCCESS(Status))
{
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NTIoComplete: Returning Error status = %X\n",Status));
// ASSERTMSG("Nbt: Error Ret Code In IoComplete",0);
}
#endif
pIrp->IoStatus.Status = Status;
// use -1 as a flag to mean do not adjust the sent length since it is
// already set
if (SentLength != -1)
{
pIrp->IoStatus.Information = SentLength;
}
NbtTrace(NBT_TRACE_IOCTL, ("pIrp %p: %!status! Information %d",
pIrp, Status, (ULONG)(pIrp->IoStatus.Information)));
#if DBG
if ( (Status != STATUS_SUCCESS) &&
(Status != STATUS_PENDING) &&
(Status != STATUS_INVALID_DEVICE_REQUEST) &&
(Status != STATUS_INVALID_PARAMETER) &&
(Status != STATUS_IO_TIMEOUT) &&
(Status != STATUS_BUFFER_OVERFLOW) &&
(Status != STATUS_BUFFER_TOO_SMALL) &&
(Status != STATUS_INVALID_HANDLE) &&
(Status != STATUS_INSUFFICIENT_RESOURCES) &&
(Status != STATUS_CANCELLED) &&
(Status != STATUS_DUPLICATE_NAME) &&
(Status != STATUS_TOO_MANY_NAMES) &&
(Status != STATUS_TOO_MANY_SESSIONS) &&
(Status != STATUS_REMOTE_NOT_LISTENING) &&
(Status != STATUS_BAD_NETWORK_PATH) &&
(Status != STATUS_HOST_UNREACHABLE) &&
(Status != STATUS_CONNECTION_REFUSED) &&
(Status != STATUS_WORKING_SET_QUOTA) &&
(Status != STATUS_REMOTE_DISCONNECT) &&
(Status != STATUS_LOCAL_DISCONNECT) &&
(Status != STATUS_LINK_FAILED) &&
(Status != STATUS_SHARING_VIOLATION) &&
(Status != STATUS_UNSUCCESSFUL) &&
(Status != STATUS_ACCESS_VIOLATION) &&
#ifdef MULTIPLE_WINS
(Status != STATUS_NETWORK_UNREACHABLE) &&
#endif
(Status != STATUS_NONEXISTENT_EA_ENTRY) )
{
KdPrint(("Nbt.NTIoComplete: returning unusual status = %X\n",Status));
}
#endif
// set the Irps cancel routine to null or the system may bugcheck
// with a bug code of CANCEL_STATE_IN_COMPLETED_IRP
//
// refer to IoCancelIrp() ..\ntos\io\iosubs.c
//
IoAcquireCancelSpinLock(&OldIrq);
IoSetCancelRoutine(pIrp,NULL);
IoReleaseCancelSpinLock(OldIrq);
IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT);
}
//----------------------------------------------------------------------------
// ***** ***** Cancel Utilities ***** *****
//----------------------------------------------------------------------------
NTSTATUS
NTGetIrpIfNotCancelled(
IN PIRP pIrp,
IN PIRP *ppIrpInStruct
)
/*++
Routine Description:
This Routine gets the IOCancelSpinLock to coordinate with cancelling
irps It then returns STATUS_SUCCESS. It also nulls the irp in the structure
pointed to by the second parameter - so that the irp cancel routine
will not also be called.
Arguments:
status - a completion status for the Irp
Return Value:
NTSTATUS - status of the request
--*/
{
KIRQL OldIrq;
NTSTATUS status;
IoAcquireCancelSpinLock(&OldIrq);
// this nulls the irp in the datastructure - i.e. pConnEle->pIrp = NULL
*ppIrpInStruct = NULL;
if (!pIrp->Cancel)
{
status = STATUS_SUCCESS;
}
else
{
status = STATUS_UNSUCCESSFUL;
}
IoSetCancelRoutine(pIrp,NULL);
IoReleaseCancelSpinLock(OldIrq);
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NTCheckSetCancelRoutine(
IN PIRP pIrp,
IN PVOID CancelRoutine,
IN tDEVICECONTEXT *pDeviceContext
)
/*++
Routine Description:
This Routine sets the cancel routine for an Irp.
Arguments:
status - a completion status for the Irp
Return Value:
NTSTATUS - status of the request
--*/
{
NTSTATUS status;
//
// Check if the irp was cancelled yet and if not, then set the
// irp cancel routine.
//
IoAcquireCancelSpinLock(&pIrp->CancelIrql);
if (pIrp->Cancel)
{
pIrp->IoStatus.Status = STATUS_CANCELLED;
status = STATUS_CANCELLED;
}
else
{
// setup the cancel routine
IoMarkIrpPending(pIrp);
IoSetCancelRoutine(pIrp,CancelRoutine);
status = STATUS_SUCCESS;
}
IoReleaseCancelSpinLock(pIrp->CancelIrql);
return(status);
}
//----------------------------------------------------------------------------
NTSTATUS
NbtSetCancelRoutine(
IN PIRP pIrp,
IN PVOID CancelRoutine,
IN tDEVICECONTEXT *pDeviceContext
)
/*++
Routine Description:
This Routine sets the cancel routine for an Irp.
Arguments:
status - a completion status for the Irp
Return Value:
NTSTATUS - status of the request
--*/
{
NTSTATUS status;
//
// Check if the irp was cancelled yet and if not, then set the
// irp cancel routine.
//
IoAcquireCancelSpinLock(&pIrp->CancelIrql);
if (pIrp->Cancel)
{
pIrp->IoStatus.Status = STATUS_CANCELLED;
status = STATUS_CANCELLED;
//
// Note the cancel spin lock is released by the Cancel routine
//
(*(PDRIVER_CANCEL)CancelRoutine)((PDEVICE_OBJECT)pDeviceContext,pIrp);
}
else
{
// setup the cancel routine and mark the irp pending
//
IoMarkIrpPending(pIrp);
IoSetCancelRoutine(pIrp,CancelRoutine);
IoReleaseCancelSpinLock(pIrp->CancelIrql);
status = STATUS_SUCCESS;
}
return(status);
}
//----------------------------------------------------------------------------
VOID
NTClearContextCancel(
IN NBT_WORK_ITEM_CONTEXT *pContext
)
/*++
Routine Description:
This Routine sets the cancel routine for
((tDGRAM_SEND_TRACKING *)(pContext->pClientContext))->pClientIrp
to NULL.
NbtConfig.JointLock should be held when this routine is called.
Arguments:
status - a completion status for the Irp
Return Value:
NTSTATUS - status of the request
--*/
{
NTSTATUS status;
status = NbtCancelCancelRoutine( ((tDGRAM_SEND_TRACKING *)(pContext->pClientContext))->pClientIrp );
ASSERT ( status != STATUS_CANCELLED );
}
//----------------------------------------------------------------------------
NTSTATUS
NbtCancelCancelRoutine(
IN PIRP pIrp
)
/*++
Routine Description:
This Routine sets the cancel routine for an Irp to NULL
Arguments:
status - a completion status for the Irp
Return Value:
NTSTATUS - status of the request
--*/
{
NTSTATUS status = STATUS_SUCCESS;
if ( pIrp )
{
//
// Check if the irp was cancelled yet and if not, then set the
// irp cancel routine.
//
IoAcquireCancelSpinLock(&pIrp->CancelIrql);
if (pIrp->Cancel)
{
status = STATUS_CANCELLED;
}
IoSetCancelRoutine(pIrp,NULL);
IoReleaseCancelSpinLock(pIrp->CancelIrql);
}
return(status);
}
//----------------------------------------------------------------------------
// ***** ***** Cancel Routines ***** *****
//----------------------------------------------------------------------------
VOID
NbtCancelListen(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine handles the cancelling a listen Irp. It must release the
cancel spin lock before returning re: IoCancelIrp().
Arguments:
Return Value:
The final status from the operation.
--*/
{
tCONNECTELE *pConnEle;
tCLIENTELE *pClientEle;
KIRQL OldIrq;
PLIST_ENTRY pHead;
PLIST_ENTRY pEntry;
PIO_STACK_LOCATION pIrpSp;
tLISTENREQUESTS *pListenReq;
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt:Got a LISTEN Cancel !!! *****************\n"));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext;
if ((!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN)) ||
(!NBT_VERIFY_HANDLE2 ((pClientEle = pConnEle->pClientEle), NBT_VERIFY_CLIENT,NBT_VERIFY_CLIENT_DOWN)))
{
ASSERTMSG ("Nbt.NbtCancelListen: ERROR - Invalid Connection Handle\n", 0);
IoReleaseCancelSpinLock(pIrp->CancelIrql);
return;
}
IoReleaseCancelSpinLock(pIrp->CancelIrql);
NbtTrace(NBT_TRACE_INBOUND, ("Cancel Listen Irp %p pConnEle %p ClientEle %p", pIrp, pConnEle, pClientEle));
// now search the client's listen queue looking for this connection
//
CTESpinLock(pClientEle,OldIrq);
pHead = &pClientEle->ListenHead;
pEntry = pHead->Flink;
while (pEntry != pHead)
{
pListenReq = CONTAINING_RECORD(pEntry,tLISTENREQUESTS,Linkage);
if ((pListenReq->pConnectEle == pConnEle) &&
(pListenReq->pIrp == pIrp))
{
RemoveEntryList(pEntry);
CTESpinFree(pClientEle,OldIrq);
// complete the irp
pIrp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT);
CTEMemFree((PVOID)pListenReq);
return;
}
pEntry = pEntry->Flink;
}
CTESpinFree(pClientEle,OldIrq);
return;
}
//----------------------------------------------------------------------------
VOID
NbtCancelSession(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine handles the cancelling a connect Irp. It must release the
cancel spin lock before returning re: IoCancelIrp(). It is called when
the session setup pdu has been sent, and the state is still outbound.
The cancel routine is only setup when the timer is started to time
sending the session response pdu.
Arguments:
Return Value:
The final status from the operation.
--*/
{
tCONNECTELE *pConnEle;
KIRQL OldIrq;
PIO_STACK_LOCATION pIrpSp;
BOOLEAN DerefConnEle=FALSE;
tTIMERQENTRY *pTimer;
tDGRAM_SEND_TRACKING *pTracker;
COMPLETIONCLIENT pCompletion;
PVOID pContext;
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NbtCancelSession: Got a Cancel !!! *****************\n"));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext;
IoReleaseCancelSpinLock(pIrp->CancelIrql);
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
ASSERTMSG ("Nbt.NbtCancelSession: ERROR - Invalid Connection Handle\n", 0);
return;
}
NbtTrace(NBT_TRACE_OUTBOUND, ("Cancel TDI_CONNECT pIrp %p ConnEle=%p LowerConn=%p, ClientEle=%p",
pIrp, pConnEle, pConnEle->pLowerConnId, pConnEle->pClientEle));
#ifdef RASAUTODIAL
//
// Cancel the automatic connection if one's
// in progress. If we don't find the
// connection block in the automatic
// connection driver, then it's already
// been completed.
//
if (pConnEle->fAutoConnecting)
{
if (!NbtCancelPostConnect(pIrp))
{
return;
}
}
#endif // RASAUTODIAL
CTESpinLock(&NbtConfig.JointLock,OldIrq);
if ((!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN)) ||
(!(pConnEle->pIrp)) || // the irp could get completed while acquiring the lock
(!(pTracker = (tDGRAM_SEND_TRACKING *)pConnEle->pIrpRcv)))
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: no tracker", pIrp));
return;
}
pTracker->Flags |= TRACKER_CANCELLED;
if (pTimer = pTracker->pTimer) // check for SessionStartupTimeout
{
pTracker->pTimer = NULL;
//
// stop the timer and only continue if the timer was stopped before
// it expired
//
StopTimer(pTimer, &pCompletion, &pContext);
CTESpinFree(&NbtConfig.JointLock,OldIrq);
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p: pCompletion %p", pIrp, pCompletion));
//
// pCompletion will be set if the timer had not expired
// We want to cause a forced timeout, so we will just call the
// timeout routine with STATUS_CANCELLED
//
if (pCompletion)
{
(*pCompletion) (pContext, STATUS_CANCELLED);
}
}
else if (pConnEle->state == NBT_SESSION_OUTBOUND)
{
//
// for some reason there is no timer, but the connection is still
// outbound, so call the timer completion routine to kill off
// the connection.
//
CTESpinFree(&NbtConfig.JointLock,OldIrq);
NbtTrace(NBT_TRACE_OUTBOUND, ("pIrp %p", pIrp));
SessionStartupTimeout (pTracker, ULongToPtr(STATUS_CANCELLED), (PVOID)1);
}
else
{
//
// Free the lock
//
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
return;
}
//----------------------------------------------------------------------------
VOID
NbtCancelConnect(
IN PDEVICE_OBJECT pDeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine handles cancelling an NTConnect Irp - which has been
passed down by a client (e.g. net view). Typically, when the request
succeeds on another adapter, it will issue this cancel.
On receiving the cancel, if we are processing a Local IRP, we just
pass the cancel on to the Local Irp which will complete this Irp also
in its Completion Routine.
Arguments:
Return Value:
The final status from the operation.
--*/
{
PIO_STACK_LOCATION pIrpSp;
IN PIRP pLocalIrp;
tCONNECTELE *pConnEle;
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt:NbtCancelConnect ********** Got an Irp Cancel !!! **********\n"));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pConnEle = pIrpSp->FileObject->FsContext;
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
ASSERTMSG ("Nbt.NbtCancelConnect: ERROR - Invalid Connection Handle\n", 0);
IoReleaseCancelSpinLock(pIrp->CancelIrql);
return;
}
NbtTrace(NBT_TRACE_OUTBOUND, ("Cancel Connect pIrp %p: pConnEle %p", pIrp, pConnEle));
if ((pConnEle) &&
(pLocalIrp = pConnEle->pIrp))
{
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint (("Nbt.NbtCancelConnect: pClientIrp=<%x>, pLocalIrp=<%x>, Device=<%x>, CancelR=<%x>\n",
pIrp, pLocalIrp, pDeviceContext, pLocalIrp->CancelRoutine));
IoReleaseCancelSpinLock(pIrp->CancelIrql);
IoCancelIrp(pLocalIrp);
}
else
{
IoReleaseCancelSpinLock(pIrp->CancelIrql);
}
return;
}
//----------------------------------------------------------------------------
VOID
NbtCancelReceive(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine handles the cancelling a listen Irp. It must release the
cancel spin lock before returning re: IoCancelIrp().
Arguments:
Return Value:
The final status from the operation.
--*/
{
tCONNECTELE *pConnEle;
tLOWERCONNECTION *pLowerConn;
KIRQL OldIrq;
KIRQL OldIrq1;
PLIST_ENTRY pHead;
PLIST_ENTRY pEntry;
PIO_STACK_LOCATION pIrpSp;
PIRP pRcvIrp;
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NbtCancelReceive: Got a Cancel !!! *****************\n"));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext;
IoReleaseCancelSpinLock(pIrp->CancelIrql);
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
ASSERTMSG ("Nbt.NbtCancelReceive: ERROR - Invalid Connection Handle\n", 0);
return;
}
NbtTrace(NBT_TRACE_INBOUND, ("Cancel Receive Irp %p ConnEle=%p LowerConn=%p, ClientEle=%p",
pIrp, pConnEle, pConnEle->pLowerConnId, pConnEle->pClientEle));
CTESpinLock(&NbtConfig.JointLock,OldIrq1);
pLowerConn = pConnEle->pLowerConnId;
if (pLowerConn)
{
CTESpinLock(pLowerConn,OldIrq);
}
if (pConnEle->Verify == NBT_VERIFY_CONNECTION)
{
// now search the connection's receive queue looking for this Irp
//
pHead = &pConnEle->RcvHead;
pEntry = pHead->Flink;
while (pEntry != pHead)
{
pRcvIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry);
if (pRcvIrp == pIrp)
{
RemoveEntryList(pEntry);
// complete the irp
pIrp->IoStatus.Status = STATUS_CANCELLED;
if (pLowerConn)
{
CTESpinFree(pLowerConn,OldIrq);
}
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT);
return;
}
pEntry = pEntry->Flink;
}
}
if (pLowerConn)
{
CTESpinFree(pLowerConn,OldIrq);
}
CTESpinFree(&NbtConfig.JointLock,OldIrq1);
return;
}
//----------------------------------------------------------------------------
VOID
NbtCancelRcvDgram(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine handles the cancelling a listen Irp. It must release the
cancel spin lock before returning re: IoCancelIrp().
Arguments:
Return Value:
The final status from the operation.
--*/
{
tCLIENTELE *pClientEle;
KIRQL OldIrq;
PLIST_ENTRY pHead;
PLIST_ENTRY pEntry;
PIO_STACK_LOCATION pIrpSp;
tRCVELE *pRcvEle;
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NbtCancelRcvDgram: Got a Cancel !!! *****************\n"));
//
// Need to acquire JointLock before Cancel lock!
// Bug#: 124405
//
IoReleaseCancelSpinLock(pIrp->CancelIrql);
CTESpinLock(&NbtConfig.JointLock,OldIrq);
IoAcquireCancelSpinLock(&pIrp->CancelIrql);
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pClientEle = (tCLIENTELE *)pIrpSp->FileObject->FsContext;
NbtTrace(NBT_TRACE_RECVDGRAM, ("Cancel RcvDgram Irp %p ClientEle=%p", pIrp, pClientEle));
if (NBT_VERIFY_HANDLE (pClientEle, NBT_VERIFY_CLIENT))
{
// now search the client's listen queue looking for this connection
//
pHead = &pClientEle->RcvDgramHead;
pEntry = pHead->Flink;
while (pEntry != pHead)
{
pRcvEle = CONTAINING_RECORD(pEntry,tRCVELE,Linkage);
if (pRcvEle->pIrp == pIrp)
{
RemoveEntryList(pEntry);
// complete the irp
pIrp->IoStatus.Status = STATUS_CANCELLED;
IoReleaseCancelSpinLock(pIrp->CancelIrql);
CTESpinFree(&NbtConfig.JointLock,OldIrq);
IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT);
CTEMemFree((PVOID)pRcvEle);
return;
}
pEntry = pEntry->Flink;
}
}
else
{
ASSERTMSG ("Nbt.NbtCancelRcvDgram: ERROR - Invalid Address Handle\n", 0);
}
IoReleaseCancelSpinLock(pIrp->CancelIrql);
CTESpinFree(&NbtConfig.JointLock,OldIrq);
return;
}
//----------------------------------------------------------------------------
VOID
NbtCancelFindName(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine handles the cancelling a FindName Irp - which has
been passed down by a client (e.g. ping). Typically, when ping succeeds
on another adapter, it will issue this cancel.
On receiving the cancel, we stop any timer that is running in connection
with name query and then complete the irp with status_cancelled.
Arguments:
Return Value:
The final status from the operation.
--*/
{
tDGRAM_SEND_TRACKING *pTracker;
PIO_STACK_LOCATION pIrpSp;
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NbtCancelFindName: Got a Cancel !!! *****************\n"));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pTracker = pIrpSp->Parameters.Others.Argument4;
NbtTrace(NBT_TRACE_NAMESRV, ("Cancel FindName Irp %p pTracker=%p", pIrp, pTracker));
//
// We want to ensure that the tracker supplied by FsContext
// is the right Tracker for this Irp
//
if (pTracker && (pIrp == pTracker->pClientIrp))
{
//
// if pClientIrp still valid, completion routine hasn't run yet: go ahead
// and complete the irp here
//
pIrpSp->Parameters.Others.Argument4 = NULL;
pTracker->pClientIrp = NULL;
IoReleaseCancelSpinLock(pIrp->CancelIrql);
NTIoComplete(pIrp,STATUS_CANCELLED,(ULONG)-1);
} else
{
//
// the completion routine has run.
//
IoReleaseCancelSpinLock(pIrp->CancelIrql);
}
return;
}
//----------------------------------------------------------------------------
VOID
NbtCancelLmhSvcIrp(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine handles the cancelling a DNS name query Irp or
the CheckIpAddrs Irp that is passed down to NBT from Lmhsvc
This routine will get the Resource Lock, and Null the Irp ptr in the
DnsQueries or CheckAddr structure (as approp) and then return the irp.
Arguments:
Return Value:
The final status from the operation.
--*/
{
tLMHSVC_REQUESTS *pLmhSvcRequest = NULL;
KIRQL OldIrq;
IoReleaseCancelSpinLock(pIrp->CancelIrql);
CTESpinLock(&NbtConfig.JointLock,OldIrq);
if (pIrp == CheckAddr.QueryIrp)
{
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NbtCancelLmhSvcIrp: Got a Cancel on CheckAddr Irp !!! *****************\n"));
NbtTrace(NBT_TRACE_NAMESRV, ("Cancel LmhSvc CheckAddr Irp %p", pIrp));
pLmhSvcRequest = &CheckAddr;
}
else if (pIrp == DnsQueries.QueryIrp)
{
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NbtCancelLmhSvcIrp: Got a Cancel on DnsQueries Irp !!! *****************\n"));
NbtTrace(NBT_TRACE_NAMESRV, ("Cancel LmhSvc DnsQueries Irp %p", pIrp));
pLmhSvcRequest = &DnsQueries;
}
if (pLmhSvcRequest)
{
pIrp->IoStatus.Status = STATUS_CANCELLED;
pLmhSvcRequest->QueryIrp = NULL;
pLmhSvcRequest->pIpAddrBuf = NULL;
CTESpinFree(&NbtConfig.JointLock,OldIrq);
IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT);
}
else
{
CTESpinFree(&NbtConfig.JointLock,OldIrq);
}
return;
}
//----------------------------------------------------------------------------
VOID
NbtCancelDisconnectWait(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine handles the cancelling a Disconnect Wait Irp - which has
been passed down by a client so that when a disconnect occurs this
irp will complete and inform the client. The action here is to simply
complete the irp with status cancelled.
down to NBT from Lmhsvc, for the purpose of resolving a name with DNS.
Nbt will complete this irp each time it has a name to resolve with DNS.
This routine will get the Resource Lock, and Null the Irp ptr in the
DnsQueries structure and then return the irp.
Arguments:
Return Value:
The final status from the operation.
--*/
{
tCONNECTELE *pConnEle;
PIO_STACK_LOCATION pIrpSp;
CTELockHandle OldIrq;
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt:Got a Disc Wait Irp Cancel !!! *****************\n"));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pConnEle = (tCONNECTELE *)pIrpSp->FileObject->FsContext;
IoReleaseCancelSpinLock(pIrp->CancelIrql);
CTESpinLock(pConnEle,OldIrq);
if (!NBT_VERIFY_HANDLE2 (pConnEle, NBT_VERIFY_CONNECTION, NBT_VERIFY_CONNECTION_DOWN))
{
ASSERTMSG ("Nbt.NbtCancelDisconnectWait: ERROR - Invalid Connection Handle\n", 0);
pIrp->IoStatus.Status = STATUS_INVALID_HANDLE;
}
else if (pConnEle->pIrpClose == pIrp)
{
pConnEle->pIrpClose = NULL;
pIrp->IoStatus.Status = STATUS_CANCELLED;
}
CTESpinFree(pConnEle,OldIrq);
IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT);
return;
}
//----------------------------------------------------------------------------
VOID
NbtCancelWaitForLmhSvcIrp(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine handles the cancelling a Query to DNS, so that the client's
irp can be returned to the client. This cancellation is instigated
by the client (i.e. RDR).
Arguments:
Return Value:
The final status from the operation.
--*/
{
BOOLEAN FoundIt = FALSE;
NBT_WORK_ITEM_CONTEXT *Context;
CTELockHandle OldIrq;
tDGRAM_SEND_TRACKING *pTracker;
PVOID pClientCompletion;
PVOID pClientContext;
PLIST_ENTRY pHead;
PLIST_ENTRY pEntry;
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NbtCancelWaitForLmhSvcIrp: Got a Cancel !!! *****************\n"));
IoReleaseCancelSpinLock(pIrp->CancelIrql);
CTESpinLock(&NbtConfig.JointLock,OldIrq);
//
// First check the lmhost list, then the CheckAddr list, then the Dns list
//
if (!(Context = FindLmhSvcRequest (DeviceContext, pIrp, &LmHostQueries)))
{
if (!(Context = FindLmhSvcRequest (DeviceContext, pIrp, &CheckAddr)))
{
Context = FindLmhSvcRequest (DeviceContext, pIrp, &DnsQueries);
}
}
CTESpinFree(&NbtConfig.JointLock,OldIrq);
NbtTrace(NBT_TRACE_NAMESRV, ("Cancel WaitForLmhsvc Irp %p", pIrp));
//
// Now complete the clients request to return the irp to the client
//
if (Context)
{
//
// this is the name Query tracker
//
pTracker = Context->pTracker;
pClientCompletion = Context->ClientCompletion;
pClientContext = Context->pClientContext;
// for dns names (NameLen>16), pTracker would be NULL
if (pTracker)
{
// name did not resolve, so delete from table
SetNameState (pTracker->pNameAddr, NULL, FALSE);
NBT_DEREFERENCE_TRACKER(pTracker, FALSE);
}
//
// this should complete any name queries that are waiting on
// this first name query - i.e. queries to the resolving name
//
CompleteClientReq(pClientCompletion, pClientContext, STATUS_CANCELLED);
CTEMemFree(Context);
}
}
//----------------------------------------------------------------------------
VOID
NbtCancelDgramSend(
IN PDEVICE_OBJECT DeviceContext,
IN PIRP pIrp
)
/*++
Routine Description:
This routine handles the cancelling of a Datagram Send. The action here is to simply
complete the irp with status cancelled.
This routine will Null the Irp ptr in the Tracker structure (if available) so that
SendDgramContinue does not find it.
Arguments:
Return Value:
The final status from the operation.
--*/
{
tDGRAM_SEND_TRACKING *pTracker;
PIO_STACK_LOCATION pIrpSp;
CTELockHandle OldIrq;
IF_DBG(NBT_DEBUG_NAMESRV)
KdPrint(("Nbt.NbtCancelDgramSend: Got a DatagramSend Irp Cancel !!! *****************\n"));
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pTracker = pIrpSp->Parameters.Others.Argument4;
NbtTrace(NBT_TRACE_SENDDGRAM, ("Cancel SendDgram Irp %p", pIrp));
if ((NBT_VERIFY_HANDLE (pTracker, NBT_VERIFY_TRACKER)) &&
(pTracker->pClientIrp == pIrp))
{
pTracker->pClientIrp = NULL;
pIrpSp->Parameters.Others.Argument4 = NULL;
pIrp->IoStatus.Status = STATUS_CANCELLED;
IoReleaseCancelSpinLock(pIrp->CancelIrql);
NTIoComplete(pIrp,STATUS_CANCELLED,(ULONG)-1);
}
else
{
IoReleaseCancelSpinLock(pIrp->CancelIrql);
}
return;
}
int
check_unicode_string(IN PUNICODE_STRING str)
{
try {
ProbeForRead(str, sizeof (UNICODE_STRING), sizeof(BYTE));
if ((str->MaximumLength <= 0) || (str->Length <= 0) || (str->Length >= str->MaximumLength)) {
return (-1);
}
ProbeForRead(str->Buffer, str->MaximumLength, sizeof(WCHAR));
ASSERT((str->Length&1) == 0);
if (str->Buffer[str->Length/sizeof(WCHAR)]) {
return (-1);
}
return 0;
} except(EXCEPTION_EXECUTE_HANDLER) {
KdPrint (("Nbt.check_unicode_string: Exception <0x%x> trying to access unicode string 0x%x\n",
GetExceptionCode(), str));
return (-1);
}
}