Windows NT 4.0 source code leak
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

/*++
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