4977 lines
124 KiB
C
4977 lines
124 KiB
C
/*++
|
||
|
||
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 );
|
||
}
|
||
}
|
||
|
||
|