Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1788 lines
61 KiB

/*++
Copyright (c) 1989-1999 Microsoft Corporation
Module Name:
bind.c
Abstract:
Contains AfdBind for binding an endpoint to a transport address.
Author:
David Treadwell (davidtr) 25-Feb-1992
Revision History:
Vadim Eydelman (vadime) 1999 - C_ROOT endpoint handling,
exclusive access endpoints.
--*/
#include "afdp.h"
NTSTATUS
AfdRestartGetAddress (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
AfdRestartBindGetAddress (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
BOOLEAN
AfdIsAddressInUse (
PAFD_ENDPOINT Endpoint,
BOOLEAN OtherProcessesOnly
);
PFILE_OBJECT
AfdGetAddressFileReference (
PAFD_ENDPOINT Endpoint
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfdBind )
#pragma alloc_text( PAGE, AfdIsAddressInUse )
#pragma alloc_text( PAGE, AfdGetAddress )
#pragma alloc_text( PAGEAFD, AfdAreTransportAddressesEqual )
#pragma alloc_text( PAGEAFD, AfdGetAddressFileReference )
#pragma alloc_text( PAGEAFD, AfdRestartGetAddress )
#pragma alloc_text( PAGEAFD, AfdRestartBindGetAddress )
#if NOT_YET
NTSTATUS
AfdStealClosedEnpointAddress (
PAFD_ENDPOINT Endpoint,
NTSTATUS Status
);
#pragma alloc_text( PAGE, AfdStealClosedEnpointAddress )
#endif // NOT_YET
#endif
NTSTATUS
FASTCALL
AfdBind (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Handles the IOCTL_AFD_BIND IOCTL.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
NTSTATUS status;
ULONG shareAccess, afdShareAccess;
ULONG tdiAddressLength;
PAFD_ENDPOINT endpoint;
PSECURITY_DESCRIPTOR sd = NULL; // Use endpoint sd for secure transports
PSECURITY_DESCRIPTOR oldSd;
ULONG options;
PTRANSPORT_ADDRESS localAddress;
HANDLE addressHandle;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK iosb;
PFILE_FULL_EA_INFORMATION eaInfo;
ULONG eaLength;
// Local buffer to avoid memory allocation
PCHAR eaBuffer[sizeof(FILE_FULL_EA_INFORMATION) - 1 +
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
AFD_MAX_FAST_TRANSPORT_ADDRESS];
PAGED_CODE( );
//
// Initialize returned parameter
//
Irp->IoStatus.Information = 0;
//
// Need to have output buffer at least as long as input buffer
// to pass the address that was actually used by the transport
// back.
//
if ( (IrpSp->Parameters.DeviceIoControl.InputBufferLength<
(ULONG)FIELD_OFFSET (AFD_BIND_INFO, Address.Address)) ||
IrpSp->Parameters.DeviceIoControl.OutputBufferLength<
(ULONG)FIELD_OFFSET (TDI_ADDRESS_INFO, Address.Address[0].Address) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength-
(ULONG)FIELD_OFFSET (TDI_ADDRESS_INFO, Address) <
IrpSp->Parameters.DeviceIoControl.InputBufferLength-
(ULONG)FIELD_OFFSET (AFD_BIND_INFO, Address) ) ) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
//
// Set up local pointers.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
localAddress = NULL;
addressHandle = NULL;
tdiAddressLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength
- FIELD_OFFSET (AFD_BIND_INFO, Address);
//
// This is a state change operation, there should be no other
// state changes going at the same time.
//
if (!AFD_START_STATE_CHANGE (endpoint, AfdEndpointStateBound)) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
//
// Bomb off if this endpoind already has address associated with it
//
if ( endpoint->State != AfdEndpointStateOpen ) {
status = STATUS_ADDRESS_ALREADY_ASSOCIATED;
goto complete_wrong_state;
}
//
// Remember SD (so we can destroy it later) since it shares the storage
// with device object field
//
oldSd = endpoint->SecurityDescriptor;
AFD_W4_INIT status = STATUS_SUCCESS;
try {
PAFD_BIND_INFO bindInfo;
bindInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
if (Irp->RequestorMode!=KernelMode) {
ProbeForRead (IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
PROBE_ALIGNMENT (AFD_BIND_INFO));
}
//
// Allocate space for local address
//
localAddress = AFD_ALLOCATE_POOL_WITH_QUOTA (
NonPagedPool,
tdiAddressLength,
AFD_LOCAL_ADDRESS_POOL_TAG
);
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag
ASSERT ( localAddress != NULL );
afdShareAccess = bindInfo->ShareAccess;
RtlMoveMemory(
localAddress,
&bindInfo->Address,
tdiAddressLength
);
//
// Validate internal consistency of the transport address structure.
// Note that we HAVE to do this after copying since the malicious
// application can change the content of the buffer on us any time
// and our check will be bypassed.
//
if ((localAddress->TAAddressCount!=1) ||
(LONG)tdiAddressLength<
FIELD_OFFSET (TRANSPORT_ADDRESS,
Address[0].Address[localAddress->Address[0].AddressLength])) {
status = STATUS_INVALID_PARAMETER;
goto complete_state_change;
}
if (IoAllocateMdl (Irp->UserBuffer,
IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
FALSE, // SecondaryBuffer
TRUE, // ChargeQuota
Irp // Irp
)==NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto complete_state_change;
}
MmProbeAndLockPages (Irp->MdlAddress,
Irp->RequestorMode,
IoWriteAccess);
if (MmGetSystemAddressForMdlSafe (Irp->MdlAddress, HighPagePriority)==NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto complete_state_change;
}
}
except (AFD_EXCEPTION_FILTER(status)) {
ASSERT (NT_ERROR (status));
goto complete_state_change;
}
//
// Make sure we have a valid provider info structure.
// (we do not take any locks because this is read access only
// and additional verification will be performed inside of the
// routine under the lock).
// If not, attempt to get it from provider
// (this can happen if when the socket was created, transport
// was not loaded yet)
//
if (!endpoint->TransportInfo->InfoValid) {
status = AfdGetTransportInfo (
&endpoint->TransportInfo->TransportDeviceName,
&endpoint->TransportInfo);
if (!NT_SUCCESS (status)) {
goto complete_state_change;
}
//
// Must be valid because we got success.
//
ASSERT (endpoint->TransportInfo->InfoValid);
}
//
// Cache service flags for quick determination of provider characteristics
// such as bufferring and messaging. Do it even if provider structure was
// valid already because in may have become valid between the time the
// flags were set and we checked it here. In any case update to the same
// value won't hurt.
//
endpoint->TdiServiceFlags = endpoint->TransportInfo->ProviderInfo.ServiceFlags;
//
// Attempt to take ownership of the address.
// We have to do this before we start looking for a conflict
// so if someone does it in parallel with us, will see him or
// he sees us.
//
ASSERT (endpoint->LocalAddress==NULL);
endpoint->LocalAddress = localAddress;
endpoint->LocalAddressLength = tdiAddressLength;
if (IS_TDI_ADDRESS_SECURITY(endpoint) &&
endpoint->SecurityDescriptor!=NULL) {
sd = endpoint->SecurityDescriptor;
shareAccess = 0;
}
else
{
//
// There are three possibilities here.
//
switch (afdShareAccess) {
case AFD_NORMALADDRUSE:
if (IS_TDI_ADDRESS_SECURITY(endpoint)) {
shareAccess = 0;
sd = &AfdEmptySd;
}
else {
//
// This is the default. Application did not request to reuse
// the address that is already owned by someone else, so we
// have to check against all addresses that we know about.
// There is still a possibility that another TDI client has
// this address in exclusive use, so the transport can reject
// our request even if we succeed. Note that we cannot relegate
// this check to the transport because we request shared access
// to the transport address: we cannot request exclusive access
// because this is not what application asked for.
//
if (AfdIsAddressInUse (endpoint, FALSE)) {
status = STATUS_SHARING_VIOLATION;
goto complete_state_change;
}
shareAccess = FILE_SHARE_READ|FILE_SHARE_WRITE;
}
break;
case AFD_REUSEADDRESS:
//
// We are asked to reuse the existing address
//
if (IS_TDI_ADDRESS_SECURITY(endpoint)) {
sd = &AfdEmptySd;
}
shareAccess = FILE_SHARE_READ|FILE_SHARE_WRITE;
break;
case AFD_WILDCARDADDRESS:
//
// Application is binding to a wildcard port, so we leave the
// decision with the transport.
//
if (IS_TDI_ADDRESS_SECURITY(endpoint)) {
shareAccess = 0;
sd = &AfdEmptySd;
}
else {
shareAccess = FILE_SHARE_READ|FILE_SHARE_WRITE;
}
break;
case AFD_EXCLUSIVEADDRUSE:
//
// Application has requested exclisuve access to the address.
// We let the transport to decide by requesting exclusive access.
//
shareAccess = 0;
break;
default:
ASSERT (!"Invalid share access");
status = STATUS_INVALID_PARAMETER;
goto complete_state_change;
}
}
//
// Set create options.
//
options = IO_NO_PARAMETER_CHECKING;
if (IS_TDI_FORCE_ACCESS_CHECK(endpoint)) {
options |= IO_FORCE_ACCESS_CHECK;
}
else {
//
// If this is an open of a raw address, fail if user is
// not an admin and transport does not perform security
// checking itself.
//
if ( endpoint->afdRaw && !AfdDisableRawSecurity) {
if (!endpoint->AdminAccessGranted) {
status = STATUS_ACCESS_DENIED;
goto complete_state_change;
}
}
}
//
// Allocate memory to hold the EA buffer we'll use to specify the
// transport address to NtCreateFile.
//
eaLength = sizeof(FILE_FULL_EA_INFORMATION) - 1 +
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
tdiAddressLength;
if (eaLength<=sizeof (eaBuffer)) {
eaInfo = (PVOID)eaBuffer;
}
else {
try {
#if DBG
eaInfo = AFD_ALLOCATE_POOL_WITH_QUOTA(
NonPagedPool,
eaLength,
AFD_EA_POOL_TAG
);
#else
eaInfo = AFD_ALLOCATE_POOL_WITH_QUOTA(
PagedPool,
eaLength,
AFD_EA_POOL_TAG
);
#endif
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag
ASSERT ( eaInfo != NULL );
}
except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode ();
goto complete_state_change;
}
}
//
// Initialize the EA.
//
eaInfo->NextEntryOffset = 0;
eaInfo->Flags = 0;
eaInfo->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
eaInfo->EaValueLength = (USHORT)tdiAddressLength;
RtlMoveMemory(
eaInfo->EaName,
TdiTransportAddress,
TDI_TRANSPORT_ADDRESS_LENGTH + 1
);
RtlMoveMemory(
&eaInfo->EaName[TDI_TRANSPORT_ADDRESS_LENGTH + 1],
localAddress,
tdiAddressLength
);
//
// Prepare for opening the address object.
// We ask to create a kernel handle which is
// the handle in the context of the system process
// so that application cannot close it on us while
// we are creating and referencing it.
//
InitializeObjectAttributes(
&objectAttributes,
&endpoint->TransportInfo->TransportDeviceName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, // attributes
NULL, // RootDirectory
sd // SecurityDescriptor
);
ASSERT (endpoint->AddressHandle==NULL);
//
// We specify MAXIMUM_ALLOWED access for the following reasons:
// * transport objects are file objects, but regular file access
// permissions don't really make sense - it is not really
// meaningfull to say be able to receive and not send or vice
// versa;
// * we need to specify some access mask to get the IO manager and
// possibly the transport to perform an access check to verify
// that current caller has access, hence MAXIMUM_ALLOWED;
// * the handle and the object is going to be used by AFD in kernel
// mode - no access check will be performed anyway, so it doesn't
// matter what we actually get;
// * MAXIMUM_ALLOWED is not perfect in terms of performance since
// security monitor is going to walk the whole DACL, it would be
// better to specify a specific mask;
// * currently IO manager allows the World only EXECUTE access to
// transport device objects BEFORE any further restrictions imposed
// by a particular transport so we can in theory specify
// just GENERIC_EXECUTE, however, it is not clear if this is
// going to remain valid forever;
//
status = IoCreateFile(
&endpoint->AddressHandle,
MAXIMUM_ALLOWED, // DesiredAccess (see above).
&objectAttributes,
&iosb, // returned status information.
0, // block size (unused).
0, // file attributes.
shareAccess, // share access
FILE_CREATE, // create disposition.
0, // create options.
eaInfo, // EaBuffer
eaLength, // EaLength
CreateFileTypeNone, // CreateFileType
NULL, // ExtraCreateParameters
options
);
if (eaInfo!=(PVOID)eaBuffer) {
AFD_FREE_POOL (eaInfo, AFD_EA_POOL_TAG);
}
if ( !NT_SUCCESS(status) ) {
if ((status==STATUS_SHARING_VIOLATION) ||
(status==STATUS_ADDRESS_ALREADY_EXISTS)) {
if (afdShareAccess==AFD_REUSEADDRESS) {
//
// Map error code if application requested address
// reuse, but the transport denied it (apparently due to
// other client having this address for its exclusive use).
//
status = STATUS_ACCESS_DENIED;
}
}
goto complete_state_change;
}
#if DBG
{
NTSTATUS status1;
OBJECT_HANDLE_FLAG_INFORMATION handleInfo;
handleInfo.Inherit = FALSE;
handleInfo.ProtectFromClose = TRUE;
status1 = ZwSetInformationObject (
endpoint->AddressHandle,
ObjectHandleFlagInformation,
&handleInfo,
sizeof (handleInfo)
);
ASSERT (NT_SUCCESS (status1));
}
#endif
AfdRecordAddrOpened();
//
// Get a pointer to the file object of the address.
//
status = ObReferenceObjectByHandle(
endpoint->AddressHandle,
0L, // DesiredAccess - we don't really need
// one for kernel mode caller.
NULL,
KernelMode,
(PVOID *)&endpoint->AddressFileObject,
NULL
);
if ( !NT_SUCCESS(status) ) {
goto complete_state_change;
}
AfdRecordAddrRef();
//
// Now open the handle for our caller.
// If transport does not support new TDI_SERVICE_FORCE_ACCESS_CHECK_FLAG
// we get the maximum possible access for the handle so that helper
// DLL can do what it wants with it. Of course this compromises the
// security, but we can't enforce it without the transport cooperation.
//
status = ObOpenObjectByPointer(
endpoint->AddressFileObject,
OBJ_CASE_INSENSITIVE,
NULL,
MAXIMUM_ALLOWED,
*IoFileObjectType,
(KPROCESSOR_MODE)((endpoint->TdiServiceFlags&TDI_SERVICE_FORCE_ACCESS_CHECK)
? Irp->RequestorMode
: KernelMode),
&addressHandle
);
if ( !NT_SUCCESS(status) ) {
goto complete_state_change;
}
//
// Remember the device object to which we need to give requests for
// this address object. We can't just use the
// fileObject->DeviceObject pointer because there may be a device
// attached to the transport protocol.
//
endpoint->AddressDeviceObject =
IoGetRelatedDeviceObject( endpoint->AddressFileObject );
#ifdef _AFD_VARIABLE_STACK_
if (endpoint->AddressDeviceObject->StackSize!=endpoint->TransportInfo->StackSize &&
endpoint->AddressDeviceObject->StackSize > AfdTdiStackSize) {
AfdFixTransportEntryPointsForBigStackSize (
endpoint->TransportInfo,
endpoint->AddressDeviceObject->StackSize);
}
#endif //_AFD_VARIABLE_STACK
//
// Set up indication handlers on the address object. Only set up
// appropriate event handlers--don't set unnecessary event handlers.
//
status = AfdSetEventHandler(
endpoint->AddressFileObject,
TDI_EVENT_ERROR,
(PVOID)AfdErrorEventHandler,
endpoint
);
#if DBG
if ( !NT_SUCCESS(status) ) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdBind: Transport %*ls failed setting TDI_EVENT_ERROR: %lx\n",
endpoint->TransportInfo->TransportDeviceName.Length/2,
endpoint->TransportInfo->TransportDeviceName.Buffer,
status ));
}
#endif
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
endpoint->EventsActive = AFD_POLL_SEND;
IF_DEBUG(EVENT_SELECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdBind: Endp %08lX, Active %08lX\n",
endpoint,
endpoint->EventsActive
));
}
status = AfdSetEventHandler(
endpoint->AddressFileObject,
TDI_EVENT_RECEIVE_DATAGRAM,
(PVOID)AfdReceiveDatagramEventHandler,
endpoint
);
#if DBG
if ( !NT_SUCCESS(status) ) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
"AfdBind: Transport %*ls failed setting TDI_EVENT_RECEIVE_DATAGRAM: %lx\n",
endpoint->TransportInfo->TransportDeviceName.Length/2,
endpoint->TransportInfo->TransportDeviceName.Buffer,
status ));
}
#endif
status = AfdSetEventHandler(
endpoint->AddressFileObject,
TDI_EVENT_ERROR_EX,
(PVOID)AfdErrorExEventHandler,
endpoint
);
if ( !NT_SUCCESS(status)) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdBind: Transport %*ls failed setting TDI_EVENT_ERROR_EX: %lx\n",
endpoint->TransportInfo->TransportDeviceName.Length/2,
endpoint->TransportInfo->TransportDeviceName.Buffer,
status ));
}
//
// Since we don't use a spinlock for performance and AfdCleanup
// relies upon endpoint->State != AfdEndpointStateOpen to
// determine whether or not to remove TDI event handlers and
// call AfdUnbind, we must guarantee that all of the above
// instructions have completed and weren't reordered before we
// set the state. If AfdCleanup winds up missing the state
// transition, it's no big deal as the application would have
// to be behaving rather badly for this race condition, and TDI
// will eventually recycle the address (just not immediately).
//
// However, the current code calls a function just prior to
// this assignment in all cases so a memory fence is unecessary.
// We'll leave it commented out until future revisions of the
// code add write instructions we depend on which could be
// reordered. We could also use InterlockedExchange for this
// on endpoint->State.
//
// KeMemoryBarrier();
//
//
// Remember that the endpoint has been bound to a transport address.
// (this is the fact even though the call below can fail for some reason)
//
endpoint->State = AfdEndpointStateBound;
} else {
status = AfdSetEventHandler(
endpoint->AddressFileObject,
TDI_EVENT_DISCONNECT,
(PVOID)AfdDisconnectEventHandler,
endpoint
);
#if DBG
if ( !NT_SUCCESS(status) ) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
"AfdBind: Transport %*ls failed setting TDI_EVENT_DISCONNECT: %lx\n",
endpoint->TransportInfo->TransportDeviceName.Length/2,
endpoint->TransportInfo->TransportDeviceName.Buffer,
status ));
}
#endif
if ( IS_TDI_BUFFERRING(endpoint) ) {
status = AfdSetEventHandler(
endpoint->AddressFileObject,
TDI_EVENT_RECEIVE,
(PVOID)AfdReceiveEventHandler,
endpoint
);
#if DBG
if ( !NT_SUCCESS(status) ) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
"AfdBind: Transport %*ls failed setting TDI_EVENT_RECEIVE: %lx\n",
endpoint->TransportInfo->TransportDeviceName.Length/2,
endpoint->TransportInfo->TransportDeviceName.Buffer,
status ));
}
#endif
//
// PROBLEM: Why don't we check for this
// if (IS_TDI_EXPEDITED (endpoint)) {
status = AfdSetEventHandler(
endpoint->AddressFileObject,
TDI_EVENT_RECEIVE_EXPEDITED,
(PVOID)AfdReceiveExpeditedEventHandler,
endpoint
);
#if DBG
if ( !NT_SUCCESS(status) ) {
DbgPrint( "AfdBind: Transport %*ls failed setting TDI_EVENT_RECEIVE_EXPEDITED: %lx\n",
endpoint->TransportInfo->TransportDeviceName.Length/2,
endpoint->TransportInfo->TransportDeviceName.Buffer,
status );
}
#endif
// }
status = AfdSetEventHandler(
endpoint->AddressFileObject,
TDI_EVENT_SEND_POSSIBLE,
(PVOID)AfdSendPossibleEventHandler,
endpoint
);
#if DBG
if ( !NT_SUCCESS(status) ) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
"AfdBind: Transport %*ls failed setting TDI_EVENT_SEND_POSSIBLE: %lx\n",
endpoint->TransportInfo->TransportDeviceName.Length/2,
endpoint->TransportInfo->TransportDeviceName.Buffer,
status ));
}
#endif
} else {
status = AfdSetEventHandler(
endpoint->AddressFileObject,
TDI_EVENT_RECEIVE,
(PVOID)AfdBReceiveEventHandler,
endpoint
);
#if DBG
if ( !NT_SUCCESS(status) ) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
"AfdBind: Transport %*ls failed setting TDI_EVENT_RECEIVE: %lx\n",
endpoint->TransportInfo->TransportDeviceName.Length/2,
endpoint->TransportInfo->TransportDeviceName.Buffer,
status ));
}
#endif
if (IS_TDI_EXPEDITED (endpoint)) {
status = AfdSetEventHandler(
endpoint->AddressFileObject,
TDI_EVENT_RECEIVE_EXPEDITED,
(PVOID)AfdBReceiveExpeditedEventHandler,
endpoint
);
#if DBG
if ( !NT_SUCCESS(status) ) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdBind: Transport %*ls failed setting TDI_EVENT_RECEIVE_EXPEDITED: %lx\n",
endpoint->TransportInfo->TransportDeviceName.Length/2,
endpoint->TransportInfo->TransportDeviceName.Buffer,
status ));
}
#endif
}
if (!AfdDisableChainedReceive) {
status = AfdSetEventHandler(
endpoint->AddressFileObject,
TDI_EVENT_CHAINED_RECEIVE,
(PVOID)AfdBChainedReceiveEventHandler,
endpoint
);
if ( !NT_SUCCESS(status) ) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdBind: Transport %*ls failed setting TDI_EVENT_CHAINED_RECEIVE: %lx\n",
endpoint->TransportInfo->TransportDeviceName.Length/2,
endpoint->TransportInfo->TransportDeviceName.Buffer,
status ));
}
}
}
if (IS_CROOT_ENDPOINT(endpoint)) {
PAFD_CONNECTION connection;
//
// Create root connection
// This one will be used to send data to all
// leaf nodes (what if there are none -> the transport
// should handle this.
//
status = AfdCreateConnection(
endpoint->TransportInfo,
endpoint->AddressHandle,
IS_TDI_BUFFERRING(endpoint),
endpoint->InLine,
endpoint->OwningProcess,
&connection
);
if (!NT_SUCCESS(status)) {
goto complete_state_change;
}
//
// Set up a referenced pointer from the connection to the endpoint.
// Note that we set up the connection's pointer to the endpoint
// BEFORE the endpoint's pointer to the connection so that AfdPoll
// doesn't try to back reference the endpoint from the connection.
//
REFERENCE_ENDPOINT( endpoint );
connection->Endpoint = endpoint;
//
// Remember that this is now a connecting type of endpoint, and set
// up a pointer to the connection in the endpoint. This is
// implicitly a referenced pointer.
//
endpoint->Common.VirtualCircuit.Connection = connection;
endpoint->Type = AfdBlockTypeVcConnecting;
//
// The root connection is marked as connected immediately upon
// creation. See the comment above
//
AfdAddConnectedReference (connection);
//
// Since we don't use a spinlock for performance and AfdCleanup
// relies upon endpoint->State != AfdEndpointStateOpen to
// determine whether or not to remove TDI event handlers and
// call AfdUnbind, we must guarantee that all of the above
// instructions have completed and weren't reordered before we
// set the state. If AfdCleanup winds up missing the state
// transition, it's no big deal as the application would have
// to be behaving rather badly for this race condition, and TDI
// will eventually recycle the address (just not immediately).
//
// However, the current code calls a function just prior to
// this assignment in all cases so a memory fence is unecessary.
// We'll leave it commented out until future revisions of the
// code add write instructions we depend on which could be
// reordered. We could also use InterlockedExchange for this
// on endpoint->State.
//
// KeMemoryBarrier();
//
endpoint->State = AfdEndpointStateConnected;
connection->State = AfdConnectionStateConnected;
ASSERT( IS_TDI_BUFFERRING(endpoint) == connection->TdiBufferring );
} else {
//
// Since we don't use a spinlock for performance and AfdCleanup
// relies upon endpoint->State != AfdEndpointStateOpen to
// determine whether or not to remove TDI event handlers and
// call AfdUnbind, we must guarantee that all of the above
// instructions have completed and weren't reordered before we
// set the state. If AfdCleanup winds up missing the state
// transition, it's no big deal as the application would have
// to be behaving rather badly for this race condition, and TDI
// will eventually recycle the address (just not immediately).
//
// However, the current code calls a function just prior to
// this assignment in all cases so a memory fence is unecessary.
// We'll leave it commented out until future revisions of the
// code add write instructions we depend on which could be
// reordered. We could also use InterlockedExchange for this
// on endpoint->State.
//
// KeMemoryBarrier();
//
//
// Remember that the endpoint has been bound to a transport address.
// (this is the fact even though the call below can fail for some reason)
//
endpoint->State = AfdEndpointStateBound;
}
}
AFD_END_STATE_CHANGE (endpoint);
//
// endpoint->State is no longer Open. Dispose of the SD
//
if (oldSd != NULL) {
ObDereferenceSecurityDescriptor( oldSd, 1 );
}
TdiBuildQueryInformation(
Irp,
endpoint->AddressDeviceObject,
endpoint->AddressFileObject,
AfdRestartBindGetAddress,
endpoint,
TDI_QUERY_ADDRESS_INFO,
Irp->MdlAddress
);
//
// Save address handle to use in completion routine
//
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = addressHandle;
IF_DEBUG(BIND) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdBind: endpoint at %p (address at %p, address file at %p).\n",
endpoint,
endpoint->LocalAddress,
endpoint->AddressFileObject ));
}
status = AfdIoCallDriver( endpoint, endpoint->AddressDeviceObject, Irp );
return status;
complete_state_change:
if (endpoint->AddressFileObject!=NULL) {
ObDereferenceObject (endpoint->AddressFileObject);
endpoint->AddressFileObject = NULL;
ASSERT (endpoint->AddressHandle!=NULL);
}
//
// Restore SD (might have been over-written by AddressDeviceObject)
// We're still holding endpoint state change lock.
//
ASSERT (endpoint->State == AfdEndpointStateOpen);
endpoint->SecurityDescriptor = oldSd;
if (endpoint->AddressHandle!=NULL) {
#if DBG
{
NTSTATUS status1;
OBJECT_HANDLE_FLAG_INFORMATION handleInfo;
handleInfo.Inherit = FALSE;
handleInfo.ProtectFromClose = FALSE;
status1 = ZwSetInformationObject (
endpoint->AddressHandle,
ObjectHandleFlagInformation,
&handleInfo,
sizeof (handleInfo)
);
ASSERT (NT_SUCCESS (status1));
}
#endif
ZwClose(endpoint->AddressHandle);
endpoint->AddressHandle = NULL;
ASSERT (localAddress!=NULL);
}
if (localAddress!=NULL) {
//
// Need to have exclusive access to make sure no one
// uses it (to compare) as we are going to free it.
// We'll use a local variable to free memory
//
//
// Make sure the thread in which we execute cannot get
// suspeneded in APC while we own the global resource.
//
KeEnterCriticalRegion ();
ExAcquireResourceExclusiveLite( AfdResource, TRUE);
endpoint->LocalAddress = NULL;
endpoint->LocalAddressLength = 0;
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
AFD_FREE_POOL (
localAddress,
AFD_LOCAL_ADDRESS_POOL_TAG
);
}
complete_wrong_state:
AFD_END_STATE_CHANGE (endpoint);
//
// Free MDL here as IO system can't do it if it is
// not locked.
//
if (Irp->MdlAddress!=NULL) {
if (Irp->MdlAddress->MdlFlags & MDL_PAGES_LOCKED) {
MmUnlockPages (Irp->MdlAddress);
}
IoFreeMdl (Irp->MdlAddress);
Irp->MdlAddress = NULL;
}
complete:
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdBind
NTSTATUS
AfdRestartBindGetAddress (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
NTSTATUS status;
PAFD_ENDPOINT endpoint = Context;
UNREFERENCED_PARAMETER (DeviceObject);
ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint));
//
// If the request succeeded, save the address in the endpoint so
// we can use it to handle address sharing.
//
if ( NT_SUCCESS(Irp->IoStatus.Status) ) {
ULONG addressLength;
//
// First determine the length of the address by walking the MDL
// chain.
//
//
// We cannot have a chain here.
//
ASSERT( Irp->MdlAddress != NULL);
ASSERT( Irp->MdlAddress->Next == NULL );
//
// If the new address is longer than the original address, allocate
// a new local address buffer. The +4 accounts for the ActivityCount
// field that is returned by a query address but is not part
// of a TRANSPORT_ADDRESS.
//
// This cannot happen, in any case msafd does not retry if buffer is
// insuffucient, so application perceives this as failure to bind
// or get address.
//
addressLength = MmGetMdlByteCount (Irp->MdlAddress) - FIELD_OFFSET (TDI_ADDRESS_INFO, Address);
if (addressLength>endpoint->LocalAddressLength) {
addressLength = (ULONG)Irp->IoStatus.Information - FIELD_OFFSET (TDI_ADDRESS_INFO, Address);
}
if ( addressLength <= endpoint->LocalAddressLength) {
status = TdiCopyMdlToBuffer(
Irp->MdlAddress,
FIELD_OFFSET (TDI_ADDRESS_INFO, Address),
endpoint->LocalAddress,
0,
addressLength,
&endpoint->LocalAddressLength
);
ASSERT( NT_SUCCESS(status) );
}
else {
DbgPrint ("AfdRestartBindGetAddress: Endpoint %p transport returned"
" address is longer than the original one.\n",
endpoint);
ASSERT (FALSE);
}
}
else {
//
//
//
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
"AfdRestartBindGetAddress: Transport %*ls failed get address query, status %lx.\n",
endpoint->TransportInfo->TransportDeviceName.Length/2,
endpoint->TransportInfo->TransportDeviceName.Buffer,
Irp->IoStatus.Status));
}
//
// If pending has been returned for this irp then mark the current
// stack as pending.
//
if ( Irp->PendingReturned ) {
IoMarkIrpPending( Irp );
}
//
// Retreive and return address handle in the information field
//
Irp->IoStatus.Information =
(ULONG_PTR)IoGetCurrentIrpStackLocation (Irp)->Parameters.DeviceIoControl.Type3InputBuffer;
AfdCompleteOutstandingIrp( endpoint, Irp );
return STATUS_SUCCESS;
} // AfdRestartBindGetAddress
NTSTATUS
FASTCALL
AfdGetAddress (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Handles the IOCTL_AFD_BIND IOCTL.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
NTSTATUS status;
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
PAGED_CODE( );
Irp->IoStatus.Information = 0;
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength<
(ULONG) FIELD_OFFSET(TDI_ADDRESS_INFO, Address.Address[0].Address)) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
AFD_W4_INIT status = STATUS_SUCCESS;
try {
if (IoAllocateMdl (Irp->UserBuffer,
IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
FALSE, // SecondaryBuffer
TRUE, // ChargeQuota
Irp // Irp
)==NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto complete;
}
MmProbeAndLockPages (Irp->MdlAddress,
Irp->RequestorMode,
IoWriteAccess);
if (MmGetSystemAddressForMdlSafe (Irp->MdlAddress, HighPagePriority)==NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto complete;
}
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
goto complete;
}
//
// Make sure that the endpoint is in the correct state.
//
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
if ( endpoint->State!=AfdEndpointStateBound &&
endpoint->State != AfdEndpointStateConnected ) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
//
// If the endpoint is connected, use the connection's file object.
// Otherwise, use the address file object. Don't use the connection
// file object if this is a Netbios endpoint because NETBT cannot
// support this TDI feature.
//
if ( endpoint->LocalAddress->Address[0].AddressType !=
TDI_ADDRESS_TYPE_NETBIOS &&
endpoint->Type == AfdBlockTypeVcConnecting &&
endpoint->State == AfdEndpointStateConnected &&
((connection=AfdGetConnectionReferenceFromEndpoint (endpoint)) != NULL)
) {
ASSERT( connection->Type == AfdBlockTypeConnection );
fileObject = connection->FileObject;
deviceObject = connection->DeviceObject;
//
// Reference file object so it cannot go away even if
// connection is disassociated in TransmitFile (REUSE)
// and closed.
//
ObReferenceObject (fileObject);
DEREFERENCE_CONNECTION (connection);
}
//
// Reference address file object under the lock so it cannot go away
// in TransmitFile (REUSE).
//
else if ((fileObject=AfdGetAddressFileReference (endpoint))!=NULL) {
fileObject = endpoint->AddressFileObject;
deviceObject = IoGetRelatedDeviceObject (fileObject);
}
else {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
//
// Set up the query info to the TDI provider.
//
TdiBuildQueryInformation(
Irp,
deviceObject,
fileObject,
AfdRestartGetAddress,
fileObject,
TDI_QUERY_ADDRESS_INFO,
Irp->MdlAddress
);
//
// Call the TDI provider to get the address.
//
return AfdIoCallDriver( endpoint, deviceObject, Irp );
complete:
if (Irp->MdlAddress!=NULL) {
if (Irp->MdlAddress->MdlFlags & MDL_PAGES_LOCKED) {
MmUnlockPages (Irp->MdlAddress);
}
IoFreeMdl (Irp->MdlAddress);
Irp->MdlAddress = NULL;
}
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdGetAddress
PFILE_OBJECT
AfdGetAddressFileReference (
PAFD_ENDPOINT Endpoint
)
{
AFD_LOCK_QUEUE_HANDLE lockHandle;
PFILE_OBJECT fileObject;
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
fileObject = Endpoint->AddressFileObject;
if (fileObject!=NULL) {
ObReferenceObject (fileObject);
}
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
return fileObject;
}
NTSTATUS
AfdRestartGetAddress (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
NTSTATUS status;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp);
PAFD_ENDPOINT endpoint = irpSp->FileObject->FsContext;
PFILE_OBJECT fileObject = Context;
UNREFERENCED_PARAMETER (DeviceObject);
ASSERT (IS_AFD_ENDPOINT_TYPE( endpoint ));
//
// If the request succeeded, save the address in the endpoint so
// we can use it to handle address sharing.
// Avoid updating accepting endpoint since they share address
// with listening endpoint.
//
if ( NT_SUCCESS(Irp->IoStatus.Status) &&
(endpoint->Type!=AfdBlockTypeVcConnecting ||
endpoint->Common.VcConnecting.ListenEndpoint==NULL)) {
ULONG addressLength;
//
// First determine the length of the address by walking the MDL
// chain.
//
//
// We cannot have a chain here.
//
ASSERT( Irp->MdlAddress != NULL);
ASSERT( Irp->MdlAddress->Next == NULL );
//
// If the new address is longer than the original address, allocate
// a new local address buffer. The +4 accounts for the ActivityCount
// field that is returned by a query address but is not part
// of a TRANSPORT_ADDRESS.
//
// This cannot happen, in any case msafd does not retry if buffer is
// insuffucient, so application perceives this as failure to bind
// or get address.
//
addressLength = MmGetMdlByteCount (Irp->MdlAddress) - FIELD_OFFSET (TDI_ADDRESS_INFO, Address);
if (addressLength>endpoint->LocalAddressLength) {
addressLength = (ULONG)Irp->IoStatus.Information - FIELD_OFFSET (TDI_ADDRESS_INFO, Address);
}
if ( addressLength <= endpoint->LocalAddressLength) {
status = TdiCopyMdlToBuffer(
Irp->MdlAddress,
FIELD_OFFSET (TDI_ADDRESS_INFO, Address),
endpoint->LocalAddress,
0,
addressLength,
&endpoint->LocalAddressLength
);
ASSERT( NT_SUCCESS(status) );
}
else {
DbgPrint ("AfdRestartGetAddress: Endpoint %p transport returned"
" address is longer than the original one.\n",
endpoint);
ASSERT (FALSE);
}
}
//
// If pending has been returned for this irp then mark the current
// stack as pending.
//
if ( Irp->PendingReturned ) {
IoMarkIrpPending( Irp );
}
AfdCompleteOutstandingIrp( endpoint, Irp );
ObDereferenceObject (fileObject);
return STATUS_SUCCESS;
} // AfdRestartGetAddress
const CHAR ZeroNodeAddress[6]={0};
const CHAR ZeroIP6Address[16]={0};
BOOLEAN
AfdAreTransportAddressesEqual (
IN PTRANSPORT_ADDRESS EndpointAddress,
IN ULONG EndpointAddressLength,
IN PTRANSPORT_ADDRESS RequestAddress,
IN ULONG RequestAddressLength,
IN BOOLEAN HonorWildcardIpPortInEndpointAddress
)
{
//
// Make sure we can safely access the address type and length fields
//
if ((EndpointAddressLength<(ULONG)FIELD_OFFSET (TRANSPORT_ADDRESS,Address[0].Address))
|| (RequestAddressLength<(ULONG)FIELD_OFFSET (TRANSPORT_ADDRESS,Address[0].Address)) ) {
return FALSE;
}
if ( EndpointAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IP &&
RequestAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IP ) {
TDI_ADDRESS_IP UNALIGNED *ipEndpointAddress;
TDI_ADDRESS_IP UNALIGNED *ipRequestAddress;
//
// They are both IP addresses. If the ports are the same, and
// the IP addresses are or _could_be_ the same, then the addresses
// are equal. The "cound be" part is true if either IP address
// is 0, the "wildcard" IP address.
//
ipEndpointAddress = (TDI_ADDRESS_IP UNALIGNED *)&EndpointAddress->Address[0].Address[0];
ipRequestAddress = (TDI_ADDRESS_IP UNALIGNED *)&RequestAddress->Address[0].Address[0];
if ( (EndpointAddressLength>=(ULONG)FIELD_OFFSET (TA_IP_ADDRESS, Address[0].Address[0].sin_zero)) &&
(RequestAddressLength>=(ULONG)FIELD_OFFSET (TA_IP_ADDRESS, Address[0].Address[0].sin_zero)) &&
( ipEndpointAddress->sin_port == ipRequestAddress->sin_port ||
( HonorWildcardIpPortInEndpointAddress &&
ipEndpointAddress->sin_port == 0 ) ) &&
( ipEndpointAddress->in_addr == ipRequestAddress->in_addr ||
ipEndpointAddress->in_addr == 0 || ipRequestAddress->in_addr == 0 ) ) {
return TRUE;
}
//
// The addresses are not equal.
//
return FALSE;
}
if ( EndpointAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IP6 &&
RequestAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IP6 ) {
TDI_ADDRESS_IP6 UNALIGNED *ipEndpointAddress;
TDI_ADDRESS_IP6 UNALIGNED *ipRequestAddress;
C_ASSERT (sizeof (ZeroIP6Address)==sizeof (ipEndpointAddress->sin6_addr));
//
// They are both IPv6 addresses. If the ports are the same, and
// the IPv6 addresses are or _could_be_ the same, then the addresses
// are equal. The "could be" part is true if either IPv6 address
// is the unspecified IPv6 address.
//
ipEndpointAddress = (TDI_ADDRESS_IP6 UNALIGNED *)&EndpointAddress->Address[0].Address;
ipRequestAddress = (TDI_ADDRESS_IP6 UNALIGNED *)&RequestAddress->Address[0].Address;
if ( (EndpointAddressLength>=sizeof (TA_IP6_ADDRESS)) &&
(RequestAddressLength>=sizeof (TA_IP6_ADDRESS)) &&
(ipEndpointAddress->sin6_port == ipRequestAddress->sin6_port ||
( HonorWildcardIpPortInEndpointAddress &&
ipEndpointAddress->sin6_port == 0 ) ) &&
( RtlEqualMemory(&ipEndpointAddress->sin6_addr,
&ipRequestAddress->sin6_addr,
sizeof (ipEndpointAddress->sin6_addr)) ||
RtlEqualMemory(&ipEndpointAddress->sin6_addr,
ZeroIP6Address,
sizeof (ipEndpointAddress->sin6_addr)) ||
RtlEqualMemory(&ipRequestAddress->sin6_addr,
ZeroIP6Address,
sizeof (ipEndpointAddress->sin6_addr)) ) ) {
return TRUE;
}
//
// The addresses are not equal.
//
return FALSE;
}
if ( EndpointAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IPX &&
RequestAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IPX ) {
TDI_ADDRESS_IPX UNALIGNED *ipxEndpointAddress;
TDI_ADDRESS_IPX UNALIGNED *ipxRequestAddress;
C_ASSERT (sizeof (ZeroNodeAddress)==sizeof (ipxEndpointAddress->NodeAddress));
ipxEndpointAddress = (TDI_ADDRESS_IPX UNALIGNED *)&EndpointAddress->Address[0].Address[0];
ipxRequestAddress = (TDI_ADDRESS_IPX UNALIGNED *)&RequestAddress->Address[0].Address[0];
//
// They are both IPX addresses. Check the network addresses
// first--if they don't match and both != 0, the addresses
// are different.
//
if ( (EndpointAddressLength<sizeof (TA_IPX_ADDRESS)) ||
(RequestAddressLength<sizeof (TA_IPX_ADDRESS)) ||
( ipxEndpointAddress->NetworkAddress != ipxRequestAddress->NetworkAddress &&
ipxEndpointAddress->NetworkAddress != 0 &&
ipxRequestAddress->NetworkAddress != 0 )) {
return FALSE;
}
//
// Now check the node addresses. Again, if they don't match
// and neither is 0, the addresses don't match.
//
ASSERT( ZeroNodeAddress[0] == 0 );
ASSERT( ZeroNodeAddress[1] == 0 );
ASSERT( ZeroNodeAddress[2] == 0 );
ASSERT( ZeroNodeAddress[3] == 0 );
ASSERT( ZeroNodeAddress[4] == 0 );
ASSERT( ZeroNodeAddress[5] == 0 );
if ( !RtlEqualMemory(
ipxEndpointAddress->NodeAddress,
ipxRequestAddress->NodeAddress,
6 ) &&
!RtlEqualMemory(
ipxEndpointAddress->NodeAddress,
ZeroNodeAddress,
6 ) &&
!RtlEqualMemory(
ipxRequestAddress->NodeAddress,
ZeroNodeAddress,
6 ) ) {
return FALSE;
}
//
// Finally, make sure the socket numbers match.
//
if ( ipxEndpointAddress->Socket != ipxRequestAddress->Socket ) {
return FALSE;
}
return TRUE;
}
//
// If either address is not of a known address type, then do a
// simple memory compare. (Don't go out of bounds on either
// structure).
//
if (RequestAddressLength>EndpointAddressLength)
RequestAddressLength = EndpointAddressLength;
return (BOOLEAN)(EndpointAddressLength == RtlCompareMemory(
EndpointAddress,
RequestAddress,
RequestAddressLength ) );
} // AfdAreTransportAddressesEqual
BOOLEAN
AfdIsAddressInUse (
PAFD_ENDPOINT Endpoint,
BOOLEAN OtherProcessesOnly
)
{
PLIST_ENTRY listEntry;
BOOLEAN res = FALSE;
PAGED_CODE ();
//
// We use shared access to the resource because we only need to make
// sure that endpoint list is not modified while we are accessing it
// and existing local addresses are not removed (both of these
// operations are performed under exclusive access).
//
//
// Make sure the thread in which we execute cannot get
// suspeneded in APC while we own the global resource.
//
KeEnterCriticalRegion ();
ExAcquireResourceSharedLite( AfdResource, TRUE );
//
// Walk the global list of endpoints,
// and compare this address againat the address on each endpoint.
//
for ( listEntry = AfdEndpointListHead.Flink;
listEntry != &AfdEndpointListHead;
listEntry = listEntry->Flink ) {
PAFD_ENDPOINT compareEndpoint;
compareEndpoint = CONTAINING_RECORD(
listEntry,
AFD_ENDPOINT,
GlobalEndpointListEntry
);
ASSERT( IS_AFD_ENDPOINT_TYPE( compareEndpoint ) );
//
// Check whether the endpoint has a local address, whether
// the endpoint has been disconnected, whether the
// endpoint is in the process of closing, and whether
// it represents accepted connection. If any of these
// is true, don't compare addresses with this endpoint.
//
if (compareEndpoint!=Endpoint &&
compareEndpoint->LocalAddress != NULL &&
( (compareEndpoint->DisconnectMode &
(AFD_PARTIAL_DISCONNECT_SEND |
AFD_ABORTIVE_DISCONNECT) ) == 0 ) &&
(compareEndpoint->State != AfdEndpointStateClosing) &&
((compareEndpoint->State != AfdEndpointStateConnected)
|| (compareEndpoint->Type!=AfdBlockTypeVcConnecting)
|| (compareEndpoint->Common.VcConnecting.ListenEndpoint==NULL)) &&
(!OtherProcessesOnly ||
compareEndpoint->OwningProcess!=Endpoint->OwningProcess)
) {
//
// Compare the bits in the endpoint's address and the
// address we're attempting to bind to. Note that we
// also compare the transport device names on the
// endpoints, as it is legal to bind to the same address
// on different transports (e.g. bind to same port in
// TCP and UDP). We can just compare the transport
// device name pointers because unique names are stored
// globally.
//
if ( Endpoint->TransportInfo ==
compareEndpoint->TransportInfo &&
compareEndpoint->LocalAddressLength == Endpoint->LocalAddressLength &&
AfdAreTransportAddressesEqual(
compareEndpoint->LocalAddress,
compareEndpoint->LocalAddressLength,
Endpoint->LocalAddress,
Endpoint->LocalAddressLength,
FALSE
) ) {
//
// The addresses are equal.
//
res = TRUE;
break;
}
}
}
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
return res;
}
#if NOT_YET
NTSTATUS
AfdStealClosedEnpointAddress (
PAFD_ENDPOINT Endpoint,
NTSTATUS Status
)
/*++
Routine Description:
Allows reuse of address consumed by listening socket being closed
This a workaround the problem posed by TDI semantics. The TDI client
must maintain an address object for connections accepted on such object
to stay open. The prevents creation of another address object with
the same address although from the transport point of view there
is no conflict.
Arguments:
Endpoint - endpoint that failed address object creation due to a conflict
Status - original failure status
Return Value:
STATUS_SUCCESS - the candidate for reuse was found
Original status passed - no listening endpoint with matching address was found
Other - failure to create handle for some other reason.
--*/
{
PLIST_ENTRY listEntry;
PAGED_CODE ();
//
// We use shared access to the resource because we only need to make
// sure that endpoint list is not modified while we are accessing it
// and existing local addresses are not removed (both of these
// operations are performed under exclusive access).
//
//
// Make sure the thread in which we execute cannot get
// suspeneded in APC while we own the global resource.
//
KeEnterCriticalRegion ();
ExAcquireResourceSharedLite( AfdResource, TRUE );
//
// Walk the global list of endpoints,
// and compare this address againat the address on each endpoint.
//
for ( listEntry = AfdEndpointListHead.Flink;
listEntry != &AfdEndpointListHead;
listEntry = listEntry->Flink ) {
PAFD_ENDPOINT compareEndpoint;
compareEndpoint = CONTAINING_RECORD(
listEntry,
AFD_ENDPOINT,
GlobalEndpointListEntry
);
ASSERT( IS_AFD_ENDPOINT_TYPE( compareEndpoint ) );
if (compareEndpoint!=Endpoint &&
compareEndpoint->EndpointCleanedUp &&
compareEndpoint->TransportInfo==
Endpoint->TransportInfo &&
compareEndpoint->Listening &&
compareEndpoint->AddressHandle!=NULL &&
compareEndpoint->LocalAddressLength == Endpoint->LocalAddressLength &&
AfdAreTransportAddressesEqual(
compareEndpoint->LocalAddress,
compareEndpoint->LocalAddressLength,
Endpoint->LocalAddress,
Endpoint->LocalAddressLength,
FALSE
)) {
//
// There is a theoretical security issue here.
// We open a handle from kernel mode and thus
// bypass the security checks which were done by the
// original client when calling IoCreateFile.
//
Status = ObOpenObjectByPointer(
compareEndpoint->AddressFileObject,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
MAXIMUM_ALLOWED,
*IoFileObjectType,
KernelMode,
&Endpoint->AddressHandle
);
break;
}
}
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
return Status;
}
#endif // NOT_YET