/*++ 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 ( (EndpointAddressLengthNetworkAddress != 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; }