summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr/ipx.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr/ipx.c')
-rw-r--r--private/nw/rdr/ipx.c1749
1 files changed, 1749 insertions, 0 deletions
diff --git a/private/nw/rdr/ipx.c b/private/nw/rdr/ipx.c
new file mode 100644
index 000000000..2341d1cd3
--- /dev/null
+++ b/private/nw/rdr/ipx.c
@@ -0,0 +1,1749 @@
+/*++
+
+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.
+ // BUGBUG we should get the ioctl codes for ipx into a standard place
+ //
+
+ 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;
+
+ 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:
+
+ ASSERT( *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:
+
+ Open 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 (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.MaximumLookaheadData;
+ }
+
+ 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 );
+ } 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.
+
+--*/
+{
+ 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 ) );
+
+ //
+ // 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 );
+
+ IoCallDriver ( pIpxDeviceObject, pIrp );
+}
+
+
+
+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
+