summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr/exchange.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr/exchange.c')
-rw-r--r--private/nw/rdr/exchange.c4887
1 files changed, 4887 insertions, 0 deletions
diff --git a/private/nw/rdr/exchange.c b/private/nw/rdr/exchange.c
new file mode 100644
index 000000000..92535c02c
--- /dev/null
+++ b/private/nw/rdr/exchange.c
@@ -0,0 +1,4887 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ exchange.c
+
+Abstract:
+
+ This module implements the File Create routine for the NetWare
+ redirector called by the dispatch driver.
+
+Author:
+
+ Hans Hurvig [hanshu] Aug-1992 Created
+ Colin Watson [ColinW] 19-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+#include "tdikrnl.h"
+#include <STDARG.H>
+
+#define Dbg (DEBUG_TRACE_EXCHANGE)
+
+//
+// Exchange.c Global constants
+//
+
+// broadcast to socket 0x0452
+
+TA_IPX_ADDRESS SapBroadcastAddress =
+ {
+ 1,
+ sizeof(TA_IPX_ADDRESS), TDI_ADDRESS_TYPE_IPX,
+ 0, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, SAP_SOCKET
+ };
+
+UCHAR SapPacketType = PACKET_TYPE_SAP;
+UCHAR NcpPacketType = PACKET_TYPE_NCP;
+
+extern BOOLEAN WorkerRunning; // From timer.c
+
+#ifdef NWDBG
+ULONG DropCount = 0;
+int AlwaysAllocateIrp = 1;
+#endif
+
+NTSTATUS
+CompletionSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+FspGetMessage(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+CompletionWatchDogSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+USHORT
+NextSocket(
+ IN USHORT OldValue
+ );
+
+NTSTATUS
+FormatRequest(
+ PIRP_CONTEXT pIrpC,
+ PEX pEx,
+ char* f,
+ va_list a // format specific parameters
+ );
+
+VOID
+ScheduleReconnectRetry(
+ PIRP_CONTEXT pIrpContext
+ );
+
+VOID
+ReconnectRetry(
+ PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+CopyIndicatedData(
+ PIRP_CONTEXT pIrpContext,
+ PCHAR RspData,
+ ULONG BytesIndicated,
+ PULONG BytesTaken,
+ ULONG ReceiveDatagramFlags
+ );
+
+NTSTATUS
+AllocateReceiveIrp(
+ PIRP_CONTEXT pIrpContext,
+ PVOID ReceiveData,
+ ULONG BytesAvailable,
+ PULONG BytesAccepted,
+ PNW_TDI_STRUCT pTdiStruct
+ );
+
+NTSTATUS
+ReceiveIrpCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ );
+
+NTSTATUS
+FspProcessServerDown(
+ PIRP_CONTEXT IrpContext
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NextSocket )
+#pragma alloc_text( PAGE, ExchangeWithWait )
+#pragma alloc_text( PAGE, NewRouteRetry )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, FspGetMessage )
+#pragma alloc_text( PAGE1, Exchange )
+#pragma alloc_text( PAGE1, BuildRequestPacket )
+#pragma alloc_text( PAGE1, ParseResponse )
+#pragma alloc_text( PAGE1, ParseNcpResponse )
+#pragma alloc_text( PAGE1, FormatRequest )
+#pragma alloc_text( PAGE1, PrepareAndSendPacket )
+#pragma alloc_text( PAGE1, PreparePacket )
+#pragma alloc_text( PAGE1, SendPacket )
+#pragma alloc_text( PAGE1, AppendToScbQueue )
+#pragma alloc_text( PAGE1, KickQueue )
+#pragma alloc_text( PAGE1, SendNow )
+#pragma alloc_text( PAGE1, SetEvent )
+#pragma alloc_text( PAGE1, CompletionSend )
+#pragma alloc_text( PAGE1, CopyIndicatedData )
+#pragma alloc_text( PAGE1, AllocateReceiveIrp )
+#pragma alloc_text( PAGE1, ReceiveIrpCompletion )
+#pragma alloc_text( PAGE1, VerifyResponse )
+#pragma alloc_text( PAGE1, ScheduleReconnectRetry )
+#pragma alloc_text( PAGE1, ReconnectRetry )
+#pragma alloc_text( PAGE1, NewRouteBurstRetry )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+ServerDatagramHandler
+WatchDogDatagramHandler
+SendDatagramHandler
+CompletionWatchDogSend
+MdlLength
+FreeReceiveIrp
+FspProcessServerDown
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+NTSTATUS
+_cdecl
+Exchange(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine is a wrapper for _Exchange. See the comment
+ in _Exchange for routine and argument description.
+
+--*/
+
+{
+ va_list Arguments;
+ NTSTATUS Status;
+
+ va_start( Arguments, f );
+
+ Status = FormatRequest( pIrpContext, pEx, f, Arguments );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ //
+ // We won't be completing this IRP now, so mark it pending.
+ //
+
+ IoMarkIrpPending( pIrpContext->pOriginalIrp );
+
+ //
+ // Start the packet on it's way to the wire.
+ //
+
+ Status = PrepareAndSendPacket( pIrpContext );
+
+ return( Status );
+}
+
+NTSTATUS
+_cdecl
+BuildRequestPacket(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine is a wrapper for FormatRequest. See the comment
+ in FormatRequest for routine and argument description.
+
+--*/
+
+{
+ va_list Arguments;
+ NTSTATUS Status;
+
+ va_start( Arguments, f );
+
+ Status = FormatRequest( pIrpContext, pEx, f, Arguments );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ return( Status );
+}
+
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ PIRP_CONTEXT IrpContext,
+ PUCHAR Response,
+ ULONG ResponseLength,
+ char* FormatString,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine parse an NCP response.
+
+Arguments:
+
+ pIrpC - Supplies the irp context for the exchange request. This may
+ be NULL for generic packet types.
+
+ f... - supplies the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Packet types:
+
+ 'B' Burst primary response ( byte * )
+ 'N' NCP response ( void )
+ 'S' Burst secondary response ( byte * )
+ 'G' Generic packet ( )
+
+ Field types, request/response:
+
+ 'b' byte ( byte* )
+ 'w' hi-lo word ( word* )
+ 'x' ordered word ( word* )
+ 'd' hi-lo dword ( dword* )
+ 'e' ordered dword ( dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'p' pstring to Unicode ( UNICODE_STRING * )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'R' ASCIIZ to Unicode ( UNICODE_STRING *, word )
+
+ Added 3/29/95 by CoryWest:
+
+ 'W' lo-hi word ( word / word*)
+ 'D' lo-hi dword ( dword / dword*)
+ 'S' unicode string copy as NDS_STRING (UNICODE_STRING *)
+ 'T' terminal unicode string copy as NDS_STRING (UNICODE_STRING *)
+
+ 't' terminal unicode string with the nds null copied
+ as NDS_STRING (UNICODE_STRING *) (for GetUseName)
+
+ Not in use:
+
+ 's' cstring copy as NDS_STRING (char* / char *, word)
+ 'V' sized NDS value ( byte **, dword *)
+ 'l' what's this?
+
+Return Value:
+
+ STATUS - The converted error code from the NCP response.
+
+--*/
+
+{
+
+ PEPresponse *pResponseParameters;
+ PCHAR FormatByte;
+ va_list Arguments;
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS NcpStatus;
+ ULONG Length;
+
+ va_start( Arguments, FormatString );
+
+ //
+ // Make sure that we have an IrpContext unless we are doing
+ // a scan of a generic packet.
+ //
+
+#ifdef NWDBG
+ if ( *FormatString != 'G' ) {
+ ASSERT( IrpContext != NULL );
+ }
+#endif
+
+ switch ( *FormatString ) {
+
+ //
+ // NCP response.
+ //
+
+ case 'N':
+
+ Length = 8; // The data begins 8 bytes into the packet
+
+ pResponseParameters = (PEPresponse *)( ((PEPrequest *)Response) + 1);
+
+ //
+ // If there's a message pending for us on the server and we have
+ // popups disabled, we won't pick it up, but we should continue
+ // processing NCPs correctly!
+ //
+
+ if ( ( pResponseParameters->status == 0 ) ||
+ ( pResponseParameters->status == 0x40 ) ) {
+ Status = NwErrorToNtStatus( pResponseParameters->error );
+ } else {
+ Status = NwConnectionStatusToNtStatus( pResponseParameters->status );
+ if ( Status == STATUS_REMOTE_DISCONNECT ) {
+ Stats.ServerDisconnects++;
+ IrpContext->pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+ }
+ }
+
+ break;
+
+ //
+ // Burst response, first packet
+ //
+
+ case 'B': // BUGBUG Not needed, cleanup write.c first.
+ {
+ PNCP_BURST_HEADER BurstResponse = (PNCP_BURST_HEADER)Response;
+
+ byte* b = va_arg ( Arguments, byte* );
+ ULONG Result;
+ ULONG Offset = BurstResponse->BurstOffset;
+ *b = BurstResponse->Flags;
+
+ Length = 28; // The data begins 28 bytes into the packet
+
+ if ( Offset == 0 ) {
+
+ //
+ // This is the first packet in the burst response. Look
+ // at the result code.
+ //
+ // Note that the result DWORD is in lo-hi order.
+ //
+
+ Result = *(ULONG UNALIGNED *)(Response + 36);
+
+ switch ( Result ) {
+
+ case 0:
+ case 3: // No data
+ break;
+
+ case 1:
+ Status = STATUS_DISK_FULL;
+ break;
+
+ case 2: // I/O error
+ Status = STATUS_UNEXPECTED_IO_ERROR;
+ break;
+
+ default:
+ Status = NwErrorToNtStatus( (UCHAR)Result );
+ break;
+
+ }
+ }
+
+ break;
+ }
+
+#if 0
+ //
+ // Burst response, secondary packet
+ //
+
+ case 'S':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ *b = Response[2];
+
+ Length = 28; // The data begins 28 bytes into the packet
+ break;
+ }
+#endif
+
+ case 'G':
+ Length = 0; // The data begins at the start of the packet
+ break;
+
+ default:
+ ASSERT( FALSE );
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ //
+ // If this packet contains an error, simply return the error.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ NcpStatus = Status;
+
+ FormatByte = FormatString + 1;
+ while ( *FormatByte ) {
+
+ switch ( *FormatByte ) {
+
+ case '-':
+ Length += 1;
+ break;
+
+ case '=':
+ Length += 2;
+ break;
+
+ case '_':
+ {
+ word l = va_arg ( Arguments, word );
+ Length += l;
+ break;
+ }
+
+ case 'b':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ *b = Response[Length++];
+ break;
+ }
+
+ case 'w':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ b[1] = Response[Length++];
+ b[0] = Response[Length++];
+ break;
+ }
+
+ case 'x':
+ {
+ word* w = va_arg ( Arguments, word* );
+ *w = *(word UNALIGNED *)&Response[Length];
+ Length += 2;
+ break;
+ }
+
+ case 'd':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ b[3] = Response[Length++];
+ b[2] = Response[Length++];
+ b[1] = Response[Length++];
+ b[0] = Response[Length++];
+ break;
+ }
+
+ case 'e':
+ {
+ dword UNALIGNED * d = va_arg ( Arguments, dword* );
+ *d = *(dword UNALIGNED *)&Response[Length];
+ Length += 4;
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( Arguments, char* );
+ word l = strlen( &Response[Length] );
+ memcpy ( c, &Response[Length], l+1 );
+ Length += l+1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( Arguments, char* );
+ byte l = Response[Length++];
+ memcpy ( c, &Response[Length], l );
+ c[l+1] = 0;
+ break;
+ }
+
+ case 'P':
+ {
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ OemString.Length = Response[Length++];
+ OemString.Buffer = &Response[Length];
+
+ //
+ // Note the the Rtl function would set pUString->Buffer = NULL,
+ // if OemString.Length is 0.
+ //
+
+ if ( OemString.Length != 0 ) {
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+
+ if (!NT_SUCCESS( Status )) {
+ pUString->Length = 0;
+ NcpStatus = Status;
+ }
+
+ } else {
+ pUString->Length = 0;
+ }
+
+
+ break;
+ }
+
+ case 'r':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ word l = va_arg ( Arguments, word );
+ TdiCopyLookaheadData( b, &Response[Length], l, 0);
+ Length += l;
+ break;
+ }
+
+ case 'R':
+ {
+ //
+ // Interpret the buffer as an ASCIIZ string. Convert
+ // it to unicode in the preallocated buffer.
+ //
+
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+ USHORT len = va_arg ( Arguments, USHORT );
+
+ OemString.Buffer = &Response[Length];
+ OemString.Length = strlen( OemString.Buffer );
+ OemString.MaximumLength = OemString.Length;
+
+ //
+ // Note the the Rtl function would set pUString->Buffer = NULL,
+ // if OemString.Length is 0.
+ //
+
+ if ( OemString.Length != 0) {
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+
+ if (!NT_SUCCESS( Status )) {
+
+ ASSERT( Status == STATUS_BUFFER_OVERFLOW );
+ pUString->Length = 0;
+ NcpStatus = Status;
+ }
+
+ } else {
+ pUString->Length = 0;
+ }
+
+ Length += len;
+ break;
+ }
+
+ case 'W':
+ {
+
+ WORD *w = va_arg ( Arguments, WORD* );
+ *w = (* (WORD *)&Response[Length]);
+ Length += 2;
+ break;
+
+ }
+
+ case 'D':
+ {
+
+ DWORD *d = va_arg ( Arguments, DWORD* );
+ *d = (* (DWORD *)&Response[Length]);
+ Length += 4;
+ break;
+
+ }
+
+ case 'S':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length]);
+
+ //
+ // Don't count the null terminator that is part of
+ // Novell's counted unicode string.
+ //
+
+ pU->Length = strl - sizeof( WCHAR );
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+ Length += ROUNDUP4(strl);
+
+ } else {
+
+ //
+ // Skip over the string since we don't want it.
+ //
+
+ Length += ROUNDUP4((* (DWORD *)&Response[Length] ));
+ Length += 4;
+ }
+
+
+ break;
+
+ }
+
+ case 's':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length]);
+ pU->Length = strl;
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+ Length += ROUNDUP4(strl);
+
+ } else {
+
+ //
+ // Skip over the string since we don't want it.
+ //
+
+ Length += ROUNDUP4((* (DWORD *)&Response[Length] ));
+ Length += 4;
+ }
+
+
+ break;
+
+ }
+
+ case 'T':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length] );
+ strl -= sizeof( WCHAR ); // Don't count the NULL from NDS.
+
+ if ( strl <= pU->MaximumLength ) {
+
+ pU->Length = strl;
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+
+ //
+ // No need to advance the pointers since this is
+ // specifically a termination case!
+ //
+
+ } else {
+
+ pU->Length = 0;
+ }
+
+ }
+
+ break;
+
+ }
+
+ case 't':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length] );
+
+ if ( strl <= pU->MaximumLength ) {
+
+ pU->Length = strl;
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+
+ //
+ // No need to advance the pointers since this is
+ // specifically a termination case!
+ //
+
+ } else {
+
+ pU->Length = 0;
+
+ }
+
+ }
+
+ break;
+
+ }
+
+ /*
+ case 's':
+ {
+
+ char *c = va_arg( Arguments, char * );
+ WORD l = va_arg( Arguments, WORD );
+ ULONG len = (* (DWORD *)&Response[Length]);
+ Length += 4;
+
+ // BUGBUG: How to fix this?
+ // l = WideCharToMultiByte(CP_ACP,0,(WCHAR *)&Response[Length],Length/2,c,l,0,0);
+ // if (!l) {
+ // #ifdef NWDBG
+ // DbgPrint( "ParseResponse case s couldnt translate from WCHAR.\n" );
+ // #endif
+ // goto ErrorExit;
+ // }
+
+ len = ROUNDUP4(len);
+ Length += len;
+ break;
+
+ }
+ case 'V':
+ {
+
+ BYTE **b = va_arg( Arguments, BYTE **);
+ DWORD *pLen = va_arg ( Arguments, DWORD *);
+ DWORD len = (* (DWORD *)&Response[Length]);
+ Length += 4;
+ if (b) {
+ *b = (BYTE *)&Response[Length];
+ }
+ if (pLen) {
+ *pLen = len;
+ }
+ Length += ROUNDUP4(len);
+ break;
+
+ }
+
+ case 'l':
+ {
+
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ BYTE* w = va_arg ( Arguments, BYTE* );
+ WORD i;
+
+ b[1] = Response[Length++];
+ b[0] = Response[Length++];
+
+ for ( i = 0; i < ((WORD) *b); i++, w += sizeof(WORD) )
+ {
+ w[1] = Response[Length++];
+ w[0] = Response[Length++];
+ }
+
+ break;
+ }
+ */
+
+#ifdef NWDBG
+ default:
+ DbgPrintf ( "*****exchange: invalid response field, %x\n", *FormatByte );
+ DbgBreakPoint();
+#endif
+ }
+
+ if ( Length > ResponseLength ) {
+#ifdef NWDBG
+ DbgPrintf ( "*****exchange: not enough response data, %d\n", Length );
+
+ if ( IrpContext ) {
+
+ Error( EVENT_NWRDR_INVALID_REPLY,
+ STATUS_UNEXPECTED_NETWORK_ERROR,
+ NULL,
+ 0,
+ 1,
+ IrpContext->pNpScb->ServerName.Buffer );
+
+ }
+#endif
+ return( STATUS_UNEXPECTED_NETWORK_ERROR );
+ }
+
+ FormatByte++;
+ }
+
+ va_end( Arguments );
+
+ return( NcpStatus );
+}
+
+NTSTATUS
+ParseNcpResponse(
+ PIRP_CONTEXT IrpContext,
+ PNCP_RESPONSE Response
+ )
+{
+ NTSTATUS Status;
+
+ if ( Response->Status == 0 ) {
+ Status = NwErrorToNtStatus( Response->Error );
+ } else {
+ Status = NwConnectionStatusToNtStatus( Response->Status );
+ if ( Status == STATUS_REMOTE_DISCONNECT ) {
+ Stats.ServerDisconnects++;
+ IrpContext->pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+ }
+ }
+
+ return( Status );
+}
+
+NTSTATUS
+FormatRequest(
+ PIRP_CONTEXT pIrpC,
+ PEX pEx,
+ char* f,
+ va_list a // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ Send the packet described by f and the additional parameters. When a
+ valid response has been received call pEx with the resonse.
+
+ An exchange is a generic way of assembling a request packet of a
+ given type, containing a set of fields, sending the packet, receiving
+ a response packet, and disassembling the fields of the response packet.
+
+ The packet type and each field is specified by individual
+ characters in a format string.
+
+ The exchange procedure takes such a format string plus additional
+ parameters as necessary for each character in the string as specified
+ below.
+
+Arguments: '']
+
+ pIrpC - supplies the irp context for the exchange request.
+
+ pEx - supplies the routine to process the data.
+
+ f... - supplies the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Packet types:
+
+ 'A' SAP broadcast ( void )
+ 'B' NCP burst ( dword, dword, byte )
+ 'C' NCP connect ( void )
+ 'F' NCP function ( byte )
+ 'S' NCP subfunction ( byte, byte )
+ 'N' NCP subfunction w/o size ( byte, byte )
+ 'D' NCP disconnect ( void )
+ 'E' Echo data ( void )
+
+ Field types, request/response:
+
+ 'b' byte ( byte / byte* )
+ 'w' hi-lo word ( word / word* )
+ 'd' hi-lo dword ( dword / dword* )
+ 'W' lo-hi word ( word / word* )
+ 'D' lo-hi dword ( dword / dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'U' p uppercase string( UNICODE_STRING * )
+ 'J' variant of U ( UNICODE_STRING * )
+ 'c' cstring ( char* )
+ 'v' cstring ( UNICODE_STRING* )
+ 'r' raw bytes ( byte*, word )
+ 'w' fixed length unicode ( UNICODE_STRING*, word )
+ 'C' Component format name, with count ( UNICODE_STRING * )
+ 'N' Component format name, no count ( UNICODE_STRING * )
+ 'f' separate fragment ( PMDL )
+
+ An 'f' field must be last, and in a response it cannot be
+ preceeded by 'p' or 'c' fields.
+
+
+Return Value:
+
+ Normally returns STATUS_SUCCESS.
+
+--*/
+{
+ NTSTATUS status;
+ char* z;
+ word data_size;
+ PNONPAGED_SCB pNpScb = pIrpC->pNpScb;
+ dword dwData;
+
+ ASSERT( pIrpC->NodeTypeCode == NW_NTC_IRP_CONTEXT );
+ ASSERT( pIrpC->pNpScb != NULL );
+
+ status= STATUS_LINK_FAILED;
+
+ pIrpC->pEx = pEx; // Routine to process reply
+ pIrpC->Destination = pNpScb->RemoteAddress;
+ ClearFlag( pIrpC->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+
+ switch ( *f ) {
+
+ case 'A':
+ // Send to local network (0), a broadcast (-1), socket 0x452
+ pIrpC->Destination = SapBroadcastAddress;
+ pIrpC->PacketType = SAP_BROADCAST;
+
+ data_size = 0;
+ pNpScb->RetryCount = 3;
+ pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 10;
+ pNpScb->TimeOut = pNpScb->MaxTimeOut;
+ SetFlag( pIrpC->Flags, IRP_FLAG_RETRY_SEND );
+ break;
+
+ case 'E':
+ pIrpC->Destination = pNpScb->EchoAddress;
+ pIrpC->PacketType = NCP_ECHO;
+
+ //
+ // For echo packets use a short timeout and a small retry count.
+ // Set the retry send bit, so that SendNow doesn't reset the
+ // RetryCount to a bigger number.
+ //
+
+ pNpScb->RetryCount = 0;
+ pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 7;
+ pNpScb->TimeOut = pNpScb->MaxTimeOut;
+ SetFlag( pIrpC->Flags, IRP_FLAG_RETRY_SEND );
+ SetFlag( pIrpC->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ data_size = 0;
+ break;
+
+ case 'C':
+ pIrpC->PacketType = NCP_CONNECT;
+ *(PUSHORT)&pIrpC->req[0] = PEP_COMMAND_CONNECT;
+ pIrpC->req[2] = 0x00;
+ pIrpC->req[3] = 0xFF;
+ pIrpC->req[4] = 0x00;
+ pIrpC->req[5] = 0xFF;
+ data_size = 6;
+
+ pNpScb->MaxTimeOut = 16 * pNpScb->TickCount + 10;
+ pNpScb->TimeOut = 4 * pNpScb->TickCount + 10;
+ pNpScb->SequenceNo = 0;
+ break;
+
+ case 'F':
+ pIrpC->PacketType = NCP_FUNCTION;
+ goto FallThrough;
+
+ case 'S':
+ case 'N':
+ pIrpC->PacketType = NCP_SUBFUNCTION;
+ goto FallThrough;
+
+ case 'L':
+ pIrpC->PacketType = NCP_SUBFUNCTION;
+ goto FallThrough;
+
+ case 'D':
+ pIrpC->PacketType = NCP_DISCONNECT;
+ FallThrough:
+ if ( *f == 'D' ) {
+ *(PUSHORT)&pIrpC->req[0] = PEP_COMMAND_DISCONNECT;
+ } else {
+ *(PUSHORT)&pIrpC->req[0] = PEP_COMMAND_REQUEST;
+ }
+
+ pNpScb->RetryCount = DefaultRetryCount ;
+ pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 10;
+ pNpScb->TimeOut = pNpScb->SendTimeout;
+
+ //
+ // Mark this packet as SequenceNumberRequired. We need to guarantee
+ // the packets are sent in sequence number order, so we will
+ // fill in the sequence number when we are ready to send the
+ // packet.
+ //
+
+ SetFlag( pIrpC->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+ pIrpC->req[3] = pNpScb->ConnectionNo;
+ pIrpC->req[5] = pNpScb->ConnectionNoHigh;
+
+ if ( pIrpC->Icb != NULL && pIrpC->Icb->Pid != INVALID_PID ) {
+ pIrpC->req[4] = (UCHAR)pIrpC->Icb->Pid;
+ } else {
+ pIrpC->req[4] = 0xFF;
+ }
+
+ data_size = 6;
+
+ if ( *f == 'L' ) {
+ pIrpC->req[data_size++] = NCP_LFN_FUNCTION;
+ }
+
+ if ( *f != 'D' ) {
+ pIrpC->req[data_size++] = va_arg( a, byte );
+ }
+
+ if ( *f == 'S' ) {
+ data_size += 2;
+ pIrpC->req[data_size++] = va_arg( a, byte );
+ }
+
+ if ( *f == 'N' ) {
+ pIrpC->req[data_size++] = va_arg( a, byte );
+ }
+
+ break;
+
+ case 'B':
+ pIrpC->PacketType = NCP_BURST;
+ *(PUSHORT)&pIrpC->req[0] = PEP_COMMAND_BURST;
+
+ pNpScb->TimeOut = pNpScb->MaxTimeOut;
+
+ if ( !BooleanFlagOn( pIrpC->Flags, IRP_FLAG_RETRY_SEND ) ) {
+ pNpScb->RetryCount = 20;
+ }
+
+ pIrpC->req[3] = 0x2; // Stream Type = Big Send Burst
+
+ *(PULONG)&pIrpC->req[4] = pNpScb->SourceConnectionId;
+ *(PULONG)&pIrpC->req[8] = pNpScb->DestinationConnectionId;
+
+
+ LongByteSwap( (*(PULONG)&pIrpC->req[16]) , pNpScb->CurrentBurstDelay ); // Send delay time
+ dwData = va_arg( a, dword ); // Size of data
+ LongByteSwap( pIrpC->req[24], dwData );
+ dwData = va_arg( a, dword ); // Offset of data
+ LongByteSwap( pIrpC->req[28], dwData );
+ pIrpC->req[2] = va_arg( a, byte ); // Burst flags
+
+ data_size = 34;
+
+ break;
+
+ default:
+ DbgPrintf ( "*****exchange: invalid packet type, %x\n", *f );
+ DbgBreakPoint();
+ va_end( a );
+ return status;
+ }
+
+ z = f;
+ while ( *++z && *z != 'f' )
+ {
+ switch ( *z )
+ {
+ case '=':
+ pIrpC->req[data_size++] = 0;
+ case '-':
+ pIrpC->req[data_size++] = 0;
+ break;
+
+ case '_':
+ {
+ word l = va_arg ( a, word );
+ ASSERT( data_size + l <= MAX_SEND_DATA );
+
+ while ( l-- )
+ pIrpC->req[data_size++] = 0;
+ break;
+ }
+
+ case 's':
+ {
+ word l = va_arg ( a, word );
+ ASSERT ( data_size + l <= MAX_SEND_DATA );
+ data_size += l;
+ break;
+ }
+
+ case 'i':
+ pIrpC->req[4] = va_arg ( a, byte );
+ break;
+
+ case 'b':
+ pIrpC->req[data_size++] = va_arg ( a, byte );
+ break;
+
+ case 'w':
+ {
+ word w = va_arg ( a, word );
+ pIrpC->req[data_size++] = (byte) (w >> 8);
+ pIrpC->req[data_size++] = (byte) (w >> 0);
+ break;
+ }
+
+
+ case 'd':
+ {
+ dword d = va_arg ( a, dword );
+ pIrpC->req[data_size++] = (byte) (d >> 24);
+ pIrpC->req[data_size++] = (byte) (d >> 16);
+ pIrpC->req[data_size++] = (byte) (d >> 8);
+ pIrpC->req[data_size++] = (byte) (d >> 0);
+ break;
+ }
+
+ case 'W':
+ {
+ word w = va_arg ( a, word );
+ *(word UNALIGNED *)&pIrpC->req[data_size] = w;
+ data_size += 2;
+ break;
+ }
+
+
+ case 'D':
+ {
+ dword d = va_arg ( a, dword );
+ *(dword UNALIGNED *)&pIrpC->req[data_size] = d;
+ data_size += 4;
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( a, char* );
+ word l = strlen( c );
+ ASSERT (data_size + l <= MAX_SEND_DATA );
+
+ RtlCopyMemory( &pIrpC->req[data_size], c, l+1 );
+ data_size += l + 1;
+ break;
+ }
+
+ case 'v':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+ ULONG Length;
+
+ Length = RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT (( data_size + Length <= MAX_SEND_DATA) && ( (Length & 0xffffff00) == 0) );
+
+ OemString.Buffer = &pIrpC->req[data_size];
+ OemString.MaximumLength = (USHORT)Length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+ data_size += (USHORT)Length + 1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( a, char* );
+ byte l = strlen( c );
+
+ if ((data_size+l>MAX_SEND_DATA) ||
+ ( (l & 0xffffff00) != 0) ) {
+
+ ASSERT("***exchange: Packet too long!2!\n" && FALSE );
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ pIrpC->req[data_size++] = l;
+ RtlCopyMemory( &pIrpC->req[data_size], c, l );
+ data_size += l;
+ break;
+ }
+
+ case 'J':
+ case 'U':
+ case 'u':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+ PUCHAR pOemString;
+ ULONG Length;
+ ULONG i;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ Length = RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( Length < 0x100 );
+
+ if (( data_size + Length > MAX_SEND_DATA ) ||
+ ( (Length & 0xffffff00) != 0) ) {
+ ASSERT("***exchange:Packet too long or name >255 chars!4!\n" && FALSE);
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ pIrpC->req[data_size++] = (UCHAR)Length;
+ OemString.Buffer = &pIrpC->req[data_size];
+ OemString.MaximumLength = (USHORT)Length + 1;
+
+ if ( *z == 'u' ) {
+ status = RtlUnicodeStringToCountedOemString(
+ &OemString,
+ pUString,
+ FALSE );
+ } else {
+ status = RtlUpcaseUnicodeStringToCountedOemString(
+ &OemString,
+ pUString,
+ FALSE );
+ }
+
+ if ( !NT_SUCCESS( status ) ) {
+ return status;
+ }
+
+ data_size += (USHORT)Length;
+
+ if (( Japan ) &&
+ ( *z == 'J' )) {
+
+ //
+ // Netware Japanese version The following single byte character is replaced with another one
+ // if the string is for File Name only when sending from Client to Server.
+ //
+ // U+0xFF7F SJIS+0xBF -> 0x10
+ // U+0xFF6E SJIS+0xAE -> 0x11
+ // U+0xFF64 SJIS+0xAA -> 0x12
+ //
+
+ for ( i = 0 , pOemString = OemString.Buffer ; i < Length ; i++ , pOemString++ ) {
+
+ if( FsRtlIsLeadDbcsCharacter( *pOemString ) ) {
+
+ // Skip the trailing byte
+
+ i++; pOemString++;
+
+ if (*pOemString == 0x5C ) {
+
+ //
+ // The trailbyte is 0x5C, replace it with 0x13
+ //
+
+ *pOemString = 0x13;
+
+ }
+
+ } else {
+
+ // Single byte character that may need modification.
+
+
+ if ( *pOemString == 0xBF ) {
+
+ *pOemString = 0x10;
+
+ } else if ( *pOemString == 0xAA ) {
+
+ *pOemString = 0x12;
+
+ } else if ( *pOemString == 0xAE ) {
+
+ *pOemString = 0x11;
+ }
+ }
+ }
+ }
+
+ break;
+ }
+
+ case 'r':
+ {
+ byte* b = va_arg ( a, byte* );
+ word l = va_arg ( a, word );
+ if (data_size+l>MAX_SEND_DATA) {
+ ASSERT("***exchange: Packet too long!6!\n"&& FALSE);
+ return STATUS_UNSUCCESSFUL;
+ }
+ RtlCopyMemory( &pIrpC->req[data_size], b, l );
+ data_size += l;
+ break;
+ }
+
+ case 'x':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ ULONG RequiredLength = va_arg( a, word );
+ ULONG Length;
+ OEM_STRING OemString;
+
+ //
+ // Convert this string to an OEM string.
+ //
+
+ status = RtlUnicodeStringToCountedOemString( &OemString, pUString, TRUE );
+ ASSERT( NT_SUCCESS( status ));
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ if ( data_size + RequiredLength > MAX_SEND_DATA ) {
+ ASSERT("***exchange: Packet too long!4!\n" && FALSE);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Copy the oem string to the buffer, padded with 0's if
+ // necessary.
+ //
+
+ Length = MIN( OemString.Length, RequiredLength );
+ RtlMoveMemory( &pIrpC->req[data_size], OemString.Buffer, Length );
+
+ if ( RequiredLength > Length ) {
+ RtlFillMemory(
+ &pIrpC->req[data_size+Length],
+ RequiredLength - Length,
+ 0 );
+ }
+
+ RtlFreeAnsiString(&OemString);
+
+ data_size += (USHORT)RequiredLength;
+ break;
+ }
+
+ case 'C':
+ case 'N':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+ PWCH thisChar, lastChar, firstChar;
+ PCHAR componentCountPtr, pchar;
+ CHAR componentCount;
+ UNICODE_STRING UnicodeString;
+ int i;
+
+ //
+ // Copy the oem string to the buffer, in component format.
+ //
+
+ thisChar = pUString->Buffer;
+ lastChar = &pUString->Buffer[ pUString->Length / sizeof(WCHAR) ];
+
+ //
+ // Skip leading path separators
+ //
+
+ while ( *thisChar == OBJ_NAME_PATH_SEPARATOR &&
+ thisChar < lastChar ) {
+ thisChar++;
+ }
+
+ componentCount = 0;
+ if ( *z == 'C' ) {
+ componentCountPtr = &pIrpC->req[data_size++];
+ }
+
+
+ while ( thisChar < lastChar ) {
+
+ if ( data_size >= MAX_SEND_DATA - 1 ) {
+ ASSERT( ("***exchange: Packet too long or name > 255 chars!5!\n" && FALSE) );
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ firstChar = thisChar;
+
+ while ( thisChar < lastChar &&
+ *thisChar != OBJ_NAME_PATH_SEPARATOR ) {
+
+ thisChar++;
+
+ }
+
+ ++componentCount;
+
+ UnicodeString.Buffer = firstChar;
+ UnicodeString.Length = ( thisChar - firstChar ) * sizeof(WCHAR);
+
+ OemString.Buffer = &pIrpC->req[data_size + 1];
+ OemString.MaximumLength = MAX_SEND_DATA - data_size - 1;
+
+ status = RtlUnicodeStringToCountedOemString( &OemString, &UnicodeString, FALSE );
+
+ pIrpC->req[data_size] = (UCHAR)OemString.Length;
+ data_size += OemString.Length + 1;
+
+ if ( !NT_SUCCESS( status ) || data_size > MAX_SEND_DATA ) {
+ ASSERT("***exchange: Packet too long or name > 255 chars!5!\n" && FALSE );
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ //
+ // Search the result OEM string for the character 0xFF.
+ // If it's there, fail this request. The server doesn't
+ // deal with 0xFF very well.
+ //
+
+ for ( pchar = OemString.Buffer, i = 0;
+ i < OemString.Length;
+ pchar++, i++ ) {
+
+ //
+ // We need to check for dbcs, because 0xff is a
+ // legal trail byte for EUDC characters.
+ //
+ if ( FsRtlIsLeadDbcsCharacter( (UCHAR)*pchar ) ) {
+
+ //
+ // Skip dbcs character.
+ //
+
+ pchar++; i++;
+ continue;
+ }
+
+ if (( (UCHAR)*pchar == LFN_META_CHARACTER ) ||
+ !FsRtlIsAnsiCharacterLegalHpfs(*pchar, FALSE) ) {
+
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ }
+
+ thisChar++; // Skip the path separator
+
+ }
+
+ if ( *z == 'C' ) {
+ *componentCountPtr = componentCount;
+ }
+
+ break;
+ }
+
+ default:
+#ifdef NWDBG
+ DbgPrintf ( "*****exchange: invalid request field, %x\n", *z );
+ DbgBreakPoint();
+#endif
+ ;
+ }
+
+ if ( data_size > MAX_SEND_DATA )
+ {
+ DbgPrintf( "*****exchange: CORRUPT, too much request data\n" );
+ DbgBreakPoint();
+ va_end( a );
+ return STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ pIrpC->TxMdl->ByteCount = data_size;
+
+ if ( *z == 'f' )
+ {
+ PMDL mdl;
+
+ //
+ // Fragment of data following Ipx header. Next parameter is
+ // the address of the mdl describing the fragment.
+ //
+ ++z;
+ mdl = (PMDL) va_arg ( a, byte* );
+ pIrpC->TxMdl->Next = mdl;
+
+ data_size += (USHORT)MdlLength( mdl );
+ }
+
+ if ( *f == 'S' ) {
+
+ pIrpC->req[7] = (data_size-9) >> 8;
+ pIrpC->req[8] = (data_size-9);
+
+ } else if ( *f == 'B' ) {
+
+ //
+ // For burst packets set the number of bytes in this packet to
+ // a real number for burst requests, and to 0 for a missing packet
+ // request.
+ //
+
+ if ( *(PUSHORT)&pIrpC->req[34] == 0 ) {
+ USHORT RealDataSize = data_size - 36;
+ ShortByteSwap( pIrpC->req[32], RealDataSize );
+ } else {
+ *(PUSHORT)&pIrpC->req[32] = 0;
+ }
+ }
+
+ va_end( a );
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+PrepareAndSendPacket(
+ PIRP_CONTEXT pIrpContext
+ )
+{
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+
+ return SendPacket( pIrpContext, pIrpContext->pNpScb );
+}
+
+VOID
+PreparePacket(
+ PIRP_CONTEXT pIrpContext,
+ PIRP pIrp,
+ PMDL pMdl
+ )
+/*++
+
+Routine Description:
+
+ This routine builds the IRP for sending a packet.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for the request
+ being processed.
+
+ Irp - The IRP to be used to submit the request to the transport.
+
+ Mdl - A pointer to the MDL for the data to send.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PIO_COMPLETION_ROUTINE CompletionRoutine;
+ PNW_TDI_STRUCT pTdiStruct;
+
+ DebugTrace(0, Dbg, "PreparePacket...\n", 0);
+
+ pIrpContext->ConnectionInformation.UserDataLength = 0;
+ pIrpContext->ConnectionInformation.OptionsLength = sizeof( UCHAR );
+ pIrpContext->ConnectionInformation.Options =
+ (pIrpContext->PacketType == SAP_BROADCAST) ?
+ &SapPacketType : &NcpPacketType;
+ pIrpContext->ConnectionInformation.RemoteAddressLength = sizeof(TA_IPX_ADDRESS);
+ pIrpContext->ConnectionInformation.RemoteAddress = &pIrpContext->Destination;
+
+#if NWDBG
+ dump( Dbg,
+ &pIrpContext->Destination.Address[0].Address[0],
+ sizeof(TDI_ADDRESS_IPX));
+ dumpMdl( Dbg, pMdl);
+#endif
+
+ //
+ // Set the socket to use for this send. If unspecified in the
+ // IRP context, use the default (server) socket.
+ //
+
+ pTdiStruct = pIrpContext->pTdiStruct == NULL ?
+ &pIrpContext->pNpScb->Server : pIrpContext->pTdiStruct;
+
+ CompletionRoutine = pIrpContext->CompletionSendRoutine == NULL ?
+ CompletionSend : pIrpContext->CompletionSendRoutine;
+
+ TdiBuildSendDatagram(
+ pIrp,
+ pTdiStruct->pDeviceObject,
+ pTdiStruct->pFileObject,
+ CompletionRoutine,
+ pIrpContext,
+ pMdl,
+ MdlLength( pMdl ),
+ &pIrpContext->ConnectionInformation );
+
+ //
+ // Set the run routine to send now, only if this is the main IRP
+ // for this irp context.
+ //
+
+ if ( pIrp == pIrpContext->pOriginalIrp ) {
+ pIrpContext->RunRoutine = SendNow;
+ }
+
+ return;
+}
+
+
+NTSTATUS
+SendPacket(
+ PIRP_CONTEXT pIrpC,
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ Queue a packet created by exchange and try to send it to the server.
+
+Arguments:
+
+ pIrpC - supplies the irp context for the request creating the socket.
+
+ pNpScb - supplies the server to receive the request.
+
+Return Value:
+
+ STATUS_PENDING
+
+--*/
+{
+ if ( AppendToScbQueue( pIrpC, pNpScb ) ) {
+ KickQueue( pNpScb );
+ }
+
+ return STATUS_PENDING;
+}
+
+
+BOOLEAN
+AppendToScbQueue(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_SCB NpScb
+ )
+/*++
+
+Routine Description:
+
+ Queue an IRP context to the SCB, if it is not already there.
+
+Arguments:
+
+ IrpContext - Supplies the IRP context to queue.
+
+ NpScb - Supplies the server to receive the request.
+
+Return Value:
+
+ TRUE - The IRP Context is at the front of the queue.
+ FALSE - The IRP Context is not at the front of the queue.
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+#ifdef MSWDBG
+ KIRQL OldIrql;
+#endif
+ DebugTrace(0, Dbg, "AppendToScbQueue... %08lx\n", NpScb);
+ DebugTrace(0, Dbg, "IrpContext = %08lx\n", IrpContext );
+
+ //
+ // Look at the IRP Context flags. If the IRP is already on the
+ // queue, then it must be at the front and ready for processing.
+ //
+
+ if ( FlagOn( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) ) {
+ ASSERT( NpScb->Requests.Flink == &IrpContext->NextRequest );
+ return( TRUE );
+ }
+
+#ifdef MSWDBG
+ NpScb->RequestQueued = TRUE;
+#endif
+
+#if 0 // Resource layout changed on Daytona. Disable for now.
+
+ //
+ // Make sure that this thread isn't holding the RCB while waiting for
+ // the SCB queue.
+ //
+
+ ASSERT ( NwRcb.Resource.InitialOwnerThreads[0] != (ULONG)PsGetCurrentThread() );
+#endif
+
+ //
+ // The IRP Context was not at the front. Queue it, then look to
+ // see if it was appended to an empty queue.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+#ifdef MSWDBG
+ ExAcquireSpinLock( &NpScb->NpScbSpinLock, &OldIrql );
+ if ( IsListEmpty( &NpScb->Requests ) ) {
+ ListEntry = NULL;
+ } else {
+ ListEntry = NpScb->Requests.Flink;
+ }
+
+ InsertTailList( &NpScb->Requests, &IrpContext->NextRequest );
+ IrpContext->SequenceNumber = NpScb->SequenceNumber++;
+ ExReleaseSpinLock( &NpScb->NpScbSpinLock, OldIrql );
+
+#else
+ ListEntry = ExInterlockedInsertTailList(
+ &NpScb->Requests,
+ &IrpContext->NextRequest,
+ &NpScb->NpScbSpinLock );
+#endif
+
+ if ( ListEntry == NULL ) {
+ ASSERT( NpScb->Requests.Flink == &IrpContext->NextRequest );
+ DebugTrace(-1, Dbg, "AppendToScbQueue -> TRUE\n", 0);
+ return( TRUE );
+ } else {
+ DebugTrace(-1, Dbg, "AppendToScbQueue -> FALSE\n", 0);
+ return( FALSE );
+ }
+
+}
+
+
+VOID
+KickQueue(
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ Queue a packet created by exchange and try to send it to the server.
+
+ Note: NpScbSpinLock must be held before calling this routine.
+
+Arguments:
+
+ pNpScb - supplies the server queue to kick into life.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ PIRP_CONTEXT pIrpC;
+ PRUN_ROUTINE RunRoutine;
+ KIRQL OldIrql;
+
+
+ DebugTrace( +1, Dbg, "KickQueue...%08lx\n", pNpScb);
+
+ KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql );
+ if ( IsListEmpty( &pNpScb->Requests )) {
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ DebugTrace( -1, Dbg, " Empty Queue\n", 0);
+ return;
+ }
+
+ pIrpC = CONTAINING_RECORD(pNpScb->Requests.Flink, IRP_CONTEXT, NextRequest);
+
+ ASSERT( pIrpC->pNpScb->Requests.Flink == &pIrpC->NextRequest );
+ ASSERT( pIrpC->NodeTypeCode == NW_NTC_IRP_CONTEXT);
+
+ RunRoutine = pIrpC->RunRoutine;
+
+ // Only call the routine to tell it it is at the front once
+
+ pIrpC->RunRoutine = NULL;
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+
+ //
+ // If the redir is shutting down do not process this request
+ // unless we must.
+ //
+
+ if ( NwRcb.State != RCB_STATE_RUNNING &&
+ !FlagOn( pIrpC->Flags, IRP_FLAG_SEND_ALWAYS ) ) {
+
+ //
+ // Note that it's safe to call the pEx routine without the
+ // spin lock held since this IrpContext just made it to the
+ // front of the queue, and so can't have i/o in progress.
+ //
+
+ if ( pIrpC->pEx != NULL) {
+ pIrpC->pEx( pIrpC, 0, NULL );
+ DebugTrace( -1, Dbg, "KickQueue\n", 0);
+ return;
+ }
+ }
+
+ if ( RunRoutine != NULL ) {
+
+ ASSERT( pNpScb->Receiving == FALSE );
+
+ RunRoutine( pIrpC );
+
+ }
+
+ DebugTrace( -1, Dbg, "KickQueue\n", 0);
+ return;
+}
+
+VOID
+SendNow(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine submits a TDI send request to the tranport layer.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for the request
+ being processed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+
+ pNpScb = IrpContext->pNpScb;
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RETRY_SEND ) ) {
+ pNpScb->RetryCount = DefaultRetryCount;
+ }
+
+ //
+ // Ensure that this IRP Context is really at the front of the queue.
+ //
+
+ ASSERT( pNpScb->Requests.Flink == &IrpContext->NextRequest );
+ IrpContext->RunRoutine = NULL;
+
+ //
+ // Make sure that this is a correctly formatted send request.
+ //
+
+ IrpSp = IoGetNextIrpStackLocation( IrpContext->pOriginalIrp );
+ ASSERT( IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL );
+ ASSERT( IrpSp->MinorFunction == TDI_SEND_DATAGRAM );
+
+ //
+ // This IRP context has a packet ready to send. Send it now.
+ //
+
+ pNpScb->Sending = TRUE;
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE ) ) {
+ pNpScb->OkToReceive = TRUE;
+ }
+ pNpScb->Receiving = FALSE;
+ pNpScb->Received = FALSE;
+
+ //
+ // If this packet requires a sequence number, set it now.
+ // The sequence number is updated when we receive a response.
+ //
+ // We do not need to synchronize access to SequenceNo since
+ // this is the only active packet for this SCB.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED ) ) {
+ ClearFlag( IrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+ IrpContext->req[2] = pNpScb->SequenceNo;
+ }
+
+ //
+ // If this packet is a burst packet, fill in the burst sequence number
+ // now, and burst request number.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_BURST_PACKET ) ) {
+
+ LongByteSwap( IrpContext->req[12], pNpScb->BurstSequenceNo );
+ pNpScb->BurstSequenceNo++;
+
+ ShortByteSwap( IrpContext->req[20], pNpScb->BurstRequestNo );
+ ShortByteSwap( IrpContext->req[22], pNpScb->BurstRequestNo );
+
+ }
+
+ DebugTrace( +0, Dbg, "Irp %X\n", IrpContext->pOriginalIrp);
+ DebugTrace( +0, Dbg, "pIrpC %X\n", IrpContext);
+ DebugTrace( +0, Dbg, "Mdl %X\n", IrpContext->TxMdl);
+
+#if NWDBG
+ dumpMdl( Dbg, IrpContext->TxMdl);
+#endif
+
+ {
+ ULONG len = 0;
+ PMDL Next = IrpContext->TxMdl;
+
+ do {
+ len += MmGetMdlByteCount(Next);
+ } while (Next = Next->Next);
+
+ Stats.BytesTransmitted.QuadPart += len;
+ }
+
+ Status = IoCallDriver(pNpScb->Server.pDeviceObject, IrpContext->pOriginalIrp);
+ DebugTrace( -1, Dbg, "Transport returned: %08lx\n", Status );
+
+ Stats.NcpsTransmitted.QuadPart++;
+
+ return;
+
+}
+
+
+VOID
+SetEvent(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine set the IrpContext Event to the signalled state.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for the request
+ being processed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ //
+ // Ensure that this IRP Context is really at the front of the queue.
+ //
+
+ ASSERT( IrpContext->pNpScb->Requests.Flink == &IrpContext->NextRequest );
+
+ //
+ // This IRP context has a thread waiting to get to the front of
+ // the queue. Set the event to indicate that it can continue.
+ //
+
+#ifdef MSWDBG
+ ASSERT( IrpContext->Event.Header.SignalState == 0 );
+ IrpContext->DebugValue = 0x105;
+#endif
+
+ DebugTrace( +0, Dbg, "Setting event for IrpContext %X\n", IrpContext );
+ NwSetIrpContextEvent( IrpContext );
+}
+
+
+USHORT
+NextSocket(
+ IN USHORT OldValue
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the byteswapped OldValue++ wrapping from 7fff.
+
+Arguments:
+
+ OldValue - supplies the existing socket number in the range
+ 0x4000 to 0x7fff.
+
+Return Value:
+
+ USHORT OldValue++
+
+--*/
+
+{
+ USHORT TempValue = OldValue + 0x0100;
+
+ if ( TempValue < 0x100 ) {
+ if ( TempValue == 0x007f ) {
+ // Wrap back to 0x4000 from 0xff7f
+ return 0x0040;
+ } else {
+ // Go from something like 0xff40 to 0x0041
+ return TempValue + 1;
+ }
+ }
+ return TempValue;
+}
+
+
+ULONG
+MdlLength (
+ register IN PMDL Mdl
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the number of bytes in an MDL.
+
+Arguments:
+
+ IN PMDL Mdl - Supplies the MDL to determine the length on.
+
+Return Value:
+
+ ULONG - Number of bytes in the MDL
+
+--*/
+
+{
+ register ULONG Size = 0;
+ while (Mdl!=NULL) {
+ Size += MmGetMdlByteCount(Mdl);
+ Mdl = Mdl->Next;
+ }
+ return Size;
+}
+
+
+NTSTATUS
+CompletionSend(
+ 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 IrpContext associated with the Irp.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ PIRP_CONTEXT pIrpC = (PIRP_CONTEXT) Context;
+ KIRQL OldIrql;
+
+ //
+ // Avoid completing the Irp because the Mdl etc. do not contain
+ // their original values.
+ //
+
+ DebugTrace( +1, Dbg, "CompletionSend\n", 0);
+ DebugTrace( +0, Dbg, "Irp %X\n", Irp);
+ DebugTrace( +0, Dbg, "pIrpC %X\n", pIrpC);
+ DebugTrace( +0, Dbg, "Status %X\n", Irp->IoStatus.Status);
+
+ pNpScb = pIrpC->pNpScb;
+ KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql );
+
+ ASSERT( pNpScb->Sending == TRUE );
+ pNpScb->Sending = FALSE;
+
+ //
+ // If we got a receive indication while waiting for send
+ // completion and the data is all valid, call the receive handler routine now.
+ //
+
+ if ( pNpScb->Received ) {
+
+ pNpScb->Receiving = FALSE;
+ pNpScb->Received = FALSE;
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+
+ pIrpC->pEx(
+ pIrpC,
+ pIrpC->ResponseLength,
+ pIrpC->rsp );
+
+ } else if (( Irp->IoStatus.Status == STATUS_DEVICE_DOES_NOT_EXIST ) ||
+ ( Irp->IoStatus.Status == STATUS_BAD_NETWORK_PATH ) ||
+ ( Irp->IoStatus.Status == STATUS_INVALID_BUFFER_SIZE ) ||
+ ( Irp->IoStatus.Status == STATUS_NETWORK_UNREACHABLE )) {
+
+ //
+ // The send failed.
+ //
+
+ //
+ // FIXFIX I would prefer to use !NT_SUCCESS(Irp->IoStatus.Status) to
+ // get into this code but would need more time to check its ok.
+ //
+
+ //
+ // If this SCB is still flagged okay to receive (how could it not?)
+ // simply call the callback routine to indicate failure.
+ //
+ // If the SendCompletion hasn't happened, set up so that send
+ // completion will call the callback routine.
+ //
+
+ if ( pNpScb->OkToReceive ) {
+
+ pNpScb->OkToReceive = FALSE;
+ ClearFlag( pIrpC->Flags, IRP_FLAG_RETRY_SEND );
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ DebugTrace(+0, Dbg, "Send failed\n", 0 );
+
+ pIrpC->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
+ pIrpC->pEx( pIrpC, 0, NULL );
+
+ } else {
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ }
+
+ } else {
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ }
+
+ DebugTrace( -1, Dbg, "CompletionSend STATUS_MORE_PROCESSING_REQUIRED\n", 0);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+ UNREFERENCED_PARAMETER( Irp );
+}
+
+#if NWDBG
+BOOLEAN UseIrpReceive = FALSE;
+#endif
+
+
+NTSTATUS
+ServerDatagramHandler(
+ IN PVOID TdiEventContext,
+ IN int SourceAddressLength,
+ IN PVOID SourceAddress,
+ IN int OptionsLength,
+ IN PVOID Options,
+ IN ULONG ReceiveDatagramFlags,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PVOID Tsdu,
+ OUT PIRP *IoRequestPacket
+ )
+/*++
+
+Routine Description:
+
+ This routine is the receive datagram event indication handler for the
+ Server socket.
+
+Arguments:
+
+ TdiEventContext - Context provided for this event, a pointer to the
+ non paged SCB.
+
+ SourceAddressLength - Length of the originator of the datagram.
+
+ SourceAddress - String describing the originator of the datagram.
+
+ OptionsLength - Length of the buffer pointed to by Options.
+
+ Options - Options for the receive.
+
+ ReceiveDatagramFlags - Ignored.
+
+ BytesIndicated - Number of bytes this indication.
+
+ BytesAvailable - Number of bytes in complete Tsdu.
+
+ BytesTaken - Returns the number of bytes used.
+
+ Tsdu - Pointer describing this TSDU, typically a lump of bytes.
+
+ IoRequestPacket - TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ PNONPAGED_SCB pNpScb = (PNONPAGED_SCB)TdiEventContext;
+ NTSTATUS Status = STATUS_DATA_NOT_ACCEPTED;
+ UCHAR PacketType;
+ PUCHAR RspData = (PUCHAR)Tsdu;
+ PIRP_CONTEXT pIrpC;
+ PNW_TDI_STRUCT pTdiStruct;
+ BOOLEAN AcceptPacket = TRUE;
+ PNCP_BURST_READ_RESPONSE pBurstRsp;
+ NTSTATUS BurstStatus;
+
+ *IoRequestPacket = NULL;
+#if DBG
+ pTdiStruct = NULL;
+#endif
+
+ if (pNpScb->NodeTypeCode != NW_NTC_SCBNP ) {
+
+ DebugTrace(+0, 0, "nwrdr: Invalid Server Indication %x\n", pNpScb );
+#if DBG
+ DbgBreakPoint();
+#endif
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+#if NWDBG
+
+ // Debug only trick to test IRP receive.
+
+ if ( UseIrpReceive ) {
+ BytesIndicated = 0;
+ }
+#endif
+
+ DebugTrace(+1, Dbg, "ServerDatagramHandler\n", 0);
+ DebugTrace(+0, Dbg, "Server %x\n", pNpScb);
+ DebugTrace(+0, Dbg, "BytesIndicated %x\n", BytesIndicated);
+ DebugTrace(+0, Dbg, "BytesAvailable %x\n", BytesAvailable);
+
+ //
+ // SourceAddress is the address of the server or the bridge tbat sent
+ // the packet.
+ //
+
+#if NWDBG
+ dump( Dbg, SourceAddress, SourceAddressLength );
+ dump( Dbg, Tsdu, BytesIndicated );
+#endif
+
+ if ( OptionsLength == 1 ) {
+ PacketType = *(PCHAR)Options;
+ DebugTrace(+0, Dbg, "PacketType %x\n", PacketType);
+ } else {
+ DebugTrace(+0, Dbg, "OptionsLength %x\n", OptionsLength);
+#if NWDBG
+ dump( Dbg, Options, OptionsLength );
+#endif
+ }
+
+ KeAcquireSpinLockAtDpcLevel(&pNpScb->NpScbSpinLock );
+
+ if ( !pNpScb->OkToReceive ) {
+
+ //
+ // This SCB is not expecting to receive any data.
+ // Discard this packet.
+ //
+
+#ifdef NWDBG
+ DropCount++;
+#endif
+ DebugTrace(+0, Dbg, "OkToReceive == FALSE - discard packet\n", 0);
+ AcceptPacket = FALSE;
+ goto process_packet;
+ }
+
+ pIrpC = CONTAINING_RECORD(pNpScb->Requests.Flink, IRP_CONTEXT, NextRequest);
+
+ ASSERT( pIrpC->NodeTypeCode == NW_NTC_IRP_CONTEXT);
+
+ //
+ // Verify that this packet came from where we expect it to come from,
+ // and that is has a minimum size.
+ //
+
+ if ( ( pIrpC->PacketType != SAP_BROADCAST &&
+ RtlCompareMemory(
+ &pIrpC->Destination,
+ SourceAddress,
+ SourceAddressLength ) != (ULONG)SourceAddressLength ) ||
+ BytesIndicated < 8 ) {
+
+ AcceptPacket = FALSE;
+#ifdef NWDBG
+ DbgPrintf ( "***exchange: stray response tossed\n", 0 );
+#endif
+ goto process_packet;
+ }
+
+ switch ( pIrpC->PacketType ) {
+
+ case SAP_BROADCAST:
+
+ //
+ // We are expected a SAP Broadcast frame. Ensure that this
+ // is a correctly formatted SAP.
+ //
+
+ if ( pIrpC->req[0] != RspData[0] ||
+ pIrpC->req[2] != RspData[2] ||
+ pIrpC->req[3] != RspData[3] ||
+ SourceAddressLength != sizeof(TA_IPX_ADDRESS) ) {
+
+ DbgPrintf ( "***exchange: bad SAP packet\n" );
+ AcceptPacket = FALSE;
+ }
+
+ pTdiStruct = &pNpScb->Server;
+ break;
+
+ case NCP_BURST:
+
+ if ( *(USHORT UNALIGNED *)&RspData[0] == PEP_COMMAND_BURST ) {
+
+ if ( BytesIndicated < 36 ) {
+
+ AcceptPacket = FALSE;
+
+ } else if ( ( RspData[2] & BURST_FLAG_SYSTEM_PACKET ) &&
+ RspData[34] == 0 &&
+ RspData[35] == 0 ) {
+
+ //
+ // We have burst mode busy reponse.
+ //
+
+ DebugTrace(+0, Dbg, "Burst mode busy\n", 0 );
+ NwProcessPositiveAck( pNpScb );
+
+ AcceptPacket = FALSE;
+
+ } else {
+
+ USHORT Brn;
+
+ //
+ // Check the burst sequence number.
+ //
+
+ ShortByteSwap( Brn, RspData[20] );
+
+ if ( pNpScb->BurstRequestNo == Brn ) {
+ pTdiStruct = &pNpScb->Burst;
+ AcceptPacket = TRUE;
+ } else {
+ AcceptPacket = FALSE;
+ }
+ }
+ } else {
+ AcceptPacket = FALSE;
+ }
+
+ break;
+
+ case NCP_ECHO:
+
+ pTdiStruct = &pNpScb->Echo;
+ AcceptPacket = TRUE;
+ break;
+
+ default:
+
+ pTdiStruct = &pNpScb->Server;
+
+ //
+ // This is the handling for all packets types other than
+ // SAP Broadcasts.
+ //
+
+ ASSERT( (pIrpC->PacketType == NCP_CONNECT) ||
+ (pIrpC->PacketType == NCP_FUNCTION) ||
+ (pIrpC->PacketType == NCP_SUBFUNCTION) ||
+ (pIrpC->PacketType == NCP_DISCONNECT));
+
+ if ( *(USHORT UNALIGNED *)&RspData[0] == PEP_COMMAND_ACKNOWLEDGE ) {
+
+ AcceptPacket = FALSE;
+
+ if ( RspData[2] == pIrpC->req[2] &&
+ RspData[3] == pIrpC->req[3] ) {
+
+ //
+ // We have received an ACK frame.
+ //
+
+ DebugTrace(+0, Dbg, "Received positive acknowledge\n", 0 );
+ NwProcessPositiveAck( pNpScb );
+
+ }
+
+ break;
+
+ } else if ( *(USHORT UNALIGNED *)&RspData[0] == PEP_COMMAND_BURST ) {
+
+ //
+ // This is a stray burst response, ignore it.
+ //
+
+ AcceptPacket = FALSE;
+ break;
+
+ } else if ( *(USHORT UNALIGNED *)&RspData[0] != PEP_COMMAND_RESPONSE ) {
+
+ //
+ // We have received an invalid frame.
+ //
+
+ DbgPrintf ( "***exchange: invalid Response\n" );
+ AcceptPacket = FALSE;
+ break;
+
+ } else if ( pIrpC->PacketType == NCP_CONNECT ) {
+
+ pNpScb->SequenceNo = RspData[2];
+ pNpScb->ConnectionNo = RspData[3];
+ pNpScb->ConnectionNoHigh = RspData[5];
+
+ // We should now continue to process the Connect
+ break;
+ }
+
+ //
+ // Make sure this the response we expect.
+ //
+
+ if ( !VerifyResponse( pIrpC, RspData ) ) {
+
+ //
+ // This is a stray or corrupt response. Ignore it.
+ //
+
+ AcceptPacket = FALSE;
+ break;
+
+ } else {
+
+ //
+ // We have received a valid, in sequence response.
+ // Bump the current sequence number.
+ //
+
+ ++pNpScb->SequenceNo;
+
+ }
+
+ if ( pIrpC->PacketType == NCP_FUNCTION ||
+ pIrpC->PacketType == NCP_SUBFUNCTION ) {
+
+ if ( ( RspData[7] &
+ ( NCP_STATUS_BAD_CONNECTION |
+ NCP_STATUS_NO_CONNECTIONS ) ) != 0 ) {
+ //
+ // We've lost our connection to the server.
+ // Try to reconnect if it is allowed for this request.
+ //
+
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+
+ if ( BooleanFlagOn( pIrpC->Flags, IRP_FLAG_RECONNECTABLE ) ) {
+ ClearFlag( pIrpC->Flags, IRP_FLAG_RECONNECTABLE );
+ AcceptPacket = FALSE;
+ if (!pNpScb->Sending) {
+ ScheduleReconnectRetry( pIrpC );
+ pNpScb->OkToReceive = FALSE;
+ } else {
+ //
+ // If we are sending, it is not OK schedule the
+ // retry now, because if we do and the send
+ // completion hasnt been run we could end up
+ // with 2 guys thinking they are at the front
+ // of the queue. We let the send complete and
+ // wait for that to fail instead. We will
+ // eventually reconnect.
+ //
+ }
+ }
+
+ break;
+
+ } else if ( ( RspData[7] & NCP_STATUS_SHUTDOWN ) != 0 ) {
+
+ //
+ // This server's going down. We need to process this
+ // message in the FSP. Copy the indicated data and
+ // process in the FSP.
+ //
+
+ pNpScb->State = SCB_STATE_ATTACHING;
+ AcceptPacket = FALSE;
+ pNpScb->OkToReceive = FALSE;
+ pNpScb->Receiving = TRUE;
+
+ CopyIndicatedData(
+ pIrpC,
+ RspData,
+ BytesIndicated,
+ BytesTaken,
+ ReceiveDatagramFlags );
+
+ pIrpC->PostProcessRoutine = FspProcessServerDown;
+ Status = NwPostToFsp( pIrpC, FALSE );
+
+ break;
+ }
+
+ } else if ( pIrpC->PacketType == NCP_DISCONNECT ) {
+
+ //
+ // We have received a disconnect frame.
+ //
+
+ break;
+ }
+
+ }
+
+process_packet:
+ if ( AcceptPacket ) {
+
+ ASSERT ( !IsListEmpty( &pNpScb->Requests ));
+ ASSERT( pIrpC->pEx != NULL );
+
+
+ //
+ // If we received this packet without a retry, adjust the
+ // send timeout value.
+ //
+
+ if (( !BooleanFlagOn( pIrpC->Flags, IRP_FLAG_RETRY_SEND ) ) &&
+ ( pIrpC->PacketType != NCP_BURST )) {
+
+ SHORT NewTimeout;
+
+ NewTimeout = ( pNpScb->SendTimeout + pNpScb->TickCount ) / 2;
+ pNpScb->SendTimeout = MAX( NewTimeout, pNpScb->TickCount + 1 );
+
+ DebugTrace( 0, Dbg, "Successful exchange, new send timeout = %d\n", pNpScb->SendTimeout );
+ }
+
+ //
+ // If the transport didn't indicate all of the data, we'll need
+ // to post a receive IRP.
+ //
+
+#ifdef NWDBG
+ if (( BytesIndicated < BytesAvailable ) ||
+ ( AlwaysAllocateIrp )){
+#else
+ if ( BytesIndicated < BytesAvailable ) {
+#endif
+
+ if ( ( BooleanFlagOn( pIrpC->Flags, IRP_FLAG_BURST_REQUEST ) ) &&
+ ( IsListEmpty( &pIrpC->Specific.Read.PacketList ) ) ) {
+
+ pBurstRsp = (PNCP_BURST_READ_RESPONSE)RspData;
+ BurstStatus = NwBurstResultToNtStatus( pBurstRsp->Result );
+
+ //
+ // If this entire burst failed with an error, we can't
+ // let the receive data routine signal the caller until
+ // the pEx gets called and we exit on the correct paths.
+ //
+
+ if ( !NT_SUCCESS( BurstStatus ) ) {
+
+ DebugTrace( 0, Dbg, "Special burst termination %08lx.\n", BurstStatus );
+ pIrpC->Specific.Read.Status = BurstStatus;
+
+ if ( pNpScb->Sending ) {
+
+ //
+ // If the send hasn't completed yet, we can't accept
+ // the packet because IPX may not have completed back
+ // to us yet!
+ //
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+ DebugTrace(-1, Dbg, "ServerDatagramHandler -> STATUS_DATA_NOT_ACCEPTED (%08lx)\n", BurstStatus );
+ return( STATUS_DATA_NOT_ACCEPTED );
+
+ } else {
+
+ //
+ // Handle this one just like normal, except that we
+ // know it's going to fail in the receive data routine
+ // and we don't want the timeout routine to fire
+ // causing us all sort of grief, so we set OkToReceive
+ // to FALSE.
+ //
+
+ pNpScb->OkToReceive = FALSE;
+ }
+ }
+
+ }
+
+ FreeReceiveIrp( pIrpC ); // Free old Irp if one was allocated
+
+ Status = AllocateReceiveIrp(
+ pIrpC,
+ RspData,
+ BytesAvailable,
+ BytesTaken,
+ pTdiStruct );
+
+ if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
+
+ pNpScb->OkToReceive = FALSE;
+ pNpScb->Receiving = TRUE;
+
+ } else if (!NT_SUCCESS( Status ) ) {
+
+ pIrpC->ReceiveIrp = NULL;
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+
+ *IoRequestPacket = pIrpC->ReceiveIrp;
+
+ } else {
+
+ pNpScb->OkToReceive = FALSE;
+
+ //
+ // The transport has indicated all of the data.
+ // If the send has completed, call the pEx routine,
+ // otherwise copy the data to a buffer and let the
+ // send completion routine call the pEx routine.
+ //
+
+ if ( pNpScb->Sending ) {
+ DebugTrace( 0, Dbg, "Received data before send completion\n", 0 );
+
+ Status = CopyIndicatedData(
+ pIrpC,
+ RspData,
+ BytesIndicated,
+ BytesTaken,
+ ReceiveDatagramFlags );
+
+ if (NT_SUCCESS(Status)) {
+ pNpScb->Received = TRUE;
+ pNpScb->Receiving = TRUE;
+ } else {
+ // Ignore this packet
+ pNpScb->OkToReceive = TRUE;
+ }
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+
+ } else {
+ pNpScb->Receiving = FALSE;
+ pNpScb->Received = FALSE;
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+
+ DebugTrace(+0, Dbg, "Call pIrpC->pEx %x\n", pIrpC->pEx );
+
+ Status = pIrpC->pEx(pIrpC,
+ BytesAvailable,
+ RspData);
+ }
+
+ *BytesTaken = BytesAvailable;
+
+ }
+
+ } else {
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+ Status = STATUS_DATA_NOT_ACCEPTED;
+
+ }
+
+ Stats.NcpsReceived.QuadPart++;
+ Stats.BytesReceived.QuadPart += BytesAvailable;
+
+ DebugTrace(-1, Dbg, "ServerDatagramHandler -> %08lx\n", Status );
+ return( Status );
+
+} // ServerDatagramHandler
+
+NTSTATUS
+CopyIndicatedData(
+ PIRP_CONTEXT pIrpContext,
+ PCHAR ReceiveData,
+ ULONG BytesIndicated,
+ PULONG BytesAccepted,
+ ULONG ReceiveDatagramFlags
+ )
+/*++
+
+Routine Description:
+
+ This routine copies indicated data to a buffer. If the packet is small
+ enough the data is copied to the preallocated receive buffer in the
+ IRP context. If the packet is too long, a new buffer is allocated.
+
+Arguments:
+
+ pIrpContext - A pointer the block of context information for the request
+ in progress.
+
+ ReceiveData - A pointer to the indicated data.
+
+ BytesIndicated - The number of bytes available in the received packet.
+
+ BytesAccepted - Returns the number of bytes accepted by the receive
+ routine.
+
+ ReceiveDatagramFlags - Receive flags given to us by the transport.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ NTSTATUS Status;
+ PMDL ReceiveMdl;
+ PVOID MappedVa;
+ ULONG BytesToCopy;
+ BOOLEAN DeleteMdl = FALSE;
+
+ pIrpContext->ResponseLength = BytesIndicated;
+
+ //
+ // If there is a receive data routine, use it to generate the receive
+ // MDL, otherwise use the default MDL.
+ //
+
+ if ( pIrpContext->ReceiveDataRoutine != NULL ) {
+
+ Status = pIrpContext->ReceiveDataRoutine(
+ pIrpContext,
+ BytesIndicated,
+ BytesAccepted,
+ ReceiveData,
+ &ReceiveMdl );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ //
+ // We can accept up to the size of a burst read header, plus
+ // 3 bytes of fluff for the unaligned read case.
+ //
+
+ ASSERT( *BytesAccepted <= sizeof(NCP_BURST_READ_RESPONSE) + 3 );
+
+ BytesIndicated -= *BytesAccepted;
+ ReceiveData += *BytesAccepted;
+
+ DeleteMdl = TRUE;
+
+ } else {
+
+ *BytesAccepted = 0;
+ ReceiveMdl = pIrpContext->RxMdl;
+
+ }
+
+ if ( ReceiveMdl != NULL ) {
+
+ while ( BytesIndicated > 0 && ReceiveMdl != NULL ) {
+
+ MappedVa = MmGetSystemAddressForMdl( ReceiveMdl );
+ BytesToCopy = MIN( MmGetMdlByteCount( ReceiveMdl ), BytesIndicated );
+ TdiCopyLookaheadData( MappedVa, ReceiveData, BytesToCopy, ReceiveDatagramFlags );
+
+ ReceiveMdl = ReceiveMdl->Next;
+ BytesIndicated -= BytesToCopy;
+ ReceiveData += BytesToCopy;
+
+ ASSERT( !( BytesIndicated != 0 && ReceiveMdl == NULL ) );
+ }
+
+ if (DeleteMdl) {
+
+ PMDL Mdl = pIrpContext->Specific.Read.PartialMdl;
+ PMDL NextMdl;
+
+ while ( Mdl != NULL ) {
+ NextMdl = Mdl->Next;
+ DebugTrace( 0, Dbg, "Freeing MDL %x\n", Mdl );
+ FREE_MDL( Mdl );
+ Mdl = NextMdl;
+ }
+
+ pIrpContext->Specific.Read.PartialMdl = NULL;
+ }
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+AllocateReceiveIrp(
+ PIRP_CONTEXT pIrpContext,
+ PVOID ReceiveData,
+ ULONG BytesAvailable,
+ PULONG BytesAccepted,
+ PNW_TDI_STRUCT pTdiStruct
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates an IRP and if necessary a receive buffer. It
+ then builds an MDL for the buffer and formats the IRP to do a TDI
+ receive.
+
+ BUGBUG - Consider preallocating and queueing for efficiency.
+
+Arguments:
+
+ pIrpContext - A pointer the block of context information for the request
+ in progress.
+
+ ReceiveData - The indicated data.
+
+ BytesAvailable - The number of bytes available in the received packet.
+
+ BytesAccepted - Returns the number of bytes accepted from the packet.
+
+ pTdiStruct - A pointer to the TdiStruct which has indicated the receive.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+ STATUS_MORE_PROCESSING_REQUIRED means we were successful.
+
+--*/
+{
+ PIRP Irp = NULL;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ ASSERT( pTdiStruct != NULL );
+
+ Irp = ALLOCATE_IRP( pIrpContext->pNpScb->Server.pDeviceObject->StackSize, FALSE );
+
+ if ( Irp == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto CleanExit;
+ }
+
+ //
+ // If there is no receive data routine for this IRP, the
+ // RxMdl must point to a valid place to put the data.
+ //
+ // If there is a ReceiveDataRoutine it will build an MDL
+ //
+
+ if ( pIrpContext->ReceiveDataRoutine == NULL ) {
+
+ ULONG LengthOfMdl;
+
+ LengthOfMdl = MdlLength( pIrpContext->RxMdl );
+
+ //
+ // If the server sent more data than we can receive, simply
+ // ignore the excess. In particular 3.11 pads long name
+ // response with an excess of junk.
+ //
+
+ if ( BytesAvailable > LengthOfMdl ) {
+ BytesAvailable = LengthOfMdl;
+ }
+
+ Irp->MdlAddress = pIrpContext->RxMdl;
+ *BytesAccepted = 0;
+
+ } else {
+
+ Status = pIrpContext->ReceiveDataRoutine(
+ pIrpContext,
+ BytesAvailable,
+ BytesAccepted,
+ ReceiveData,
+ &Irp->MdlAddress );
+
+ if ( !NT_SUCCESS( Status ) ||
+ Irp->MdlAddress == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto CleanExit;
+
+ }
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_FREE_RECEIVE_MDL );
+
+ }
+
+CleanExit:
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( Irp != NULL ) {
+ FREE_IRP( Irp );
+ }
+
+ Irp = NULL;
+ pIrpContext->ReceiveIrp = NULL;
+ Status = STATUS_DATA_NOT_ACCEPTED;
+ return( Status );
+ }
+
+ pIrpContext->ReceiveIrp = Irp;
+ Status = STATUS_MORE_PROCESSING_REQUIRED;
+
+ pIrpContext->ResponseLength = BytesAvailable;
+
+ TdiBuildReceive(
+ Irp,
+ pTdiStruct->pDeviceObject,
+ pTdiStruct->pFileObject,
+ ReceiveIrpCompletion,
+ pIrpContext,
+ Irp->MdlAddress,
+ 0,
+ BytesAvailable - *BytesAccepted );
+
+ IoSetNextIrpStackLocation( Irp );
+
+ return( Status );
+}
+
+NTSTATUS
+ReceiveIrpCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when a recieve IRP completes.
+
+Arguments:
+
+ DeviceObject - Unused.
+
+ Irp - The IRP that completed.
+
+ Context - A pointer the block of context information for the request
+ in progress.
+
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ PIRP_CONTEXT IrpContext = (PIRP_CONTEXT)Context;
+ PIO_STACK_LOCATION IrpSp;
+ PNONPAGED_SCB pNpScb;
+ PMDL Mdl, NextMdl;
+ KIRQL OldIrql;
+
+ ASSERT( Irp == IrpContext->ReceiveIrp );
+
+ pNpScb = IrpContext->pNpScb;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ //
+ // Free the IRP MDL if we allocated one specifically for this IRP.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_FREE_RECEIVE_MDL ) ) {
+
+ Mdl = IrpContext->Specific.Read.PartialMdl;
+ IrpContext->Specific.Read.PartialMdl = NULL;
+
+ while ( Mdl != NULL ) {
+ NextMdl = Mdl->Next;
+ DebugTrace( 0, Dbg, "Freeing MDL %x\n", Mdl );
+ FREE_MDL( Mdl );
+ Mdl = NextMdl;
+ }
+
+ }
+
+ if ( !NT_SUCCESS( Irp->IoStatus.Status ) ) {
+
+ //
+ // Failed to receive the data. Wait for more.
+ //
+
+ pNpScb->OkToReceive = TRUE;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ }
+
+ //
+ // If the send has completed, call the pEx routine,
+ // otherwise copy the data to a buffer and let the
+ // send completion routine call the pEx routine.
+ //
+
+ KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql );
+
+ if ( pNpScb->Sending ) {
+ DebugTrace( 0, Dbg, "Received data before send completion\n", 0 );
+
+ //
+ // Tell send completion to call pEx.
+ //
+
+ pNpScb->Received = TRUE;
+ KeReleaseSpinLock(&pNpScb->NpScbSpinLock, OldIrql );
+
+ } else {
+ pNpScb->Receiving = FALSE;
+ pNpScb->Received = FALSE;
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ DebugTrace(+0, Dbg, "Call pIrpC->pEx %x\n", IrpContext->pEx );
+ IrpContext->pEx(
+ IrpContext,
+ IrpContext->ResponseLength,
+ IrpContext->rsp );
+
+ }
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+VOID
+FreeReceiveIrp(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine frees a IRP that was allocated to do a receive.
+
+Arguments:
+
+ IrpContext - A pointer the block of context information for the request
+ in progress.
+
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ if ( IrpContext->ReceiveIrp == NULL ) {
+ return;
+ }
+
+ FREE_IRP( IrpContext->ReceiveIrp );
+ IrpContext->ReceiveIrp = NULL;
+}
+
+
+NTSTATUS
+WatchDogDatagramHandler(
+ IN PVOID TdiEventContext,
+ IN int SourceAddressLength,
+ IN PVOID SourceAddress,
+ IN int OptionsLength,
+ IN PVOID Options,
+ IN ULONG ReceiveDatagramFlags,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PVOID Tsdu,
+ OUT PIRP *IoRequestPacket
+ )
+/*++
+
+Routine Description:
+
+ This routine is the receive datagram event indication handler for the
+ Server socket.
+
+Arguments:
+
+ TdiEventContext - Context provided for this event, a pointer to the
+ non paged SCB.
+
+ SourceAddressLength - Length of the originator of the datagram.
+
+ SourceAddress - String describing the originator of the datagram.
+
+ OptionsLength - Length of the buffer pointed to by Options.
+
+ Options - Options for the receive.
+
+ ReceiveDatagramFlags - Ignored.
+
+ BytesIndicated - Number of bytes this indication.
+
+ BytesAvailable - Number of bytes in complete Tsdu.
+
+ BytesTaken - Returns the number of bytes used.
+
+ Tsdu - Pointer describing this TSDU, typically a lump of bytes.
+
+ IoRequestPacket - TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ PNONPAGED_SCB pNpScb = (PNONPAGED_SCB)TdiEventContext;
+ PUCHAR RspData = (PUCHAR)Tsdu;
+
+ *IoRequestPacket = NULL;
+
+
+ //
+ // Transport will complete the processing of the request, we don't
+ // want the datagram.
+ //
+
+
+ DebugTrace(+1, Dbg, "WatchDogDatagramHandler\n", 0);
+ DebugTrace(+0, Dbg, "SourceAddressLength %x\n", SourceAddressLength);
+ DebugTrace(+0, Dbg, "BytesIndicated %x\n", BytesIndicated);
+ DebugTrace(+0, Dbg, "BytesAvailable %x\n", BytesAvailable);
+ DebugTrace(+0, Dbg, "BytesTaken %x\n", *BytesTaken);
+ //
+ // SourceAddress is the address of the server or the bridge tbat sent
+ // the packet.
+ //
+
+#if NWDBG
+ dump( Dbg, SourceAddress, SourceAddressLength );
+ dump( Dbg, Tsdu, BytesIndicated );
+#endif
+
+ if (pNpScb->NodeTypeCode != NW_NTC_SCBNP ) {
+ DebugTrace(+0, 0, "nwrdr: Invalid Watchdog Indication %x\n", pNpScb );
+#if DBG
+ DbgBreakPoint();
+#endif
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+ Stats.NcpsReceived.QuadPart++;
+ Stats.BytesReceived.QuadPart += BytesAvailable;
+
+ if ( RspData[1] == NCP_SEARCH_CONTINUE ) {
+ PIRP pIrp;
+ PIRP_CONTEXT pIrpContext;
+
+ pIrp = ALLOCATE_IRP( pNpScb->WatchDog.pDeviceObject->StackSize, FALSE);
+ if (pIrp == NULL) {
+ DebugTrace(-1, Dbg, " %lx\n", STATUS_DATA_NOT_ACCEPTED);
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+ try {
+ pIrpContext = AllocateIrpContext( pIrp );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_IRP( pIrp );
+ DebugTrace(-1, Dbg, " %lx\n", STATUS_DATA_NOT_ACCEPTED);
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+
+ pIrpContext->req[0] = pNpScb->ConnectionNo;
+
+ //
+ // Response 'Y' or connection is valid and its from the right server,
+ // or 'N' if it is not.
+ //
+
+ if (( RspData[0] == pNpScb->ConnectionNo ) &&
+ ( RtlCompareMemory(
+ ((PTA_IPX_ADDRESS)SourceAddress)->Address[0].Address,
+ &pNpScb->ServerAddress,
+ 8) == 8 ))
+ {
+ LARGE_INTEGER KillTime, Now;
+ BOOL ScbIsOld ;
+
+ //
+ // Check if this is a not-logged-in SCB that has not been used
+ // for while. If it is, answer NO. In attach.c, we dont disconnect
+ // from a nearest server immediately to avoid the re-connect
+ // overheads. This is where we time the sucker out.
+ //
+
+ KeQuerySystemTime( &Now );
+ KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME);
+
+ ScbIsOld = ((pNpScb->State == SCB_STATE_LOGIN_REQUIRED) &&
+ (pNpScb->LastUsedTime.QuadPart < KillTime.QuadPart)) ;
+
+
+ pIrpContext->req[1] = ScbIsOld ? 'N' : 'Y';
+
+ if (ScbIsOld)
+ {
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED ;
+ }
+
+ DebugTrace(-1,Dbg,"WatchDog Response: %s\n", ScbIsOld ? "N" : "Y");
+
+ } else {
+
+ pIrpContext->req[1] = 'N';
+ }
+
+ pIrpContext->TxMdl->ByteCount = 2;
+
+ pIrpContext->ConnectionInformation.UserDataLength = 0;
+ pIrpContext->ConnectionInformation.OptionsLength = sizeof( UCHAR );
+ pIrpContext->ConnectionInformation.Options = &SapPacketType;
+ pIrpContext->ConnectionInformation.RemoteAddressLength = sizeof(TA_IPX_ADDRESS);
+ pIrpContext->ConnectionInformation.RemoteAddress = &pIrpContext->Destination;
+
+ BuildIpxAddress(
+ ((PTA_IPX_ADDRESS)SourceAddress)->Address[0].Address[0].NetworkAddress,
+ ((PTA_IPX_ADDRESS)SourceAddress)->Address[0].Address[0].NodeAddress,
+ ((PTA_IPX_ADDRESS)SourceAddress)->Address[0].Address[0].Socket,
+ &pIrpContext->Destination);
+
+ TdiBuildSendDatagram(
+ pIrpContext->pOriginalIrp,
+ pNpScb->WatchDog.pDeviceObject,
+ pNpScb->WatchDog.pFileObject,
+ &CompletionWatchDogSend,
+ pIrpContext,
+ pIrpContext->TxMdl,
+ MdlLength(pIrpContext->TxMdl),
+ &pIrpContext->ConnectionInformation);
+
+ IoCallDriver(
+ pNpScb->WatchDog.pDeviceObject,
+ pIrpContext->pOriginalIrp );
+ }
+
+ DebugTrace(-1, Dbg, " %lx\n", STATUS_DATA_NOT_ACCEPTED);
+ return STATUS_DATA_NOT_ACCEPTED;
+
+ UNREFERENCED_PARAMETER( SourceAddressLength );
+ UNREFERENCED_PARAMETER( BytesIndicated );
+ UNREFERENCED_PARAMETER( BytesAvailable );
+ UNREFERENCED_PARAMETER( BytesTaken );
+ UNREFERENCED_PARAMETER( Tsdu );
+ UNREFERENCED_PARAMETER( OptionsLength );
+ UNREFERENCED_PARAMETER( Options );
+}
+
+
+NTSTATUS
+CompletionWatchDogSend(
+ 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 IrpContext associated with the Irp.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+
+ PIRP_CONTEXT pIrpC = (PIRP_CONTEXT) Context;
+
+ //
+ // Avoid completing the Irp because the Mdl etc. do not contain
+ // their original values.
+ //
+
+ DebugTrace( +1, Dbg, "CompletionWatchDogSend\n", 0);
+ DebugTrace( +0, Dbg, "Irp %X\n", Irp);
+ DebugTrace( -1, Dbg, "pIrpC %X\n", pIrpC);
+
+ FREE_IRP( pIrpC->pOriginalIrp );
+
+ pIrpC->pOriginalIrp = NULL; // Avoid FreeIrpContext modifying freed Irp.
+
+ FreeIrpContext( pIrpC );
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+ UNREFERENCED_PARAMETER( Irp );
+}
+
+
+NTSTATUS
+SendDatagramHandler(
+ IN PVOID TdiEventContext,
+ IN int SourceAddressLength,
+ IN PVOID SourceAddress,
+ IN int OptionsLength,
+ IN PVOID Options,
+ IN ULONG ReceiveDatagramFlags,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PVOID Tsdu,
+ OUT PIRP *IoRequestPacket
+ )
+/*++
+
+Routine Description:
+
+ This routine is the receive datagram event indication handler for the
+ Server socket.
+
+Arguments:
+
+ TdiEventContext - Context provided for this event, a pointer to the
+ non paged SCB.
+
+ SourceAddressLength - Length of the originator of the datagram.
+
+ SourceAddress - String describing the originator of the datagram.
+
+ OptionsLength - Length of the buffer pointed to by Options.
+
+ Options - Options for the receive.
+
+ ReceiveDatagramFlags - Ignored.
+
+ BytesIndicated - Number of bytes this indication.
+
+ BytesAvailable - Number of bytes in complete Tsdu.
+
+ BytesTaken - Returns the number of bytes used.
+
+ Tsdu - Pointer describing this TSDU, typically a lump of bytes.
+
+ IoRequestPacket - TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+
+{
+ PNONPAGED_SCB pNpScb = (PNONPAGED_SCB)TdiEventContext;
+ PUCHAR RspData = (PUCHAR)Tsdu;
+ PIRP_CONTEXT pIrpContext;
+ PLIST_ENTRY listEntry;
+ PIRP Irp;
+
+ *IoRequestPacket = NULL;
+
+ DebugTrace(0, Dbg, "SendDatagramHandler\n", 0);
+
+ Stats.NcpsReceived.QuadPart++;
+ Stats.BytesReceived.QuadPart += BytesAvailable;
+
+ //
+ // Transport will complete the processing of the request, we don't
+ // want the datagram.
+ //
+
+ DebugTrace(+1, Dbg, "SendDatagramHandler\n", 0);
+ DebugTrace(+0, Dbg, "SourceAddressLength %x\n", SourceAddressLength);
+ DebugTrace(+0, Dbg, "BytesIndicated %x\n", BytesIndicated);
+ DebugTrace(+0, Dbg, "BytesAvailable %x\n", BytesAvailable);
+ DebugTrace(+0, Dbg, "BytesTaken %x\n", *BytesTaken);
+
+ //
+ // SourceAddress is the address of the server or the bridge tbat sent
+ // the packet.
+ //
+
+#if NWDBG
+ dump( Dbg, SourceAddress, SourceAddressLength );
+ dump( Dbg, Tsdu, BytesIndicated );
+#endif
+
+ if (pNpScb->NodeTypeCode != NW_NTC_SCBNP ) {
+ DebugTrace(+0, Dbg, "nwrdr: Invalid SendDatagram Indication %x\n", pNpScb );
+#if DBG
+ DbgBreakPoint();
+#endif
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+ if (RspData[1] == BROADCAST_MESSAGE_WAITING ) {
+
+ //
+ // Broadcast message waiting. If the scavenger
+ // isn't running, it's safe to go get it.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &NwScavengerSpinLock );
+
+ if ( WorkerRunning ) {
+
+ //
+ // The scavenger is running, we can't pick up this
+ // message until the scavenger is done!
+ //
+
+ DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Delaying get message for scavenger.\n", 0 );
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+
+ } else {
+
+ //
+ // Make sure the scavenger doesn't start.
+ //
+
+ WorkerRunning = TRUE;
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+
+ listEntry = ExInterlockedRemoveHeadList(
+ &NwGetMessageList,
+ &NwMessageSpinLock );
+
+ if ( listEntry != NULL ) {
+
+ pIrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
+
+ //
+ // Clear the cancel routine for this IRP.
+ //
+
+ Irp = pIrpContext->pOriginalIrp;
+
+ IoAcquireCancelSpinLock( &Irp->CancelIrql );
+ IoSetCancelRoutine( Irp, NULL );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ pIrpContext->PostProcessRoutine = FspGetMessage;
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pNpScb->pScb;
+
+ NwPostToFsp( pIrpContext, TRUE );
+
+ } else {
+
+ WorkerRunning = FALSE;
+ }
+ }
+
+ }
+
+ DebugTrace(-1, Dbg, " %lx\n", STATUS_DATA_NOT_ACCEPTED);
+ return STATUS_DATA_NOT_ACCEPTED;
+
+ UNREFERENCED_PARAMETER( SourceAddressLength );
+ UNREFERENCED_PARAMETER( BytesIndicated );
+ UNREFERENCED_PARAMETER( BytesAvailable );
+ UNREFERENCED_PARAMETER( BytesTaken );
+ UNREFERENCED_PARAMETER( Tsdu );
+ UNREFERENCED_PARAMETER( OptionsLength );
+ UNREFERENCED_PARAMETER( Options );
+}
+
+
+NTSTATUS
+FspGetMessage(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine continues process a broadcast message waiting message.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+
+ UNICODE_STRING Message;
+ NTSTATUS Status;
+ PNWR_SERVER_MESSAGE ServerMessage;
+ PUNICODE_STRING ServerName;
+ ULONG MessageLength;
+ int i;
+
+ PAGED_CODE();
+
+ NwReferenceUnlockableCodeSection();
+
+ //
+ // The Scb may be being deleted so carefully walk the list and reference it if
+ // we find it.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = ScbQueue.Flink;
+
+ while ( ScbQueueEntry != &ScbQueue ) {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ if (pNpScb == IrpContext->pNpScb ) {
+
+ NwReferenceScb( pNpScb );
+
+ break;
+ }
+
+ ScbQueueEntry = ScbQueueEntry->Flink;
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ if (pNpScb != IrpContext->pNpScb ) {
+
+ //
+ // Server deleted. Its easiest to continue processing the IrpContext
+ // with an error than try to recover it and return it to the queue.
+ //
+
+ Status = STATUS_UNSUCCESSFUL;
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+ }
+
+ //
+ // If the message is telling us that the server is going down then don't
+ // work too hard trying to get the message. The server is persistent with
+ // respect to other messages so we'll come through here again when the
+ // problem has been resolved.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ if ( UP_LEVEL_SERVER( IrpContext->pScb ) ) {
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "S",
+ NCP_MESSAGE_FUNCTION, NCP_GET_ENTIRE_MESSAGE );
+ } else {
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "S",
+ NCP_MESSAGE_FUNCTION, NCP_GET_MESSAGE );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwDereferenceScb( pNpScb );
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+ }
+
+ ServerMessage = (PNWR_SERVER_MESSAGE)IrpContext->Specific.FileSystemControl.Buffer;
+ MessageLength = IrpContext->Specific.FileSystemControl.Length;
+
+ ServerName = &IrpContext->pNpScb->ServerName;
+ if ( ServerName->Length + FIELD_OFFSET( NWR_SERVER_MESSAGE, Server ) + sizeof(WCHAR) > MessageLength ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+ NwDereferenceScb( pNpScb );
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+
+ } else {
+
+ //
+ // Copy the server name to the output buffer.
+ //
+
+ ServerMessage->MessageOffset =
+ ServerName->Length +
+ FIELD_OFFSET( NWR_SERVER_MESSAGE, Server ) +
+ sizeof(WCHAR);
+
+ RtlMoveMemory(
+ ServerMessage->Server,
+ ServerName->Buffer,
+ ServerName->Length );
+
+ ServerMessage->Server[ ServerName->Length / sizeof(WCHAR) ] = L'\0';
+ }
+
+ //
+ // Copy the message to the user's buffer.
+ //
+
+ Message.Buffer = &ServerMessage->Server[ ServerName->Length / sizeof(WCHAR) ] + 1;
+ Message.MaximumLength = (USHORT)( MessageLength - ( ServerName->Length + FIELD_OFFSET( NWR_SERVER_MESSAGE, Server ) + sizeof(WCHAR) ) );
+
+ if ( NT_SUCCESS( Status) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "NP",
+ &Message );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwDereferenceScb( pNpScb );
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+ }
+
+ //
+ // Strip the trailing spaces and append a NUL terminator to the message.
+ //
+
+ for ( i = Message.Length / sizeof(WCHAR) - 1; i >= 0 ; i-- ) {
+ if ( Message.Buffer[ i ] != L' ') {
+ Message.Length = (i + 1) * sizeof(WCHAR);
+ break;
+ }
+ }
+
+ if ( Message.Length > 0 ) {
+ Message.Buffer[ Message.Length / sizeof(WCHAR) ] = L'\0';
+ }
+
+ IrpContext->pOriginalIrp->IoStatus.Information =
+ ServerName->Length +
+ FIELD_OFFSET( NWR_SERVER_MESSAGE, Server ) + sizeof(WCHAR) +
+ Message.Length + sizeof(WCHAR);
+
+ NwDereferenceScb( pNpScb );
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+}
+
+
+NTSTATUS
+_cdecl
+ExchangeWithWait(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine sends a NCP packet and waits for the response.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ pEX, Context, f - See _Exchange
+
+Return Value:
+
+ NTSTATUS - Status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+ va_list Arguments;
+
+ PAGED_CODE();
+
+ //KeResetEvent( &pIrpContext->Event );
+
+ va_start( Arguments, f );
+
+ Status = FormatRequest( pIrpContext, pEx, f, Arguments );
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ va_end( Arguments );
+
+ Status = PrepareAndSendPacket( pIrpContext );
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = KeWaitForSingleObject(
+ &pIrpContext->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = pIrpContext->pOriginalIrp->IoStatus.Status;
+
+ if ( NT_SUCCESS( Status ) &&
+ pIrpContext->PacketType != SAP_BROADCAST ) {
+ Status = NwErrorToNtStatus( pIrpContext->ResponseParameters.Error );
+ }
+
+ return( Status );
+}
+
+BOOLEAN
+VerifyResponse(
+ PIRP_CONTEXT pIrpContext,
+ PVOID Response
+ )
+/*++
+
+Routine Description:
+
+ This routine verifies that a received response is the expected
+ response for the current request.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ Response - A pointer to the buffer containing the response.
+
+Return Value:
+
+ TRUE - This is a valid response.
+ FALSE - This is an invalid response.
+
+--*/
+
+{
+ PNCP_RESPONSE pNcpResponse;
+ PNONPAGED_SCB pNpScb;
+
+ pNcpResponse = (PNCP_RESPONSE)Response;
+ pNpScb = pIrpContext->pNpScb;
+
+ if ( pNcpResponse->NcpHeader.ConnectionIdLow != pNpScb->ConnectionNo ) {
+ DebugTrace(+0, Dbg, "VerifyResponse, bad connection number\n", 0);
+
+ return( FALSE );
+ }
+
+ if ( pNcpResponse->NcpHeader.SequenceNumber != pNpScb->SequenceNo ) {
+ DebugTrace(+1, Dbg, "VerifyResponse, bad sequence number %x\n", 0);
+ DebugTrace(+0, Dbg, " pNcpResponse->NcpHeader.SequenceNumber %x\n",
+ pNcpResponse->NcpHeader.SequenceNumber);
+ DebugTrace(-1, Dbg, " pNpScb->SequenceNo %x\n", pNpScb->SequenceNo );
+
+ return( FALSE );
+ }
+
+ return( TRUE );
+}
+
+VOID
+ScheduleReconnectRetry(
+ PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine schedules an a reconnect attempt, and then resubmits
+ our request if the reconnect was successful.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PWORK_QUEUE_ITEM WorkItem;
+
+ WorkItem = ALLOCATE_POOL( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) );
+
+ if ( WorkItem == NULL ) {
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+ }
+
+ pIrpContext->pWorkItem = WorkItem;
+ ExInitializeWorkItem( WorkItem, ReconnectRetry, pIrpContext );
+ ExQueueWorkItem( WorkItem, DelayedWorkQueue );
+
+ return;
+}
+
+VOID
+ReconnectRetry(
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to reconnect to a disconnected server. If it
+ is successful it resubmits an existing request.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PIRP_CONTEXT pNewIrpContext;
+ PSCB pScb, pNewScb;
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ pNpScb = pIrpContext->pNpScb;
+ pScb = pNpScb->pScb;
+
+ Stats.Reconnects++;
+
+ if ( pScb == NULL ) {
+ pScb = pNpScb->pScb;
+ pIrpContext->pScb = pScb;
+ }
+
+ //
+ // Free the work item
+ //
+
+ FREE_POOL( pIrpContext->pWorkItem );
+
+ //
+ // Allocate a temporary IRP context to use to reconnect to the server
+ //
+
+ if ( !NwAllocateExtraIrpContext( &pNewIrpContext, pNpScb ) ) {
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+ return;
+ }
+
+ pNewIrpContext->Specific.Create.UserUid = pScb->UserUid;
+ pNewIrpContext->pNpScb = pNpScb;
+ pNewIrpContext->pScb = pScb;
+
+ //
+ // Reset the sequence numbers.
+ //
+
+ pNpScb->SequenceNo = 0;
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ //
+ // Now insert this new IrpContext to the head of the SCB queue for
+ // processing. We can get away with this because we own the IRP context
+ // currently at the front of the queue. With the RECONNECT_ATTEMPT
+ // flag set, ConnectScb() will not remove us from the head of the queue.
+ //
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+
+ ExInterlockedInsertHeadList(
+ &pNpScb->Requests,
+ &pNewIrpContext->NextRequest,
+ &pNpScb->NpScbSpinLock );
+
+ pNewScb = pNpScb->pScb;
+
+ Status = ConnectScb( &pNewScb,
+ pNewIrpContext,
+ &pNpScb->ServerName,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ FALSE,
+ TRUE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Couldn't reconnect. Free the extra IRP context, complete the
+ // original request with an error.
+ //
+
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+ NwFreeExtraIrpContext( pNewIrpContext );
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+ return;
+ }
+
+ ASSERT( pNewScb == pScb );
+
+ //
+ // Try to reconnect the VCBs.
+ //
+
+ NwReopenVcbHandlesForScb( pNewIrpContext, pScb );
+
+ //
+ // Dequeue and free the bonus IRP context.
+ //
+
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+ NwFreeExtraIrpContext( pNewIrpContext );
+
+ //
+ // Resubmit the original request, with a new sequence number. Note that
+ // it's back at the front of the queue, but no longer reconnectable.
+ //
+
+ pIrpContext->req[2] = pNpScb->SequenceNo;
+ pIrpContext->req[3] = pNpScb->ConnectionNo;
+ pIrpContext->req[5] = pNpScb->ConnectionNoHigh;
+
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+ SendNow( pIrpContext );
+
+ return;
+}
+
+
+NTSTATUS
+NewRouteRetry(
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to establish a new route to a non-responding server.
+ If it is successful it resubmits the request in progress.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ PNONPAGED_SCB pNpScb = pIrpContext->pNpScb;
+ LARGE_INTEGER CurrentTime = {0, 0};
+
+ PAGED_CODE();
+
+ //
+ // Don't bother to re-rip if we are shutting down.
+ //
+
+ if ( NwRcb.State != RCB_STATE_SHUTDOWN ) {
+ Status = GetNewRoute( pIrpContext );
+ } else {
+ Status = STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // Ask the transport to establish a new route to the server.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Attempt to get new route failed, fail the current request.
+ //
+
+ pIrpContext->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+
+ if ( pNpScb != &NwPermanentNpScb ) {
+
+
+ KeQuerySystemTime( &CurrentTime );
+
+ if ( CanLogTimeOutEvent( pNpScb->NwNextEventTime,
+ CurrentTime
+ )) {
+ Error(
+ EVENT_NWRDR_TIMEOUT,
+ STATUS_UNEXPECTED_NETWORK_ERROR,
+ NULL,
+ 0,
+ 1,
+ pNpScb->ServerName.Buffer );
+
+ //
+ // Set the LastEventTime to the CurrentTime
+ //
+
+ UpdateNextEventTime(
+ pNpScb->NwNextEventTime,
+ CurrentTime,
+ TimeOutEventInterval
+ );
+
+ }
+
+
+ pNpScb->State = SCB_STATE_ATTACHING;
+ }
+
+ } else {
+
+ //
+ // Got a new route, resubmit the request. Allow retries
+ // with the new route.
+ //
+
+ pIrpContext->pNpScb->RetryCount = DefaultRetryCount / 2;
+
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+ SendNow( pIrpContext );
+ }
+
+ //
+ // Return STATUS_PENDING so that the FSP dispatcher doesn't complete
+ // this request.
+ //
+
+ return( STATUS_PENDING );
+}
+
+
+NTSTATUS
+NewRouteBurstRetry(
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to establish a new route to a non-responding server.
+ If it is successful it resubmits the request in progress.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT pNewIrpContext;
+ PNONPAGED_SCB pNpScb = pIrpContext->pNpScb;
+ BOOLEAN LIPNegotiated ;
+ LARGE_INTEGER CurrentTime = {0, 0};
+
+ PAGED_CODE();
+
+ //
+ // Don't bother to re-rip if we are shutting down.
+ //
+
+ if ( NwRcb.State == RCB_STATE_SHUTDOWN ) {
+ return( STATUS_REMOTE_NOT_LISTENING );
+ }
+
+ //
+ // Ask the transport to establish a new route to the server.
+ //
+
+ Status = GetNewRoute( pIrpContext );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // If this is a burst write, we must first complete the write
+ // request (there is no way to tell the server to abandon the write).
+ //
+ // Set packet size down to 512 to guarantee that the packets will be
+ // forwarded, and resend the burst data. Queue the new IRP context
+ // behind the burst write, so that we can establish a new burst
+ // connection.
+ //
+ // Note that ResubmitBurstWrite may complete the request and
+ // free the IrpContext.
+ //
+
+ pNpScb->RetryCount = DefaultRetryCount / 2;
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_BURST_WRITE ) ) {
+
+ Status = ResubmitBurstWrite( pIrpContext );
+
+ } else {
+
+ //
+ // Allocate a temporary IRP context to use to reconnect to the server
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+ if ( !NwAllocateExtraIrpContext( &pNewIrpContext, pNpScb ) ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+ pNewIrpContext->Specific.Create.UserUid = pIrpContext->Specific.Create.UserUid;
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+
+ //
+ // Since we're doing this from a worker thread, we can't
+ // let the dpc timer schedule _another_ worker thread
+ // request if this also times out or we may deadlock
+ // the delayed work queue.
+ //
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ pNewIrpContext->pNpScb = pNpScb;
+
+ }
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Insert this new IrpContext to the head of
+ // the SCB queue for processing. We can get away with this
+ // because we own the IRP context currently at the front of
+ // the queue.
+ //
+
+ ExInterlockedInsertHeadList(
+ &pNpScb->Requests,
+ &pNewIrpContext->NextRequest,
+ &pNpScb->NpScbSpinLock );
+
+ //
+ // Now prepare to resend the burst read.
+ //
+
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+
+ //
+ // Renegotiate the burst connection, this will automatically re-sync
+ // the burst connection.
+ //
+ // BUGBUG: We lose sizeof( NCP_BURST_WRITE_REQUEST ) each time
+ // we do this right now.
+ //
+
+ NegotiateBurstMode( pNewIrpContext, pNpScb, &LIPNegotiated );
+
+ //
+ // Reset the sequence numbers.
+ //
+
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ //
+ // Dequeue and free the bonus IRP context.
+ //
+
+ ASSERT( pNpScb->Requests.Flink == &pNewIrpContext->NextRequest );
+
+ ExInterlockedRemoveHeadList(
+ &pNpScb->Requests,
+ &pNpScb->NpScbSpinLock );
+
+ ClearFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ NwFreeExtraIrpContext( pNewIrpContext );
+
+ //
+ // Got a new route, resubmit the request
+ //
+
+ Status = ResubmitBurstRead( pIrpContext );
+ }
+ }
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Attempt to get new route failed, fail the current request.
+ //
+
+ pIrpContext->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+
+ if ( pNpScb != &NwPermanentNpScb ) {
+
+
+ KeQuerySystemTime( &CurrentTime );
+
+ if ( CanLogTimeOutEvent( pNpScb->NwNextEventTime,
+ CurrentTime
+ )) {
+ Error(
+ EVENT_NWRDR_TIMEOUT,
+ STATUS_UNEXPECTED_NETWORK_ERROR,
+ NULL,
+ 0,
+ 1,
+ pNpScb->ServerName.Buffer );
+
+ //
+ // Set the LastEventTime to the CurrentTime
+ //
+
+ UpdateNextEventTime(
+ pNpScb->NwNextEventTime,
+ CurrentTime,
+ TimeOutEventInterval
+ );
+
+ }
+
+ }
+ }
+
+ //
+ // Return STATUS_PENDING so that the FSP dispatcher doesn't complete
+ // this request.
+ //
+
+ return( STATUS_PENDING );
+}
+
+NTSTATUS
+FspProcessServerDown(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine process a response with the server shutdown bit set.
+ It close all open handles for the server, and puts the server in
+ the attaching state.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ STATUS_PENDING.
+
+--*/
+{
+ KIRQL OldIrql;
+
+ PNONPAGED_SCB pNpScb = IrpContext->pNpScb;
+
+ //
+ // Avoid the Scb from disappearing under us.
+ //
+
+ NwReferenceScb( pNpScb );
+
+ //
+ // Move the IrpContext from the front of the queue just in-case it
+ // owns the Rcb.
+ //
+
+ KeAcquireSpinLock( &IrpContext->pNpScb->NpScbSpinLock, &OldIrql );
+
+ if ( IrpContext->pNpScb->Sending ) {
+
+ //
+ // Let send completion call the pEx routine
+ //
+
+ IrpContext->pNpScb->Received = TRUE;
+ KeReleaseSpinLock( &IrpContext->pNpScb->NpScbSpinLock, OldIrql );
+
+ } else {
+
+ IrpContext->pNpScb->Receiving = FALSE;
+ IrpContext->pNpScb->Received = FALSE;
+ KeReleaseSpinLock( &IrpContext->pNpScb->NpScbSpinLock, OldIrql );
+
+ //
+ // Now call the callback routine.
+ //
+
+ IrpContext->pEx(
+ IrpContext,
+ IrpContext->ResponseLength,
+ IrpContext->rsp );
+
+ }
+
+ //
+ // Close all active handles for this server.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ NwInvalidateAllHandlesForScb( pNpScb->pScb );
+ NwReleaseRcb( &NwRcb );
+
+ NwDereferenceScb( pNpScb );
+
+ //
+ // Return STATUS_PENDING so that the FSP process doesn't complete
+ // this request.
+ //
+
+ return( STATUS_PENDING );
+}
+
+
+VOID
+NwProcessSendBurstFailure(
+ PNONPAGED_SCB NpScb,
+ USHORT MissingFragmentCount
+ )
+/*++
+
+Routine Description:
+
+ This routine adjust burst parameters after an unsuccessful burst operation.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has experienced a burst failure.
+
+ MissingFragmentCount - A measure of how many chunks were lost.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG temp;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Burst failure, NpScb = %X\n", NpScb );
+
+ if ( NpScb->NwSendDelay != NpScb->CurrentBurstDelay ) {
+
+ //
+ // This burst has already failed
+ //
+
+ return;
+ }
+
+ NpScb->NwBadSendDelay = NpScb->NwSendDelay;
+
+ //
+ // Add to the send delay. Never let it go above 5000ms.
+ //
+
+ temp = NpScb->NwGoodSendDelay - NpScb->NwBadSendDelay;
+
+ if (temp >= 0) {
+ NpScb->NwSendDelay += temp + 2;
+ } else {
+ NpScb->NwSendDelay += -temp + 2;
+ }
+
+ if ( NpScb->NwSendDelay > NpScb->NwMaxSendDelay ) {
+
+ NpScb->NwSendDelay = NpScb->NwMaxSendDelay;
+
+ //
+ // If we have slowed down a lot then it might be that the server or a
+ // bridge only has a small buffer on its NIC. If this is the case then
+ // rather than sending a big burst with long even gaps between the
+ // packets, we should try to send a burst the size of the buffer.
+ //
+
+ if ( !DontShrink ) {
+
+ if (((NpScb->MaxSendSize - 1) / NpScb->MaxPacketSize) > 2 ) {
+
+ // Round down to the next packet
+
+ NpScb->MaxSendSize = ((NpScb->MaxSendSize - 1) / NpScb->MaxPacketSize) * NpScb->MaxPacketSize;
+
+ //
+ // Adjust SendDelay below threshold to see if things improve before
+ // we shrink the size again.
+ //
+
+ NpScb->NwSendDelay = NpScb->NwGoodSendDelay = NpScb->NwBadSendDelay = MinSendDelay;
+
+ } else {
+
+ //
+ // We reached the minimum size with the maximum delay. Give up on burst.
+ //
+
+ NpScb->SendBurstModeEnabled = FALSE;
+
+ }
+
+ }
+ }
+
+ NpScb->NtSendDelay.QuadPart = NpScb->NwSendDelay * -1000 ;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "New Send Delay = %d\n", NpScb->NwSendDelay );
+
+ NpScb->SendBurstSuccessCount = 0;
+
+}
+
+
+VOID
+NwProcessReceiveBurstFailure(
+ PNONPAGED_SCB NpScb,
+ USHORT MissingFragmentCount
+ )
+/*++
+
+Routine Description:
+
+ This routine adjust burst parameters after an unsuccessful burst operation.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has experienced a burst failure.
+
+ MissingFragmentCount - A measure of how many chunks were lost.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG temp;
+
+ DebugTrace(+0, DEBUG_TRACE_LIP, "Burst failure, NpScb = %X\n", NpScb );
+
+ if ( NpScb->NwReceiveDelay != NpScb->CurrentBurstDelay ) {
+
+ //
+ // This burst has already failed
+ //
+
+ return;
+ }
+
+ NpScb->NwBadReceiveDelay = NpScb->NwReceiveDelay;
+
+ //
+ // Add to the Receive delay. Never let it go above 5000ms.
+ //
+
+ temp = NpScb->NwGoodReceiveDelay - NpScb->NwBadReceiveDelay;
+
+ if (temp >= 0) {
+ NpScb->NwReceiveDelay += temp + 2;
+ } else {
+ NpScb->NwReceiveDelay += -temp + 2;
+ }
+
+
+ if ( NpScb->NwReceiveDelay > NpScb->NwMaxReceiveDelay ) {
+
+ NpScb->NwReceiveDelay = MaxReceiveDelay;
+
+ //
+ // If we have slowed down a lot then it might be that the server or a
+ // bridge only has a small buffer on its NIC. If this is the case then
+ // rather than Receiveing a big burst with long even gaps between the
+ // packets, we should try to Receive a burst the size of the buffer.
+ //
+
+ if ( !DontShrink ) {
+
+ if (((NpScb->MaxReceiveSize - 1) / NpScb->MaxPacketSize) > 2 ) {
+
+ // Round down to the next packet
+
+ NpScb->MaxReceiveSize = ((NpScb->MaxReceiveSize - 1) / NpScb->MaxPacketSize) * NpScb->MaxPacketSize;
+
+ //
+ // Adjust ReceiveDelay below threshold to see if things improve before
+ // we shrink the size again.
+ //
+
+ NpScb->NwReceiveDelay = NpScb->NwGoodReceiveDelay = NpScb->NwBadReceiveDelay = MinReceiveDelay;
+
+ } else {
+
+ //
+ // We reached the minimum size with the maximum delay. Give up on burst.
+ //
+
+ NpScb->ReceiveBurstModeEnabled = FALSE;
+
+ }
+
+ }
+
+ }
+
+ NpScb->ReceiveBurstSuccessCount = 0;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "New Receive Delay = %d\n", NpScb->NwReceiveDelay );
+}
+
+
+VOID
+NwProcessSendBurstSuccess(
+ PNONPAGED_SCB NpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine adjust burst parameters after a successful burst operation.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has completed the burst.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG temp;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Successful burst, NpScb = %X\n", NpScb );
+
+ if ( NpScb->NwSendDelay != NpScb->CurrentBurstDelay ) {
+
+ //
+ // This burst has already failed
+ //
+
+ return;
+ }
+
+ if ( NpScb->SendBurstSuccessCount > BurstSuccessCount ) {
+
+ if (NpScb->NwSendDelay != MinSendDelay ) {
+
+ NpScb->NwGoodSendDelay = NpScb->NwSendDelay;
+
+ temp = NpScb->NwGoodSendDelay - NpScb->NwBadSendDelay;
+
+ if (temp >= 0) {
+ NpScb->NwSendDelay -= 1 + temp;
+ } else {
+ NpScb->NwSendDelay -= 1 - temp;
+ }
+
+ if (NpScb->NwSendDelay < MinSendDelay ) {
+
+ NpScb->NwSendDelay = MinSendDelay;
+
+ }
+
+ NpScb->NtSendDelay.QuadPart = NpScb->NwSendDelay * -1000;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "New Send Delay = %d\n", NpScb->NwSendDelay );
+
+ //
+ // Start monitoring success at the new rate.
+ //
+
+ NpScb->SendBurstSuccessCount = 0;
+
+ } else if ( NpScb->SendBurstSuccessCount > BurstSuccessCount2 ) {
+
+ //
+ // We may have had a really bad patch causing BadSendDelay to be very big.
+ // If we leave it at its current value then at the first sign of trouble
+ // we will make SendDelay very big
+ //
+
+ NpScb->NwGoodSendDelay = NpScb->NwBadSendDelay = NpScb->NwSendDelay;
+
+ //
+ // Is it time to increase the number of packets in the burst?
+ // AllowGrowth == 0 to be the same as the VLM client.
+ //
+
+ if (( AllowGrowth ) &&
+ ( NpScb->NwSendDelay <= MinSendDelay ) &&
+ ( NpScb->MaxSendSize < NwMaxSendSize)) {
+
+ NpScb->MaxSendSize += NpScb->MaxPacketSize;
+
+
+ if ( NpScb->MaxSendSize > NwMaxSendSize) {
+
+ NpScb->MaxSendSize = NwMaxSendSize;
+
+ }
+ }
+
+ NpScb->SendBurstSuccessCount = 0;
+
+ } else {
+
+ NpScb->SendBurstSuccessCount++;
+
+ }
+
+
+ } else {
+
+ NpScb->SendBurstSuccessCount++;
+
+ }
+
+}
+
+
+VOID
+NwProcessReceiveBurstSuccess(
+ PNONPAGED_SCB NpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine adjust burst parameters after a successful burst operation.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has completed the burst.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG temp;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Successful burst, NpScb = %X\n", NpScb );
+
+ if ( NpScb->NwReceiveDelay != NpScb->CurrentBurstDelay ) {
+
+ //
+ // This burst has already failed
+ //
+
+ return;
+ }
+
+ if ( NpScb->ReceiveBurstSuccessCount > BurstSuccessCount ) {
+
+ //
+ // Once the vlm client reaches the Maximum delay it does not
+ // shrink again.
+ //
+
+ if ( NpScb->NwReceiveDelay != MinReceiveDelay ) {
+
+ NpScb->NwGoodReceiveDelay = NpScb->NwReceiveDelay;
+
+ temp = NpScb->NwGoodReceiveDelay - NpScb->NwBadReceiveDelay;
+
+ if (temp >= 0) {
+ NpScb->NwReceiveDelay -= 1 + temp;
+ } else {
+ NpScb->NwReceiveDelay -= 1 - temp;
+ }
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "New Receive Delay = %d\n", NpScb->NwReceiveDelay );
+
+
+ if (NpScb->NwReceiveDelay < MinReceiveDelay ) {
+ NpScb->NwReceiveDelay = MinReceiveDelay;
+
+ }
+
+ //
+ // Start monitoring success at the new rate.
+ //
+
+ NpScb->ReceiveBurstSuccessCount = 0;
+
+ } else if ( NpScb->ReceiveBurstSuccessCount > BurstSuccessCount2 ) {
+
+ //
+ // We may have had a really bad patch causing BadReceiveDelay to be very big.
+ // If we leave it at its current value then at the first sign of trouble
+ // we will make ReceiveDelay very big
+ //
+
+ NpScb->NwGoodReceiveDelay = NpScb->NwBadReceiveDelay = NpScb->NwReceiveDelay;
+
+
+ //
+ // Is it time to increase the number of packets in the burst?
+ //
+
+ if (( AllowGrowth ) &&
+ ( NpScb->NwReceiveDelay <= MinReceiveDelay ) &&
+ ( NpScb->MaxReceiveSize < NwMaxReceiveSize)) {
+
+ NpScb->MaxReceiveSize += NpScb->MaxPacketSize;
+
+
+ if ( NpScb->MaxReceiveSize > NwMaxReceiveSize) {
+
+ NpScb->MaxReceiveSize = NwMaxReceiveSize;
+
+ }
+ }
+
+ NpScb->ReceiveBurstSuccessCount = 0;
+
+ } else {
+
+ NpScb->ReceiveBurstSuccessCount++;
+
+ }
+
+ } else {
+
+ NpScb->ReceiveBurstSuccessCount++;
+
+ }
+
+}
+
+
+VOID
+NwProcessPositiveAck(
+ PNONPAGED_SCB NpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine processes a positive acknowledgement.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has experienced a burst failure.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DebugTrace( 0, Dbg, "Positive ACK, NpScb = %X\n", NpScb );
+
+ NpScb->TotalWaitTime += DefaultRetryCount;
+
+ //
+ // If we have not waited longer than the absolute total, keep waiting.
+ // If we have waited too long, let ourselves timeout.
+ //
+ // If NwAbsoluteTotalWaitTime is 0, then we are prepared to wait forever.
+ //
+
+ if ( NpScb->TotalWaitTime < NwAbsoluteTotalWaitTime ||
+ NwAbsoluteTotalWaitTime == 0) {
+
+ NpScb->RetryCount = DefaultRetryCount;
+
+ } else {
+ DebugTrace( 0, Dbg, "Request exceeds absolute total wait time\n", 0 );
+ }
+}
+
+