/*++ Copyright (c) 2000 Microsoft Corporation Module Name: ALGio.c Abstract: This module contains code for the ALG transparent proxy's network I/O completion routines. Author: Qiang Wang (qiangw) 10-Apr-2000 Revision History: --*/ #include "precomp.h" #pragma hdrstop #include "Algmsg.h" VOID AlgAcceptCompletionRoutine( ULONG ErrorCode, ULONG BytesTransferred, PNH_BUFFER Bufferp ) /*++ Routine Description: This routine is invoked upon completion of an accept operation on a ALG transparent proxy stream socket. Arguments: ErrorCode - Win32 status code for the I/O operation BytesTransferred - number of bytes in 'Bufferp' Bufferp - holds the local and remote IP address and port for the connection. Return Value: none. Environment: Runs in the context of a worker-thread which has just dequeued an I/O completion packet from the common I/O completion port with which our stream sockets are associated. A reference to the component will have been made on our behalf by 'NhAcceptStreamSocket'. A reference to the interface will have been made on our behalf by whoever issued the I/O request. --*/ { SOCKET AcceptedSocket; PALG_CONNECTION Connectionp; ULONG Error; PALG_INTERFACE Interfacep; SOCKET ListeningSocket; PROFILE("AlgAcceptCompletionRoutine"); do { AcceptedSocket = (SOCKET)Bufferp->Socket; Interfacep = (PALG_INTERFACE)Bufferp->Context; ListeningSocket = (SOCKET)Bufferp->Context2; // // Acquire three additional references to the interface // for the followup requests that we will issue below, // and lock the interface. // EnterCriticalSection(&AlgInterfaceLock); if (!ALG_REFERENCE_INTERFACE(Interfacep)) { LeaveCriticalSection(&AlgInterfaceLock); NhReleaseBuffer(Bufferp); NhDeleteStreamSocket(AcceptedSocket); break; } ALG_REFERENCE_INTERFACE(Interfacep); ALG_REFERENCE_INTERFACE(Interfacep); LeaveCriticalSection(&AlgInterfaceLock); ACQUIRE_LOCK(Interfacep); // // Process the accept-completion. // First look for an error code. If an error occurred // and the interface is no longer active, end the completion-handling. // Otherwise, attempt to reissue the accept-request. // if (ErrorCode) { NhTrace( TRACE_FLAG_IO, "AlgAcceptCompletionRoutine: error %d for interface %d", ErrorCode, Interfacep->Index ); // // See if the interface is still active and, if so, reissue // the accept-request. Since we will not be creating an active // endpoint, we won't need the second reference to the interface. // ALG_DEREFERENCE_INTERFACE(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); if (!ALG_INTERFACE_ACTIVE(Interfacep)) { RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhReleaseBuffer(Bufferp); NhDeleteStreamSocket(AcceptedSocket); } else { // // Reissue the accept-request. Note that the callee is now // responsible for the reference we made to the interface. // Error = AlgAcceptConnectionInterface( Interfacep, ListeningSocket, AcceptedSocket, Bufferp, NULL ); RELEASE_LOCK(Interfacep); if (Error) { NhReleaseBuffer(Bufferp); NhDeleteStreamSocket(AcceptedSocket); NhTrace( TRACE_FLAG_IO, "AlgAcceptCompletionRoutine: error %d reissuing accept", Error ); } } break; } // // Now see if the interface is operational. // If it isn't, we need to destroy the accepted socket // and return control. // if (!ALG_INTERFACE_ACTIVE(Interfacep)) { RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhReleaseBuffer(Bufferp); NhDeleteStreamSocket(AcceptedSocket); NhTrace( TRACE_FLAG_IO, "AlgAcceptCompletionRoutine: interface %d inactive", Interfacep->Index ); InterlockedIncrement( reinterpret_cast(&AlgStatistics.ConnectionsDropped) ); break; } // // We now create a 'ALG_CONNECTION' for the new connection, // in the process launching operations for the connection. // The connection management module will handle the accepted socket // from here onward, and is responsible for the references to the // interface that were made above. // NhTrace( TRACE_FLAG_IO, "AlgAcceptCompletionRoutine: socket %d accepting connection", ListeningSocket ); Error = AlgCreateConnection( Interfacep, ListeningSocket, AcceptedSocket, Bufferp->Buffer, &Connectionp ); if (Error) { InterlockedIncrement( reinterpret_cast(&AlgStatistics.ConnectionsDropped) ); } else { InterlockedIncrement( reinterpret_cast(&AlgStatistics.ConnectionsAccepted) ); } // // Finally, issue an accept operation for the next connection-request // on the listening socket. Note that the callee is responsible // for releasing the reference to the interface in case of a failure. // Error = AlgAcceptConnectionInterface( Interfacep, ListeningSocket, INVALID_SOCKET, Bufferp, NULL ); RELEASE_LOCK(Interfacep); if (Error) { NhReleaseBuffer(Bufferp); } } while(FALSE); ALG_DEREFERENCE_INTERFACE(Interfacep); DEREFERENCE_ALG(); } // AlgAcceptCompletionRoutine VOID AlgCloseEndpointNotificationRoutine( ULONG ErrorCode, ULONG BytesTransferred, PNH_BUFFER Bufferp ) /*++ Routine Description: This routine is invoked upon notification of a close operation on a ALG transparent proxy stream socket. Arguments: ErrorCode - Win32 status code for the I/O operation BytesTransferred - number of bytes in 'Bufferp' Bufferp - holds context information for the closed socket. Note that we are not allowed to release this buffer here. Return Value: none. Environment: Runs in the context of a wait-thread. A reference to the component will have been made on our behalf by 'NhAcceptStreamSocket' or 'NhConnectStreamSocket'. A reference to the interface will have been made on our behalf by whoever issued the I/O request. Both of these references are released here. --*/ { SOCKET ClosedSocket; ULONG EndpointId; PALG_INTERFACE Interfacep; PROFILE("AlgCloseEndpointNotificationRoutine"); do { ClosedSocket = (SOCKET)Bufferp->Socket; Interfacep = (PALG_INTERFACE)Bufferp->Context; EndpointId = PtrToUlong(Bufferp->Context2); NhTrace( TRACE_FLAG_IO, "AlgCloseEndpointNotificationRoutine: endpoint %d socket %d " "closed, error %d", EndpointId, ClosedSocket, ErrorCode ); #if 0 PALG_ENDPOINT Endpointp; // // Lock the interface, and retrieve the endpoint whose socket has // been closed. // ACQUIRE_LOCK(Interfacep); Endpointp = AlgLookupInterfaceEndpoint(Interfacep, EndpointId, NULL); if (Endpointp) { AlgCloseActiveEndpoint(Endpointp, ClosedSocket); } RELEASE_LOCK(Interfacep); #endif } while(FALSE); ALG_DEREFERENCE_INTERFACE(Interfacep); DEREFERENCE_ALG(); } // AlgCloseEndpointNotificationRoutine VOID AlgConnectEndpointCompletionRoutine( ULONG ErrorCode, ULONG BytesTransferred, PNH_BUFFER Bufferp ) /*++ Routine Description: This routine is invoked upon completion of a connect operation on a ALG transparent proxy stream socket. Arguments: ErrorCode - Win32 status code for the I/O operation BytesTransferred - number of bytes in 'Bufferp' Bufferp - holds the context information for the endpoint. Note that we are not allowed to release this buffer here. Return Value: none. Environment: Runs in the context of a wait-thread. A reference to the component will have been made on our behalf by 'NhConnectStreamSocket'. A reference to the interface will have been made on our behalf by whoever issued the I/O request. Neither of these references may be released here; they are both released in the close-notification routine, which we are guaranteed will be invoked. (Eventually.) --*/ { SOCKET ConnectedSocket; ULONG EndpointId; PALG_ENDPOINT Endpointp; ULONG Error; PALG_INTERFACE Interfacep; PROFILE("AlgConnectEndpointCompletionRoutine"); do { ConnectedSocket = (SOCKET)Bufferp->Socket; Interfacep = (PALG_INTERFACE)Bufferp->Context; EndpointId = PtrToUlong(Bufferp->Context2); // // Acquire two additional references to the interface // for the endpoint-activation that we will initiate below, // lock the interface, and retrieve the endpoint. // EnterCriticalSection(&AlgInterfaceLock); if (!ALG_REFERENCE_INTERFACE(Interfacep)) { LeaveCriticalSection(&AlgInterfaceLock); break; } ALG_REFERENCE_INTERFACE(Interfacep); LeaveCriticalSection(&AlgInterfaceLock); ACQUIRE_LOCK(Interfacep); Endpointp = AlgLookupInterfaceEndpoint(Interfacep, EndpointId, NULL); // // First look for an error code. // If an error occurred and the interface is still active, // destroy the endpoint. // If the interface is inactive, we're done, since the endpoint // will have already been destroyed. // If the interface is active but the endpoint has already // been destroyed, end this connection-attempt. // if (ErrorCode) { if (Endpointp) { NhTrace( TRACE_FLAG_IO, "AlgConnectEndpointCompletionRoutine: deleting endpoint %d " "on error %d", EndpointId, ErrorCode ); AlgDeleteActiveEndpoint(Endpointp); } RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); break; } else if (!ALG_INTERFACE_ACTIVE(Interfacep)) { RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhTrace( TRACE_FLAG_IO, "AlgConnectEndpointCompletionRoutine: interface %d inactive", Interfacep->Index ); break; } else if (!Endpointp) { RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhTrace( TRACE_FLAG_IO, "AlgConnectEndpointCompletionRoutine: endpoint %d removed", EndpointId ); break; } // // We now activate the endpoint, beginning data transfer. // Note that it is the caller's responsibility to release // the two new references to the interface if an error occurs. // NhTrace( TRACE_FLAG_IO, "AlgConnectEndpointCompletionRoutine: endpoint %d socket %d " "connected", EndpointId, ConnectedSocket ); Error = AlgActivateActiveEndpoint(Interfacep, Endpointp); RELEASE_LOCK(Interfacep); } while(FALSE); } // AlgConnectEndpointCompletionRoutine VOID AlgReadEndpointCompletionRoutine( ULONG ErrorCode, ULONG BytesTransferred, PNH_BUFFER Bufferp ) /*++ Routine Description: This routine is invoked upon completion of a read operation on a ALG transparent proxy stream socket. The contexts for all reads are the interface and endpoint-identifier corresponding to the socket, stored in 'Context' and 'Context2', respectively. Arguments: ErrorCode - Win32 status code for the I/O operation BytesTransferred - number of bytes in 'Bufferp' Bufferp - holds data read from the socket Return Value: none. Environment: Runs in the context of a worker-thread which has just dequeued an I/O completion packet from the common I/O completion port with which our stream sockets are associated. A reference to the component will have been made on our behalf by 'NhReadStreamSocket'. A reference to the interface will have been made on our behalf by whoever issued the I/O request. --*/ { ULONG EndpointId; PALG_ENDPOINT Endpointp; ULONG Error; PALG_INTERFACE Interfacep; PROFILE("AlgReadEndpointCompletionRoutine"); do { Interfacep = (PALG_INTERFACE)Bufferp->Context; EndpointId = PtrToUlong(Bufferp->Context2); // // Acquire two additional references to the interface // for the followup requests that we will issue below, // lock the interface, and retrieve the endpoint. // EnterCriticalSection(&AlgInterfaceLock); if (!ALG_REFERENCE_INTERFACE(Interfacep)) { LeaveCriticalSection(&AlgInterfaceLock); NhReleaseBuffer(Bufferp); break; } ALG_REFERENCE_INTERFACE(Interfacep); LeaveCriticalSection(&AlgInterfaceLock); ACQUIRE_LOCK(Interfacep); Endpointp = AlgLookupInterfaceEndpoint(Interfacep, EndpointId, NULL); // // Process the read-completion. First we look for an error-code, // and if we find one, we decide whether to re-issue the read-request. // If the interface is still active, the error-code is non-fatal, and // the endpoint still exists, we reissue the read. // if (ErrorCode) { // // We won't be needing the second reference to the interface, // since we won't be calling 'AlgProcessMessage. // ALG_DEREFERENCE_INTERFACE(Interfacep); NhTrace( TRACE_FLAG_IO, "AlgReadEndpointCompletionRoutine: error %d for endpoint %d", ErrorCode, EndpointId ); if (!ALG_INTERFACE_ACTIVE(Interfacep) || !Endpointp) { RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhReleaseBuffer(Bufferp); } else if (NhIsFatalSocketError(ErrorCode)) { AlgDeleteActiveEndpoint(Endpointp); RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhReleaseBuffer(Bufferp); NhTrace( TRACE_FLAG_IO, "AlgReadEndpointCompletionRoutine: deleting endpoint %d " "on fatal read-error %d", EndpointId, ErrorCode ); } else { // // We need to repost the buffer for another read operation, // so we now reissue a read for the same number of bytes as // before. // Error = NhReadStreamSocket( &AlgComponentReference, Bufferp->Socket, Bufferp, Bufferp->BytesToTransfer, Bufferp->TransferOffset, AlgReadEndpointCompletionRoutine, Bufferp->Context, Bufferp->Context2 ); if (Error) { AlgDeleteActiveEndpoint(Endpointp); RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhTrace( TRACE_FLAG_IO, "AlgReadEndpointCompletionRoutine: deleting endpoint " "%d, NhReadStreamSocket=%d", EndpointId, Error ); if (Error != ERROR_NETNAME_DELETED) { NhWarningLog( IP_ALG_LOG_RECEIVE_FAILED, Error, "%I", NhQueryAddressSocket(Bufferp->Socket) ); } NhReleaseBuffer(Bufferp); break; } RELEASE_LOCK(Interfacep); } break; } else if (!BytesTransferred) { // // Zero bytes were read from the endpoint's socket. // This indicates that the sender has closed the socket. // We now propagate the closure to the alternate socket // for the endpoint. When the 'other' sender is done, // this endpoint will be removed altogether. // NhTrace( TRACE_FLAG_IO, "AlgReadEndpointCompletionRoutine: endpoint %d socket %d " "closed", EndpointId, Bufferp->Socket ); if (Endpointp) { AlgCloseActiveEndpoint(Endpointp, Bufferp->Socket); } RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhReleaseBuffer(Bufferp); break; } // // The original request completed successfully. // Now see if the interface and endpoint are operational and, // if not, return control. // if (!ALG_INTERFACE_ACTIVE(Interfacep)) { RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhReleaseBuffer(Bufferp); NhTrace( TRACE_FLAG_IO, "AlgReadEndpointCompletionRoutine: interface %d inactive", Interfacep->Index ); break; } else if (!Endpointp) { RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhReleaseBuffer(Bufferp); NhTrace( TRACE_FLAG_IO, "AlgReadEndpointCompletionRoutine: endpoint %d not found", EndpointId ); break; } // // Record the number of bytes read, and issue a read-request // for the remainder if necessary. Otherwise, process the completed // message. // NhTrace( TRACE_FLAG_IO, "AlgReadEndpointCompletionRoutine: endpoint %d socket %d read %d " "bytes", EndpointId, Bufferp->Socket, BytesTransferred ); ASSERT(BytesTransferred <= Bufferp->BytesToTransfer); Bufferp->BytesToTransfer -= BytesTransferred; Bufferp->TransferOffset += BytesTransferred; if (Bufferp->BytesToTransfer > 0 && AlgIsFullMessage( reinterpret_cast(Bufferp->Buffer), Bufferp->TransferOffset ) == NULL) { // // Read the remainder of the message, after releasing // the second reference to the interface, which is needed // only when we call 'AlgProcessMessage'. // ALG_DEREFERENCE_INTERFACE(Interfacep); Error = NhReadStreamSocket( &AlgComponentReference, Bufferp->Socket, Bufferp, Bufferp->BytesToTransfer, Bufferp->TransferOffset, AlgReadEndpointCompletionRoutine, Bufferp->Context, Bufferp->Context2 ); if (Error) { AlgDeleteActiveEndpoint(Endpointp); RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhTrace( TRACE_FLAG_IO, "AlgReadEndpointCompletionRoutine: deleting endpoint " "%d, NhReadStreamSocket=%d", EndpointId, Error ); if (Error != ERROR_NETNAME_DELETED) { NhWarningLog( IP_ALG_LOG_RECEIVE_FAILED, Error, "%I", NhQueryAddressSocket(Bufferp->Socket) ); } NhReleaseBuffer(Bufferp); break; } } else { // // We've finished reading something. Process it. // AlgProcessMessage(Interfacep, Endpointp, Bufferp); } RELEASE_LOCK(Interfacep); } while(FALSE); ALG_DEREFERENCE_INTERFACE(Interfacep); DEREFERENCE_ALG(); } // AlgReadEndpointCompletionRoutine VOID AlgWriteEndpointCompletionRoutine( ULONG ErrorCode, ULONG BytesTransferred, PNH_BUFFER Bufferp ) /*++ Routine Description: This routine is invoked upon completion of a write-operation on a stream socket for a ALG control-channel connection. The contexts for all writes are the interface and endpoint-identifier corresponding to the socket, stored in 'Context' and 'Context2', respectively. Arguments: ErrorCode - Win32 status code for the I/O operation BytesTransferred - number of bytes in 'Bufferp' Bufferp - holds data read from the stream socket Return Value: none. Environment: Runs in the context of a worker-thread which has just dequeued an I/O completion packet from the common I/O completion port with which our stream sockets are associated. A reference to the component will have been made on our behalf by 'NhWriteStreamSocket'. A reference to the interface will have been made on our behalf by whoever issued the I/O request. --*/ { ULONG Error; ULONG EndpointId; PALG_ENDPOINT Endpointp; PALG_INTERFACE Interfacep; PROFILE("AlgWriteEndpointCompletionRoutine"); do { Interfacep = (PALG_INTERFACE)Bufferp->Context; EndpointId = PtrToUlong(Bufferp->Context2); // // Acquire an additional reference to the interface // for the followup requests that we will issue below, // lock the interface, and retrieve the endpoint. // EnterCriticalSection(&AlgInterfaceLock); if (!ALG_REFERENCE_INTERFACE(Interfacep)) { LeaveCriticalSection(&AlgInterfaceLock); NhReleaseBuffer(Bufferp); break; } LeaveCriticalSection(&AlgInterfaceLock); ACQUIRE_LOCK(Interfacep); Endpointp = AlgLookupInterfaceEndpoint(Interfacep, EndpointId, NULL); // // Process the write-completion. First we look for an error-code, // and if we find one, we decide whether to re-issue the write-request. // If the interface is still active, the error-code is non-fatal, and // the endpoint still exists, we reissue the write. // if (ErrorCode) { NhTrace( TRACE_FLAG_IO, "AlgWriteEndpointCompletionRoutine: error %d for endpoint %d", ErrorCode, EndpointId ); if (!ALG_INTERFACE_ACTIVE(Interfacep) || !Endpointp) { RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhReleaseBuffer(Bufferp); } else if (NhIsFatalSocketError(ErrorCode)) { AlgDeleteActiveEndpoint(Endpointp); RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhReleaseBuffer(Bufferp); NhTrace( TRACE_FLAG_IO, "AlgWriteEndpointCompletionRoutine: deleting endpoint %d " "on fatal write-error %d", EndpointId, ErrorCode ); } else { // // We need to repost the buffer for another write operation, // so we now reissue a write for the same number of bytes // as before. // Error = NhWriteStreamSocket( &AlgComponentReference, Bufferp->Socket, Bufferp, Bufferp->BytesToTransfer, Bufferp->TransferOffset, AlgWriteEndpointCompletionRoutine, Bufferp->Context, Bufferp->Context2 ); if (Error) { AlgDeleteActiveEndpoint(Endpointp); RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhTrace( TRACE_FLAG_IO, "AlgWriteEndpointCompletionRoutine: deleting endpoint " "%d, NhWriteStreamSocket=%d", EndpointId, Error ); NhWarningLog( IP_ALG_LOG_SEND_FAILED, Error, "%I", NhQueryAddressSocket(Bufferp->Socket) ); NhReleaseBuffer(Bufferp); break; } RELEASE_LOCK(Interfacep); } break; } // // The original request completed successfully. // Now see if the interface and endpoint are operational and, // if not, return control. // if (!ALG_INTERFACE_ACTIVE(Interfacep)) { RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhReleaseBuffer(Bufferp); NhTrace( TRACE_FLAG_IO, "AlgWriteEndpointCompletionRoutine: interface %d inactive", Interfacep->Index ); break; } else if (!Endpointp) { RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhReleaseBuffer(Bufferp); NhTrace( TRACE_FLAG_IO, "AlgWriteEndpointCompletionRoutine: endpoint %d not found", EndpointId ); break; } // // Record the number of bytes written, and issue a write-request // for the remainder if necessary. Otherwise, we are done, // and we return to reading from the 'other' socket for the // control-channel. // NhTrace( TRACE_FLAG_IO, "AlgWriteEndpointCompletionRoutine: endpoint %d socket %d wrote %d " "bytes", EndpointId, Bufferp->Socket, BytesTransferred ); ASSERT(BytesTransferred <= Bufferp->BytesToTransfer); Bufferp->BytesToTransfer -= BytesTransferred; Bufferp->TransferOffset += BytesTransferred; if (Bufferp->BytesToTransfer) { // // Write the remainder of the message // Error = NhWriteStreamSocket( &AlgComponentReference, Bufferp->Socket, Bufferp, Bufferp->BytesToTransfer, Bufferp->TransferOffset, AlgWriteEndpointCompletionRoutine, Bufferp->Context, Bufferp->Context2 ); if (Error) { AlgDeleteActiveEndpoint(Endpointp); RELEASE_LOCK(Interfacep); ALG_DEREFERENCE_INTERFACE(Interfacep); NhTrace( TRACE_FLAG_IO, "AlgWriteEndpointCompletionRoutine: deleting endpoint %d, " "NhWriteStreamSocket=%d", EndpointId, Error ); NhWarningLog( IP_ALG_LOG_SEND_FAILED, Error, "%I", NhQueryAddressSocket(Bufferp->Socket) ); NhReleaseBuffer(Bufferp); break; } } else { SOCKET Socket; ULONG UserFlags; // // We now go back to reading from the other socket of the // endpoint, by issuing the next read on the endpoint's other // socket. Note that it is the responsibility of the callee // to release the reference to the interface if a failure occurs. // UserFlags = Bufferp->UserFlags; if (UserFlags & ALG_BUFFER_FLAG_FROM_ACTUAL_CLIENT) { Socket = Endpointp->HostSocket; UserFlags &= ~(ULONG)ALG_BUFFER_FLAG_CONTINUATION; UserFlags |= ALG_BUFFER_FLAG_FROM_ACTUAL_CLIENT; } else { Socket = Endpointp->ClientSocket; UserFlags &= ~(ULONG)ALG_BUFFER_FLAG_CONTINUATION; UserFlags |= ALG_BUFFER_FLAG_FROM_ACTUAL_HOST; } NhReleaseBuffer(Bufferp); Error = AlgReadActiveEndpoint( Interfacep, Endpointp, Socket, UserFlags ); if (Error) { NhTrace( TRACE_FLAG_IO, "AlgWriteEndpointCompletionRoutine: deleting endpoint %d, " "AlgReadActiveEndpoint=%d", EndpointId, Error ); AlgDeleteActiveEndpoint(Endpointp); RELEASE_LOCK(Interfacep); NhWarningLog( IP_ALG_LOG_RECEIVE_FAILED, Error, "%I", NhQueryAddressSocket(Socket) ); break; } } RELEASE_LOCK(Interfacep); } while(FALSE); ALG_DEREFERENCE_INTERFACE(Interfacep); DEREFERENCE_ALG(); } // AlgWriteEndpointCompletionRoutine