mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
807 lines
18 KiB
807 lines
18 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
shutdown.c
|
|
|
|
Abstract:
|
|
|
|
This module contains support for the socket( ) and closesocket( )
|
|
WinSock APIs.
|
|
|
|
Author:
|
|
|
|
David Treadwell (davidtr) 20-Feb-1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "winsockp.h"
|
|
|
|
|
|
int
|
|
WSPAPI
|
|
WSPShutdown(
|
|
IN SOCKET Handle,
|
|
IN int HowTo,
|
|
OUT LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used on all types of sockets to disable reception,
|
|
transmission, or both.
|
|
|
|
If how is SD_RECEIVE, subsequent receives on the socket will be
|
|
disallowed. This has no effect on the lower protocol layers. For TCP
|
|
sockets, if there is still data queued on the socket waiting to be
|
|
received, or data arrives subsequently, the connection is reset, since the
|
|
data cannot be delivered to the user. For UDP sockets, incoming datagrams
|
|
are accepted and queued. In no case will an ICMP error packet
|
|
be generated.
|
|
|
|
If how is SD_SEND, subsequent sends on the socket are disallowed. For TCP
|
|
sockets, a FIN will be sent. Setting how to SD_BOTH disables both sends
|
|
and receives as described above.
|
|
|
|
Note that WSPShutdown() does not close the socket, and resources attached
|
|
to the socket will not be freed until WSPCloseSocket() is invoked.
|
|
|
|
WSPShutdown() does not block regardless of the SO_LINGER setting on the
|
|
socket. A WinSock SPI client should not rely on being able to re-use a
|
|
socket after it has been shut down. In particular, a WinSock service
|
|
provider is not required to support the use of WSPConnect() on such a
|
|
socket.
|
|
|
|
Arguments:
|
|
|
|
s - A descriptor identifying a socket.
|
|
|
|
how - A flag that describes what types of operation will no longer be
|
|
allowed.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
If no error occurs, WSPShutdown() returns 0. Otherwise, a value of
|
|
SOCKET_ERROR is returned, and a specific error code is available
|
|
in lpErrno.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PSOCKET_INFORMATION socket;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
int err;
|
|
AFD_PARTIAL_DISCONNECT_INFO disconnectInfo;
|
|
DWORD notificationEvent;
|
|
|
|
WS_ENTER( "WSPShutdown", (PVOID)Handle, (PVOID)HowTo, NULL, NULL );
|
|
|
|
WS_ASSERT( lpErrno != NULL );
|
|
|
|
err = SockEnterApi( TRUE, TRUE, FALSE );
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPShutdown", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up locals so that we know how to clean up on exit.
|
|
//
|
|
|
|
socket = NULL;
|
|
|
|
//
|
|
// Find a pointer to the socket structure corresponding to the
|
|
// passed-in handle.
|
|
//
|
|
|
|
socket = SockFindAndReferenceSocket( Handle, TRUE );
|
|
|
|
if ( socket == NULL ) {
|
|
|
|
err = WSAENOTSOCK;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Acquire the lock that protect this socket.
|
|
//
|
|
|
|
SockAcquireSocketLockExclusive( socket );
|
|
|
|
//
|
|
// If this is not a datagram socket, then it must be connected in order
|
|
// for WSPShutdown() to be a legal operation.
|
|
//
|
|
|
|
if ( !IS_DGRAM_SOCK(socket->SocketType) &&
|
|
!SockIsSocketConnected( socket ) ) {
|
|
|
|
err = WSAENOTCONN;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Translate the How parameter into the AFD disconnect information
|
|
// structure.
|
|
//
|
|
|
|
switch ( HowTo ) {
|
|
|
|
case SD_RECEIVE:
|
|
|
|
disconnectInfo.DisconnectMode = AFD_PARTIAL_DISCONNECT_RECEIVE;
|
|
socket->ReceiveShutdown = TRUE;
|
|
notificationEvent = WSH_NOTIFY_SHUTDOWN_RECEIVE;
|
|
break;
|
|
|
|
case SD_SEND:
|
|
|
|
disconnectInfo.DisconnectMode = AFD_PARTIAL_DISCONNECT_SEND;
|
|
socket->SendShutdown = TRUE;
|
|
notificationEvent = WSH_NOTIFY_SHUTDOWN_SEND;
|
|
break;
|
|
|
|
case SD_BOTH:
|
|
|
|
disconnectInfo.DisconnectMode =
|
|
AFD_PARTIAL_DISCONNECT_RECEIVE | AFD_PARTIAL_DISCONNECT_SEND;
|
|
socket->ReceiveShutdown = TRUE;
|
|
socket->SendShutdown = TRUE;
|
|
notificationEvent = WSH_NOTIFY_SHUTDOWN_ALL;
|
|
break;
|
|
|
|
default:
|
|
|
|
err = WSAEINVAL;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
// !!! temporary HACK for tp4!
|
|
|
|
if ( (HowTo == 1 || HowTo == 2) && socket->AddressFamily == AF_OSI ) {
|
|
|
|
disconnectInfo.DisconnectMode = AFD_ABORTIVE_DISCONNECT;
|
|
|
|
}
|
|
|
|
//
|
|
// This routine should complete immediately, not when the remote client
|
|
// acknowledges the disconnect.
|
|
//
|
|
|
|
disconnectInfo.Timeout = RtlConvertLongToLargeInteger( -1 );
|
|
|
|
IF_DEBUG(CLOSE) {
|
|
|
|
WS_PRINT(( "starting WSPShutdown for socket %lx\n", Handle ));
|
|
|
|
}
|
|
|
|
//
|
|
// Send the IOCTL to AFD for processing.
|
|
//
|
|
|
|
status = NtDeviceIoControlFile(
|
|
(HANDLE)socket->Handle,
|
|
SockThreadEvent,
|
|
NULL, // APC Routine
|
|
NULL, // APC Context
|
|
&ioStatusBlock,
|
|
IOCTL_AFD_PARTIAL_DISCONNECT,
|
|
&disconnectInfo,
|
|
sizeof(disconnectInfo),
|
|
NULL, // OutputBuffer
|
|
0L // OutputBufferLength
|
|
);
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
|
|
SockReleaseSocketLock( socket );
|
|
SockWaitForSingleObject(
|
|
SockThreadEvent,
|
|
socket->Handle,
|
|
SOCK_NEVER_CALL_BLOCKING_HOOK,
|
|
SOCK_NO_TIMEOUT
|
|
);
|
|
SockAcquireSocketLockExclusive( socket );
|
|
status = ioStatusBlock.Status;
|
|
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
err = SockNtStatusToSocketError( status );
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Notify the helper DLL that the socket has been shut down.
|
|
//
|
|
|
|
err = SockNotifyHelperDll( socket, notificationEvent );
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
exit:
|
|
|
|
IF_DEBUG(SHUTDOWN) {
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_PRINT(( "WSPShutdown(%ld) on socket %lx (%lx) failed: %ld.\n",
|
|
HowTo, Handle, socket, err ));
|
|
|
|
} else {
|
|
|
|
WS_PRINT(( "WSPShutdown(%ld) on socket %lx (%lx) succeeded.\n",
|
|
HowTo, Handle, socket ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( socket != NULL ) {
|
|
|
|
SockReleaseSocketLock( socket );
|
|
SockDereferenceSocket( socket );
|
|
|
|
}
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPShutdown", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
WS_EXIT( "WSPShutdown", NO_ERROR, FALSE );
|
|
return NO_ERROR;
|
|
|
|
} // WSPShutdown
|
|
|
|
|
|
int
|
|
WSPAPI
|
|
WSPRecvDisconnect(
|
|
SOCKET Handle,
|
|
LPWSABUF lpInboundDisconnectData,
|
|
LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used on connection-oriented sockets to disable reception,
|
|
and retrieve any incoming disconnect data from the remote party.
|
|
|
|
After this routine has been successfully issued, subsequent receives on the
|
|
socket will be disallowed. This has no effect on the lower protocol layers.
|
|
For TCP, the TCP window is not changed and incoming data will be accepted
|
|
(but not acknowledged) until the window is exhausted. For UDP, incoming
|
|
datagrams are accepted and queued. In no case will an ICMP error packet be
|
|
generated.
|
|
|
|
To successfully receive incoming disconnect data, a WinSock SPI client must
|
|
use other mechanisms to determine that the circuit has been closed. For
|
|
example, a client needs to receive an FD_CLOSE notification, or get a 0
|
|
return value, or a WSAEDISCON error code from WSPRecv().
|
|
|
|
Note that WSPRecvDisconnect() does not close the socket, and resources
|
|
attached to the socket will not be freed until WSPCloseSocket() is invoked.
|
|
|
|
WSPRecvDisconnect() does not block regardless of the SO_LINGER setting on
|
|
the socket.
|
|
|
|
A WinSock SPI client should not rely on being able to re-use a socket after
|
|
it has been WSPRecvDisconnect()ed. In particular, a WinSock provider is not
|
|
required to support the use of WSPConnect() on such a socket.
|
|
|
|
Arguments:
|
|
|
|
s - A descriptor identifying a socket.
|
|
|
|
lpInboundDisconnectData - A pointer to a buffer into which disconnect
|
|
data is to be copied.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
If no error occurs, WSPRecvDisconnect() returns 0. Otherwise, a value of
|
|
SOCKET_ERROR is returned, and a specific error code is available in
|
|
lpErrno.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PSOCKET_INFORMATION socket;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
int err;
|
|
AFD_PARTIAL_DISCONNECT_INFO disconnectInfo;
|
|
|
|
WS_ENTER( "WSPRecvDisconnect", (PVOID)Handle, (PVOID)lpInboundDisconnectData, NULL, NULL );
|
|
|
|
WS_ASSERT( lpErrno != NULL );
|
|
|
|
err = SockEnterApi( TRUE, TRUE, FALSE );
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPRecvDisconnect", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up locals so that we know how to clean up on exit.
|
|
//
|
|
|
|
socket = NULL;
|
|
|
|
//
|
|
// Find a pointer to the socket structure corresponding to the
|
|
// passed-in handle.
|
|
//
|
|
|
|
socket = SockFindAndReferenceSocket( Handle, TRUE );
|
|
|
|
if ( socket == NULL ) {
|
|
|
|
err = WSAENOTSOCK;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Acquire the lock that protect this socket.
|
|
//
|
|
|
|
SockAcquireSocketLockExclusive( socket );
|
|
|
|
//
|
|
// If this is not a datagram socket, then it must be connected in order
|
|
// for WSPRecvDisconnect() to be a legal operation.
|
|
//
|
|
|
|
if ( !IS_DGRAM_SOCK(socket->SocketType) &&
|
|
!SockIsSocketConnected( socket ) ) {
|
|
|
|
err = WSAENOTCONN;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Setup the disconnect info.
|
|
//
|
|
|
|
disconnectInfo.DisconnectMode = AFD_PARTIAL_DISCONNECT_RECEIVE;
|
|
disconnectInfo.Timeout = RtlConvertLongToLargeInteger( -1 );
|
|
|
|
// !!! temporary HACK for tp4!
|
|
|
|
if( socket->AddressFamily == AF_OSI ) {
|
|
|
|
disconnectInfo.DisconnectMode = AFD_ABORTIVE_DISCONNECT;
|
|
|
|
}
|
|
|
|
if( lpInboundDisconnectData != NULL &&
|
|
lpInboundDisconnectData->buf != NULL &&
|
|
lpInboundDisconnectData->len > 0 ) {
|
|
|
|
INT bufferLength;
|
|
|
|
//
|
|
// Retrieve the disconnect data from AFD.
|
|
//
|
|
|
|
bufferLength = (INT)lpInboundDisconnectData->len;
|
|
|
|
err = SockPassConnectData(
|
|
socket,
|
|
IOCTL_AFD_GET_DISCONNECT_DATA,
|
|
(PCHAR)lpInboundDisconnectData->buf,
|
|
bufferLength,
|
|
&bufferLength
|
|
);
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Note in the socket state that receives are shutdown.
|
|
//
|
|
|
|
socket->ReceiveShutdown = TRUE;
|
|
|
|
IF_DEBUG(SHUTDOWN) {
|
|
|
|
WS_PRINT(( "starting WSPRecvDisconnect for socket %lx\n", Handle ));
|
|
|
|
}
|
|
|
|
//
|
|
// Send the IOCTL to AFD for processing.
|
|
//
|
|
|
|
status = NtDeviceIoControlFile(
|
|
(HANDLE)socket->Handle,
|
|
SockThreadEvent,
|
|
NULL, // APC Routine
|
|
NULL, // APC Context
|
|
&ioStatusBlock,
|
|
IOCTL_AFD_PARTIAL_DISCONNECT,
|
|
&disconnectInfo,
|
|
sizeof(disconnectInfo),
|
|
NULL, // OutputBuffer
|
|
0L // OutputBufferLength
|
|
);
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
|
|
SockReleaseSocketLock( socket );
|
|
SockWaitForSingleObject(
|
|
SockThreadEvent,
|
|
socket->Handle,
|
|
SOCK_NEVER_CALL_BLOCKING_HOOK,
|
|
SOCK_NO_TIMEOUT
|
|
);
|
|
SockAcquireSocketLockExclusive( socket );
|
|
status = ioStatusBlock.Status;
|
|
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
err = SockNtStatusToSocketError( status );
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Notify the helper DLL that the socket has been shut down.
|
|
//
|
|
|
|
err = SockNotifyHelperDll( socket, WSH_NOTIFY_SHUTDOWN_RECEIVE );
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// CODEWORK: disconnect data!
|
|
//
|
|
|
|
if( lpInboundDisconnectData != NULL ) {
|
|
lpInboundDisconnectData->len = 0;
|
|
lpInboundDisconnectData->buf = NULL;
|
|
}
|
|
|
|
exit:
|
|
|
|
IF_DEBUG(SHUTDOWN) {
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_PRINT(( "WSPRecvDisconnect() on socket %lx (%lx) failed: %ld.\n",
|
|
Handle, socket, err ));
|
|
|
|
} else {
|
|
|
|
WS_PRINT(( "WSPRecvDisconnect() on socket %lx (%lx) succeeded.\n",
|
|
Handle, socket ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( socket != NULL ) {
|
|
|
|
SockReleaseSocketLock( socket );
|
|
SockDereferenceSocket( socket );
|
|
|
|
}
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPRecvDisconnect", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
WS_EXIT( "WSPRecvDisconnect", NO_ERROR, FALSE );
|
|
return NO_ERROR;
|
|
|
|
|
|
} // WSPRecvDisconnect
|
|
|
|
|
|
int
|
|
WSPAPI
|
|
WSPSendDisconnect (
|
|
SOCKET Handle,
|
|
LPWSABUF lpOutboundDisconnectData,
|
|
LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used on connection-oriented sockets to disable
|
|
transmission, and to initiate termination of the connection along with the
|
|
transmission of disconnect data, if any.
|
|
|
|
After this routine has been successfully issued, subsequent sends are
|
|
disallowed.
|
|
|
|
lpOutboundDisconnectData, if not NULL, points to a buffer containing the
|
|
outgoing disconnect data to be sent to the remote party.
|
|
|
|
Note that WSPSendDisconnect() does not close the socket, and resources
|
|
attached to the socket will not be freed until WSPCloseSocket() is invoked.
|
|
|
|
WSPSendDisconnect() does not block regardless of the SO_LINGER setting on
|
|
the socket.
|
|
|
|
A WinSock SPI client should not rely on being able to re-use a socket after
|
|
it has been WSPSendDisconnect()ed. In particular, a WinSock provider is not
|
|
required to support the use of WSPConnect() on such a socket.
|
|
|
|
Arguments:
|
|
|
|
s - A descriptor identifying a socket.
|
|
|
|
lpOutboundDisconnectData - A pointer to the outgoing disconnect data.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
If no error occurs, WSPSendDisconnect() returns 0. Otherwise, a value of
|
|
SOCKET_ERROR is returned, and a specific error code is available in
|
|
lpErrno.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PSOCKET_INFORMATION socket;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
int err;
|
|
AFD_PARTIAL_DISCONNECT_INFO disconnectInfo;
|
|
|
|
WS_ENTER( "WSPSendDisconnect", (PVOID)Handle, (PVOID)lpOutboundDisconnectData, NULL, NULL );
|
|
|
|
WS_ASSERT( lpErrno != NULL );
|
|
|
|
err = SockEnterApi( TRUE, TRUE, FALSE );
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPSendDisconnect", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up locals so that we know how to clean up on exit.
|
|
//
|
|
|
|
socket = NULL;
|
|
|
|
//
|
|
// Find a pointer to the socket structure corresponding to the
|
|
// passed-in handle.
|
|
//
|
|
|
|
socket = SockFindAndReferenceSocket( Handle, TRUE );
|
|
|
|
if ( socket == NULL ) {
|
|
|
|
err = WSAENOTSOCK;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Acquire the lock that protect this socket.
|
|
//
|
|
|
|
SockAcquireSocketLockExclusive( socket );
|
|
|
|
//
|
|
// If this is not a datagram socket, then it must be connected in order
|
|
// for WSPSendDisconnect() to be a legal operation.
|
|
//
|
|
|
|
if ( !IS_DGRAM_SOCK(socket->SocketType) &&
|
|
!SockIsSocketConnected( socket ) ) {
|
|
|
|
err = WSAENOTCONN;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Setup the disconnect info.
|
|
//
|
|
|
|
disconnectInfo.DisconnectMode = AFD_PARTIAL_DISCONNECT_SEND;
|
|
disconnectInfo.Timeout = RtlConvertLongToLargeInteger( -1 );
|
|
|
|
// !!! temporary HACK for tp4!
|
|
|
|
if( socket->AddressFamily == AF_OSI ) {
|
|
|
|
disconnectInfo.DisconnectMode = AFD_ABORTIVE_DISCONNECT;
|
|
|
|
}
|
|
|
|
if( lpOutboundDisconnectData != NULL &&
|
|
lpOutboundDisconnectData->buf != NULL &&
|
|
lpOutboundDisconnectData->len > 0 ) {
|
|
|
|
INT bufferLength;
|
|
|
|
//
|
|
// Set the disconnect data on the socket.
|
|
//
|
|
|
|
bufferLength = (INT)lpOutboundDisconnectData->len;
|
|
|
|
err = SockPassConnectData(
|
|
socket,
|
|
IOCTL_AFD_SET_DISCONNECT_DATA,
|
|
(PCHAR)lpOutboundDisconnectData->buf,
|
|
bufferLength,
|
|
&bufferLength
|
|
);
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Note in the socket state that sends are shutdown.
|
|
//
|
|
|
|
socket->SendShutdown = TRUE;
|
|
|
|
IF_DEBUG(SHUTDOWN) {
|
|
|
|
WS_PRINT(( "starting WSPSendDisconnect for socket %lx\n", Handle ));
|
|
|
|
}
|
|
|
|
//
|
|
// Send the IOCTL to AFD for processing.
|
|
//
|
|
|
|
status = NtDeviceIoControlFile(
|
|
(HANDLE)socket->Handle,
|
|
SockThreadEvent,
|
|
NULL, // APC Routine
|
|
NULL, // APC Context
|
|
&ioStatusBlock,
|
|
IOCTL_AFD_PARTIAL_DISCONNECT,
|
|
&disconnectInfo,
|
|
sizeof(disconnectInfo),
|
|
NULL, // OutputBuffer
|
|
0L // OutputBufferLength
|
|
);
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
|
|
SockReleaseSocketLock( socket );
|
|
SockWaitForSingleObject(
|
|
SockThreadEvent,
|
|
socket->Handle,
|
|
SOCK_NEVER_CALL_BLOCKING_HOOK,
|
|
SOCK_NO_TIMEOUT
|
|
);
|
|
SockAcquireSocketLockExclusive( socket );
|
|
status = ioStatusBlock.Status;
|
|
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
err = SockNtStatusToSocketError( status );
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Notify the helper DLL that the socket has been shut down.
|
|
//
|
|
|
|
err = SockNotifyHelperDll( socket, WSH_NOTIFY_SHUTDOWN_SEND );
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
exit:
|
|
|
|
IF_DEBUG(SHUTDOWN) {
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_PRINT(( "WSPSendDisconnect() on socket %lx (%lx) failed: %ld.\n",
|
|
Handle, socket, err ));
|
|
|
|
} else {
|
|
|
|
WS_PRINT(( "WSPSendDisconnect() on socket %lx (%lx) succeeded.\n",
|
|
Handle, socket ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( socket != NULL ) {
|
|
|
|
SockReleaseSocketLock( socket );
|
|
SockDereferenceSocket( socket );
|
|
|
|
}
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPSendDisconnect", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
WS_EXIT( "WSPSendDisconnect", NO_ERROR, FALSE );
|
|
return NO_ERROR;
|
|
|
|
} // WSPSendDisconnect
|
|
|