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.
 
 
 
 
 
 

2812 lines
81 KiB

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
sockopt.c
Abstract:
This module contains support for the getsockopt( ) and setsockopt( )
WinSock APIs.
Author:
David Treadwell (davidtr) 31-Mar-1992
Revision History:
--*/
#include "winsockp.h"
typedef struct _SOCK_EXTENSIONS {
GUID Guid;
LPVOID Function;
} SOCK_EXTENSIONS, *PSOCK_EXTENSIONS;
SOCK_EXTENSIONS SockExtensions[] =
{
{
WSAID_TRANSMITFILE,
(LPVOID)&TransmitFile
},
{
WSAID_ACCEPTEX,
(LPVOID)&AcceptEx
},
{
WSAID_GETACCEPTEXSOCKADDRS,
(LPVOID)&GetAcceptExSockaddrs
}
};
#define NUM_EXTENSIONS ( sizeof(SockExtensions) / sizeof(SockExtensions[0]) )
int
GetReceiveInformation (
IN PSOCKET_INFORMATION Socket,
OUT PAFD_RECEIVE_INFORMATION ReceiveInformation
);
BOOLEAN
IsValidOptionForSocket (
IN PSOCKET_INFORMATION Socket,
IN int Level,
IN int OptionName
);
int
WSPAPI
WSPGetSockOpt (
IN SOCKET Handle,
IN int Level,
IN int OptionName,
char *OptionValue,
int *OptionLength,
LPINT lpErrno
)
/*++
Routine Description:
This routine retrieves the current value for a socket option associated
with a socket of any type, in any state, and stores the result in optval.
Options may exist at multiple protocol levels, but they are always present
at the uppermost "socket" level. Options affect socket operations, such as
the routing of packets, out-of-band data transfer, etc.
The value associated with the selected option is returned in the buffer
optval. The integer pointed to by optlen should originally contain the size
of this buffer; on return, it will be set to the size of the value
returned. For SO_LINGER, this will be the size of a struct linger; for most
other options it will be the size of an integer.
The WinSock SPI client is responsible for allocating any memory space
pointed to directly or indirectly by any of the parameters it specifies.
If the option was never set with WSPSetSockOpt(), then WSPGetSockOpt()
returns the default value for the option.
Value Type Meaning
~~~~~ ~~~~ ~~~~~~~
SO_ACCEPTCONN BOOL Socket is WSPListen()ing.
SO_BROADCAST BOOL Socket is configured for the
transmission of broadcast messages
on the socket.
SO_DEBUG BOOL Debugging is enabled.
SO_DONTROUTE BOOL Routing is disabled.
SO_GROUP_ID GROUP The identifier of the group to
which the socket belongs.
SO_GROUP_PRIORITY int The relative priority for sockets
that are part of a socket group.
SO_KEEPALIVE BOOL Keepalives are being sent.
SO_LINGER struct linger Returns the current linger options.
SO_MAX_MSG_SIZE unsigned int Maximum size of a message for
message-oriented socket types
(e.g. SOCK_DGRAM). Has no meaning
for stream-oriented sockets.
SO_OOBINLINE BOOL Out-of-band data is being received
in the normal data stream.
SO_PROTOCOL_INFOW WSAPROTOCOL_INFOW Description of the protocol info
for the protocol that is bound
to this socket.
SO_RCVBUF int Buffer size for receives.
SO_REUSEADDR BOOL The socket may be bound to an
address which is already in use.
SO_SNDBUF int Buffer size for sends.
SO_TYPE int The type of the socket (e.g.
SOCK_STREAM).
PVD_CONFIG Service An "opaque" data structure object
Provider from the service provider
Dependent associated with socket s. This
object stores the current
configuration information of the
service provider. The exact format
of this data structure is service
provider specific.
Calling WSPGetSockOpt() with an unsupported option will result in an error
code of WSAENOPROTOOPT being returned in lpErrno.
SO_DEBUG - WinSock service providers are encouraged (but not required) to
supply output debug information if the SO_DEBUG option is set by a WinSock
SPI client. The mechanism for generating the debug information and the form
it takes are beyond the scope of this specification.
SO_ERROR - The SO_ERROR option returns and resets the per-socket based
error code (which is not necessarily the same as the per-thread error code
that is maintained by the WinSock 2 DLL). A successful WinSock call on the
socket does not reset the socket-based error code returned by the SO_ERROR
option.
SO_GROUP_ID - This is a get-only socket option which supplies the
identifier of the group this socket belongs to. Note that socket group IDs
are unique across all processes for a give service provider. If this socket
is not a group socket, the value is NULL.
SO_GROUP_PRIORITY - Group priority indicates the priority of the specified
socket relative to other sockets within the socket group. Values are non-
negative integers, with zero corresponding to the highest priority.
Priority values represent a hint to the service provider about how
potentially scarce resources should be allocated. For example, whenever
two or more sockets are both ready to transmit data, the highest priority
socket (lowest value for SO_GROUP_PRIORITY) should be serviced first, with
the remainder serviced in turn according to their relative priorities.
The WSAENOPROTOOPT error is indicated for non group sockets or for service
providers which do not support group sockets.
SO_KEEPALIVE - An WinSock SPI client may request that a TCP/IP provider
enable the use of "keep-alive" packets on TCP connections by turning on the
SO_KEEPALIVE socket option. A WinSock provider need not support the use of
keep-alives: if it does, the precise semantics are implementation-specific
but should conform to section 4.2.3.6 of RFC 1122: Requirements for
Internet Hosts -- Communication Layers. If a connection is dropped as the
result of "keep-alives" the error code WSAENETRESET is returned to any
calls in progress on the socket, and any subsequent calls will fail with
WSAENOTCONN.
SO_LINGER - SO_LINGER controls the action taken when unsent data is queued
on a socket and a WSPCloseSocket() is performed. See WSPCloseSocket() for a
description of the way in which the SO_LINGER settings affect the semantics
of WSPCloseSocket(). The WinSock SPI client sets the desired behavior by
creating a struct linger (pointed to by the optval argument) with the
following elements:
struct linger {
u_short l_onoff;
u_short l_linger;
};
To enable SO_LINGER, a WinSock SPI client should set l_onoff to a non-zero
value, set l_linger to 0 or the desired timeout (in seconds), and call
WSPSetSockOpt(). To enable SO_DONTLINGER (i.e. disable SO_LINGER) l_onoff
should be set to zero and WSPSetSockOpt() should be called. Note that
enabling SO_LINGER with a non-zero timeout on a non-blocking socket is not
recommended (see WSPCloseSocket() for details).
Enabling SO_LINGER also disables SO_DONTLINGER, and vice versa. Note that
if SO_DONTLINGER is DISABLED (i.e. SO_LINGER is ENABLED) then no timeout
value is specified. In this case the timeout used is implementation
dependent. If a previous timeout has been established for a socket (by
enabling SO_LINGER), then this timeout value should be reinstated by the
service provider.
SO_MAX_MSG_SIZE - This is a get-only socket option which indicates the
maximum size of a message for message-oriented socket types (e.g.
SOCK_DGRAM) as implemented by the service provider. It has no meaning for
byte stream oriented sockets.
SO_PROTOCOL_INFOW - This is a get-only option which supplies the
WSAPROTOCOL_INFOW structure associated with this socket.
SO_RCVBUF & SO_SNDBUF - When a Windows Sockets implementation supports the
SO_RCVBUF and SO_SNDBUF options, a WinSock SPI client may request different
buffer sizes (larger or smaller). The call may succeed even though the
service provider did not make available the entire amount requested. A
WinSock SPI client must call WSPGetSockOpt() with the same option to check
the buffer size actually provided.
SO_REUSEADDR - By default, a socket may not be bound (see WSPBind()) to a
local address which is already in use. On occasions, however, it may be
desirable to "re-use" an address in this way. Since every connection is
uniquely identified by the combination of local and remote addresses, there
is no problem with having two sockets bound to the same local address as
long as the remote addresses are different. To inform the WinSock provider
that a WSPBind() on a socket should be allowed to bind to a local address
that is already in use by another socket, the WinSock SPI client should set
the SO_REUSEADDR socket option for the socket before issuing the WSPBind().
Note that the option is interpreted only at the time of the WSPBind(): it
is therefore unnecessary (but harmless) to set the option on a socket which
is not to be bound to an existing address, and setting or resetting the
option after the WSPBind() has no effect on this or any other socket.
PVD_CONFIG - This object stores the configuration information for the
service provider associated with socket s. The exact format of this data
structure is service provider specific.
Arguments:
s - A descriptor identifying a socket.
level - The level at which the option is defined; the supported levels
include SOL_SOCKET.
optname - The socket option for which the value is to be retrieved.
optval - A pointer to the buffer in which the value for the requested
option is to be returned.
optlen - A pointer to the size of the optval buffer.
lpErrno - A pointer to the error code.
Return Value:
If no error occurs, WSPGetSockOpt() returns 0. Otherwise, a value of
SOCKET_ERROR is returned, and a specific error code is available in
lpErrno.
--*/
{
int err;
PSOCKET_INFORMATION socket;
WS_ENTER( "WSPGetSockOpt", (PVOID)Handle, (PVOID)Level, (PVOID)OptionName, OptionValue );
WS_ASSERT( lpErrno != NULL );
err = SockEnterApi( TRUE, TRUE, FALSE );
if( err != NO_ERROR ) {
WS_EXIT( "WSPGetSockOpt", INVALID_SOCKET, TRUE );
*lpErrno = err;
return INVALID_SOCKET;
}
//
// Initialize 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 ) {
WS_EXIT( "WSPGetSockOpt", SOCKET_ERROR, TRUE );
*lpErrno = WSAENOTSOCK;
return SOCKET_ERROR;
}
//
// Get exclusive access to the socket in question. This is
// necessary because we'll be changing the socket information
// data structure.
//
SockAcquireSocketLockExclusive( socket );
//
// Ensure the socket isn't closing on us.
//
if( socket->State == SocketStateClosing ) {
err = WSAENOTSOCK;
goto exit;
}
//
// OptionValue must point to a buffer of at least sizeof(int) bytes.
//
if ( !ARGUMENT_PRESENT( OptionValue ) ||
!ARGUMENT_PRESENT( OptionLength ) ||
*OptionLength < sizeof(int) ||
(*OptionLength & 0x80000000) != 0 ) {
err = WSAEFAULT;
goto exit;
}
//
// Make sure that this is a legitimate option for the socket.
//
if ( !IsValidOptionForSocket( socket, Level, OptionName ) ) {
err = WSAENOPROTOOPT;
goto exit;
}
//
// If the specified option is supprted here, clear out the input
// buffer so that we know we start with a clean slate.
//
if ( Level == SOL_SOCKET &&
( OptionName == SO_BROADCAST ||
OptionName == SO_DEBUG ||
OptionName == SO_DONTLINGER ||
OptionName == SO_LINGER ||
OptionName == SO_OOBINLINE ||
OptionName == SO_RCVBUF ||
OptionName == SO_REUSEADDR ||
OptionName == SO_SNDBUF ||
OptionName == SO_TYPE ||
OptionName == SO_ACCEPTCONN ||
OptionName == SO_ERROR ) ) {
RtlZeroMemory( OptionValue, *OptionLength );
*OptionLength = sizeof(int);
}
//
// Act based on the level and option being set.
//
switch ( Level ) {
case SOL_SOCKET:
switch ( OptionName ) {
case SO_BROADCAST:
*OptionValue = socket->Broadcast;
break;
case SO_DEBUG:
*OptionValue = socket->Debug;
break;
case SO_ERROR: {
int *optionValue = (int *)OptionValue;
*optionValue = socket->LastError;
break;
}
case SO_DONTLINGER:
*OptionValue = socket->LingerInfo.l_onoff == 0 ? TRUE : FALSE;
break;
case SO_LINGER: {
struct linger *optionValue = (struct linger *)OptionValue;
if ( *OptionLength < sizeof(struct linger) ) {
err = WSAEFAULT;
goto exit;
}
*optionValue = socket->LingerInfo;
*OptionLength = sizeof(struct linger);
break;
}
case SO_OOBINLINE:
*OptionValue = socket->OobInline;
break;
case SO_RCVBUF: {
int *optionValue = (int *)OptionValue;
*optionValue = socket->ReceiveBufferSize;
break;
}
case SO_REUSEADDR:
*OptionValue = socket->ReuseAddresses;
break;
case SO_SNDBUF: {
int *optionValue = (int *)OptionValue;
*optionValue = socket->SendBufferSize;
break;
}
case SO_TYPE: {
int *optionValue = (int *)OptionValue;
*optionValue = socket->SocketType;
break;
}
case SO_ACCEPTCONN:
if ( socket->State == SocketStateListening ) {
*OptionValue = TRUE;
} else {
*OptionValue = FALSE;
}
break;
case SO_CONNDATA:
err = SockPassConnectData(
socket,
IOCTL_AFD_GET_CONNECT_DATA,
(PCHAR)OptionValue,
*OptionLength,
OptionLength
);
goto exit;
case SO_CONNOPT:
err = SockPassConnectData(
socket,
IOCTL_AFD_GET_CONNECT_OPTIONS,
(PCHAR)OptionValue,
*OptionLength,
OptionLength
);
goto exit;
case SO_DISCDATA:
err = SockPassConnectData(
socket,
IOCTL_AFD_GET_DISCONNECT_DATA,
(PCHAR)OptionValue,
*OptionLength,
OptionLength
);
goto exit;
case SO_DISCOPT:
err = SockPassConnectData(
socket,
IOCTL_AFD_GET_DISCONNECT_OPTIONS,
(PCHAR)OptionValue,
*OptionLength,
OptionLength
);
goto exit;
case SO_SNDTIMEO:
*(PINT)OptionValue = socket->SendTimeout;
goto exit;
case SO_RCVTIMEO:
*(PINT)OptionValue = socket->ReceiveTimeout;
goto exit;
case SO_MAXDG:
case SO_MAX_MSG_SIZE :
err = SockGetInformation(
socket,
AFD_MAX_SEND_SIZE,
NULL,
0,
NULL,
(PULONG)OptionValue,
NULL
);
if ( err == NO_ERROR ) {
*OptionLength = sizeof(ULONG);
}
goto exit;
case SO_CONNECT_TIME:
err = SockGetInformation(
socket,
AFD_CONNECT_TIME,
NULL,
0,
NULL,
(PULONG)OptionValue,
NULL
);
if ( err == NO_ERROR ) {
*OptionLength = sizeof(ULONG);
}
goto exit;
case SO_MAXPATHDG: {
PTRANSPORT_ADDRESS tdiAddress;
ULONG tdiAddressLength;
//
// Allocate enough space to hold the TDI address structure
// we'll pass to AFD.
//
tdiAddressLength = socket->HelperDll->MaxTdiAddressLength;
tdiAddress = ALLOCATE_HEAP( tdiAddressLength );
if ( tdiAddress == NULL ) {
err = WSAENOBUFS;
goto exit;
}
//
// Convert the address from the sockaddr structure to the
// appropriate TDI structure.
//
SockBuildTdiAddress(
tdiAddress,
(PSOCKADDR)OptionValue,
*OptionLength
);
err = SockGetInformation(
socket,
AFD_MAX_PATH_SEND_SIZE,
tdiAddress,
tdiAddress->Address[0].AddressLength,
NULL,
(PULONG)OptionValue,
NULL
);
FREE_HEAP( tdiAddress );
if ( err == NO_ERROR ) {
*OptionLength = sizeof(ULONG);
}
goto exit;
}
case SO_PROTOCOL_INFOW: {
LPWSAPROTOCOL_INFOW protocolInfo = (LPWSAPROTOCOL_INFOW)OptionValue;
if( *OptionLength < sizeof(WSAPROTOCOL_INFOW) ) {
err = WSAEFAULT;
goto exit;
}
SockBuildProtocolInfoForSocket(
socket,
protocolInfo
);
*OptionLength = sizeof(WSAPROTOCOL_INFOW);
err = NO_ERROR;
}
goto exit;
case SO_GROUP_ID:
*(PINT)OptionValue = socket->GroupID;
goto exit;
case SO_GROUP_PRIORITY:
*(PINT)OptionValue = socket->GroupPriority;
goto exit;
case SO_DONTROUTE:
case SO_KEEPALIVE:
case SO_RCVLOWAT:
case SO_SNDLOWAT:
default:
//
// We don't support this option here in the winsock DLL. Give
// it to the helper DLL.
//
err = SockGetTdiHandles( socket );
if ( err != NO_ERROR ) {
goto exit;
}
err = socket->HelperDll->WSHGetSocketInformation(
socket->HelperDllContext,
Handle,
socket->TdiAddressHandle,
socket->TdiConnectionHandle,
Level,
OptionName,
OptionValue,
OptionLength
);
if ( err != NO_ERROR ) {
goto exit;
}
break;
}
break;
default:
//
// The specified level isn't supported here in the winsock DLL.
// Give the request to the helper DLL.
//
err = SockGetTdiHandles( socket );
if ( err != NO_ERROR ) {
goto exit;
}
err = socket->HelperDll->WSHGetSocketInformation(
socket->HelperDllContext,
Handle,
socket->TdiAddressHandle,
socket->TdiConnectionHandle,
Level,
OptionName,
OptionValue,
OptionLength
);
if ( err != NO_ERROR ) {
goto exit;
}
break;
}
exit:
//
// Release the resource, dereference the socket, set the error if
// any, and return.
//
IF_DEBUG(SOCKOPT) {
if ( err != NO_ERROR ) {
WS_PRINT(( "WSPGetSockOpt on socket %lx (%lx) failed: %ld.\n",
Handle, socket, err ));
} else {
WS_PRINT(( "WSPGetSockOpt on socket %lx (%lx) succeeded.\n",
Handle, socket ));
}
}
if ( socket != NULL ) {
SockReleaseSocketLock( socket );
SockDereferenceSocket( socket );
}
if ( err != NO_ERROR ) {
WS_EXIT( "WSPGetSockOpt", SOCKET_ERROR, TRUE );
*lpErrno = err;
return SOCKET_ERROR;
}
WS_EXIT( "WSPGetSockOpt", NO_ERROR, FALSE );
return NO_ERROR;
} // WSPGetSockOpt
int
WSPAPI
WSPIoctl (
SOCKET Handle,
DWORD dwIoControlCode,
LPVOID lpvInBuffer,
DWORD cbInBuffer,
LPVOID lpvOutBuffer,
DWORD cbOutBuffer,
LPDWORD lpcbBytesReturned,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
LPWSATHREADID lpThreadId,
LPINT lpErrno
)
/*++
Routine Description:
This routine is used to set or retrieve operating parameters associated
with the socket, the transport protocol, or the communications subsystem.
For non-overlapped sockets, lpOverlapped and lpCompletionRoutine
parameters are ignored, and this routine may block if socket s is in the
blocking mode. Note that if socket s is in the non-blocking mode, this
routine may return WSAEWOULDBLOCK if the specified operation cannot be
finished immediately. In this case, the WinSock SPI client should change
the socket to the blocking mode and reissue the request. For overlapped
sockets, operations that cannot be completed immediately will be initiated,
and completion will be indicated at a later time. The final completion
status is retrieved via the WSPGetOverlappedResult().
In as much as the dwIoControlCode parameter is now a 32 bit entity, it is
possible to adopt an encoding scheme that provides a convenient way to
partition the opcode identifier space. The dwIoControlCode parameter is
architected to allow for protocol and vendor independence when adding new
control codes, while retaining backward compatibility with Windows Sockets
1.1 and Unix control codes. The dwIoControlCode parameter has the following
form:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|3|3|2|2|2|2|2|2|2|2|2|2|1|1|1|1|1|1|1|1|1|1| | | | | | | | | | |
|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|I|O|V| T |Vendor/Address Family| Code |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
I - Set if the input buffer is valid for the code, as with IOC_IN.
O - Set if the output buffer is valid for the code, as with IOC_OUT.
Note that for codes with both input and output parameters, both I
and O will be set.
V - Set if there are no parameters for the code, as with IOC_VOID.
T - A two-bit quantity which defines the type of ioctl. The following
values are defined:
0 - The ioctl is a standard Unix ioctl code, as with FIONREAD,
FIONBIO, etc.
1 - The ioctl is a generic Windows Sockets 2 ioctl code. New
ioctl codes defined for Windows Sockets 2 will have T == 1.
2 - The ioctl applies only to a specific address family.
3 - The ioctl applies only to a specific vendor's provider. This
type allows companies to be assigned a vendor number which
appears in the Vendor/Address Family field, and then the vendor
can define new ioctls specific to that vendor without having to
register the ioctl with a clearinghouse, thereby providing vendor
flexibility and privacy.
Vendor/Address Family - An 11-bit quantity which defines the vendor
who owns the code (if T == 3) or which contains the address family
to which the code applies (if T == 2). If this is a Unix ioctl code
(T == 0) then this field has the same value as the code on Unix. If
this is a generic Windows Sockets 2 ioctl (T == 1) then this field
can be used as an extension of the "code" field to provide additional
code values.
Code - The specific ioctl code for the operation.
The following Unix commands are supported:
FIONBIO - Enable or disable non-blocking mode on socket s. lpvInBuffer
points at an unsigned long, which is non-zero if non-blocking mode is to be
enabled and zero if it is to be disabled. When a socket is created, it
operates in blocking mode (i.e. non-blocking mode is disabled). This is
consistent with BSD sockets.
The WSPAsyncSelect() or WSPEventSelect() routine automatically sets a
socket to nonblocking mode. If WSPAsyncSelect() or WSPEventSelect() has
been issued on a socket, then any attempt to use WSPIoctl() to set the
socket back to blocking mode will fail with WSAEINVAL. To set the socket
back to blocking mode, a WinSock SPI client must first disable
WSPAsyncSelect() by calling WSPAsyncSelect() with the lEvent parameter
equal to 0, or disable WSPEventSelect() by calling WSPEventSelect() with
the lNetworkEvents parameter equal to 0.
FIONREAD - Determine the amount of data which can be read atomically from
socket s. lpvOutBuffer points at an unsigned long in which WSPIoctl()
stores the result. If s is stream-oriented (e.g., type SOCK_STREAM),
FIONREAD returns the total amount of data which may be read in a single
receive operation; this is normally the same as the total amount of data
queued on the socket. If s is message-oriented (e.g., type SOCK_DGRAM),
FIONREAD returns the size of the first datagram (message) queued on the
socket.
SIOCATMARK - Determine whether or not all out-of-band data has been read.
This applies only to a socket of stream style (e.g., type SOCK_STREAM)
which has been configured for in-line reception of any out-of-band data
(SO_OOBINLINE). If no out-of-band data is waiting to be read, the operation
returns TRUE. Otherwise it returns FALSE, and the next receive operation
performed on the socket will retrieve some or all of the data preceding the "
mark"; the WinSock SPI client should use the SIOCATMARK operation to
determine whether any remains. If there is any normal data preceding the
"urgent" (out of band) data, it will be received in order. (Note that
receive operations will never mix out-of-band and normal data in the same
call.) lpvOutBuffer points at a BOOL in which WSPIoctl() stores the result.
The following WinSock 2 commands are supported:
SIO_ASSOCIATE_HANDLE (opcode setting: I, T==1) - Associate this socket with
the specified handle of a companion interface. The input buffer contains
the integer value corresponding to the manifest constant for the companion
interface (e.g., TH_NETDEV, TH_TAPI, etc.), followed by a value which is a
handle of the specified companion interface, along with any other required
information. Refer to the appropriate section in the Windows Sockets 2
Protocol-Specific Annex and/or documentation for the particular companion
interface for additional details. The total size is reflected in the input
buffer length. No output buffer is required. The WSAENOPROTOOPT error code
is indicated for service providers which do not support this ioctl.
SIO_ENABLE_CIRCULAR_QUEUEING (opcode setting: V, T==1) - Indicates to a
message-oriented service provider that a newly arrived message should
never be dropped because of a buffer queue overflow. Instead, the oldest
message in the queue should be eliminated in order to accommodate the newly
arrived message. No input and output buffers are required. Note that this
ioctl is only valid for sockets associated with unreliable, message-
oriented protocols. The WSAENOPROTOOPT error code is indicated for service
providers which do not support this ioctl.
SIO_FIND_ROUTE (opcode setting: O, T==1) - When issued, this ioctl requests
that the route to the remote address specified as a sockaddr in the input
buffer be discovered. If the address already exists in the local cache, its
entry is invalidated. In the case of Novell's IPX, this call initiates an
IPX GetLocalTarget (GLT), which queries the network for the given remote
address.
SIO_FLUSH (opcode setting: V, T==1) - Discards current contents of the
sending queue associated with this socket. No input and output buffers are
required. The WSAENOPROTOOPT error code is indicated for service providers
which do not support this ioctl.
SIO_GET_BROADCAST_ADDRESS (opcode setting: O, T==1) - This ioctl fills the
output buffer with a sockaddr struct containing a suitable broadcast
address for use with WSPIoctl().
SIO_GET_EXTENSION_FUNCTION_POINTER (opcode setting: O, I, T==1) - Retrieve
a pointer to the specified extension function supported by the associated
service provider. The input buffer contains a GUID whose value identifies
the extension function in question. The pointer to the desired function is
returned in the output buffer. Extension function identifiers are
established by service provider vendors and should be included in vendor
documentation that describes extension function capabilities and semantics.
SIO_GET_QOS (opcode setting: O,I, T==1) - Retrieve the QOS structure
associated with the socket. The input buffer is optional. Some protocols
(e.g. RSVP) allow the input buffer to be used to qualify a QOS request.
The QOS structure will be copied into the output buffer. The output buffer
must be sized large enough to be able to contain the full QOS struct. The
WSAENOPROTOOPT error code is indicated for service providers which do not
support QOS.
SIO_GET_GROUP_QOS (opcode setting: O,I, T==1) - Retrieve the QOS structure
associated with the socket group to which this socket belongs. The input
buffer is optional. Some protocols (e.g. RSVP) allow the input buffer to
be used to qualify a QOS request. The QOS structure will be copied into
the output buffer. If this socket does not belong to an appropriate socket
group, the SendingFlowspec and ReceivingFlowspec fields of the returned QOS
struct are set to NULL. The WSAENOPROTOOPT error code is indicated for
service providers which do not support QOS.
SIO_MULTIPOINT_LOOPBACK (opcode setting: I, T==1) - Controls whether data
sent in a multipoint session will also be received by the same socket on
the local host. A value of TRUE causes loopback reception to occur while a
value of FALSE prohibits this.
SIO_MULTICAST_SCOPE (opcode setting: I, T==1) - Specifies the scope over
which multicast transmissions will occur. Scope is defined as the number of
routed network segments to be covered. A scope of zero would indicate that
the multicast transmission would not be placed "on the wire" but could be
disseminated across sockets within the local host. A scope value of one
(the default) indicates that the transmission will be placed on the wire,
but will not cross any routers. Higher scope values determine the number of
routers that may be crossed. Note that this corresponds to the time-to-live
(TTL) parameter in IP multicasting.
SIO_SET_QOS (opcode setting: I, T==1) - Associate the supplied QOS
structure with the socket. No output buffer is required, the QOS structure
will be obtained from the input buffer. The WSAENOPROTOOPT error code is
indicated for service providers which do not support QOS.
SIO_SET_GROUP_QOS (opcode setting: I, T==1) - Establish the supplied QOS
structure with the socket group to which this socket belongs. No output
buffer is required, the QOS structure will be obtained from the input
buffer. The WSAENOPROTOOPT error code is indicated for service providers
which do not support QOS, or if the socket descriptor specified is not the
creator of the associated socket group.
SIO_TRANSLATE_HANDLE (opcode setting: I, O, T==1) - To obtain a
corresponding handle for socket s that is valid in the context of a
companion interface (e.g., TH_NETDEV, TH_TAPI, etc.). A manifest constant
identifying the companion interface along with any other needed parameters
are specified in the input buffer. The corresponding handle will be
available in the output buffer upon completion of this routine. Refer to
the appropriate section in the Windows Sockets 2 Protocol-Specific Annex
and/or documentation for the particular companion interface for additional
details. The WSAENOPROTOOPT error code is indicated for service providers
which do not support this ioctl for the specified companion interface.
If an overlapped operation completes immediately, WSPIoctl() returns a
value of zero and the lpNumberOfBytesReturned parameter is updated with
the number of bytes returned. If the overlapped operation is successfully
initiated and will complete later, WSPIoctl() returns SOCKET_ERROR and
indicates error code WSA_IO_PENDING. In this case, lpNumberOfBytesReturned
is not updated. When the overlapped operation completes the amount of data
transferred is indicated either via the cbTransferred parameter in the
completion routine (if specified), or via the lpcbTransfer parameter in
WSPGetOverlappedResult().
Providers must allow this routine to be called from within the completion
routine of a previous WSPRecv(), WSPRecvFrom(), WSPSend() or WSPSendTo()
function. However, for a given socket, I/O completion routines may not be
nested. This permits time-sensitive data transmissions to occur entirely
within a preemptive context.
The lpOverlapped parameter must be valid for the duration of the
overlapped operation. If multiple I/O operations are simultaneously
outstanding, each must reference a separate overlapped structure. The
WSAOVERLAPPED structure has the following form:
typedef struct _WSAOVERLAPPED {
DWORD Internal; // reserved
DWORD InternalHigh; // reserved
DWORD Offset; // reserved
DWORD OffsetHigh; // reserved
WSAEVENT hEvent;
} WSAOVERLAPPED, FAR * LPWSAOVERLAPPED;
If the lpCompletionRoutine parameter is NULL, the service provider signals
the hEvent field of lpOverlapped when the overlapped operation completes
if it contains a valid event object handle. The WinSock SPI client can use
WSPGetOverlappedResult() to wait or poll on the event object.
If lpCompletionRoutine is not NULL, the hEvent field is ignored and can be
used by the WinSock SPI client to pass context information to the
completion routine. It is the service provider's responsibility to arrange
for invocation of the client-specified completion routine when the
overlapped operation completes. Since the completion routine must be
executed in the context of the same thread that initiated the overlapped
operation, it cannot be invoked directly from the service provider. The
WinSock DLL offers an asynchronous procedure call (APC) mechanism to
facilitate invocation of completion routines.
A service provider arranges for a function to be executed in the proper
thread by calling WPUQueueApc(). Note that this routine must be invoked
while in the context of the same process (but not necessarily the same
thread) that was used to initiate the overlapped operation. It is the
service provider's responsibility to arrange for this process context to
be active prior to calling WPUQueueApc().
WPUQueueApc() takes as input parameters a pointer to a WSATHREADID
structure (supplied to the provider via the lpThreadId input parameter),
a pointer to an APC function to be invoked, and a 32 bit context value
that is subsequently passed to the APC function. Because only a single
32-bit context value is available, the APC function cannot itself be the
client-specified completion routine. The service provider must instead
supply a pointer to its own APC function which uses the supplied context
value to access the needed result information for the overlapped operation,
and then invokes the client-specified completion routine.
The prototype for the client-supplied completion routine is as follows:
void
CALLBACK
CompletionRoutine(
IN DWORD dwError,
IN DWORD cbTransferred,
IN LPWSAOVERLAPPED lpOverlapped,
IN DWORD dwFlags
);
CompletionRoutine is a placeholder for a client supplied function
name.
dwError specifies the completion status for the overlapped
operation as indicated by lpOverlapped.
cbTransferred specifies the number of bytes sent.
No flag values are currently defined and the dwFlags value will
be zero.
This routine does not return a value.
The completion routines may be called in any order, not necessarily in
the same order the overlapped operations are completed. However, the
service provider guarantees to the client that posted buffers are sent
in the same order they are supplied.
The ioctl codes with T == 0 are a subset of the ioctl codes used in
Berkeley sockets. In particular, there is no command which is equivalent
to FIOASYNC.
Arguments:
s - Handle to a socket
dwIoControlCode - Control code of operation to perform
lpvInBuffer - Address of input buffer
cbInBuffer - Size of input buffer
lpvOutBuffer - Address of output buffer
cbOutBuffer - Size of output buffer
lpcbBytesReturned - A pointer to the size of output buffer's contents.
lpOverlapped - Address of WSAOVERLAPPED structure
lpCompletionRoutine - A pointer to the completion routine called when
the operation has been completed.
lpThreadId - A pointer to a thread ID structure to be used by the
provider in a subsequent call to WPUQueueApc(). The provider
should store the referenced WSATHREADID structure (not the pointer
to same) until after the WPUQueueApc() function returns.
lpErrno - A pointer to the error code.
Return Value:
If no error occurs and the operation has completed immediately, WSPIoctl()
returns 0. Note that in this case the completion routine, if specified,
will have already been queued. Otherwise, a value of SOCKET_ERROR is
returned, and a specific error code is available in lpErrno. The error
code WSA_IO_PENDING indicates that an overlapped operation has been
successfully initiated and that completion will be indicated at a later
time. Any other error code indicates that no overlapped operation was
initiated and no completion indication will occur.
--*/
{
int err;
PSOCKET_INFORMATION socket;
AFD_RECEIVE_INFORMATION receiveInformation;
BOOLEAN blocking;
u_long * inArgument = (u_long *)lpvInBuffer;
u_long * outArgument = (u_long *)lpvOutBuffer;
BOOL needsCompletion = TRUE;
WS_ENTER( "WSPIoctl", (PVOID)Handle, (PVOID)dwIoControlCode, lpvInBuffer, (PVOID)cbInBuffer );
WS_ASSERT( lpErrno != NULL );
err = SockEnterApi( TRUE, TRUE, FALSE );
if( err != NO_ERROR ) {
WS_EXIT( "WSPIoctl", SOCKET_ERROR, TRUE );
*lpErrno = err;
return SOCKET_ERROR;
}
if( lpcbBytesReturned == NULL ) {
WS_EXIT( "WSPIoctl", SOCKET_ERROR, TRUE );
*lpErrno = WSAEFAULT;
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 ) {
WS_EXIT( "WSPIoctl", SOCKET_ERROR, TRUE );
*lpErrno = WSAENOTSOCK;
return SOCKET_ERROR;
}
//
// Get exclusive access to the socket in question. This is necessary
// because we'll be changing the socket information data structure.
//
SockAcquireSocketLockExclusive( socket );
//
// Act based on the specified command.
//
switch ( dwIoControlCode ) {
case FIONBIO:
//
// Put the socket into or take it out of non-blocking mode.
//
if( cbInBuffer < sizeof(ULONG) || lpvInBuffer == NULL ) {
err = WSAEFAULT;
goto exit;
}
*lpcbBytesReturned = 0;
if ( *inArgument != 0 ) {
blocking = TRUE;
err = SockSetInformation(
socket,
AFD_NONBLOCKING_MODE,
&blocking,
NULL,
NULL
);
if ( err == NO_ERROR ) {
socket->NonBlocking = TRUE;
}
} else {
//
// It is illegal to set a socket to blocking if there are
// WSAAsyncSelect() or WSAEventSelect() events on the socket.
//
if ( socket->AsyncSelectlEvent != 0 ||
socket->EventSelectlNetworkEvents != 0 ) {
err = WSAEINVAL;
goto exit;
}
blocking = FALSE;
err = SockSetInformation(
socket,
AFD_NONBLOCKING_MODE,
&blocking,
NULL,
NULL
);
if ( err == NO_ERROR ) {
socket->NonBlocking = FALSE;
}
}
//
// If setting the socket blocking mode succeeded, remember the
// changed state of this socket.
//
if ( err == NO_ERROR ) {
err = SockSetHandleContext( socket );
if ( err != NO_ERROR ) {
goto exit;
}
}
break;
case FIONREAD:
//
// Return the number of bytes that can be read from the socket
// without blocking.
//
if( cbOutBuffer < sizeof(ULONG) || lpvOutBuffer == NULL ) {
err = WSAEFAULT;
goto exit;
}
err = GetReceiveInformation( socket, &receiveInformation );
if ( err != NO_ERROR ) {
goto exit;
}
*lpcbBytesReturned = sizeof(ULONG);
//
// If this socket is set for reading out-of-band data inline,
// include the number of expedited bytes available in the result.
// If the socket is not set for SO_OOBINLINE, just return the
// number of normal bytes available.
//
if ( socket->OobInline ) {
*outArgument = receiveInformation.BytesAvailable +
receiveInformation.ExpeditedBytesAvailable;
} else {
*outArgument = receiveInformation.BytesAvailable;
}
//
// If there are more bytes available than the size of the socket's
// buffer, truncate to the buffer size.
//
if ( *outArgument > socket->ReceiveBufferSize ) {
*outArgument = socket->ReceiveBufferSize;
}
break;
case SIOCATMARK: {
//
// If this is a datagram socket, fail the request.
//
if( cbOutBuffer < sizeof(ULONG) || lpvOutBuffer == NULL ) {
err = WSAEFAULT;
goto exit;
}
if ( IS_DGRAM_SOCK(socket->SocketType) ) {
err = WSAEINVAL;
goto exit;
}
//
// Return a BOOL that indicates whether there is expedited data
// to be read on the socket.
//
err = GetReceiveInformation( socket, &receiveInformation );
if ( err != NO_ERROR ) {
goto exit;
}
*lpcbBytesReturned = sizeof(ULONG);
if ( receiveInformation.ExpeditedBytesAvailable != 0 ) {
*outArgument = FALSE;
} else {
*outArgument = TRUE;
}
}
break;
case SIO_GET_QOS :
case SIO_GET_GROUP_QOS : {
NTSTATUS status;
AFD_QOS_INFO qosInfo;
IO_STATUS_BLOCK ioStatusBlock;
if( cbOutBuffer < sizeof(QOS) || lpvOutBuffer == NULL ) {
err = WSAEFAULT;
goto exit;
}
//
// Get it from AFD.
//
qosInfo.GroupQos = ( dwIoControlCode == SIO_GET_GROUP_QOS );
status = NtDeviceIoControlFile(
(HANDLE)Handle,
SockThreadEvent,
NULL,
NULL,
&ioStatusBlock,
IOCTL_AFD_GET_QOS,
&qosInfo,
sizeof(qosInfo),
&qosInfo,
sizeof(qosInfo)
);
if( status == STATUS_PENDING ) {
BOOLEAN success;
success = SockWaitForSingleObject(
SockThreadEvent,
Handle,
SOCK_NEVER_CALL_BLOCKING_HOOK,
SOCK_NO_TIMEOUT
);
status = ioStatusBlock.Status;
}
if( !NT_SUCCESS(status) ) {
err = SockNtStatusToSocketError( status );
goto exit;
}
//
// Success!
//
RtlCopyMemory(
lpvOutBuffer,
&qosInfo.Qos,
sizeof(QOS)
);
*lpcbBytesReturned = sizeof(QOS);
}
break;
case SIO_SET_QOS :
case SIO_SET_GROUP_QOS : {
NTSTATUS status;
AFD_QOS_INFO qosInfo;
IO_STATUS_BLOCK ioStatusBlock;
if( cbInBuffer < sizeof(QOS) || lpvInBuffer == NULL ) {
err = WSAEFAULT;
goto exit;
}
//
// Give it to AFD.
//
RtlCopyMemory(
&qosInfo.Qos,
lpvInBuffer,
sizeof(QOS)
);
qosInfo.GroupQos = ( dwIoControlCode == SIO_SET_GROUP_QOS );
status = NtDeviceIoControlFile(
(HANDLE)Handle,
SockThreadEvent,
NULL,
NULL,
&ioStatusBlock,
IOCTL_AFD_SET_QOS,
&qosInfo,
sizeof(qosInfo),
NULL,
0
);
if( status == STATUS_PENDING ) {
BOOLEAN success;
success = SockWaitForSingleObject(
SockThreadEvent,
Handle,
SOCK_NEVER_CALL_BLOCKING_HOOK,
SOCK_NO_TIMEOUT
);
status = ioStatusBlock.Status;
}
if( !NT_SUCCESS(status) ) {
err = SockNtStatusToSocketError( status );
goto exit;
}
//
// Success!
//
*lpcbBytesReturned = 0;
}
break;
case SIO_ENABLE_CIRCULAR_QUEUEING : {
BOOLEAN enable = TRUE;
if( !IS_DGRAM_SOCK( socket->SocketType ) ) {
err = WSAEINVAL;
goto exit;
}
err = SockSetInformation(
socket,
AFD_CIRCULAR_QUEUEING,
&enable,
NULL,
NULL
);
}
break;
case SIO_GET_BROADCAST_ADDRESS : {
PWSH_GET_BROADCAST_SOCKADDR getBroadcastSockaddr;
INT sockaddrLength;
if( !IS_DGRAM_SOCK( socket->SocketType ) ) {
err = WSAEINVAL;
goto exit;
}
getBroadcastSockaddr = socket->HelperDll->WSHGetBroadcastSockaddr;
if( getBroadcastSockaddr == NULL ) {
err = WSAENOPROTOOPT;
goto exit;
}
sockaddrLength = (INT)cbOutBuffer;
err = (getBroadcastSockaddr)(
socket->HelperDllContext,
(PSOCKADDR)lpvOutBuffer,
&sockaddrLength
);
if( err == NO_ERROR ) {
*lpcbBytesReturned = (DWORD)sockaddrLength;
}
}
break;
case SIO_ASSOCIATE_HANDLE :
case SIO_TRANSLATE_HANDLE :
err = WSAENOPROTOOPT;
break;
case SIO_GET_EXTENSION_FUNCTION_POINTER : {
LPVOID * outBuffer;
INT i;
if( cbInBuffer < sizeof(GUID) || lpvInBuffer == NULL ||
cbOutBuffer < sizeof(LPVOID) || lpvOutBuffer == NULL ) {
err = WSAEFAULT;
goto exit;
}
outBuffer = (LPVOID *)lpvOutBuffer;
for( i = 0 ; i < NUM_EXTENSIONS ; i++ ) {
if( RtlEqualMemory(
lpvInBuffer,
&SockExtensions[i].Guid,
sizeof(GUID)
) ) {
*outBuffer = SockExtensions[i].Function;
*lpcbBytesReturned = sizeof(LPVOID);
goto exit;
}
}
}
//
// Fall through to default in case the helper DLL can support
// this extension.
//
default: {
PWSH_IOCTL wshIoctl;
//
// Unknown IOCTL. If this socket's helper DLL supports the
// WSHIoctl entrypoint, just pass the request off to it.
// Otherwise, fail it here.
//
wshIoctl = socket->HelperDll->WSHIoctl;
if( wshIoctl == NULL ) {
err = WSAEINVAL;
goto exit;
}
err = (wshIoctl)(
socket->HelperDllContext,
Handle,
socket->TdiAddressHandle,
socket->TdiConnectionHandle,
dwIoControlCode,
lpvInBuffer,
cbInBuffer,
lpvOutBuffer,
cbOutBuffer,
lpcbBytesReturned,
lpOverlapped,
lpCompletionRoutine,
&needsCompletion
);
if( err != NO_ERROR &&
dwIoControlCode == SIO_GET_EXTENSION_FUNCTION_POINTER ) {
//
// Ensure the proper error is returned.
//
err = WSAEINVAL;
}
}
break;
}
exit:
IF_DEBUG(SOCKOPT) {
if ( err != NO_ERROR ) {
WS_PRINT(( "WSPIoctl on socket %lx, command %lx failed: %ld\n",
Handle, dwIoControlCode, err ));
} else {
WS_PRINT(( "WSPIoctl on socket %lx command %lx returning arg "
"%ld\n", Handle, dwIoControlCode, *outArgument ));
}
}
//
// If this is an overlapped request, then send a "NOP" IOCTL to
// AFD to do all of the correct IO completion stuff. This is necessary
// as we do not yet (really) support overlapped WSPIoctl(), but the
// caller will be expecting the call to Do The Right Thing regarding
// completion routines, event object, and IO completion ports.
//
if( lpOverlapped != NULL &&
err == NO_ERROR &&
( socket->CreationFlags & WSA_FLAG_OVERLAPPED ) != 0 &&
needsCompletion ) {
NTSTATUS status;
PIO_STATUS_BLOCK ioStatusBlock;
HANDLE event;
PIO_APC_ROUTINE apcRoutine;
PVOID apcContext;
if( lpCompletionRoutine == NULL ) {
//
// No APC, use event object from OVERLAPPED structure.
//
event = lpOverlapped->hEvent;
apcRoutine = NULL;
apcContext = ( (DWORD)event & 1 ) ? NULL : lpOverlapped;
} else {
//
// APC, ignore event object.
//
event = NULL;
apcRoutine = SockIoCompletion;
apcContext = lpCompletionRoutine;
}
//
// Use part of the OVERLAPPED structure as our IO_STATUS_BLOCK.
//
ioStatusBlock = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
//
// Issue the request.
//
ioStatusBlock->Status = STATUS_PENDING;
status = NtDeviceIoControlFile(
(HANDLE)Handle,
event,
apcRoutine,
apcContext,
ioStatusBlock,
IOCTL_AFD_NO_OPERATION,
NULL,
0,
NULL,
0
);
WS_ASSERT( NT_SUCCESS(status) );
}
if ( socket != NULL ) {
SockReleaseSocketLock( socket );
SockDereferenceSocket( socket );
}
if ( err != NO_ERROR ) {
WS_EXIT( "WSPIoctl", SOCKET_ERROR, TRUE );
*lpErrno = err;
return SOCKET_ERROR;
}
WS_EXIT( "WSPIoctl", NO_ERROR, FALSE );
return NO_ERROR;
} // WSPIoctl
int
WSPAPI
WSPSetSockOpt (
IN SOCKET Handle,
IN int Level,
IN int OptionName,
IN const char *OptionValue,
IN int OptionLength,
OUT LPINT lpErrno
)
/*++
Routine Description:
This routine sets the current value for a socket option associated with a
socket of any type, in any state. Although options may exist at multiple
protocol levels, they are always present at the uppermost "socket' level.
Options affect socket operations, such as whether broadcast messages may
be sent on the socket, etc.
There are two types of socket options: Boolean options that enable or
disable a feature or behavior, and options which require an integer value
or structure. To enable a Boolean option, optval points to a nonzero
integer. To disable the option optval points to an integer equal to zero.
optlen should be equal to sizeof(int) for Boolean options. For other
options, optval points to the an integer or structure that contains the
desired value for the option, and optlen is the length of the integer or
structure.
Value Type Meaning
~~~~~ ~~~~ ~~~~~~~
SO_BROADCAST BOOL Allow transmission of broadcast
messages on the socket.
SO_DEBUG BOOL Record debugging information.
SO_DONTLINGER BOOL Don't block close waiting for
unsent data to be sent. Setting
this option is equivalent to
setting SO_LINGER with l_onoff set
to zero.
SO_DONTROUTE BOOL Don't route: send directly to
interface.
SO_GROUP_PRIORITY int Specify the relative priority to
be established for sockets that
are part of a socket group.
SO_KEEPALIVE BOOL Send keepalives.
SO_LINGER struct linger Linger on close if unsent data is
present.
SO_OOBINLINE BOOL Receive out-of-band data in the
normal data stream.
SO_RCVBUF int Specify buffer size for receives.
SO_REUSEADDR BOOL Allow the socket to be bound to an
address which is already in use.
(See WSPBind().)
SO_SNDBUF int Specify buffer size for sends.
PVD_CONFIG Service This object stores the
Provider configuration information for the
Dependent service provider associated with
socket s. The exact format of this
data structure is service provider
specific.
Calling WSPSetSockOpt() with an unsupported option will result in an error
code of WSAENOPROTOOPT being returned in lpErrno.
SO_DEBUG - WinSock service providers are encouraged (but not required) to
supply output debug information if the SO_DEBUG option is set by a WinSock
SPI client. The mechanism for generating the debug information and the form
it takes are beyond the scope of this specification.
SO_GROUP_PRIORITY - Group priority indicates the priority of the specified
socket relative to other sockets within the socket group. Values are non-
negative integers, with zero corresponding to the highest priority.
Priority values represent a hint to the service provider about how
potentially scarce resources should be allocated. For example, whenever
two or more sockets are both ready to transmit data, the highest priority
socket (lowest value for SO_GROUP_PRIORITY) should be serviced first, with
the remainder serviced in turn according to their relative priorities.
The WSAENOPROTOOPT error is indicated for non group sockets or for service
providers which do not support group sockets.
SO_KEEPALIVE - An WinSock SPI client may request that a TCP/IP provider
enable the use of "keep-alive" packets on TCP connections by turning on the
SO_KEEPALIVE socket option. A WinSock provider need not support the use of
keep-alives: if it does, the precise semantics are implementation-specific
but should conform to section 4.2.3.6 of RFC 1122: Requirements for
Internet Hosts -- Communication Layers. If a connection is dropped as the
result of "keep-alives" the error code WSAENETRESET is returned to any
calls in progress on the socket, and any subsequent calls will fail with
WSAENOTCONN.
SO_LINGER - SO_LINGER controls the action taken when unsent data is queued
on a socket and a WSPCloseSocket() is performed. See WSPCloseSocket() for a
description of the way in which the SO_LINGER settings affect the semantics
of WSPCloseSocket(). The WinSock SPI client sets the desired behavior by
creating a struct linger (pointed to by the optval argument) with the
following elements:
struct linger {
u_short l_onoff;
u_short l_linger;
};
To enable SO_LINGER, a WinSock SPI client should set l_onoff to a non-zero
value, set l_linger to 0 or the desired timeout (in seconds), and call
WSPSetSockOpt(). To enable SO_DONTLINGER (i.e. disable SO_LINGER) l_onoff
should be set to zero and WSPSetSockOpt() should be called. Note that
enabling SO_LINGER with a non-zero timeout on a non-blocking socket is not
recommended (see WSPCloseSocket() for details).
Enabling SO_LINGER also disables SO_DONTLINGER, and vice versa. Note that
if SO_DONTLINGER is DISABLED (i.e. SO_LINGER is ENABLED) then no timeout
value is specified. In this case the timeout used is implementation
dependent. If a previous timeout has been established for a socket (by
enabling SO_LINGER), then this timeout value should be reinstated by the
service provider.
SO_REUSEADDR - By default, a socket may not be bound (see WSPBind()) to a
local address which is already in use. On occasions, however, it may be
desirable to "re-use" an address in this way. Since every connection is
uniquely identified by the combination of local and remote addresses, there
is no problem with having two sockets bound to the same local address as
long as the remote addresses are different. To inform the WinSock provider
that a WSPBind() on a socket should be allowed to bind to a local address
that is already in use by another socket, the WinSock SPI client should set
the SO_REUSEADDR socket option for the socket before issuing the WSPBind().
Note that the option is interpreted only at the time of the WSPBind(): it
is therefore unnecessary (but harmless) to set the option on a socket which
is not to be bound to an existing address, and setting or resetting the
option after the WSPBind() has no effect on this or any other socket.
SO_RCVBUF & SO_SNDBUF - When a Windows Sockets implementation supports the
SO_RCVBUF and SO_SNDBUF options, a WinSock SPI client may request different
buffer sizes (larger or smaller). The call may succeed even though the
service provider did not make available the entire amount requested. A
WinSock SPI client must call WSPGetSockOpt() with the same option to check
the buffer size actually provided.
PVD_CONFIG - This object stores the configuration information for the
service provider associated with socket s. The exact format of this data
structure is service provider specific.
Arguments:
s - A descriptor identifying a socket.
level - The level at which the option is defined; the supported levels
include SOL_SOCKET.
optname - The socket option for which the value is to be set.
optval - A pointer to the buffer in which the value for the requested
option is supplied.
optlen - The size of the optval buffer.
lpErrno - A pointer to the error code.
Return Value:
If no error occurs, WSPSetSockOpt() returns 0. Otherwise, a value of
SOCKET_ERROR is returned, and a specific error code is available in
lpErrno.
--*/
{
int err;
PSOCKET_INFORMATION socket;
INT optionValue;
INT previousValue;
WS_ENTER( "WSPSetSockOpt", (PVOID)Handle, (PVOID)Level, (PVOID)OptionName, (PVOID)OptionValue );
WS_ASSERT( lpErrno != NULL );
err = SockEnterApi( TRUE, TRUE, FALSE );
if( err != NO_ERROR ) {
WS_EXIT( "WSPSetSockOpt", INVALID_SOCKET, TRUE );
*lpErrno = err;
return INVALID_SOCKET;
}
//
// 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 ) {
WS_EXIT( "WSPSetSockOpt", SOCKET_ERROR, TRUE );
*lpErrno = WSAENOTSOCK;
return SOCKET_ERROR;
}
//
// Get exclusive access to the socket in question. This is necessary
// because we'll be changing the socket information data structure.
//
SockAcquireSocketLockExclusive( socket );
//
// Ensure the socket isn't closing on us.
//
if( socket->State == SocketStateClosing ) {
err = WSAENOTSOCK;
goto exit;
}
//
// Make sure that the OptionValue argument is present.
//
if ( !ARGUMENT_PRESENT( OptionValue ) ) {
err = WSAEFAULT;
goto exit;
}
//
// OptionLength must be at least sizeof(int). For SO_LINGER it must
// be larger; there is a special test in the handling of that
// option.
//
if ( OptionLength < sizeof(int) ) {
err = WSAEFAULT;
goto exit;
}
//
// Make sure that this is a legitimate option for the socket.
//
if ( !IsValidOptionForSocket( socket, Level, OptionName ) ) {
err = WSAENOPROTOOPT;
goto exit;
}
optionValue = *(PINT)OptionValue;
//
// Act on the specified level.
//
switch ( Level ) {
case SOL_SOCKET:
//
// Act based on the option being set.
//
switch ( OptionName ) {
case SO_BROADCAST:
if ( optionValue == 0 ) {
socket->Broadcast = FALSE;
} else {
socket->Broadcast = TRUE;
}
break;
case SO_DEBUG:
if ( optionValue == 0 ) {
socket->Debug = FALSE;
} else {
socket->Debug = TRUE;
}
break;
case SO_DONTLINGER:
if ( optionValue == 0 ) {
socket->LingerInfo.l_onoff = 1;
} else {
socket->LingerInfo.l_onoff = 0;
}
break;
case SO_LINGER:
if ( OptionLength < sizeof(struct linger) ) {
err = WSAEFAULT;
goto exit;
}
RtlCopyMemory(
&socket->LingerInfo,
OptionValue,
sizeof(socket->LingerInfo)
);
break;
case SO_OOBINLINE: {
BOOLEAN inLine;
if ( optionValue == 0 ) {
inLine = FALSE;
} else {
inLine = TRUE;
}
err = SockSetInformation(
socket,
AFD_INLINE_MODE,
&inLine,
NULL,
NULL
);
if ( err != NO_ERROR ) {
goto exit;
}
socket->OobInline = inLine;
break;
}
case SO_RCVBUF:
previousValue = socket->ReceiveBufferSize;
socket->ReceiveBufferSize = optionValue;
err = SockUpdateWindowSizes( socket, TRUE );
if ( err != NO_ERROR ) {
socket->ReceiveBufferSize = previousValue;
goto exit;
}
break;
case SO_SNDBUF:
previousValue = socket->SendBufferSize;
socket->SendBufferSize = optionValue;
err = SockUpdateWindowSizes( socket, TRUE );
if ( err != NO_ERROR ) {
socket->SendBufferSize = previousValue;
goto exit;
}
break;
case SO_REUSEADDR:
if ( optionValue == 0 ) {
socket->ReuseAddresses = FALSE;
} else {
socket->ReuseAddresses = TRUE;
}
break;
case SO_CONNDATA:
err = SockPassConnectData(
socket,
IOCTL_AFD_SET_CONNECT_DATA,
(PVOID)OptionValue,
OptionLength,
NULL
);
goto exit;
case SO_CONNOPT:
err = SockPassConnectData(
socket,
IOCTL_AFD_SET_CONNECT_OPTIONS,
(PCHAR)OptionValue,
OptionLength,
NULL
);
goto exit;
case SO_DISCDATA:
err = SockPassConnectData(
socket,
IOCTL_AFD_SET_DISCONNECT_DATA,
(PCHAR)OptionValue,
OptionLength,
NULL
);
goto exit;
case SO_DISCOPT:
err = SockPassConnectData(
socket,
IOCTL_AFD_SET_DISCONNECT_OPTIONS,
(PCHAR)OptionValue,
OptionLength,
NULL
);
goto exit;
case SO_CONNDATALEN:
err = SockPassConnectData(
socket,
IOCTL_AFD_SIZE_CONNECT_DATA,
(PCHAR)OptionValue,
OptionLength,
NULL
);
goto exit;
case SO_CONNOPTLEN:
err = SockPassConnectData(
socket,
IOCTL_AFD_SIZE_CONNECT_OPTIONS,
(PCHAR)OptionValue,
OptionLength,
NULL
);
goto exit;
case SO_DISCDATALEN:
err = SockPassConnectData(
socket,
IOCTL_AFD_SIZE_DISCONNECT_DATA,
(PCHAR)OptionValue,
OptionLength,
NULL
);
goto exit;
case SO_DISCOPTLEN:
err = SockPassConnectData(
socket,
IOCTL_AFD_SIZE_DISCONNECT_OPTIONS,
(PCHAR)OptionValue,
OptionLength,
NULL
);
goto exit;
case SO_SNDTIMEO:
socket->SendTimeout = optionValue;
goto exit;
case SO_RCVTIMEO:
socket->ReceiveTimeout = optionValue;
goto exit;
case SO_ERROR:
socket->LastError = optionValue;
goto exit;
case SO_UPDATE_ACCEPT_CONTEXT: {
PSOCKET_INFORMATION listenSocket;
listenSocket = SockFindAndReferenceSocket( (SOCKET)optionValue, FALSE );
if ( listenSocket == NULL ) {
err = WSAENOTSOCK;
goto exit;
}
//
// Get exclusive access to the listening socket.
//
SockAcquireSocketLockExclusive( listenSocket );
//
// Ensure the socket isn't closing on us.
//
if( listenSocket->State == SocketStateClosing ) {
err = WSAENOTSOCK;
SockReleaseSocketLock( listenSocket );
goto exit;
}
//
// First free any old, leftover information.
//
if ( socket->TdiAddressHandle != NULL ) {
NtClose( socket->TdiAddressHandle );
socket->TdiAddressHandle = NULL;
}
if ( socket->TdiConnectionHandle != NULL ) {
NtClose( socket->TdiConnectionHandle );
socket->TdiConnectionHandle = NULL;
}
err = SockCoreAccept( listenSocket, socket );
SockReleaseSocketLock( listenSocket );
SockDereferenceSocket( listenSocket );
goto exit;
}
case 0x8000:
//
// This is the "special" allow-us-to-bind-to-the-zero-address
// hack. Put special stuff in the sin_zero part of the address
// to tell UDP that 0.0.0.0 means bind to that address, rather
// than wildcard.
//
// This feature is needed to allow DHCP to actually bind to
// the zero address.
//
if ( *(PUSHORT)OptionValue == 1234 ) {
socket->DontUseWildcard = TRUE;
} else {
err = WSAENOPROTOOPT;
}
break;
case SO_GROUP_PRIORITY:
socket->GroupPriority = optionValue;
goto exit;
case SO_DONTROUTE:
case SO_ACCEPTCONN:
case SO_KEEPALIVE:
case SO_RCVLOWAT:
case SO_SNDLOWAT:
case SO_TYPE:
default:
//
// The specified option isn't supported here in the winsock DLL.
// Give the request to the helper DLL.
//
err = SockGetTdiHandles( socket );
if ( err != NO_ERROR ) {
goto exit;
}
err = socket->HelperDll->WSHSetSocketInformation(
socket->HelperDllContext,
Handle,
socket->TdiAddressHandle,
socket->TdiConnectionHandle,
Level,
OptionName,
(PCHAR)OptionValue,
OptionLength
);
if ( err != NO_ERROR ) {
goto exit;
}
}
break;
default:
//
// The specified level isn't supported here in the winsock DLL.
// Give the request to the helper DLL.
//
err = SockGetTdiHandles( socket );
if ( err != NO_ERROR ) {
goto exit;
}
err = socket->HelperDll->WSHSetSocketInformation(
socket->HelperDllContext,
Handle,
socket->TdiAddressHandle,
socket->TdiConnectionHandle,
Level,
OptionName,
(PCHAR)OptionValue,
OptionLength
);
if ( err != NO_ERROR ) {
goto exit;
}
}
exit:
//
// Release the resource, dereference the socket, set the error if
// any, and return.
//
IF_DEBUG(SOCKOPT) {
if ( err != NO_ERROR ) {
WS_PRINT(( "WSPSetSockOpt on socket %lx (%lx) failed: %ld.\n",
Handle, socket, err ));
} else {
WS_PRINT(( "WSPSetSockOpt on socket %lx (%lx) succeeded.\n",
Handle, socket ));
}
}
if ( err != NO_ERROR ) {
if ( socket != NULL ) {
SockReleaseSocketLock( socket );
SockDereferenceSocket( socket );
}
WS_EXIT( "WSPSetSockOpt", SOCKET_ERROR, TRUE );
*lpErrno = err;
return SOCKET_ERROR;
}
//
// Remember the changed state of this socket.
//
if ( socket != NULL ) {
err = SockSetHandleContext( socket );
if ( err != NO_ERROR ) {
goto exit;
}
SockReleaseSocketLock( socket );
SockDereferenceSocket( socket );
}
WS_EXIT( "WSPSetSockOpt", NO_ERROR, FALSE );
return NO_ERROR;
} // WSPSetSockOpt
int
GetReceiveInformation (
IN PSOCKET_INFORMATION Socket,
OUT PAFD_RECEIVE_INFORMATION ReceiveInformation
)
{
NTSTATUS status;
IO_STATUS_BLOCK ioStatusBlock;
//
// Get data about the bytes available to be read on the socket.
//
status = NtDeviceIoControlFile(
(HANDLE)Socket->Handle,
SockThreadEvent,
NULL, // APC Routine
NULL, // APC Context
&ioStatusBlock,
IOCTL_AFD_QUERY_RECEIVE_INFO,
NULL, // InputBuffer
0L, // InputBufferLength
ReceiveInformation,
sizeof(*ReceiveInformation)
);
if ( status == STATUS_PENDING ) {
SockWaitForSingleObject(
SockThreadEvent,
Socket->Handle,
SOCK_NEVER_CALL_BLOCKING_HOOK,
SOCK_NO_TIMEOUT
);
status = ioStatusBlock.Status;
}
if ( !NT_SUCCESS(status) ) {
return SockNtStatusToSocketError( status );
}
return NO_ERROR;
} // GetReceiveInformation
BOOLEAN
IsValidOptionForSocket (
IN PSOCKET_INFORMATION Socket,
IN int Level,
IN int OptionName
)
{
//
// All levels other than SOL_SOCKET and SOL_INTERNAL could be legal.
// SOL_INTERNAL is never legal; it is used only for internal
// communication between the Windows Sockets DLL and helper
// DLLs.
//
if ( Level == SOL_INTERNAL ) {
return FALSE;
}
if ( Level != SOL_SOCKET ) {
return TRUE;
}
//
// Based on the option, determine whether it is possibly legal for
// the socket. For unknown options, assume that the helper DLL will
// take care of it and return that it is a legal option.
//
switch ( OptionName ) {
case SO_DONTLINGER:
case SO_KEEPALIVE:
case SO_LINGER:
case SO_OOBINLINE:
case SO_ACCEPTCONN:
//
// These options are only legal on VC sockets--if this is a
// datagram socket, fail.
//
if ( IS_DGRAM_SOCK(Socket->SocketType) ) {
return FALSE;
}
return TRUE;
case SO_BROADCAST:
//
// These options are only valid on datagram sockets--if this is
// a VC socket, fail.
//
if (!IS_DGRAM_SOCK(Socket->SocketType) ) {
return FALSE;
}
return TRUE;
case SO_PROTOCOL_INFOA:
//
// We should never see this one; it gets mapped at the main
// WinSock 2 DLL level.
//
return FALSE;
case SO_DEBUG:
case SO_DONTROUTE:
case SO_ERROR:
case SO_RCVBUF:
case SO_REUSEADDR:
case SO_SNDBUF:
case SO_TYPE:
case SO_PROTOCOL_INFOW:
case SO_GROUP_ID:
case SO_GROUP_PRIORITY:
default:
//
// These options are legal on any socket. Succeed.
//
return TRUE;
}
} // IsValidOptionForSocket
INT
SockUpdateWindowSizes (
IN PSOCKET_INFORMATION Socket,
IN BOOLEAN AlwaysUpdate
)
{
INT error;
//
// If this is an unbound datagram socket or an unconnected VC
// socket, don't do anything here.
//
if ( ( IS_DGRAM_SOCK(Socket->SocketType) &&
Socket->State == SocketStateOpen
)
||
( !IS_DGRAM_SOCK(Socket->SocketType) &&
Socket->State != SocketStateConnected
)
) {
return NO_ERROR;
}
//
// Give the helper DLL the receive buffer size so it can tell the
// transport the window size to advertize.
//
if ( !IS_DGRAM_SOCK(Socket->SocketType) ) {
error = SockGetTdiHandles( Socket );
if ( error != NO_ERROR ) {
return error;
}
error = Socket->HelperDll->WSHSetSocketInformation(
Socket->HelperDllContext,
Socket->Handle,
Socket->TdiAddressHandle,
Socket->TdiConnectionHandle,
SOL_SOCKET,
SO_RCVBUF,
(PCHAR)&Socket->ReceiveBufferSize,
sizeof(Socket->ReceiveBufferSize)
);
}
//
// If the receive buffer changed or if we should always update the
// count in AFD, then give the receive info to AFD.
//
if ( Socket->ReceiveBufferSize != SockReceiveBufferWindow || AlwaysUpdate ) {
error = SockSetInformation(
Socket,
AFD_RECEIVE_WINDOW_SIZE,
NULL,
&Socket->ReceiveBufferSize,
NULL
);
if ( error != NO_ERROR ) {
return error;
}
}
//
// Repeat for the send buffer.
//
if ( Socket->SendBufferSize != SockSendBufferWindow || AlwaysUpdate ) {
error = SockSetInformation(
Socket,
AFD_SEND_WINDOW_SIZE,
NULL,
&Socket->SendBufferSize,
NULL
);
if ( error != NO_ERROR ) {
return error;
}
}
return NO_ERROR;
} // SockSetSocketReceiveBuffer
INT
SockPassConnectData (
IN PSOCKET_INFORMATION Socket,
IN ULONG AfdIoctl,
IN PCHAR Buffer,
IN INT BufferLength,
OUT PINT OutBufferLength
)
{
NTSTATUS status;
IO_STATUS_BLOCK ioStatusBlock;
//
// Give request to AFD.
//
status = NtDeviceIoControlFile(
(HANDLE)Socket->Handle,
SockThreadEvent,
NULL,
0,
&ioStatusBlock,
AfdIoctl,
Buffer,
BufferLength,
Buffer,
BufferLength
);
//
// If the call pended and we were supposed to wait for completion,
// then wait.
//
if ( status == STATUS_PENDING ) {
SockWaitForSingleObject(
SockThreadEvent,
Socket->Handle,
SOCK_NEVER_CALL_BLOCKING_HOOK,
SOCK_NO_TIMEOUT
);
status = ioStatusBlock.Status;
}
if ( !NT_SUCCESS(status) ) {
return SockNtStatusToSocketError( status );
}
//
// If requested, set up the length of returned buffer.
//
if ( ARGUMENT_PRESENT(OutBufferLength) ) {
*OutBufferLength = ioStatusBlock.Information;
}
return NO_ERROR;
} // SockPassConnectData