599 lines
16 KiB
C
599 lines
16 KiB
C
|
/*++
|
|||
|
|
|||
|
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:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#define WINSOCK_API_LINKAGE
|
|||
|
#define getsockopt getsockopt_v11
|
|||
|
#define setsockopt setsockopt_v11
|
|||
|
|
|||
|
#include <windows.h>
|
|||
|
#include <winsock2.h>
|
|||
|
#include <ws2tcpip.h>
|
|||
|
|
|||
|
//
|
|||
|
// The versions of WSOCK32.DLL that shiped with NT 3.1, NT 3.5, NT 3.51,
|
|||
|
// TCP/IP-32 for WFW, and Win95 all use the "Steve Deering" values for the
|
|||
|
// IP Multicast options. Unfortunately, the TCP/IP subgroup of the Windows
|
|||
|
// Sockets 2.0 standards effort chose to use the BSD values for these options.
|
|||
|
// Since these values overlap considerably, we have a rather unfortunate
|
|||
|
// situation.
|
|||
|
//
|
|||
|
// Here's how we'll deal with this.
|
|||
|
//
|
|||
|
// Applications built using WINSOCK2.H & WS2TCPIP.H will use the BSD
|
|||
|
// values as #defined in WS2TCPIP.H. These applications will link with
|
|||
|
// WS2_32.DLL, and life is swell.
|
|||
|
//
|
|||
|
// Applications built using WINSOCK.H will use the Steve Deering values
|
|||
|
// as #defined in WINSOCK.H. These applications will link with WSOCK32.DLL,
|
|||
|
// which will map these options to the BSD values before passing them
|
|||
|
// down to WS2_32.DLL. Life is still swell.
|
|||
|
//
|
|||
|
// These are the "old" Steve Deering values that must be mapped:
|
|||
|
//
|
|||
|
|
|||
|
#define OLD_IP_MULTICAST_IF 2
|
|||
|
#define OLD_IP_MULTICAST_TTL 3
|
|||
|
#define OLD_IP_MULTICAST_LOOP 4
|
|||
|
#define OLD_IP_ADD_MEMBERSHIP 5
|
|||
|
#define OLD_IP_DROP_MEMBERSHIP 6
|
|||
|
#define OLD_IP_TTL 7
|
|||
|
#define OLD_IP_TOS 8
|
|||
|
#define OLD_IP_DONTFRAGMENT 9
|
|||
|
|
|||
|
#define TCP_BSDURGENT 0x7000
|
|||
|
|
|||
|
INT
|
|||
|
MapOldIpMulticastOptionToBsdValue(
|
|||
|
INT OptionName
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
int PASCAL
|
|||
|
getsockopt(
|
|||
|
IN SOCKET Handle,
|
|||
|
IN int Level,
|
|||
|
IN int OptionName,
|
|||
|
char *OptionValue,
|
|||
|
int *OptionLength
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
getsockopt() 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 whether an operation
|
|||
|
blocks or not, 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 all other options it will be the size of an
|
|||
|
integer.
|
|||
|
|
|||
|
If the option was never set with setsockopt(), then getsockopt()
|
|||
|
returns the default value for the option.
|
|||
|
|
|||
|
The following options are supported for
|
|||
|
getsockopt(). The Type identifies the type of
|
|||
|
data addressed by optval.
|
|||
|
|
|||
|
Value Type Meaning
|
|||
|
|
|||
|
SO_ACCEPTCONN BOOL Socket is listen()ing.
|
|||
|
|
|||
|
SO_BROADCAST BOOL Socket is configured for the transmission
|
|||
|
of broadcast messages.
|
|||
|
|
|||
|
SO_DEBUG BOOL Debugging is enabled.
|
|||
|
|
|||
|
SO_DONTLINGER BOOL If true, the SO_LINGER option is disabled.
|
|||
|
|
|||
|
SO_DONTROUTE BOOL Routing is disabled.
|
|||
|
|
|||
|
SO_ERROR int Retrieve error status and clear.
|
|||
|
|
|||
|
SO_KEEPALIVE BOOL Keepalives are being sent.
|
|||
|
|
|||
|
SO_LINGER struct Returns the current linger
|
|||
|
linger options.
|
|||
|
FAR *
|
|||
|
|
|||
|
SO_OOBINLINE BOOL Out-of-band data is being received in the
|
|||
|
normal data stream.
|
|||
|
|
|||
|
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).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
s - A descriptor identifying a socket.
|
|||
|
|
|||
|
level - The level at which the option is defined; the only supported
|
|||
|
level is 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.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
If no error occurs, getsockopt() returns 0. Otherwise, a value of
|
|||
|
SOCKET_ERROR is returned, and a specific error code may be retrieved
|
|||
|
by calling WSAGetLastError().
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG error;
|
|||
|
#undef getsockopt
|
|||
|
extern int WSAAPI getsockopt( SOCKET s, int level, int optname,
|
|||
|
char FAR * optval, int FAR * optlen );
|
|||
|
|
|||
|
//
|
|||
|
// Set up locals so that we know how to clean up on exit.
|
|||
|
//
|
|||
|
|
|||
|
error = NO_ERROR;
|
|||
|
|
|||
|
//
|
|||
|
// Map the old IP multicast values to their BSD equivilants.
|
|||
|
//
|
|||
|
|
|||
|
if( Level == IPPROTO_IP ) {
|
|||
|
|
|||
|
OptionName = MapOldIpMulticastOptionToBsdValue( OptionName );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Handle TCP_BSDURGENT specially.
|
|||
|
//
|
|||
|
|
|||
|
if( Level == IPPROTO_TCP && OptionName == TCP_BSDURGENT ) {
|
|||
|
|
|||
|
if( getsockopt(
|
|||
|
Handle,
|
|||
|
Level,
|
|||
|
TCP_EXPEDITED_1122,
|
|||
|
OptionValue,
|
|||
|
OptionLength
|
|||
|
) == SOCKET_ERROR ) {
|
|||
|
|
|||
|
return SOCKET_ERROR;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// TCP_BSDURGENT is the inverse of TCP_EXPEDITED_1122.
|
|||
|
//
|
|||
|
|
|||
|
*OptionValue = !(*OptionValue);
|
|||
|
goto exit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Forward it to the "real" WS2_32.DLL.
|
|||
|
//
|
|||
|
|
|||
|
if( getsockopt(
|
|||
|
Handle,
|
|||
|
Level,
|
|||
|
OptionName,
|
|||
|
OptionValue,
|
|||
|
OptionLength
|
|||
|
) == SOCKET_ERROR ) {
|
|||
|
|
|||
|
return SOCKET_ERROR;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
exit:
|
|||
|
|
|||
|
if ( error != NO_ERROR ) {
|
|||
|
SetLastError( error );
|
|||
|
return SOCKET_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
return NO_ERROR;
|
|||
|
|
|||
|
} // getsockopt
|
|||
|
|
|||
|
|
|||
|
int PASCAL
|
|||
|
setsockopt(
|
|||
|
IN SOCKET Handle,
|
|||
|
IN int Level,
|
|||
|
IN int OptionName,
|
|||
|
IN const char *OptionValue,
|
|||
|
IN int OptionLength
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
setsockopt() 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, this specification only defines options
|
|||
|
that exist at the uppermost "socket'' level. Options affect socket
|
|||
|
operations, such as whether expedited data is received in the normal
|
|||
|
data stream, 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.
|
|||
|
|
|||
|
SO_LINGER controls the action taken when unsent data is queued on a
|
|||
|
socket and a closesocket() is performed. See closesocket() for a
|
|||
|
description of the way in which the SO_LINGER settings affect the
|
|||
|
semantics of closesocket(). The application sets the desired
|
|||
|
behavior by creating a struct linger (pointed to by the optval
|
|||
|
argument) with the following elements:
|
|||
|
|
|||
|
struct linger {
|
|||
|
int l_onoff;
|
|||
|
int l_linger;
|
|||
|
}
|
|||
|
|
|||
|
To enable SO_LINGER, the application should set l_onoff to a
|
|||
|
non-zero value, set l_linger to 0 or the desired timeout (in
|
|||
|
seconds), and call setsockopt(). To enable SO_DONTLINGER (i.e.
|
|||
|
disable SO_LINGER) l_onoff should be set to zero and setsockopt()
|
|||
|
should be called.
|
|||
|
|
|||
|
By default, a socket may not be bound (see bind()) 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 Windows Sockets implementation that a
|
|||
|
bind() on a socket should not be disallowed because of address
|
|||
|
re-use, the application should set the SO_REUSEADDR socket option
|
|||
|
for the socket before issuing the bind(). Note that the option is
|
|||
|
interpreted only at the time of the bind(): 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 bind() has no effect on this or any other socket..
|
|||
|
|
|||
|
An application may request that the Windows Sockets implementation
|
|||
|
enable the use of "keep- alive" packets on TCP connections by
|
|||
|
turning on the SO_KEEPALIVE socket option. A Windows Sockets
|
|||
|
implementation 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.
|
|||
|
|
|||
|
The following options are supported for setsockopt(). The Type
|
|||
|
identifies the type of data addressed by optval.
|
|||
|
|
|||
|
Value Type Meaning
|
|||
|
|
|||
|
SO_ACCEPTCONN BOOL Socket is listen()ing.
|
|||
|
|
|||
|
SO_BROADCAST BOOL Socket is configured for the transmission
|
|||
|
of broadcast messages.
|
|||
|
|
|||
|
SO_DEBUG BOOL Debugging is enabled.
|
|||
|
|
|||
|
SO_DONTLINGER BOOL If true, the SO_LINGER option is disabled.
|
|||
|
|
|||
|
SO_DONTROUTE BOOL Routing is disabled.
|
|||
|
|
|||
|
SO_ERROR int Retrieve error status and clear.
|
|||
|
|
|||
|
SO_KEEPALIVE BOOL Keepalives are being sent.
|
|||
|
|
|||
|
SO_LINGER struct Returns the current linger
|
|||
|
linger options.
|
|||
|
FAR *
|
|||
|
|
|||
|
SO_OOBINLINE BOOL Out-of-band data is being received in the
|
|||
|
normal data stream.
|
|||
|
|
|||
|
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).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
If no error occurs, setsockopt() returns 0. Otherwise, a value of
|
|||
|
SOCKET_ERROR is returned, and a specific error code may be retrieved
|
|||
|
by calling WSAGetLastError().
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG error;
|
|||
|
INT optionValue;
|
|||
|
INT invertedValue;
|
|||
|
char FAR * valuePointer;
|
|||
|
|
|||
|
#undef setsockopt
|
|||
|
extern int WSAAPI setsockopt( SOCKET s, int level, int optname,
|
|||
|
const char FAR * optval, int optlen );
|
|||
|
|
|||
|
//
|
|||
|
// Set up locals so that we know how to clean up on exit.
|
|||
|
//
|
|||
|
|
|||
|
error = NO_ERROR;
|
|||
|
|
|||
|
//
|
|||
|
// Map the old IP multicast values to their BSD equivilants.
|
|||
|
//
|
|||
|
|
|||
|
if( Level == IPPROTO_IP ) {
|
|||
|
|
|||
|
OptionName = MapOldIpMulticastOptionToBsdValue( OptionName );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Handle TCP_BSDURGENT specially.
|
|||
|
//
|
|||
|
|
|||
|
valuePointer = (char FAR *)OptionValue;
|
|||
|
|
|||
|
if( Level == IPPROTO_TCP && OptionName == TCP_BSDURGENT ) {
|
|||
|
|
|||
|
OptionName = TCP_EXPEDITED_1122;
|
|||
|
|
|||
|
if( OptionLength >= sizeof(INT) ) {
|
|||
|
|
|||
|
invertedValue = !(*OptionValue);
|
|||
|
valuePointer = (char FAR *)&invertedValue;
|
|||
|
OptionLength = sizeof(invertedValue);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return setsockopt(
|
|||
|
Handle,
|
|||
|
Level,
|
|||
|
OptionName,
|
|||
|
valuePointer,
|
|||
|
OptionLength
|
|||
|
);
|
|||
|
|
|||
|
} // setsockopt
|
|||
|
|
|||
|
|
|||
|
INT
|
|||
|
MapOldIpMulticastOptionToBsdValue(
|
|||
|
INT OptionName
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
switch( OptionName ) {
|
|||
|
|
|||
|
case OLD_IP_MULTICAST_IF :
|
|||
|
OptionName = IP_MULTICAST_IF;
|
|||
|
break;
|
|||
|
|
|||
|
case OLD_IP_MULTICAST_TTL :
|
|||
|
OptionName = IP_MULTICAST_TTL;
|
|||
|
break;
|
|||
|
|
|||
|
case OLD_IP_MULTICAST_LOOP :
|
|||
|
OptionName = IP_MULTICAST_LOOP;
|
|||
|
break;
|
|||
|
|
|||
|
case OLD_IP_ADD_MEMBERSHIP :
|
|||
|
OptionName = IP_ADD_MEMBERSHIP;
|
|||
|
break;
|
|||
|
|
|||
|
case OLD_IP_DROP_MEMBERSHIP :
|
|||
|
OptionName = IP_DROP_MEMBERSHIP;
|
|||
|
break;
|
|||
|
|
|||
|
case OLD_IP_TTL :
|
|||
|
OptionName = IP_TTL;
|
|||
|
break;
|
|||
|
|
|||
|
case OLD_IP_TOS :
|
|||
|
OptionName = IP_TOS;
|
|||
|
break;
|
|||
|
|
|||
|
case OLD_IP_DONTFRAGMENT :
|
|||
|
OptionName = IP_DONTFRAGMENT;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return OptionName;
|
|||
|
|
|||
|
} // MapOldIpMulticastOptionToBsdValue
|
|||
|
|
|||
|
|
|||
|
int WSAAPI
|
|||
|
recv(
|
|||
|
IN SOCKET s,
|
|||
|
OUT char FAR * buf,
|
|||
|
IN int len,
|
|||
|
IN int flags
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Receive data from a socket.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
s - A descriptor identifying a connected socket.
|
|||
|
|
|||
|
buf - A buffer for the incoming data.
|
|||
|
|
|||
|
len - The length of buf.
|
|||
|
|
|||
|
flags - Specifies the way in which the call is made.
|
|||
|
|
|||
|
Returns:
|
|||
|
|
|||
|
The number of bytes received. If the connection has been gracefully
|
|||
|
closed, the return value is 0. Otherwise, a value of SOCKET_ERROR is
|
|||
|
returned, and a specific error code is stored with SetErrorCode().
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
INT ReturnValue;
|
|||
|
WSABUF Buffers;
|
|||
|
DWORD LocalFlags;
|
|||
|
INT ErrorCode;
|
|||
|
|
|||
|
Buffers.len = len;
|
|||
|
Buffers.buf = buf;
|
|||
|
LocalFlags = (DWORD) flags;
|
|||
|
|
|||
|
ErrorCode = WSARecv(s,
|
|||
|
&Buffers,
|
|||
|
1, // Buffer count
|
|||
|
(LPDWORD)&ReturnValue,
|
|||
|
&LocalFlags,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
if (SOCKET_ERROR == ErrorCode) {
|
|||
|
ReturnValue = SOCKET_ERROR;
|
|||
|
} else if (LocalFlags & MSG_PARTIAL) {
|
|||
|
|
|||
|
// If the receive was a partial message (won't happen on a
|
|||
|
// streams transport like TCP) set the last error to
|
|||
|
// WSAEMSGSIZE and negate ths number of bytes received.
|
|||
|
// This allows the app to know that the receive was partial
|
|||
|
// and also how many bytes were received.
|
|||
|
//
|
|||
|
|
|||
|
ReturnValue *= -1;
|
|||
|
SetLastError (WSAEMSGSIZE);
|
|||
|
}
|
|||
|
|
|||
|
return(ReturnValue);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int WSAAPI
|
|||
|
recvfrom(
|
|||
|
IN SOCKET s,
|
|||
|
OUT char FAR * buf,
|
|||
|
IN int len,
|
|||
|
IN int flags,
|
|||
|
OUT struct sockaddr FAR *from,
|
|||
|
IN OUT int FAR * fromlen
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Receive a datagram and store the source address.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
s - A descriptor identifying a bound socket.
|
|||
|
|
|||
|
buf - A buffer for the incoming data.
|
|||
|
|
|||
|
len - The length of buf.
|
|||
|
|
|||
|
flags - Specifies the way in which the call is made.
|
|||
|
|
|||
|
from - An optional pointer to a buffer which will hold the source
|
|||
|
address upon return.
|
|||
|
|
|||
|
fromlen - An optional pointer to the size of the from buffer.
|
|||
|
|
|||
|
Returns:
|
|||
|
|
|||
|
The number of bytes received. If the connection has been gracefully
|
|||
|
closed, the return value is 0. Otherwise, a value of SOCKET_ERROR is
|
|||
|
returned, and a specific error code is stored with SetErrorCode().
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
INT ReturnValue;
|
|||
|
WSABUF Buffers;
|
|||
|
DWORD LocalFlags;
|
|||
|
INT ErrorCode;
|
|||
|
|
|||
|
Buffers.len = len;
|
|||
|
Buffers.buf = buf;
|
|||
|
LocalFlags = (DWORD) flags;
|
|||
|
|
|||
|
ErrorCode = WSARecvFrom(s,
|
|||
|
&Buffers,
|
|||
|
1,
|
|||
|
(LPDWORD)&ReturnValue,
|
|||
|
&LocalFlags,
|
|||
|
from,
|
|||
|
fromlen,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
if (SOCKET_ERROR == ErrorCode) {
|
|||
|
ReturnValue = SOCKET_ERROR;
|
|||
|
} else if (LocalFlags & MSG_PARTIAL) {
|
|||
|
|
|||
|
// If the receive was a partial message (won't happen on a
|
|||
|
// streams transport like TCP) set the last error to
|
|||
|
// WSAEMSGSIZE and negate ths number of bytes received.
|
|||
|
// This allows the app to know that the receive was partial
|
|||
|
// and also how many bytes were received.
|
|||
|
//
|
|||
|
|
|||
|
ReturnValue *= -1;
|
|||
|
SetLastError (WSAEMSGSIZE);
|
|||
|
}
|
|||
|
|
|||
|
return ReturnValue;
|
|||
|
}
|