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.
1791 lines
40 KiB
1791 lines
40 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Ipx.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the low level Ipx support routines for the NetWare
|
|
redirector.
|
|
|
|
Author:
|
|
|
|
Colin Watson [ColinW] 28-Dec-1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "Procs.h"
|
|
#include "wsnwlink.h"
|
|
|
|
//
|
|
// Define IPX interfaces that should be in a public header file but aren't
|
|
// (at least for NT 1.0). For Daytona, include isnkrnl.h.
|
|
//
|
|
|
|
#define IPX_ID 'M'<<24 | 'I'<<16 | 'P'<<8 | 'X'
|
|
|
|
#define I_MIPX (('I' << 24) | ('D' << 16) | ('P' << 8))
|
|
#define MIPX_SENDPTYPE I_MIPX | 118 /* Send ptype in options on recv*/
|
|
#define MIPX_RERIPNETNUM I_MIPX | 144 /* ReRip a network */
|
|
#define MIPX_GETNETINFO I_MIPX | 135 /* Get info on a network num */
|
|
#define MIPX_LINECHANGE I_MIPX | 310 /* queued until WAN line goes up/down */
|
|
|
|
#define Dbg (DEBUG_TRACE_IPX)
|
|
|
|
extern BOOLEAN WorkerRunning; // From timer.c
|
|
|
|
extern POBJECT_TYPE *IoFileObjectType;
|
|
|
|
typedef TA_IPX_ADDRESS UNALIGNED *PUTA_IPX_ADDRESS;
|
|
|
|
typedef struct _ADDRESS_INFORMATION {
|
|
ULONG ActivityCount;
|
|
TA_IPX_ADDRESS NetworkName;
|
|
ULONG Unused; // Junk needed to work around streams NWLINK bug.
|
|
} ADDRESS_INFORMATION, *PADDRESS_INFORMATION;
|
|
|
|
//
|
|
// Handle difference between NT1.0 and use of ntifs.h
|
|
//
|
|
#ifdef IFS
|
|
#define ATTACHPROCESS(_X) KeAttachProcess(_X);
|
|
#else
|
|
#define ATTACHPROCESS(_X) KeAttachProcess(&(_X)->Pcb);
|
|
#endif
|
|
|
|
NTSTATUS
|
|
SubmitTdiRequest (
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp
|
|
);
|
|
|
|
NTSTATUS
|
|
CompletionEvent(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
QueryAddressInformation(
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
IN PNW_TDI_STRUCT pTdiStruct,
|
|
OUT PADDRESS_INFORMATION AddressInformation
|
|
);
|
|
|
|
NTSTATUS
|
|
QueryProviderInformation(
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
IN PNW_TDI_STRUCT pTdiStruct,
|
|
OUT PTDI_PROVIDER_INFO ProviderInfo
|
|
);
|
|
|
|
USHORT
|
|
GetSocketNumber(
|
|
IN PIRP_CONTEXT pIrpC,
|
|
IN PNW_TDI_STRUCT pTdiStruc
|
|
);
|
|
|
|
NTSTATUS
|
|
SetTransportOption(
|
|
IN PIRP_CONTEXT pIrpC,
|
|
IN PNW_TDI_STRUCT pTdiStruc,
|
|
IN ULONG Option
|
|
);
|
|
|
|
#ifndef QFE_BUILD
|
|
|
|
NTSTATUS
|
|
CompletionLineChange(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
#endif
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, IPX_Get_Local_Target )
|
|
#pragma alloc_text( PAGE, IPX_Get_Internetwork_Address )
|
|
#pragma alloc_text( PAGE, IPX_Get_Interval_Marker )
|
|
#pragma alloc_text( PAGE, IPX_Open_Socket )
|
|
#pragma alloc_text( PAGE, IPX_Close_Socket )
|
|
#pragma alloc_text( PAGE, IpxOpen )
|
|
#pragma alloc_text( PAGE, IpxOpenHandle )
|
|
#pragma alloc_text( PAGE, BuildIpxAddressEa )
|
|
#pragma alloc_text( PAGE, IpxClose )
|
|
#pragma alloc_text( PAGE, SetEventHandler )
|
|
#pragma alloc_text( PAGE, SubmitTdiRequest )
|
|
#pragma alloc_text( PAGE, GetSocketNumber )
|
|
#pragma alloc_text( PAGE, GetMaximumPacketSize )
|
|
#pragma alloc_text( PAGE, QueryAddressInformation )
|
|
#pragma alloc_text( PAGE, QueryProviderInformation )
|
|
#pragma alloc_text( PAGE, SetTransportOption )
|
|
#pragma alloc_text( PAGE, GetNewRoute )
|
|
#ifndef QFE_BUILD
|
|
#pragma alloc_text( PAGE, SubmitLineChangeRequest )
|
|
#pragma alloc_text( PAGE, FspProcessLineChange )
|
|
#endif
|
|
|
|
#ifndef QFE_BUILD
|
|
#pragma alloc_text( PAGE1, CompletionEvent )
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if 0 // Not pageable
|
|
BuildIpxAddress
|
|
CompletionLineChange
|
|
|
|
// see ifndef QFE_BUILD above
|
|
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
IPX_Get_Local_Target(
|
|
IN IPXaddress* RemoteAddress,
|
|
OUT NodeAddress* LocalTarget,
|
|
OUT word* Ticks
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine the address in the caller's own network to which to transmit
|
|
in order to reach the specified machine.
|
|
|
|
This is not required for NT since the IPX transport handles the
|
|
issue of determining routing between this machine and the remote
|
|
address.
|
|
|
|
Arguments:
|
|
|
|
RemoteAddress - Supplies the remote computers address
|
|
NodeAddress - Where to store the intermediate machine address
|
|
Ticks - Returns the expected number of ticks to reach the remote address
|
|
|
|
Return Value:
|
|
|
|
status of the operation
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(0, Dbg, "IPX_Get_Local_Target\n", 0);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
VOID
|
|
IPX_Get_Internetwork_Address(
|
|
OUT IPXaddress* LocalAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine the callers full address in a set of interconnected networks.
|
|
in order to reach the specified machine.
|
|
|
|
This is not required for NT since the IPX transport handles the
|
|
issue of determining routing between this machine and the remote
|
|
address.
|
|
|
|
Arguments:
|
|
|
|
LocalAddress - Where to store the local address
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(0, Dbg, "IPX_Get_Internetwork_Address\n", 0);
|
|
RtlFillMemory(LocalAddress, sizeof(IPXaddress), 0xff);
|
|
}
|
|
|
|
|
|
word
|
|
IPX_Get_Interval_Marker(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine the interval marker in clock ticks.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
interval marker
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(0, Dbg, "IPX_Get_Interval_Marker\n", 0);
|
|
return 0xff;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPX_Open_Socket(
|
|
IN PIRP_CONTEXT pIrpC,
|
|
IN PNW_TDI_STRUCT pTdiStruc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open a local socket to be used for a conection to a remote server.
|
|
|
|
Arguments:
|
|
|
|
pIrpC - supplies the irp context for the request creating the socket.
|
|
|
|
pTdiStruc - supplies where to record the handle and both device and file
|
|
object pointers
|
|
|
|
Return Value:
|
|
|
|
0 success
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
UCHAR NetworkName[ sizeof( FILE_FULL_EA_INFORMATION )-1 +
|
|
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
|
|
sizeof(TA_IPX_ADDRESS)];
|
|
|
|
static UCHAR LocalNodeAddress[6] = {0,0,0,0,0,0};
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "IPX_Open_Socket %X\n", pTdiStruc->Socket);
|
|
|
|
//
|
|
// Let the transport decide the network number and node address
|
|
// if the caller specified socket 0. This will allow the transport
|
|
// to use whatever local adapters are available to get to the
|
|
// remote server.
|
|
//
|
|
|
|
BuildIpxAddressEa( (ULONG)0,
|
|
LocalNodeAddress,
|
|
(USHORT)pTdiStruc->Socket,
|
|
NetworkName );
|
|
|
|
Status = IpxOpenHandle( &pTdiStruc->Handle,
|
|
&pTdiStruc->pDeviceObject,
|
|
&pTdiStruc->pFileObject,
|
|
NetworkName,
|
|
FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) +
|
|
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
|
|
sizeof(TA_IPX_ADDRESS));
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return( Status );
|
|
}
|
|
|
|
if ( pTdiStruc->Socket == 0 ) {
|
|
|
|
//
|
|
// Find out the socket number assigned by the transport
|
|
//
|
|
|
|
pTdiStruc->Socket = GetSocketNumber( pIrpC, pTdiStruc );
|
|
DebugTrace(0, Dbg, "Assigned socket number %X\n", pTdiStruc->Socket );
|
|
}
|
|
|
|
//
|
|
// Tell transport to accept packet type being set in the connection
|
|
// information provided with the send datagram. Transport reports
|
|
// the packet type similarly on receive datagram.
|
|
//
|
|
|
|
Status = SetTransportOption(
|
|
pIrpC,
|
|
pTdiStruc,
|
|
MIPX_SENDPTYPE );
|
|
|
|
DebugTrace(-1, Dbg, " %X\n", Status );
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
IPX_Close_Socket(
|
|
IN PNW_TDI_STRUCT pTdiStruc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminate a connection over the network.
|
|
|
|
Arguments:
|
|
|
|
pTdiStruc - supplies where to record the handle and both device and file
|
|
object pointers
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN ProcessAttached = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "IPX_Close_Socket %x\n", pTdiStruc->Socket);
|
|
|
|
if ( pTdiStruc->Handle == NULL ) {
|
|
return;
|
|
}
|
|
|
|
ObDereferenceObject( pTdiStruc->pFileObject );
|
|
|
|
//
|
|
// Attach to the redirector's FSP to allow the handle for the
|
|
// connection to hang around.
|
|
//
|
|
|
|
if (PsGetCurrentProcess() != FspProcess) {
|
|
ATTACHPROCESS(FspProcess);
|
|
ProcessAttached = TRUE;
|
|
}
|
|
|
|
ZwClose( pTdiStruc->Handle );
|
|
|
|
if (ProcessAttached) {
|
|
//
|
|
// Now re-attach back to our original process
|
|
//
|
|
|
|
KeDetachProcess();
|
|
}
|
|
|
|
pTdiStruc->Handle = NULL;
|
|
|
|
pTdiStruc->pFileObject = NULL;
|
|
|
|
DebugTrace(-1, Dbg, "IPX_Close_Socket\n", 0);
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IpxOpen(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open handle to the Ipx transport.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = IpxOpenHandle( &IpxHandle,
|
|
&pIpxDeviceObject,
|
|
&pIpxFileObject,
|
|
NULL,
|
|
0 );
|
|
|
|
DebugTrace(-1, Dbg, "IpxOpen of local node address %X\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IpxOpenHandle(
|
|
OUT PHANDLE pHandle,
|
|
OUT PDEVICE_OBJECT* ppDeviceObject,
|
|
OUT PFILE_OBJECT* ppFileObject,
|
|
IN PVOID EaBuffer OPTIONAL,
|
|
IN ULONG EaLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open handle to the Ipx transport.
|
|
|
|
Arguments:
|
|
|
|
OUT Handle - The handle to the transport if return value is NT_SUCCESS
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
OBJECT_ATTRIBUTES AddressAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS Status;
|
|
BOOLEAN ProcessAttached = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "IpxOpenHandle\n", 0);
|
|
|
|
*pHandle = NULL;
|
|
|
|
if (IpxTransportName.Buffer == NULL) {
|
|
|
|
//
|
|
// we are being called with an open ipx when transport is not bound
|
|
//
|
|
|
|
Status = STATUS_CONNECTION_INVALID ;
|
|
DebugTrace(-1, Dbg, "IpxOpenHandle %X\n", Status);
|
|
return Status ;
|
|
}
|
|
|
|
InitializeObjectAttributes (&AddressAttributes,
|
|
&IpxTransportName,
|
|
OBJ_CASE_INSENSITIVE,// Attributes
|
|
NULL, // RootDirectory
|
|
NULL); // SecurityDescriptor
|
|
|
|
//
|
|
// Attach to the redirector's FSP to allow the handle for the
|
|
// connection to hang around. Normally we create 3 handles at once
|
|
// so the outer code already has done this to avoid the expensive
|
|
// attach procedure.
|
|
//
|
|
|
|
if (PsGetCurrentProcess() != FspProcess) {
|
|
ATTACHPROCESS(FspProcess);
|
|
ProcessAttached = TRUE;
|
|
}
|
|
|
|
Status = ZwCreateFile(pHandle,
|
|
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
|
&AddressAttributes, // Object Attributes
|
|
&IoStatusBlock, // Final I/O status block
|
|
NULL, // Allocation Size
|
|
FILE_ATTRIBUTE_NORMAL, // Normal attributes
|
|
FILE_SHARE_READ,// Sharing attributes
|
|
FILE_OPEN_IF, // Create disposition
|
|
0, // CreateOptions
|
|
EaBuffer,EaLength);
|
|
|
|
if (!NT_SUCCESS(Status) ||
|
|
!NT_SUCCESS(Status = IoStatusBlock.Status)) {
|
|
|
|
goto error_cleanup2;
|
|
|
|
}
|
|
|
|
//
|
|
// Obtain a referenced pointer to the file object.
|
|
//
|
|
Status = ObReferenceObjectByHandle (
|
|
*pHandle,
|
|
0,
|
|
NULL,
|
|
KernelMode,
|
|
ppFileObject,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto error_cleanup;
|
|
|
|
}
|
|
|
|
if (ProcessAttached) {
|
|
|
|
//
|
|
// Now re-attach back to our original process
|
|
//
|
|
|
|
KeDetachProcess();
|
|
}
|
|
|
|
*ppDeviceObject = IoGetRelatedDeviceObject( *ppFileObject );
|
|
|
|
DebugTrace(-1, Dbg, "IpxOpenHandle %X\n", Status);
|
|
return Status;
|
|
|
|
error_cleanup2:
|
|
|
|
if ( *pHandle != NULL ) {
|
|
|
|
ZwClose( *pHandle );
|
|
*pHandle = NULL;
|
|
}
|
|
|
|
error_cleanup:
|
|
if (ProcessAttached) {
|
|
|
|
//
|
|
// Now re-attach back to our original process
|
|
//
|
|
|
|
KeDetachProcess();
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "IpxOpenHandle %X\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
BuildIpxAddress(
|
|
IN ULONG NetworkAddress,
|
|
IN PUCHAR NodeAddress,
|
|
IN USHORT Socket,
|
|
OUT PTA_IPX_ADDRESS NetworkName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds a TA_NETBIOS_ADDRESS structure in the locations pointed
|
|
to by NetworkName. All fields are filled out.
|
|
|
|
Arguments:
|
|
NetworkAddress - Supplies the network number
|
|
NodeAddress - Supplies the node number
|
|
Socket - The socket number (in Hi-Lo order)
|
|
NetworkName - Supplies the structure to place the address
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
// Warn compiler that TAAddressCount may be mis-aligned.
|
|
PUTA_IPX_ADDRESS UNetworkName = (PUTA_IPX_ADDRESS)NetworkName;
|
|
|
|
DebugTrace(+0, Dbg, "BuildIpxAddress\n", 0);
|
|
|
|
UNetworkName->TAAddressCount = 1;
|
|
UNetworkName->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX;
|
|
UNetworkName->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IPX;
|
|
|
|
RtlMoveMemory (
|
|
UNetworkName->Address[0].Address[0].NodeAddress,
|
|
NodeAddress,
|
|
6);
|
|
UNetworkName->Address[0].Address[0].NetworkAddress = NetworkAddress;
|
|
UNetworkName->Address[0].Address[0].Socket = Socket;
|
|
|
|
} /* TdiBuildIpxAddress */
|
|
|
|
|
|
VOID
|
|
BuildIpxAddressEa (
|
|
IN ULONG NetworkAddress,
|
|
IN PUCHAR NodeAddress,
|
|
IN USHORT Socket,
|
|
OUT PVOID NetworkName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds an EA describing a Netbios address in the buffer supplied by the
|
|
user.
|
|
|
|
Arguments:
|
|
|
|
NetworkAddress - Supplies the network number
|
|
NodeAddress - Supplies the node number
|
|
Socket -
|
|
NetworkName - The Ea structure that describes the input parameters.
|
|
|
|
Return Value:
|
|
|
|
An informative error code if something goes wrong. STATUS_SUCCESS if the
|
|
ea is built properly.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFILE_FULL_EA_INFORMATION EaBuffer;
|
|
PTA_IPX_ADDRESS TAAddress;
|
|
ULONG Length;
|
|
|
|
DebugTrace(+0, Dbg, "BuildIpxAddressEa\n", 0);
|
|
|
|
Length = FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) +
|
|
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
|
|
sizeof (TA_IPX_ADDRESS);
|
|
EaBuffer = (PFILE_FULL_EA_INFORMATION)NetworkName;
|
|
|
|
EaBuffer->NextEntryOffset = 0;
|
|
EaBuffer->Flags = 0;
|
|
EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
|
|
EaBuffer->EaValueLength = sizeof (TA_IPX_ADDRESS);
|
|
|
|
RtlCopyMemory (
|
|
EaBuffer->EaName,
|
|
TdiTransportAddress,
|
|
EaBuffer->EaNameLength + 1);
|
|
|
|
TAAddress = (PTA_IPX_ADDRESS)&EaBuffer->EaName[EaBuffer->EaNameLength+1];
|
|
|
|
BuildIpxAddress(
|
|
NetworkAddress,
|
|
NodeAddress,
|
|
Socket,
|
|
TAAddress);
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
IpxClose(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Close handle to the Ipx transport.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "IpxClose...\n", 0);
|
|
if ( pIpxFileObject ) {
|
|
ObDereferenceObject( pIpxFileObject );
|
|
pIpxFileObject = NULL;
|
|
}
|
|
|
|
// if ( pIpxDeviceObject ) {
|
|
// ObDereferenceObject( pIpxDeviceObject );
|
|
// pIpxDeviceObject = NULL;
|
|
// }
|
|
|
|
pIpxDeviceObject = NULL;
|
|
|
|
if ( IpxTransportName.Buffer != NULL ) {
|
|
FREE_POOL( IpxTransportName.Buffer );
|
|
IpxTransportName.Buffer = NULL;
|
|
}
|
|
|
|
if (IpxHandle) {
|
|
//
|
|
// Attach to the redirector's FSP to allow the handle for the
|
|
// connection to hang around.
|
|
//
|
|
|
|
if (PsGetCurrentProcess() != FspProcess) {
|
|
ATTACHPROCESS(FspProcess);
|
|
ZwClose( IpxHandle );
|
|
KeDetachProcess();
|
|
} else {
|
|
ZwClose( IpxHandle );
|
|
}
|
|
|
|
IpxHandle = NULL;
|
|
}
|
|
DebugTrace(-1, Dbg, "IpxClose\n", 0);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SetEventHandler (
|
|
IN PIRP_CONTEXT pIrpC,
|
|
IN PNW_TDI_STRUCT pTdiStruc,
|
|
IN ULONG EventType,
|
|
IN PVOID pEventHandler,
|
|
IN PVOID pContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine registers an event handler with a TDI transport provider.
|
|
|
|
Arguments:
|
|
|
|
pIrpC - supplies an Irp among other things.
|
|
|
|
pTdiStruc - supplies the handle and both device and file object pointers
|
|
to the transport.
|
|
|
|
IN ULONG EventType, - Supplies the type of event.
|
|
|
|
IN PVOID pEventHandler - Supplies the event handler.
|
|
|
|
IN PVOID pContext - Supplies the context to be supplied to the event
|
|
handler.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Final status of the set event operation
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
TdiBuildSetEventHandler(pIrpC->pOriginalIrp,
|
|
pTdiStruc->pDeviceObject,
|
|
pTdiStruc->pFileObject,
|
|
NULL,
|
|
NULL,
|
|
EventType,
|
|
pEventHandler,
|
|
pContext);
|
|
|
|
Status = SubmitTdiRequest(pTdiStruc->pDeviceObject,
|
|
pIrpC->pOriginalIrp);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SubmitTdiRequest (
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine submits a request to TDI and waits for it to complete.
|
|
|
|
Arguments:
|
|
|
|
IN PDevice_OBJECT DeviceObject - Connection or Address handle for TDI request
|
|
IN PIRP Irp - TDI request to submit.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Final status of request.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
KEVENT Event;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "SubmitTdiRequest\n", 0);
|
|
|
|
KeInitializeEvent (&Event, NotificationEvent, FALSE);
|
|
|
|
IoSetCompletionRoutine(pIrp, CompletionEvent, &Event, TRUE, TRUE, TRUE);
|
|
|
|
//
|
|
// Submit the request
|
|
//
|
|
|
|
Status = IoCallDriver(pDeviceObject, pIrp);
|
|
|
|
//
|
|
// If it failed immediately, return now, otherwise wait.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugTrace(-1, Dbg, "SubmitTdiRequest %X\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
|
|
DebugTrace(+0, Dbg, "Waiting....\n", 0);
|
|
|
|
Status = KeWaitForSingleObject(&Event, // Object to wait on.
|
|
Executive, // Reason for waiting
|
|
KernelMode, // Processor mode
|
|
FALSE, // Alertable
|
|
NULL); // Timeout
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugTrace(-1, Dbg, "SubmitTdiRequest could not wait %X\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
Status = pIrp->IoStatus.Status;
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "SubmitTdiRequest %X\n", Status);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CompletionEvent(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does not complete the Irp. It is used to signal to a
|
|
synchronous part of the driver that it can proceed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - unused.
|
|
|
|
Irp - Supplies Irp that the transport has finished processing.
|
|
|
|
Context - Supplies the event associated with the Irp.
|
|
|
|
Return Value:
|
|
|
|
The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
|
|
processing Irp stack locations at this point.
|
|
|
|
--*/
|
|
{
|
|
DebugTrace( 0, Dbg, "CompletionEvent\n", 0 );
|
|
|
|
KeSetEvent((PKEVENT )Context, 0, FALSE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
UNREFERENCED_PARAMETER( Irp );
|
|
}
|
|
|
|
|
|
USHORT
|
|
GetSocketNumber(
|
|
IN PIRP_CONTEXT pIrpC,
|
|
IN PNW_TDI_STRUCT pTdiStruc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Use a TDI_ACTION to set the Option.
|
|
|
|
Arguments:
|
|
|
|
pIrpC - supplies an Irp among other things.
|
|
|
|
pTdiStruc - supplies the handle and both device and file object pointers
|
|
to the transport.
|
|
|
|
Option - supplies the option to set.
|
|
|
|
Return Value:
|
|
|
|
0 failed otherwise the socket number.
|
|
|
|
--*/
|
|
{
|
|
ADDRESS_INFORMATION AddressInfo;
|
|
NTSTATUS Status;
|
|
USHORT SocketNumber;
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = QueryAddressInformation( pIrpC, pTdiStruc, &AddressInfo );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
SocketNumber = 0;
|
|
} else {
|
|
SocketNumber = AddressInfo.NetworkName.Address[0].Address[0].Socket;
|
|
|
|
RtlCopyMemory( &OurAddress,
|
|
&AddressInfo.NetworkName.Address[0].Address[0],
|
|
sizeof(TDI_ADDRESS_IPX));
|
|
|
|
}
|
|
|
|
return( SocketNumber );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
GetMaximumPacketSize(
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
IN PNW_TDI_STRUCT pTdiStruct,
|
|
OUT PULONG pMaximumPacketSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Query the maximum packet size for this network.
|
|
|
|
Arguments:
|
|
|
|
pIrpContext - supplies an Irp among other things.
|
|
|
|
pTdiStruct - supplies the handle and both device and file object pointers
|
|
to the transport.
|
|
|
|
pMaximumPacketSize - Returns the maximum packet size for the network.
|
|
|
|
Return Value:
|
|
|
|
The status of the query.
|
|
|
|
--*/
|
|
{
|
|
TDI_PROVIDER_INFO ProviderInfo;
|
|
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = QueryProviderInformation( pIrpContext, pTdiStruct, &ProviderInfo );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
*pMaximumPacketSize = ProviderInfo.MaxDatagramSize;
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
NTSTATUS
|
|
QueryAddressInformation(
|
|
PIRP_CONTEXT pIrpContext,
|
|
IN PNW_TDI_STRUCT pTdiStruct,
|
|
PADDRESS_INFORMATION AddressInformation
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PMDL MdlSave = pIrpContext->pOriginalIrp->MdlAddress;
|
|
PMDL Mdl;
|
|
|
|
PAGED_CODE();
|
|
|
|
Mdl = ALLOCATE_MDL(
|
|
AddressInformation,
|
|
sizeof( *AddressInformation ),
|
|
FALSE, // Secondary Buffer
|
|
FALSE, // Charge Quota
|
|
NULL);
|
|
|
|
if ( Mdl == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
try {
|
|
MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
FREE_MDL( Mdl );
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
TdiBuildQueryInformation(
|
|
pIrpContext->pOriginalIrp,
|
|
pTdiStruct->pDeviceObject,
|
|
pTdiStruct->pFileObject,
|
|
CompletionEvent,
|
|
NULL,
|
|
TDI_QUERY_ADDRESS_INFO,
|
|
Mdl);
|
|
|
|
Status = SubmitTdiRequest( pTdiStruct->pDeviceObject, pIrpContext->pOriginalIrp);
|
|
|
|
pIrpContext->pOriginalIrp->MdlAddress = MdlSave;
|
|
MmUnlockPages( Mdl );
|
|
FREE_MDL( Mdl );
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
QueryProviderInformation(
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
IN PNW_TDI_STRUCT pTdiStruct,
|
|
PTDI_PROVIDER_INFO ProviderInfo
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PMDL MdlSave = pIrpContext->pOriginalIrp->MdlAddress;
|
|
PMDL Mdl;
|
|
|
|
PAGED_CODE();
|
|
|
|
Mdl = ALLOCATE_MDL(
|
|
ProviderInfo,
|
|
sizeof( *ProviderInfo ),
|
|
FALSE, // Secondary Buffer
|
|
FALSE, // Charge Quota
|
|
NULL);
|
|
|
|
if ( Mdl == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
try {
|
|
MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
FREE_MDL( Mdl );
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
TdiBuildQueryInformation(
|
|
pIrpContext->pOriginalIrp,
|
|
pTdiStruct->pDeviceObject,
|
|
pTdiStruct->pFileObject,
|
|
CompletionEvent,
|
|
NULL,
|
|
TDI_QUERY_PROVIDER_INFO,
|
|
Mdl);
|
|
|
|
Status = SubmitTdiRequest(pTdiStruct->pDeviceObject, pIrpContext->pOriginalIrp);
|
|
|
|
pIrpContext->pOriginalIrp->MdlAddress = MdlSave;
|
|
MmUnlockPages( Mdl );
|
|
FREE_MDL( Mdl );
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SetTransportOption(
|
|
IN PIRP_CONTEXT pIrpC,
|
|
IN PNW_TDI_STRUCT pTdiStruc,
|
|
IN ULONG Option
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Use a TDI_ACTION to set the Option.
|
|
|
|
Arguments:
|
|
|
|
pIrpC - supplies an Irp among other things.
|
|
|
|
pTdiStruc - supplies the handle and both device and file object pointers
|
|
to the transport.
|
|
|
|
Option - supplies the option to set.
|
|
|
|
Return Value:
|
|
|
|
0 success
|
|
|
|
--*/
|
|
{
|
|
static struct {
|
|
TDI_ACTION_HEADER Header;
|
|
BOOLEAN DatagramOption;
|
|
ULONG BufferLength;
|
|
ULONG Option;
|
|
} SetPacketType = {
|
|
IPX_ID,
|
|
0, // ActionCode
|
|
0, // Reserved
|
|
TRUE, // DatagramOption
|
|
sizeof(ULONG) // BufferLength
|
|
};
|
|
|
|
KEVENT Event;
|
|
NTSTATUS Status;
|
|
|
|
PIRP pIrp = pIrpC->pOriginalIrp;
|
|
|
|
//
|
|
// Save the original MDL and System buffer address, to restore
|
|
// after the IRP completes.
|
|
//
|
|
// We use both the MDL and SystemBuffer because NWLINK assumes that
|
|
// we are using SystemBuffer even though we are supposed to use the
|
|
// MDL to pass a pointer to the action buffer.
|
|
//
|
|
|
|
PMDL MdlSave = pIrp->MdlAddress;
|
|
PCHAR SystemBufferSave = pIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
PMDL Mdl;
|
|
|
|
PAGED_CODE();
|
|
|
|
Mdl = ALLOCATE_MDL(
|
|
&SetPacketType,
|
|
sizeof( SetPacketType ),
|
|
FALSE, // Secondary Buffer
|
|
FALSE, // Charge Quota
|
|
NULL );
|
|
|
|
if ( Mdl == NULL ) {
|
|
IPX_Close_Socket( pTdiStruc );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
SetPacketType.Option = Option;
|
|
|
|
try {
|
|
MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
FREE_MDL( Mdl );
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
KeInitializeEvent (
|
|
&Event,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
TdiBuildAction(
|
|
pIrp,
|
|
pTdiStruc->pDeviceObject,
|
|
pTdiStruc->pFileObject,
|
|
CompletionEvent,
|
|
&Event,
|
|
Mdl );
|
|
|
|
//
|
|
// Set up the system buffer for NWLINK.
|
|
//
|
|
|
|
pIrp->AssociatedIrp.SystemBuffer = &SetPacketType;
|
|
|
|
Status = IoCallDriver (pTdiStruc->pDeviceObject, pIrp);
|
|
|
|
if ( Status == STATUS_PENDING ) {
|
|
Status = KeWaitForSingleObject (
|
|
&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
Status = pIrp->IoStatus.Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now restore the system buffer and MDL address in the IRP
|
|
//
|
|
|
|
pIrp->AssociatedIrp.SystemBuffer = SystemBufferSave;
|
|
pIrp->MdlAddress = MdlSave;
|
|
|
|
MmUnlockPages( Mdl );
|
|
FREE_MDL( Mdl );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
GetNewRoute(
|
|
IN PIRP_CONTEXT pIrpContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Use a TDI_ACTION to get a new route.
|
|
|
|
Arguments:
|
|
|
|
pIrpContext - Supplies IRP context information.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
struct {
|
|
TDI_ACTION_HEADER Header;
|
|
BOOLEAN DatagramOption;
|
|
ULONG BufferLength;
|
|
ULONG Option;
|
|
ULONG info_netnum;
|
|
USHORT info_hopcount;
|
|
USHORT info_netdelay;
|
|
int info_cardnum;
|
|
UCHAR info_router[6];
|
|
} ReRipRequest = {
|
|
IPX_ID,
|
|
0, // ActionCode
|
|
0, // Reserved
|
|
TRUE, // DatagramOption
|
|
24 // Buffer length (not including header)
|
|
};
|
|
|
|
KEVENT Event;
|
|
NTSTATUS Status;
|
|
|
|
PIRP pIrp = pIrpContext->pOriginalIrp;
|
|
|
|
//
|
|
// Save the original MDL and System buffer address, to restore
|
|
// after the IRP completes.
|
|
//
|
|
// We use both the MDL and SystemBuffer because NWLINK assumes that
|
|
// we are using SystemBuffer even though we are supposed to use the
|
|
// MDL to pass a pointer to the action buffer.
|
|
//
|
|
|
|
PMDL MdlSave = pIrp->MdlAddress;
|
|
PCHAR SystemBufferSave = pIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
PMDL Mdl;
|
|
|
|
PAGED_CODE();
|
|
|
|
Mdl = ALLOCATE_MDL(
|
|
&ReRipRequest,
|
|
sizeof( ReRipRequest ),
|
|
FALSE, // Secondary Buffer
|
|
FALSE, // Charge Quota
|
|
NULL );
|
|
|
|
if ( Mdl == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ReRipRequest.Option = MIPX_RERIPNETNUM;
|
|
ReRipRequest.info_netnum = pIrpContext->pNpScb->ServerAddress.Net;
|
|
|
|
try {
|
|
MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
FREE_MDL( Mdl );
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
KeInitializeEvent (
|
|
&Event,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
TdiBuildAction(
|
|
pIrp,
|
|
pIrpContext->pNpScb->Server.pDeviceObject,
|
|
pIrpContext->pNpScb->Server.pFileObject,
|
|
CompletionEvent,
|
|
&Event,
|
|
Mdl );
|
|
|
|
//
|
|
// Set up the system buffer for NWLINK.
|
|
//
|
|
|
|
pIrp->AssociatedIrp.SystemBuffer = &ReRipRequest;
|
|
|
|
Status = IoCallDriver ( pIrpContext->pNpScb->Server.pDeviceObject, pIrp);
|
|
|
|
if ( Status == STATUS_PENDING ) {
|
|
Status = KeWaitForSingleObject (
|
|
&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
Status = pIrp->IoStatus.Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now restore the system buffer and MDL address in the IRP
|
|
//
|
|
|
|
pIrp->AssociatedIrp.SystemBuffer = SystemBufferSave;
|
|
pIrp->MdlAddress = MdlSave;
|
|
|
|
MmUnlockPages( Mdl );
|
|
FREE_MDL( Mdl );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
GetTickCount(
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
OUT PUSHORT TickCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Use a TDI_ACTION to get a new route.
|
|
|
|
Arguments:
|
|
|
|
pIrpContext - Supplies IRP context information.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
struct {
|
|
TDI_ACTION_HEADER Header;
|
|
BOOLEAN DatagramOption;
|
|
ULONG BufferLength;
|
|
ULONG Option;
|
|
IPX_NETNUM_DATA NetNumData;
|
|
} GetTickCountInput = {
|
|
IPX_ID,
|
|
0, // ActionCode
|
|
0, // Reserved
|
|
TRUE, // DatagramOption
|
|
sizeof( IPX_NETNUM_DATA) + 2 * sizeof( ULONG )
|
|
};
|
|
|
|
struct _GET_TICK_COUNT_OUTPUT {
|
|
ULONG Option;
|
|
IPX_NETNUM_DATA NetNumData;
|
|
};
|
|
|
|
struct _GET_TICK_COUNT_OUTPUT *GetTickCountOutput;
|
|
|
|
KEVENT Event;
|
|
NTSTATUS Status;
|
|
|
|
PIRP pIrp = pIrpContext->pOriginalIrp;
|
|
|
|
//
|
|
// Save the original MDL and System buffer address, to restore
|
|
// after the IRP completes.
|
|
//
|
|
// We use both the MDL and SystemBuffer because NWLINK assumes that
|
|
// we are using SystemBuffer even though we are supposed to use the
|
|
// MDL to pass a pointer to the action buffer.
|
|
//
|
|
|
|
PMDL MdlSave = pIrp->MdlAddress;
|
|
PCHAR SystemBufferSave = pIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
PMDL Mdl;
|
|
|
|
PAGED_CODE();
|
|
|
|
Mdl = ALLOCATE_MDL(
|
|
&GetTickCountInput,
|
|
sizeof( GetTickCountInput ),
|
|
FALSE, // Secondary Buffer
|
|
FALSE, // Charge Quota
|
|
NULL );
|
|
|
|
if ( Mdl == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
GetTickCountInput.Option = MIPX_GETNETINFO;
|
|
*(PULONG)GetTickCountInput.NetNumData.netnum = pIrpContext->pNpScb->ServerAddress.Net;
|
|
|
|
try {
|
|
MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
FREE_MDL( Mdl );
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
KeInitializeEvent (
|
|
&Event,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
TdiBuildAction(
|
|
pIrp,
|
|
pIrpContext->pNpScb->Server.pDeviceObject,
|
|
pIrpContext->pNpScb->Server.pFileObject,
|
|
CompletionEvent,
|
|
&Event,
|
|
Mdl );
|
|
|
|
//
|
|
// Set up the system buffer for NWLINK.
|
|
//
|
|
|
|
pIrp->AssociatedIrp.SystemBuffer = &GetTickCountInput;
|
|
|
|
Status = IoCallDriver ( pIrpContext->pNpScb->Server.pDeviceObject, pIrp);
|
|
|
|
if ( Status == STATUS_PENDING ) {
|
|
Status = KeWaitForSingleObject (
|
|
&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
Status = pIrp->IoStatus.Status;
|
|
}
|
|
}
|
|
|
|
DebugTrace( +0, Dbg, "Get Tick Count, net= %x\n", pIrpContext->pNpScb->ServerAddress.Net );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// HACK-o-rama. Streams and non-streams IPX have different output
|
|
// buffer formats. For now accept both.
|
|
//
|
|
|
|
if ( IpxTransportName.Length == 32 ) {
|
|
|
|
// ISNIPX format
|
|
|
|
*TickCount = GetTickCountInput.NetNumData.netdelay;
|
|
} else {
|
|
|
|
// NWLINK format
|
|
|
|
GetTickCountOutput = (struct _GET_TICK_COUNT_OUTPUT *)&GetTickCountInput;
|
|
*TickCount = GetTickCountOutput->NetNumData.netdelay;
|
|
}
|
|
|
|
DebugTrace( +0, Dbg, "Tick Count = %d\n", *TickCount );
|
|
|
|
//
|
|
// Don't let the transport have us wait forever.
|
|
//
|
|
|
|
if ( *TickCount > 600 ) {
|
|
ASSERT( FALSE );
|
|
}
|
|
|
|
} else {
|
|
DebugTrace( +0, Dbg, "GetTickCount failed, status = %X\n", Status );
|
|
}
|
|
|
|
//
|
|
// Now restore the system buffer and MDL address in the IRP
|
|
//
|
|
|
|
pIrp->AssociatedIrp.SystemBuffer = SystemBufferSave;
|
|
pIrp->MdlAddress = MdlSave;
|
|
|
|
MmUnlockPages( Mdl );
|
|
FREE_MDL( Mdl );
|
|
|
|
return Status;
|
|
}
|
|
|
|
#ifndef QFE_BUILD
|
|
|
|
static PIRP LineChangeIrp = NULL;
|
|
|
|
|
|
NTSTATUS
|
|
SubmitLineChangeRequest(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Use a TDI_ACTION to get a new route.
|
|
|
|
Arguments:
|
|
|
|
pIrpContext - Supplies IRP context information.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
struct _LINE_CHANGE {
|
|
TDI_ACTION_HEADER Header;
|
|
BOOLEAN DatagramOption;
|
|
ULONG BufferLength;
|
|
ULONG Option;
|
|
} *LineChangeInput;
|
|
|
|
PIRP pIrp;
|
|
PMDL Mdl;
|
|
|
|
PAGED_CODE();
|
|
|
|
LineChangeInput = ALLOCATE_POOL( NonPagedPool, sizeof( struct _LINE_CHANGE ) );
|
|
|
|
if (!LineChangeInput) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Complete initialization of the request, and allocate and build an
|
|
// MDL for the request input buffer.
|
|
//
|
|
|
|
LineChangeInput->Header.TransportId = IPX_ID;
|
|
LineChangeInput->Header.ActionCode = 0;
|
|
LineChangeInput->Header.Reserved = 0;
|
|
LineChangeInput->DatagramOption = 2;
|
|
LineChangeInput->BufferLength = 2 * sizeof( ULONG );
|
|
LineChangeInput->Option = MIPX_LINECHANGE;
|
|
|
|
Mdl = ALLOCATE_MDL(
|
|
LineChangeInput,
|
|
sizeof( *LineChangeInput ),
|
|
FALSE, // Secondary Buffer
|
|
FALSE, // Charge Quota
|
|
NULL );
|
|
|
|
if ( Mdl == NULL ) {
|
|
FREE_POOL( LineChangeInput );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
pIrp = ALLOCATE_IRP( pIpxDeviceObject->StackSize, FALSE );
|
|
|
|
if ( pIrp == NULL ) {
|
|
FREE_POOL( LineChangeInput );
|
|
FREE_MDL( Mdl );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Remember this IRP so that we can cancel it.
|
|
//
|
|
|
|
LineChangeIrp = pIrp;
|
|
|
|
MmBuildMdlForNonPagedPool( Mdl );
|
|
|
|
//
|
|
// Build and submit a TDI request packet.
|
|
//
|
|
|
|
TdiBuildAction(
|
|
pIrp,
|
|
pIpxDeviceObject,
|
|
pIpxFileObject,
|
|
CompletionLineChange,
|
|
NULL,
|
|
Mdl );
|
|
|
|
Status = IoCallDriver ( pIpxDeviceObject, pIrp );
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CompletionLineChange(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the transport completes a line change IRP.
|
|
This means that we have switched nets, and that we should mark
|
|
all of our servers disconnected.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - unused.
|
|
|
|
Irp - Supplies Irp that the transport has finished processing.
|
|
|
|
Context - unused.
|
|
|
|
Return Value:
|
|
|
|
The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
|
|
processing Irp stack locations at this point.
|
|
|
|
--*/
|
|
{
|
|
PMDL Mdl;
|
|
PWORK_QUEUE_ITEM WorkQueueItem;
|
|
|
|
DebugTrace( 0, Dbg, "CompletionLineChange\n", 0 );
|
|
|
|
Mdl = Irp->MdlAddress;
|
|
|
|
if ( !NT_SUCCESS( Irp->IoStatus.Status ) ) {
|
|
FREE_POOL( Mdl->MappedSystemVa );
|
|
FREE_MDL( Mdl );
|
|
FREE_IRP( Irp );
|
|
return( STATUS_MORE_PROCESSING_REQUIRED );
|
|
}
|
|
|
|
//
|
|
// If the scavenger is running, simply make a note that
|
|
// we need to do this when it is finished.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel( &NwScavengerSpinLock );
|
|
|
|
if ( WorkerRunning ) {
|
|
|
|
if ( ( DelayedProcessLineChange != FALSE ) &&
|
|
( DelayedLineChangeIrp != NULL ) ) {
|
|
|
|
//
|
|
// We've already got a line change. Dump this one.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
|
|
|
|
DebugTrace( 0, Dbg, "Dumping an additional line change request.\n", 0 );
|
|
|
|
FREE_POOL( Mdl->MappedSystemVa );
|
|
FREE_MDL( Mdl );
|
|
FREE_IRP( Irp );
|
|
return( STATUS_MORE_PROCESSING_REQUIRED );
|
|
|
|
} else {
|
|
|
|
DebugTrace( 0, Dbg, "Delaying a line change request.\n", 0 );
|
|
|
|
DelayedProcessLineChange = TRUE;
|
|
DelayedLineChangeIrp = Irp;
|
|
|
|
KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Don't let the scavenger start up while we're running.
|
|
//
|
|
|
|
WorkerRunning = TRUE;
|
|
KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
|
|
}
|
|
|
|
WorkQueueItem = ALLOCATE_POOL( NonPagedPool, sizeof( *WorkQueueItem ) );
|
|
if ( WorkQueueItem == NULL ) {
|
|
FREE_POOL( Mdl->MappedSystemVa );
|
|
FREE_MDL( Mdl );
|
|
FREE_IRP( Irp );
|
|
return( STATUS_MORE_PROCESSING_REQUIRED );
|
|
}
|
|
|
|
//
|
|
// Use the user buffer field as a convenient place to remember where
|
|
// the address of the WorkQueueItem. We can get away with this since
|
|
// we don't let this IRP complete.
|
|
//
|
|
|
|
Irp->UserBuffer = WorkQueueItem;
|
|
|
|
//
|
|
// Process the line change in the FSP.
|
|
//
|
|
|
|
ExInitializeWorkItem( WorkQueueItem, FspProcessLineChange, Irp );
|
|
ExQueueWorkItem( WorkQueueItem, DelayedWorkQueue );
|
|
|
|
return( STATUS_MORE_PROCESSING_REQUIRED );
|
|
}
|
|
|
|
VOID
|
|
FspProcessLineChange(
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PIRP Irp;
|
|
ULONG ActiveHandles;
|
|
|
|
NwReferenceUnlockableCodeSection();
|
|
|
|
Irp = (PIRP)Context;
|
|
|
|
//
|
|
// Free the work queue item
|
|
//
|
|
|
|
FREE_POOL( Irp->UserBuffer );
|
|
Irp->UserBuffer = NULL;
|
|
|
|
//
|
|
// Invalid all remote handles
|
|
//
|
|
|
|
ActiveHandles = NwInvalidateAllHandles(NULL, NULL);
|
|
|
|
//
|
|
// Now that we're done walking all the servers, it's safe
|
|
// to let the scavenger run again.
|
|
//
|
|
|
|
WorkerRunning = FALSE;
|
|
|
|
//
|
|
// Resubmit the IRP
|
|
//
|
|
|
|
TdiBuildAction(
|
|
Irp,
|
|
pIpxDeviceObject,
|
|
pIpxFileObject,
|
|
CompletionLineChange,
|
|
NULL,
|
|
Irp->MdlAddress );
|
|
|
|
IoCallDriver ( pIpxDeviceObject, Irp );
|
|
|
|
NwDereferenceUnlockableCodeSection ();
|
|
return;
|
|
}
|
|
#endif
|
|
|