/*++ 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 #include #include // // 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; }