/*++ Copyright (c) 1992-1999 Microsoft Corporation Module Name: blkendp.c Abstract: This module contains allocate, free, close, reference, and dereference routines for AFD endpoints. Author: David Treadwell (davidtr) 10-Mar-1992 Revision History: Vadim Eydelman (vadime) 1999 - Don't attach to system proces, use system handles instead Delayed acceptance endpoints. --*/ #include "afdp.h" VOID AfdFreeEndpointResources ( PAFD_ENDPOINT endpoint ); VOID AfdFreeEndpoint ( IN PVOID Context ); PAFD_ENDPOINT AfdReuseEndpoint ( VOID ); VOID AfdFreeTransportInfo ( PAFD_TRANSPORT_INFO TransportInfo ); #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, AfdAllocateEndpoint ) #pragma alloc_text( PAGE, AfdFreeEndpointResources ) #pragma alloc_text( PAGE, AfdFreeEndpoint ) #pragma alloc_text( PAGE, AfdReuseEndpoint ) #pragma alloc_text( PAGE, AfdGetTransportInfo ) #pragma alloc_text( PAGE, AfdFreeTransportInfo ) #pragma alloc_text( PAGEAFD, AfdRefreshEndpoint ) #pragma alloc_text( PAGEAFD, AfdDereferenceEndpoint ) #if REFERENCE_DEBUG #pragma alloc_text( PAGEAFD, AfdReferenceEndpoint ) #endif #pragma alloc_text( PAGEAFD, AfdFreeQueuedConnections ) #endif NTSTATUS AfdAllocateEndpoint ( OUT PAFD_ENDPOINT * NewEndpoint, IN PUNICODE_STRING TransportDeviceName, IN LONG GroupID ) /*++ Routine Description: Allocates and initializes a new AFD endpoint structure. Arguments: NewEndpoint - Receives a pointer to the new endpoint structure if successful. TransportDeviceName - the name of the TDI transport provider corresponding to the endpoint structure. GroupID - Identifies the group ID for the new endpoint. Return Value: NTSTATUS - The completion status. --*/ { PAFD_ENDPOINT endpoint; PAFD_TRANSPORT_INFO transportInfo = NULL; NTSTATUS status; AFD_GROUP_TYPE groupType; PAGED_CODE( ); DEBUG *NewEndpoint = NULL; if ( TransportDeviceName != NULL ) { // // First, make sure that the transport device name is stored globally // for AFD. Since there will typically only be a small number of // transport device names, we store the name strings once globally // for access by all endpoints. // status = AfdGetTransportInfo( TransportDeviceName, &transportInfo ); // // If transport device is not activated, we'll try again during bind // if ( !NT_SUCCESS (status) && (status!=STATUS_OBJECT_NAME_NOT_FOUND) && (status!=STATUS_OBJECT_PATH_NOT_FOUND) && (status!=STATUS_NO_SUCH_DEVICE) ) { // // Dereference transport info structure if one was created for us. // (Should not happen in current implementation). // ASSERT (transportInfo==NULL); return status; } ASSERT (transportInfo!=NULL); } // // Validate the incoming group ID, allocate a new one if necessary. // if( AfdGetGroup( &GroupID, &groupType ) ) { PEPROCESS process = IoGetCurrentProcess (); status = PsChargeProcessPoolQuota( process, NonPagedPool, sizeof (AFD_ENDPOINT) ); if (!NT_SUCCESS (status)) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL, "AfdAllocateEndpoint: PsChargeProcessPoolQuota failed.\n" )); goto Cleanup; } // See if we have too many endpoins waiting to be freed and reuse one of them if ((AfdEndpointsFreeingReferenceCount = 2; // // Initialize the endpoint structure. // if ( TransportDeviceName == NULL ) { endpoint->Type = AfdBlockTypeHelper; endpoint->State = AfdEndpointStateInvalid; } else { endpoint->Type = AfdBlockTypeEndpoint; endpoint->State = AfdEndpointStateOpen; endpoint->TransportInfo = transportInfo; // // Cache service flags for quick determination of provider characteristics // such as bufferring and messaging // if (transportInfo->InfoValid) { endpoint->TdiServiceFlags = endpoint->TransportInfo->ProviderInfo.ServiceFlags; } } endpoint->GroupID = GroupID; endpoint->GroupType = groupType; AfdInitializeSpinLock( &endpoint->SpinLock ); InitializeListHead (&endpoint->RoutingNotifications); InitializeListHead (&endpoint->RequestList); #if DBG InitializeListHead( &endpoint->OutstandingIrpListHead ); #endif // // Remember the process which opened the endpoint. We'll use this to // charge quota to the process as necessary. Reference the process // so that it does not go away until we have returned all charged // quota to the process. // endpoint->OwningProcess = process; ObReferenceObject(endpoint->OwningProcess); // // Insert the endpoint on the global list. // AfdInsertNewEndpointInList( endpoint ); // // Return a pointer to the new endpoint to the caller. // IF_DEBUG(ENDPOINT) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdAllocateEndpoint: new endpoint at %p\n", endpoint )); } *NewEndpoint = endpoint; return STATUS_SUCCESS; } else { PsReturnPoolQuota( process, NonPagedPool, sizeof (AFD_ENDPOINT) ); status= STATUS_INSUFFICIENT_RESOURCES; } Cleanup: if( GroupID != 0 ) { AfdDereferenceGroup( GroupID ); } } else { status = STATUS_INVALID_PARAMETER; } if (transportInfo!=NULL) { if (InterlockedDecrement (&transportInfo->ReferenceCount)==0) { AfdFreeTransportInfo (transportInfo); } } return status; } // AfdAllocateEndpoint VOID AfdFreeQueuedConnections ( IN PAFD_ENDPOINT Endpoint ) /*++ Routine Description: Frees queued connection objects on a listening AFD endpoint. Arguments: Endpoint - a pointer to the AFD endpoint structure. Return Value: None. --*/ { AFD_LOCK_QUEUE_HANDLE lockHandle; KIRQL oldIrql; PAFD_CONNECTION connection; PIRP irp; ASSERT( Endpoint->Type == AfdBlockTypeVcListening || Endpoint->Type == AfdBlockTypeVcBoth ); // // Free the unaccepted connections. // // We must hold endpoint spinLock to call AfdGetUnacceptedConnection, // but we may not hold it when calling AfdDereferenceConnection. // KeRaiseIrql (DISPATCH_LEVEL, &oldIrql); AfdAcquireSpinLockAtDpcLevel( &Endpoint->SpinLock, &lockHandle ); while ( (connection = AfdGetUnacceptedConnection( Endpoint )) != NULL ) { ASSERT( connection->Endpoint == Endpoint ); AfdReleaseSpinLockFromDpcLevel( &Endpoint->SpinLock, &lockHandle ); if (connection->SanConnection) { AfdSanAbortConnection ( connection ); } else { AfdAbortConnection( connection ); } AfdAcquireSpinLockAtDpcLevel( &Endpoint->SpinLock, &lockHandle ); } // // Free the returned connections. // while ( (connection = AfdGetReturnedConnection( Endpoint, 0 )) != NULL ) { ASSERT( connection->Endpoint == Endpoint ); AfdReleaseSpinLockFromDpcLevel( &Endpoint->SpinLock, &lockHandle ); if (connection->SanConnection) { AfdSanAbortConnection ( connection ); } else { AfdAbortConnection( connection ); } AfdAcquireSpinLockAtDpcLevel( &Endpoint->SpinLock, &lockHandle ); } if (IS_DELAYED_ACCEPTANCE_ENDPOINT (Endpoint)) { while (!IsListEmpty (&Endpoint->Common.VcListening.ListenConnectionListHead)) { PIRP listenIrp; connection = CONTAINING_RECORD ( Endpoint->Common.VcListening.ListenConnectionListHead.Flink, AFD_CONNECTION, ListEntry ); RemoveEntryList (&connection->ListEntry); listenIrp = InterlockedExchangePointer ((PVOID *)&connection->ListenIrp, NULL); if (listenIrp!=NULL) { IoAcquireCancelSpinLock (&listenIrp->CancelIrql); ASSERT (listenIrp->CancelIrql==DISPATCH_LEVEL); AfdReleaseSpinLockFromDpcLevel (&Endpoint->SpinLock, &lockHandle); AfdCancelIrp (listenIrp); AfdAcquireSpinLockAtDpcLevel( &Endpoint->SpinLock, &lockHandle ); } } AfdReleaseSpinLockFromDpcLevel( &Endpoint->SpinLock, &lockHandle ); } else { AfdReleaseSpinLockFromDpcLevel( &Endpoint->SpinLock, &lockHandle ); // // And finally, purge the free connection queue. // while ( (connection = AfdGetFreeConnection( Endpoint, &irp )) != NULL ) { ASSERT( connection->Type == AfdBlockTypeConnection ); if (irp!=NULL) { AfdCleanupSuperAccept (irp, STATUS_CANCELLED); if (irp->Cancel) { KIRQL cancelIrql; // // Need to sycn with cancel routine which may // have been called from AfdCleanup for accepting // endpoint // IoAcquireCancelSpinLock (&cancelIrql); IoReleaseCancelSpinLock (cancelIrql); } IoCompleteRequest (irp, AfdPriorityBoost); } DEREFERENCE_CONNECTION( connection ); } } KeLowerIrql (oldIrql); return; } // AfdFreeQueuedConnections VOID AfdFreeEndpointResources ( PAFD_ENDPOINT endpoint ) /*++ Routine Description: Does the actual work to deallocate an AFD endpoint structure and associated structures. Note that all other references to the endpoint structure must be gone before this routine is called, since it frees the endpoint and assumes that nobody else will be looking at the endpoint. Arguments: Endpoint to be cleaned up Return Value: None --*/ { NTSTATUS status; PLIST_ENTRY listEntry; PAGED_CODE (); ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); ASSERT( endpoint->ReferenceCount == 0 ); ASSERT( endpoint->State == AfdEndpointStateClosing ); ASSERT( endpoint->ObReferenceBias == 0 ); ASSERT( KeGetCurrentIrql( ) == 0 ); // // If this is a listening endpoint, then purge the endpoint of all // queued connections. // if ( (endpoint->Type & AfdBlockTypeVcListening) == AfdBlockTypeVcListening ) { AfdFreeQueuedConnections( endpoint ); } // // Dereference any group ID associated with this endpoint. // if( endpoint->GroupID != 0 ) { AfdDereferenceGroup( endpoint->GroupID ); } // // If this is a bufferring datagram endpoint, remove all the // bufferred datagrams from the endpoint's list and free them. // if ( IS_DGRAM_ENDPOINT(endpoint) && endpoint->ReceiveDatagramBufferListHead.Flink != NULL ) { while ( !IsListEmpty( &endpoint->ReceiveDatagramBufferListHead ) ) { PAFD_BUFFER_HEADER afdBuffer; listEntry = RemoveHeadList( &endpoint->ReceiveDatagramBufferListHead ); afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER_HEADER, BufferListEntry ); AfdReturnBuffer( afdBuffer, endpoint->OwningProcess ); } } // // Close and dereference the TDI address object on the endpoint, if // any. // if ( endpoint->AddressFileObject != NULL ) { // // Little extra precaution. It is possible that there exists // a duplicated handle in user process, so transport can in // theory call event handler with bogus endpoint pointer that // we are about to free. The event handlers for datagram // endpoints are reset in AfdCleanup. // Connection-oriented accepted endpoints cannot have address handles // in their structures because they share them with listening // endpoint (it would be a grave mistake if we tried to close // address handle owned by listening endpoint while closing connection // accepted on it). // if ( endpoint->AddressHandle != NULL && IS_VC_ENDPOINT (endpoint)) { ASSERT (((endpoint->Type&AfdBlockTypeVcConnecting)!=AfdBlockTypeVcConnecting) || (endpoint->Common.VcConnecting.ListenEndpoint==NULL)); status = AfdSetEventHandler( endpoint->AddressFileObject, TDI_EVENT_ERROR, NULL, NULL ); //ASSERT( NT_SUCCESS(status) ); status = AfdSetEventHandler( endpoint->AddressFileObject, TDI_EVENT_DISCONNECT, NULL, NULL ); //ASSERT( NT_SUCCESS(status) ); status = AfdSetEventHandler( endpoint->AddressFileObject, TDI_EVENT_RECEIVE, NULL, NULL ); //ASSERT( NT_SUCCESS(status) ); if (IS_TDI_EXPEDITED (endpoint)) { status = AfdSetEventHandler( endpoint->AddressFileObject, TDI_EVENT_RECEIVE_EXPEDITED, NULL, NULL ); //ASSERT( NT_SUCCESS(status) ); } if ( IS_TDI_BUFFERRING(endpoint) ) { status = AfdSetEventHandler( endpoint->AddressFileObject, TDI_EVENT_SEND_POSSIBLE, NULL, NULL ); //ASSERT( NT_SUCCESS(status) ); } else { status = AfdSetEventHandler( endpoint->AddressFileObject, TDI_EVENT_CHAINED_RECEIVE, NULL, NULL ); //ASSERT( NT_SUCCESS(status) ); } } ObDereferenceObject( endpoint->AddressFileObject ); endpoint->AddressFileObject = NULL; endpoint->AddressDeviceObject = NULL; AfdRecordAddrDeref(); } else { if (endpoint->SecurityDescriptor!=NULL) { ObDereferenceSecurityDescriptor( endpoint->SecurityDescriptor, 1 ); } endpoint->SecurityDescriptor = 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 status = ZwClose( endpoint->AddressHandle ); ASSERT( NT_SUCCESS(status) ); endpoint->AddressHandle = NULL; AfdRecordAddrClosed(); } // // Remove the endpoint from the global list. Do this before any // deallocations to prevent someone else from seeing an endpoint in // an invalid state. // AfdRemoveEndpointFromList( endpoint ); // // Return the quota we charged to this process when we allocated // the endpoint object. // PsReturnPoolQuota( endpoint->OwningProcess, NonPagedPool, sizeof (AFD_ENDPOINT) ); AfdRecordQuotaHistory( endpoint->OwningProcess, -(LONG)sizeof (AFD_ENDPOINT), "EndpDealloc ", endpoint ); AfdRecordPoolQuotaReturned( sizeof (AFD_ENDPOINT) ); // // Dereference the owning process. // ObDereferenceObject( endpoint->OwningProcess ); endpoint->OwningProcess = NULL; // // Dereference the listening or c-root endpoint on the endpoint, if // any. // if ( endpoint->Type == AfdBlockTypeVcConnecting && endpoint->Common.VcConnecting.ListenEndpoint != NULL ) { PAFD_ENDPOINT listenEndpoint = endpoint->Common.VcConnecting.ListenEndpoint; ASSERT (((listenEndpoint->Type&AfdBlockTypeVcListening)==AfdBlockTypeVcListening) || IS_CROOT_ENDPOINT (listenEndpoint)); ASSERT (endpoint->LocalAddress==listenEndpoint->LocalAddress); DEREFERENCE_ENDPOINT( listenEndpoint ); endpoint->Common.VcConnecting.ListenEndpoint = NULL; // // We used the local address from the listening endpoint, // simply reset it, it will be freed when listening endpoint // is freed. // endpoint->LocalAddress = NULL; endpoint->LocalAddressLength = 0; } if (IS_SAN_ENDPOINT (endpoint)) { AfdSanCleanupEndpoint (endpoint); } else if (IS_SAN_HELPER (endpoint)) { AfdSanCleanupHelper (endpoint); } // // Free local and remote address buffers. // if ( endpoint->LocalAddress != NULL ) { AFD_FREE_POOL( endpoint->LocalAddress, AFD_LOCAL_ADDRESS_POOL_TAG ); endpoint->LocalAddress = NULL; } if ( IS_DGRAM_ENDPOINT(endpoint) && endpoint->Common.Datagram.RemoteAddress != NULL ) { AFD_RETURN_REMOTE_ADDRESS( endpoint->Common.Datagram.RemoteAddress, endpoint->Common.Datagram.RemoteAddressLength ); endpoint->Common.Datagram.RemoteAddress = NULL; } // // Free context and connect data buffers. // if ( endpoint->Context != NULL ) { AFD_FREE_POOL( endpoint->Context, AFD_CONTEXT_POOL_TAG ); endpoint->Context = NULL; } if ( IS_VC_ENDPOINT (endpoint) && endpoint->Common.VirtualCircuit.ConnectDataBuffers != NULL ) { AfdFreeConnectDataBuffers( endpoint->Common.VirtualCircuit.ConnectDataBuffers ); } // // If there's an active EventSelect() on this endpoint, dereference // the associated event object. // if( endpoint->EventObject != NULL ) { ObDereferenceObject( endpoint->EventObject ); endpoint->EventObject = NULL; } // ASSERT ( endpoint->Irp == NULL ); if (endpoint->TransportInfo!=NULL) { if (InterlockedDecrement (&endpoint->TransportInfo->ReferenceCount)==0) { AfdFreeTransportInfo (endpoint->TransportInfo); } endpoint->TransportInfo = NULL; } ASSERT (endpoint->OutstandingIrpCount==0); IF_DEBUG(ENDPOINT) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdFreeEndpoint: freeing endpoint at %p\n", endpoint )); } endpoint->Type = AfdBlockTypeInvalidEndpoint; } VOID AfdFreeEndpoint ( IN PVOID Context ) /*++ Routine Description: Calls AfdFreeEndpointResources to cleanup endpoint and frees the endpoint structure itself Arguments: Context - Actually points to the endpoint's embedded AFD_WORK_ITEM structure. From this we can determine the endpoint to free. Return Value: None. --*/ { PAFD_ENDPOINT endpoint; PAGED_CODE( ); ASSERT( Context != NULL ); InterlockedDecrement(&AfdEndpointsFreeing); endpoint = CONTAINING_RECORD( Context, AFD_ENDPOINT, WorkItem ); AfdFreeEndpointResources (endpoint); // // Free the pool used for the endpoint itself. // AFD_FREE_POOL( endpoint, AFD_ENDPOINT_POOL_TAG ); } // AfdFreeEndpoint PAFD_ENDPOINT AfdReuseEndpoint ( VOID ) /*++ Routine Description: Finds a AfdFreeEndpoint work item in the list and calls AfdFreeEndpointResources to cleanup endpoint Arguments: None Return Value: Reinitialized endpoint. --*/ { PAFD_ENDPOINT endpoint; PVOID Context; PAGED_CODE( ); Context = AfdGetWorkerByRoutine (AfdFreeEndpoint); if (Context==NULL) return NULL; endpoint = CONTAINING_RECORD( Context, AFD_ENDPOINT, WorkItem ); AfdFreeEndpointResources (endpoint); return endpoint; } // AfdReuseEndpoint #if REFERENCE_DEBUG VOID AfdDereferenceEndpoint ( IN PAFD_ENDPOINT Endpoint, IN LONG LocationId, IN ULONG Param ) #else VOID AfdDereferenceEndpoint ( IN PAFD_ENDPOINT Endpoint ) #endif /*++ Routine Description: Dereferences an AFD endpoint and calls the routine to free it if appropriate. Arguments: Endpoint - a pointer to the AFD endpoint structure. Return Value: None. --*/ { LONG result; #if REFERENCE_DEBUG IF_DEBUG(ENDPOINT) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdDereferenceEndpoint: endpoint at %p, new refcnt %ld\n", Endpoint, Endpoint->ReferenceCount-1 )); } ASSERT( IS_AFD_ENDPOINT_TYPE( Endpoint ) ); ASSERT( Endpoint->ReferenceCount > 0 ); ASSERT( Endpoint->ReferenceCount != 0xDAADF00D ); AFD_UPDATE_REFERENCE_DEBUG(Endpoint, Endpoint->ReferenceCount-1, LocationId, Param); // // We must hold AfdSpinLock while doing the dereference and check // for free. This is because some code makes the assumption that // the connection structure will not go away while AfdSpinLock is // held, and that code references the endpoint before releasing // AfdSpinLock. If we did the InterlockedDecrement() without the // lock held, our count may go to zero, that code may reference the // connection, and then a double free might occur. // // It is still valuable to use the interlocked routines for // increment and decrement of structures because it allows us to // avoid having to hold the spin lock for a reference. // // In NT40+ we use InterlockedCompareExchange and make sure that // we do not increment reference count if it is 0, so holding // a spinlock is no longer necessary // // Decrement the reference count; if it is 0, we may need to // free the endpoint. // #endif result = InterlockedDecrement( (PLONG)&Endpoint->ReferenceCount ); if ( result == 0 ) { ASSERT( Endpoint->State == AfdEndpointStateClosing ); if ((Endpoint->Type==AfdBlockTypeVcConnecting) && (Endpoint->Common.VcConnecting.ListenEndpoint != NULL) && (KeGetCurrentIrql()==PASSIVE_LEVEL)) { ASSERT (Endpoint->AddressHandle==NULL); // // If this is a connecting endpoint assoicated with the // listening endpoint and we already at passive level, // free the endpoint here. We can do this because in such // a case we know that reference to the transport object // is not the last one - at least one more is still in the // listening endpoint and we remove transport object reference // before dereferencing listening endpoint. // // AfdFreeEndpointResources (Endpoint); // // Free the pool used for the endpoint itself. // AFD_FREE_POOL( Endpoint, AFD_ENDPOINT_POOL_TAG ); } else { // // We're going to do this by queueing a request to an executive // worker thread. We do this for several reasons: to ensure // that we're at IRQL 0 so we can free pageable memory, and to // ensure that we're in a legitimate context for a close // operation and not in conntext of event indication from // the transport driver // InterlockedIncrement(&AfdEndpointsFreeing); AfdQueueWorkItem( AfdFreeEndpoint, &Endpoint->WorkItem ); } } } // AfdDereferenceEndpoint #if REFERENCE_DEBUG VOID AfdReferenceEndpoint ( IN PAFD_ENDPOINT Endpoint, IN LONG LocationId, IN ULONG Param ) /*++ Routine Description: References an AFD endpoint. Arguments: Endpoint - a pointer to the AFD endpoint structure. Return Value: None. --*/ { LONG result; ASSERT( Endpoint->ReferenceCount > 0 ); ASSERT( Endpoint->ReferenceCount < 0xFFFF || ((Endpoint->Listening || Endpoint->afdC_Root) && Endpoint->ReferenceCount<0xFFFFFFF)); result = InterlockedIncrement( (PLONG)&Endpoint->ReferenceCount ); AFD_UPDATE_REFERENCE_DEBUG(Endpoint, result, LocationId, Param); IF_DEBUG(ENDPOINT) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReferenceEndpoint: endpoint at %p, new refcnt %ld\n", Endpoint, result )); } } // AfdReferenceEndpoint VOID AfdUpdateEndpoint ( IN PAFD_ENDPOINT Endpoint, IN LONG LocationId, IN ULONG Param ) /*++ Routine Description: Update an AFD endpoint reference debug information. Arguments: Endpoint - a pointer to the AFD endpoint structure. Return Value: None. --*/ { ASSERT( Endpoint->ReferenceCount > 0 ); ASSERT( Endpoint->ReferenceCount < 0xFFFF || ((Endpoint->Listening || Endpoint->afdC_Root) && Endpoint->ReferenceCount<0xFFFFFFF)); AFD_UPDATE_REFERENCE_DEBUG(Endpoint, Endpoint->ReferenceCount, LocationId, Param); } // AfdUpdateEndpoint #endif #if REFERENCE_DEBUG BOOLEAN AfdCheckAndReferenceEndpoint ( IN PAFD_ENDPOINT Endpoint, IN LONG LocationId, IN ULONG Param ) #else BOOLEAN AfdCheckAndReferenceEndpoint ( IN PAFD_ENDPOINT Endpoint ) #endif { LONG result; do { result = Endpoint->ReferenceCount; if (result<=0) break; } while (InterlockedCompareExchange ((PLONG)&Endpoint->ReferenceCount, (result+1), result)!=result); if (result>0) { #if REFERENCE_DEBUG AFD_UPDATE_REFERENCE_DEBUG(Endpoint, result+1, LocationId, Param); IF_DEBUG(ENDPOINT) { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL, "AfdReferenceEndpoint: endpoint at %p, new refcnt %ld\n", Endpoint, result+1 )); } ASSERT( Endpoint->ReferenceCount < 0xFFFF || ((Endpoint->Listening || Endpoint->afdC_Root) && Endpoint->ReferenceCount<0xFFFFFFF)); #endif return TRUE; } else { KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_ERROR_LEVEL, "AfdCheckAndReferenceEndpoint: Endpoint %p is gone (refcount: %ld)!\n", Endpoint, result)); return FALSE; } } VOID AfdRefreshEndpoint ( IN PAFD_ENDPOINT Endpoint ) /*++ Routine Description: Prepares an AFD endpoint structure to be reused. All other references to the endpoint must be freed before this routine is called, since this routine assumes that nobody will access the old information in the endpoint structure. This fact is ensured by the state change primitive. Arguments: Endpoint - a pointer to the AFD endpoint structure. Return Value: None. --*/ { AFD_ENDPOINT_STATE_FLAGS flags; ASSERT( Endpoint->Type == AfdBlockTypeVcConnecting ); ASSERT( Endpoint->Common.VcConnecting.Connection == NULL ); ASSERT( Endpoint->StateChangeInProgress!=0); ASSERT( Endpoint->State == AfdEndpointStateTransmitClosing ); if ( Endpoint->Common.VcConnecting.ListenEndpoint != NULL ) { // // TransmitFile after SuperAccept, cleanup back to open state. // // // Dereference the listening endpoint and its address object. // PAFD_ENDPOINT listenEndpoint = Endpoint->Common.VcConnecting.ListenEndpoint; ASSERT (((listenEndpoint->Type&AfdBlockTypeVcListening)==AfdBlockTypeVcListening) || IS_CROOT_ENDPOINT (listenEndpoint)); ASSERT (Endpoint->LocalAddress==listenEndpoint->LocalAddress); ASSERT (Endpoint->AddressFileObject==listenEndpoint->AddressFileObject); DEREFERENCE_ENDPOINT( listenEndpoint ); Endpoint->Common.VcConnecting.ListenEndpoint = NULL; // // Close and dereference the TDI address object on the endpoint, if // any. // ObDereferenceObject( Endpoint->AddressFileObject ); Endpoint->AddressFileObject = NULL; Endpoint->AddressDeviceObject = NULL; AfdRecordAddrDeref(); // // We used the local address from the listening endpoint, // simply reset it, it will be freed when listening endpoint // is freed. // Endpoint->LocalAddress = NULL; Endpoint->LocalAddressLength = 0; ASSERT (Endpoint->AddressHandle == NULL); // // Reinitialize the endpoint structure. // Endpoint->Type = AfdBlockTypeEndpoint; Endpoint->State = AfdEndpointStateOpen; } else { // // TransmitFile after SuperConnect, cleanup back to bound state. // Endpoint->Type = AfdBlockTypeEndpoint; ASSERT (Endpoint->AddressHandle!=NULL); ASSERT (Endpoint->AddressFileObject!=NULL); ASSERT (Endpoint->AddressDeviceObject!=NULL); Endpoint->State = AfdEndpointStateBound; } // // Remember if poll is pending on this endpoint, to enable cleanup // is socket gets closed // flags.EndpointStateFlags = 0; // reset flags.PollCalled = Endpoint->PollCalled; Endpoint->EndpointStateFlags = flags.EndpointStateFlags; Endpoint->DisconnectMode = 0; Endpoint->EventsActive = 0; AfdRecordEndpointsReused (); return; } // AfdRefreshEndpoint NTSTATUS AfdGetTransportInfo ( IN PUNICODE_STRING TransportDeviceName, IN OUT PAFD_TRANSPORT_INFO *TransportInfo ) /*++ Routine Description: Returns a transport information structure corresponding to the specified TDI transport provider. Each unique transport string gets a single provider structure, so that multiple endpoints for the same transport share the same transport information structure. Arguments: TransportDeviceName - the name of the TDI transport provider. TransportInfo - place to return referenced pointer to transport info Return Value: STATUS_SUCCESS - returned transport info is valid. STATUS_INSUFFICIENT_RESOURCES - not enough memory to allocate transport info structure STATUS_OBJECT_NAME_NOT_FOUND - transport's device is not available yet --*/ { PLIST_ENTRY listEntry; PAFD_TRANSPORT_INFO transportInfo; ULONG structureLength; NTSTATUS status; TDI_PROVIDER_INFO localProviderInfo; BOOLEAN resourceShared = TRUE; #ifdef _AFD_VARIABLE_STACK_ CCHAR stackSize; #endif // _AFD_VARIABLE_STACK_ PAGED_CODE( ); // // Make sure the thread in which we execute cannot get // suspeneded in APC while we own the global resource. // KeEnterCriticalRegion (); ExAcquireResourceSharedLite( AfdResource, TRUE ); // // If this is the first endpoint, we may be paged out // entirely. // if (!AfdLoaded) { // // Take the exclusive lock and page the locked sections in // if necessary // // // There should be no endpoints in the list. // ASSERT (IsListEmpty (&AfdEndpointListHead)); ExReleaseResourceLite ( AfdResource); ExAcquireResourceExclusiveLite( AfdResource, TRUE ); resourceShared = FALSE; if (!AfdLoaded) { // // There should be no endpoints in the list. // ASSERT (IsListEmpty (&AfdEndpointListHead)); MmResetDriverPaging ((PVOID)DriverEntry); AfdLoaded = (PKEVENT)1; } } ASSERT (AfdLoaded==(PKEVENT)1); if (*TransportInfo==NULL) { ScanTransportList: // // If caller did not have transport info allocated, walk the list // of transport device names looking for an identical name. // for ( listEntry = AfdTransportInfoListHead.Flink; listEntry != &AfdTransportInfoListHead; listEntry = listEntry->Flink ) { transportInfo = CONTAINING_RECORD( listEntry, AFD_TRANSPORT_INFO, TransportInfoListEntry ); if ( RtlCompareUnicodeString( &transportInfo->TransportDeviceName, TransportDeviceName, TRUE ) == 0 ) { // // We found an exact match. Reference the structure // to return it to the caller // do { LONG localCount; localCount = transportInfo->ReferenceCount; if (localCount==0) { // // We hit a small window when the structure is // about to be freed. We can't stop this from // happenning, so we'll go on to allocate and // requery. After all info is not valid anyway, // thus we are just loosing on the allocation/dealocation // code. // ASSERT (transportInfo->InfoValid==FALSE); goto AllocateInfo; } if (InterlockedCompareExchange ( (PLONG)&transportInfo->ReferenceCount, (localCount+1), localCount)==localCount) { if (transportInfo->InfoValid) { // // Info is valid return referenced pointer to // the caller. // *TransportInfo = transportInfo; ExReleaseResourceLite( AfdResource ); KeLeaveCriticalRegion (); return STATUS_SUCCESS; } else { // // We found match, but info is not valid // goto QueryInfo; } } } while (1); } } // for AllocateInfo: if (resourceShared) { // // If we do not own resource exlusively, we will // have to release and reacquire it and then // rescan the list // ExReleaseResourceLite ( AfdResource); ExAcquireResourceExclusiveLite( AfdResource, TRUE ); resourceShared = FALSE; goto ScanTransportList; } // // This is a brand new device name, allocate transport info // structure for it. // structureLength = sizeof(AFD_TRANSPORT_INFO) + TransportDeviceName->Length + sizeof(UNICODE_NULL); transportInfo = AFD_ALLOCATE_POOL_PRIORITY( NonPagedPool, structureLength, AFD_TRANSPORT_INFO_POOL_TAG, NormalPoolPriority ); if ( transportInfo == NULL ) { ExReleaseResourceLite( AfdResource ); KeLeaveCriticalRegion (); return STATUS_INSUFFICIENT_RESOURCES; } // // Initialize the structure // transportInfo->ReferenceCount = 1; transportInfo->InfoValid = FALSE; #ifdef _AFD_VARIABLE_STACK_ transportInfo->StackSize = 0; transportInfo->GetBuffer = AfdGetBufferFast; transportInfo->GetTpInfo = AfdGetTpInfoFast; transportInfo->CallDriver = IofCallDriver; #endif // _AFD_VARIABLE_STACK_ // // Fill in the transport device name. // transportInfo->TransportDeviceName.MaximumLength = TransportDeviceName->Length + sizeof(WCHAR); transportInfo->TransportDeviceName.Buffer = (PWSTR)(transportInfo + 1); RtlCopyUnicodeString( &transportInfo->TransportDeviceName, TransportDeviceName ); // // Insert the structure into the list so that the successive callers // can reuse it. // InsertHeadList (&AfdTransportInfoListHead, &transportInfo->TransportInfoListEntry); } else { transportInfo = *TransportInfo; // // Caller has already referenced info in the list // but transport device was not available at the // time of the call. Recheck if it is valid under the lock // if (transportInfo->InfoValid) { // // Yes, it is, return success // ExReleaseResourceLite( AfdResource ); KeLeaveCriticalRegion (); return STATUS_SUCCESS; } } QueryInfo: // // Release the resource and leave critical region to let // the IRP's in AfdQueryProviderInfo complete // ExReleaseResourceLite (AfdResource); KeLeaveCriticalRegion (); status = AfdQueryProviderInfo (TransportDeviceName, #ifdef _AFD_VARIABLE_STACK_ &stackSize, #endif // _AFD_VARIABLE_STACK_ &localProviderInfo); // // Make sure the thread in which we execute cannot get // suspeneded in APC while we own the global resource. // KeEnterCriticalRegion (); ExAcquireResourceExclusiveLite( AfdResource, TRUE ); if (NT_SUCCESS (status)) { // // Check if someone did not get the info in parallel with us. // if (!transportInfo->InfoValid) { // // Copy local info structure to the one in the list. // transportInfo->ProviderInfo = localProviderInfo; // // Bump the reference count on this info structure // as we know that it is a valid TDI provider and we // want to cache, it so it stays even of all endpoints // that use it are gone. // InterlockedIncrement ((PLONG)&transportInfo->ReferenceCount); // // Set the flag so that everyone knows it is now valid. // transportInfo->InfoValid = TRUE; #ifdef _AFD_VARIABLE_STACK_ transportInfo->StackSize = stackSize; #endif // _AFD_VARIABLE_STACK_ } *TransportInfo = transportInfo; } else { if (status==STATUS_OBJECT_NAME_NOT_FOUND || status==STATUS_OBJECT_PATH_NOT_FOUND || status==STATUS_NO_SUCH_DEVICE) { // // Transport driver must not have been loaded yet // Return transport info structure anyway // Caller will know that info structure is not // valid because we did not set the flag // *TransportInfo = transportInfo; } else { // // Something else went wrong, free the strucuture // if it was allocated in this routine // if (*TransportInfo==NULL) { if (InterlockedDecrement ((PLONG)&transportInfo->ReferenceCount)==0) { RemoveEntryList (&transportInfo->TransportInfoListEntry); AFD_FREE_POOL( transportInfo, AFD_TRANSPORT_INFO_POOL_TAG ); } } } } ExReleaseResourceLite( AfdResource ); KeLeaveCriticalRegion (); return status; } // AfdGetTransportInfo VOID AfdFreeTransportInfo ( PAFD_TRANSPORT_INFO TransportInfo ) { // // Reference count has gone to 0, we need to remove the structure // from the global list and free it. // Note that no code increments reference count if doesn't // know for fact that its current reference count is above 0). // // // Make sure the thread in which we execute cannot get // suspeneded in APC while we own the global resource. // KeEnterCriticalRegion (); ExAcquireResourceExclusiveLite( AfdResource, TRUE ); ASSERT (TransportInfo->ReferenceCount==0); ASSERT (TransportInfo->InfoValid==FALSE); RemoveEntryList (&TransportInfo->TransportInfoListEntry); ExReleaseResourceLite( AfdResource ); KeLeaveCriticalRegion (); AFD_FREE_POOL (TransportInfo, AFD_TRANSPORT_INFO_POOL_TAG); }