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.
2918 lines
84 KiB
2918 lines
84 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
network.c
|
|
|
|
Abstract:
|
|
|
|
This module contains routines for interfacing the LAN Manager server
|
|
to the network.
|
|
|
|
Author:
|
|
|
|
Chuck Lenzmeier (chuckl) 7-Oct-1989
|
|
|
|
Environment:
|
|
|
|
File System Process, kernel mode only
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "network.tmh"
|
|
#pragma hdrstop
|
|
|
|
#include <ntddip.h>
|
|
#include <ntddtcp.h>
|
|
#include <ipfltdrv.h>
|
|
#include <tcpinfo.h>
|
|
#include <tdiinfo.h>
|
|
#include <nbtioctl.h>
|
|
|
|
// WinSock definitions
|
|
|
|
#define AF_INET 2
|
|
|
|
struct sockaddr_in {
|
|
short sin_family;
|
|
USHORT sin_port;
|
|
struct in_addr sin_addr;
|
|
char sin_zero[8];
|
|
};
|
|
|
|
#define AF_INET6 23
|
|
|
|
/* IPv6 socket address structure, RFC 2553 */
|
|
|
|
struct sockaddr_in6 {
|
|
short sin6_family; /* AF_INET6 */
|
|
USHORT sin6_port; /* Transport level port number */
|
|
ULONG sin6_flowinfo; /* IPv6 flow information */
|
|
union {
|
|
UCHAR Byte[16];
|
|
USHORT Word[8];
|
|
} sin6_addr;
|
|
ULONG sin6_scope_id; /* set of interfaces for a scope */
|
|
};
|
|
|
|
|
|
#define BugCheckFileId SRV_FILE_NETWORK
|
|
|
|
//
|
|
// Local declarations
|
|
//
|
|
|
|
NTSTATUS
|
|
GetNetworkAddress (
|
|
IN PENDPOINT Endpoint
|
|
);
|
|
|
|
NTSTATUS
|
|
OpenEndpoint (
|
|
OUT PENDPOINT *Endpoint,
|
|
IN PUNICODE_STRING NetworkName,
|
|
IN PUNICODE_STRING TransportName,
|
|
IN PANSI_STRING TransportAddress,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN ULONG TransportAddFlags,
|
|
IN BOOLEAN AlternateEndpoint
|
|
);
|
|
|
|
NTSTATUS
|
|
OpenNetbiosAddress (
|
|
IN OUT PENDPOINT Endpoint,
|
|
IN PVOID DeviceName,
|
|
IN PVOID NetbiosName
|
|
);
|
|
|
|
NTSTATUS
|
|
OpenNetbiosExAddress (
|
|
IN OUT PENDPOINT Endpoint,
|
|
IN PVOID DeviceName,
|
|
IN PVOID NetbiosName
|
|
);
|
|
|
|
NTSTATUS
|
|
OpenNonNetbiosAddress (
|
|
IN OUT PENDPOINT Endpoint,
|
|
IN PVOID DeviceName,
|
|
IN PVOID NetbiosName
|
|
);
|
|
|
|
NTSTATUS
|
|
OpenIpxSocket (
|
|
OUT PHANDLE Handle,
|
|
OUT PFILE_OBJECT *FileObject,
|
|
OUT PDEVICE_OBJECT *DeviceObject,
|
|
IN PVOID DeviceName,
|
|
IN USHORT Socket
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, SrvAddServedNet )
|
|
#pragma alloc_text( PAGE, SrvDeleteServedNet )
|
|
#pragma alloc_text( PAGE, SrvDoDisconnect )
|
|
#pragma alloc_text( PAGE, GetNetworkAddress )
|
|
#pragma alloc_text( PAGE, OpenEndpoint )
|
|
#pragma alloc_text( PAGE, OpenNetbiosAddress )
|
|
#pragma alloc_text( PAGE, OpenNonNetbiosAddress )
|
|
#pragma alloc_text( PAGE, OpenIpxSocket )
|
|
#pragma alloc_text( PAGE, SrvRestartAccept )
|
|
#pragma alloc_text( PAGE, GetIpxMaxBufferSize )
|
|
|
|
#endif
|
|
#if 0
|
|
NOT PAGEABLE -- SrvOpenConnection
|
|
NOT PAGEABLE -- SrvPrepareReceiveWorkItem
|
|
NOT PAGEABLE -- SrvStartSend
|
|
NOT PAGEABLE -- SrvStartSend2
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
SrvAddServedNet(
|
|
IN PUNICODE_STRING NetworkName,
|
|
IN PUNICODE_STRING TransportName,
|
|
IN PANSI_STRING TransportAddress,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN ULONG TransportAddFlags,
|
|
IN DWORD PasswordLength,
|
|
IN PBYTE Password
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes the server on a network. This
|
|
involves making the server known by creating a transport endpoint,
|
|
posting a Listen request, and setting up event handlers.
|
|
|
|
Arguments:
|
|
|
|
NetworkName - The administrative name of the network (e.g., NET1)
|
|
|
|
TransportName - The fully qualified name of the transport device.
|
|
For example, "\Device\Nbf".
|
|
|
|
TransportAddress - The fully qualified address (or name ) of the
|
|
server's endpoint. This name is used exactly as specified. For
|
|
NETBIOS-compatible networks, the caller must upcase and
|
|
blank-fill the name. For example, "NTSERVERbbbbbbbb".
|
|
|
|
DomainName - The name of the domain to service
|
|
|
|
Password/PasswordLength - used for mutual authentication (optional)
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicates whether the network was successfully started.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PENDPOINT endpoint;
|
|
|
|
PAGED_CODE( );
|
|
|
|
IF_DEBUG(TRACE1) KdPrint(( "SrvAddServedNet entered\n" ));
|
|
|
|
//
|
|
// Call OpenEndpoint to open the transport provider, bind to the
|
|
// server address, and register the FSD receive event handler.
|
|
//
|
|
|
|
status = OpenEndpoint(
|
|
&endpoint,
|
|
NetworkName,
|
|
TransportName,
|
|
TransportAddress,
|
|
DomainName,
|
|
TransportAddFlags,
|
|
FALSE); // primary endpoint
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvAddServedNet: unable to open endpoint %wZ for addr %z, status %X\n", TransportName, (PCSTRING)TransportAddress, status ));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Dereference the endpoint. (When it was created, the reference
|
|
// count was incremented to account for our pointer.)
|
|
//
|
|
SrvDereferenceEndpoint( endpoint );
|
|
|
|
//
|
|
// Call OpenEndpoint to open the transport provider, bind to the
|
|
// server address, and register the FSD receive event handler. This is
|
|
// the auxillary endpoint registration in the new TDI address format. Since
|
|
// this is not supported by all the transports it cannot be deemed an error.
|
|
//
|
|
//
|
|
status = OpenEndpoint(
|
|
&endpoint,
|
|
NetworkName,
|
|
TransportName,
|
|
TransportAddress,
|
|
DomainName,
|
|
TransportAddFlags,
|
|
TRUE); // Alternate endpoint
|
|
|
|
if ( NT_SUCCESS( status ) ) {
|
|
SrvDereferenceEndpoint( endpoint );
|
|
}
|
|
|
|
if( PasswordLength ) {
|
|
SrvAddSecurityCredentials( TransportAddress, DomainName, PasswordLength, Password );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // SrvAddServedNet
|
|
|
|
|
|
NTSTATUS
|
|
SrvDeleteServedNet(
|
|
IN PUNICODE_STRING TransportName,
|
|
IN PANSI_STRING TransportAddress
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function causes the server to stop listening to a network.
|
|
|
|
Arguments:
|
|
|
|
TransportAddress - the transport address (e.g. \Device\Nbf\POPCORN
|
|
of the endpoint to delete.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicates whether the network was successfully stopped.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY listEntry;
|
|
PENDPOINT endpoint;
|
|
BOOLEAN match;
|
|
NTSTATUS status = STATUS_NONEXISTENT_NET_NAME;
|
|
|
|
PAGED_CODE( );
|
|
IF_DEBUG(TRACE1) KdPrint(( "SrvDeleteServedNet entered\n" ));
|
|
|
|
//
|
|
// Find the endpoint block with the specified name.
|
|
//
|
|
|
|
top:
|
|
ACQUIRE_LOCK( &SrvEndpointLock );
|
|
|
|
listEntry = SrvEndpointList.ListHead.Flink;
|
|
|
|
while ( listEntry != &SrvEndpointList.ListHead ) {
|
|
|
|
endpoint = CONTAINING_RECORD(
|
|
listEntry,
|
|
ENDPOINT,
|
|
GlobalEndpointListEntry
|
|
);
|
|
|
|
if( GET_BLOCK_STATE(endpoint) == BlockStateActive ) {
|
|
|
|
//
|
|
// We have a match if the transport name is correct and we either
|
|
// haven't specified a transport address, or if the transport
|
|
// address matches
|
|
//
|
|
match = (BOOLEAN)(
|
|
RtlEqualUnicodeString(
|
|
TransportName,
|
|
&endpoint->TransportName,
|
|
TRUE // case insensitive compare
|
|
)
|
|
&&
|
|
(
|
|
TransportAddress->Length == 0 ||
|
|
|
|
RtlEqualString(
|
|
(PSTRING)TransportAddress,
|
|
(PSTRING)&endpoint->TransportAddress,
|
|
TRUE // case insensitive compare
|
|
)
|
|
)
|
|
|
|
);
|
|
|
|
if ( match ) {
|
|
|
|
//
|
|
// The specified network name (endpoint) exists. Close the
|
|
// endpoint. This releases the endpoint lock.
|
|
//
|
|
|
|
SrvCloseEndpoint( endpoint );
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Restart this loop, since this endpoint may have vaporized and
|
|
// the list might have changed because we dropped SrvEndpointLock.
|
|
//
|
|
// SrvCloseEndpoint will have marked this endpoint as BlockStateClosing,
|
|
// so we will not be in an infinite loop!
|
|
//
|
|
goto top;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go to the next one.
|
|
//
|
|
|
|
listEntry = listEntry->Flink;
|
|
|
|
}
|
|
|
|
//
|
|
// We are done. If we successfully matched an endpoint, we return STATUS_SUCCESS
|
|
//
|
|
|
|
RELEASE_LOCK( &SrvEndpointLock );
|
|
|
|
return status;
|
|
|
|
} // SrvDeleteServedNet
|
|
|
|
|
|
NTSTATUS
|
|
SrvDoDisconnect (
|
|
IN OUT PCONNECTION Connection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function issues a Disconnect request on a network. The request
|
|
is performed synchronously -- control is not returned to the caller
|
|
until the request completes.
|
|
|
|
Arguments:
|
|
|
|
Connection - Supplies a pointer to an Connection Block
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicates whether the disconnect was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE( );
|
|
|
|
IF_DEBUG(TRACE2) KdPrint(( "SrvDoDisconnect entered\n" ));
|
|
#if SRVDBG29
|
|
UpdateConnectionHistory( "SDSC", Connection->Endpoint, Connection );
|
|
#endif
|
|
|
|
ASSERT( !Connection->Endpoint->IsConnectionless );
|
|
|
|
//
|
|
// Issue the disconnect request.
|
|
//
|
|
|
|
status = SrvIssueDisconnectRequest(
|
|
Connection->FileObject,
|
|
&Connection->DeviceObject,
|
|
TDI_DISCONNECT_ABORT
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"SrvDoDisconnect: NtDeviceIoControlFile failed: %X",
|
|
status,
|
|
NULL
|
|
);
|
|
|
|
#if SRVDBG29
|
|
if (status != STATUS_LINK_FAILED && status != STATUS_REMOTE_DISCONNECT) {
|
|
KdPrint(( "SRV: SrvDoDisconnect: SrvIssueDisconnectRequest failed\n" ));
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
//
|
|
// Mark the connection as not reusable, because the transport
|
|
// probably still thinks it's active.
|
|
//
|
|
|
|
Connection->NotReusable = TRUE;
|
|
|
|
SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status );
|
|
|
|
}
|
|
|
|
//
|
|
// Return the status of the I/O operation.
|
|
//
|
|
|
|
return status;
|
|
|
|
} // SrvDoDisconnect
|
|
|
|
|
|
NTSTATUS
|
|
SrvOpenConnection (
|
|
IN PENDPOINT Endpoint
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function opens a connection for an endpoint and queues it to
|
|
the endpoint's free connection list.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - Supplies a pointer to an Endpoint Block
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicates whether the connection was successfully opened.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PCONNECTION connection;
|
|
PPAGED_CONNECTION pagedConnection;
|
|
CHAR eaBuffer[sizeof(FILE_FULL_EA_INFORMATION) - 1 +
|
|
TDI_CONNECTION_CONTEXT_LENGTH + 1 +
|
|
sizeof(CONNECTION_CONTEXT)];
|
|
PFILE_FULL_EA_INFORMATION ea;
|
|
CONNECTION_CONTEXT UNALIGNED *ctx;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK iosb;
|
|
KIRQL oldIrql;
|
|
PTABLE_HEADER tableHeader;
|
|
SHORT sidIndex;
|
|
USHORT i;
|
|
PTABLE_ENTRY entry = NULL;
|
|
TDI_PROVIDER_INFO providerInfo;
|
|
|
|
//
|
|
// Allocate a connection block.
|
|
//
|
|
|
|
SrvAllocateConnection( &connection );
|
|
|
|
if ( connection == NULL ) {
|
|
return STATUS_INSUFF_SERVER_RESOURCES;
|
|
}
|
|
|
|
pagedConnection = connection->PagedConnection;
|
|
|
|
//
|
|
// Allocate an entry in the endpoint's connection table.
|
|
//
|
|
|
|
ACQUIRE_SPIN_LOCK( &ENDPOINT_SPIN_LOCK(0), &oldIrql );
|
|
for ( i = 1; i < ENDPOINT_LOCK_COUNT ; i++ ) {
|
|
ACQUIRE_DPC_SPIN_LOCK( &ENDPOINT_SPIN_LOCK(i) );
|
|
}
|
|
|
|
tableHeader = &Endpoint->ConnectionTable;
|
|
|
|
if ( tableHeader->FirstFreeEntry == -1 &&
|
|
SrvGrowTable(
|
|
tableHeader,
|
|
8,
|
|
0x7fff,
|
|
NULL ) == FALSE ) {
|
|
|
|
for ( i = ENDPOINT_LOCK_COUNT-1 ; i > 0 ; i-- ) {
|
|
RELEASE_DPC_SPIN_LOCK( &ENDPOINT_SPIN_LOCK(i) );
|
|
}
|
|
RELEASE_SPIN_LOCK( &ENDPOINT_SPIN_LOCK(0), oldIrql );
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
sidIndex = tableHeader->FirstFreeEntry;
|
|
entry = &tableHeader->Table[sidIndex];
|
|
tableHeader->FirstFreeEntry = entry->NextFreeEntry;
|
|
DEBUG entry->NextFreeEntry = -2;
|
|
if ( tableHeader->LastFreeEntry == sidIndex ) {
|
|
tableHeader->LastFreeEntry = -1;
|
|
}
|
|
|
|
for ( i = ENDPOINT_LOCK_COUNT-1 ; i > 0 ; i-- ) {
|
|
RELEASE_DPC_SPIN_LOCK( &ENDPOINT_SPIN_LOCK(i) );
|
|
}
|
|
RELEASE_SPIN_LOCK( &ENDPOINT_SPIN_LOCK(0), oldIrql );
|
|
|
|
if ( !Endpoint->IsConnectionless ) {
|
|
|
|
//
|
|
// Create the EA for the connection context.
|
|
//
|
|
|
|
ea = (PFILE_FULL_EA_INFORMATION)eaBuffer;
|
|
ea->NextEntryOffset = 0;
|
|
ea->Flags = 0;
|
|
ea->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH;
|
|
ea->EaValueLength = sizeof(CONNECTION_CONTEXT);
|
|
|
|
RtlCopyMemory( ea->EaName, StrConnectionContext, ea->EaNameLength + 1 );
|
|
|
|
ctx = (CONNECTION_CONTEXT UNALIGNED *)&ea->EaName[ea->EaNameLength + 1];
|
|
*ctx = connection;
|
|
|
|
//
|
|
// Create the connection file object.
|
|
//
|
|
|
|
SrvInitializeObjectAttributes_U(
|
|
&objectAttributes,
|
|
&Endpoint->TransportName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = NtCreateFile(
|
|
&pagedConnection->ConnectionHandle,
|
|
0,
|
|
&objectAttributes,
|
|
&iosb,
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
eaBuffer,
|
|
FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) +
|
|
ea->EaNameLength + 1 + ea->EaValueLength
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvOpenConnection: NtCreateFile failed: %X\n", status ));
|
|
}
|
|
goto cleanup;
|
|
}
|
|
SRVDBG_CLAIM_HANDLE( pagedConnection->ConnectionHandle, "CON", 7, connection );
|
|
|
|
//
|
|
// Obtain a referenced pointer to the file object.
|
|
//
|
|
|
|
status = ObReferenceObjectByHandle(
|
|
pagedConnection->ConnectionHandle,
|
|
0,
|
|
(POBJECT_TYPE) NULL,
|
|
KernelMode,
|
|
(PVOID *)&connection->FileObject,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
SrvLogServiceFailure( SRV_SVC_OB_REF_BY_HANDLE, status );
|
|
|
|
//
|
|
// This internal error bugchecks the system.
|
|
//
|
|
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_IMPOSSIBLE,
|
|
"SrvOpenConnection: ObReferenceObjectByHandle failed: %X",
|
|
status,
|
|
NULL
|
|
);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// Get the address of the device object for the endpoint.
|
|
//
|
|
|
|
connection->DeviceObject = IoGetRelatedDeviceObject(
|
|
connection->FileObject
|
|
);
|
|
|
|
//
|
|
// Associate the connection with the endpoint's address.
|
|
//
|
|
|
|
status = SrvIssueAssociateRequest(
|
|
connection->FileObject,
|
|
&connection->DeviceObject,
|
|
Endpoint->EndpointHandle
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_UNEXPECTED,
|
|
"SrvOpenConnection: SrvIssueAssociateRequest failed: %X",
|
|
status,
|
|
NULL
|
|
);
|
|
|
|
SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status );
|
|
goto cleanup;
|
|
}
|
|
|
|
IF_DEBUG(NET1) {
|
|
KdPrint(( "SrvOpenConnection: Connection on %p %z opened; handle %p, pointer %p\n", Endpoint,
|
|
(PCSTRING)&Endpoint->TransportAddress,
|
|
connection->PagedConnection->ConnectionHandle,
|
|
connection->FileObject ));
|
|
}
|
|
|
|
//
|
|
// Initialize the MaximumSendSize for the transport that we're using
|
|
//
|
|
|
|
status = SrvIssueTdiQuery(
|
|
connection->FileObject,
|
|
&connection->DeviceObject,
|
|
(PCHAR)&providerInfo,
|
|
sizeof(providerInfo),
|
|
TDI_QUERY_PROVIDER_INFO
|
|
);
|
|
|
|
//
|
|
// If we got the provider info, make sure the maximum send size is at
|
|
// least 1K-1. If we have no provider info, then maximum send size is 64KB.
|
|
//
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
connection->MaximumSendSize = providerInfo.MaxSendSize;
|
|
if ( connection->MaximumSendSize < MIN_SEND_SIZE ) {
|
|
connection->MaximumSendSize = MIN_SEND_SIZE;
|
|
}
|
|
} else {
|
|
connection->MaximumSendSize = MAX_PARTIAL_BUFFER_SIZE;
|
|
}
|
|
|
|
|
|
} else { // if ( Endpoint->IsConnectionless )
|
|
if (sidIndex > 0xfff) {
|
|
// The IPXSID index can only span 12 bits
|
|
// Code needs to be added to ensure that we can try to locate
|
|
// an index less than 0xfff
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
// Give this the default initialization
|
|
connection->MaximumSendSize = MAX_PARTIAL_BUFFER_SIZE;
|
|
}
|
|
//
|
|
// Set the reference count on the connection to zero, in order to
|
|
// put it on the free list. (SrvAllocateConnection initialized the
|
|
// count to two.)
|
|
//
|
|
|
|
connection->BlockHeader.ReferenceCount = 0;
|
|
|
|
UPDATE_REFERENCE_HISTORY( connection, TRUE );
|
|
UPDATE_REFERENCE_HISTORY( connection, TRUE );
|
|
|
|
//
|
|
// Reference the endpoint and link the connection into the
|
|
// endpoint's free connection list.
|
|
//
|
|
|
|
connection->Endpoint = Endpoint;
|
|
connection->EndpointSpinLock =
|
|
&ENDPOINT_SPIN_LOCK(sidIndex & ENDPOINT_LOCK_MASK);
|
|
|
|
ACQUIRE_LOCK( &SrvEndpointLock );
|
|
|
|
SrvReferenceEndpoint( Endpoint );
|
|
|
|
ACQUIRE_SPIN_LOCK( connection->EndpointSpinLock, &oldIrql );
|
|
INCREMENT_IPXSID_SEQUENCE( entry->SequenceNumber );
|
|
if ( sidIndex == 0 && entry->SequenceNumber == 0 ) {
|
|
INCREMENT_IPXSID_SEQUENCE( entry->SequenceNumber );
|
|
}
|
|
|
|
connection->Sid = MAKE_IPXSID( sidIndex, entry->SequenceNumber );
|
|
connection->SidIndex = sidIndex;
|
|
|
|
entry->Owner = connection;
|
|
RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
|
|
|
|
ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
|
|
SrvInsertTailList(
|
|
&Endpoint->FreeConnectionList,
|
|
&connection->EndpointFreeListEntry
|
|
);
|
|
#if SRVDBG29
|
|
UpdateConnectionHistory( "OPEN", Endpoint, connection );
|
|
#endif
|
|
Endpoint->FreeConnectionCount++;
|
|
Endpoint->TotalConnectionCount++;
|
|
|
|
RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
|
|
|
|
IF_DEBUG(TDI) {
|
|
KdPrint(( "SrvOpenConnection created connection %p for endpoint %p; free %ld, total %ld\n", connection, Endpoint,
|
|
Endpoint->FreeConnectionCount,
|
|
Endpoint->TotalConnectionCount ));
|
|
}
|
|
|
|
RELEASE_LOCK( &SrvEndpointLock );
|
|
|
|
//
|
|
// The connection open was successful.
|
|
//
|
|
|
|
IF_DEBUG(TRACE1) {
|
|
KdPrint(( "SrvOpenConnection complete: %X\n", STATUS_SUCCESS ));
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
//
|
|
// Out-of-line error cleanup.
|
|
//
|
|
|
|
cleanup:
|
|
|
|
//
|
|
// Something failed. Clean up as appropriate.
|
|
//
|
|
|
|
if ( !Endpoint->IsConnectionless ) {
|
|
if ( connection->FileObject != NULL ) {
|
|
ObDereferenceObject( connection->FileObject );
|
|
}
|
|
if ( pagedConnection->ConnectionHandle != NULL ) {
|
|
SRVDBG_RELEASE_HANDLE( pagedConnection->ConnectionHandle, "CON", 12, connection );
|
|
SrvNtClose( pagedConnection->ConnectionHandle, FALSE );
|
|
}
|
|
}
|
|
|
|
if ( entry != NULL ) {
|
|
SrvRemoveEntryTable( tableHeader, sidIndex );
|
|
}
|
|
|
|
SrvFreeConnection( connection );
|
|
|
|
return status;
|
|
|
|
} // SrvOpenConnection
|
|
|
|
|
|
NTSTATUS
|
|
GetNetworkAddress (
|
|
IN PENDPOINT Endpoint
|
|
)
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PCHAR adapterStatus;
|
|
PCHAR adapterAddress;
|
|
ANSI_STRING ansiString;
|
|
CHAR addressData[12+1];
|
|
ULONG i;
|
|
|
|
struct {
|
|
ULONG ActivityCount;
|
|
TA_IPX_ADDRESS LocalAddress;
|
|
} addressInfo;
|
|
|
|
PAGED_CODE( );
|
|
|
|
if ( !Endpoint->IsConnectionless ) {
|
|
|
|
//
|
|
// Allocate a buffer to receive adapter information.
|
|
//
|
|
// *** We want to get the ADAPTER_STATUS structure, but it is
|
|
// defined in the windows header file sdk\inc\nb30.h.
|
|
// Rather than including all the windows header files, just
|
|
// allocate about a page, which should always be enough for
|
|
// that structure.
|
|
//
|
|
|
|
adapterStatus = ALLOCATE_NONPAGED_POOL( 4080, BlockTypeAdapterStatus );
|
|
if ( adapterStatus == NULL ) {
|
|
return STATUS_INSUFF_SERVER_RESOURCES;
|
|
}
|
|
|
|
status = SrvIssueTdiQuery(
|
|
Endpoint->FileObject,
|
|
&Endpoint->DeviceObject,
|
|
adapterStatus,
|
|
4080,
|
|
TDI_QUERY_ADAPTER_STATUS
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_UNEXPECTED,
|
|
"GetNetworkAddress: SrvIssueTdiQuery failed: %X\n",
|
|
status,
|
|
NULL
|
|
);
|
|
SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status );
|
|
DEALLOCATE_NONPAGED_POOL( adapterStatus );
|
|
return status;
|
|
}
|
|
|
|
adapterAddress = adapterStatus;
|
|
|
|
} else {
|
|
|
|
status = SrvIssueTdiQuery(
|
|
Endpoint->NameSocketFileObject,
|
|
&Endpoint->NameSocketDeviceObject,
|
|
(PCHAR)&addressInfo,
|
|
sizeof(addressInfo),
|
|
TDI_QUERY_ADDRESS_INFO
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_UNEXPECTED,
|
|
"GetNetworkAddress: SrvIssueTdiQuery failed: %X\n",
|
|
status,
|
|
NULL
|
|
);
|
|
SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status );
|
|
return status;
|
|
}
|
|
|
|
Endpoint->LocalAddress = addressInfo.LocalAddress.Address[0].Address[0];
|
|
|
|
adapterAddress = addressInfo.LocalAddress.Address[0].Address[0].NodeAddress;
|
|
|
|
}
|
|
|
|
//
|
|
// Get an ANSI string that contains the adapter address.
|
|
//
|
|
|
|
ansiString.Buffer = addressData;
|
|
ansiString.Length = 12;
|
|
ansiString.MaximumLength = 13;
|
|
|
|
#define tohexdigit(a) ((CHAR)( (a) > 9 ? ((a) + 'a' - 0xA) : ((a) + '0') ))
|
|
|
|
for ( i = 0; i < 6; i++ ) {
|
|
addressData[2*i] = tohexdigit( (adapterAddress[i] >> 4) & 0x0F );
|
|
addressData[2*i+1] = tohexdigit( adapterAddress[i] & 0x0F );
|
|
}
|
|
|
|
addressData[12] = '\0';
|
|
|
|
//
|
|
// Convert the address string to Unicode.
|
|
//
|
|
|
|
status = RtlAnsiStringToUnicodeString(
|
|
&Endpoint->NetworkAddress,
|
|
&ansiString,
|
|
FALSE
|
|
);
|
|
ASSERT( NT_SUCCESS(status) );
|
|
|
|
if ( !Endpoint->IsConnectionless ) {
|
|
DEALLOCATE_NONPAGED_POOL( adapterStatus );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // GetNetworkAddress
|
|
|
|
|
|
NTSTATUS
|
|
OpenEndpoint (
|
|
OUT PENDPOINT *Endpoint,
|
|
IN PUNICODE_STRING NetworkName,
|
|
IN PUNICODE_STRING TransportName,
|
|
IN PANSI_STRING TransportAddress,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN DWORD TransportAddFlags,
|
|
IN BOOLEAN AlternateEndpoint
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function opens a transport provider, simultaneously binding the
|
|
server's address to the transport endpoint, and registers a Receive
|
|
event handler for the endpoint.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - Returns a pointer to an Endpoint Block
|
|
|
|
NetworkName - Supplies the administrative name of the network (e.g.,
|
|
NET1).
|
|
|
|
TransportName - The fully qualified name of the transport device.
|
|
For example, "\Device\Nbf".
|
|
|
|
TransportAddress - The exact name of the server to be used on the
|
|
specified transport. For NETBIOS-compatible networks, the
|
|
caller must upcase and blank-fill the name. For example,
|
|
"NTSERVERbbbbbbbb".
|
|
|
|
DomainName - name of domain to serve
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Indicates whether the network was successfully opened.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PENDPOINT endpoint = NULL; // local copy of Endpoint
|
|
|
|
PAGED_CODE( );
|
|
|
|
IF_DEBUG(TRACE1) KdPrint(( "OpenEndpoint %wZ entered\n", TransportName ));
|
|
|
|
//
|
|
// Allocate an endpoint block.
|
|
//
|
|
|
|
SrvAllocateEndpoint(
|
|
&endpoint,
|
|
NetworkName,
|
|
TransportName,
|
|
TransportAddress,
|
|
DomainName
|
|
);
|
|
|
|
if ( endpoint == NULL ) {
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "OpenEndpoint complete: %X\n",
|
|
STATUS_INSUFF_SERVER_RESOURCES ));
|
|
}
|
|
return STATUS_INSUFF_SERVER_RESOURCES;
|
|
}
|
|
|
|
if( TransportAddFlags & SRP_XADD_PRIMARY_MACHINE ) {
|
|
|
|
endpoint->IsPrimaryName = 1;
|
|
|
|
if( SrvComputerName.Buffer == NULL ) {
|
|
|
|
status = RtlAnsiStringToUnicodeString( &SrvComputerName, TransportAddress, TRUE );
|
|
|
|
if( NT_SUCCESS( status ) ) {
|
|
|
|
//
|
|
// Trim off the trailing spaces
|
|
//
|
|
|
|
while( SrvComputerName.Buffer[(SrvComputerName.Length-sizeof(WCHAR))/sizeof(WCHAR)]
|
|
== L' ' ) {
|
|
SrvComputerName.Length -= sizeof(WCHAR);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( NT_SUCCESS( status ) ) {
|
|
|
|
if( TransportAddFlags & SRP_XADD_REMAP_PIPE_NAMES ) {
|
|
endpoint->RemapPipeNames = 1;
|
|
}
|
|
|
|
if (AlternateEndpoint) {
|
|
|
|
status = OpenNetbiosExAddress(
|
|
endpoint,
|
|
TransportName,
|
|
TransportAddress->Buffer);
|
|
|
|
endpoint->AlternateAddressFormat = TRUE;
|
|
|
|
} else {
|
|
|
|
endpoint->AlternateAddressFormat = FALSE;
|
|
|
|
//
|
|
// Assume that the transport is a NetBIOS provider, and try to
|
|
// open the server's address using the NetBIOS name.
|
|
//
|
|
|
|
status = OpenNetbiosAddress(
|
|
endpoint,
|
|
TransportName,
|
|
TransportAddress->Buffer
|
|
);
|
|
|
|
//
|
|
// We could not open the transport as a NetBIOS provider. We will now try
|
|
// to see if it is a direct host IPX provider. However, if we have been
|
|
// configured to use Security Signatures, do not attempt direct host IPX since
|
|
// security signatures are not supported for direct host IPX transports
|
|
//
|
|
if ( !NT_SUCCESS(status) && SrvSmbSecuritySignaturesRequired == FALSE ) {
|
|
|
|
BOOLEAN isDuplicate = FALSE;
|
|
PLIST_ENTRY listEntry;
|
|
|
|
//
|
|
// Apparently the transport is not a NetBIOS provider. We can
|
|
// not open multiple connectionless providers through the same
|
|
// TransportName.
|
|
//
|
|
|
|
ACQUIRE_LOCK( &SrvEndpointLock );
|
|
|
|
for( listEntry = SrvEndpointList.ListHead.Flink;
|
|
listEntry != &SrvEndpointList.ListHead;
|
|
listEntry = listEntry->Flink ) {
|
|
|
|
PENDPOINT tmpEndpoint;
|
|
|
|
tmpEndpoint = CONTAINING_RECORD( listEntry, ENDPOINT, GlobalEndpointListEntry );
|
|
|
|
if( GET_BLOCK_STATE( tmpEndpoint ) == BlockStateActive &&
|
|
tmpEndpoint->IsConnectionless &&
|
|
RtlCompareUnicodeString( &tmpEndpoint->TransportName, TransportName, TRUE ) == 0 ) {
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "OpenEndpoint: Only one connectionless endpoint on %wZ allowed!\n",
|
|
TransportName ));
|
|
}
|
|
|
|
isDuplicate = TRUE;
|
|
status = STATUS_TOO_MANY_NODES;
|
|
break;
|
|
}
|
|
}
|
|
|
|
RELEASE_LOCK( &SrvEndpointLock );
|
|
|
|
//
|
|
// Try to open it as a connectionless provider.
|
|
//
|
|
if( isDuplicate == FALSE ) {
|
|
NTSTATUS status2;
|
|
|
|
status2 = OpenNonNetbiosAddress(
|
|
endpoint,
|
|
TransportName,
|
|
TransportAddress->Buffer
|
|
);
|
|
|
|
if( NT_SUCCESS( status2 ) ) {
|
|
status = status2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// We couldn't open the provider as either a NetBIOS provider
|
|
// or as a connectionless provider.
|
|
//
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "OpenEndpoint: OpenAddress failed: %X\n", status ));
|
|
}
|
|
|
|
//
|
|
// Close all free connections.
|
|
//
|
|
|
|
EmptyFreeConnectionList( endpoint );
|
|
|
|
SrvFreeEndpoint( endpoint );
|
|
|
|
ACQUIRE_LOCK( &SrvEndpointLock );
|
|
SrvEndpointCount--;
|
|
|
|
if( SrvEndpointCount == 0 )
|
|
{
|
|
KeSetEvent( &SrvEndpointEvent, 0, FALSE );
|
|
}
|
|
|
|
RELEASE_LOCK( &SrvEndpointLock );
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Query the provider for the send entry point
|
|
//
|
|
|
|
SrvQuerySendEntryPoint(
|
|
endpoint->FileObject,
|
|
&endpoint->DeviceObject,
|
|
IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER,
|
|
(PVOID*)&endpoint->FastTdiSend
|
|
);
|
|
|
|
//
|
|
// Query the provider for the send entry point
|
|
//
|
|
|
|
SrvQuerySendEntryPoint(
|
|
endpoint->FileObject,
|
|
&endpoint->DeviceObject,
|
|
IOCTL_TDI_QUERY_DIRECT_SENDDG_HANDLER,
|
|
(PVOID*)&endpoint->FastTdiSendDatagram
|
|
);
|
|
|
|
//
|
|
// The network open was successful. Link the new endpoint into the
|
|
// list of active endpoints. Return with a success status. (We
|
|
// don't dereference the endpoint because we're returning a pointer
|
|
// to the endpoint.)
|
|
//
|
|
|
|
SrvInsertEntryOrderedList( &SrvEndpointList, endpoint );
|
|
|
|
*Endpoint = endpoint;
|
|
|
|
IF_DEBUG(TRACE1) {
|
|
KdPrint(( "OpenEndpoint complete: %wZ %X\n", TransportName, STATUS_SUCCESS ));
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // OpenEndpoint
|
|
|
|
NTSTATUS
|
|
SetupConnectionEndpointHandlers(
|
|
IN OUT PENDPOINT Endpoint)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
|
|
Endpoint->IsConnectionless = FALSE;
|
|
|
|
status = SrvVerifyDeviceStackSize(
|
|
Endpoint->EndpointHandle,
|
|
TRUE,
|
|
&Endpoint->FileObject,
|
|
&Endpoint->DeviceObject,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"OpenNetbiosAddress: Verify Device Stack Size failed: %X\n",
|
|
status,
|
|
NULL
|
|
);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Find the network address of the adapter used by corresponding to
|
|
// this endpoint.
|
|
//
|
|
|
|
GetNetworkAddress( Endpoint );
|
|
|
|
//
|
|
// Register the server's Receive event handler.
|
|
//
|
|
|
|
status = SrvIssueSetEventHandlerRequest(
|
|
Endpoint->FileObject,
|
|
&Endpoint->DeviceObject,
|
|
TDI_EVENT_RECEIVE,
|
|
(PVOID)SrvFsdTdiReceiveHandler,
|
|
Endpoint
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"OpenNetbiosAddress: set receive event handler failed: %X",
|
|
status,
|
|
NULL
|
|
);
|
|
|
|
SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status );
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Register the server's Disconnect event handler.
|
|
//
|
|
|
|
status = SrvIssueSetEventHandlerRequest(
|
|
Endpoint->FileObject,
|
|
&Endpoint->DeviceObject,
|
|
TDI_EVENT_DISCONNECT,
|
|
(PVOID)SrvFsdTdiDisconnectHandler,
|
|
Endpoint
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_UNEXPECTED,
|
|
"OpenNetbiosAddress: set disconnect event handler failed: %X",
|
|
status,
|
|
NULL
|
|
);
|
|
|
|
SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status );
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Create a number of free connections for the endpoint. These
|
|
// connections will be used to service Connect events.
|
|
//
|
|
// *** If we fail in an attempt to create a connection, but we can
|
|
// successfully create at least one, we keep the endpoint. The
|
|
// cleanup code below depends on this behavior.
|
|
//
|
|
|
|
for ( i = 0; i < SrvFreeConnectionMinimum; i++ ) {
|
|
|
|
status = SrvOpenConnection( Endpoint );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"OpenNetbiosAddress: SrvOpenConnection failed: %X",
|
|
status,
|
|
NULL
|
|
);
|
|
if ( i == 0 ) {
|
|
goto cleanup;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Register the server's Connect event handler.
|
|
//
|
|
// *** Note that Connect events can be delivered IMMEDIATELY upon
|
|
// completion of this request!
|
|
//
|
|
|
|
status = SrvIssueSetEventHandlerRequest(
|
|
Endpoint->FileObject,
|
|
&Endpoint->DeviceObject,
|
|
TDI_EVENT_CONNECT,
|
|
(PVOID)SrvFsdTdiConnectHandler,
|
|
Endpoint
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_UNEXPECTED,
|
|
"OpenNetbiosAddress: set connect event handler failed: %X",
|
|
status,
|
|
NULL
|
|
);
|
|
|
|
SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status );
|
|
goto cleanup;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
//
|
|
// Out-of-line error cleanup.
|
|
//
|
|
|
|
cleanup:
|
|
|
|
//
|
|
// Something failed. Clean up as appropriate.
|
|
//
|
|
|
|
if ( Endpoint->FileObject != NULL ) {
|
|
ObDereferenceObject( Endpoint->FileObject );
|
|
Endpoint->FileObject = NULL;
|
|
}
|
|
if ( Endpoint->EndpointHandle != NULL ) {
|
|
SRVDBG_RELEASE_HANDLE( Endpoint->EndpointHandle, "END", 14, Endpoint );
|
|
SrvNtClose( Endpoint->EndpointHandle, FALSE );
|
|
Endpoint->EndpointHandle = NULL;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
OpenNetbiosAddress (
|
|
IN OUT PENDPOINT Endpoint,
|
|
IN PVOID DeviceName,
|
|
IN PVOID NetbiosName
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
|
|
CHAR eaBuffer[sizeof(FILE_FULL_EA_INFORMATION) +
|
|
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
|
|
sizeof(TA_NETBIOS_ADDRESS)];
|
|
|
|
PAGED_CODE( );
|
|
|
|
status = TdiOpenNetbiosAddress(
|
|
&Endpoint->EndpointHandle,
|
|
eaBuffer,
|
|
DeviceName,
|
|
NetbiosName
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
IF_DEBUG( ERRORS ) {
|
|
KdPrint(( "TdiOpenNetbiosAddress( %wZ ) status %X\n", DeviceName, status ));
|
|
}
|
|
return status;
|
|
}
|
|
|
|
status = SetupConnectionEndpointHandlers(Endpoint);
|
|
|
|
return status;
|
|
} // OpenNetbiosAddress
|
|
|
|
NTSTATUS
|
|
OpenNetbiosExAddress(
|
|
IN OUT PENDPOINT Endpoint,
|
|
IN PVOID DeviceName,
|
|
IN PVOID NetbiosName
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PFILE_FULL_EA_INFORMATION ea;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
ULONG length;
|
|
CHAR buffer[sizeof(FILE_FULL_EA_INFORMATION) +
|
|
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
|
|
sizeof(TA_NETBIOS_EX_ADDRESS)];
|
|
|
|
TA_NETBIOS_EX_ADDRESS NetbiosExAddress;
|
|
PTDI_ADDRESS_NETBIOS_EX pTdiNetbiosExAddress;
|
|
PTDI_ADDRESS_NETBIOS pNetbiosAddress;
|
|
|
|
ULONG NetbiosExAddressLength;
|
|
|
|
PAGED_CODE( );
|
|
|
|
//
|
|
// Build the NETBIOS Extended address.
|
|
//
|
|
|
|
NetbiosExAddress.TAAddressCount = 1;
|
|
NetbiosExAddress.Address[0].AddressLength = TDI_ADDRESS_LENGTH_NETBIOS_EX;
|
|
NetbiosExAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS_EX;
|
|
|
|
pTdiNetbiosExAddress = NetbiosExAddress.Address[0].Address;
|
|
pNetbiosAddress = &pTdiNetbiosExAddress->NetbiosAddress;
|
|
pNetbiosAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
|
|
|
|
NetbiosExAddressLength = FIELD_OFFSET(TRANSPORT_ADDRESS,Address)
|
|
+ FIELD_OFFSET(TA_ADDRESS,Address)
|
|
+ FIELD_OFFSET(TDI_ADDRESS_NETBIOS_EX,NetbiosAddress)
|
|
+ TDI_ADDRESS_LENGTH_NETBIOS;
|
|
|
|
RtlCopyMemory(
|
|
pNetbiosAddress->NetbiosName,
|
|
NetbiosName,
|
|
NETBIOS_NAME_LEN);
|
|
|
|
// Copy the default endpoint name onto the NETBIOS Extended address.
|
|
RtlCopyMemory(
|
|
pTdiNetbiosExAddress->EndpointName,
|
|
SMBSERVER_LOCAL_ENDPOINT_NAME,
|
|
NETBIOS_NAME_LEN);
|
|
|
|
length = FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) +
|
|
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
|
|
NetbiosExAddressLength;
|
|
ea = (PFILE_FULL_EA_INFORMATION)buffer;
|
|
|
|
ea->NextEntryOffset = 0;
|
|
ea->Flags = 0;
|
|
ea->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
|
|
ea->EaValueLength = (USHORT)NetbiosExAddressLength;
|
|
|
|
RtlCopyMemory( ea->EaName, StrTransportAddress, ea->EaNameLength + 1 );
|
|
|
|
RtlCopyMemory(
|
|
&ea->EaName[ea->EaNameLength + 1],
|
|
&NetbiosExAddress,
|
|
NetbiosExAddressLength
|
|
);
|
|
|
|
InitializeObjectAttributes( &objectAttributes, DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL );
|
|
|
|
status = NtCreateFile (
|
|
&Endpoint->EndpointHandle,
|
|
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, // desired access
|
|
&objectAttributes, // object attributes
|
|
&iosb, // returned status information
|
|
NULL, // block size (unused)
|
|
0, // file attributes
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, // share access
|
|
FILE_CREATE, // create disposition
|
|
0, // create options
|
|
buffer, // EA buffer
|
|
length // EA length
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
IF_DEBUG( ERRORS ) {
|
|
KdPrint(("NtCreateFile %wZ NETBIOS_EX address status %X\n", DeviceName, status ));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
Endpoint->IsNoNetBios = TRUE;
|
|
status = SetupConnectionEndpointHandlers(Endpoint);
|
|
|
|
IF_DEBUG( ERRORS ) {
|
|
if( !NT_SUCCESS( status ) ) {
|
|
KdPrint(("SetupConnectionEndpointHandlers failed, status %X\n", status ));
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
OpenNonNetbiosAddress (
|
|
IN OUT PENDPOINT Endpoint,
|
|
IN PVOID DeviceName,
|
|
IN PVOID NetbiosName
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
ULONG numAdapters;
|
|
PULONG maxPktArray = NULL;
|
|
UCHAR buffer[sizeof(NWLINK_ACTION) + sizeof(IPX_ADDRESS_DATA) - 1];
|
|
PNWLINK_ACTION action;
|
|
PIPX_ADDRESS_DATA ipxAddressData;
|
|
|
|
PAGED_CODE( );
|
|
|
|
//
|
|
// Open the NetBIOS name socket.
|
|
//
|
|
|
|
status = OpenIpxSocket(
|
|
&Endpoint->NameSocketHandle,
|
|
&Endpoint->NameSocketFileObject,
|
|
&Endpoint->NameSocketDeviceObject,
|
|
DeviceName,
|
|
SMB_IPX_NAME_SOCKET
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
Endpoint->IsConnectionless = TRUE;
|
|
action = (PNWLINK_ACTION)buffer;
|
|
|
|
//
|
|
// Put the endpoint in broadcast reception mode.
|
|
//
|
|
|
|
action->Header.TransportId = 'XPIM'; // "MIPX"
|
|
action->Header.ActionCode = 0;
|
|
action->Header.Reserved = 0;
|
|
action->OptionType = NWLINK_OPTION_ADDRESS;
|
|
action->BufferLength = sizeof(action->Option);
|
|
action->Option = MIPX_RCVBCAST;
|
|
|
|
status = SrvIssueTdiAction(
|
|
Endpoint->NameSocketFileObject,
|
|
&Endpoint->NameSocketDeviceObject,
|
|
(PCHAR)action,
|
|
sizeof(NWLINK_ACTION)
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Tell the transport to give you the extended receive info
|
|
//
|
|
|
|
action->Header.TransportId = 'XPIM'; // "MIPX"
|
|
action->Header.ActionCode = 0;
|
|
action->Header.Reserved = 0;
|
|
action->OptionType = NWLINK_OPTION_ADDRESS;
|
|
action->BufferLength = sizeof(action->Option);
|
|
action->Option = MIPX_SETRCVFLAGS;
|
|
|
|
status = SrvIssueTdiAction(
|
|
Endpoint->NameSocketFileObject,
|
|
&Endpoint->NameSocketDeviceObject,
|
|
(PCHAR)action,
|
|
sizeof(NWLINK_ACTION)
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the max adapter number
|
|
//
|
|
|
|
action->Header.TransportId = 'XPIM'; // "MIPX"
|
|
action->Header.ActionCode = 0;
|
|
action->Header.Reserved = 0;
|
|
action->OptionType = NWLINK_OPTION_ADDRESS;
|
|
action->BufferLength = sizeof(action->Option) + sizeof(ULONG);
|
|
action->Option = MIPX_ADAPTERNUM2;
|
|
|
|
status = SrvIssueTdiAction(
|
|
Endpoint->NameSocketFileObject,
|
|
&Endpoint->NameSocketDeviceObject,
|
|
(PCHAR)action,
|
|
sizeof(NWLINK_ACTION) + sizeof(ULONG) - 1
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
numAdapters = *((PULONG)action->Data);
|
|
|
|
//
|
|
// Allocate an array to store the max pkt size for each adapter
|
|
//
|
|
|
|
maxPktArray = ALLOCATE_HEAP( numAdapters * sizeof(ULONG), BlockTypeBuffer );
|
|
|
|
if ( maxPktArray == NULL ) {
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
Endpoint->IpxMaxPacketSizeArray = maxPktArray;
|
|
Endpoint->MaxAdapters = numAdapters;
|
|
|
|
//
|
|
// Query the max pkt size for each adapter
|
|
//
|
|
|
|
action->Header.TransportId = 'XPIM'; // "MIPX"
|
|
action->Header.ActionCode = 0;
|
|
action->Header.Reserved = 0;
|
|
action->OptionType = NWLINK_OPTION_ADDRESS;
|
|
action->BufferLength = sizeof(action->Option) + sizeof(IPX_ADDRESS_DATA);
|
|
action->Option = MIPX_GETCARDINFO2;
|
|
ipxAddressData = (PIPX_ADDRESS_DATA)action->Data;
|
|
|
|
for ( i = 0; i < numAdapters; i++ ) {
|
|
|
|
ipxAddressData->adapternum = i;
|
|
|
|
status = SrvIssueTdiAction(
|
|
Endpoint->NameSocketFileObject,
|
|
&Endpoint->NameSocketDeviceObject,
|
|
(PCHAR)action,
|
|
sizeof(NWLINK_ACTION) + sizeof(IPX_ADDRESS_DATA) - 1
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// If this is a wan link, then we need to query the length each
|
|
// time we get a connection.
|
|
//
|
|
|
|
if ( ipxAddressData->wan ) {
|
|
maxPktArray[i] = 0;
|
|
} else {
|
|
maxPktArray[i] = ipxAddressData->maxpkt;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find the network address of the adapter used by corresponding to
|
|
// this endpoint.
|
|
//
|
|
|
|
GetNetworkAddress( Endpoint );
|
|
|
|
//
|
|
// Register the name claim Receive Datagram event handler.
|
|
//
|
|
|
|
status = SrvIssueSetEventHandlerRequest(
|
|
Endpoint->NameSocketFileObject,
|
|
&Endpoint->NameSocketDeviceObject,
|
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
|
(PVOID)SrvIpxNameDatagramHandler,
|
|
Endpoint
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"OpenNonNetbiosAddress: set receive datagram event handler failed: %X",
|
|
status,
|
|
NULL
|
|
);
|
|
SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status );
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Claim the server name.
|
|
//
|
|
|
|
status = SrvIpxClaimServerName( Endpoint, NetbiosName );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Open the server socket.
|
|
//
|
|
|
|
status = OpenIpxSocket(
|
|
&Endpoint->EndpointHandle,
|
|
&Endpoint->FileObject,
|
|
&Endpoint->DeviceObject,
|
|
DeviceName,
|
|
SMB_IPX_SERVER_SOCKET
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Create a number of free connections for the endpoint. These
|
|
// connections will be used to service Connect events.
|
|
//
|
|
// *** If we fail in an attempt to create a connection, but we can
|
|
// successfully create at least one, we keep the endpoint. The
|
|
// cleanup code below depends on this behavior.
|
|
//
|
|
|
|
for ( i = 0; i < SrvFreeConnectionMinimum; i++ ) {
|
|
|
|
status = SrvOpenConnection( Endpoint );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"OpenNonNetbiosAddress: SrvOpenConnection failed: %X",
|
|
status,
|
|
NULL
|
|
);
|
|
if ( i == 0 ) {
|
|
goto cleanup;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Register the server Receive Datagram event handler.
|
|
//
|
|
|
|
status = SrvIssueSetEventHandlerRequest(
|
|
Endpoint->FileObject,
|
|
&Endpoint->DeviceObject,
|
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
|
(PVOID)SrvIpxServerDatagramHandler,
|
|
Endpoint
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"OpenNonNetbiosAddress: set receive datagram event handler failed: %X",
|
|
status,
|
|
NULL
|
|
);
|
|
SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status );
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Register the server Chained Receive Datagram event handler.
|
|
//
|
|
|
|
status = SrvIssueSetEventHandlerRequest(
|
|
Endpoint->FileObject,
|
|
&Endpoint->DeviceObject,
|
|
TDI_EVENT_CHAINED_RECEIVE_DATAGRAM,
|
|
(PVOID)SrvIpxServerChainedDatagramHandler,
|
|
Endpoint
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"OpenNonNetbiosAddress: set chained receive datagram event handler failed: %X",
|
|
status,
|
|
NULL
|
|
);
|
|
SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status );
|
|
goto cleanup;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
//
|
|
// Out-of-line error cleanup.
|
|
//
|
|
|
|
cleanup:
|
|
|
|
//
|
|
// Something failed. Clean up as appropriate.
|
|
//
|
|
|
|
if ( maxPktArray != NULL ) {
|
|
Endpoint->IpxMaxPacketSizeArray = NULL;
|
|
FREE_HEAP( maxPktArray );
|
|
}
|
|
if ( Endpoint->FileObject != NULL ) {
|
|
ObDereferenceObject( Endpoint->FileObject );
|
|
Endpoint->FileObject = NULL;
|
|
}
|
|
if ( Endpoint->EndpointHandle != NULL ) {
|
|
SRVDBG_RELEASE_HANDLE( Endpoint->EndpointHandle, "END", 14, Endpoint );
|
|
SrvNtClose( Endpoint->EndpointHandle, FALSE );
|
|
Endpoint->FileObject = NULL;
|
|
}
|
|
|
|
if ( Endpoint->NameSocketFileObject != NULL ) {
|
|
ObDereferenceObject( Endpoint->NameSocketFileObject );
|
|
Endpoint->NameSocketFileObject = NULL;
|
|
}
|
|
if ( Endpoint->NameSocketHandle != NULL ) {
|
|
SRVDBG_RELEASE_HANDLE( Endpoint->NameSocketHandle, "END", 14, Endpoint );
|
|
SrvNtClose( Endpoint->NameSocketHandle, FALSE );
|
|
Endpoint->NameSocketHandle = NULL;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // OpenNonNetbiosAddress
|
|
|
|
|
|
NTSTATUS
|
|
OpenIpxSocket (
|
|
OUT PHANDLE Handle,
|
|
OUT PFILE_OBJECT *FileObject,
|
|
OUT PDEVICE_OBJECT *DeviceObject,
|
|
IN PVOID DeviceName,
|
|
IN USHORT Socket
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG length;
|
|
PFILE_FULL_EA_INFORMATION ea;
|
|
TA_IPX_ADDRESS ipxAddress;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
CHAR buffer[sizeof(FILE_FULL_EA_INFORMATION) +
|
|
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
|
|
sizeof(TA_IPX_ADDRESS)];
|
|
|
|
PAGED_CODE( );
|
|
|
|
//
|
|
// Build the IPX socket address.
|
|
//
|
|
|
|
length = FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) +
|
|
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
|
|
sizeof(TA_IPX_ADDRESS);
|
|
ea = (PFILE_FULL_EA_INFORMATION)buffer;
|
|
|
|
ea->NextEntryOffset = 0;
|
|
ea->Flags = 0;
|
|
ea->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
|
|
ea->EaValueLength = sizeof (TA_IPX_ADDRESS);
|
|
|
|
RtlCopyMemory( ea->EaName, StrTransportAddress, ea->EaNameLength + 1 );
|
|
|
|
//
|
|
// Create a copy of the NETBIOS address descriptor in a local
|
|
// first, in order to avoid alignment problems.
|
|
//
|
|
|
|
ipxAddress.TAAddressCount = 1;
|
|
ipxAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_IPX;
|
|
ipxAddress.Address[0].AddressLength = sizeof (TDI_ADDRESS_IPX);
|
|
ipxAddress.Address[0].Address[0].Socket = Socket;
|
|
|
|
RtlCopyMemory(
|
|
&ea->EaName[ea->EaNameLength + 1],
|
|
&ipxAddress,
|
|
sizeof(TA_IPX_ADDRESS)
|
|
);
|
|
|
|
InitializeObjectAttributes( &objectAttributes, DeviceName, 0, NULL, NULL );
|
|
|
|
status = NtCreateFile (
|
|
Handle,
|
|
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, // desired access
|
|
&objectAttributes, // object attributes
|
|
&iosb, // returned status information
|
|
NULL, // block size (unused)
|
|
0, // file attributes
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, // share access
|
|
FILE_CREATE, // create disposition
|
|
0, // create options
|
|
buffer, // EA buffer
|
|
length // EA length
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
return status;
|
|
}
|
|
|
|
status = SrvVerifyDeviceStackSize(
|
|
*Handle,
|
|
TRUE,
|
|
FileObject,
|
|
DeviceObject,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"OpenIpxSocket: Verify Device Stack Size failed: %X\n",
|
|
status,
|
|
NULL
|
|
);
|
|
return status;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // OpenIpxSocket
|
|
|
|
|
|
VOID
|
|
SrvPrepareReceiveWorkItem (
|
|
IN OUT PWORK_CONTEXT WorkContext,
|
|
IN BOOLEAN QueueItemToFreeList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes a Receive work item and optionally queues
|
|
it to a list anchored in the server FSD device object. The
|
|
transport receive event handler in the FSD dequeues work items from
|
|
this list and passes their associated IRPS to the transport
|
|
provider.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the preallocated work context
|
|
block that represents the work item.
|
|
|
|
QueueItemToFreeList - If TRUE queue this work item on the receive
|
|
free queue.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSMB_HEADER header;
|
|
|
|
IF_DEBUG(TRACE2) KdPrint(( "SrvPrepareReceiveWorkItem entered\n" ));
|
|
|
|
//
|
|
// Set up pointers to the SMB header and parameters for the request
|
|
// and the response. Note that we currently write the response over
|
|
// the request. SMB processors must be able to handle this. We
|
|
// maintain separate request and response pointers so that we can
|
|
// use a separate buffer if necessary. Maintaining separate request
|
|
// and response parameter pointers also allows us to process AndX
|
|
// SMBs without having to pack the AndX commands as we go.
|
|
//
|
|
|
|
WorkContext->ResponseBuffer = WorkContext->RequestBuffer;
|
|
|
|
header = (PSMB_HEADER)WorkContext->RequestBuffer->Buffer;
|
|
|
|
WorkContext->RequestHeader = header;
|
|
WorkContext->RequestParameters = (PVOID)(header + 1);
|
|
|
|
WorkContext->ResponseHeader = header;
|
|
WorkContext->ResponseParameters = (PVOID)(header + 1);
|
|
|
|
//
|
|
// Set up the restart routine in the work context.
|
|
//
|
|
|
|
WorkContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
|
|
WorkContext->FspRestartRoutine = SrvRestartReceive;
|
|
|
|
if ( QueueItemToFreeList ) {
|
|
|
|
//
|
|
// Queue the prepared receive work item to the FSD list.
|
|
//
|
|
|
|
GET_SERVER_TIME( WorkContext->CurrentWorkQueue, &WorkContext->Timestamp );
|
|
RETURN_FREE_WORKITEM( WorkContext );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Make the work item look like it's in use by setting its
|
|
// reference count to 1.
|
|
//
|
|
|
|
ASSERT( WorkContext->BlockHeader.ReferenceCount == 0 );
|
|
WorkContext->BlockHeader.ReferenceCount = 1;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} // SrvPrepareReceiveWorkItem
|
|
|
|
|
|
VOID SRVFASTCALL
|
|
SrvRestartAccept (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the worker thread restart routine for Accept
|
|
requests. If the endpoint on which the connection was established
|
|
is no longer active, this routine disconnects the connection. This
|
|
is necessary because the connect indication handler cannot
|
|
atomically verify that the endpoint is active and install the active
|
|
connection. (This is because the handler runs at DPC level.)
|
|
|
|
This routine also checks the status of the TdiAccept. In case of
|
|
an error, it frees the connection.
|
|
|
|
If all is well, but the endpoint is short of free connections, a new
|
|
one is created.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block describing
|
|
server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONNECTION connection;
|
|
PENDPOINT endpoint;
|
|
PIRP irp;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE( );
|
|
|
|
IF_DEBUG(WORKER1) KdPrint(( " - SrvRestartAccept\n" ));
|
|
|
|
connection = WorkContext->Connection;
|
|
endpoint = WorkContext->Endpoint;
|
|
irp = WorkContext->Irp;
|
|
IF_DEBUG(TRACE2) {
|
|
KdPrint(( " connection %p, endpoint %p, IRP %p\n",
|
|
connection, endpoint, irp ));
|
|
}
|
|
|
|
//
|
|
// If the I/O request failed or was canceled, or if the endpoint
|
|
// block is closing, clean up.
|
|
//
|
|
|
|
ACQUIRE_LOCK( &SrvEndpointLock );
|
|
|
|
if ( irp->Cancel ||
|
|
!NT_SUCCESS(irp->IoStatus.Status) ||
|
|
(GET_BLOCK_STATE(endpoint) != BlockStateActive) ) {
|
|
|
|
RELEASE_LOCK( &SrvEndpointLock );
|
|
|
|
DEBUG {
|
|
KdPrint(( "SrvRestartAccept: Accept failed!" ));
|
|
if ( irp->Cancel ) {
|
|
KdPrint(( " I/O canceled\n" ));
|
|
} else if ( !NT_SUCCESS(irp->IoStatus.Status) ) {
|
|
KdPrint(( " I/O failed: %X\n", irp->IoStatus.Status ));
|
|
} else {
|
|
KdPrint(( " Endpoint no longer active\n" ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close the connection. If the Accept succeeded, we need to
|
|
// issue a Disconnect.
|
|
//
|
|
|
|
#if SRVDBG29
|
|
if (irp->Cancel) {
|
|
UpdateConnectionHistory( "ACC1", endpoint, connection );
|
|
} else if (!NT_SUCCESS(irp->IoStatus.Status)) {
|
|
UpdateConnectionHistory( "ACC2", endpoint, connection );
|
|
} else {
|
|
UpdateConnectionHistory( "ACC3", endpoint, connection );
|
|
}
|
|
#endif
|
|
|
|
connection->DisconnectReason = DisconnectAcceptFailedOrCancelled;
|
|
SrvCloseConnection(
|
|
connection,
|
|
(BOOLEAN)(irp->Cancel || !NT_SUCCESS(irp->IoStatus.Status) ?
|
|
TRUE : FALSE) // RemoteDisconnect
|
|
);
|
|
|
|
} else {
|
|
|
|
PNBT_ADDRESS_PAIR_INFO AddressPairInfo;
|
|
UNICODE_STRING clientMachineName;
|
|
|
|
//
|
|
// The Accept worked, and the endpoint is still active. Create
|
|
// a new free connection, if necessary.
|
|
//
|
|
|
|
if ( endpoint->FreeConnectionCount < SrvFreeConnectionMinimum ) {
|
|
(VOID)SrvOpenConnection( endpoint );
|
|
(VOID)SrvOpenConnection( endpoint );
|
|
}
|
|
|
|
RELEASE_LOCK( &SrvEndpointLock );
|
|
|
|
//
|
|
// Get the IP address of the client (if it has one)
|
|
//
|
|
AddressPairInfo = WorkContext->RequestBuffer->Buffer;
|
|
AddressPairInfo = (PNBT_ADDRESS_PAIR_INFO)(((ULONG_PTR)AddressPairInfo + 7) & ~7);
|
|
|
|
//
|
|
// Get the IP address of the client, if appropriate
|
|
//
|
|
status = SrvIssueTdiQuery( connection->FileObject,
|
|
&connection->DeviceObject,
|
|
(PCHAR)AddressPairInfo, sizeof( *AddressPairInfo ),
|
|
TDI_QUERY_ADDRESS_INFO
|
|
);
|
|
|
|
if( NT_SUCCESS( status ) &&
|
|
AddressPairInfo->AddressPair.TAAddressCount == 2 ) {
|
|
|
|
// Copy out the SockAddr info
|
|
if( AddressPairInfo->AddressPair.AddressIP.AddressType == TDI_ADDRESS_TYPE_IP )
|
|
{
|
|
PTDI_ADDRESS_IP Address = &AddressPairInfo->AddressPair.AddressIP.Address;
|
|
struct sockaddr_in* sockaddr = (struct sockaddr_in*)connection->SockAddr;
|
|
|
|
sockaddr->sin_family = AF_INET;
|
|
sockaddr->sin_port = Address->sin_port;
|
|
sockaddr->sin_addr.S_un.S_addr = Address->in_addr;
|
|
}
|
|
else if( AddressPairInfo->AddressPair.AddressIP.AddressType == TDI_ADDRESS_TYPE_IP6 )
|
|
{
|
|
PTDI_ADDRESS_IP6 Address = &AddressPairInfo->AddressPair.AddressIP.AddressIp6;
|
|
struct sockaddr_in6* sockaddr = (struct sockaddr_in6*)connection->SockAddr;
|
|
|
|
ASSERT(sizeof(TDI_ADDRESS_IP6)==sizeof(struct sockaddr_in6)-sizeof(USHORT));
|
|
sockaddr->sin6_family = AF_INET6;
|
|
RtlCopyMemory( &sockaddr->sin6_port, Address, sizeof(TDI_ADDRESS_IP6) );
|
|
}
|
|
|
|
// Setup the ClientIPAddress and the Keep-alives
|
|
if( AddressPairInfo->AddressPair.AddressIP.AddressType == TDI_ADDRESS_TYPE_IP )
|
|
{
|
|
PTCP_REQUEST_SET_INFORMATION_EX tcpSetInfo;
|
|
struct TCPKeepalive *keepAlive;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
connection->ClientIPAddress = AddressPairInfo->AddressPair.AddressIP.Address.in_addr;
|
|
|
|
//
|
|
// We have an IP client. Set a reasonable keepalive interval.
|
|
//
|
|
tcpSetInfo = ALLOCATE_HEAP( sizeof(*tcpSetInfo) + sizeof( *keepAlive ), BlockTypeMisc );
|
|
|
|
if( tcpSetInfo != NULL ) {
|
|
|
|
RtlZeroMemory( tcpSetInfo, sizeof( *tcpSetInfo ) + sizeof( *keepAlive ) );
|
|
|
|
tcpSetInfo->ID.toi_entity.tei_entity = CO_TL_ENTITY;
|
|
tcpSetInfo->ID.toi_class = INFO_CLASS_PROTOCOL;
|
|
tcpSetInfo->ID.toi_type = INFO_TYPE_CONNECTION;
|
|
tcpSetInfo->ID.toi_id = TCP_SOCKET_KEEPALIVE_VALS;
|
|
|
|
tcpSetInfo->BufferSize = sizeof( *keepAlive );
|
|
keepAlive = (TCPKeepalive *)(&tcpSetInfo->Buffer[0]);
|
|
|
|
keepAlive->onoff = TRUE; // turn on keepalives
|
|
|
|
//
|
|
// keepalive time is the time to first keepalive transmission, by default it
|
|
// is 2 hours (7,200,000 milliseconds) for TCP. If there is no data transfer between
|
|
// client and server for keepalive time, the server will send first keepalive
|
|
// probe. Successive probes are determined by keepalive interval. If there is any
|
|
// data transfer, timer is reset to keepalive time.
|
|
//
|
|
// keepalive interval is the interval in milliseconds between keepalive transmissions
|
|
// until a response is received, by default it is 1000. Server sends a total of 10
|
|
// keepalive probes, keepalive interval apart, and if there is no response from the
|
|
// client, the connection is terminated.
|
|
//
|
|
keepAlive->keepalivetime = 2 * (60 * 1000); // 2 minutes
|
|
keepAlive->keepaliveinterval = 2 * 1000; // 2 seconds
|
|
|
|
//
|
|
// Set the keepalive values
|
|
//
|
|
(VOID)NtDeviceIoControlFile(
|
|
connection->PagedConnection->ConnectionHandle,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&iosb,
|
|
IOCTL_NETBT_SET_TCP_CONNECTION_INFO,
|
|
tcpSetInfo, sizeof( *tcpSetInfo ) + sizeof( *keepAlive ),
|
|
NULL, 0
|
|
);
|
|
|
|
FREE_HEAP( tcpSetInfo );
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
connection->ClientIPAddress = 0;
|
|
RtlZeroMemory( connection->SockAddr, SRV_CONNECTION_SOCKADDR_SIZE );
|
|
}
|
|
|
|
//
|
|
// Convert the client machine name to unicode
|
|
//
|
|
|
|
clientMachineName.Buffer = connection->ClientMachineName;
|
|
clientMachineName.MaximumLength =
|
|
(USHORT)(COMPUTER_NAME_LENGTH+1)*sizeof(WCHAR);
|
|
|
|
(VOID)RtlOemStringToUnicodeString(
|
|
&clientMachineName,
|
|
&connection->OemClientMachineNameString,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Add the double backslashes to the length
|
|
//
|
|
|
|
connection->ClientMachineNameString.Length =
|
|
(USHORT)(clientMachineName.Length + 2*sizeof(WCHAR));
|
|
|
|
}
|
|
|
|
SrvDereferenceWorkItem( WorkContext );
|
|
|
|
IF_DEBUG(TRACE2) KdPrint(( "SrvRestartAccept complete\n" ));
|
|
return;
|
|
|
|
} // SrvRestartAccept
|
|
|
|
|
|
VOID
|
|
SrvStartSend (
|
|
IN OUT PWORK_CONTEXT WorkContext,
|
|
IN PIO_COMPLETION_ROUTINE SendCompletionRoutine,
|
|
IN PMDL Mdl OPTIONAL,
|
|
IN ULONG SendOptions
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function starts a Send request. It is started as an
|
|
asynchronous I/O request. When the Send completes, it is delivered
|
|
via the I/O completion routine to the server FSD, which routes it to
|
|
the specified FsdRestartRoutine. (This may be
|
|
SrvQueueWorkToFspAtDpcLevel, which queues the work item to the FSP
|
|
at the FspRestartRoutine.)
|
|
|
|
Partial sends and chained sends are supported. A partial send is one
|
|
that is not the last segment of a "message" or "record". A chained
|
|
send is one made up of multiple virtually discontiguous buffers.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to a Work Context block. The
|
|
following fields of this structure must be valid:
|
|
|
|
TdiRequest
|
|
Irp (optional; actual address copied here)
|
|
Endpoint
|
|
Endpoint->FileObject
|
|
Endpoint->DeviceObject
|
|
Connection
|
|
Connection->ConnectionId
|
|
|
|
Mdl - Supplies a pointer to the first (or only) MDL describing the
|
|
data that is to be sent. To effect a chained send, the Next
|
|
pointer of each MDL in the chain must point to the next MDL;
|
|
the end of the chain is indicated by the NULL Next pointer.
|
|
|
|
The total length of the send is calculated by summing the
|
|
ByteCount fields of each MDL in the chain.
|
|
|
|
This parameter is optional. If it is omitted, a zero-length
|
|
message is sent.
|
|
|
|
SendOptions - Supplied TDI send options, which indicate whether this
|
|
send is the last (or only) in a "chain" of partial sends.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTDI_REQUEST_KERNEL_SEND parameters;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PIRP irp;
|
|
ULONG sendLength;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PFILE_OBJECT fileObject;
|
|
|
|
IF_DEBUG(TRACE2) KdPrint(( "SrvStartSend entered\n" ));
|
|
|
|
ASSERT( !WorkContext->Endpoint->IsConnectionless );
|
|
|
|
//
|
|
// Set ProcessingCount to zero so this send cannot be cancelled.
|
|
// This is used together with setting the cancel flag to false below.
|
|
//
|
|
// WARNING: This still presents us with a tiny window where this
|
|
// send could be cancelled.
|
|
//
|
|
|
|
WorkContext->ProcessingCount = 0;
|
|
|
|
//
|
|
// Get the irp, device, and file objects
|
|
//
|
|
|
|
irp = WorkContext->Irp;
|
|
deviceObject = WorkContext->Connection->DeviceObject;
|
|
fileObject = WorkContext->Connection->FileObject;
|
|
|
|
CHECKIRP( irp );
|
|
|
|
if( irp->AssociatedIrp.SystemBuffer &&
|
|
(irp->Flags & IRP_DEALLOCATE_BUFFER) ) {
|
|
|
|
ExFreePool( irp->AssociatedIrp.SystemBuffer );
|
|
irp->Flags &= ~IRP_DEALLOCATE_BUFFER;
|
|
}
|
|
|
|
sendLength = WorkContext->ResponseBuffer->DataLength;
|
|
|
|
//
|
|
// Build the I/O request packet.
|
|
//
|
|
// *** Note that the connection block is not referenced to account
|
|
// for this I/O request. The WorkContext block already has a
|
|
// referenced pointer to the connection, and this pointer is not
|
|
// dereferenced until after the I/O completes.
|
|
//
|
|
|
|
ASSERT( irp->StackCount >= deviceObject->StackSize );
|
|
|
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|
irp->Tail.Overlay.Thread = WorkContext->CurrentWorkQueue->IrpThread;
|
|
irp->RequestorMode = KernelMode;
|
|
//
|
|
// Get a pointer to the next stack location. This one is used to
|
|
// hold the parameters for the device I/O control request.
|
|
//
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
|
|
//
|
|
// Set up the completion routine.
|
|
//
|
|
|
|
IoSetCompletionRoutine(
|
|
irp,
|
|
SendCompletionRoutine,
|
|
(PVOID)WorkContext,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
|
|
irpSp->FileObject = fileObject;
|
|
irpSp->DeviceObject = deviceObject;
|
|
|
|
parameters = (PTDI_REQUEST_KERNEL_SEND)&irpSp->Parameters;
|
|
parameters->SendFlags = SendOptions;
|
|
parameters->SendLength = sendLength;
|
|
|
|
//
|
|
// For these two cases, InputBuffer is the buffered I/O "system
|
|
// buffer". Build an MDL for either read or write access,
|
|
// depending on the method, for the output buffer.
|
|
//
|
|
|
|
irp->MdlAddress = Mdl;
|
|
|
|
//
|
|
// If statistics are to be gathered for this work item, do so now.
|
|
//
|
|
|
|
UPDATE_STATISTICS(
|
|
WorkContext,
|
|
sendLength,
|
|
WorkContext->ResponseHeader->Command
|
|
);
|
|
|
|
#if DBG
|
|
if( sendLength >= sizeof( SMB_HEADER ) && sendLength <= 0xffff && Mdl ) {
|
|
|
|
PSMB_HEADER Smb = MmGetSystemAddressForMdl( Mdl );
|
|
|
|
if ( SmbGetAlignedUlong( (PULONG)Smb->Protocol ) == SMB_HEADER_PROTOCOL ) {
|
|
|
|
ULONG len;
|
|
PMDL tmpMdl;
|
|
|
|
//
|
|
// For debugging purposes, put extra data in the response smb. This will help us figure
|
|
// out what went wrong if the client detects an SMB format error
|
|
//
|
|
|
|
//
|
|
// Put the send length in PidHigh
|
|
//
|
|
SmbPutUshort( &Smb->PidHigh, (USHORT)sendLength );
|
|
|
|
//
|
|
// Put the overall MDL length in Pid. The transport is only supposed to transmit SendLength -- so
|
|
// this will help us figure out if the transport is sending too much data.
|
|
//
|
|
for( len = 0, tmpMdl = Mdl; tmpMdl != NULL; tmpMdl = tmpMdl->Next ) {
|
|
len += MmGetMdlByteCount( tmpMdl );
|
|
}
|
|
|
|
SmbPutUshort( &Smb->Pid, (USHORT)len );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// If we are doing security signatures, we need to sign this packet
|
|
//
|
|
if( sendLength &&
|
|
WorkContext->Connection &&
|
|
WorkContext->Connection->SmbSecuritySignatureActive == TRUE &&
|
|
WorkContext->NoResponseSmbSecuritySignature == FALSE ) {
|
|
|
|
SrvAddSmbSecuritySignature( WorkContext, Mdl, sendLength );
|
|
}
|
|
|
|
//
|
|
// Pass the request to the transport provider.
|
|
//
|
|
IF_DEBUG(TRACE2) {
|
|
KdPrint(( "SrvStartSend posting Send IRP %p\n", irp ));
|
|
}
|
|
|
|
WorkContext->Irp->Cancel = FALSE;
|
|
|
|
//
|
|
// Increment the pending operation count
|
|
//
|
|
InterlockedIncrement( &WorkContext->Connection->OperationsPendingOnTransport );
|
|
|
|
//
|
|
// Set the cancel flag to FALSE in case this was cancelled by
|
|
// the SrvSmbNtCancel routine.
|
|
//
|
|
|
|
if ( WorkContext->Endpoint->FastTdiSend ) {
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.DirectSendsAttempted );
|
|
irpSp->MinorFunction = TDI_DIRECT_SEND;
|
|
IoSetNextIrpStackLocation( irp );
|
|
WorkContext->Endpoint->FastTdiSend( deviceObject, irp );
|
|
|
|
} else {
|
|
|
|
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
irpSp->MinorFunction = TDI_SEND;
|
|
irp->AssociatedIrp.SystemBuffer = NULL;
|
|
irp->Flags = (ULONG)IRP_BUFFERED_IO;
|
|
|
|
(VOID)IoCallDriver( deviceObject, irp );
|
|
}
|
|
|
|
IF_DEBUG(TRACE2) KdPrint(( "SrvStartSend complete\n" ));
|
|
return;
|
|
|
|
} // SrvStartSend
|
|
|
|
VOID
|
|
SrvStartSend2 (
|
|
IN OUT PWORK_CONTEXT WorkContext,
|
|
IN PIO_COMPLETION_ROUTINE SendCompletionRoutine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function starts a Send request. It is started as an
|
|
asynchronous I/O request. When the Send completes, it is delivered
|
|
via the I/O completion routine to the server FSD, which routes it to
|
|
the specified FsdRestartRoutine. (This may be
|
|
SrvQueueWorkToFspAtDpcLevel, which queues the work item to the FSP
|
|
at the FspRestartRoutine.)
|
|
|
|
Partial sends and chained sends are supported. A partial send is one
|
|
that is not the last segment of a "message" or "record". A chained
|
|
send is one made up of multiple virtually discontiguous buffers.
|
|
|
|
** This is identical to SrvStartSend except that the parameter mdl
|
|
is assumed to be ResponseBuffer->Mdl and sendOptions is assumed to be
|
|
0 **
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to a Work Context block. The
|
|
following fields of this structure must be valid:
|
|
|
|
TdiRequest
|
|
Irp (optional; actual address copied here)
|
|
Endpoint
|
|
Endpoint->FileObject
|
|
Endpoint->DeviceObject
|
|
Connection
|
|
Connection->ConnectionId
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTDI_REQUEST_KERNEL_SEND parameters;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PIRP irp;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PFILE_OBJECT fileObject;
|
|
|
|
PMDL mdl = WorkContext->ResponseBuffer->Mdl;
|
|
ULONG sendLength = WorkContext->ResponseBuffer->DataLength;
|
|
|
|
IF_DEBUG(TRACE2) KdPrint(( "SrvStartSend2 entered\n" ));
|
|
|
|
ASSERT( !WorkContext->Endpoint->IsConnectionless );
|
|
|
|
//
|
|
// Set ProcessingCount to zero so this send cannot be cancelled.
|
|
// This is used together with setting the cancel flag to false below.
|
|
//
|
|
// WARNING: This still presents us with a tiny window where this
|
|
// send could be cancelled.
|
|
//
|
|
|
|
WorkContext->ProcessingCount = 0;
|
|
|
|
//
|
|
// Get the irp, device, and file objects
|
|
//
|
|
|
|
irp = WorkContext->Irp;
|
|
deviceObject = WorkContext->Connection->DeviceObject;
|
|
fileObject = WorkContext->Connection->FileObject;
|
|
|
|
CHECKIRP( irp );
|
|
|
|
if( irp->AssociatedIrp.SystemBuffer &&
|
|
(irp->Flags & IRP_DEALLOCATE_BUFFER) ) {
|
|
|
|
ExFreePool( irp->AssociatedIrp.SystemBuffer );
|
|
irp->Flags &= ~IRP_DEALLOCATE_BUFFER;
|
|
}
|
|
|
|
//
|
|
// Build the I/O request packet.
|
|
//
|
|
// *** Note that the connection block is not referenced to account
|
|
// for this I/O request. The WorkContext block already has a
|
|
// referenced pointer to the connection, and this pointer is not
|
|
// dereferenced until after the I/O completes.
|
|
//
|
|
|
|
ASSERT( irp->StackCount >= deviceObject->StackSize );
|
|
|
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|
irp->Tail.Overlay.Thread = WorkContext->CurrentWorkQueue->IrpThread;
|
|
DEBUG irp->RequestorMode = KernelMode;
|
|
|
|
//
|
|
// Get a pointer to the next stack location. This one is used to
|
|
// hold the parameters for the device I/O control request.
|
|
//
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
|
|
//
|
|
// Set up the completion routine.
|
|
//
|
|
|
|
IoSetCompletionRoutine(
|
|
irp,
|
|
SendCompletionRoutine,
|
|
(PVOID)WorkContext,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
irpSp->FileObject = fileObject;
|
|
irpSp->DeviceObject = deviceObject;
|
|
|
|
parameters = (PTDI_REQUEST_KERNEL_SEND)&irpSp->Parameters;
|
|
parameters->SendFlags = 0;
|
|
parameters->SendLength = sendLength;
|
|
|
|
//
|
|
// For these two cases, InputBuffer is the buffered I/O "system
|
|
// buffer". Build an MDL for either read or write access,
|
|
// depending on the method, for the output buffer.
|
|
//
|
|
|
|
irp->MdlAddress = mdl;
|
|
|
|
//
|
|
// If statistics are to be gathered for this work item, do so now.
|
|
//
|
|
|
|
UPDATE_STATISTICS(
|
|
WorkContext,
|
|
sendLength,
|
|
WorkContext->ResponseHeader->Command
|
|
);
|
|
|
|
#if DBG
|
|
if( sendLength >= sizeof( SMB_HEADER ) && sendLength <= 0xffff && mdl ) {
|
|
|
|
PSMB_HEADER Smb = MmGetSystemAddressForMdl( mdl );
|
|
|
|
if ( SmbGetAlignedUlong( (PULONG)Smb->Protocol ) == SMB_HEADER_PROTOCOL ) {
|
|
|
|
ULONG len;
|
|
PMDL tmpMdl;
|
|
|
|
//
|
|
// For debugging purposes, put extra data in the response smb. This
|
|
// will help us figure out what went wrong if the client detects an
|
|
// SMB format error
|
|
//
|
|
|
|
//
|
|
// Put the send length in PidHigh
|
|
//
|
|
SmbPutUshort( &Smb->PidHigh, (USHORT)sendLength );
|
|
|
|
//
|
|
// Put the overall MDL length in Pid. The transport is only supposed
|
|
// to transmit SendLength -- so this will help us figure out if the
|
|
// transport is sending too much data.
|
|
//
|
|
for( len = 0, tmpMdl = mdl; tmpMdl != NULL; tmpMdl = tmpMdl->Next ) {
|
|
len += MmGetMdlByteCount( tmpMdl );
|
|
}
|
|
|
|
SmbPutUshort( &Smb->Pid, (USHORT)len );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// If we are doing security signatures, we need to sign this packet
|
|
//
|
|
if( sendLength &&
|
|
WorkContext->Connection &&
|
|
WorkContext->Connection->SmbSecuritySignatureActive == TRUE &&
|
|
WorkContext->NoResponseSmbSecuritySignature == FALSE ) {
|
|
|
|
SrvAddSmbSecuritySignature( WorkContext, mdl, sendLength );
|
|
}
|
|
|
|
//
|
|
// Pass the request to the transport provider.
|
|
//
|
|
|
|
IF_DEBUG(TRACE2) {
|
|
KdPrint(( "SrvStartSend2 posting Send IRP %p\n", irp ));
|
|
}
|
|
|
|
WorkContext->Irp->Cancel = FALSE;
|
|
|
|
//
|
|
// Increment the pending operation count
|
|
//
|
|
InterlockedIncrement( &WorkContext->Connection->OperationsPendingOnTransport );
|
|
|
|
//
|
|
// Set the cancel flag to FALSE in case this was cancelled by
|
|
// the SrvSmbNtCancel routine.
|
|
//
|
|
|
|
if ( WorkContext->Endpoint->FastTdiSend ) {
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.DirectSendsAttempted );
|
|
irpSp->MinorFunction = TDI_DIRECT_SEND;
|
|
IoSetNextIrpStackLocation( irp );
|
|
WorkContext->Endpoint->FastTdiSend( deviceObject, irp );
|
|
|
|
} else {
|
|
|
|
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
irpSp->MinorFunction = TDI_SEND;
|
|
irp->AssociatedIrp.SystemBuffer = NULL;
|
|
irp->Flags = (ULONG)IRP_BUFFERED_IO;
|
|
|
|
(VOID)IoCallDriver( deviceObject, irp );
|
|
}
|
|
|
|
IF_DEBUG(TRACE2) KdPrint(( "SrvStartSend2 complete\n" ));
|
|
return;
|
|
|
|
} // SrvStartSend2
|
|
|
|
ULONG
|
|
GetIpxMaxBufferSize(
|
|
PENDPOINT Endpoint,
|
|
ULONG AdapterNumber,
|
|
ULONG DefaultMaxBufferSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine computes the max buffer size the server negotiates
|
|
with the client. It takes the smaller of DefaultMaxBufferSize
|
|
and the max packet length returned by the ipx transport.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - pointer to the endpoint corresponding to the ipx transport
|
|
AdapterNumber - the adapter number for which the max buffer size is to
|
|
be computed for.
|
|
DefaultMaxBufferSize - the maximum size that can be returned by this
|
|
routine.
|
|
|
|
Return Value:
|
|
|
|
The max buffer size to be negotiated by the server.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
ULONG maxBufferSize;
|
|
PNWLINK_ACTION action;
|
|
PIPX_ADDRESS_DATA ipxAddressData;
|
|
UCHAR buffer[sizeof(NWLINK_ACTION) + sizeof(IPX_ADDRESS_DATA) - 1];
|
|
|
|
PAGED_CODE( );
|
|
|
|
action = (PNWLINK_ACTION)buffer;
|
|
|
|
//
|
|
// Verify that the adapter number is within bounds
|
|
//
|
|
|
|
if ( AdapterNumber > Endpoint->MaxAdapters ) {
|
|
return DefaultMaxBufferSize;
|
|
}
|
|
|
|
//
|
|
// If value in array is non-zero, then this is not a wan link.
|
|
// Use that value.
|
|
//
|
|
|
|
if ( Endpoint->IpxMaxPacketSizeArray[AdapterNumber-1] != 0 ) {
|
|
|
|
maxBufferSize = MIN(
|
|
Endpoint->IpxMaxPacketSizeArray[AdapterNumber-1],
|
|
DefaultMaxBufferSize
|
|
);
|
|
|
|
return (maxBufferSize & ~3);
|
|
}
|
|
|
|
//
|
|
// This is a wan link, query the max packet size.
|
|
//
|
|
|
|
action->Header.TransportId = 'XPIM'; // "MIPX"
|
|
action->Header.ActionCode = 0;
|
|
action->Header.Reserved = 0;
|
|
action->OptionType = NWLINK_OPTION_ADDRESS;
|
|
action->BufferLength = sizeof(action->Option) + sizeof(IPX_ADDRESS_DATA);
|
|
action->Option = MIPX_GETCARDINFO2;
|
|
ipxAddressData = (PIPX_ADDRESS_DATA)action->Data;
|
|
|
|
ipxAddressData->adapternum = AdapterNumber - 1;
|
|
|
|
status = SrvIssueTdiAction(
|
|
Endpoint->NameSocketFileObject,
|
|
&Endpoint->NameSocketDeviceObject,
|
|
(PCHAR)action,
|
|
sizeof(NWLINK_ACTION) + sizeof(IPX_ADDRESS_DATA) - 1
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
return DefaultMaxBufferSize;
|
|
}
|
|
|
|
ASSERT( ipxAddressData->wan );
|
|
|
|
maxBufferSize = MIN(
|
|
(ULONG)ipxAddressData->maxpkt,
|
|
DefaultMaxBufferSize
|
|
);
|
|
|
|
return (maxBufferSize & ~3);
|
|
|
|
} // GetMaxIpxPacketSize
|
|
|
|
|
|
VOID
|
|
SrvpNotifyChangesToNetBt(
|
|
IN TDI_PNP_OPCODE PnPOpcode,
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN PWSTR MultiSZBindList)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine should not be part of srv. It has been introduced into this
|
|
component to overcome current limitations in NetBt. The NetBt transport
|
|
exposes two kinds of devices -- the traditional NetBt device and the
|
|
new non Netbios device which make use of the NetBt framing code without the
|
|
name resolution aspects of it. The current implementation in NetBt exposes
|
|
the former devices on a per adapter basis while the second category of device
|
|
is exposed on a global basis ( one for all the adapters ). This poses
|
|
problems in disabling/enabling srv on a given adapter.
|
|
|
|
The correct solution is to expose the second category of devices on a per
|
|
adapter basis. Till it is done this workaround is reqd. With this workaround
|
|
whenever the server is notified of any changes to the binding string it turns
|
|
around and notifies the NetBt transport about these changes.
|
|
|
|
This routine is based upon the following assumptions ...
|
|
|
|
1) The notification from TDI is not done at raised IRQL.
|
|
|
|
2) The thread on which this notification occurs has enough access rights.
|
|
|
|
3) The notification to NetBt is done asynchronously with srv's reaction
|
|
to the change. The srv handles the PNP notification by passing it off to
|
|
user mode and have it come through the server service.
|
|
|
|
Arguments:
|
|
|
|
PNPOpcode - the PNP opcode
|
|
|
|
DeviceName - the transport for which this opcode is intended
|
|
|
|
MultiSZBindList - the binding list
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE NetbioslessSmbHandle;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING NetbioslessSmbName = {36,36, L"\\device\\NetbiosSmb"};
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&NetbioslessSmbName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL );
|
|
|
|
Status = ZwCreateFile (
|
|
&NetbioslessSmbHandle,
|
|
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, // desired access
|
|
&ObjectAttributes, // object attributes
|
|
&IoStatusBlock, // returned status information
|
|
NULL, // block size (unused)
|
|
0, // file attributes
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, // share access
|
|
FILE_CREATE, // create disposition
|
|
0, // create options
|
|
NULL, // EA buffer
|
|
0 // EA length
|
|
);
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
NETBT_SMB_BIND_REQUEST NetBtNotificationParameters;
|
|
|
|
NetBtNotificationParameters.RequestType = SMB_SERVER;
|
|
NetBtNotificationParameters.PnPOpCode = PnPOpcode;
|
|
NetBtNotificationParameters.pDeviceName = DeviceName;
|
|
NetBtNotificationParameters.MultiSZBindList = MultiSZBindList;
|
|
|
|
Status = ZwDeviceIoControlFile(
|
|
NetbioslessSmbHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_NETBT_SET_SMBDEVICE_BIND_INFO,
|
|
&NetBtNotificationParameters,
|
|
sizeof(NetBtNotificationParameters),
|
|
NULL,
|
|
0);
|
|
|
|
IF_DEBUG( ERRORS ) {
|
|
KdPrint(("NtFsControlFile %wZ in SrvpNotifyChangesToNetBt status %X\n", &NetbioslessSmbName, Status ));
|
|
}
|
|
|
|
Status = ZwClose(NetbioslessSmbHandle);
|
|
|
|
IF_DEBUG( ERRORS ) {
|
|
KdPrint(("NtCloseFile %wZ in SrvpNotifyChangesToNetBt status %X\n", &NetbioslessSmbName, Status ));
|
|
}
|
|
} else {
|
|
IF_DEBUG( ERRORS ) {
|
|
KdPrint(("NtCreateFile %wZ in SrvpNotifyChangesToNetBt status %X\n", &NetbioslessSmbName, Status ));
|
|
}
|
|
}
|
|
}
|
|
|
|
|