|
|
/*++
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
ULONG DropCount = 0;
#ifdef NWDBG
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 );
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': { 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 = (word)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 = (USHORT)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;
// 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. If we start getting packets
// after we've timed out, we'll increase the wait time.
//
pNpScb->RetryCount = 0; pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 7 + pNpScb->LipTickAdjustment; 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;
//
// tommye - MS bug 2743 changed the RetryCount from 20 to be based off the
// default retry count, nudged up a little.
//
if ( !BooleanFlagOn( pIrpC->Flags, IRP_FLAG_RETRY_SEND ) ) { pNpScb->RetryCount = DefaultRetryCount * 2; }
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 = (word)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 = (byte)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++ ) {
//
// In fact Novell server seems to convert all 0xBF, 0xAA, 0xAE
// and 0x5C even if they are DBCS lead or trail byte.
// We can't single out DBCS case in the conversion.
//
if( FsRtlIsLeadDbcsCharacter( *pOemString ) ) {
if(*pOemString == 0xBF ) {
*pOemString = 0x10;
}else if(*pOemString == 0xAE ) {
*pOemString = 0x11;
}else if(*pOemString == 0xAA ) {
*pOemString = 0x12;
}
// Trail byte
i++; pOemString++;
if(*pOemString == 0x5C ) {
//
// The trailbyte is 0x5C, replace it with 0x13
//
*pOemString = 0x13;
} //
// Continue to check other conversions for trailbyte.
//
}
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 < lastChar) && (*thisChar == OBJ_NAME_PATH_SEPARATOR)) { 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 = (USHORT) (( 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.
//
//
// 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.
//
DropCount++; 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:
//
// If this is the LIP packet that we are expecting, then accept it.
// However, on a slow link, it could be an old LIP packet that we
// have already given up on. If this is the case, we should drop
// the packet and increase the LIP max wait time.
//
// The sequence number is the fourth DWORD in the response and the
// maximum LIP tick adjustment that we will allow is 18 ticks, which
// is 1 second.
//
pTdiStruct = &pNpScb->Echo;
if ( *(DWORD UNALIGNED *)&RspData[12] != pNpScb->LipSequenceNumber ) {
DebugTrace( 0, DEBUG_TRACE_ALWAYS, "LIP packet received out of order.\n", 0 );
if ( pNpScb->LipTickAdjustment < 18 ) { pNpScb->LipTickAdjustment += 2; }
AcceptPacket = FALSE;
} else {
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;
//
// tommye - MS bug 10511 - added code to set pNpScb->TimeOut
// to sames as pNpScb->SendTimeout per bug report recommendation.
//
pNpScb->TimeOut = 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 { //(!AcceptPacket)
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 = MmGetSystemAddressForMdlSafe( ReceiveMdl, NormalPagePriority ); 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.
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 it 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 ; //
//---- Multi-user code merge ----
//
Stats.Sessions--;
if ( pNpScb->MajorVersion == 2 ) { Stats.NW2xConnects--; } else if ( pNpScb->MajorVersion == 3 ) { Stats.NW3xConnects--; } else if ( pNpScb->MajorVersion == 4 ) { Stats.NW4xConnects--; } //---------------------------------
}
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; short 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 { // --- Multi-user -------------
// Need Login ID to send messages
//
ServerMessage->LogonId = *((PLUID)&IrpContext->pScb->UserUid);
//
// 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_CONTEXT workContext;
if (WorkerThreadRunning == TRUE) {
//
// Prepare the work context
//
workContext = AllocateWorkContext();
if (workContext == NULL) {
pIrpContext->pEx( pIrpContext, 0, NULL ); return; } workContext->pIrpC = pIrpContext; workContext->NodeWorkCode = NWC_NWC_RECONNECT;
//
// and queue it.
//
DebugTrace( 0, Dbg, "Queueing reconnect work.\n", 0 );
KeInsertQueue( &KernelQueue, &workContext->Next );
} else {
//
// The worker thread is not running...
//
pIrpContext->pEx( pIrpContext, 0, NULL ); 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; }
//
// 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.
//
// TRACKING: 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 );
//
// tommye MS 90541 / MCS 277
//
// Theory has it that we end up here about every half second,
// but I don't think we really know how long this packet has been
// outstanding. So, we'll just count this half-second event towards
// our timeout. Once this exceeds NwAbsoluteTotalWaitTime, then we
// won't reset the RetryCount and the DPC should handle it from there.
//
NpScb->TotalWaitTime++;
//
// 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 ); } }
|