mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4976 lines
124 KiB
4976 lines
124 KiB
/*++
|
|
|
|
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 );
|
|
}
|
|
}
|
|
|
|
|