windows-nt/Source/XPSP1/NT/net/sockets/winsock2/wsp/afdsys/bind.c

1491 lines
48 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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
);
#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, AfdRestartGetAddress )
#pragma alloc_text( PAGEAFD, AfdRestartBindGetAddress )
#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;
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<
sizeof (TDI_ADDRESS_INFO) ||
(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;
}
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)) {
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
//
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;
//
// There are three possibilities here.
//
switch (afdShareAccess) {
case AFD_NORMALADDRUSE:
//
// 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
//
// Check if we are configured to prevent sharing addresses
// between applications by default.
//
if (AfdDontShareAddresses) {
if (AfdIsAddressInUse (endpoint, TRUE)) {
status = STATUS_ACCESS_DENIED;
goto complete_state_change;
}
}
//
// Lack of break is intentional.
//
case AFD_WILDCARDADDRESS:
//
// Application is binding to a wildcard port, so we leave the
// decision with the transport.
//
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, but perform a security check
// so that only admin can take an address for exclusive use.
// The transport can check for this but not all of them are
// aware of this new feature.
//
if (!endpoint->AdminAccessGranted) {
status = STATUS_ACCESS_DENIED;
goto complete_state_change;
}
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,
NULL
);
ASSERT (endpoint->AddressHandle==NULL);
status = IoCreateFile(
&endpoint->AddressHandle,
MAXIMUM_ALLOWED,
&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) ) {
//
// Map error code if application requested address
// reuse, but the transport denied it (due to
// other client having this address for exclusive use).
//
if (((status==STATUS_SHARING_VIOLATION) ||
(status==STATUS_ADDRESS_ALREADY_EXISTS))
&&
(afdShareAccess==AFD_REUSEADDRESS)) {
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
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 );
//
// 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,
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,
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,
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 ));
}
//
// 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,
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,
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,
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,
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,
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,
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,
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->TransportDeviceName,
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);
endpoint->State = AfdEndpointStateConnected;
connection->State = AfdConnectionStateConnected;
ASSERT( IS_TDI_BUFFERRING(endpoint) == connection->TdiBufferring );
}
else {
//
// 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);
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);
}
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;
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<
sizeof (TDI_ADDRESS_INFO)) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
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)) {
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;
DEREFERENCE_CONNECTION (connection);
} else {
fileObject = endpoint->AddressFileObject;
deviceObject = endpoint->AddressDeviceObject;
}
//
// Set up the query info to the TDI provider.
//
TdiBuildQueryInformation(
Irp,
deviceObject,
fileObject,
AfdRestartGetAddress,
endpoint,
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
NTSTATUS
AfdRestartGetAddress (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
NTSTATUS status;
PAFD_ENDPOINT endpoint = Context;
//
// 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 ("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 );
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 (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 ( compareEndpoint->LocalAddressLength == Endpoint->LocalAddressLength &&
AfdAreTransportAddressesEqual(
compareEndpoint->LocalAddress,
compareEndpoint->LocalAddressLength,
Endpoint->LocalAddress,
Endpoint->LocalAddressLength,
FALSE
)
&&
Endpoint->TransportInfo ==
compareEndpoint->TransportInfo ) {
//
// The addresses are equal.
//
res = TRUE;
break;
}
}
}
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
return res;
}